IOCase.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.io;
import java.io.File;
import java.util.Objects;
import java.util.stream.Stream;
/**
* Enumeration of IO case sensitivity.
* <p>
* Different filing systems have different rules for case-sensitivity.
* Windows is case-insensitive, UNIX is case-sensitive.
* </p>
* <p>
* This class captures that difference, providing an enumeration to
* control how file name comparisons should be performed. It also provides
* methods that use the enumeration to perform comparisons.
* </p>
* <p>
* Wherever possible, you should use the {@code check} methods in this
* class to compare file names.
* </p>
*
* @since 1.3
*/
public enum IOCase {
/**
* The constant for case-sensitive regardless of operating system.
*/
SENSITIVE("Sensitive", true),
/**
* The constant for case-insensitive regardless of operating system.
*/
INSENSITIVE("Insensitive", false),
/**
* The constant for case sensitivity determined by the current operating system.
* Windows is case-insensitive when comparing file names, UNIX is case-sensitive.
* <p>
* <strong>Note:</strong> This only caters for Windows and Unix. Other operating
* systems (e.g. OSX and OpenVMS) are treated as case-sensitive if they use the
* UNIX file separator and case-insensitive if they use the Windows file separator
* (see {@link File#separatorChar}).
* </p>
* <p>
* If you serialize this constant on Windows, and deserialize on Unix, or vice
* versa, then the value of the case-sensitivity flag will change.
* </p>
*/
SYSTEM("System", FileSystem.getCurrent().isCaseSensitive());
/** Serialization version. */
private static final long serialVersionUID = -6343169151696340687L;
/**
* Looks up an IOCase by name.
*
* @param name the name to find
* @return the IOCase object
* @throws IllegalArgumentException if the name is invalid
*/
public static IOCase forName(final String name) {
return Stream.of(values()).filter(ioCase -> ioCase.getName().equals(name)).findFirst()
.orElseThrow(() -> new IllegalArgumentException("Illegal IOCase name: " + name));
}
/**
* Tests for cases sensitivity in a null-safe manner.
*
* @param ioCase an IOCase.
* @return true if the input is non-null and {@link #isCaseSensitive()}.
* @since 2.10.0
*/
public static boolean isCaseSensitive(final IOCase ioCase) {
return ioCase != null && ioCase.isCaseSensitive();
}
/**
* Returns the given value if not-null, the defaultValue if null.
*
* @param value the value to test.
* @param defaultValue the default value.
* @return the given value if not-null, the defaultValue if null.
* @since 2.12.0
*/
public static IOCase value(final IOCase value, final IOCase defaultValue) {
return value != null ? value : defaultValue;
}
/** The enumeration name. */
private final String name;
/** The sensitivity flag. */
private final transient boolean sensitive;
/**
* Constructs a new instance.
*
* @param name the name.
* @param sensitive the sensitivity.
*/
IOCase(final String name, final boolean sensitive) {
this.name = name;
this.sensitive = sensitive;
}
/**
* Compares two strings using the case-sensitivity rule.
* <p>
* This method mimics {@link String#compareTo} but takes case-sensitivity
* into account.
* </p>
*
* @param str1 the first string to compare, not null.
* @param str2 the second string to compare, not null.
* @return true if equal using the case rules.
* @throws NullPointerException if either string is null.
*/
public int checkCompareTo(final String str1, final String str2) {
Objects.requireNonNull(str1, "str1");
Objects.requireNonNull(str2, "str2");
return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);
}
/**
* Checks if one string ends with another using the case-sensitivity rule.
* <p>
* This method mimics {@link String#endsWith} but takes case-sensitivity
* into account.
* </p>
*
* @param str the string to check.
* @param end the end to compare against.
* @return true if equal using the case rules, false if either input is null.
*/
public boolean checkEndsWith(final String str, final String end) {
if (str == null || end == null) {
return false;
}
final int endLen = end.length();
return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
}
/**
* Compares two strings using the case-sensitivity rule.
* <p>
* This method mimics {@link String#equals} but takes case-sensitivity
* into account.
* </p>
*
* @param str1 the first string to compare.
* @param str2 the second string to compare.
* @return true if equal using the case rules.
*/
public boolean checkEquals(final String str1, final String str2) {
return str1 == str2 || str1 != null && (sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2));
}
/**
* Checks if one string contains another starting at a specific index using the
* case-sensitivity rule.
* <p>
* This method mimics parts of {@link String#indexOf(String, int)}
* but takes case-sensitivity into account.
* </p>
*
* @param str the string to check.
* @param strStartIndex the index to start at in str.
* @param search the start to search for.
* @return the first index of the search String,
* -1 if no match or {@code null} string input.
* @since 2.0
*/
public int checkIndexOf(final String str, final int strStartIndex, final String search) {
if (str != null && search != null) {
final int endIndex = str.length() - search.length();
if (endIndex >= strStartIndex) {
for (int i = strStartIndex; i <= endIndex; i++) {
if (checkRegionMatches(str, i, search)) {
return i;
}
}
}
}
return -1;
}
/**
* Checks if one string contains another at a specific index using the case-sensitivity rule.
* <p>
* This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)}
* but takes case-sensitivity into account.
* </p>
*
* @param str the string to check.
* @param strStartIndex the index to start at in str.
* @param search the start to search for,.
* @return true if equal using the case rules.
*/
public boolean checkRegionMatches(final String str, final int strStartIndex, final String search) {
return str != null && search != null && str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
}
/**
* Checks if one string starts with another using the case-sensitivity rule.
* <p>
* This method mimics {@link String#startsWith(String)} but takes case-sensitivity
* into account.
* </p>
*
* @param str the string to check.
* @param start the start to compare against.
* @return true if equal using the case rules, false if either input is null.
*/
public boolean checkStartsWith(final String str, final String start) {
return str != null && start != null && str.regionMatches(!sensitive, 0, start, 0, start.length());
}
/**
* Gets the name of the constant.
*
* @return the name of the constant
*/
public String getName() {
return name;
}
/**
* Does the object represent case-sensitive comparison.
*
* @return true if case-sensitive.
*/
public boolean isCaseSensitive() {
return sensitive;
}
/**
* Replaces the enumeration from the stream with a real one.
* This ensures that the correct flag is set for SYSTEM.
*
* @return the resolved object.
*/
private Object readResolve() {
return forName(name);
}
/**
* Gets a string describing the sensitivity.
*
* @return a string describing the sensitivity.
*/
@Override
public String toString() {
return name;
}
}