IOCase.java

  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. import java.io.File;
  19. import java.util.Objects;
  20. import java.util.stream.Stream;

  21. /**
  22.  * Enumeration of IO case sensitivity.
  23.  * <p>
  24.  * Different filing systems have different rules for case-sensitivity.
  25.  * Windows is case-insensitive, Unix is case-sensitive.
  26.  * </p>
  27.  * <p>
  28.  * This class captures that difference, providing an enumeration to
  29.  * control how file name comparisons should be performed. It also provides
  30.  * methods that use the enumeration to perform comparisons.
  31.  * </p>
  32.  * <p>
  33.  * Wherever possible, you should use the {@code check} methods in this
  34.  * class to compare file names.
  35.  * </p>
  36.  *
  37.  * @since 1.3
  38.  */
  39. public enum IOCase {

  40.     /**
  41.      * The constant for case-sensitive regardless of operating system.
  42.      */
  43.     SENSITIVE("Sensitive", true),

  44.     /**
  45.      * The constant for case-insensitive regardless of operating system.
  46.      */
  47.     INSENSITIVE("Insensitive", false),

  48.     /**
  49.      * The constant for case sensitivity determined by the current operating system.
  50.      * Windows is case-insensitive when comparing file names, Unix is case-sensitive.
  51.      * <p>
  52.      * <strong>Note:</strong> This only caters for Windows and Unix. Other operating
  53.      * systems (e.g. OSX and OpenVMS) are treated as case-sensitive if they use the
  54.      * Unix file separator and case-insensitive if they use the Windows file separator
  55.      * (see {@link File#separatorChar}).
  56.      * </p>
  57.      * <p>
  58.      * If you serialize this constant on Windows, and deserialize on Unix, or vice
  59.      * versa, then the value of the case-sensitivity flag will change.
  60.      * </p>
  61.      */
  62.     SYSTEM("System", FileSystem.getCurrent().isCaseSensitive());

  63.     /** Serialization version. */
  64.     private static final long serialVersionUID = -6343169151696340687L;

  65.     /**
  66.      * Looks up an IOCase by name.
  67.      *
  68.      * @param name  the name to find
  69.      * @return the IOCase object
  70.      * @throws IllegalArgumentException if the name is invalid
  71.      */
  72.     public static IOCase forName(final String name) {
  73.         return Stream.of(values()).filter(ioCase -> ioCase.getName().equals(name)).findFirst()
  74.                 .orElseThrow(() -> new IllegalArgumentException("Illegal IOCase name: " + name));
  75.     }

  76.     /**
  77.      * Tests for cases sensitivity in a null-safe manner.
  78.      *
  79.      * @param ioCase an IOCase.
  80.      * @return true if the input is non-null and {@link #isCaseSensitive()}.
  81.      * @since 2.10.0
  82.      */
  83.     public static boolean isCaseSensitive(final IOCase ioCase) {
  84.         return ioCase != null && ioCase.isCaseSensitive();
  85.     }

  86.     /**
  87.      * Returns the given value if not-null, the defaultValue if null.
  88.      *
  89.      * @param value the value to test.
  90.      * @param defaultValue the default value.
  91.      * @return the given value if not-null, the defaultValue if null.
  92.      * @since 2.12.0
  93.      */
  94.     public static IOCase value(final IOCase value, final IOCase defaultValue) {
  95.         return value != null ? value : defaultValue;
  96.     }

  97.     /** The enumeration name. */
  98.     private final String name;

  99.     /** The sensitivity flag. */
  100.     private final transient boolean sensitive;

  101.     /**
  102.      * Constructs a new instance.
  103.      *
  104.      * @param name  the name.
  105.      * @param sensitive  the sensitivity.
  106.      */
  107.     IOCase(final String name, final boolean sensitive) {
  108.         this.name = name;
  109.         this.sensitive = sensitive;
  110.     }

  111.     /**
  112.      * Compares two strings using the case-sensitivity rule.
  113.      * <p>
  114.      * This method mimics {@link String#compareTo} but takes case-sensitivity
  115.      * into account.
  116.      * </p>
  117.      *
  118.      * @param str1  the first string to compare, not null.
  119.      * @param str2  the second string to compare, not null.
  120.      * @return true if equal using the case rules.
  121.      * @throws NullPointerException if either string is null.
  122.      */
  123.     public int checkCompareTo(final String str1, final String str2) {
  124.         Objects.requireNonNull(str1, "str1");
  125.         Objects.requireNonNull(str2, "str2");
  126.         return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);
  127.     }

  128.     /**
  129.      * Checks if one string ends with another using the case-sensitivity rule.
  130.      * <p>
  131.      * This method mimics {@link String#endsWith} but takes case-sensitivity
  132.      * into account.
  133.      * </p>
  134.      *
  135.      * @param str  the string to check.
  136.      * @param end  the end to compare against.
  137.      * @return true if equal using the case rules, false if either input is null.
  138.      */
  139.     public boolean checkEndsWith(final String str, final String end) {
  140.         if (str == null || end == null) {
  141.             return false;
  142.         }
  143.         final int endLen = end.length();
  144.         return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
  145.     }

  146.     /**
  147.      * Compares two strings using the case-sensitivity rule.
  148.      * <p>
  149.      * This method mimics {@link String#equals} but takes case-sensitivity
  150.      * into account.
  151.      * </p>
  152.      *
  153.      * @param str1  the first string to compare.
  154.      * @param str2  the second string to compare.
  155.      * @return true if equal using the case rules.
  156.      */
  157.     public boolean checkEquals(final String str1, final String str2) {
  158.         return str1 == str2 || str1 != null && (sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2));
  159.     }

  160.     /**
  161.      * Checks if one string contains another starting at a specific index using the
  162.      * case-sensitivity rule.
  163.      * <p>
  164.      * This method mimics parts of {@link String#indexOf(String, int)}
  165.      * but takes case-sensitivity into account.
  166.      * </p>
  167.      *
  168.      * @param str  the string to check.
  169.      * @param strStartIndex  the index to start at in str.
  170.      * @param search  the start to search for.
  171.      * @return the first index of the search String,
  172.      *  -1 if no match or {@code null} string input.
  173.      * @since 2.0
  174.      */
  175.     public int checkIndexOf(final String str, final int strStartIndex, final String search) {
  176.         if (str != null && search != null) {
  177.             final int endIndex = str.length() - search.length();
  178.             if (endIndex >= strStartIndex) {
  179.                 for (int i = strStartIndex; i <= endIndex; i++) {
  180.                     if (checkRegionMatches(str, i, search)) {
  181.                         return i;
  182.                     }
  183.                 }
  184.             }
  185.         }
  186.         return -1;
  187.     }

  188.     /**
  189.      * Checks if one string contains another at a specific index using the case-sensitivity rule.
  190.      * <p>
  191.      * This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)}
  192.      * but takes case-sensitivity into account.
  193.      * </p>
  194.      *
  195.      * @param str  the string to check.
  196.      * @param strStartIndex  the index to start at in str.
  197.      * @param search  the start to search for,.
  198.      * @return true if equal using the case rules.
  199.      */
  200.     public boolean checkRegionMatches(final String str, final int strStartIndex, final String search) {
  201.         return str != null && search != null && str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
  202.     }

  203.     /**
  204.      * Checks if one string starts with another using the case-sensitivity rule.
  205.      * <p>
  206.      * This method mimics {@link String#startsWith(String)} but takes case-sensitivity
  207.      * into account.
  208.      * </p>
  209.      *
  210.      * @param str  the string to check.
  211.      * @param start  the start to compare against.
  212.      * @return true if equal using the case rules, false if either input is null.
  213.      */
  214.     public boolean checkStartsWith(final String str, final String start) {
  215.         return str != null && start != null && str.regionMatches(!sensitive, 0, start, 0, start.length());
  216.     }

  217.     /**
  218.      * Gets the name of the constant.
  219.      *
  220.      * @return the name of the constant
  221.      */
  222.     public String getName() {
  223.         return name;
  224.     }

  225.     /**
  226.      * Does the object represent case-sensitive comparison.
  227.      *
  228.      * @return true if case-sensitive.
  229.      */
  230.     public boolean isCaseSensitive() {
  231.         return sensitive;
  232.     }

  233.     /**
  234.      * Replaces the enumeration from the stream with a real one.
  235.      * This ensures that the correct flag is set for SYSTEM.
  236.      *
  237.      * @return the resolved object.
  238.      */
  239.     private Object readResolve() {
  240.         return forName(name);
  241.     }

  242.     /**
  243.      * Gets a string describing the sensitivity.
  244.      *
  245.      * @return a string describing the sensitivity.
  246.      */
  247.     @Override
  248.     public String toString() {
  249.         return name;
  250.     }

  251. }