001/* 002 * Copyright 2023 the original author or authors. 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.tools.io; 017 018/** 019 * Enumeration of IO case sensitivity. 020 * <p> 021 * Different filing systems have different rules for case-sensitivity. Windows 022 * is case-insensitive, Unix is case-sensitive. 023 * <p> 024 * This class captures that difference, providing an enumeration to control how 025 * filename comparisons should be performed. It also provides methods that use 026 * the enumeration to perform comparisons. 027 * <p> 028 * Wherever possible, you should use the <code>check</code> methods in this 029 * class to compare filenames. 030 * 031 * @author commons.io:org.apache.commons.io.IOCase 032 */ 033public enum IOCase { 034 035 /** 036 * The constant for case-sensitive regardless of operating system. 037 */ 038 SENSITIVE("Sensitive", true), 039 040 /** 041 * The constant for case-insensitive regardless of operating system. 042 */ 043 INSENSITIVE("Insensitive", false), 044 045 /** 046 * The constant for case sensitivity determined by the current operating system. 047 * Windows is case-insensitive when comparing filenames, Unix is case-sensitive. 048 * <p> 049 * <strong>Note:</strong> This only caters for Windows and Unix. Other operating 050 * systems (e.g. OSX and OpenVMS) are treated as case-sensitive if they use the 051 * Unix file separator and case-insensitive if they use the Windows file 052 * separator (see {@link java.io.File#separatorChar}). 053 * <p> 054 * If you serialize this constant on Windows, and deserialize on Unix, or vice 055 * versa, then the value of the case-sensitivity flag will change. 056 */ 057 SYSTEM("System", !FilenameUtils.isSystemWindows()); 058 059 /** 060 * The enumeration name. 061 */ 062 private final String name; 063 064 /** 065 * The sensitivity flag. 066 */ 067 private final transient boolean sensitive; 068 069 // ----------------------------------------------------------------------- 070 071 /** 072 * Factory method to create an IOCase from a name. 073 * 074 * @param name the name to find 075 * @return the IOCase object 076 * @throws IllegalArgumentException if the name is invalid 077 */ 078 public static IOCase forName(final String name) { 079 for (final IOCase ioCase : IOCase.values()) { 080 if (ioCase.getName().equals(name)) { 081 return ioCase; 082 } 083 } 084 throw new IllegalArgumentException("Invalid IOCase name: " + name); 085 } 086 087 // ----------------------------------------------------------------------- 088 089 /** 090 * Private constructor. 091 * 092 * @param name the name 093 * @param sensitive the sensitivity 094 */ 095 IOCase(final String name, final boolean sensitive) { 096 this.name = name; 097 this.sensitive = sensitive; 098 } 099 100 /** 101 * Replaces the enumeration from the stream with a real one. This ensures that 102 * the correct flag is set for SYSTEM. 103 * 104 * @return the resolved object 105 */ 106 Object readResolve() { 107 return forName(name); 108 } 109 110 // ----------------------------------------------------------------------- 111 112 /** 113 * Gets the name of the constant. 114 * 115 * @return the name of the constant 116 */ 117 public String getName() { 118 return name; 119 } 120 121 /** 122 * Does the object represent case-sensitive comparison. 123 * 124 * @return true if case-sensitive 125 */ 126 public boolean isCaseSensitive() { 127 return sensitive; 128 } 129 130 // ----------------------------------------------------------------------- 131 132 /** 133 * Compares two strings using the case-sensitivity rule. 134 * <p> 135 * This method mimics {@link String#compareTo} but takes case-sensitivity into 136 * account. 137 * 138 * @param str1 the first string to compare, not null 139 * @param str2 the second string to compare, not null 140 * @return the value 0 if the argument string is equal to this string; a value 141 * less than 0 if this string is lexicographically less than the string 142 * argument; and a value greater than 0 if this string is 143 * lexicographically greater than the string argument. 144 * @throws NullPointerException if either string is null 145 */ 146 public int checkCompareTo(final String str1, final String str2) { 147 if (str1 == null || str2 == null) { 148 throw new NullPointerException("The strings must not be null"); 149 } 150 return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2); 151 } 152 153 /** 154 * Compares two strings using the case-sensitivity rule. 155 * <p> 156 * This method mimics {@link String#equals} but takes case-sensitivity into 157 * account. 158 * 159 * @param str1 the first string to compare, not null 160 * @param str2 the second string to compare, not null 161 * @return true if equal using the case rules 162 * @throws NullPointerException if either string is null 163 */ 164 public boolean checkEquals(final String str1, final String str2) { 165 if (str1 == null || str2 == null) { 166 throw new NullPointerException("The strings must not be null"); 167 } 168 return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2); 169 } 170 171 /** 172 * Checks if one string starts with another using the case-sensitivity rule. 173 * <p> 174 * This method mimics {@link String#startsWith(String)} but takes 175 * case-sensitivity into account. 176 * 177 * @param str the string to check, not null 178 * @param start the start to compare against, not null 179 * @return true if equal using the case rules 180 * @throws NullPointerException if either string is null 181 */ 182 public boolean checkStartsWith(final String str, final String start) { 183 return str.regionMatches(!sensitive, 0, start, 0, start.length()); 184 } 185 186 /** 187 * Checks if one string ends with another using the case-sensitivity rule. 188 * <p> 189 * This method mimics {@link String#endsWith} but takes case-sensitivity into 190 * account. 191 * 192 * @param str the string to check, not null 193 * @param end the end to compare against, not null 194 * @return true if equal using the case rules 195 * @throws NullPointerException if either string is null 196 */ 197 public boolean checkEndsWith(final String str, final String end) { 198 final var endLen = end.length(); 199 return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen); 200 } 201 202 /** 203 * Checks if one string contains another starting at a specific index using the 204 * case-sensitivity rule. 205 * <p> 206 * This method mimics parts of {@link String#indexOf(String, int)} but takes 207 * case-sensitivity into account. 208 * 209 * @param str the string to check, not null 210 * @param strStartIndex the index to start at in str 211 * @param search the start to search for, not null 212 * @return the first index of the search String, -1 if no match or {@code null} 213 * string input 214 * @throws NullPointerException if either string is null 215 */ 216 public int checkIndexOf(final String str, final int strStartIndex, final String search) { 217 final var endIndex = str.length() - search.length(); 218 if (endIndex >= strStartIndex) { 219 for (var i = strStartIndex; i <= endIndex; i++) { 220 if (checkRegionMatches(str, i, search)) { 221 return i; 222 } 223 } 224 } 225 return -1; 226 } 227 228 /** 229 * Checks if one string contains another at a specific index using the 230 * case-sensitivity rule. 231 * <p> 232 * This method mimics parts of 233 * {@link String#regionMatches(boolean, int, String, int, int)} but takes 234 * case-sensitivity into account. 235 * 236 * @param str the string to check, not null 237 * @param strStartIndex the index to start at in str 238 * @param search the start to search for, not null 239 * @return true if equal using the case rules 240 * @throws NullPointerException if either string is null 241 */ 242 public boolean checkRegionMatches(final String str, final int strStartIndex, final String search) { 243 return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length()); 244 } 245 246 // ----------------------------------------------------------------------- 247 248 /** 249 * Gets a string describing the sensitivity. 250 * 251 * @return a string describing the sensitivity 252 */ 253 @Override 254 public String toString() { 255 return name; 256 } 257 258}