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 * Looks up an IOCase by 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. 168 * @param str2 the second string to compare. 169 * @return true if equal using the case rules. 170 */ 171 public boolean checkEquals(final String str1, final String str2) { 172 return str1 == str2 || str1 != null && (sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2)); 173 } 174 175 /** 176 * Checks if one string contains another starting at a specific index using the 177 * case-sensitivity rule. 178 * <p> 179 * This method mimics parts of {@link String#indexOf(String, int)} 180 * but takes case-sensitivity into account. 181 * </p> 182 * 183 * @param str the string to check. 184 * @param strStartIndex the index to start at in str. 185 * @param search the start to search for. 186 * @return the first index of the search String, 187 * -1 if no match or {@code null} string input. 188 * @since 2.0 189 */ 190 public int checkIndexOf(final String str, final int strStartIndex, final String search) { 191 if (str != null && search != null) { 192 final int endIndex = str.length() - search.length(); 193 if (endIndex >= strStartIndex) { 194 for (int i = strStartIndex; i <= endIndex; i++) { 195 if (checkRegionMatches(str, i, search)) { 196 return i; 197 } 198 } 199 } 200 } 201 return -1; 202 } 203 204 /** 205 * Checks if one string contains another at a specific index using the case-sensitivity rule. 206 * <p> 207 * This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)} 208 * but takes case-sensitivity into account. 209 * </p> 210 * 211 * @param str the string to check. 212 * @param strStartIndex the index to start at in str. 213 * @param search the start to search for,. 214 * @return true if equal using the case rules. 215 */ 216 public boolean checkRegionMatches(final String str, final int strStartIndex, final String search) { 217 return str != null && search != null && str.regionMatches(!sensitive, strStartIndex, search, 0, search.length()); 218 } 219 220 /** 221 * Checks if one string starts with another using the case-sensitivity rule. 222 * <p> 223 * This method mimics {@link String#startsWith(String)} but takes case-sensitivity 224 * into account. 225 * </p> 226 * 227 * @param str the string to check. 228 * @param start the start to compare against. 229 * @return true if equal using the case rules, false if either input is null. 230 */ 231 public boolean checkStartsWith(final String str, final String start) { 232 return str != null && start != null && str.regionMatches(!sensitive, 0, start, 0, start.length()); 233 } 234 235 /** 236 * Gets the name of the constant. 237 * 238 * @return the name of the constant 239 */ 240 public String getName() { 241 return name; 242 } 243 244 /** 245 * Does the object represent case-sensitive comparison. 246 * 247 * @return true if case-sensitive. 248 */ 249 public boolean isCaseSensitive() { 250 return sensitive; 251 } 252 253 /** 254 * Replaces the enumeration from the stream with a real one. 255 * This ensures that the correct flag is set for SYSTEM. 256 * 257 * @return the resolved object. 258 */ 259 private Object readResolve() { 260 return forName(name); 261 } 262 263 /** 264 * Gets a string describing the sensitivity. 265 * 266 * @return a string describing the sensitivity. 267 */ 268 @Override 269 public String toString() { 270 return name; 271 } 272 273}