41 |
import java.util.Vector; |
import java.util.Vector; |
42 |
|
|
43 |
/** |
/** |
44 |
* This class is a concrete subclass of <code>Collator</code> suitable |
* This class is a concrete subclass of <code>Collator</code> suitable |
45 |
* for string collation in a wide variety of languages. An instance of |
* for string collation in a wide variety of languages. An instance of |
46 |
* this class is normally returned by the <code>getInstance</code> method |
* this class is normally returned by the <code>getInstance</code> method |
47 |
* of <code>Collator</code> with rules predefined for the requested |
* of <code>Collator</code> with rules predefined for the requested |
48 |
* locale. However, an instance of this class can be created manually |
* locale. However, an instance of this class can be created manually |
49 |
* with any desired rules. |
* with any desired rules. |
50 |
* <p> |
* <p> |
51 |
* Rules take the form of a <code>String</code> with the following syntax |
* Rules take the form of a <code>String</code> with the following syntax |
52 |
* <ul> |
* <ul> |
53 |
* <li> Modifier: '@' |
* <li> Modifier: '@' |
54 |
* <li> Relation: '<' | ';' | ',' | '=' : <text> |
* <li> Relation: '<' | ';' | ',' | '=' : <text> |
55 |
* <li> Reset: '&' : <text> |
* <li> Reset: '&' : <text> |
56 |
* </ul> |
* </ul> |
57 |
* The modifier character indicates that accents sort backward as is the |
* The modifier character indicates that accents sort backward as is the |
58 |
* case with French. The relational operators specify how the text |
* case with French. The relational operators specify how the text |
59 |
* argument relates to the previous term. The relation characters have |
* argument relates to the previous term. The relation characters have |
60 |
* the following meanings: |
* the following meanings: |
61 |
* <ul> |
* <ul> |
62 |
* <li>'<' - The text argument is greater than the prior term at the primary |
* <li>'<' - The text argument is greater than the prior term at the primary |
63 |
* difference level. |
* difference level. |
64 |
* <li>';' - The text argument is greater than the prior term at the secondary |
* <li>';' - The text argument is greater than the prior term at the secondary |
65 |
* difference level. |
* difference level. |
66 |
* <li>',' - The text argument is greater than the prior term at the tertiary |
* <li>',' - The text argument is greater than the prior term at the tertiary |
67 |
* difference level. |
* difference level. |
68 |
* <li>'=' - The text argument is equal to the prior term |
* <li>'=' - The text argument is equal to the prior term |
69 |
* </ul> |
* </ul> |
70 |
* <p> |
* <p> |
71 |
* As for the text argument itself, this is any sequence of Unicode |
* As for the text argument itself, this is any sequence of Unicode |
72 |
* characters not in the following ranges: 0x0009-0x000D, 0x0020-0x002F, |
* characters not in the following ranges: 0x0009-0x000D, 0x0020-0x002F, |
73 |
* 0x003A-0x0040, 0x005B-0x0060, and 0x007B-0x007E. If these characters are |
* 0x003A-0x0040, 0x005B-0x0060, and 0x007B-0x007E. If these characters are |
74 |
* desired, they must be enclosed in single quotes. If any whitespace is |
* desired, they must be enclosed in single quotes. If any whitespace is |
75 |
* encountered, it is ignored. (For example, "a b" is equal to "ab"). |
* encountered, it is ignored. (For example, "a b" is equal to "ab"). |
76 |
* <p> |
* <p> |
77 |
* The reset operation inserts the following rule at the point where the |
* The reset operation inserts the following rule at the point where the |
78 |
* text argument to it exists in the previously declared rule string. This |
* text argument to it exists in the previously declared rule string. This |
79 |
* makes it easy to add new rules to an existing string by simply including |
* makes it easy to add new rules to an existing string by simply including |
80 |
* them in a reset sequence at the end. Note that the text argument, or |
* them in a reset sequence at the end. Note that the text argument, or |
81 |
* at least the first character of it, must be present somewhere in the |
* at least the first character of it, must be present somewhere in the |
82 |
* previously declared rules in order to be inserted properly. If this |
* previously declared rules in order to be inserted properly. If this |
83 |
* is not satisfied, a <code>ParseException</code> will be thrown. |
* is not satisfied, a <code>ParseException</code> will be thrown. |
84 |
* <p> |
* <p> |
85 |
* This system of configuring <code>RuleBasedCollator</code> is needlessly |
* This system of configuring <code>RuleBasedCollator</code> is needlessly |
86 |
* complex and the people at Taligent who developed it (along with the folks |
* complex and the people at Taligent who developed it (along with the folks |
87 |
* at Sun who accepted it into the Java standard library) deserve a slow |
* at Sun who accepted it into the Java standard library) deserve a slow |
88 |
* and agonizing death. |
* and agonizing death. |
89 |
* <p> |
* <p> |
90 |
* Here are a couple of example of rule strings: |
* Here are a couple of example of rule strings: |
91 |
* <p> |
* <p> |
92 |
* "< a < b < c" - This string says that a is greater than b which is |
* "< a < b < c" - This string says that a is greater than b which is |
93 |
* greater than c, with all differences being primary differences. |
* greater than c, with all differences being primary differences. |
94 |
* <p> |
* <p> |
95 |
* "< a,A < b,B < c,C" - This string says that 'A' is greater than 'a' with |
* "< a,A < b,B < c,C" - This string says that 'A' is greater than 'a' with |
96 |
* a tertiary strength comparison. Both 'b' and 'B' are greater than 'a' and |
* a tertiary strength comparison. Both 'b' and 'B' are greater than 'a' and |
97 |
* 'A' during a primary strength comparison. But 'B' is greater than 'b' |
* 'A' during a primary strength comparison. But 'B' is greater than 'b' |
98 |
* under a tertiary strength comparison. |
* under a tertiary strength comparison. |
99 |
* <p> |
* <p> |
100 |
* "< a < c & a < b " - This sequence is identical in function to the |
* "< a < c & a < b " - This sequence is identical in function to the |
101 |
* "< a < b < c" rule string above. The '&' reset symbol indicates that |
* "< a < b < c" rule string above. The '&' reset symbol indicates that |
102 |
* the rule "< b" is to be inserted after the text argument "a" in the |
* the rule "< b" is to be inserted after the text argument "a" in the |
103 |
* previous rule string segment. |
* previous rule string segment. |
104 |
* <p> |
* <p> |
105 |
* "< a < b & y < z" - This is an error. The character 'y' does not appear |
* "< a < b & y < z" - This is an error. The character 'y' does not appear |
106 |
* anywhere in the previous rule string segment so the rule following the |
* anywhere in the previous rule string segment so the rule following the |
107 |
* reset rule cannot be inserted. |
* reset rule cannot be inserted. |
108 |
* <p> |
* <p> |
109 |
* For a description of the various comparison strength types, see the |
* For a description of the various comparison strength types, see the |
110 |
* documentation for the <code>Collator</code> class. |
* documentation for the <code>Collator</code> class. |
111 |
* <p> |
* <p> |
112 |
* As an additional complication to this already overly complex rule scheme, |
* As an additional complication to this already overly complex rule scheme, |
113 |
* if any characters precede the first rule, these characters are considered |
* if any characters precede the first rule, these characters are considered |
114 |
* ignorable. They will be treated as if they did not exist during |
* ignorable. They will be treated as if they did not exist during |
115 |
* comparisons. For example, "- < a < b ..." would make '-' an ignorable |
* comparisons. For example, "- < a < b ..." would make '-' an ignorable |
116 |
* character such that the strings "high-tech" and "hightech" would |
* character such that the strings "high-tech" and "hightech" would |
117 |
* be considered identical. |
* be considered identical. |
118 |
* <p> |
* <p> |
119 |
* A <code>ParseException</code> will be thrown for any of the following |
* A <code>ParseException</code> will be thrown for any of the following |
120 |
* conditions: |
* conditions: |
121 |
* <ul> |
* <ul> |
122 |
* <li>Unquoted punctuation characters in a text argument. |
* <li>Unquoted punctuation characters in a text argument. |
123 |
* <li>A relational or reset operator not followed by a text argument |
* <li>A relational or reset operator not followed by a text argument |
124 |
* <li>A reset operator where the text argument is not present in |
* <li>A reset operator where the text argument is not present in |
125 |
* the previous rule string section. |
* the previous rule string section. |
126 |
* </ul> |
* </ul> |
127 |
* |
* |
128 |
* @author Aaron M. Renn (arenn@urbanophile.com) |
* @author Aaron M. Renn (arenn@urbanophile.com) |
|
*/ |
|
|
public class RuleBasedCollator extends Collator |
|
|
{ |
|
|
|
|
|
/* |
|
|
* Inner Classes |
|
|
*/ |
|
|
|
|
|
class CollationElement |
|
|
{ |
|
|
String char_seq; |
|
|
int primary; |
|
|
short secondary; |
|
|
short tertiary; |
|
|
|
|
|
CollationElement(String char_seq, int primary, short secondary, short tertiary) |
|
|
{ |
|
|
this.char_seq = char_seq; |
|
|
this.primary = primary; |
|
|
this.secondary = secondary; |
|
|
this.tertiary = tertiary; |
|
|
} |
|
|
|
|
|
} // inner class CollationElement |
|
|
|
|
|
/*************************************************************************/ |
|
|
|
|
|
/* |
|
|
* Instance Variables |
|
|
*/ |
|
|
|
|
|
/** |
|
|
* This the the original rule string. |
|
|
*/ |
|
|
private String rules; |
|
|
|
|
|
/** |
|
|
* This is the table of collation element values |
|
|
*/ |
|
|
private Object[] ce_table; |
|
|
|
|
|
/*************************************************************************/ |
|
|
|
|
|
/* |
|
|
* Constructors |
|
129 |
*/ |
*/ |
130 |
|
public class RuleBasedCollator extends Collator |
|
/** |
|
|
* This method initializes a new instance of <code>RuleBasedCollator</code> |
|
|
* with the specified collation rules. Note that an application normally |
|
|
* obtains an instance of <code>RuleBasedCollator</code> by calling the |
|
|
* <code>getInstance</code> method of <code>Collator</code>. That method |
|
|
* automatically loads the proper set of rules for the desired locale. |
|
|
* |
|
|
* @param rules The collation rule string. |
|
|
* |
|
|
* @exception ParseException If the rule string contains syntax errors. |
|
|
*/ |
|
|
public |
|
|
RuleBasedCollator(String rules) throws ParseException |
|
131 |
{ |
{ |
132 |
this.rules = rules; |
class CollationElement |
133 |
|
{ |
134 |
if (rules.equals("")) |
String char_seq; |
135 |
throw new IllegalArgumentException("Empty rule set"); |
int primary; |
136 |
|
short secondary; |
137 |
|
short tertiary; |
138 |
|
|
139 |
Vector v = new Vector(); |
CollationElement(String char_seq, int primary, short secondary, short tertiary) |
|
boolean ignore_chars = true; |
|
|
int primary_seq = 0; |
|
|
short secondary_seq = 0; |
|
|
short tertiary_seq = 0; |
|
|
StringBuffer sb = new StringBuffer(""); |
|
|
for (int i = 0; i < rules.length(); i++) |
|
140 |
{ |
{ |
141 |
char c = rules.charAt(i); |
this.char_seq = char_seq; |
142 |
|
this.primary = primary; |
143 |
// Check if it is a whitespace character |
this.secondary = secondary; |
144 |
if (((c >= 0x09) && (c <= 0x0D)) || (c == 0x20)) |
this.tertiary = tertiary; |
145 |
continue; |
} |
146 |
|
|
147 |
// Primary difference |
} // inner class CollationElement |
|
if (c == '<') |
|
|
{ |
|
|
ignore_chars = false; |
|
|
secondary_seq = 0; |
|
|
tertiary_seq = 0; |
|
|
++primary_seq; |
|
|
|
|
|
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
|
|
secondary_seq, |
|
|
tertiary_seq); |
|
|
v.add(e); |
|
|
sb.setLength(0); |
|
|
continue; |
|
|
} |
|
|
|
|
|
// Secondary difference |
|
|
if (c == ';') |
|
|
{ |
|
|
if (primary_seq == 0) |
|
|
throw new ParseException(rules, i); |
|
|
|
|
|
++secondary_seq; |
|
|
tertiary_seq = 0; |
|
|
|
|
|
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
|
|
secondary_seq, |
|
|
tertiary_seq); |
|
|
v.add(e); |
|
|
sb.setLength(0); |
|
|
continue; |
|
|
} |
|
|
|
|
|
// Tertiary difference |
|
|
if (c == ',') |
|
|
{ |
|
|
if (primary_seq == 0) |
|
|
throw new ParseException(rules, i); |
|
148 |
|
|
149 |
++tertiary_seq; |
/** |
150 |
|
* This the the original rule string. |
151 |
|
*/ |
152 |
|
private String rules; |
153 |
|
|
154 |
|
/** |
155 |
|
* This is the table of collation element values |
156 |
|
*/ |
157 |
|
private Object[] ce_table; |
158 |
|
|
159 |
|
/** |
160 |
|
* This method initializes a new instance of <code>RuleBasedCollator</code> |
161 |
|
* with the specified collation rules. Note that an application normally |
162 |
|
* obtains an instance of <code>RuleBasedCollator</code> by calling the |
163 |
|
* <code>getInstance</code> method of <code>Collator</code>. That method |
164 |
|
* automatically loads the proper set of rules for the desired locale. |
165 |
|
* |
166 |
|
* @param rules The collation rule string. |
167 |
|
* |
168 |
|
* @exception ParseException If the rule string contains syntax errors. |
169 |
|
*/ |
170 |
|
public RuleBasedCollator(String rules) throws ParseException |
171 |
|
{ |
172 |
|
this.rules = rules; |
173 |
|
|
174 |
|
if (rules.equals("")) |
175 |
|
throw new IllegalArgumentException("Empty rule set"); |
176 |
|
|
177 |
|
Vector v = new Vector(); |
178 |
|
boolean ignore_chars = true; |
179 |
|
int primary_seq = 0; |
180 |
|
short secondary_seq = 0; |
181 |
|
short tertiary_seq = 0; |
182 |
|
StringBuffer sb = new StringBuffer(""); |
183 |
|
for (int i = 0; i < rules.length(); i++) |
184 |
|
{ |
185 |
|
char c = rules.charAt(i); |
186 |
|
|
187 |
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
// Check if it is a whitespace character |
188 |
secondary_seq, |
if (((c >= 0x09) && (c <= 0x0D)) || (c == 0x20)) |
|
tertiary_seq); |
|
|
v.add(e); |
|
|
sb.setLength(0); |
|
189 |
continue; |
continue; |
|
} |
|
190 |
|
|
191 |
// Is equal to |
// Primary difference |
192 |
if (c == '=') |
if (c == '<') |
193 |
{ |
{ |
194 |
if (primary_seq == 0) |
ignore_chars = false; |
195 |
throw new ParseException(rules, i); |
secondary_seq = 0; |
196 |
|
tertiary_seq = 0; |
197 |
|
++primary_seq; |
198 |
|
|
199 |
|
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
200 |
|
secondary_seq, |
201 |
|
tertiary_seq); |
202 |
|
v.add(e); |
203 |
|
sb.setLength(0); |
204 |
|
continue; |
205 |
|
} |
206 |
|
|
207 |
|
// Secondary difference |
208 |
|
if (c == ';') |
209 |
|
{ |
210 |
|
if (primary_seq == 0) |
211 |
|
throw new ParseException(rules, i); |
212 |
|
|
213 |
|
++secondary_seq; |
214 |
|
tertiary_seq = 0; |
215 |
|
|
216 |
|
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
217 |
|
secondary_seq, |
218 |
|
tertiary_seq); |
219 |
|
v.add(e); |
220 |
|
sb.setLength(0); |
221 |
|
continue; |
222 |
|
} |
223 |
|
|
224 |
|
// Tertiary difference |
225 |
|
if (c == ',') |
226 |
|
{ |
227 |
|
if (primary_seq == 0) |
228 |
|
throw new ParseException(rules, i); |
229 |
|
|
230 |
|
++tertiary_seq; |
231 |
|
|
232 |
|
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
233 |
|
secondary_seq, |
234 |
|
tertiary_seq); |
235 |
|
v.add(e); |
236 |
|
sb.setLength(0); |
237 |
|
continue; |
238 |
|
} |
239 |
|
|
240 |
|
// Is equal to |
241 |
|
if (c == '=') |
242 |
|
{ |
243 |
|
if (primary_seq == 0) |
244 |
|
throw new ParseException(rules, i); |
245 |
|
|
246 |
|
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
247 |
|
secondary_seq, |
248 |
|
tertiary_seq); |
249 |
|
v.add(e); |
250 |
|
sb.setLength(0); |
251 |
|
continue; |
252 |
|
} |
253 |
|
|
254 |
|
// Sort accents backwards |
255 |
|
if (c == '@') |
256 |
|
{ |
257 |
|
throw new ParseException("French style accents not implemented yet", 0); |
258 |
|
} |
259 |
|
|
260 |
|
// Reset command |
261 |
|
if (c == '&') |
262 |
|
{ |
263 |
|
throw new ParseException("Reset not implemented yet", 0); |
264 |
|
} |
265 |
|
|
266 |
|
// See if we are still reading characters to skip |
267 |
|
if (ignore_chars == true) |
268 |
|
{ |
269 |
|
CollationElement e = new CollationElement(c + "", 0, (short)0, |
270 |
|
(short)0); |
271 |
|
v.add(e); |
272 |
|
continue; |
273 |
|
} |
274 |
|
|
275 |
CollationElement e = new CollationElement(sb.toString(), primary_seq, |
sb.append(c); |
276 |
secondary_seq, |
} |
|
tertiary_seq); |
|
|
v.add(e); |
|
|
sb.setLength(0); |
|
|
continue; |
|
|
} |
|
|
|
|
|
// Sort accents backwards |
|
|
if (c == '@') |
|
|
{ |
|
|
throw new ParseException("French style accents not implemented yet", 0); |
|
|
} |
|
277 |
|
|
278 |
// Reset command |
ce_table = v.toArray(); |
279 |
if (c == '&') |
} |
|
{ |
|
|
throw new ParseException("Reset not implemented yet", 0); |
|
|
} |
|
280 |
|
|
281 |
// See if we are still reading characters to skip |
/** |
282 |
if (ignore_chars == true) |
* This method returns a <code>String</code> containing the collation rules |
283 |
|
* for this object. |
284 |
|
* |
285 |
|
* @return The collation rules for this object. |
286 |
|
*/ |
287 |
|
public String getRules() |
288 |
|
{ |
289 |
|
return(rules); |
290 |
|
} |
291 |
|
|
292 |
|
/** |
293 |
|
* This method calculates the collation element value for the specified |
294 |
|
* character(s). |
295 |
|
*/ |
296 |
|
int getCollationElementValue(String str) |
297 |
|
{ |
298 |
|
CollationElement e = null; |
299 |
|
|
300 |
|
// The table is sorted. Change to a binary search later. |
301 |
|
for (int i = 0; i < ce_table.length; i++) |
302 |
|
if (((CollationElement)ce_table[i]).char_seq.equals(str)) |
303 |
{ |
{ |
304 |
CollationElement e = new CollationElement(c + "", 0, (short)0, |
e = (CollationElement)ce_table[i]; |
305 |
(short)0); |
break; |
|
v.add(e); |
|
|
continue; |
|
306 |
} |
} |
307 |
|
|
308 |
sb.append(c); |
if (e == null) |
309 |
} |
e = new CollationElement(str, 0xFFFF, (short)0xFF, (short)0xFF); |
|
|
|
|
ce_table = v.toArray(); |
|
|
} |
|
|
|
|
|
/*************************************************************************/ |
|
|
|
|
|
/* |
|
|
* Instance Methods |
|
|
*/ |
|
310 |
|
|
311 |
/** |
int retval = (e.primary << 16) + (e.secondary << 8) + e.tertiary; |
|
* This method returns a <code>String</code> containing the collation rules |
|
|
* for this object. |
|
|
* |
|
|
* @return The collation rules for this object. |
|
|
*/ |
|
|
public String |
|
|
getRules() |
|
|
{ |
|
|
return(rules); |
|
|
} |
|
312 |
|
|
313 |
/*************************************************************************/ |
return(retval); |
314 |
|
} |
315 |
|
|
316 |
/** |
/** |
317 |
* This method calculates the collation element value for the specified |
* This method returns an instance for <code>CollationElementIterator</code> |
318 |
* character(s). |
* for the specified <code>String</code> under the collation rules for this |
319 |
*/ |
* object. |
320 |
int |
* |
321 |
getCollationElementValue(String str) |
* @param str The <code>String</code> to return the <code>CollationElementIterator</code> instance for. |
322 |
{ |
* |
323 |
CollationElement e = null; |
* @return A <code>CollationElementIterator</code> for the specified <code>String</code>. |
324 |
|
*/ |
325 |
// The table is sorted. Change to a binary search later. |
public CollationElementIterator getCollationElementIterator(String str) |
326 |
for (int i = 0; i < ce_table.length; i++) |
{ |
327 |
if (((CollationElement)ce_table[i]).char_seq.equals(str)) |
return(new CollationElementIterator(this, str)); |
328 |
|
} |
329 |
|
|
330 |
|
/** |
331 |
|
* This method returns an instance of <code>CollationElementIterator</code> |
332 |
|
* for the <code>String</code> represented by the specified |
333 |
|
* <code>CharacterIterator</code>. |
334 |
|
* |
335 |
|
* @param ci The <code>CharacterIterator</code> with the desired <code>String</code>. |
336 |
|
* |
337 |
|
* @return A <code>CollationElementIterator</code> for the specified <code>String</code>. |
338 |
|
*/ |
339 |
|
public CollationElementIterator getCollationElementIterator(CharacterIterator ci) |
340 |
|
{ |
341 |
|
StringBuffer sb = new StringBuffer(""); |
342 |
|
|
343 |
|
// Right now we assume that we will read from the beginning of the string. |
344 |
|
char c = ci.first(); |
345 |
|
while (c != CharacterIterator.DONE) |
346 |
{ |
{ |
347 |
e = (CollationElement)ce_table[i]; |
sb.append(c); |
348 |
break; |
c = ci.next(); |
349 |
} |
} |
350 |
|
|
351 |
if (e == null) |
return(getCollationElementIterator(sb.toString())); |
352 |
e = new CollationElement(str, 0xFFFF, (short)0xFF, (short)0xFF); |
} |
|
|
|
|
int retval = (e.primary << 16) + (e.secondary << 8) + e.tertiary; |
|
353 |
|
|
354 |
return(retval); |
/** |
355 |
} |
* This method returns an integer which indicates whether the first |
356 |
|
* specified <code>String</code> is less than, greater than, or equal to |
357 |
/*************************************************************************/ |
* the second. The value depends not only on the collation rules in |
358 |
|
* effect, but also the strength and decomposition settings of this object. |
359 |
|
* |
360 |
|
* @param s1 The first <code>String</code> to compare. |
361 |
|
* @param s2 A second <code>String</code> to compare to the first. |
362 |
|
* |
363 |
|
* @return A negative integer if s1 < s2, a positive integer |
364 |
|
* if s1 > s2, or 0 if s1 == s2. |
365 |
|
*/ |
366 |
|
public int compare(String s1, String s2) |
367 |
|
{ |
368 |
|
CollationElementIterator cei1 = getCollationElementIterator(s1); |
369 |
|
CollationElementIterator cei2 = getCollationElementIterator(s2); |
370 |
|
|
371 |
/** |
for(;;) |
372 |
* This method returns an instance for <code>CollationElementIterator</code> |
{ |
373 |
* for the specified <code>String</code> under the collation rules for this |
int ord1 = cei1.next(); |
374 |
* object. |
int ord2 = cei2.next(); |
375 |
* |
|
376 |
* @param str The <code>String</code> to return the <code>CollationElementIterator</code> instance for. |
// Check for end of string |
377 |
* |
if (ord1 == CollationElementIterator.NULLORDER) |
378 |
* @return A <code>CollationElementIterator</code> for the specified <code>String</code>. |
if (ord2 == CollationElementIterator.NULLORDER) |
379 |
*/ |
return(0); |
380 |
public CollationElementIterator |
else |
381 |
getCollationElementIterator(String str) |
return(-1); |
382 |
{ |
else if (ord2 == CollationElementIterator.NULLORDER) |
383 |
return(new CollationElementIterator(this, str)); |
return(1); |
|
} |
|
|
|
|
|
/*************************************************************************/ |
|
|
|
|
|
/** |
|
|
* This method returns an instance of <code>CollationElementIterator</code> |
|
|
* for the <code>String</code> represented by the specified |
|
|
* <code>CharacterIterator</code>. |
|
|
* |
|
|
* @param ci The <code>CharacterIterator</code> with the desired <code>String</code>. |
|
|
* |
|
|
* @return A <code>CollationElementIterator</code> for the specified <code>String</code>. |
|
|
*/ |
|
|
public CollationElementIterator |
|
|
getCollationElementIterator(CharacterIterator ci) |
|
|
{ |
|
|
StringBuffer sb = new StringBuffer(""); |
|
|
|
|
|
// Right now we assume that we will read from the beginning of the string. |
|
|
char c = ci.first(); |
|
|
while (c != CharacterIterator.DONE) |
|
|
{ |
|
|
sb.append(c); |
|
|
c = ci.next(); |
|
|
} |
|
|
|
|
|
return(getCollationElementIterator(sb.toString())); |
|
|
} |
|
|
|
|
|
/*************************************************************************/ |
|
384 |
|
|
385 |
/** |
// We know chars are totally equal, so skip |
386 |
* This method returns an integer which indicates whether the first |
if (ord1 == ord2) |
387 |
* specified <code>String</code> is less than, greater than, or equal to |
continue; |
|
* the second. The value depends not only on the collation rules in |
|
|
* effect, but also the strength and decomposition settings of this object. |
|
|
* |
|
|
* @param s1 The first <code>String</code> to compare. |
|
|
* @param s2 A second <code>String</code> to compare to the first. |
|
|
* |
|
|
* @return A negative integer if s1 < s2, a positive integer |
|
|
* if s1 > s2, or 0 if s1 == s2. |
|
|
*/ |
|
|
public int |
|
|
compare(String s1, String s2) |
|
|
{ |
|
|
CollationElementIterator cei1 = getCollationElementIterator(s1); |
|
|
CollationElementIterator cei2 = getCollationElementIterator(s2); |
|
388 |
|
|
389 |
for(;;) |
// Check for primary strength differences |
390 |
{ |
int prim1 = cei1.primaryOrder(ord1); |
391 |
int ord1 = cei1.next(); |
int prim2 = cei2.primaryOrder(ord2); |
|
int ord2 = cei2.next(); |
|
392 |
|
|
393 |
// Check for end of string |
if (prim1 < prim2) |
|
if (ord1 == CollationElementIterator.NULLORDER) |
|
|
if (ord2 == CollationElementIterator.NULLORDER) |
|
|
return(0); |
|
|
else |
|
394 |
return(-1); |
return(-1); |
395 |
else if (ord2 == CollationElementIterator.NULLORDER) |
else if (prim1 > prim2) |
396 |
return(1); |
return(1); |
397 |
|
else if (getStrength() == PRIMARY) |
398 |
// We know chars are totally equal, so skip |
continue; |
|
if (ord1 == ord2) |
|
|
continue; |
|
|
|
|
|
// Check for primary strength differences |
|
|
int prim1 = cei1.primaryOrder(ord1); |
|
|
int prim2 = cei2.primaryOrder(ord2); |
|
|
|
|
|
if (prim1 < prim2) |
|
|
return(-1); |
|
|
else if (prim1 > prim2) |
|
|
return(1); |
|
|
else if (getStrength() == PRIMARY) |
|
|
continue; |
|
|
|
|
|
// Check for secondary strength differences |
|
|
int sec1 = cei1.secondaryOrder(ord1); |
|
|
int sec2 = cei2.secondaryOrder(ord2); |
|
|
|
|
|
if (sec1 < sec2) |
|
|
return(-1); |
|
|
else if (sec1 > sec2) |
|
|
return(1); |
|
|
else if (getStrength() == SECONDARY) |
|
|
continue; |
|
|
|
|
|
// Check for tertiary differences |
|
|
int tert1 = cei1.tertiaryOrder(ord1); |
|
|
int tert2 = cei2.tertiaryOrder(ord1); |
|
|
|
|
|
if (tert1 < tert2) |
|
|
return(-1); |
|
|
else if (tert1 > tert2) |
|
|
return(1); |
|
|
} |
|
|
} |
|
|
|
|
|
/*************************************************************************/ |
|
|
|
|
|
/** |
|
|
* This method returns an instance of <code>CollationKey</code> for the |
|
|
* specified <code>String</code>. The object returned will have a |
|
|
* more efficient mechanism for its comparison function that could |
|
|
* provide speed benefits if multiple comparisons are performed, such |
|
|
* as during a sort. |
|
|
* |
|
|
* @param str The <code>String</code> to create a <code>CollationKey</code> for. |
|
|
* |
|
|
* @return A <code>CollationKey</code> for the specified <code>String</code>. |
|
|
*/ |
|
|
public CollationKey |
|
|
getCollationKey(String str) |
|
|
{ |
|
|
CollationElementIterator cei = getCollationElementIterator(str); |
|
|
Vector vect = new Vector(25); |
|
|
|
|
|
int ord = cei.next(); |
|
|
while (ord != CollationElementIterator.NULLORDER) |
|
|
{ |
|
|
switch (getStrength()) |
|
|
{ |
|
|
case PRIMARY: |
|
|
ord = cei.primaryOrder(ord); |
|
|
break; |
|
|
|
|
|
case SECONDARY: |
|
|
ord = cei.secondaryOrder(ord); |
|
399 |
|
|
400 |
default: |
// Check for secondary strength differences |
401 |
break; |
int sec1 = cei1.secondaryOrder(ord1); |
402 |
} |
int sec2 = cei2.secondaryOrder(ord2); |
403 |
|
|
404 |
vect.add(new Integer(ord)); |
if (sec1 < sec2) |
405 |
} |
return(-1); |
406 |
|
else if (sec1 > sec2) |
407 |
|
return(1); |
408 |
|
else if (getStrength() == SECONDARY) |
409 |
|
continue; |
410 |
|
|
411 |
Object[] objarr = vect.toArray(); |
// Check for tertiary differences |
412 |
byte[] key = new byte[objarr.length * 4]; |
int tert1 = cei1.tertiaryOrder(ord1); |
413 |
|
int tert2 = cei2.tertiaryOrder(ord1); |
414 |
|
|
415 |
for (int i = 0; i < key.length; i++) |
if (tert1 < tert2) |
416 |
{ |
return(-1); |
417 |
int j = ((Integer)objarr[i]).intValue(); |
else if (tert1 > tert2) |
418 |
key[i++] = (byte)((j & 0xFF000000) >> 24); |
return(1); |
419 |
key[i++] = (byte)((j & 0x00FF0000) >> 16); |
} |
420 |
key[i++] = (byte)((j & 0x0000FF00) >> 8); |
} |
|
key[i++] = (byte)(j & 0x000000FF); |
|
|
} |
|
421 |
|
|
422 |
return(new CollationKey(this, str, key)); |
/** |
423 |
} |
* This method returns an instance of <code>CollationKey</code> for the |
424 |
|
* specified <code>String</code>. The object returned will have a |
425 |
|
* more efficient mechanism for its comparison function that could |
426 |
|
* provide speed benefits if multiple comparisons are performed, such |
427 |
|
* as during a sort. |
428 |
|
* |
429 |
|
* @param str The <code>String</code> to create a <code>CollationKey</code> for. |
430 |
|
* |
431 |
|
* @return A <code>CollationKey</code> for the specified <code>String</code>. |
432 |
|
*/ |
433 |
|
public CollationKey getCollationKey(String str) |
434 |
|
{ |
435 |
|
CollationElementIterator cei = getCollationElementIterator(str); |
436 |
|
Vector vect = new Vector(25); |
437 |
|
|
438 |
/*************************************************************************/ |
int ord = cei.next(); |
439 |
|
while (ord != CollationElementIterator.NULLORDER) |
440 |
|
{ |
441 |
|
switch (getStrength()) |
442 |
|
{ |
443 |
|
case PRIMARY: |
444 |
|
ord = cei.primaryOrder(ord); |
445 |
|
break; |
446 |
|
|
447 |
|
case SECONDARY: |
448 |
|
ord = cei.secondaryOrder(ord); |
449 |
|
|
450 |
|
default: |
451 |
|
break; |
452 |
|
} |
453 |
|
|
454 |
/** |
vect.add(new Integer(ord)); |
455 |
* This method tests this object for equality against the specified |
} |
|
* object. This will be true if and only if the specified object is |
|
|
* another reference to this object. |
|
|
* |
|
|
* @param obj The <code>Object</code> to compare against this object. |
|
|
* |
|
|
* @return <code>true</code> if the specified object is equal to this object, <code>false</code> otherwise. |
|
|
*/ |
|
|
public boolean |
|
|
equals(Object obj) |
|
|
{ |
|
|
if (obj == this) |
|
|
return(true); |
|
|
else |
|
|
return(false); |
|
|
} |
|
456 |
|
|
457 |
/*************************************************************************/ |
Object[] objarr = vect.toArray(); |
458 |
|
byte[] key = new byte[objarr.length * 4]; |
459 |
|
|
460 |
/** |
for (int i = 0; i < key.length; i++) |
461 |
* This method returns a hash value for this object. |
{ |
462 |
* |
int j = ((Integer)objarr[i]).intValue(); |
463 |
* @return A hash value for this object. |
key[i++] = (byte)((j & 0xFF000000) >> 24); |
464 |
*/ |
key[i++] = (byte)((j & 0x00FF0000) >> 16); |
465 |
public int |
key[i++] = (byte)((j & 0x0000FF00) >> 8); |
466 |
hashCode() |
key[i++] = (byte)(j & 0x000000FF); |
467 |
{ |
} |
|
return(System.identityHashCode(this)); |
|
|
} |
|
468 |
|
|
469 |
/*************************************************************************/ |
return(new CollationKey(this, str, key)); |
470 |
|
} |
471 |
|
|
472 |
/** |
/** |
473 |
* This method creates a copy of this object. |
* This method tests this object for equality against the specified |
474 |
* |
* object. This will be true if and only if the specified object is |
475 |
* @return A copy of this object. |
* another reference to this object. |
476 |
*/ |
* |
477 |
public Object |
* @param obj The <code>Object</code> to compare against this object. |
478 |
clone() |
* |
479 |
{ |
* @return <code>true</code> if the specified object is equal to this object, <code>false</code> otherwise. |
480 |
return super.clone(); |
*/ |
481 |
|
public boolean equals(Object obj) |
482 |
|
{ |
483 |
|
if (obj == this) |
484 |
|
return(true); |
485 |
|
else |
486 |
|
return(false); |
487 |
|
} |
488 |
|
|
489 |
|
/** |
490 |
|
* This method returns a hash value for this object. |
491 |
|
* |
492 |
|
* @return A hash value for this object. |
493 |
|
*/ |
494 |
|
public int hashCode() |
495 |
|
{ |
496 |
|
return(System.identityHashCode(this)); |
497 |
|
} |
498 |
|
|
499 |
|
/** |
500 |
|
* This method creates a copy of this object. |
501 |
|
* |
502 |
|
* @return A copy of this object. |
503 |
|
*/ |
504 |
|
public Object clone() |
505 |
|
{ |
506 |
|
return super.clone(); |
507 |
|
} |
508 |
} |
} |
|
|
|
|
} // class RuleBasedCollator |
|
|
|
|