001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: StringComparable.java 468655 2006-10-28 07:12:06Z minchau $ 020 */ 021 022 package org.apache.xml.utils; 023 024 import java.util.Vector; 025 import java.text.Collator; 026 import java.text.RuleBasedCollator; 027 import java.text.CollationElementIterator; 028 import java.util.Locale; 029 import java.text.CollationKey; 030 031 032 /** 033 * International friendly string comparison with case-order 034 * @author Igor Hersht, igorh@ca.ibm.com 035 */ 036 public class StringComparable implements Comparable { 037 038 public final static int UNKNOWN_CASE = -1; 039 public final static int UPPER_CASE = 1; 040 public final static int LOWER_CASE = 2; 041 042 private String m_text; 043 private Locale m_locale; 044 private RuleBasedCollator m_collator; 045 private String m_caseOrder; 046 private int m_mask = 0xFFFFFFFF; 047 048 public StringComparable(final String text, final Locale locale, final Collator collator, final String caseOrder){ 049 m_text = text; 050 m_locale = locale; 051 m_collator = (RuleBasedCollator)collator; 052 m_caseOrder = caseOrder; 053 m_mask = getMask(m_collator.getStrength()); 054 } 055 056 public final static Comparable getComparator( final String text, final Locale locale, final Collator collator, final String caseOrder){ 057 if((caseOrder == null) ||(caseOrder.length() == 0)){// no case-order specified 058 return ((RuleBasedCollator)collator).getCollationKey(text); 059 }else{ 060 return new StringComparable(text, locale, collator, caseOrder); 061 } 062 } 063 064 public final String toString(){return m_text;} 065 066 public int compareTo(Object o) { 067 final String pattern = ((StringComparable)o).toString(); 068 if(m_text.equals(pattern)){//Code-point equals 069 return 0; 070 } 071 final int savedStrength = m_collator.getStrength(); 072 int comp = 0; 073 // Is there difference more significant than case-order? 074 if(((savedStrength == Collator.PRIMARY) || (savedStrength == Collator.SECONDARY))){ 075 comp = m_collator.compare(m_text, pattern ); 076 }else{// more than SECONDARY 077 m_collator.setStrength(Collator.SECONDARY); 078 comp = m_collator.compare(m_text, pattern ); 079 m_collator.setStrength(savedStrength); 080 } 081 if(comp != 0){//Difference more significant than case-order 082 return comp ; 083 } 084 085 // No difference more significant than case-order. 086 // Find case difference 087 comp = getCaseDiff(m_text, pattern); 088 if(comp != 0){ 089 return comp; 090 }else{// No case differences. Less significant difference could exist 091 return m_collator.compare(m_text, pattern ); 092 } 093 } 094 095 096 private final int getCaseDiff (final String text, final String pattern){ 097 final int savedStrength = m_collator.getStrength(); 098 final int savedDecomposition = m_collator.getDecomposition(); 099 m_collator.setStrength(Collator.TERTIARY);// not to ignore case 100 m_collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION );// corresponds NDF 101 102 final int diff[] =getFirstCaseDiff (text, pattern, m_locale); 103 m_collator.setStrength(savedStrength);// restore 104 m_collator.setDecomposition(savedDecomposition); //restore 105 if(diff != null){ 106 if((m_caseOrder).equals("upper-first")){ 107 if(diff[0] == UPPER_CASE){ 108 return -1; 109 }else{ 110 return 1; 111 } 112 }else{// lower-first 113 if(diff[0] == LOWER_CASE){ 114 return -1; 115 }else{ 116 return 1; 117 } 118 } 119 }else{// No case differences 120 return 0; 121 } 122 123 } 124 125 126 127 private final int[] getFirstCaseDiff(final String text, final String pattern, final Locale locale){ 128 129 final CollationElementIterator targIter = m_collator.getCollationElementIterator(text); 130 final CollationElementIterator patIter = m_collator.getCollationElementIterator(pattern); 131 int startTarg = -1; 132 int endTarg = -1; 133 int startPatt = -1; 134 int endPatt = -1; 135 final int done = getElement(CollationElementIterator.NULLORDER); 136 int patternElement = 0, targetElement = 0; 137 boolean getPattern = true, getTarget = true; 138 139 while (true) { 140 if (getPattern){ 141 startPatt = patIter.getOffset(); 142 patternElement = getElement(patIter.next()); 143 endPatt = patIter.getOffset(); 144 } 145 if ((getTarget)){ 146 startTarg = targIter.getOffset(); 147 targetElement = getElement(targIter.next()); 148 endTarg = targIter.getOffset(); 149 } 150 getTarget = getPattern = true; 151 if ((patternElement == done) ||( targetElement == done)) { 152 return null; 153 } else if (targetElement == 0) { 154 getPattern = false; 155 } else if (patternElement == 0) { 156 getTarget = false; 157 } else if (targetElement != patternElement) {// mismatch 158 if((startPatt < endPatt) && (startTarg < endTarg)){ 159 final String subText = text.substring(startTarg, endTarg); 160 final String subPatt = pattern.substring(startPatt, endPatt); 161 final String subTextUp = subText.toUpperCase(locale); 162 final String subPattUp = subPatt.toUpperCase(locale); 163 if(m_collator.compare(subTextUp, subPattUp) != 0){ // not case diffference 164 continue; 165 } 166 167 int diff[] = {UNKNOWN_CASE, UNKNOWN_CASE}; 168 if(m_collator.compare(subText, subTextUp) == 0){ 169 diff[0] = UPPER_CASE; 170 }else if(m_collator.compare(subText, subText.toLowerCase(locale)) == 0){ 171 diff[0] = LOWER_CASE; 172 } 173 if(m_collator.compare(subPatt, subPattUp) == 0){ 174 diff[1] = UPPER_CASE; 175 }else if(m_collator.compare(subPatt, subPatt.toLowerCase(locale)) == 0){ 176 diff[1] = LOWER_CASE; 177 } 178 179 if(((diff[0] == UPPER_CASE) && ( diff[1] == LOWER_CASE)) || 180 ((diff[0] == LOWER_CASE) && ( diff[1] == UPPER_CASE))){ 181 return diff; 182 }else{// not case diff 183 continue; 184 } 185 }else{ 186 continue; 187 } 188 189 } 190 } 191 192 } 193 194 195 // Return a mask for the part of the order we're interested in 196 private static final int getMask(final int strength) { 197 switch (strength) { 198 case Collator.PRIMARY: 199 return 0xFFFF0000; 200 case Collator.SECONDARY: 201 return 0xFFFFFF00; 202 default: 203 return 0xFFFFFFFF; 204 } 205 } 206 //get collation element with given strength 207 // from the element with max strength 208 private final int getElement(int maxStrengthElement){ 209 210 return (maxStrengthElement & m_mask); 211 } 212 213 }//StringComparable 214 215