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