1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
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
22package com.sun.org.apache.xml.internal.utils;
23
24import java.util.Vector;
25import java.text.Collator;
26import java.text.RuleBasedCollator;
27import java.text.CollationElementIterator;
28import java.util.Locale;
29import java.text.CollationKey;
30
31
32/**
33* International friendly string comparison with case-order
34 * @author Igor Hersht, igorh@ca.ibm.com
35*/
36public class StringComparable implements Comparable  {
37
38     public final static int UNKNOWN_CASE = -1;
39     public final static int UPPER_CASE = 1;
40     public final static int LOWER_CASE = 2;
41
42     private  String m_text;
43     private  Locale m_locale;
44     private RuleBasedCollator m_collator;
45     private String m_caseOrder;
46     private int m_mask = 0xFFFFFFFF;
47
48    public StringComparable(final String text, final Locale locale, final Collator collator, final String caseOrder){
49         m_text =  text;
50         m_locale = locale;
51         m_collator = (RuleBasedCollator)collator;
52         m_caseOrder = caseOrder;
53         m_mask = getMask(m_collator.getStrength());
54    }
55
56   public final static Comparable getComparator( final String text, final Locale locale, final Collator collator, final String caseOrder){
57       if((caseOrder == null) ||(caseOrder.length() == 0)){// no case-order specified
58            return  ((RuleBasedCollator)collator).getCollationKey(text);
59       }else{
60            return new StringComparable(text, locale, collator, caseOrder);
61       }
62   }
63
64   public final String toString(){return m_text;}
65
66   public int compareTo(Object o) {
67   final String pattern = ((StringComparable)o).toString();
68   if(m_text.equals(pattern)){//Code-point equals
69      return 0;
70   }
71   final int savedStrength = m_collator.getStrength();
72   int comp = 0;
73      // Is there difference more significant than case-order?
74     if(((savedStrength == Collator.PRIMARY) || (savedStrength == Collator.SECONDARY))){
75         comp = m_collator.compare(m_text, pattern );
76     }else{// more than SECONDARY
77         m_collator.setStrength(Collator.SECONDARY);
78         comp = m_collator.compare(m_text, pattern );
79         m_collator.setStrength(savedStrength);
80     }
81     if(comp != 0){//Difference more significant than case-order
82        return comp ;
83     }
84
85      // No difference more significant than case-order.
86      // Find case difference
87       comp = getCaseDiff(m_text, pattern);
88       if(comp != 0){
89           return comp;
90       }else{// No case differences. Less significant difference could exist
91            return m_collator.compare(m_text, pattern );
92       }
93  }
94
95
96  private final int getCaseDiff (final String text, final String pattern){
97     final int savedStrength = m_collator.getStrength();
98     final int savedDecomposition = m_collator.getDecomposition();
99     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