1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xml.internal.utils; 23 24 import java.text.CollationElementIterator; 25 import java.text.Collator; 26 import java.text.RuleBasedCollator; 27 import java.util.Locale; 28 29 30 /** 31 * International friendly string comparison with case-order 32 * @author Igor Hersht, igorh@ca.ibm.com 33 */ 34 public class StringComparable implements Comparable<StringComparable> { 35 36 public final static int UNKNOWN_CASE = -1; 37 public final static int UPPER_CASE = 1; 38 public final static int LOWER_CASE = 2; 39 40 private String m_text; 41 private Locale m_locale; 42 private RuleBasedCollator m_collator; 43 private String m_caseOrder; 44 private int m_mask = 0xFFFFFFFF; 45 46 public StringComparable(final String text, final Locale locale, 47 final Collator collator, final String caseOrder){ 48 m_text = text; 49 m_locale = locale; 50 m_collator = (RuleBasedCollator)collator; 51 m_caseOrder = caseOrder; 52 m_mask = getMask(m_collator.getStrength()); 53 } 54 55 @SuppressWarnings({"rawtypes", "unchecked"}) 56 public final static Comparable getComparator( final String text, final Locale locale, 57 final Collator collator, final String caseOrder){ 58 if((caseOrder == null) ||(caseOrder.length() == 0)){// no case-order specified 59 return ((RuleBasedCollator)collator).getCollationKey(text); 60 }else{ 61 return new StringComparable(text, locale, collator, caseOrder); 62 } 63 } 64 65 public final String toString(){return m_text;} 66 67 public int compareTo(StringComparable o) { 68 final String pattern = o.toString(); 69 if(m_text.equals(pattern)){//Code-point equals 70 return 0; 71 } 72 final int savedStrength = m_collator.getStrength(); 73 int comp = 0; 74 // Is there difference more significant than case-order? 75 if(((savedStrength == Collator.PRIMARY) || (savedStrength == Collator.SECONDARY))){ 76 comp = m_collator.compare(m_text, pattern ); 77 }else{// more than SECONDARY 78 m_collator.setStrength(Collator.SECONDARY); 79 comp = m_collator.compare(m_text, pattern ); 80 m_collator.setStrength(savedStrength); 81 } 82 if(comp != 0){//Difference more significant than case-order 83 return comp ; 84 } 85 86 // No difference more significant than case-order. 87 // Find case difference 88 comp = getCaseDiff(m_text, pattern); 89 if(comp != 0){ 90 return comp; 91 }else{// No case differences. Less significant difference could exist 92 return m_collator.compare(m_text, pattern ); 93 } 94 } 95 96 97 private final int getCaseDiff (final String text, final String pattern){ 98 final int savedStrength = m_collator.getStrength(); 99 final int savedDecomposition = m_collator.getDecomposition(); 100 m_collator.setStrength(Collator.TERTIARY);// not to ignore case 101 m_collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION );// corresponds NDF 102 103 final int diff[] =getFirstCaseDiff (text, pattern, m_locale); 104 m_collator.setStrength(savedStrength);// restore 105 m_collator.setDecomposition(savedDecomposition); //restore 106 if(diff != null){ 107 if((m_caseOrder).equals("upper-first")){ 108 if(diff[0] == UPPER_CASE){ 109 return -1; 110 }else{ 111 return 1; 112 } 113 }else{// lower-first 114 if(diff[0] == LOWER_CASE){ 115 return -1; 116 }else{ 117 return 1; 118 } 119 } 120 }else{// No case differences 121 return 0; 122 } 123 124 } 125 126 127 128 private final int[] getFirstCaseDiff(final String text, final String pattern, final Locale locale){ 129 130 final CollationElementIterator targIter = m_collator.getCollationElementIterator(text); 131 final CollationElementIterator patIter = m_collator.getCollationElementIterator(pattern); 132 int startTarg = -1; 133 int endTarg = -1; 134 int startPatt = -1; 135 int endPatt = -1; 136 final int done = getElement(CollationElementIterator.NULLORDER); 137 int patternElement = 0, targetElement = 0; 138 boolean getPattern = true, getTarget = true; 139 140 while (true) { 141 if (getPattern){ 142 startPatt = patIter.getOffset(); 143 patternElement = getElement(patIter.next()); 144 endPatt = patIter.getOffset(); 145 } 146 if ((getTarget)){ 147 startTarg = targIter.getOffset(); 148 targetElement = getElement(targIter.next()); 149 endTarg = targIter.getOffset(); 150 } 151 getTarget = getPattern = true; 152 if ((patternElement == done) ||( targetElement == done)) { 153 return null; 154 } else if (targetElement == 0) { 155 getPattern = false; 156 } else if (patternElement == 0) { 157 getTarget = false; 158 } else if (targetElement != patternElement) {// mismatch 159 if((startPatt < endPatt) && (startTarg < endTarg)){ 160 final String subText = text.substring(startTarg, endTarg); 161 final String subPatt = pattern.substring(startPatt, endPatt); 162 final String subTextUp = subText.toUpperCase(locale); 163 final String subPattUp = subPatt.toUpperCase(locale); 164 if(m_collator.compare(subTextUp, subPattUp) != 0){ // not case diffference 165 continue; 166 } 167 168 int diff[] = {UNKNOWN_CASE, UNKNOWN_CASE}; 169 if(m_collator.compare(subText, subTextUp) == 0){ 170 diff[0] = UPPER_CASE; 171 }else if(m_collator.compare(subText, subText.toLowerCase(locale)) == 0){ 172 diff[0] = LOWER_CASE; 173 } 174 if(m_collator.compare(subPatt, subPattUp) == 0){ 175 diff[1] = UPPER_CASE; 176 }else if(m_collator.compare(subPatt, subPatt.toLowerCase(locale)) == 0){ 177 diff[1] = LOWER_CASE; 178 } 179 180 if(((diff[0] == UPPER_CASE) && ( diff[1] == LOWER_CASE)) || 181 ((diff[0] == LOWER_CASE) && ( diff[1] == UPPER_CASE))){ 182 return diff; 183 }else{// not case diff 184 continue; 185 } 186 }else{ 187 continue; 188 } 189 190 } 191 } 192 193 } 194 195 196 // Return a mask for the part of the order we're interested in 197 private static final int getMask(final int strength) { 198 switch (strength) { 199 case Collator.PRIMARY: 200 return 0xFFFF0000; 201 case Collator.SECONDARY: 202 return 0xFFFFFF00; 203 default: 204 return 0xFFFFFFFF; 205 } 206 } 207 //get collation element with given strength 208 // from the element with max strength 209 private final int getElement(int maxStrengthElement){ 210 211 return (maxStrengthElement & m_mask); 212 } 213 214 }//StringComparable