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