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