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; 020 021/** 022 * Enumeration of IO case sensitivity. 023 * <p> 024 * Different filing systems have different rules for case-sensitivity. 025 * Windows is case-insensitive, Unix is case-sensitive. 026 * </p> 027 * <p> 028 * This class captures that difference, providing an enumeration to 029 * control how file name comparisons should be performed. It also provides 030 * methods that use the enumeration to perform comparisons. 031 * </p> 032 * <p> 033 * Wherever possible, you should use the {@code check} methods in this 034 * class to compare file names. 035 * </p> 036 * 037 * @since 1.3 038 */ 039public enum IOCase { 040 041 /** 042 * The constant for case sensitive regardless of operating system. 043 */ 044 SENSITIVE("Sensitive", true), 045 046 /** 047 * The constant for case insensitive regardless of operating system. 048 */ 049 INSENSITIVE("Insensitive", false), 050 051 /** 052 * The constant for case sensitivity determined by the current operating system. 053 * Windows is case-insensitive when comparing file names, Unix is case-sensitive. 054 * <p> 055 * <strong>Note:</strong> This only caters for Windows and Unix. Other operating 056 * systems (e.g. OSX and OpenVMS) are treated as case sensitive if they use the 057 * Unix file separator and case-insensitive if they use the Windows file separator 058 * (see {@link java.io.File#separatorChar}). 059 * </p> 060 * <p> 061 * If you serialize this constant on Windows, and deserialize on Unix, or vice 062 * versa, then the value of the case-sensitivity flag will change. 063 * </p> 064 */ 065 SYSTEM("System", !FilenameUtils.isSystemWindows()); 066 067 /** 068 * Tests for cases sensitivity in a null-safe manner. 069 * 070 * @param caseSensitivity an IOCase. 071 * @return true if the input is non-null and {@link #isCaseSensitive()}. 072 * @since 2.10.0 073 */ 074 public static boolean isCaseSensitive(final IOCase caseSensitivity) { 075 return caseSensitivity != null && !caseSensitivity.isCaseSensitive(); 076 } 077 078 /** Serialization version. */ 079 private static final long serialVersionUID = -6343169151696340687L; 080 081 /** The enumeration name. */ 082 private final String name; 083 084 /** The sensitivity flag. */ 085 private final transient boolean sensitive; 086 087 /** 088 * Factory method to create an IOCase from a name. 089 * 090 * @param name the name to find 091 * @return the IOCase object 092 * @throws IllegalArgumentException if the name is invalid 093 */ 094 public static IOCase forName(final String name) { 095 for (final IOCase ioCase : IOCase.values()) { 096 if (ioCase.getName().equals(name)) { 097 return ioCase; 098 } 099 } 100 throw new IllegalArgumentException("Invalid IOCase name: " + name); 101 } 102 103 /** 104 * Constructs a new instance. 105 * 106 * @param name the name 107 * @param sensitive the sensitivity 108 */ 109 IOCase(final String name, final boolean sensitive) { 110 this.name = name; 111 this.sensitive = sensitive; 112 } 113 114 /** 115 * Replaces the enumeration from the stream with a real one. 116 * This ensures that the correct flag is set for SYSTEM. 117 * 118 * @return the resolved object 119 */ 120 private Object readResolve() { 121 return forName(name); 122 } 123 124 /** 125 * Gets the name of the constant. 126 * 127 * @return the name of the constant 128 */ 129 public String getName() { 130 return name; 131 } 132 133 /** 134 * Does the object represent case sensitive comparison. 135 * 136 * @return true if case sensitive 137 */ 138 public boolean isCaseSensitive() { 139 return sensitive; 140 } 141 142 /** 143 * Compares two strings using the case-sensitivity rule. 144 * <p> 145 * This method mimics {@link String#compareTo} but takes case-sensitivity 146 * into account. 147 * </p> 148 * 149 * @param str1 the first string to compare, not null 150 * @param str2 the second string to compare, not null 151 * @return true if equal using the case rules 152 * @throws NullPointerException if either string is null 153 */ 154 public int checkCompareTo(final String str1, final String str2) { 155 Objects.requireNonNull(str1, "str1"); 156 Objects.requireNonNull(str2, "str2"); 157 return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2); 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 starts with another using the case-sensitivity rule. 180 * <p> 181 * This method mimics {@link String#startsWith(String)} but takes case-sensitivity 182 * into account. 183 * </p> 184 * 185 * @param str the string to check 186 * @param start the start to compare against 187 * @return true if equal using the case rules, false if either input is null 188 */ 189 public boolean checkStartsWith(final String str, final String start) { 190 return str != null && start != null && str.regionMatches(!sensitive, 0, start, 0, start.length()); 191 } 192 193 /** 194 * Checks if one string ends with another using the case-sensitivity rule. 195 * <p> 196 * This method mimics {@link String#endsWith} but takes case-sensitivity 197 * into account. 198 * </p> 199 * 200 * @param str the string to check 201 * @param end the end to compare against 202 * @return true if equal using the case rules, false if either input is null 203 */ 204 public boolean checkEndsWith(final String str, final String end) { 205 if (str == null || end == null) { 206 return false; 207 } 208 final int endLen = end.length(); 209 return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen); 210 } 211 212 /** 213 * Checks if one string contains another starting at a specific index using the 214 * case-sensitivity rule. 215 * <p> 216 * This method mimics parts of {@link String#indexOf(String, int)} 217 * but takes case-sensitivity into account. 218 * </p> 219 * 220 * @param str the string to check, not null 221 * @param strStartIndex the index to start at in str 222 * @param search the start to search for, not null 223 * @return the first index of the search String, 224 * -1 if no match or {@code null} string input 225 * @throws NullPointerException if either string is null 226 * @since 2.0 227 */ 228 public int checkIndexOf(final String str, final int strStartIndex, final String search) { 229 final int endIndex = str.length() - search.length(); 230 if (endIndex >= strStartIndex) { 231 for (int i = strStartIndex; i <= endIndex; i++) { 232 if (checkRegionMatches(str, i, search)) { 233 return i; 234 } 235 } 236 } 237 return -1; 238 } 239 240 /** 241 * Checks if one string contains another at a specific index using the case-sensitivity rule. 242 * <p> 243 * This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)} 244 * but takes case-sensitivity into account. 245 * </p> 246 * 247 * @param str the string to check, not null 248 * @param strStartIndex the index to start at in str 249 * @param search the start to search for, not null 250 * @return true if equal using the case rules 251 * @throws NullPointerException if either string is null 252 */ 253 public boolean checkRegionMatches(final String str, final int strStartIndex, final String search) { 254 return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length()); 255 } 256 257 /** 258 * Gets a string describing the sensitivity. 259 * 260 * @return a string describing the sensitivity 261 */ 262 @Override 263 public String toString() { 264 return name; 265 } 266 267}