View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io;
18  
19  import java.io.File;
20  import java.util.Objects;
21  import java.util.stream.Stream;
22  
23  /**
24   * Enumeration of IO case sensitivity.
25   * <p>
26   * Different filing systems have different rules for case-sensitivity.
27   * Windows is case-insensitive, UNIX is case-sensitive.
28   * </p>
29   * <p>
30   * This class captures that difference, providing an enumeration to
31   * control how file name comparisons should be performed. It also provides
32   * methods that use the enumeration to perform comparisons.
33   * </p>
34   * <p>
35   * Wherever possible, you should use the {@code check} methods in this
36   * class to compare file names.
37   * </p>
38   *
39   * @since 1.3
40   */
41  public enum IOCase {
42  
43      /**
44       * The constant for case-sensitive regardless of operating system.
45       */
46      SENSITIVE("Sensitive", true),
47  
48      /**
49       * The constant for case-insensitive regardless of operating system.
50       */
51      INSENSITIVE("Insensitive", false),
52  
53      /**
54       * The constant for case sensitivity determined by the current operating system.
55       * Windows is case-insensitive when comparing file names, UNIX is case-sensitive.
56       * <p>
57       * <strong>Note:</strong> This only caters for Windows and Unix. Other operating
58       * systems (e.g. OSX and OpenVMS) are treated as case-sensitive if they use the
59       * UNIX file separator and case-insensitive if they use the Windows file separator
60       * (see {@link File#separatorChar}).
61       * </p>
62       * <p>
63       * If you serialize this constant on Windows, and deserialize on Unix, or vice
64       * versa, then the value of the case-sensitivity flag will change.
65       * </p>
66       */
67      SYSTEM("System", FileSystem.getCurrent().isCaseSensitive());
68  
69      /** Serialization version. */
70      private static final long serialVersionUID = -6343169151696340687L;
71  
72      /**
73       * Looks up an IOCase by name.
74       *
75       * @param name  the name to find
76       * @return the IOCase object
77       * @throws IllegalArgumentException if the name is invalid
78       */
79      public static IOCase forName(final String name) {
80          return Stream.of(values()).filter(ioCase -> ioCase.getName().equals(name)).findFirst()
81                  .orElseThrow(() -> new IllegalArgumentException("Illegal IOCase name: " + name));
82      }
83  
84      /**
85       * Tests for cases sensitivity in a null-safe manner.
86       *
87       * @param ioCase an IOCase.
88       * @return true if the input is non-null and {@link #isCaseSensitive()}.
89       * @since 2.10.0
90       */
91      public static boolean isCaseSensitive(final IOCase ioCase) {
92          return ioCase != null && ioCase.isCaseSensitive();
93      }
94  
95      /**
96       * Returns the given value if not-null, the defaultValue if null.
97       *
98       * @param value the value to test.
99       * @param defaultValue the default value.
100      * @return the given value if not-null, the defaultValue if null.
101      * @since 2.12.0
102      */
103     public static IOCase value(final IOCase value, final IOCase defaultValue) {
104         return value != null ? value : defaultValue;
105     }
106 
107     /** The enumeration name. */
108     private final String name;
109 
110     /** The sensitivity flag. */
111     private final transient boolean sensitive;
112 
113     /**
114      * Constructs a new instance.
115      *
116      * @param name  the name.
117      * @param sensitive  the sensitivity.
118      */
119     IOCase(final String name, final boolean sensitive) {
120         this.name = name;
121         this.sensitive = sensitive;
122     }
123 
124     /**
125      * Compares two strings using the case-sensitivity rule.
126      * <p>
127      * This method mimics {@link String#compareTo} but takes case-sensitivity
128      * into account.
129      * </p>
130      *
131      * @param str1  the first string to compare, not null.
132      * @param str2  the second string to compare, not null.
133      * @return true if equal using the case rules.
134      * @throws NullPointerException if either string is null.
135      */
136     public int checkCompareTo(final String str1, final String str2) {
137         Objects.requireNonNull(str1, "str1");
138         Objects.requireNonNull(str2, "str2");
139         return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);
140     }
141 
142     /**
143      * Checks if one string ends with another using the case-sensitivity rule.
144      * <p>
145      * This method mimics {@link String#endsWith} but takes case-sensitivity
146      * into account.
147      * </p>
148      *
149      * @param str  the string to check.
150      * @param end  the end to compare against.
151      * @return true if equal using the case rules, false if either input is null.
152      */
153     public boolean checkEndsWith(final String str, final String end) {
154         if (str == null || end == null) {
155             return false;
156         }
157         final int endLen = end.length();
158         return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
159     }
160 
161     /**
162      * Compares two strings using the case-sensitivity rule.
163      * <p>
164      * This method mimics {@link String#equals} but takes case-sensitivity
165      * into account.
166      * </p>
167      *
168      * @param str1  the first string to compare.
169      * @param str2  the second string to compare.
170      * @return true if equal using the case rules.
171      */
172     public boolean checkEquals(final String str1, final String str2) {
173         return str1 == str2 || str1 != null && (sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2));
174     }
175 
176     /**
177      * Checks if one string contains another starting at a specific index using the
178      * case-sensitivity rule.
179      * <p>
180      * This method mimics parts of {@link String#indexOf(String, int)}
181      * but takes case-sensitivity into account.
182      * </p>
183      *
184      * @param str  the string to check.
185      * @param strStartIndex  the index to start at in str.
186      * @param search  the start to search for.
187      * @return the first index of the search String,
188      *  -1 if no match or {@code null} string input.
189      * @since 2.0
190      */
191     public int checkIndexOf(final String str, final int strStartIndex, final String search) {
192         if (str != null && search != null) {
193             final int endIndex = str.length() - search.length();
194             if (endIndex >= strStartIndex) {
195                 for (int i = strStartIndex; i <= endIndex; i++) {
196                     if (checkRegionMatches(str, i, search)) {
197                         return i;
198                     }
199                 }
200             }
201         }
202         return -1;
203     }
204 
205     /**
206      * Checks if one string contains another at a specific index using the case-sensitivity rule.
207      * <p>
208      * This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)}
209      * but takes case-sensitivity into account.
210      * </p>
211      *
212      * @param str  the string to check.
213      * @param strStartIndex  the index to start at in str.
214      * @param search  the start to search for,.
215      * @return true if equal using the case rules.
216      */
217     public boolean checkRegionMatches(final String str, final int strStartIndex, final String search) {
218         return str != null && search != null && str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
219     }
220 
221     /**
222      * Checks if one string starts with another using the case-sensitivity rule.
223      * <p>
224      * This method mimics {@link String#startsWith(String)} but takes case-sensitivity
225      * into account.
226      * </p>
227      *
228      * @param str  the string to check.
229      * @param start  the start to compare against.
230      * @return true if equal using the case rules, false if either input is null.
231      */
232     public boolean checkStartsWith(final String str, final String start) {
233         return str != null && start != null && str.regionMatches(!sensitive, 0, start, 0, start.length());
234     }
235 
236     /**
237      * Gets the name of the constant.
238      *
239      * @return the name of the constant
240      */
241     public String getName() {
242         return name;
243     }
244 
245     /**
246      * Does the object represent case-sensitive comparison.
247      *
248      * @return true if case-sensitive.
249      */
250     public boolean isCaseSensitive() {
251         return sensitive;
252     }
253 
254     /**
255      * Replaces the enumeration from the stream with a real one.
256      * This ensures that the correct flag is set for SYSTEM.
257      *
258      * @return the resolved object.
259      */
260     private Object readResolve() {
261         return forName(name);
262     }
263 
264     /**
265      * Gets a string describing the sensitivity.
266      *
267      * @return a string describing the sensitivity.
268      */
269     @Override
270     public String toString() {
271         return name;
272     }
273 
274 }