• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  **********************************************************************
3  * Copyright (c) 2002-2004, International Business Machines
4  * Corporation and others.  All Rights Reserved.
5  **********************************************************************
6  * Author: Mark Davis
7  **********************************************************************
8  */
9 package org.unicode.cldr.util;
10 
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.Comparator;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.TreeMap;
19 
20 import com.ibm.icu.text.Collator;
21 import com.ibm.icu.text.RuleBasedCollator;
22 import com.ibm.icu.text.UnicodeSet;
23 import com.ibm.icu.util.Freezable;
24 import com.ibm.icu.util.ULocale;
25 
26 public class MapComparator<K> implements Comparator<K>, Freezable<MapComparator<K>> {
27     private RuleBasedCollator uca = (RuleBasedCollator) Collator.getInstance(ULocale.ROOT);
28     {
29         uca.setNumericCollation(true);
30     }
31     private Map<K, Integer> ordering = new TreeMap<K, Integer>(); // maps from name to rank
32     private List<K> rankToName = new ArrayList<K>();
33     private boolean errorOnMissing = true;
34     private volatile boolean locked = false;
35     private int before = 1;
36     private boolean fallback = true;
37 
38     /**
39      * @return Returns the errorOnMissing.
40      */
isErrorOnMissing()41     public boolean isErrorOnMissing() {
42         return errorOnMissing;
43     }
44 
45     /**
46      * @param errorOnMissing
47      *            The errorOnMissing to set.
48      */
setErrorOnMissing(boolean errorOnMissing)49     public MapComparator<K> setErrorOnMissing(boolean errorOnMissing) {
50         if (locked) throw new UnsupportedOperationException("Attempt to modify locked object");
51         this.errorOnMissing = errorOnMissing;
52         return this;
53     }
54 
isSortBeforeOthers()55     public boolean isSortBeforeOthers() {
56         return before == 1;
57     }
58 
setSortBeforeOthers(boolean sortBeforeOthers)59     public MapComparator<K> setSortBeforeOthers(boolean sortBeforeOthers) {
60         if (locked) throw new UnsupportedOperationException("Attempt to modify locked object");
61         this.before = sortBeforeOthers ? 1 : -1;
62         return this;
63     }
64 
isDoFallback()65     public boolean isDoFallback() {
66         return fallback;
67     }
68 
setDoFallback(boolean doNumeric)69     public MapComparator<K> setDoFallback(boolean doNumeric) {
70         if (locked) throw new UnsupportedOperationException("Attempt to modify locked object");
71         this.fallback = doNumeric;
72         return this;
73     }
74 
75     /**
76      * @return Returns the rankToName.
77      */
getOrder()78     public List<K> getOrder() {
79         return Collections.unmodifiableList(rankToName);
80     }
81 
MapComparator()82     public MapComparator() {
83     }
84 
MapComparator(K[] data)85     public MapComparator(K[] data) {
86         add(data);
87     }
88 
MapComparator(Collection<K> c)89     public MapComparator(Collection<K> c) {
90         add(c);
91     }
92 
add(K newObject)93     public MapComparator<K> add(K newObject) {
94         Integer already = ordering.get(newObject);
95         if (already == null) {
96             if (locked) throw new UnsupportedOperationException("Attempt to modify locked object");
97             ordering.put(newObject, new Integer(rankToName.size()));
98             rankToName.add(newObject);
99         }
100         return this;
101     }
102 
getNumericOrder(K object)103     public Integer getNumericOrder(K object) {
104         return ordering.get(object);
105     }
106 
add(Collection<K> c)107     public MapComparator<K> add(Collection<K> c) {
108         for (Iterator<K> it = c.iterator(); it.hasNext();) {
109             add(it.next());
110         }
111         return this;
112     }
113 
114     @SuppressWarnings("unchecked")
add(K... data)115     public MapComparator<K> add(K... data) {
116         for (int i = 0; i < data.length; ++i) {
117             add(data[i]);
118         }
119         return this;
120     }
121 
122     private static final UnicodeSet numbers = new UnicodeSet("[\\-0-9.]").freeze();
123 
124     @SuppressWarnings({ "unchecked", "rawtypes" })
compare(K a, K b)125     public int compare(K a, K b) {
126         if (false && (a.equals("lines") || b.equals("lines"))) {
127             System.out.println();
128         }
129         Integer aa = ordering.get(a);
130         Integer bb = ordering.get(b);
131         if (aa != null && bb != null) {
132             return aa.compareTo(bb);
133         }
134         if (errorOnMissing) {
135             throw new IllegalArgumentException("Missing Map Comparator value(s): "
136                 + a.toString() + "(" + aa + "),\t"
137                 + b.toString() + "(" + bb + "),\t");
138         }
139         // must handle halfway case, otherwise we are not transitive!!!
140         if (aa == null && bb != null) {
141             return before;
142         }
143         if (aa != null && bb == null) {
144             return -before;
145         }
146         if (!fallback) {
147             return 0;
148         }
149         // do numeric
150         // first we do a quick check, then parse.
151         // for transitivity, we have to check both.
152         boolean anumeric = numbers.containsAll((String) a);
153         double an = Double.NaN, bn = Double.NaN;
154         if (anumeric) {
155             try {
156                 an = Double.parseDouble((String) a);
157             } catch (NumberFormatException e) {
158                 anumeric = false;
159             }
160         }
161         boolean bnumeric = numbers.containsAll((String) b);
162         if (bnumeric) {
163             try {
164                 bn = Double.parseDouble((String) b);
165             } catch (NumberFormatException e) {
166                 bnumeric = false;
167             }
168         }
169         if (anumeric && bnumeric) {
170             if (an < bn) return -1;
171             if (an > bn) return 1;
172             return 0;
173         }
174         // must handle halfway case, otherwise we are not transitive!!!
175         if (!anumeric && bnumeric) return 1;
176         if (anumeric && !bnumeric) return -1;
177 
178         if (a instanceof CharSequence) {
179             if (b instanceof CharSequence) {
180                 int result = uca.compare(a.toString(), b.toString());
181                 if (result != 0) {
182                     return result;
183                 }
184             } else {
185                 return 1; // handle for transitivity
186             }
187         } else {
188             return -1; // handle for transitivity
189         }
190 
191         // do fallback
192         return ((Comparable) a).compareTo(b);
193     }
194 
toString()195     public String toString() {
196         StringBuffer buffer = new StringBuffer();
197         boolean isFirst = true;
198         for (Iterator<K> it = rankToName.iterator(); it.hasNext();) {
199             K key = it.next();
200             if (isFirst)
201                 isFirst = false;
202             else
203                 buffer.append(" ");
204             buffer.append("<").append(key).append(">");
205         }
206         return buffer.toString();
207     }
208 
209     /*
210      * (non-Javadoc)
211      *
212      * @see com.ibm.icu.dev.test.util.Freezeble
213      */
isFrozen()214     public boolean isFrozen() {
215         return locked;
216     }
217 
218     /*
219      * (non-Javadoc)
220      *
221      * @see com.ibm.icu.dev.test.util.Freezeble
222      */
freeze()223     public MapComparator<K> freeze() {
224         locked = true;
225         return this;
226     }
227 
228     /*
229      * (non-Javadoc)
230      *
231      * @see com.ibm.icu.dev.test.util.Freezeble
232      */
233     @SuppressWarnings("unchecked")
cloneAsThawed()234     public MapComparator<K> cloneAsThawed() {
235         try {
236             MapComparator<K> result = (MapComparator<K>) super.clone();
237             result.locked = false;
238             result.ordering = (Map<K, Integer>) ((TreeMap<K, Integer>) ordering).clone();
239             result.rankToName = (List<K>) ((ArrayList<K>) rankToName).clone();
240             return result;
241         } catch (CloneNotSupportedException e) {
242             throw new InternalError("should never happen");
243         }
244     }
245 }