]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/text/CollationKey.java
go
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / text / CollationKey.java
1 /**\r
2 *******************************************************************************\r
3 * Copyright (C) 1996-2007, International Business Machines Corporation and    *\r
4 * others. All Rights Reserved.                                                *\r
5 *******************************************************************************\r
6 */\r
7 package com.ibm.icu.text;\r
8 \r
9 /**\r
10  * <p>A <code>CollationKey</code> represents a <code>String</code>\r
11  * under the rules of a specific <code>Collator</code>\r
12  * object. Comparing two <code>CollationKey</code>s returns the\r
13  * relative order of the <code>String</code>s they represent.</p>\r
14  *\r
15  * <p>Since the rule set of <code>Collator</code>s can differ, the\r
16  * sort orders of the same string under two different\r
17  * <code>Collator</code>s might differ.  Hence comparing\r
18  * <code>CollationKey</code>s generated from different\r
19  * <code>Collator</code>s can give incorrect results.</p>\r
20  \r
21  * <p>Both the method\r
22  * <code>CollationKey.compareTo(CollationKey)</code> and the method\r
23  * <code>Collator.compare(String, String)</code> compare two strings\r
24  * and returns their relative order.  The performance characterictics\r
25  * of these two approaches can differ.</p>\r
26  *\r
27  * <p>During the construction of a <code>CollationKey</code>, the\r
28  * entire source string is examined and processed into a series of\r
29  * bits terminated by a null, that are stored in the <code>CollationKey</code>. \r
30  * When <code>CollationKey.compareTo(CollationKey)</code> executes, it\r
31  * performs bitwise comparison on the bit sequences.  This can incurs\r
32  * startup cost when creating the <code>CollationKey</code>, but once\r
33  * the key is created, binary comparisons are fast.  This approach is\r
34  * recommended when the same strings are to be compared over and over\r
35  * again.</p>\r
36  *\r
37  * <p>On the other hand, implementations of\r
38  * <code>Collator.compare(String, String)</code> can examine and\r
39  * process the strings only until the first characters differing in\r
40  * order.  This approach is recommended if the strings are to be\r
41  * compared only once.</p>\r
42  * \r
43  * <p>More information about the composition of the bit sequence can\r
44  * be found in the \r
45  * <a href="http://www.icu-project.org/userguide/Collate_ServiceArchitecture.html">\r
46  * user guide</a>.</p>\r
47  *\r
48  * <p>The following example shows how <code>CollationKey</code>s can be used\r
49  * to sort a list of <code>String</code>s.</p>\r
50  * <blockquote>\r
51  * <pre>\r
52  * // Create an array of CollationKeys for the Strings to be sorted.\r
53  * Collator myCollator = Collator.getInstance();\r
54  * CollationKey[] keys = new CollationKey[3];\r
55  * keys[0] = myCollator.getCollationKey("Tom");\r
56  * keys[1] = myCollator.getCollationKey("Dick");\r
57  * keys[2] = myCollator.getCollationKey("Harry");\r
58  * sort( keys );\r
59  * <br>\r
60  * //...\r
61  * <br>\r
62  * // Inside body of sort routine, compare keys this way\r
63  * if( keys[i].compareTo( keys[j] ) > 0 )\r
64  *    // swap keys[i] and keys[j]\r
65  * <br>\r
66  * //...\r
67  * <br>\r
68  * // Finally, when we've returned from sort.\r
69  * System.out.println( keys[0].getSourceString() );\r
70  * System.out.println( keys[1].getSourceString() );\r
71  * System.out.println( keys[2].getSourceString() );\r
72  * </pre>\r
73  * </blockquote>\r
74  * </p>\r
75  * <p>\r
76  * This class is not subclassable\r
77  * </p>\r
78  * @see Collator\r
79  * @see RuleBasedCollator\r
80  * @author Syn Wee Quek\r
81  * @stable ICU 2.8 \r
82  */\r
83 public final class CollationKey implements Comparable \r
84 {\r
85     // public inner classes -------------------------------------------------\r
86     \r
87     /** \r
88      * Options that used in the API CollationKey.getBound() for getting a \r
89      * CollationKey based on the bound mode requested.\r
90      * @stable ICU 2.6\r
91      */\r
92     public static final class BoundMode \r
93     {\r
94         /*\r
95          * do not change the values assigned to the members of this enum. \r
96          * Underlying code depends on them having these numbers  \r
97          */\r
98          \r
99         /** \r
100          * Lower bound\r
101          * @stable ICU 2.6\r
102          */\r
103         public static final int LOWER = 0;\r
104 \r
105         /** \r
106          * Upper bound that will match strings of exact size\r
107          * @stable ICU 2.6\r
108          */\r
109         public static final int UPPER = 1;\r
110 \r
111         /** \r
112          * Upper bound that will match all the strings that have the same \r
113          * initial substring as the given string\r
114          * @stable ICU 2.6\r
115          */\r
116         public static final int UPPER_LONG = 2;\r
117 \r
118         /**\r
119          * Number of bound mode\r
120          * @stable ICU 2.6\r
121          */\r
122         public static final int COUNT = 3;\r
123         \r
124         /**\r
125          * Private Constructor\r
126          */\r
127         ///CLOVER:OFF\r
128         private BoundMode(){}\r
129         ///CLOVER:ON\r
130     }\r
131     \r
132     // public constructor ---------------------------------------------------\r
133     \r
134     /**\r
135      * CollationKey constructor.\r
136      * This constructor is given public access, unlike the JDK version, to\r
137      * allow access to users extending the Collator class. See \r
138      * {@link Collator#getCollationKey(String)}. \r
139      * @param source string this CollationKey is to represent\r
140      * @param key array of bytes that represent the collation order of argument\r
141      *            source terminated by a null\r
142      * @see Collator\r
143      * @stable ICU 2.8\r
144      */\r
145     public CollationKey(String source, byte key[])\r
146     {\r
147         m_source_ = source;\r
148         m_key_ = key;\r
149         m_hashCode_ = 0;\r
150         m_length_ = -1;\r
151     }\r
152     \r
153     /**\r
154      * CollationKey constructor that forces key to release its internal byte \r
155      * array for adoption. key will have a null byte array after this \r
156      * construction.\r
157      * @param source string this CollationKey is to represent\r
158      * @param key RawCollationKey object that represents the collation order of \r
159      *            argument source. \r
160      * @see Collator\r
161      * @see RawCollationKey\r
162      * @stable ICU 2.8 \r
163      */\r
164     public CollationKey(String source, RawCollationKey key)\r
165     {\r
166         m_source_ = source;\r
167         m_key_ = key.releaseBytes();\r
168         m_hashCode_ = 0;\r
169         m_length_ = -1;\r
170     }\r
171     \r
172     // public getters -------------------------------------------------------\r
173     \r
174     /**\r
175      * Return the source string that this CollationKey represents.\r
176      * @return source string that this CollationKey represents\r
177      * @stable ICU 2.8\r
178      */\r
179     public String getSourceString() \r
180     {\r
181         return m_source_;\r
182     }\r
183 \r
184     /**\r
185      * <p>Duplicates and returns the value of this CollationKey as a sequence \r
186      * of big-endian bytes terminated by a null.</p> \r
187      *\r
188      * <p>If two CollationKeys can be legitimately compared, then one can\r
189      * compare the byte arrays of each to obtain the same result, e.g.\r
190      * <pre>\r
191      * byte key1[] = collationkey1.toByteArray();\r
192      * byte key2[] = collationkey2.toByteArray();\r
193      * int key, targetkey;\r
194      * int i = 0;\r
195      * do {\r
196      *       key = key1[i] & 0xFF;\r
197      *     targetkey = key2[i] & 0xFF;\r
198      *     if (key &lt; targetkey) {\r
199      *         System.out.println("String 1 is less than string 2");\r
200      *         return;\r
201      *     }\r
202      *     if (targetkey &lt; key) {\r
203      *         System.out.println("String 1 is more than string 2");\r
204      *     }\r
205      *     i ++;\r
206      * } while (key != 0 && targetKey != 0);\r
207      *\r
208      * System.out.println("Strings are equal.");\r
209      * </pre>\r
210      * </p>  \r
211      * @return CollationKey value in a sequence of big-endian byte bytes \r
212      *         terminated by a null.\r
213      * @stable ICU 2.8\r
214      */\r
215     public byte[] toByteArray() \r
216     {\r
217         int length = 0;\r
218         while (true) {\r
219             if (m_key_[length] == 0) {\r
220               break;\r
221             }\r
222             length ++;\r
223         }\r
224         length ++;\r
225         byte result[] = new byte[length];\r
226         System.arraycopy(m_key_, 0, result, 0, length);\r
227         return result;\r
228     }\r
229 \r
230     // public other methods -------------------------------------------------    \r
231      \r
232     /**\r
233      * <p>Compare this CollationKey to another CollationKey.  The\r
234      * collation rules of the Collator that created this key are\r
235      * applied.</p>\r
236      *\r
237      * <p><strong>Note:</strong> Comparison between CollationKeys\r
238      * created by different Collators might return incorrect\r
239      * results.  See class documentation.</p>\r
240      *\r
241      * @param target target CollationKey\r
242      * @return an integer value.  If the value is less than zero this CollationKey\r
243      *         is less than than target, if the value is zero they are equal, and\r
244      *         if the value is greater than zero this CollationKey is greater \r
245      *         than target.\r
246      * @exception NullPointerException is thrown if argument is null.\r
247      * @see Collator#compare(String, String)\r
248      * @stable ICU 2.8 \r
249      */\r
250     public int compareTo(CollationKey target)\r
251     {\r
252     for (int i = 0;; ++i) {\r
253         int l = m_key_[i]&0xff;\r
254         int r = target.m_key_[i]&0xff;\r
255         if (l < r) {\r
256         return -1;\r
257         } else if (l > r) {\r
258         return 1;\r
259         } else if (l == 0) {\r
260         return 0;\r
261         }\r
262     }\r
263     }\r
264 \r
265     /**\r
266      * <p>Compare this CollationKey with the specified Object.  The\r
267      * collation rules of the Collator that created this key are\r
268      * applied.</p>\r
269      * \r
270      * <p>See note in compareTo(CollationKey) for warnings about possible\r
271      * incorrect results.</p>\r
272      *\r
273      * @param obj the Object to be compared to.\r
274      * @return Returns a negative integer, zero, or a positive integer \r
275      *         respectively if this CollationKey is less than, equal to, or \r
276      *         greater than the given Object.\r
277      * @exception ClassCastException is thrown when the argument is not \r
278      *            a CollationKey.  NullPointerException is thrown when the argument \r
279      *            is null.\r
280      * @see #compareTo(CollationKey)\r
281      * @stable ICU 2.8 \r
282      */\r
283     public int compareTo(Object obj) \r
284     {\r
285        return compareTo((CollationKey)obj);\r
286     }\r
287 \r
288     /**\r
289      * <p>Compare this CollationKey and the specified Object for\r
290      * equality.  The collation rules of the Collator that created\r
291      * this key are applied.</p>\r
292      *\r
293      * <p>See note in compareTo(CollationKey) for warnings about\r
294      * possible incorrect results.</p>\r
295      *\r
296      * @param target the object to compare to.\r
297      * @return true if the two keys compare as equal, false otherwise.\r
298      * @see #compareTo(CollationKey)\r
299      * @exception ClassCastException is thrown when the argument is not \r
300      *            a CollationKey.  NullPointerException is thrown when the argument \r
301      *            is null.\r
302      * @stable ICU 2.8 \r
303      */\r
304     public boolean equals(Object target) \r
305     {\r
306         if (!(target instanceof CollationKey)) {\r
307             return false;\r
308         }\r
309         \r
310         return equals((CollationKey)target);\r
311     }\r
312     \r
313     /**\r
314      * <p>\r
315      * Compare this CollationKey and the argument target CollationKey for \r
316      * equality.\r
317      * The collation \r
318      * rules of the Collator object which created these objects are applied.\r
319      * </p>\r
320      * <p>\r
321      * See note in compareTo(CollationKey) for warnings of incorrect results\r
322      * </p>\r
323      * @param target the CollationKey to compare to.\r
324      * @return true if two objects are equal, false otherwise.\r
325      * @exception NullPointerException is thrown when the argument is null.\r
326      * @stable ICU 2.8\r
327      */\r
328     public boolean equals(CollationKey target) \r
329     {\r
330         if (this == target) {\r
331             return true;\r
332         }\r
333         if (target == null) {\r
334             return false;\r
335         }\r
336         CollationKey other = (CollationKey)target;\r
337         int i = 0;\r
338         while (true) {\r
339             if (m_key_[i] != other.m_key_[i]) {\r
340                 return false;\r
341             }\r
342             if (m_key_[i] == 0) {\r
343               break;\r
344             }\r
345             i ++;\r
346         }\r
347         return true;\r
348     }\r
349 \r
350     /**\r
351      * <p>Returns a hash code for this CollationKey. The hash value is calculated \r
352      * on the key itself, not the String from which the key was created. Thus \r
353      * if x and y are CollationKeys, then x.hashCode(x) == y.hashCode() \r
354      * if x.equals(y) is true. This allows language-sensitive comparison in a \r
355      * hash table.\r
356      * </p>\r
357      * @return the hash value.\r
358      * @stable ICU 2.8\r
359      */\r
360     public int hashCode() \r
361     {\r
362         if (m_hashCode_ == 0) {\r
363             if (m_key_ == null) {\r
364                 m_hashCode_ = 1;\r
365             }\r
366             else {\r
367                 int size = m_key_.length >> 1;\r
368                 StringBuffer key = new StringBuffer(size);\r
369                 int i = 0;\r
370                 while (m_key_[i] != 0 && m_key_[i + 1] != 0) {\r
371                     key.append((char)((m_key_[i] << 8) | m_key_[i + 1]));\r
372                     i += 2;\r
373                 }\r
374                 if (m_key_[i] != 0) {\r
375                     key.append((char)(m_key_[i] << 8));\r
376                 }\r
377                 m_hashCode_ = key.toString().hashCode();\r
378             }\r
379         }\r
380         return m_hashCode_;\r
381     }\r
382     \r
383     /**\r
384      * <p>\r
385      * Produce a bound for the sort order of a given collation key and a \r
386      * strength level. This API does not attempt to find a bound for the \r
387      * CollationKey String representation, hence null will be returned in its \r
388      * place.\r
389      * </p>\r
390      * <p>\r
391      * Resulting bounds can be used to produce a range of strings that are\r
392      * between upper and lower bounds. For example, if bounds are produced\r
393      * for a sortkey of string "smith", strings between upper and lower \r
394      * bounds with primary strength would include "Smith", "SMITH", "sMiTh".\r
395      * </p>\r
396      * <p>\r
397      * There are two upper bounds that can be produced. If BoundMode.UPPER\r
398      * is produced, strings matched would be as above. However, if a bound\r
399      * is produced using BoundMode.UPPER_LONG is used, the above example will\r
400      * also match "Smithsonian" and similar.\r
401      * </p>\r
402      * <p>\r
403      * For more on usage, see example in test procedure \r
404      * <a href="http://source.icu-project.org/repos/icu/icu4j/trunk/src/com/ibm/icu/dev/test/collator/CollationAPITest.java">\r
405      * src/com/ibm/icu/dev/test/collator/CollationAPITest/TestBounds.\r
406      * </a>\r
407      * </p>\r
408      * <p>\r
409      * Collation keys produced may be compared using the <TT>compare</TT> API.\r
410      * </p>\r
411      * @param boundType Mode of bound required. It can be BoundMode.LOWER, which \r
412      *              produces a lower inclusive bound, BoundMode.UPPER, that \r
413      *              produces upper bound that matches strings of the same \r
414      *              length or BoundMode.UPPER_LONG that matches strings that \r
415      *              have the same starting substring as the source string.\r
416      * @param noOfLevels Strength levels required in the resulting bound \r
417      *                 (for most uses, the recommended value is PRIMARY). This\r
418      *                 strength should be less than the maximum strength of \r
419      *                 this CollationKey.\r
420      *                 See users guide for explanation on the strength levels a \r
421      *                 collation key can have. \r
422      * @return the result bounded CollationKey with a valid sort order but \r
423      *         a null String representation.\r
424      * @exception IllegalArgumentException thrown when the strength level \r
425      *            requested is higher than or equal to the strength in this\r
426      *            CollationKey. \r
427      *            In the case of an Exception, information \r
428      *            about the maximum strength to use will be returned in the \r
429      *            Exception. The user can then call getBound() again with the \r
430      *            appropriate strength.\r
431      * @see CollationKey\r
432      * @see CollationKey.BoundMode\r
433      * @see Collator#PRIMARY\r
434      * @see Collator#SECONDARY\r
435      * @see Collator#TERTIARY\r
436      * @see Collator#QUATERNARY\r
437      * @see Collator#IDENTICAL\r
438      * @stable ICU 2.6\r
439      */\r
440     public CollationKey getBound(int boundType, int noOfLevels) \r
441     {\r
442         // Scan the string until we skip enough of the key OR reach the end of \r
443         // the key\r
444         int offset = 0;\r
445         int keystrength = Collator.PRIMARY;\r
446         \r
447         if (noOfLevels > Collator.PRIMARY) {\r
448             while (offset < m_key_.length && m_key_[offset] != 0) {\r
449                 if (m_key_[offset ++] \r
450                         == RuleBasedCollator.SORT_LEVEL_TERMINATOR_) {\r
451                     keystrength ++;\r
452                     noOfLevels --;\r
453                     if (noOfLevels == Collator.PRIMARY \r
454                         || offset == m_key_.length || m_key_[offset] == 0) {\r
455                         offset --;\r
456                         break;\r
457                     }\r
458                 }\r
459             } \r
460         }\r
461         \r
462         if (noOfLevels > 0) {\r
463             throw new IllegalArgumentException(\r
464                                   "Source collation key has only " \r
465                                   + keystrength \r
466                                   + " strength level. Call getBound() again "\r
467                                   + " with noOfLevels < " + keystrength);\r
468         }\r
469         \r
470         // READ ME: this code assumes that the values for BoundMode variables \r
471         // will not changes. They are set so that the enum value corresponds to \r
472         // the number of extra bytes each bound type needs.\r
473         byte resultkey[] = new byte[offset + boundType + 1];\r
474         System.arraycopy(m_key_, 0, resultkey, 0, offset);\r
475         switch (boundType) {\r
476             case BoundMode.LOWER: // = 0\r
477                 // Lower bound just gets terminated. No extra bytes\r
478                 break;\r
479             case BoundMode.UPPER: // = 1\r
480                 // Upper bound needs one extra byte\r
481                 resultkey[offset ++] = 2;\r
482                 break;\r
483             case BoundMode.UPPER_LONG: // = 2\r
484                 // Upper long bound needs two extra bytes\r
485                 resultkey[offset ++] = (byte)0xFF;\r
486                 resultkey[offset ++] = (byte)0xFF;\r
487                 break;\r
488             default:\r
489                 throw new IllegalArgumentException(\r
490                                                 "Illegal boundType argument");\r
491         }\r
492         resultkey[offset ++] = 0;\r
493         return new CollationKey(null, resultkey);\r
494     }\r
495 \r
496 \r
497     \r
498     /** \r
499      * <p>\r
500      * Merges this CollationKey with another. Only the sorting order of the \r
501      * CollationKeys will be merged. This API does not attempt to merge the \r
502      * String representations of the CollationKeys, hence null will be returned\r
503      * as the String representation.\r
504      * </p>\r
505      * <p>\r
506      * The strength levels are merged with their corresponding counterparts \r
507      * (PRIMARIES with PRIMARIES, SECONDARIES with SECONDARIES etc.). \r
508      * </p>\r
509      * <p>\r
510      * The merged String representation of the result CollationKey will be a\r
511      * concatenation of the String representations of the 2 source \r
512      * CollationKeys.\r
513      * </p>\r
514      * <p>\r
515      * Between the values from the same level a separator is inserted.\r
516      * example (uncompressed):\r
517      * <pre> \r
518      * 191B1D 01 050505 01 910505 00 and 1F2123 01 050505 01 910505 00\r
519      * will be merged as \r
520      * 191B1D 02 1F212301 050505 02 050505 01 910505 02 910505 00\r
521      * </pre>\r
522      * </p>\r
523      * <p>\r
524      * This allows for concatenating of first and last names for sorting, among \r
525      * other things.\r
526      * </p>\r
527      * </p>\r
528      * @param source CollationKey to merge with \r
529      * @return a CollationKey that contains the valid merged sorting order \r
530      *         with a null String representation, \r
531      *         i.e. <tt>new CollationKey(null, merge_sort_order)</tt>\r
532      * @exception IllegalArgumentException thrown if source CollationKey\r
533      *            argument is null or of 0 length.\r
534      * @stable ICU 2.6\r
535      */\r
536     public CollationKey merge(CollationKey source)\r
537     {\r
538         // check arguments\r
539         if (source == null || source.getLength() == 0) {\r
540             throw new IllegalArgumentException(\r
541                       "CollationKey argument can not be null or of 0 length");\r
542         }\r
543     \r
544         getLength(); // gets the length of this sort key\r
545         int sourcelength = source.getLength();\r
546         // 1 extra for the last strength that has no seperators\r
547         byte result[] = new byte[m_length_ + sourcelength + 2];\r
548     \r
549         // merge the sort keys with the same number of levels\r
550         int rindex = 0;\r
551         int index = 0;\r
552         int sourceindex = 0;\r
553         while (true) { \r
554             // while both have another level\r
555             // copy level from src1 not including 00 or 01\r
556             // unsigned issues\r
557             while (m_key_[index] < 0 || m_key_[index] >= MERGE_SEPERATOR_) {\r
558                 result[rindex ++] = m_key_[index ++];\r
559             }\r
560     \r
561             // add a 02 merge separator\r
562             result[rindex ++] = MERGE_SEPERATOR_;\r
563     \r
564             // copy level from src2 not including 00 or 01\r
565             while (source.m_key_[sourceindex] < 0 \r
566                    || source.m_key_[sourceindex] >= MERGE_SEPERATOR_) {\r
567                 result[rindex ++] = source.m_key_[sourceindex ++];\r
568             }\r
569     \r
570             // if both sort keys have another level, then add a 01 level \r
571             // separator and continue\r
572             if (m_key_[index] == RuleBasedCollator.SORT_LEVEL_TERMINATOR_\r
573                 && source.m_key_[sourceindex] \r
574                         == RuleBasedCollator.SORT_LEVEL_TERMINATOR_) {\r
575                 ++ index;\r
576                 ++ sourceindex;\r
577                 result[rindex ++] = RuleBasedCollator.SORT_LEVEL_TERMINATOR_;\r
578             }\r
579             else {\r
580                 break;\r
581             }\r
582         }\r
583     \r
584         // here, at least one sort key is finished now, but the other one\r
585         // might have some contents left from containing more levels;\r
586         // that contents is just appended to the result\r
587         if (m_key_[index] != 0) {\r
588             System.arraycopy(m_key_, index, result, rindex,\r
589                              m_length_ - index);\r
590         }\r
591         else if (source.m_key_[sourceindex] != 0) {\r
592             System.arraycopy(source.m_key_, sourceindex, result, rindex,  \r
593                              source.m_length_ - sourceindex);\r
594         }\r
595         result[result.length - 1] = 0;\r
596     \r
597         // trust that neither sort key contained illegally embedded zero bytes\r
598         return new CollationKey(null, result);\r
599     }\r
600 \r
601     // private data members -------------------------------------------------\r
602 \r
603     /**\r
604      * Sequence of bytes that represents the sort key\r
605      */\r
606     private byte m_key_[];\r
607     \r
608     /**\r
609      * Source string this CollationKey represents\r
610      */    \r
611     private String m_source_;\r
612 \r
613     /**\r
614      * Hash code for the key\r
615      */\r
616     private int m_hashCode_;\r
617     /**\r
618      * Gets the length of this CollationKey\r
619      */\r
620     private int m_length_;\r
621     /**\r
622      * Collation key merge seperator\r
623      */\r
624     private static final int MERGE_SEPERATOR_ = 2;\r
625     \r
626     // private methods ------------------------------------------------------\r
627     \r
628     /**\r
629      * Gets the length of the CollationKey\r
630      * @return length of the CollationKey\r
631      */\r
632     private int getLength()\r
633     {\r
634         if (m_length_ >= 0) {\r
635             return m_length_;\r
636         }\r
637         int length = m_key_.length;\r
638         for (int index = 0; index < length; index ++) {\r
639             if (m_key_[index] == 0) {\r
640                 length = index;\r
641                 break;\r
642             }\r
643         } \r
644         m_length_ = length;\r
645         return m_length_;\r
646     }\r
647 }\r