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