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