Home | History | Annotate | Download | only in html
      1 // Copyright (c) 2011, Mike Samuel
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions
      6 // are met:
      7 //
      8 // Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 // Redistributions in binary form must reproduce the above copyright
     11 // notice, this list of conditions and the following disclaimer in the
     12 // documentation and/or other materials provided with the distribution.
     13 // Neither the name of the OWASP nor the names of its contributors may
     14 // be used to endorse or promote products derived from this software
     15 // without specific prior written permission.
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     20 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     26 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27 // POSSIBILITY OF SUCH DAMAGE.
     28 
     29 package org.owasp.html;
     30 
     31 import javax.annotation.Nullable;
     32 
     33 /**
     34  * Locale independent versions of String case-insensitive operations.
     35  * <p>
     36  * The normal case insensitive operators {@link String#toLowerCase}
     37  * and {@link String#equalsIgnoreCase} depend upon the current locale.
     38  * They will fold the letters "i" and "I" differently if the locale is
     39  * Turkish than if it is English.
     40  * <p>
     41  * These operations ignore all case folding for non-Roman letters, and are
     42  * independent of the current locale.
     43  * Lower-casing is exactly equivalent to {@code tr/A-Z/a-z/}, upper-casing to
     44  * {@code tr/a-z/A-Z/}, and case insensitive comparison is equivalent to
     45  * lower-casing both then comparing by code-unit.
     46  * <p>
     47  * Because of this simpler case folding, it is the case that for all Strings s
     48  * <code>
     49  * Strings.toUpperCase(s).equals(Strings.toUpperCase(Strings.toLowerCase(s)))
     50  * </code>.
     51  *
     52  * @author Mike Samuel <mikesamuel (at) gmail.com>
     53  */
     54 final class Strings {
     55   public static boolean equalsIgnoreCase(
     56       @Nullable String a, @Nullable String b) {
     57     if (a == null) { return b == null; }
     58     if (b == null) { return false; }
     59     int length = a.length();
     60     if (b.length() != length) { return false; }
     61     for (int i = length; --i >= 0;) {
     62       char c = a.charAt(i), d = b.charAt(i);
     63       if (c != d) {
     64         if (c <= 'z' && c >= 'A') {
     65           if (c <= 'Z') { c |= 0x20; }
     66           if (d <= 'Z' && d >= 'A') { d |= 0x20; }
     67           if (c == d) { continue; }
     68         }
     69         return false;
     70       }
     71     }
     72     return true;
     73   }
     74 
     75   public static boolean regionMatchesIgnoreCase(
     76       CharSequence a, int aoffset, CharSequence b, int boffset, int n) {
     77     if (aoffset + n > a.length() || boffset + n > b.length()) { return false; }
     78     for (int i = n; --i >= 0;) {
     79       char c = a.charAt(aoffset + i), d = b.charAt(boffset + i);
     80       if (c != d) {
     81         if (c <= 'z' && c >= 'A') {
     82           if (c <= 'Z') { c |= 0x20; }
     83           if (d <= 'Z' && d >= 'A') { d |= 0x20; }
     84           if (c == d) { continue; }
     85         }
     86         return false;
     87       }
     88     }
     89     return true;
     90   }
     91 
     92   /** True iff {@code s.equals(String.toLowerCase(s))}. */
     93   public static boolean isLowerCase(CharSequence s) {
     94     for (int i = s.length(); --i >= 0;) {
     95       char c = s.charAt(i);
     96       if (c <= 'Z' && c >= 'A') {
     97         return false;
     98       }
     99     }
    100     return true;
    101   }
    102 
    103   private static final char[] LCASE_CHARS = new char['Z' + 1];
    104   private static final char[] UCASE_CHARS = new char['z' + 1];
    105   static {
    106     for (int i = 0; i < 'A'; ++i) { LCASE_CHARS[i] = (char) i; }
    107     for (int i = 'A'; i <= 'Z'; ++i) { LCASE_CHARS[i] = (char) (i | 0x20); }
    108     for (int i = 0; i < 'a'; ++i) { UCASE_CHARS[i] = (char) i; }
    109     for (int i = 'a'; i <= 'z'; ++i) { UCASE_CHARS[i] = (char) (i & ~0x20); }
    110   }
    111   public static String toLowerCase(String s) {
    112     for (int i = s.length(); --i >= 0;) {
    113       char c = s.charAt(i);
    114       if (c <= 'Z' && c >= 'A') {
    115         char[] chars = s.toCharArray();
    116         chars[i] = LCASE_CHARS[c];
    117         while (--i >= 0) {
    118           c = chars[i];
    119           if (c <= 'Z') {
    120             chars[i] = LCASE_CHARS[c];
    121           }
    122         }
    123         return String.valueOf(chars);
    124       }
    125     }
    126     return s;
    127   }
    128 
    129   public static String toUpperCase(String s) {
    130     for (int i = s.length(); --i >= 0;) {
    131       char c = s.charAt(i);
    132       if (c <= 'z' && c >= 'a') {
    133         char[] chars = s.toCharArray();
    134         chars[i] = UCASE_CHARS[c];
    135         while (--i >= 0) {
    136           c = chars[i];
    137           if (c <= 'z') {
    138             chars[i] = UCASE_CHARS[c];
    139           }
    140         }
    141         return String.valueOf(chars);
    142       }
    143     }
    144     return s;
    145   }
    146 
    147   private Strings() { /* uninstantiable */ }
    148 }
    149