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