]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/text/UnicodeSet.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / text / UnicodeSet.java
1 //##header\r
2 /*\r
3  *******************************************************************************\r
4  * Copyright (C) 1996-2009, International Business Machines Corporation and    *\r
5  * others. All Rights Reserved.                                                *\r
6  *******************************************************************************\r
7  */\r
8 package com.ibm.icu.text;\r
9 \r
10 import java.text.*;\r
11 import com.ibm.icu.lang.*;\r
12 \r
13 import java.io.IOException;\r
14 \r
15 import com.ibm.icu.impl.NormalizerImpl;\r
16 import com.ibm.icu.impl.Utility;\r
17 import com.ibm.icu.impl.UCharacterProperty;\r
18 import com.ibm.icu.impl.UBiDiProps;\r
19 import com.ibm.icu.impl.UCaseProps;\r
20 import com.ibm.icu.impl.UPropertyAliases;\r
21 import com.ibm.icu.impl.SortedSetRelation;\r
22 import com.ibm.icu.impl.RuleCharacterIterator;\r
23 \r
24 import com.ibm.icu.util.Freezable;\r
25 import com.ibm.icu.util.ULocale;\r
26 import com.ibm.icu.util.VersionInfo;\r
27 \r
28 import com.ibm.icu.text.BreakIterator;\r
29 \r
30 import java.util.MissingResourceException;\r
31 import java.util.TreeSet;\r
32 import java.util.Iterator;\r
33 import java.util.Collection;\r
34 \r
35 /**\r
36  * A mutable set of Unicode characters and multicharacter strings.  Objects of this class\r
37  * represent <em>character classes</em> used in regular expressions.\r
38  * A character specifies a subset of Unicode code points.  Legal\r
39  * code points are U+0000 to U+10FFFF, inclusive.\r
40  *\r
41  * <p>The UnicodeSet class is not designed to be subclassed.\r
42  *\r
43  * <p><code>UnicodeSet</code> supports two APIs. The first is the\r
44  * <em>operand</em> API that allows the caller to modify the value of\r
45  * a <code>UnicodeSet</code> object. It conforms to Java 2's\r
46  * <code>java.util.Set</code> interface, although\r
47  * <code>UnicodeSet</code> does not actually implement that\r
48  * interface. All methods of <code>Set</code> are supported, with the\r
49  * modification that they take a character range or single character\r
50  * instead of an <code>Object</code>, and they take a\r
51  * <code>UnicodeSet</code> instead of a <code>Collection</code>.  The\r
52  * operand API may be thought of in terms of boolean logic: a boolean\r
53  * OR is implemented by <code>add</code>, a boolean AND is implemented\r
54  * by <code>retain</code>, a boolean XOR is implemented by\r
55  * <code>complement</code> taking an argument, and a boolean NOT is\r
56  * implemented by <code>complement</code> with no argument.  In terms\r
57  * of traditional set theory function names, <code>add</code> is a\r
58  * union, <code>retain</code> is an intersection, <code>remove</code>\r
59  * is an asymmetric difference, and <code>complement</code> with no\r
60  * argument is a set complement with respect to the superset range\r
61  * <code>MIN_VALUE-MAX_VALUE</code>\r
62  *\r
63  * <p>The second API is the\r
64  * <code>applyPattern()</code>/<code>toPattern()</code> API from the\r
65  * <code>java.text.Format</code>-derived classes.  Unlike the\r
66  * methods that add characters, add categories, and control the logic\r
67  * of the set, the method <code>applyPattern()</code> sets all\r
68  * attributes of a <code>UnicodeSet</code> at once, based on a\r
69  * string pattern.\r
70  *\r
71  * <p><b>Pattern syntax</b></p>\r
72  *\r
73  * Patterns are accepted by the constructors and the\r
74  * <code>applyPattern()</code> methods and returned by the\r
75  * <code>toPattern()</code> method.  These patterns follow a syntax\r
76  * similar to that employed by version 8 regular expression character\r
77  * classes.  Here are some simple examples:\r
78  *\r
79  * <blockquote>\r
80  *   <table>\r
81  *     <tr align="top">\r
82  *       <td nowrap valign="top" align="left"><code>[]</code></td>\r
83  *       <td valign="top">No characters</td>\r
84  *     </tr><tr align="top">\r
85  *       <td nowrap valign="top" align="left"><code>[a]</code></td>\r
86  *       <td valign="top">The character 'a'</td>\r
87  *     </tr><tr align="top">\r
88  *       <td nowrap valign="top" align="left"><code>[ae]</code></td>\r
89  *       <td valign="top">The characters 'a' and 'e'</td>\r
90  *     </tr>\r
91  *     <tr>\r
92  *       <td nowrap valign="top" align="left"><code>[a-e]</code></td>\r
93  *       <td valign="top">The characters 'a' through 'e' inclusive, in Unicode code\r
94  *       point order</td>\r
95  *     </tr>\r
96  *     <tr>\r
97  *       <td nowrap valign="top" align="left"><code>[\\u4E01]</code></td>\r
98  *       <td valign="top">The character U+4E01</td>\r
99  *     </tr>\r
100  *     <tr>\r
101  *       <td nowrap valign="top" align="left"><code>[a{ab}{ac}]</code></td>\r
102  *       <td valign="top">The character 'a' and the multicharacter strings &quot;ab&quot; and\r
103  *       &quot;ac&quot;</td>\r
104  *     </tr>\r
105  *     <tr>\r
106  *       <td nowrap valign="top" align="left"><code>[\p{Lu}]</code></td>\r
107  *       <td valign="top">All characters in the general category Uppercase Letter</td>\r
108  *     </tr>\r
109  *   </table>\r
110  * </blockquote>\r
111  *\r
112  * Any character may be preceded by a backslash in order to remove any special\r
113  * meaning.  White space characters, as defined by UCharacterProperty.isRuleWhiteSpace(), are\r
114  * ignored, unless they are escaped.\r
115  *\r
116  * <p>Property patterns specify a set of characters having a certain\r
117  * property as defined by the Unicode standard.  Both the POSIX-like\r
118  * "[:Lu:]" and the Perl-like syntax "\p{Lu}" are recognized.  For a\r
119  * complete list of supported property patterns, see the User's Guide\r
120  * for UnicodeSet at\r
121  * <a href="http://www.icu-project.org/userguide/unicodeSet.html">\r
122  * http://www.icu-project.org/userguide/unicodeSet.html</a>.\r
123  * Actual determination of property data is defined by the underlying\r
124  * Unicode database as implemented by UCharacter.\r
125  *\r
126  * <p>Patterns specify individual characters, ranges of characters, and\r
127  * Unicode property sets.  When elements are concatenated, they\r
128  * specify their union.  To complement a set, place a '^' immediately\r
129  * after the opening '['.  Property patterns are inverted by modifying\r
130  * their delimiters; "[:^foo]" and "\P{foo}".  In any other location,\r
131  * '^' has no special meaning.\r
132  *\r
133  * <p>Ranges are indicated by placing two a '-' between two\r
134  * characters, as in "a-z".  This specifies the range of all\r
135  * characters from the left to the right, in Unicode order.  If the\r
136  * left character is greater than or equal to the\r
137  * right character it is a syntax error.  If a '-' occurs as the first\r
138  * character after the opening '[' or '[^', or if it occurs as the\r
139  * last character before the closing ']', then it is taken as a\r
140  * literal.  Thus "[a\\-b]", "[-ab]", and "[ab-]" all indicate the same\r
141  * set of three characters, 'a', 'b', and '-'.\r
142  *\r
143  * <p>Sets may be intersected using the '&' operator or the asymmetric\r
144  * set difference may be taken using the '-' operator, for example,\r
145  * "[[:L:]&[\\u0000-\\u0FFF]]" indicates the set of all Unicode letters\r
146  * with values less than 4096.  Operators ('&' and '|') have equal\r
147  * precedence and bind left-to-right.  Thus\r
148  * "[[:L:]-[a-z]-[\\u0100-\\u01FF]]" is equivalent to\r
149  * "[[[:L:]-[a-z]]-[\\u0100-\\u01FF]]".  This only really matters for\r
150  * difference; intersection is commutative.\r
151  *\r
152  * <table>\r
153  * <tr valign=top><td nowrap><code>[a]</code><td>The set containing 'a'\r
154  * <tr valign=top><td nowrap><code>[a-z]</code><td>The set containing 'a'\r
155  * through 'z' and all letters in between, in Unicode order\r
156  * <tr valign=top><td nowrap><code>[^a-z]</code><td>The set containing\r
157  * all characters but 'a' through 'z',\r
158  * that is, U+0000 through 'a'-1 and 'z'+1 through U+10FFFF\r
159  * <tr valign=top><td nowrap><code>[[<em>pat1</em>][<em>pat2</em>]]</code>\r
160  * <td>The union of sets specified by <em>pat1</em> and <em>pat2</em>\r
161  * <tr valign=top><td nowrap><code>[[<em>pat1</em>]&[<em>pat2</em>]]</code>\r
162  * <td>The intersection of sets specified by <em>pat1</em> and <em>pat2</em>\r
163  * <tr valign=top><td nowrap><code>[[<em>pat1</em>]-[<em>pat2</em>]]</code>\r
164  * <td>The asymmetric difference of sets specified by <em>pat1</em> and\r
165  * <em>pat2</em>\r
166  * <tr valign=top><td nowrap><code>[:Lu:] or \p{Lu}</code>\r
167  * <td>The set of characters having the specified\r
168  * Unicode property; in\r
169  * this case, Unicode uppercase letters\r
170  * <tr valign=top><td nowrap><code>[:^Lu:] or \P{Lu}</code>\r
171  * <td>The set of characters <em>not</em> having the given\r
172  * Unicode property\r
173  * </table>\r
174  *\r
175  * <p><b>Warning</b>: you cannot add an empty string ("") to a UnicodeSet.</p>\r
176  *\r
177  * <p><b>Formal syntax</b></p>\r
178  *\r
179  * <blockquote>\r
180  *   <table>\r
181  *     <tr align="top">\r
182  *       <td nowrap valign="top" align="right"><code>pattern :=&nbsp; </code></td>\r
183  *       <td valign="top"><code>('[' '^'? item* ']') |\r
184  *       property</code></td>\r
185  *     </tr>\r
186  *     <tr align="top">\r
187  *       <td nowrap valign="top" align="right"><code>item :=&nbsp; </code></td>\r
188  *       <td valign="top"><code>char | (char '-' char) | pattern-expr<br>\r
189  *       </code></td>\r
190  *     </tr>\r
191  *     <tr align="top">\r
192  *       <td nowrap valign="top" align="right"><code>pattern-expr :=&nbsp; </code></td>\r
193  *       <td valign="top"><code>pattern | pattern-expr pattern |\r
194  *       pattern-expr op pattern<br>\r
195  *       </code></td>\r
196  *     </tr>\r
197  *     <tr align="top">\r
198  *       <td nowrap valign="top" align="right"><code>op :=&nbsp; </code></td>\r
199  *       <td valign="top"><code>'&amp;' | '-'<br>\r
200  *       </code></td>\r
201  *     </tr>\r
202  *     <tr align="top">\r
203  *       <td nowrap valign="top" align="right"><code>special :=&nbsp; </code></td>\r
204  *       <td valign="top"><code>'[' | ']' | '-'<br>\r
205  *       </code></td>\r
206  *     </tr>\r
207  *     <tr align="top">\r
208  *       <td nowrap valign="top" align="right"><code>char :=&nbsp; </code></td>\r
209  *       <td valign="top"><em>any character that is not</em><code> special<br>\r
210  *       | ('\\' </code><em>any character</em><code>)<br>\r
211  *       | ('&#92;u' hex hex hex hex)<br>\r
212  *       </code></td>\r
213  *     </tr>\r
214  *     <tr align="top">\r
215  *       <td nowrap valign="top" align="right"><code>hex :=&nbsp; </code></td>\r
216  *       <td valign="top"><em>any character for which\r
217  *       </em><code>Character.digit(c, 16)</code><em>\r
218  *       returns a non-negative result</em></td>\r
219  *     </tr>\r
220  *     <tr>\r
221  *       <td nowrap valign="top" align="right"><code>property :=&nbsp; </code></td>\r
222  *       <td valign="top"><em>a Unicode property set pattern</td>\r
223  *     </tr>\r
224  *   </table>\r
225  *   <br>\r
226  *   <table border="1">\r
227  *     <tr>\r
228  *       <td>Legend: <table>\r
229  *         <tr>\r
230  *           <td nowrap valign="top"><code>a := b</code></td>\r
231  *           <td width="20" valign="top">&nbsp; </td>\r
232  *           <td valign="top"><code>a</code> may be replaced by <code>b</code> </td>\r
233  *         </tr>\r
234  *         <tr>\r
235  *           <td nowrap valign="top"><code>a?</code></td>\r
236  *           <td valign="top"></td>\r
237  *           <td valign="top">zero or one instance of <code>a</code><br>\r
238  *           </td>\r
239  *         </tr>\r
240  *         <tr>\r
241  *           <td nowrap valign="top"><code>a*</code></td>\r
242  *           <td valign="top"></td>\r
243  *           <td valign="top">one or more instances of <code>a</code><br>\r
244  *           </td>\r
245  *         </tr>\r
246  *         <tr>\r
247  *           <td nowrap valign="top"><code>a | b</code></td>\r
248  *           <td valign="top"></td>\r
249  *           <td valign="top">either <code>a</code> or <code>b</code><br>\r
250  *           </td>\r
251  *         </tr>\r
252  *         <tr>\r
253  *           <td nowrap valign="top"><code>'a'</code></td>\r
254  *           <td valign="top"></td>\r
255  *           <td valign="top">the literal string between the quotes </td>\r
256  *         </tr>\r
257  *       </table>\r
258  *       </td>\r
259  *     </tr>\r
260  *   </table>\r
261  * </blockquote>\r
262  * <p>To iterate over contents of UnicodeSet, use UnicodeSetIterator class.\r
263  *\r
264  * @author Alan Liu\r
265  * @stable ICU 2.0\r
266  * @see UnicodeSetIterator\r
267  */\r
268 public class UnicodeSet extends UnicodeFilter implements Freezable {\r
269 \r
270     private static final int LOW = 0x000000; // LOW <= all valid values. ZERO for codepoints\r
271     private static final int HIGH = 0x110000; // HIGH > all valid values. 10000 for code units.\r
272                                              // 110000 for codepoints\r
273 \r
274     /**\r
275      * Minimum value that can be stored in a UnicodeSet.\r
276      * @stable ICU 2.0\r
277      */\r
278     public static final int MIN_VALUE = LOW;\r
279 \r
280     /**\r
281      * Maximum value that can be stored in a UnicodeSet.\r
282      * @stable ICU 2.0\r
283      */\r
284     public static final int MAX_VALUE = HIGH - 1;\r
285 \r
286     private int len;      // length used; list may be longer to minimize reallocs\r
287     private int[] list;   // MUST be terminated with HIGH\r
288     private int[] rangeList; // internal buffer\r
289     private int[] buffer; // internal buffer\r
290 \r
291     // NOTE: normally the field should be of type SortedSet; but that is missing a public clone!!\r
292     // is not private so that UnicodeSetIterator can get access\r
293     TreeSet strings = new TreeSet();\r
294 \r
295     /**\r
296      * The pattern representation of this set.  This may not be the\r
297      * most economical pattern.  It is the pattern supplied to\r
298      * applyPattern(), with variables substituted and whitespace\r
299      * removed.  For sets constructed without applyPattern(), or\r
300      * modified using the non-pattern API, this string will be null,\r
301      * indicating that toPattern() must generate a pattern\r
302      * representation from the inversion list.\r
303      */\r
304     private String pat = null;\r
305 \r
306     private static final int START_EXTRA = 16;         // initial storage. Must be >= 0\r
307     private static final int GROW_EXTRA = START_EXTRA; // extra amount for growth. Must be >= 0\r
308 \r
309     // Special property set IDs\r
310     private static final String ANY_ID   = "ANY";   // [\u0000-\U0010FFFF]\r
311     private static final String ASCII_ID = "ASCII"; // [\u0000-\u007F]\r
312     private static final String ASSIGNED = "Assigned"; // [:^Cn:]\r
313 \r
314     /**\r
315      * A set of all characters _except_ the second through last characters of\r
316      * certain ranges.  These ranges are ranges of characters whose\r
317      * properties are all exactly alike, e.g. CJK Ideographs from\r
318      * U+4E00 to U+9FA5.\r
319      */\r
320     private static UnicodeSet INCLUSIONS[] = null;\r
321 \r
322     //----------------------------------------------------------------\r
323     // Public API\r
324     //----------------------------------------------------------------\r
325 \r
326     /**\r
327      * Constructs an empty set.\r
328      * @stable ICU 2.0\r
329      */\r
330     public UnicodeSet() {\r
331         list = new int[1 + START_EXTRA];\r
332         list[len++] = HIGH;\r
333     }\r
334 \r
335     /**\r
336      * Constructs a copy of an existing set.\r
337      * @stable ICU 2.0\r
338      */\r
339     public UnicodeSet(UnicodeSet other) {\r
340         set(other);\r
341     }\r
342 \r
343     /**\r
344      * Constructs a set containing the given range. If <code>end >\r
345      * start</code> then an empty set is created.\r
346      *\r
347      * @param start first character, inclusive, of range\r
348      * @param end last character, inclusive, of range\r
349      * @stable ICU 2.0\r
350      */\r
351     public UnicodeSet(int start, int end) {\r
352         this();\r
353         complement(start, end);\r
354     }\r
355 \r
356     /**\r
357      * Constructs a set from the given pattern.  See the class description\r
358      * for the syntax of the pattern language.  Whitespace is ignored.\r
359      * @param pattern a string specifying what characters are in the set\r
360      * @exception java.lang.IllegalArgumentException if the pattern contains\r
361      * a syntax error.\r
362      * @stable ICU 2.0\r
363      */\r
364     public UnicodeSet(String pattern) {\r
365         this();\r
366         applyPattern(pattern, null, null, IGNORE_SPACE);\r
367     }\r
368 \r
369     /**\r
370      * Constructs a set from the given pattern.  See the class description\r
371      * for the syntax of the pattern language.\r
372      * @param pattern a string specifying what characters are in the set\r
373      * @param ignoreWhitespace if true, ignore characters for which\r
374      * UCharacterProperty.isRuleWhiteSpace() returns true\r
375      * @exception java.lang.IllegalArgumentException if the pattern contains\r
376      * a syntax error.\r
377      * @stable ICU 2.0\r
378      */\r
379     public UnicodeSet(String pattern, boolean ignoreWhitespace) {\r
380         this();\r
381         applyPattern(pattern, null, null, ignoreWhitespace ? IGNORE_SPACE : 0);\r
382     }\r
383 \r
384     /**\r
385      * Constructs a set from the given pattern.  See the class description\r
386      * for the syntax of the pattern language.\r
387      * @param pattern a string specifying what characters are in the set\r
388      * @param options a bitmask indicating which options to apply.\r
389      * Valid options are IGNORE_SPACE and CASE.\r
390      * @exception java.lang.IllegalArgumentException if the pattern contains\r
391      * a syntax error.\r
392      * @stable ICU 3.8\r
393      */\r
394     public UnicodeSet(String pattern, int options) {\r
395         this();\r
396         applyPattern(pattern, null, null, options);\r
397     }\r
398 \r
399     /**\r
400      * Constructs a set from the given pattern.  See the class description\r
401      * for the syntax of the pattern language.\r
402      * @param pattern a string specifying what characters are in the set\r
403      * @param pos on input, the position in pattern at which to start parsing.\r
404      * On output, the position after the last character parsed.\r
405      * @param symbols a symbol table mapping variables to char[] arrays\r
406      * and chars to UnicodeSets\r
407      * @exception java.lang.IllegalArgumentException if the pattern\r
408      * contains a syntax error.\r
409      * @stable ICU 2.0\r
410      */\r
411     public UnicodeSet(String pattern, ParsePosition pos, SymbolTable symbols) {\r
412         this();\r
413         applyPattern(pattern, pos, symbols, IGNORE_SPACE);\r
414     }\r
415 \r
416     /**\r
417      * Constructs a set from the given pattern.  See the class description\r
418      * for the syntax of the pattern language.\r
419      * @param pattern a string specifying what characters are in the set\r
420      * @param pos on input, the position in pattern at which to start parsing.\r
421      * On output, the position after the last character parsed.\r
422      * @param symbols a symbol table mapping variables to char[] arrays\r
423      * and chars to UnicodeSets\r
424      * @param options a bitmask indicating which options to apply.\r
425      * Valid options are IGNORE_SPACE and CASE.\r
426      * @exception java.lang.IllegalArgumentException if the pattern\r
427      * contains a syntax error.\r
428      * @stable ICU 3.2\r
429      */\r
430     public UnicodeSet(String pattern, ParsePosition pos, SymbolTable symbols, int options) {\r
431         this();\r
432         applyPattern(pattern, pos, symbols, options);\r
433     }\r
434 \r
435 \r
436     /**\r
437      * Return a new set that is equivalent to this one.\r
438      * @stable ICU 2.0\r
439      */\r
440     public Object clone() {\r
441         UnicodeSet result = new UnicodeSet(this);\r
442         result.frozen = this.frozen;\r
443         return result;\r
444     }\r
445 \r
446     /**\r
447      * Make this object represent the range <code>start - end</code>.\r
448      * If <code>end > start</code> then this object is set to an\r
449      * an empty range.\r
450      *\r
451      * @param start first character in the set, inclusive\r
452      * @param end last character in the set, inclusive\r
453      * @stable ICU 2.0\r
454      */\r
455     public UnicodeSet set(int start, int end) {\r
456         checkFrozen();\r
457         clear();\r
458         complement(start, end);\r
459         return this;\r
460     }\r
461 \r
462     /**\r
463      * Make this object represent the same set as <code>other</code>.\r
464      * @param other a <code>UnicodeSet</code> whose value will be\r
465      * copied to this object\r
466      * @stable ICU 2.0\r
467      */\r
468     public UnicodeSet set(UnicodeSet other) {\r
469         checkFrozen();\r
470         list = (int[]) other.list.clone();\r
471         len = other.len;\r
472         pat = other.pat;\r
473         strings = (TreeSet)other.strings.clone();\r
474         return this;\r
475     }\r
476 \r
477     /**\r
478      * Modifies this set to represent the set specified by the given pattern.\r
479      * See the class description for the syntax of the pattern language.\r
480      * Whitespace is ignored.\r
481      * @param pattern a string specifying what characters are in the set\r
482      * @exception java.lang.IllegalArgumentException if the pattern\r
483      * contains a syntax error.\r
484      * @stable ICU 2.0\r
485      */\r
486     public final UnicodeSet applyPattern(String pattern) {\r
487         checkFrozen();\r
488         return applyPattern(pattern, null, null, IGNORE_SPACE);\r
489     }\r
490 \r
491     /**\r
492      * Modifies this set to represent the set specified by the given pattern,\r
493      * optionally ignoring whitespace.\r
494      * See the class description for the syntax of the pattern language.\r
495      * @param pattern a string specifying what characters are in the set\r
496      * @param ignoreWhitespace if true then characters for which\r
497      * UCharacterProperty.isRuleWhiteSpace() returns true are ignored\r
498      * @exception java.lang.IllegalArgumentException if the pattern\r
499      * contains a syntax error.\r
500      * @stable ICU 2.0\r
501      */\r
502     public UnicodeSet applyPattern(String pattern, boolean ignoreWhitespace) {\r
503         checkFrozen();\r
504         return applyPattern(pattern, null, null, ignoreWhitespace ? IGNORE_SPACE : 0);\r
505     }\r
506 \r
507     /**\r
508      * Modifies this set to represent the set specified by the given pattern,\r
509      * optionally ignoring whitespace.\r
510      * See the class description for the syntax of the pattern language.\r
511      * @param pattern a string specifying what characters are in the set\r
512      * @param options a bitmask indicating which options to apply.\r
513      * Valid options are IGNORE_SPACE and CASE.\r
514      * @exception java.lang.IllegalArgumentException if the pattern\r
515      * contains a syntax error.\r
516      * @stable ICU 3.8\r
517      */\r
518     public UnicodeSet applyPattern(String pattern, int options) {\r
519         checkFrozen();\r
520         return applyPattern(pattern, null, null, options);\r
521     }\r
522 \r
523     /**\r
524      * Return true if the given position, in the given pattern, appears\r
525      * to be the start of a UnicodeSet pattern.\r
526      * @stable ICU 2.0\r
527      */\r
528     public static boolean resemblesPattern(String pattern, int pos) {\r
529         return ((pos+1) < pattern.length() &&\r
530                 pattern.charAt(pos) == '[') ||\r
531             resemblesPropertyPattern(pattern, pos);\r
532     }\r
533 \r
534     /**\r
535      * Append the <code>toPattern()</code> representation of a\r
536      * string to the given <code>StringBuffer</code>.\r
537      */\r
538     private static void _appendToPat(StringBuffer buf, String s, boolean escapeUnprintable) {\r
539         for (int i = 0; i < s.length(); i += UTF16.getCharCount(i)) {\r
540             _appendToPat(buf, UTF16.charAt(s, i), escapeUnprintable);\r
541         }\r
542     }\r
543 \r
544     /**\r
545      * Append the <code>toPattern()</code> representation of a\r
546      * character to the given <code>StringBuffer</code>.\r
547      */\r
548     private static void _appendToPat(StringBuffer buf, int c, boolean escapeUnprintable) {\r
549         if (escapeUnprintable && Utility.isUnprintable(c)) {\r
550             // Use hex escape notation (<backslash>uxxxx or <backslash>Uxxxxxxxx) for anything\r
551             // unprintable\r
552             if (Utility.escapeUnprintable(buf, c)) {\r
553                 return;\r
554             }\r
555         }\r
556         // Okay to let ':' pass through\r
557         switch (c) {\r
558         case '[': // SET_OPEN:\r
559         case ']': // SET_CLOSE:\r
560         case '-': // HYPHEN:\r
561         case '^': // COMPLEMENT:\r
562         case '&': // INTERSECTION:\r
563         case '\\': //BACKSLASH:\r
564         case '{':\r
565         case '}':\r
566         case '$':\r
567         case ':':\r
568             buf.append('\\');\r
569             break;\r
570         default:\r
571             // Escape whitespace\r
572             if (UCharacterProperty.isRuleWhiteSpace(c)) {\r
573                 buf.append('\\');\r
574             }\r
575             break;\r
576         }\r
577         UTF16.append(buf, c);\r
578     }\r
579 \r
580     /**\r
581      * Returns a string representation of this set.  If the result of\r
582      * calling this function is passed to a UnicodeSet constructor, it\r
583      * will produce another set that is equal to this one.\r
584      * @stable ICU 2.0\r
585      */\r
586     public String toPattern(boolean escapeUnprintable) {\r
587         StringBuffer result = new StringBuffer();\r
588         return _toPattern(result, escapeUnprintable).toString();\r
589     }\r
590 \r
591     /**\r
592      * Append a string representation of this set to result.  This will be\r
593      * a cleaned version of the string passed to applyPattern(), if there\r
594      * is one.  Otherwise it will be generated.\r
595      */\r
596     private StringBuffer _toPattern(StringBuffer result,\r
597                                     boolean escapeUnprintable) {\r
598         if (pat != null) {\r
599             int i;\r
600             int backslashCount = 0;\r
601             for (i=0; i<pat.length(); ) {\r
602                 int c = UTF16.charAt(pat, i);\r
603                 i += UTF16.getCharCount(c);\r
604                 if (escapeUnprintable && Utility.isUnprintable(c)) {\r
605                     // If the unprintable character is preceded by an odd\r
606                     // number of backslashes, then it has been escaped.\r
607                     // Before unescaping it, we delete the final\r
608                     // backslash.\r
609                     if ((backslashCount % 2) == 1) {\r
610                         result.setLength(result.length() - 1);\r
611                     }\r
612                     Utility.escapeUnprintable(result, c);\r
613                     backslashCount = 0;\r
614                 } else {\r
615                     UTF16.append(result, c);\r
616                     if (c == '\\') {\r
617                         ++backslashCount;\r
618                     } else {\r
619                         backslashCount = 0;\r
620                     }\r
621                 }\r
622             }\r
623             return result;\r
624         }\r
625 \r
626         return _generatePattern(result, escapeUnprintable, true);\r
627     }\r
628 \r
629     /**\r
630      * Generate and append a string representation of this set to result.\r
631      * This does not use this.pat, the cleaned up copy of the string\r
632      * passed to applyPattern().\r
633      * @param result the buffer into which to generate the pattern\r
634      * @param escapeUnprintable escape unprintable characters if true\r
635      * @stable ICU 2.0\r
636      */\r
637     public StringBuffer _generatePattern(StringBuffer result, boolean escapeUnprintable) {\r
638         return _generatePattern(result, escapeUnprintable, true);\r
639     }\r
640 \r
641     /**\r
642      * Generate and append a string representation of this set to result.\r
643      * This does not use this.pat, the cleaned up copy of the string\r
644      * passed to applyPattern().\r
645      * @param includeStrings if false, doesn't include the strings.\r
646      * @stable ICU 3.8\r
647      */\r
648     public StringBuffer _generatePattern(StringBuffer result,\r
649                                          boolean escapeUnprintable, boolean includeStrings) {\r
650         result.append('[');\r
651 \r
652 //      // Check against the predefined categories.  We implicitly build\r
653 //      // up ALL category sets the first time toPattern() is called.\r
654 //      for (int cat=0; cat<CATEGORY_COUNT; ++cat) {\r
655 //          if (this.equals(getCategorySet(cat))) {\r
656 //              result.append(':');\r
657 //              result.append(CATEGORY_NAMES.substring(cat*2, cat*2+2));\r
658 //              return result.append(":]");\r
659 //          }\r
660 //      }\r
661 \r
662         int count = getRangeCount();\r
663 \r
664         // If the set contains at least 2 intervals and includes both\r
665         // MIN_VALUE and MAX_VALUE, then the inverse representation will\r
666         // be more economical.\r
667         if (count > 1 &&\r
668             getRangeStart(0) == MIN_VALUE &&\r
669             getRangeEnd(count-1) == MAX_VALUE) {\r
670 \r
671             // Emit the inverse\r
672             result.append('^');\r
673 \r
674             for (int i = 1; i < count; ++i) {\r
675                 int start = getRangeEnd(i-1)+1;\r
676                 int end = getRangeStart(i)-1;\r
677                 _appendToPat(result, start, escapeUnprintable);\r
678                 if (start != end) {\r
679                     if ((start+1) != end) {\r
680                         result.append('-');\r
681                     }\r
682                     _appendToPat(result, end, escapeUnprintable);\r
683                 }\r
684             }\r
685         }\r
686 \r
687         // Default; emit the ranges as pairs\r
688         else {\r
689             for (int i = 0; i < count; ++i) {\r
690                 int start = getRangeStart(i);\r
691                 int end = getRangeEnd(i);\r
692                 _appendToPat(result, start, escapeUnprintable);\r
693                 if (start != end) {\r
694                     if ((start+1) != end) {\r
695                         result.append('-');\r
696                     }\r
697                     _appendToPat(result, end, escapeUnprintable);\r
698                 }\r
699             }\r
700         }\r
701 \r
702         if (includeStrings && strings.size() > 0) {\r
703             Iterator it = strings.iterator();\r
704             while (it.hasNext()) {\r
705                 result.append('{');\r
706                 _appendToPat(result, (String) it.next(), escapeUnprintable);\r
707                 result.append('}');\r
708             }\r
709         }\r
710         return result.append(']');\r
711     }\r
712 \r
713     /**\r
714      * Returns the number of elements in this set (its cardinality)\r
715      * Note than the elements of a set may include both individual\r
716      * codepoints and strings.\r
717      *\r
718      * @return the number of elements in this set (its cardinality).\r
719      * @stable ICU 2.0\r
720      */\r
721     public int size() {\r
722         int n = 0;\r
723         int count = getRangeCount();\r
724         for (int i = 0; i < count; ++i) {\r
725             n += getRangeEnd(i) - getRangeStart(i) + 1;\r
726         }\r
727         return n + strings.size();\r
728     }\r
729 \r
730     /**\r
731      * Returns <tt>true</tt> if this set contains no elements.\r
732      *\r
733      * @return <tt>true</tt> if this set contains no elements.\r
734      * @stable ICU 2.0\r
735      */\r
736     public boolean isEmpty() {\r
737         return len == 1 && strings.size() == 0;\r
738     }\r
739 \r
740     /**\r
741      * Implementation of UnicodeMatcher API.  Returns <tt>true</tt> if\r
742      * this set contains any character whose low byte is the given\r
743      * value.  This is used by <tt>RuleBasedTransliterator</tt> for\r
744      * indexing.\r
745      * @stable ICU 2.0\r
746      */\r
747     public boolean matchesIndexValue(int v) {\r
748         /* The index value v, in the range [0,255], is contained in this set if\r
749          * it is contained in any pair of this set.  Pairs either have the high\r
750          * bytes equal, or unequal.  If the high bytes are equal, then we have\r
751          * aaxx..aayy, where aa is the high byte.  Then v is contained if xx <=\r
752          * v <= yy.  If the high bytes are unequal we have aaxx..bbyy, bb>aa.\r
753          * Then v is contained if xx <= v || v <= yy.  (This is identical to the\r
754          * time zone month containment logic.)\r
755          */\r
756         for (int i=0; i<getRangeCount(); ++i) {\r
757             int low = getRangeStart(i);\r
758             int high = getRangeEnd(i);\r
759             if ((low & ~0xFF) == (high & ~0xFF)) {\r
760                 if ((low & 0xFF) <= v && v <= (high & 0xFF)) {\r
761                     return true;\r
762                 }\r
763             } else if ((low & 0xFF) <= v || v <= (high & 0xFF)) {\r
764                 return true;\r
765             }\r
766         }\r
767         if (strings.size() != 0) {\r
768             Iterator it = strings.iterator();\r
769             while (it.hasNext()) {\r
770                 String s = (String) it.next();\r
771                 //if (s.length() == 0) {\r
772                 //    // Empty strings match everything\r
773                 //    return true;\r
774                 //}\r
775                 // assert(s.length() != 0); // We enforce this elsewhere\r
776                 int c = UTF16.charAt(s, 0);\r
777                 if ((c & 0xFF) == v) {\r
778                     return true;\r
779                 }\r
780             }\r
781         }\r
782         return false;\r
783     }\r
784 \r
785     /**\r
786      * Implementation of UnicodeMatcher.matches().  Always matches the\r
787      * longest possible multichar string.\r
788      * @stable ICU 2.0\r
789      */\r
790     public int matches(Replaceable text,\r
791                        int[] offset,\r
792                        int limit,\r
793                        boolean incremental) {\r
794 \r
795         if (offset[0] == limit) {\r
796             // Strings, if any, have length != 0, so we don't worry\r
797             // about them here.  If we ever allow zero-length strings\r
798             // we much check for them here.\r
799             if (contains(UnicodeMatcher.ETHER)) {\r
800                 return incremental ? U_PARTIAL_MATCH : U_MATCH;\r
801             } else {\r
802                 return U_MISMATCH;\r
803             }\r
804         } else {\r
805             if (strings.size() != 0) { // try strings first\r
806 \r
807                 // might separate forward and backward loops later\r
808                 // for now they are combined\r
809 \r
810                 // TODO Improve efficiency of this, at least in the forward\r
811                 // direction, if not in both.  In the forward direction we\r
812                 // can assume the strings are sorted.\r
813 \r
814                 Iterator it = strings.iterator();\r
815                 boolean forward = offset[0] < limit;\r
816 \r
817                 // firstChar is the leftmost char to match in the\r
818                 // forward direction or the rightmost char to match in\r
819                 // the reverse direction.\r
820                 char firstChar = text.charAt(offset[0]);\r
821 \r
822                 // If there are multiple strings that can match we\r
823                 // return the longest match.\r
824                 int highWaterLength = 0;\r
825 \r
826                 while (it.hasNext()) {\r
827                     String trial = (String) it.next();\r
828 \r
829                     //if (trial.length() == 0) {\r
830                     //    return U_MATCH; // null-string always matches\r
831                     //}\r
832                     // assert(trial.length() != 0); // We ensure this elsewhere\r
833 \r
834                     char c = trial.charAt(forward ? 0 : trial.length() - 1);\r
835 \r
836                     // Strings are sorted, so we can optimize in the\r
837                     // forward direction.\r
838                     if (forward && c > firstChar) break;\r
839                     if (c != firstChar) continue;\r
840 \r
841                     int length = matchRest(text, offset[0], limit, trial);\r
842 \r
843                     if (incremental) {\r
844                         int maxLen = forward ? limit-offset[0] : offset[0]-limit;\r
845                         if (length == maxLen) {\r
846                             // We have successfully matched but only up to limit.\r
847                             return U_PARTIAL_MATCH;\r
848                         }\r
849                     }\r
850 \r
851                     if (length == trial.length()) {\r
852                         // We have successfully matched the whole string.\r
853                         if (length > highWaterLength) {\r
854                             highWaterLength = length;\r
855                         }\r
856                         // In the forward direction we know strings\r
857                         // are sorted so we can bail early.\r
858                         if (forward && length < highWaterLength) {\r
859                             break;\r
860                         }\r
861                         continue;\r
862                     }\r
863                 }\r
864 \r
865                 // We've checked all strings without a partial match.\r
866                 // If we have full matches, return the longest one.\r
867                 if (highWaterLength != 0) {\r
868                     offset[0] += forward ? highWaterLength : -highWaterLength;\r
869                     return U_MATCH;\r
870                 }\r
871             }\r
872             return super.matches(text, offset, limit, incremental);\r
873         }\r
874     }\r
875 \r
876     /**\r
877      * Returns the longest match for s in text at the given position.\r
878      * If limit > start then match forward from start+1 to limit\r
879      * matching all characters except s.charAt(0).  If limit < start,\r
880      * go backward starting from start-1 matching all characters\r
881      * except s.charAt(s.length()-1).  This method assumes that the\r
882      * first character, text.charAt(start), matches s, so it does not\r
883      * check it.\r
884      * @param text the text to match\r
885      * @param start the first character to match.  In the forward\r
886      * direction, text.charAt(start) is matched against s.charAt(0).\r
887      * In the reverse direction, it is matched against\r
888      * s.charAt(s.length()-1).\r
889      * @param limit the limit offset for matching, either last+1 in\r
890      * the forward direction, or last-1 in the reverse direction,\r
891      * where last is the index of the last character to match.\r
892      * @return If part of s matches up to the limit, return |limit -\r
893      * start|.  If all of s matches before reaching the limit, return\r
894      * s.length().  If there is a mismatch between s and text, return\r
895      * 0\r
896      */\r
897     private static int matchRest (Replaceable text, int start, int limit, String s) {\r
898         int maxLen;\r
899         int slen = s.length();\r
900         if (start < limit) {\r
901             maxLen = limit - start;\r
902             if (maxLen > slen) maxLen = slen;\r
903             for (int i = 1; i < maxLen; ++i) {\r
904                 if (text.charAt(start + i) != s.charAt(i)) return 0;\r
905             }\r
906         } else {\r
907             maxLen = start - limit;\r
908             if (maxLen > slen) maxLen = slen;\r
909             --slen; // <=> slen = s.length() - 1;\r
910             for (int i = 1; i < maxLen; ++i) {\r
911                 if (text.charAt(start - i) != s.charAt(slen - i)) return 0;\r
912             }\r
913         }\r
914         return maxLen;\r
915     }\r
916 \r
917 //#if defined(FOUNDATION10) || defined(J2SE13)\r
918 //#else\r
919     /**\r
920      * Tests whether the text matches at the offset. If so, returns the end of the longest substring that it matches. If not, returns -1. \r
921      * @internal\r
922      * @deprecated This API is ICU internal only.\r
923      */\r
924     public int matchesAt(CharSequence text, int offset) {\r
925         int lastLen = -1;\r
926         strings:\r
927         if (strings.size() != 0) {\r
928             char firstChar = text.charAt(offset);\r
929             String trial = null;\r
930             // find the first string starting with firstChar\r
931             Iterator it = strings.iterator();\r
932             while (it.hasNext()) {\r
933                 trial = (String) it.next();\r
934                 char firstStringChar = trial.charAt(0);\r
935                 if (firstStringChar < firstChar) continue;\r
936                 if (firstStringChar > firstChar) break strings;\r
937             }\r
938             // now keep checking string until we get the longest one\r
939             for (;;) {\r
940                 int tempLen = matchesAt(text, offset, trial);\r
941                 if (lastLen > tempLen) break strings;\r
942                 lastLen = tempLen;\r
943                 if (!it.hasNext()) break;\r
944                 trial = (String) it.next();\r
945             }\r
946         }\r
947         if (lastLen < 2) {\r
948             int cp = UTF16.charAt(text, offset);\r
949             if (contains(cp)) {\r
950                 lastLen = UTF16.getCharCount(cp);\r
951             }\r
952         }\r
953         return offset+lastLen;\r
954     }\r
955 \r
956     /**\r
957      * Does one string contain another, starting at a specific offset?\r
958      * @param text\r
959      * @param offset\r
960      * @param other\r
961      * @return\r
962      */\r
963     // Note: This method was moved from CollectionUtilities\r
964     private static int matchesAt(CharSequence text, int offset, CharSequence other) {\r
965         int len = other.length();\r
966         int i = 0;\r
967         int j = offset;\r
968         for (; i < len; ++i, ++j) {\r
969             char pc = other.charAt(i);\r
970             char tc = text.charAt(j);\r
971             if (pc != tc) return -1;\r
972         }\r
973         return i;\r
974     }\r
975 //#endif\r
976 \r
977     /**\r
978      * Implementation of UnicodeMatcher API.  Union the set of all\r
979      * characters that may be matched by this object into the given\r
980      * set.\r
981      * @param toUnionTo the set into which to union the source characters\r
982      * @stable ICU 2.2\r
983      */\r
984     public void addMatchSetTo(UnicodeSet toUnionTo) {\r
985         toUnionTo.addAll(this);\r
986     }\r
987 \r
988     /**\r
989      * Returns the index of the given character within this set, where\r
990      * the set is ordered by ascending code point.  If the character\r
991      * is not in this set, return -1.  The inverse of this method is\r
992      * <code>charAt()</code>.\r
993      * @return an index from 0..size()-1, or -1\r
994      * @stable ICU 2.0\r
995      */\r
996     public int indexOf(int c) {\r
997         if (c < MIN_VALUE || c > MAX_VALUE) {\r
998             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6));\r
999         }\r
1000         int i = 0;\r
1001         int n = 0;\r
1002         for (;;) {\r
1003             int start = list[i++];\r
1004             if (c < start) {\r
1005                 return -1;\r
1006             }\r
1007             int limit = list[i++];\r
1008             if (c < limit) {\r
1009                 return n + c - start;\r
1010             }\r
1011             n += limit - start;\r
1012         }\r
1013     }\r
1014 \r
1015     /**\r
1016      * Returns the character at the given index within this set, where\r
1017      * the set is ordered by ascending code point.  If the index is\r
1018      * out of range, return -1.  The inverse of this method is\r
1019      * <code>indexOf()</code>.\r
1020      * @param index an index from 0..size()-1\r
1021      * @return the character at the given index, or -1.\r
1022      * @stable ICU 2.0\r
1023      */\r
1024     public int charAt(int index) {\r
1025         if (index >= 0) {\r
1026             // len2 is the largest even integer <= len, that is, it is len\r
1027             // for even values and len-1 for odd values.  With odd values\r
1028             // the last entry is UNICODESET_HIGH.\r
1029             int len2 = len & ~1;\r
1030             for (int i=0; i < len2;) {\r
1031                 int start = list[i++];\r
1032                 int count = list[i++] - start;\r
1033                 if (index < count) {\r
1034                     return start + index;\r
1035                 }\r
1036                 index -= count;\r
1037             }\r
1038         }\r
1039         return -1;\r
1040     }\r
1041 \r
1042     /**\r
1043      * Adds the specified range to this set if it is not already\r
1044      * present.  If this set already contains the specified range,\r
1045      * the call leaves this set unchanged.  If <code>end > start</code>\r
1046      * then an empty range is added, leaving the set unchanged.\r
1047      *\r
1048      * @param start first character, inclusive, of range to be added\r
1049      * to this set.\r
1050      * @param end last character, inclusive, of range to be added\r
1051      * to this set.\r
1052      * @stable ICU 2.0\r
1053      */\r
1054     public UnicodeSet add(int start, int end) {\r
1055         checkFrozen();\r
1056         return add_unchecked(start, end);\r
1057     }\r
1058     \r
1059     // for internal use, after checkFrozen has been called\r
1060     private UnicodeSet add_unchecked(int start, int end) {\r
1061         if (start < MIN_VALUE || start > MAX_VALUE) {\r
1062             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));\r
1063         }\r
1064         if (end < MIN_VALUE || end > MAX_VALUE) {\r
1065             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));\r
1066         }\r
1067         if (start < end) {\r
1068             add(range(start, end), 2, 0);\r
1069         } else if (start == end) {\r
1070             add(start);\r
1071         }\r
1072         return this;\r
1073     }\r
1074 \r
1075 //    /**\r
1076 //     * Format out the inversion list as a string, for debugging.  Uncomment when\r
1077 //     * needed.\r
1078 //     */\r
1079 //    public final String dump() {\r
1080 //        StringBuffer buf = new StringBuffer("[");\r
1081 //        for (int i=0; i<len; ++i) {\r
1082 //            if (i != 0) buf.append(", ");\r
1083 //            int c = list[i];\r
1084 //            //if (c <= 0x7F && c != '\n' && c != '\r' && c != '\t' && c != ' ') {\r
1085 //            //    buf.append((char) c);\r
1086 //            //} else {\r
1087 //                buf.append("U+").append(Utility.hex(c, (c<0x10000)?4:6));\r
1088 //            //}\r
1089 //        }\r
1090 //        buf.append("]");\r
1091 //        return buf.toString();\r
1092 //    }\r
1093 \r
1094     /**\r
1095      * Adds the specified character to this set if it is not already\r
1096      * present.  If this set already contains the specified character,\r
1097      * the call leaves this set unchanged.\r
1098      * @stable ICU 2.0\r
1099      */\r
1100     public final UnicodeSet add(int c) {\r
1101         checkFrozen();\r
1102         return add_unchecked(c);\r
1103     }\r
1104     \r
1105     // for internal use only, after checkFrozen has been called\r
1106     private final UnicodeSet add_unchecked(int c) {\r
1107         if (c < MIN_VALUE || c > MAX_VALUE) {\r
1108             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6));\r
1109         }\r
1110 \r
1111         // find smallest i such that c < list[i]\r
1112         // if odd, then it is IN the set\r
1113         // if even, then it is OUT of the set\r
1114         int i = findCodePoint(c);\r
1115 \r
1116         // already in set?\r
1117         if ((i & 1) != 0) return this;\r
1118 \r
1119         // HIGH is 0x110000\r
1120         // assert(list[len-1] == HIGH);\r
1121 \r
1122         // empty = [HIGH]\r
1123         // [start_0, limit_0, start_1, limit_1, HIGH]\r
1124 \r
1125         // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH]\r
1126         //                             ^\r
1127         //                             list[i]\r
1128 \r
1129         // i == 0 means c is before the first range\r
1130 \r
1131         if (c == list[i]-1) {\r
1132             // c is before start of next range\r
1133             list[i] = c;\r
1134             // if we touched the HIGH mark, then add a new one\r
1135             if (c == MAX_VALUE) {\r
1136                 ensureCapacity(len+1);\r
1137                 list[len++] = HIGH;\r
1138             }\r
1139             if (i > 0 && c == list[i-1]) {\r
1140                 // collapse adjacent ranges\r
1141 \r
1142                 // [..., start_k-1, c, c, limit_k, ..., HIGH]\r
1143                 //                     ^\r
1144                 //                     list[i]\r
1145                 System.arraycopy(list, i+1, list, i-1, len-i-1);\r
1146                 len -= 2;\r
1147             }\r
1148         }\r
1149 \r
1150         else if (i > 0 && c == list[i-1]) {\r
1151             // c is after end of prior range\r
1152             list[i-1]++;\r
1153             // no need to chcek for collapse here\r
1154         }\r
1155 \r
1156         else {\r
1157             // At this point we know the new char is not adjacent to\r
1158             // any existing ranges, and it is not 10FFFF.\r
1159 \r
1160 \r
1161             // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH]\r
1162             //                             ^\r
1163             //                             list[i]\r
1164 \r
1165             // [..., start_k-1, limit_k-1, c, c+1, start_k, limit_k, ..., HIGH]\r
1166             //                             ^\r
1167             //                             list[i]\r
1168 \r
1169             // Don't use ensureCapacity() to save on copying.\r
1170             // NOTE: This has no measurable impact on performance,\r
1171             // but it might help in some usage patterns.\r
1172             if (len+2 > list.length) {\r
1173                 int[] temp = new int[len + 2 + GROW_EXTRA];\r
1174                 if (i != 0) System.arraycopy(list, 0, temp, 0, i);\r
1175                 System.arraycopy(list, i, temp, i+2, len-i);\r
1176                 list = temp;\r
1177             } else {\r
1178                 System.arraycopy(list, i, list, i+2, len-i);\r
1179             }\r
1180 \r
1181             list[i] = c;\r
1182             list[i+1] = c+1;\r
1183             len += 2;\r
1184         }\r
1185 \r
1186         pat = null;\r
1187         return this;\r
1188     }\r
1189 \r
1190     /**\r
1191      * Adds the specified multicharacter to this set if it is not already\r
1192      * present.  If this set already contains the multicharacter,\r
1193      * the call leaves this set unchanged.\r
1194      * Thus "ch" => {"ch"}\r
1195      * <br><b>Warning: you cannot add an empty string ("") to a UnicodeSet.</b>\r
1196      * @param s the source string\r
1197      * @return this object, for chaining\r
1198      * @stable ICU 2.0\r
1199      */\r
1200     public final UnicodeSet add(String s) {\r
1201         checkFrozen();\r
1202         int cp = getSingleCP(s);\r
1203         if (cp < 0) {\r
1204             strings.add(s);\r
1205             pat = null;\r
1206         } else {\r
1207             add_unchecked(cp, cp);\r
1208         }\r
1209         return this;\r
1210     }\r
1211     \r
1212     /**\r
1213      * @return a code point IF the string consists of a single one.\r
1214      * otherwise returns -1.\r
1215      * @param string to test\r
1216      */\r
1217     private static int getSingleCP(String s) {\r
1218         if (s.length() < 1) {\r
1219             throw new IllegalArgumentException("Can't use zero-length strings in UnicodeSet");\r
1220         }\r
1221         if (s.length() > 2) return -1;\r
1222         if (s.length() == 1) return s.charAt(0);\r
1223 \r
1224         // at this point, len = 2\r
1225         int cp = UTF16.charAt(s, 0);\r
1226         if (cp > 0xFFFF) { // is surrogate pair\r
1227             return cp;\r
1228         }\r
1229         return -1;\r
1230     }\r
1231 \r
1232     /**\r
1233      * Adds each of the characters in this string to the set. Thus "ch" => {"c", "h"}\r
1234      * If this set already any particular character, it has no effect on that character.\r
1235      * @param s the source string\r
1236      * @return this object, for chaining\r
1237      * @stable ICU 2.0\r
1238      */\r
1239     public final UnicodeSet addAll(String s) {\r
1240         checkFrozen();\r
1241         int cp;\r
1242         for (int i = 0; i < s.length(); i += UTF16.getCharCount(cp)) {\r
1243             cp = UTF16.charAt(s, i);\r
1244             add_unchecked(cp, cp);\r
1245         }\r
1246         return this;\r
1247     }\r
1248 \r
1249     /**\r
1250      * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"}\r
1251      * If this set already any particular character, it has no effect on that character.\r
1252      * @param s the source string\r
1253      * @return this object, for chaining\r
1254      * @stable ICU 2.0\r
1255      */\r
1256     public final UnicodeSet retainAll(String s) {\r
1257         return retainAll(fromAll(s));\r
1258     }\r
1259 \r
1260     /**\r
1261      * Complement EACH of the characters in this string. Note: "ch" == {"c", "h"}\r
1262      * If this set already any particular character, it has no effect on that character.\r
1263      * @param s the source string\r
1264      * @return this object, for chaining\r
1265      * @stable ICU 2.0\r
1266      */\r
1267     public final UnicodeSet complementAll(String s) {\r
1268         return complementAll(fromAll(s));\r
1269     }\r
1270 \r
1271     /**\r
1272      * Remove EACH of the characters in this string. Note: "ch" == {"c", "h"}\r
1273      * If this set already any particular character, it has no effect on that character.\r
1274      * @param s the source string\r
1275      * @return this object, for chaining\r
1276      * @stable ICU 2.0\r
1277      */\r
1278     public final UnicodeSet removeAll(String s) {\r
1279         return removeAll(fromAll(s));\r
1280     }\r
1281 \r
1282     /**\r
1283      * Remove all strings from this UnicodeSet\r
1284      * @return this object, for chaining\r
1285      * @draft ICU 4.2\r
1286      * @provisional This API might change or be removed in a future release.\r
1287      */\r
1288     public final UnicodeSet removeAllStrings() {\r
1289         checkFrozen();\r
1290         if (strings.size() != 0) {\r
1291             strings.clear();\r
1292             pat = null;\r
1293         }\r
1294         return this;\r
1295     }\r
1296         \r
1297     /**\r
1298      * Makes a set from a multicharacter string. Thus "ch" => {"ch"}\r
1299      * <br><b>Warning: you cannot add an empty string ("") to a UnicodeSet.</b>\r
1300      * @param s the source string\r
1301      * @return a newly created set containing the given string\r
1302      * @stable ICU 2.0\r
1303      */\r
1304     public static UnicodeSet from(String s) {\r
1305         return new UnicodeSet().add(s);\r
1306     }\r
1307 \r
1308 \r
1309     /**\r
1310      * Makes a set from each of the characters in the string. Thus "ch" => {"c", "h"}\r
1311      * @param s the source string\r
1312      * @return a newly created set containing the given characters\r
1313      * @stable ICU 2.0\r
1314      */\r
1315     public static UnicodeSet fromAll(String s) {\r
1316         return new UnicodeSet().addAll(s);\r
1317     }\r
1318 \r
1319 \r
1320     /**\r
1321      * Retain only the elements in this set that are contained in the\r
1322      * specified range.  If <code>end > start</code> then an empty range is\r
1323      * retained, leaving the set empty.\r
1324      *\r
1325      * @param start first character, inclusive, of range to be retained\r
1326      * to this set.\r
1327      * @param end last character, inclusive, of range to be retained\r
1328      * to this set.\r
1329      * @stable ICU 2.0\r
1330      */\r
1331     public UnicodeSet retain(int start, int end) {\r
1332         checkFrozen();\r
1333         if (start < MIN_VALUE || start > MAX_VALUE) {\r
1334             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));\r
1335         }\r
1336         if (end < MIN_VALUE || end > MAX_VALUE) {\r
1337             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));\r
1338         }\r
1339         if (start <= end) {\r
1340             retain(range(start, end), 2, 0);\r
1341         } else {\r
1342             clear();\r
1343         }\r
1344         return this;\r
1345     }\r
1346 \r
1347     /**\r
1348      * Retain the specified character from this set if it is present.\r
1349      * Upon return this set will be empty if it did not contain c, or\r
1350      * will only contain c if it did contain c.\r
1351      * @param c the character to be retained\r
1352      * @return this object, for chaining\r
1353      * @stable ICU 2.0\r
1354      */\r
1355     public final UnicodeSet retain(int c) {\r
1356         return retain(c, c);\r
1357     }\r
1358 \r
1359     /**\r
1360      * Retain the specified string in this set if it is present.\r
1361      * Upon return this set will be empty if it did not contain s, or\r
1362      * will only contain s if it did contain s.\r
1363      * @param s the string to be retained\r
1364      * @return this object, for chaining\r
1365      * @stable ICU 2.0\r
1366      */\r
1367     public final UnicodeSet retain(String s) {\r
1368         int cp = getSingleCP(s);\r
1369         if (cp < 0) {\r
1370             boolean isIn = strings.contains(s);\r
1371             if (isIn && size() == 1) {\r
1372                 return this;\r
1373             }\r
1374             clear();\r
1375             strings.add(s);\r
1376             pat = null;\r
1377         } else {\r
1378             retain(cp, cp);\r
1379         }\r
1380         return this;\r
1381     }\r
1382 \r
1383     /**\r
1384      * Removes the specified range from this set if it is present.\r
1385      * The set will not contain the specified range once the call\r
1386      * returns.  If <code>end > start</code> then an empty range is\r
1387      * removed, leaving the set unchanged.\r
1388      *\r
1389      * @param start first character, inclusive, of range to be removed\r
1390      * from this set.\r
1391      * @param end last character, inclusive, of range to be removed\r
1392      * from this set.\r
1393      * @stable ICU 2.0\r
1394      */\r
1395     public UnicodeSet remove(int start, int end) {\r
1396         checkFrozen();\r
1397         if (start < MIN_VALUE || start > MAX_VALUE) {\r
1398             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));\r
1399         }\r
1400         if (end < MIN_VALUE || end > MAX_VALUE) {\r
1401             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));\r
1402         }\r
1403         if (start <= end) {\r
1404             retain(range(start, end), 2, 2);\r
1405         }\r
1406         return this;\r
1407     }\r
1408 \r
1409     /**\r
1410      * Removes the specified character from this set if it is present.\r
1411      * The set will not contain the specified character once the call\r
1412      * returns.\r
1413      * @param c the character to be removed\r
1414      * @return this object, for chaining\r
1415      * @stable ICU 2.0\r
1416      */\r
1417     public final UnicodeSet remove(int c) {\r
1418         return remove(c, c);\r
1419     }\r
1420 \r
1421     /**\r
1422      * Removes the specified string from this set if it is present.\r
1423      * The set will not contain the specified string once the call\r
1424      * returns.\r
1425      * @param s the string to be removed\r
1426      * @return this object, for chaining\r
1427      * @stable ICU 2.0\r
1428      */\r
1429     public final UnicodeSet remove(String s) {\r
1430         int cp = getSingleCP(s);\r
1431         if (cp < 0) {\r
1432             strings.remove(s);\r
1433             pat = null;\r
1434         } else {\r
1435             remove(cp, cp);\r
1436         }\r
1437         return this;\r
1438     }\r
1439 \r
1440     /**\r
1441      * Complements the specified range in this set.  Any character in\r
1442      * the range will be removed if it is in this set, or will be\r
1443      * added if it is not in this set.  If <code>end > start</code>\r
1444      * then an empty range is complemented, leaving the set unchanged.\r
1445      *\r
1446      * @param start first character, inclusive, of range to be removed\r
1447      * from this set.\r
1448      * @param end last character, inclusive, of range to be removed\r
1449      * from this set.\r
1450      * @stable ICU 2.0\r
1451      */\r
1452     public UnicodeSet complement(int start, int end) {\r
1453         checkFrozen();\r
1454         if (start < MIN_VALUE || start > MAX_VALUE) {\r
1455             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));\r
1456         }\r
1457         if (end < MIN_VALUE || end > MAX_VALUE) {\r
1458             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));\r
1459         }\r
1460         if (start <= end) {\r
1461             xor(range(start, end), 2, 0);\r
1462         }\r
1463         pat = null;\r
1464         return this;\r
1465     }\r
1466 \r
1467     /**\r
1468      * Complements the specified character in this set.  The character\r
1469      * will be removed if it is in this set, or will be added if it is\r
1470      * not in this set.\r
1471      * @stable ICU 2.0\r
1472      */\r
1473     public final UnicodeSet complement(int c) {\r
1474         return complement(c, c);\r
1475     }\r
1476 \r
1477     /**\r
1478      * This is equivalent to\r
1479      * <code>complement(MIN_VALUE, MAX_VALUE)</code>.\r
1480      * @stable ICU 2.0\r
1481      */\r
1482     public UnicodeSet complement() {\r
1483         checkFrozen();\r
1484         if (list[0] == LOW) {\r
1485             System.arraycopy(list, 1, list, 0, len-1);\r
1486             --len;\r
1487         } else {\r
1488             ensureCapacity(len+1);\r
1489             System.arraycopy(list, 0, list, 1, len);\r
1490             list[0] = LOW;\r
1491             ++len;\r
1492         }\r
1493         pat = null;\r
1494         return this;\r
1495     }\r
1496 \r
1497     /**\r
1498      * Complement the specified string in this set.\r
1499      * The set will not contain the specified string once the call\r
1500      * returns.\r
1501      * <br><b>Warning: you cannot add an empty string ("") to a UnicodeSet.</b>\r
1502      * @param s the string to complement\r
1503      * @return this object, for chaining\r
1504      * @stable ICU 2.0\r
1505      */\r
1506     public final UnicodeSet complement(String s) {\r
1507         checkFrozen();\r
1508         int cp = getSingleCP(s);\r
1509         if (cp < 0) {\r
1510             if (strings.contains(s)) strings.remove(s);\r
1511             else strings.add(s);\r
1512             pat = null;\r
1513         } else {\r
1514             complement(cp, cp);\r
1515         }\r
1516         return this;\r
1517     }\r
1518 \r
1519     /**\r
1520      * Returns true if this set contains the given character.\r
1521      * @param c character to be checked for containment\r
1522      * @return true if the test condition is met\r
1523      * @stable ICU 2.0\r
1524      */\r
1525     public boolean contains(int c) {\r
1526         if (c < MIN_VALUE || c > MAX_VALUE) {\r
1527             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6));\r
1528         }\r
1529 \r
1530         /*\r
1531         // Set i to the index of the start item greater than ch\r
1532         // We know we will terminate without length test!\r
1533         int i = -1;\r
1534         while (true) {\r
1535             if (c < list[++i]) break;\r
1536         }\r
1537         */\r
1538 \r
1539         int i = findCodePoint(c);\r
1540 \r
1541         return ((i & 1) != 0); // return true if odd\r
1542     }\r
1543 \r
1544     /**\r
1545      * Returns the smallest value i such that c < list[i].  Caller\r
1546      * must ensure that c is a legal value or this method will enter\r
1547      * an infinite loop.  This method performs a binary search.\r
1548      * @param c a character in the range MIN_VALUE..MAX_VALUE\r
1549      * inclusive\r
1550      * @return the smallest integer i in the range 0..len-1,\r
1551      * inclusive, such that c < list[i]\r
1552      */\r
1553     private final int findCodePoint(int c) {\r
1554         /* Examples:\r
1555                                            findCodePoint(c)\r
1556            set              list[]         c=0 1 3 4 7 8\r
1557            ===              ==============   ===========\r
1558            []               [110000]         0 0 0 0 0 0\r
1559            [\u0000-\u0003]  [0, 4, 110000]   1 1 1 2 2 2\r
1560            [\u0004-\u0007]  [4, 8, 110000]   0 0 0 1 1 2\r
1561            [:all:]          [0, 110000]      1 1 1 1 1 1\r
1562          */\r
1563 \r
1564         // Return the smallest i such that c < list[i].  Assume\r
1565         // list[len - 1] == HIGH and that c is legal (0..HIGH-1).\r
1566         if (c < list[0]) return 0;\r
1567         // High runner test.  c is often after the last range, so an\r
1568         // initial check for this condition pays off.\r
1569         if (len >= 2 && c >= list[len-2]) return len-1;\r
1570         int lo = 0;\r
1571         int hi = len - 1;\r
1572         // invariant: c >= list[lo]\r
1573         // invariant: c < list[hi]\r
1574         for (;;) {\r
1575             int i = (lo + hi) >>> 1;\r
1576             if (i == lo) return hi;\r
1577             if (c < list[i]) {\r
1578                 hi = i;\r
1579             } else {\r
1580                 lo = i;\r
1581             }\r
1582         }\r
1583     }\r
1584 \r
1585 //    //----------------------------------------------------------------\r
1586 //    // Unrolled binary search\r
1587 //    //----------------------------------------------------------------\r
1588 //\r
1589 //    private int validLen = -1; // validated value of len\r
1590 //    private int topOfLow;\r
1591 //    private int topOfHigh;\r
1592 //    private int power;\r
1593 //    private int deltaStart;\r
1594 //\r
1595 //    private void validate() {\r
1596 //        if (len <= 1) {\r
1597 //            throw new IllegalArgumentException("list.len==" + len + "; must be >1");\r
1598 //        }\r
1599 //\r
1600 //        // find greatest power of 2 less than or equal to len\r
1601 //        for (power = exp2.length-1; power > 0 && exp2[power] > len; power--) {}\r
1602 //\r
1603 //        // assert(exp2[power] <= len);\r
1604 //\r
1605 //        // determine the starting points\r
1606 //        topOfLow = exp2[power] - 1;\r
1607 //        topOfHigh = len - 1;\r
1608 //        deltaStart = exp2[power-1];\r
1609 //        validLen = len;\r
1610 //    }\r
1611 //\r
1612 //    private static final int exp2[] = {\r
1613 //        0x1, 0x2, 0x4, 0x8,\r
1614 //        0x10, 0x20, 0x40, 0x80,\r
1615 //        0x100, 0x200, 0x400, 0x800,\r
1616 //        0x1000, 0x2000, 0x4000, 0x8000,\r
1617 //        0x10000, 0x20000, 0x40000, 0x80000,\r
1618 //        0x100000, 0x200000, 0x400000, 0x800000,\r
1619 //        0x1000000, 0x2000000, 0x4000000, 0x8000000,\r
1620 //        0x10000000, 0x20000000 // , 0x40000000 // no unsigned int in Java\r
1621 //    };\r
1622 //\r
1623 //    /**\r
1624 //     * Unrolled lowest index GT.\r
1625 //     */\r
1626 //    private final int leastIndexGT(int searchValue) {\r
1627 //\r
1628 //        if (len != validLen) {\r
1629 //            if (len == 1) return 0;\r
1630 //            validate();\r
1631 //        }\r
1632 //        int temp;\r
1633 //\r
1634 //        // set up initial range to search. Each subrange is a power of two in length\r
1635 //        int high = searchValue < list[topOfLow] ? topOfLow : topOfHigh;\r
1636 //\r
1637 //        // Completely unrolled binary search, folhighing "Programming Pearls"\r
1638 //        // Each case deliberately falls through to the next\r
1639 //        // Logically, list[-1] < all_search_values && list[count] > all_search_values\r
1640 //        // although the values -1 and count are never actually touched.\r
1641 //\r
1642 //        // The bounds at each point are low & high,\r
1643 //        // where low == high - delta*2\r
1644 //        // so high - delta is the midpoint\r
1645 //\r
1646 //        // The invariant AFTER each line is that list[low] < searchValue <= list[high]\r
1647 //\r
1648 //        switch (power) {\r
1649 //        //case 31: if (searchValue < list[temp = high-0x40000000]) high = temp; // no unsigned int in Java\r
1650 //        case 30: if (searchValue < list[temp = high-0x20000000]) high = temp;\r
1651 //        case 29: if (searchValue < list[temp = high-0x10000000]) high = temp;\r
1652 //\r
1653 //        case 28: if (searchValue < list[temp = high- 0x8000000]) high = temp;\r
1654 //        case 27: if (searchValue < list[temp = high- 0x4000000]) high = temp;\r
1655 //        case 26: if (searchValue < list[temp = high- 0x2000000]) high = temp;\r
1656 //        case 25: if (searchValue < list[temp = high- 0x1000000]) high = temp;\r
1657 //\r
1658 //        case 24: if (searchValue < list[temp = high-  0x800000]) high = temp;\r
1659 //        case 23: if (searchValue < list[temp = high-  0x400000]) high = temp;\r
1660 //        case 22: if (searchValue < list[temp = high-  0x200000]) high = temp;\r
1661 //        case 21: if (searchValue < list[temp = high-  0x100000]) high = temp;\r
1662 //\r
1663 //        case 20: if (searchValue < list[temp = high-   0x80000]) high = temp;\r
1664 //        case 19: if (searchValue < list[temp = high-   0x40000]) high = temp;\r
1665 //        case 18: if (searchValue < list[temp = high-   0x20000]) high = temp;\r
1666 //        case 17: if (searchValue < list[temp = high-   0x10000]) high = temp;\r
1667 //\r
1668 //        case 16: if (searchValue < list[temp = high-    0x8000]) high = temp;\r
1669 //        case 15: if (searchValue < list[temp = high-    0x4000]) high = temp;\r
1670 //        case 14: if (searchValue < list[temp = high-    0x2000]) high = temp;\r
1671 //        case 13: if (searchValue < list[temp = high-    0x1000]) high = temp;\r
1672 //\r
1673 //        case 12: if (searchValue < list[temp = high-     0x800]) high = temp;\r
1674 //        case 11: if (searchValue < list[temp = high-     0x400]) high = temp;\r
1675 //        case 10: if (searchValue < list[temp = high-     0x200]) high = temp;\r
1676 //        case  9: if (searchValue < list[temp = high-     0x100]) high = temp;\r
1677 //\r
1678 //        case  8: if (searchValue < list[temp = high-      0x80]) high = temp;\r
1679 //        case  7: if (searchValue < list[temp = high-      0x40]) high = temp;\r
1680 //        case  6: if (searchValue < list[temp = high-      0x20]) high = temp;\r
1681 //        case  5: if (searchValue < list[temp = high-      0x10]) high = temp;\r
1682 //\r
1683 //        case  4: if (searchValue < list[temp = high-       0x8]) high = temp;\r
1684 //        case  3: if (searchValue < list[temp = high-       0x4]) high = temp;\r
1685 //        case  2: if (searchValue < list[temp = high-       0x2]) high = temp;\r
1686 //        case  1: if (searchValue < list[temp = high-       0x1]) high = temp;\r
1687 //        }\r
1688 //\r
1689 //        return high;\r
1690 //    }\r
1691 //\r
1692 //    // For debugging only\r
1693 //    public int len() {\r
1694 //        return len;\r
1695 //    }\r
1696 //\r
1697 //    //----------------------------------------------------------------\r
1698 //    //----------------------------------------------------------------\r
1699 \r
1700     /**\r
1701      * Returns true if this set contains every character\r
1702      * of the given range.\r
1703      * @param start first character, inclusive, of the range\r
1704      * @param end last character, inclusive, of the range\r
1705      * @return true if the test condition is met\r
1706      * @stable ICU 2.0\r
1707      */\r
1708     public boolean contains(int start, int end) {\r
1709         if (start < MIN_VALUE || start > MAX_VALUE) {\r
1710             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));\r
1711         }\r
1712         if (end < MIN_VALUE || end > MAX_VALUE) {\r
1713             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));\r
1714         }\r
1715         //int i = -1;\r
1716         //while (true) {\r
1717         //    if (start < list[++i]) break;\r
1718         //}\r
1719         int i = findCodePoint(start);\r
1720         return ((i & 1) != 0 && end < list[i]);\r
1721     }\r
1722 \r
1723     /**\r
1724      * Returns <tt>true</tt> if this set contains the given\r
1725      * multicharacter string.\r
1726      * @param s string to be checked for containment\r
1727      * @return <tt>true</tt> if this set contains the specified string\r
1728      * @stable ICU 2.0\r
1729      */\r
1730     public final boolean contains(String s) {\r
1731 \r
1732         int cp = getSingleCP(s);\r
1733         if (cp < 0) {\r
1734             return strings.contains(s);\r
1735         } else {\r
1736             return contains(cp);\r
1737         }\r
1738     }\r
1739 \r
1740     /**\r
1741      * Returns true if this set contains all the characters and strings\r
1742      * of the given set.\r
1743      * @param b set to be checked for containment\r
1744      * @return true if the test condition is met\r
1745      * @stable ICU 2.0\r
1746      */\r
1747     public boolean containsAll(UnicodeSet b) {\r
1748       // The specified set is a subset if all of its pairs are contained in\r
1749       // this set. This implementation accesses the lists directly for speed.\r
1750       // TODO: this could be faster if size() were cached. But that would affect building speed\r
1751       // so it needs investigation.\r
1752       int[] listB = b.list;\r
1753       boolean needA = true;\r
1754       boolean needB = true;\r
1755       int aPtr = 0;\r
1756       int bPtr = 0;\r
1757       int aLen = len - 1;\r
1758       int bLen = b.len - 1;\r
1759       int startA = 0, startB = 0, limitA = 0, limitB = 0;\r
1760       while (true) {\r
1761         // double iterations are such a pain...\r
1762         if (needA) {\r
1763           if (aPtr >= aLen) {\r
1764             // ran out of A. If B is also exhausted, then break;\r
1765             if (needB && bPtr >= bLen) {\r
1766               break;\r
1767             }\r
1768             return false;\r
1769           }\r
1770           startA = list[aPtr++];\r
1771           limitA = list[aPtr++];\r
1772         }\r
1773         if (needB) {\r
1774           if (bPtr >= bLen) {\r
1775             // ran out of B. Since we got this far, we have an A and we are ok so far\r
1776             break;\r
1777           }\r
1778           startB = listB[bPtr++];\r
1779           limitB = listB[bPtr++];\r
1780         }\r
1781         // if B doesn't overlap and is greater than A, get new A\r
1782         if (startB >= limitA) {\r
1783           needA = true;\r
1784           needB = false;\r
1785           continue;\r
1786         }\r
1787         // if B is wholy contained in A, then get a new B\r
1788         if (startB >= startA && limitB <= limitA) {\r
1789           needA = false;\r
1790           needB = true;\r
1791           continue;\r
1792         }\r
1793         // all other combinations mean we fail\r
1794         return false;\r
1795       }\r
1796 \r
1797       if (!strings.containsAll(b.strings)) return false;\r
1798       return true;\r
1799   }\r
1800 \r
1801 //    /**\r
1802 //     * Returns true if this set contains all the characters and strings\r
1803 //     * of the given set.\r
1804 //     * @param c set to be checked for containment\r
1805 //     * @return true if the test condition is met\r
1806 //     * @stable ICU 2.0\r
1807 //     */\r
1808 //    public boolean containsAllOld(UnicodeSet c) {\r
1809 //        // The specified set is a subset if all of its pairs are contained in\r
1810 //        // this set.  It's possible to code this more efficiently in terms of\r
1811 //        // direct manipulation of the inversion lists if the need arises.\r
1812 //        int n = c.getRangeCount();\r
1813 //        for (int i=0; i<n; ++i) {\r
1814 //            if (!contains(c.getRangeStart(i), c.getRangeEnd(i))) {\r
1815 //                return false;\r
1816 //            }\r
1817 //        }\r
1818 //        if (!strings.containsAll(c.strings)) return false;\r
1819 //        return true;\r
1820 //    }\r
1821 \r
1822     /**\r
1823      * Returns true if there is a partition of the string such that this set contains each of the partitioned strings.\r
1824      * For example, for the Unicode set [a{bc}{cd}]<br>\r
1825      * containsAll is true for each of: "a", "bc", ""cdbca"<br>\r
1826      * containsAll is false for each of: "acb", "bcda", "bcx"<br>\r
1827      * @param s string containing characters to be checked for containment\r
1828      * @return true if the test condition is met\r
1829      * @stable ICU 2.0\r
1830      */\r
1831      public boolean containsAll(String s) {\r
1832         int cp;\r
1833         for (int i = 0; i < s.length(); i += UTF16.getCharCount(cp)) {\r
1834             cp = UTF16.charAt(s, i);\r
1835             if (!contains(cp))  {\r
1836                 if (strings.size() == 0) {\r
1837                     return false;\r
1838                 }\r
1839                 return containsAll(s, 0);\r
1840             }\r
1841         }\r
1842         return true;\r
1843     }\r
1844 \r
1845     /**\r
1846      * Recursive routine called if we fail to find a match in containsAll, and there are strings\r
1847      * @param s source string\r
1848      * @param i point to match to the end on\r
1849      * @return true if ok\r
1850      */\r
1851     private boolean containsAll(String s, int i) {\r
1852         if (i >= s.length()) {\r
1853             return true;\r
1854         }\r
1855         int  cp= UTF16.charAt(s, i);\r
1856         if (contains(cp) && containsAll(s, i+UTF16.getCharCount(cp))) {\r
1857             return true;\r
1858         }\r
1859         \r
1860         Iterator it = strings.iterator();\r
1861         while (it.hasNext()) {\r
1862             String setStr = (String)it.next();\r
1863             if (s.startsWith(setStr, i) &&  containsAll(s, i+setStr.length())) {\r
1864                 return true;\r
1865             }\r
1866         }\r
1867         return false;\r
1868         \r
1869     }\r
1870 \r
1871     /**\r
1872      * Get the Regex equivalent for this UnicodeSet\r
1873      * @return regex pattern equivalent to this UnicodeSet\r
1874      * @internal\r
1875      * @deprecated This API is ICU internal only.\r
1876      */\r
1877     public String getRegexEquivalent() {\r
1878         if (strings.size() == 0) return toString();\r
1879         StringBuffer result = new StringBuffer("(?:");\r
1880         _generatePattern(result, true, false);\r
1881         Iterator it = strings.iterator();\r
1882         while (it.hasNext()) {\r
1883             result.append('|');\r
1884             _appendToPat(result, (String) it.next(), true);\r
1885         }\r
1886         return result.append(")").toString();\r
1887     }\r
1888 \r
1889     /**\r
1890      * Returns true if this set contains none of the characters\r
1891      * of the given range.\r
1892      * @param start first character, inclusive, of the range\r
1893      * @param end last character, inclusive, of the range\r
1894      * @return true if the test condition is met\r
1895      * @stable ICU 2.0\r
1896      */\r
1897     public boolean containsNone(int start, int end) {\r
1898         if (start < MIN_VALUE || start > MAX_VALUE) {\r
1899             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));\r
1900         }\r
1901         if (end < MIN_VALUE || end > MAX_VALUE) {\r
1902             throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));\r
1903         }\r
1904         int i = -1;\r
1905         while (true) {\r
1906             if (start < list[++i]) break;\r
1907         }\r
1908         return ((i & 1) == 0 && end < list[i]);\r
1909     }\r
1910 \r
1911     /**\r
1912      * Returns true if none of the characters or strings in this UnicodeSet appears in the string.\r
1913      * For example, for the Unicode set [a{bc}{cd}]<br>\r
1914      * containsNone is true for: "xy", "cb"<br>\r
1915      * containsNone is false for: "a", "bc", "bcd"<br>\r
1916      * @param b set to be checked for containment\r
1917      * @return true if the test condition is met\r
1918      * @stable ICU 2.0\r
1919      */\r
1920     public boolean containsNone(UnicodeSet b) {\r
1921       // The specified set is a subset if some of its pairs overlap with some of this set's pairs.\r
1922       // This implementation accesses the lists directly for speed.\r
1923       int[] listB = b.list;\r
1924       boolean needA = true;\r
1925       boolean needB = true;\r
1926       int aPtr = 0;\r
1927       int bPtr = 0;\r
1928       int aLen = len - 1;\r
1929       int bLen = b.len - 1;\r
1930       int startA = 0, startB = 0, limitA = 0, limitB = 0;\r
1931       while (true) {\r
1932         // double iterations are such a pain...\r
1933         if (needA) {\r
1934           if (aPtr >= aLen) {\r
1935             // ran out of A: break so we test strings\r
1936             break;\r
1937           }\r
1938           startA = list[aPtr++];\r
1939           limitA = list[aPtr++];\r
1940         }\r
1941         if (needB) {\r
1942           if (bPtr >= bLen) {\r
1943             // ran out of B: break so we test strings\r
1944             break;\r
1945           }\r
1946           startB = listB[bPtr++];\r
1947           limitB = listB[bPtr++];\r
1948         }\r
1949         // if B is higher than any part of A, get new A\r
1950         if (startB >= limitA) {\r
1951           needA = true;\r
1952           needB = false;\r
1953           continue;\r
1954         }\r
1955         // if A is higher than any part of B, get new B\r
1956         if (startA >= limitB) {\r
1957           needA = false;\r
1958           needB = true;\r
1959           continue;\r
1960         }\r
1961         // all other combinations mean we fail\r
1962         return false;\r
1963       }\r
1964 \r
1965       if (!SortedSetRelation.hasRelation(strings, SortedSetRelation.DISJOINT, b.strings)) return false;\r
1966       return true;\r
1967   }\r
1968 \r
1969 //    /**\r
1970 //     * Returns true if none of the characters or strings in this UnicodeSet appears in the string.\r
1971 //     * For example, for the Unicode set [a{bc}{cd}]<br>\r
1972 //     * containsNone is true for: "xy", "cb"<br>\r
1973 //     * containsNone is false for: "a", "bc", "bcd"<br>\r
1974 //     * @param c set to be checked for containment\r
1975 //     * @return true if the test condition is met\r
1976 //     * @stable ICU 2.0\r
1977 //     */\r
1978 //    public boolean containsNoneOld(UnicodeSet c) {\r
1979 //        // The specified set is a subset if all of its pairs are contained in\r
1980 //        // this set.  It's possible to code this more efficiently in terms of\r
1981 //        // direct manipulation of the inversion lists if the need arises.\r
1982 //        int n = c.getRangeCount();\r
1983 //        for (int i=0; i<n; ++i) {\r
1984 //            if (!containsNone(c.getRangeStart(i), c.getRangeEnd(i))) {\r
1985 //                return false;\r
1986 //            }\r
1987 //        }\r
1988 //        if (!SortedSetRelation.hasRelation(strings, SortedSetRelation.DISJOINT, c.strings)) return false;\r
1989 //        return true;\r
1990 //    }\r
1991 \r
1992     /**\r
1993      * Returns true if this set contains none of the characters\r
1994      * of the given string.\r
1995      * @param s string containing characters to be checked for containment\r
1996      * @return true if the test condition is met\r
1997      * @stable ICU 2.0\r
1998      */\r
1999     public boolean containsNone(String s) {\r
2000         int cp;\r
2001         for (int i = 0; i < s.length(); i += UTF16.getCharCount(cp)) {\r
2002             cp = UTF16.charAt(s, i);\r
2003             if (contains(cp)) return false;\r
2004         }\r
2005         if (strings.size() == 0) return true;\r
2006         // do a last check to make sure no strings are in.\r
2007         for (Iterator it = strings.iterator(); it.hasNext();) {\r
2008             String item = (String)it.next();\r
2009             if (s.indexOf(item) >= 0) return false;\r
2010         }\r
2011         return true;\r
2012     }\r
2013 \r
2014     /**\r
2015      * Returns true if this set contains one or more of the characters\r
2016      * in the given range.\r
2017      * @param start first character, inclusive, of the range\r
2018      * @param end last character, inclusive, of the range\r
2019      * @return true if the condition is met\r
2020      * @stable ICU 2.0\r
2021      */\r
2022     public final boolean containsSome(int start, int end) {\r
2023         return !containsNone(start, end);\r
2024     }\r
2025 \r
2026     /**\r
2027      * Returns true if this set contains one or more of the characters\r
2028      * and strings of the given set.\r
2029      * @param s set to be checked for containment\r
2030      * @return true if the condition is met\r
2031      * @stable ICU 2.0\r
2032      */\r
2033     public final boolean containsSome(UnicodeSet s) {\r
2034         return !containsNone(s);\r
2035     }\r
2036 \r
2037     /**\r
2038      * Returns true if this set contains one or more of the characters\r
2039      * of the given string.\r
2040      * @param s string containing characters to be checked for containment\r
2041      * @return true if the condition is met\r
2042      * @stable ICU 2.0\r
2043      */\r
2044     public final boolean containsSome(String s) {\r
2045         return !containsNone(s);\r
2046     }\r
2047 \r
2048 \r
2049     /**\r
2050      * Adds all of the elements in the specified set to this set if\r
2051      * they're not already present.  This operation effectively\r
2052      * modifies this set so that its value is the <i>union</i> of the two\r
2053      * sets.  The behavior of this operation is unspecified if the specified\r
2054      * collection is modified while the operation is in progress.\r
2055      *\r
2056      * @param c set whose elements are to be added to this set.\r
2057      * @stable ICU 2.0\r
2058      */\r
2059     public UnicodeSet addAll(UnicodeSet c) {\r
2060         checkFrozen();\r
2061         add(c.list, c.len, 0);\r
2062         strings.addAll(c.strings);\r
2063         return this;\r
2064     }\r
2065 \r
2066     /**\r
2067      * Retains only the elements in this set that are contained in the\r
2068      * specified set.  In other words, removes from this set all of\r
2069      * its elements that are not contained in the specified set.  This\r
2070      * operation effectively modifies this set so that its value is\r
2071      * the <i>intersection</i> of the two sets.\r
2072      *\r
2073      * @param c set that defines which elements this set will retain.\r
2074      * @stable ICU 2.0\r
2075      */\r
2076     public UnicodeSet retainAll(UnicodeSet c) {\r
2077         checkFrozen();\r
2078         retain(c.list, c.len, 0);\r
2079         strings.retainAll(c.strings);\r
2080         return this;\r
2081     }\r
2082 \r
2083     /**\r
2084      * Removes from this set all of its elements that are contained in the\r
2085      * specified set.  This operation effectively modifies this\r
2086      * set so that its value is the <i>asymmetric set difference</i> of\r
2087      * the two sets.\r
2088      *\r
2089      * @param c set that defines which elements will be removed from\r
2090      *          this set.\r
2091      * @stable ICU 2.0\r
2092      */\r
2093     public UnicodeSet removeAll(UnicodeSet c) {\r
2094         checkFrozen();\r
2095         retain(c.list, c.len, 2);\r
2096         strings.removeAll(c.strings);\r
2097         return this;\r
2098     }\r
2099 \r
2100     /**\r
2101      * Complements in this set all elements contained in the specified\r
2102      * set.  Any character in the other set will be removed if it is\r
2103      * in this set, or will be added if it is not in this set.\r
2104      *\r
2105      * @param c set that defines which elements will be complemented from\r
2106      *          this set.\r
2107      * @stable ICU 2.0\r
2108      */\r
2109     public UnicodeSet complementAll(UnicodeSet c) {\r
2110         checkFrozen();\r
2111         xor(c.list, c.len, 0);\r
2112         SortedSetRelation.doOperation(strings, SortedSetRelation.COMPLEMENTALL, c.strings);\r
2113         return this;\r
2114     }\r
2115 \r
2116     /**\r
2117      * Removes all of the elements from this set.  This set will be\r
2118      * empty after this call returns.\r
2119      * @stable ICU 2.0\r
2120      */\r
2121     public UnicodeSet clear() {\r
2122         checkFrozen();\r
2123         list[0] = HIGH;\r
2124         len = 1;\r
2125         pat = null;\r
2126         strings.clear();\r
2127         return this;\r
2128     }\r
2129 \r
2130     /**\r
2131      * Iteration method that returns the number of ranges contained in\r
2132      * this set.\r
2133      * @see #getRangeStart\r
2134      * @see #getRangeEnd\r
2135      * @stable ICU 2.0\r
2136      */\r
2137     public int getRangeCount() {\r
2138         return len/2;\r
2139     }\r
2140 \r
2141     /**\r
2142      * Iteration method that returns the first character in the\r
2143      * specified range of this set.\r
2144      * @exception ArrayIndexOutOfBoundsException if index is outside\r
2145      * the range <code>0..getRangeCount()-1</code>\r
2146      * @see #getRangeCount\r
2147      * @see #getRangeEnd\r
2148      * @stable ICU 2.0\r
2149      */\r
2150     public int getRangeStart(int index) {\r
2151         return list[index*2];\r
2152     }\r
2153 \r
2154     /**\r
2155      * Iteration method that returns the last character in the\r
2156      * specified range of this set.\r
2157      * @exception ArrayIndexOutOfBoundsException if index is outside\r
2158      * the range <code>0..getRangeCount()-1</code>\r
2159      * @see #getRangeStart\r
2160      * @see #getRangeEnd\r
2161      * @stable ICU 2.0\r
2162      */\r
2163     public int getRangeEnd(int index) {\r
2164         return (list[index*2 + 1] - 1);\r
2165     }\r
2166 \r
2167     /**\r
2168      * Reallocate this objects internal structures to take up the least\r
2169      * possible space, without changing this object's value.\r
2170      * @stable ICU 2.0\r
2171      */\r
2172     public UnicodeSet compact() {\r
2173         checkFrozen();\r
2174         if (len != list.length) {\r
2175             int[] temp = new int[len];\r
2176             System.arraycopy(list, 0, temp, 0, len);\r
2177             list = temp;\r
2178         }\r
2179         rangeList = null;\r
2180         buffer = null;\r
2181         return this;\r
2182     }\r
2183 \r
2184     /**\r
2185      * Compares the specified object with this set for equality.  Returns\r
2186      * <tt>true</tt> if the specified object is also a set, the two sets\r
2187      * have the same size, and every member of the specified set is\r
2188      * contained in this set (or equivalently, every member of this set is\r
2189      * contained in the specified set).\r
2190      *\r
2191      * @param o Object to be compared for equality with this set.\r
2192      * @return <tt>true</tt> if the specified Object is equal to this set.\r
2193      * @stable ICU 2.0\r
2194      */\r
2195     public boolean equals(Object o) {\r
2196         try {\r
2197             UnicodeSet that = (UnicodeSet) o;\r
2198             if (len != that.len) return false;\r
2199             for (int i = 0; i < len; ++i) {\r
2200                 if (list[i] != that.list[i]) return false;\r
2201             }\r
2202             if (!strings.equals(that.strings)) return false;\r
2203         } catch (Exception e) {\r
2204             return false;\r
2205         }\r
2206         return true;\r
2207     }\r
2208 \r
2209     /**\r
2210      * Returns the hash code value for this set.\r
2211      *\r
2212      * @return the hash code value for this set.\r
2213      * @see java.lang.Object#hashCode()\r
2214      * @stable ICU 2.0\r
2215      */\r
2216     public int hashCode() {\r
2217         int result = len;\r
2218         for (int i = 0; i < len; ++i) {\r
2219             result *= 1000003;\r
2220             result += list[i];\r
2221         }\r
2222         return result;\r
2223     }\r
2224 \r
2225     /**\r
2226      * Return a programmer-readable string representation of this object.\r
2227      * @stable ICU 2.0\r
2228      */\r
2229     public String toString() {\r
2230         return toPattern(true);\r
2231     }\r
2232 \r
2233     //----------------------------------------------------------------\r
2234     // Implementation: Pattern parsing\r
2235     //----------------------------------------------------------------\r
2236 \r
2237     /**\r
2238      * Parses the given pattern, starting at the given position.  The character\r
2239      * at pattern.charAt(pos.getIndex()) must be '[', or the parse fails.\r
2240      * Parsing continues until the corresponding closing ']'.  If a syntax error\r
2241      * is encountered between the opening and closing brace, the parse fails.\r
2242      * Upon return from a successful parse, the ParsePosition is updated to\r
2243      * point to the character following the closing ']', and an inversion\r
2244      * list for the parsed pattern is returned.  This method\r
2245      * calls itself recursively to parse embedded subpatterns.\r
2246      *\r
2247      * @param pattern the string containing the pattern to be parsed.  The\r
2248      * portion of the string from pos.getIndex(), which must be a '[', to the\r
2249      * corresponding closing ']', is parsed.\r
2250      * @param pos upon entry, the position at which to being parsing.  The\r
2251      * character at pattern.charAt(pos.getIndex()) must be a '['.  Upon return\r
2252      * from a successful parse, pos.getIndex() is either the character after the\r
2253      * closing ']' of the parsed pattern, or pattern.length() if the closing ']'\r
2254      * is the last character of the pattern string.\r
2255      * @return an inversion list for the parsed substring\r
2256      * of <code>pattern</code>\r
2257      * @exception java.lang.IllegalArgumentException if the parse fails.\r
2258      * @internal\r
2259      * @deprecated - for internal use only\r
2260      */\r
2261     public UnicodeSet applyPattern(String pattern,\r
2262                       ParsePosition pos,\r
2263                       SymbolTable symbols,\r
2264                       int options) {\r
2265 \r
2266         // Need to build the pattern in a temporary string because\r
2267         // _applyPattern calls add() etc., which set pat to empty.\r
2268         boolean parsePositionWasNull = pos == null;\r
2269         if (parsePositionWasNull) {\r
2270             pos = new ParsePosition(0);\r
2271         }\r
2272 \r
2273         StringBuffer rebuiltPat = new StringBuffer();\r
2274         RuleCharacterIterator chars =\r
2275             new RuleCharacterIterator(pattern, symbols, pos);\r
2276         applyPattern(chars, symbols, rebuiltPat, options);\r
2277         if (chars.inVariable()) {\r
2278             syntaxError(chars, "Extra chars in variable value");\r
2279         }\r
2280         pat = rebuiltPat.toString();\r
2281         if (parsePositionWasNull) {\r
2282             int i = pos.getIndex();\r
2283 \r
2284             // Skip over trailing whitespace\r
2285             if ((options & IGNORE_SPACE) != 0) {\r
2286                 i = Utility.skipWhitespace(pattern, i);\r
2287             }\r
2288 \r
2289             if (i != pattern.length()) {\r
2290                 throw new IllegalArgumentException("Parse of \"" + pattern +\r
2291                                                    "\" failed at " + i);\r
2292             }\r
2293         }\r
2294         return this;\r
2295     }\r
2296 \r
2297     /**\r
2298      * Parse the pattern from the given RuleCharacterIterator.  The\r
2299      * iterator is advanced over the parsed pattern.\r
2300      * @param chars iterator over the pattern characters.  Upon return\r
2301      * it will be advanced to the first character after the parsed\r
2302      * pattern, or the end of the iteration if all characters are\r
2303      * parsed.\r
2304      * @param symbols symbol table to use to parse and dereference\r
2305      * variables, or null if none.\r
2306      * @param rebuiltPat the pattern that was parsed, rebuilt or\r
2307      * copied from the input pattern, as appropriate.\r
2308      * @param options a bit mask of zero or more of the following:\r
2309      * IGNORE_SPACE, CASE.\r
2310      */\r
2311     void applyPattern(RuleCharacterIterator chars, SymbolTable symbols,\r
2312                       StringBuffer rebuiltPat, int options) {\r
2313 \r
2314         // Syntax characters: [ ] ^ - & { }\r
2315 \r
2316         // Recognized special forms for chars, sets: c-c s-s s&s\r
2317 \r
2318         int opts = RuleCharacterIterator.PARSE_VARIABLES |\r
2319                    RuleCharacterIterator.PARSE_ESCAPES;\r
2320         if ((options & IGNORE_SPACE) != 0) {\r
2321             opts |= RuleCharacterIterator.SKIP_WHITESPACE;\r
2322         }\r
2323 \r
2324         StringBuffer patBuf = new StringBuffer(), buf = null;\r
2325         boolean usePat = false;\r
2326         UnicodeSet scratch = null;\r
2327         Object backup = null;\r
2328 \r
2329         // mode: 0=before [, 1=between [...], 2=after ]\r
2330         // lastItem: 0=none, 1=char, 2=set\r
2331         int lastItem = 0, lastChar = 0, mode = 0;\r
2332         char op = 0;\r
2333 \r
2334         boolean invert = false;\r
2335 \r
2336         clear();\r
2337 \r
2338         while (mode != 2 && !chars.atEnd()) {\r
2339             if (false) {\r
2340                 // Debugging assertion\r
2341                 if (!((lastItem == 0 && op == 0) ||\r
2342                       (lastItem == 1 && (op == 0 || op == '-')) ||\r
2343                       (lastItem == 2 && (op == 0 || op == '-' || op == '&')))) {\r
2344                     throw new IllegalArgumentException();\r
2345                 }\r
2346             }\r
2347 \r
2348             int c = 0;\r
2349             boolean literal = false;\r
2350             UnicodeSet nested = null;\r
2351 \r
2352             // -------- Check for property pattern\r
2353 \r
2354             // setMode: 0=none, 1=unicodeset, 2=propertypat, 3=preparsed\r
2355             int setMode = 0;\r
2356             if (resemblesPropertyPattern(chars, opts)) {\r
2357                 setMode = 2;\r
2358             }\r
2359 \r
2360             // -------- Parse '[' of opening delimiter OR nested set.\r
2361             // If there is a nested set, use `setMode' to define how\r
2362             // the set should be parsed.  If the '[' is part of the\r
2363             // opening delimiter for this pattern, parse special\r
2364             // strings "[", "[^", "[-", and "[^-".  Check for stand-in\r
2365             // characters representing a nested set in the symbol\r
2366             // table.\r
2367 \r
2368             else {\r
2369                 // Prepare to backup if necessary\r
2370                 backup = chars.getPos(backup);\r
2371                 c = chars.next(opts);\r
2372                 literal = chars.isEscaped();\r
2373 \r
2374                 if (c == '[' && !literal) {\r
2375                     if (mode == 1) {\r
2376                         chars.setPos(backup); // backup\r
2377                         setMode = 1;\r
2378                     } else {\r
2379                         // Handle opening '[' delimiter\r
2380                         mode = 1;\r
2381                         patBuf.append('[');\r
2382                         backup = chars.getPos(backup); // prepare to backup\r
2383                         c = chars.next(opts);\r
2384                         literal = chars.isEscaped();\r
2385                         if (c == '^' && !literal) {\r
2386                             invert = true;\r
2387                             patBuf.append('^');\r
2388                             backup = chars.getPos(backup); // prepare to backup\r
2389                             c = chars.next(opts);\r
2390                             literal = chars.isEscaped();\r
2391                         }\r
2392                         // Fall through to handle special leading '-';\r
2393                         // otherwise restart loop for nested [], \p{}, etc.\r
2394                         if (c == '-') {\r
2395                             literal = true;\r
2396                             // Fall through to handle literal '-' below\r
2397                         } else {\r
2398                             chars.setPos(backup); // backup\r
2399                             continue;\r
2400                         }\r
2401                     }\r
2402                 } else if (symbols != null) {\r
2403                      UnicodeMatcher m = symbols.lookupMatcher(c); // may be null\r
2404                      if (m != null) {\r
2405                          try {\r
2406                              nested = (UnicodeSet) m;\r
2407                              setMode = 3;\r
2408                          } catch (ClassCastException e) {\r
2409                              syntaxError(chars, "Syntax error");\r
2410                          }\r
2411                      }\r
2412                 }\r
2413             }\r
2414 \r
2415             // -------- Handle a nested set.  This either is inline in\r
2416             // the pattern or represented by a stand-in that has\r
2417             // previously been parsed and was looked up in the symbol\r
2418             // table.\r
2419 \r
2420             if (setMode != 0) {\r
2421                 if (lastItem == 1) {\r
2422                     if (op != 0) {\r
2423                         syntaxError(chars, "Char expected after operator");\r
2424                     }\r
2425                     add_unchecked(lastChar, lastChar);\r
2426                     _appendToPat(patBuf, lastChar, false);\r
2427                     lastItem = op = 0;\r
2428                 }\r
2429 \r
2430                 if (op == '-' || op == '&') {\r
2431                     patBuf.append(op);\r
2432                 }\r
2433 \r
2434                 if (nested == null) {\r
2435                     if (scratch == null) scratch = new UnicodeSet();\r
2436                     nested = scratch;\r
2437                 }\r
2438                 switch (setMode) {\r
2439                 case 1:\r
2440                     nested.applyPattern(chars, symbols, patBuf, options);\r
2441                     break;\r
2442                 case 2:\r
2443                     chars.skipIgnored(opts);\r
2444                     nested.applyPropertyPattern(chars, patBuf, symbols);\r
2445                     break;\r
2446                 case 3: // `nested' already parsed\r
2447                     nested._toPattern(patBuf, false);\r
2448                     break;\r
2449                 }\r
2450 \r
2451                 usePat = true;\r
2452 \r
2453                 if (mode == 0) {\r
2454                     // Entire pattern is a category; leave parse loop\r
2455                     set(nested);\r
2456                     mode = 2;\r
2457                     break;\r
2458                 }\r
2459 \r
2460                 switch (op) {\r
2461                 case '-':\r
2462                     removeAll(nested);\r
2463                     break;\r
2464                 case '&':\r
2465                     retainAll(nested);\r
2466                     break;\r
2467                 case 0:\r
2468                     addAll(nested);\r
2469                     break;\r
2470                 }\r
2471 \r
2472                 op = 0;\r
2473                 lastItem = 2;\r
2474 \r
2475                 continue;\r
2476             }\r
2477 \r
2478             if (mode == 0) {\r
2479                 syntaxError(chars, "Missing '['");\r
2480             }\r
2481 \r
2482             // -------- Parse special (syntax) characters.  If the\r
2483             // current character is not special, or if it is escaped,\r
2484             // then fall through and handle it below.\r
2485 \r
2486             if (!literal) {\r
2487                 switch (c) {\r
2488                 case ']':\r
2489                     if (lastItem == 1) {\r
2490                         add_unchecked(lastChar, lastChar);\r
2491                         _appendToPat(patBuf, lastChar, false);\r
2492                     }\r
2493                     // Treat final trailing '-' as a literal\r
2494                     if (op == '-') {\r
2495                         add_unchecked(op, op);\r
2496                         patBuf.append(op);\r
2497                     } else if (op == '&') {\r
2498                         syntaxError(chars, "Trailing '&'");\r
2499                     }\r
2500                     patBuf.append(']');\r
2501                     mode = 2;\r
2502                     continue;\r
2503                 case '-':\r
2504                     if (op == 0) {\r
2505                         if (lastItem != 0) {\r
2506                             op = (char) c;\r
2507                             continue;\r
2508                         } else {\r
2509                             // Treat final trailing '-' as a literal\r
2510                             add_unchecked(c, c);\r
2511                             c = chars.next(opts);\r
2512                             literal = chars.isEscaped();\r
2513                             if (c == ']' && !literal) {\r
2514                                 patBuf.append("-]");\r
2515                                 mode = 2;\r
2516                                 continue;\r
2517                             }\r
2518                         }\r
2519                     }\r
2520                     syntaxError(chars, "'-' not after char or set");\r
2521                 case '&':\r
2522                     if (lastItem == 2 && op == 0) {\r
2523                         op = (char) c;\r
2524                         continue;\r
2525                     }\r
2526                     syntaxError(chars, "'&' not after set");\r
2527                 case '^':\r
2528                     syntaxError(chars, "'^' not after '['");\r
2529                 case '{':\r
2530                     if (op != 0) {\r
2531                         syntaxError(chars, "Missing operand after operator");\r
2532                     }\r
2533                     if (lastItem == 1) {\r
2534                         add_unchecked(lastChar, lastChar);\r
2535                         _appendToPat(patBuf, lastChar, false);\r
2536                     }\r
2537                     lastItem = 0;\r
2538                     if (buf == null) {\r
2539                         buf = new StringBuffer();\r
2540                     } else {\r
2541                         buf.setLength(0);\r
2542                     }\r
2543                     boolean ok = false;\r
2544                     while (!chars.atEnd()) {\r
2545                         c = chars.next(opts);\r
2546                         literal = chars.isEscaped();\r
2547                         if (c == '}' && !literal) {\r
2548                             ok = true;\r
2549                             break;\r
2550                         }\r
2551                         UTF16.append(buf, c);\r
2552                     }\r
2553                     if (buf.length() < 1 || !ok) {\r
2554                         syntaxError(chars, "Invalid multicharacter string");\r
2555                     }\r
2556                     // We have new string. Add it to set and continue;\r
2557                     // we don't need to drop through to the further\r
2558                     // processing\r
2559                     add(buf.toString());\r
2560                     patBuf.append('{');\r
2561                     _appendToPat(patBuf, buf.toString(), false);\r
2562                     patBuf.append('}');\r
2563                     continue;\r
2564                 case SymbolTable.SYMBOL_REF:\r
2565                     //         symbols  nosymbols\r
2566                     // [a-$]   error    error (ambiguous)\r
2567                     // [a$]    anchor   anchor\r
2568                     // [a-$x]  var "x"* literal '$'\r
2569                     // [a-$.]  error    literal '$'\r
2570                     // *We won't get here in the case of var "x"\r
2571                     backup = chars.getPos(backup);\r
2572                     c = chars.next(opts);\r
2573                     literal = chars.isEscaped();\r
2574                     boolean anchor = (c == ']' && !literal);\r
2575                     if (symbols == null && !anchor) {\r
2576                         c = SymbolTable.SYMBOL_REF;\r
2577                         chars.setPos(backup);\r
2578                         break; // literal '$'\r
2579                     }\r
2580                     if (anchor && op == 0) {\r
2581                         if (lastItem == 1) {\r
2582                             add_unchecked(lastChar, lastChar);\r
2583                             _appendToPat(patBuf, lastChar, false);\r
2584                         }\r
2585                         add_unchecked(UnicodeMatcher.ETHER);\r
2586                         usePat = true;\r
2587                         patBuf.append(SymbolTable.SYMBOL_REF).append(']');\r
2588                         mode = 2;\r
2589                         continue;\r
2590                     }\r
2591                     syntaxError(chars, "Unquoted '$'");\r
2592                 default:\r
2593                     break;\r
2594                 }\r
2595             }\r
2596 \r
2597             // -------- Parse literal characters.  This includes both\r
2598             // escaped chars ("\u4E01") and non-syntax characters\r
2599             // ("a").\r
2600 \r
2601             switch (lastItem) {\r
2602             case 0:\r
2603                 lastItem = 1;\r
2604                 lastChar = c;\r
2605                 break;\r
2606             case 1:\r
2607                 if (op == '-') {\r
2608                     if (lastChar >= c) {\r
2609                         // Don't allow redundant (a-a) or empty (b-a) ranges;\r
2610                         // these are most likely typos.\r
2611                         syntaxError(chars, "Invalid range");\r
2612                     }\r
2613                     add_unchecked(lastChar, c);\r
2614                     _appendToPat(patBuf, lastChar, false);\r
2615                     patBuf.append(op);\r
2616                     _appendToPat(patBuf, c, false);\r
2617                     lastItem = op = 0;\r
2618                 } else {\r
2619                     add_unchecked(lastChar, lastChar);\r
2620                     _appendToPat(patBuf, lastChar, false);\r
2621                     lastChar = c;\r
2622                 }\r
2623                 break;\r
2624             case 2:\r
2625                 if (op != 0) {\r
2626                     syntaxError(chars, "Set expected after operator");\r
2627                 }\r
2628                 lastChar = c;\r
2629                 lastItem = 1;\r
2630                 break;\r
2631             }\r
2632         }\r
2633 \r
2634         if (mode != 2) {\r
2635             syntaxError(chars, "Missing ']'");\r
2636         }\r
2637 \r
2638         chars.skipIgnored(opts);\r
2639 \r
2640         /**\r
2641          * Handle global flags (invert, case insensitivity).  If this\r
2642          * pattern should be compiled case-insensitive, then we need\r
2643          * to close over case BEFORE COMPLEMENTING.  This makes\r
2644          * patterns like /[^abc]/i work.\r
2645          */\r
2646         if ((options & CASE) != 0) {\r
2647             closeOver(CASE);\r
2648         }\r
2649         if (invert) {\r
2650             complement();\r
2651         }\r
2652 \r
2653         // Use the rebuilt pattern (pat) only if necessary.  Prefer the\r
2654         // generated pattern.\r
2655         if (usePat) {\r
2656             rebuiltPat.append(patBuf.toString());\r
2657         } else {\r
2658             _generatePattern(rebuiltPat, false, true);\r
2659         }\r
2660     }\r
2661 \r
2662     private static void syntaxError(RuleCharacterIterator chars, String msg) {\r
2663         throw new IllegalArgumentException("Error: " + msg + " at \"" +\r
2664                                            Utility.escape(chars.toString()) +\r
2665                                            '"');\r
2666     }\r
2667 \r
2668     /**\r
2669      * Add the contents of the UnicodeSet (as strings) into a collection.\r
2670      * @param target collection to add into\r
2671      * @stable ICU 2.8\r
2672      */\r
2673     public void addAllTo(Collection target) {\r
2674         UnicodeSetIterator it = new UnicodeSetIterator(this);\r
2675         while (it.next()) {\r
2676             target.add(it.getString());\r
2677         }\r
2678     }\r
2679 \r
2680     /**\r
2681      * Add the contents of the collection (as strings) into this UnicodeSet.\r
2682      * @param source the collection to add\r
2683      * @stable ICU 2.8\r
2684      */\r
2685     public void addAll(Collection source) {\r
2686         checkFrozen();\r
2687         Iterator it = source.iterator();\r
2688         while (it.hasNext()) {\r
2689             add(it.next().toString());\r
2690         }\r
2691     }\r
2692 \r
2693     //----------------------------------------------------------------\r
2694     // Implementation: Utility methods\r
2695     //----------------------------------------------------------------\r
2696 \r
2697     private void ensureCapacity(int newLen) {\r
2698         if (newLen <= list.length) return;\r
2699         int[] temp = new int[newLen + GROW_EXTRA];\r
2700         System.arraycopy(list, 0, temp, 0, len);\r
2701         list = temp;\r
2702     }\r
2703 \r
2704     private void ensureBufferCapacity(int newLen) {\r
2705         if (buffer != null && newLen <= buffer.length) return;\r
2706         buffer = new int[newLen + GROW_EXTRA];\r
2707     }\r
2708 \r
2709     /**\r
2710      * Assumes start <= end.\r
2711      */\r
2712     private int[] range(int start, int end) {\r
2713         if (rangeList == null) {\r
2714             rangeList = new int[] { start, end+1, HIGH };\r
2715         } else {\r
2716             rangeList[0] = start;\r
2717             rangeList[1] = end+1;\r
2718         }\r
2719         return rangeList;\r
2720     }\r
2721 \r
2722     //----------------------------------------------------------------\r
2723     // Implementation: Fundamental operations\r
2724     //----------------------------------------------------------------\r
2725 \r
2726     // polarity = 0, 3 is normal: x xor y\r
2727     // polarity = 1, 2: x xor ~y == x === y\r
2728 \r
2729     private UnicodeSet xor(int[] other, int otherLen, int polarity) {\r
2730         ensureBufferCapacity(len + otherLen);\r
2731         int i = 0, j = 0, k = 0;\r
2732         int a = list[i++];\r
2733         int b;\r
2734         if (polarity == 1 || polarity == 2) {\r
2735             b = LOW;\r
2736             if (other[j] == LOW) { // skip base if already LOW\r
2737                 ++j;\r
2738                 b = other[j];\r
2739             }\r
2740         } else {\r
2741             b = other[j++];\r
2742         }\r
2743         // simplest of all the routines\r
2744         // sort the values, discarding identicals!\r
2745         while (true) {\r
2746             if (a < b) {\r
2747                 buffer[k++] = a;\r
2748                 a = list[i++];\r
2749             } else if (b < a) {\r
2750                 buffer[k++] = b;\r
2751                 b = other[j++];\r
2752             } else if (a != HIGH) { // at this point, a == b\r
2753                 // discard both values!\r
2754                 a = list[i++];\r
2755                 b = other[j++];\r
2756             } else { // DONE!\r
2757                 buffer[k++] = HIGH;\r
2758                 len = k;\r
2759                 break;\r
2760             }\r
2761         }\r
2762         // swap list and buffer\r
2763         int[] temp = list;\r
2764         list = buffer;\r
2765         buffer = temp;\r
2766         pat = null;\r
2767         return this;\r
2768     }\r
2769 \r
2770     // polarity = 0 is normal: x union y\r
2771     // polarity = 2: x union ~y\r
2772     // polarity = 1: ~x union y\r
2773     // polarity = 3: ~x union ~y\r
2774 \r
2775     private UnicodeSet add(int[] other, int otherLen, int polarity) {\r
2776         ensureBufferCapacity(len + otherLen);\r
2777         int i = 0, j = 0, k = 0;\r
2778         int a = list[i++];\r
2779         int b = other[j++];\r
2780         // change from xor is that we have to check overlapping pairs\r
2781         // polarity bit 1 means a is second, bit 2 means b is.\r
2782         main:\r
2783         while (true) {\r
2784             switch (polarity) {\r
2785               case 0: // both first; take lower if unequal\r
2786                 if (a < b) { // take a\r
2787                     // Back up over overlapping ranges in buffer[]\r
2788                     if (k > 0 && a <= buffer[k-1]) {\r
2789                         // Pick latter end value in buffer[] vs. list[]\r
2790                         a = max(list[i], buffer[--k]);\r
2791                     } else {\r
2792                         // No overlap\r
2793                         buffer[k++] = a;\r
2794                         a = list[i];\r
2795                     }\r
2796                     i++; // Common if/else code factored out\r
2797                     polarity ^= 1;\r
2798                 } else if (b < a) { // take b\r
2799                     if (k > 0 && b <= buffer[k-1]) {\r
2800                         b = max(other[j], buffer[--k]);\r
2801                     } else {\r
2802                         buffer[k++] = b;\r
2803                         b = other[j];\r
2804                     }\r
2805                     j++;\r
2806                     polarity ^= 2;\r
2807                 } else { // a == b, take a, drop b\r
2808                     if (a == HIGH) break main;\r
2809                     // This is symmetrical; it doesn't matter if\r
2810                     // we backtrack with a or b. - liu\r
2811                     if (k > 0 && a <= buffer[k-1]) {\r
2812                         a = max(list[i], buffer[--k]);\r
2813                     } else {\r
2814                         // No overlap\r
2815                         buffer[k++] = a;\r
2816                         a = list[i];\r
2817                     }\r
2818                     i++;\r
2819                     polarity ^= 1;\r
2820                     b = other[j++]; polarity ^= 2;\r
2821                 }\r
2822                 break;\r
2823               case 3: // both second; take higher if unequal, and drop other\r
2824                 if (b <= a) { // take a\r
2825                     if (a == HIGH) break main;\r
2826                     buffer[k++] = a;\r
2827                 } else { // take b\r
2828                     if (b == HIGH) break main;\r
2829                     buffer[k++] = b;\r
2830                 }\r
2831                 a = list[i++]; polarity ^= 1;   // factored common code\r
2832                 b = other[j++]; polarity ^= 2;\r
2833                 break;\r
2834               case 1: // a second, b first; if b < a, overlap\r
2835                 if (a < b) { // no overlap, take a\r
2836                     buffer[k++] = a; a = list[i++]; polarity ^= 1;\r
2837                 } else if (b < a) { // OVERLAP, drop b\r
2838                     b = other[j++]; polarity ^= 2;\r
2839                 } else { // a == b, drop both!\r
2840                     if (a == HIGH) break main;\r
2841                     a = list[i++]; polarity ^= 1;\r
2842                     b = other[j++]; polarity ^= 2;\r
2843                 }\r
2844                 break;\r
2845               case 2: // a first, b second; if a < b, overlap\r
2846                 if (b < a) { // no overlap, take b\r
2847                     buffer[k++] = b; b = other[j++]; polarity ^= 2;\r
2848                 } else  if (a < b) { // OVERLAP, drop a\r
2849                     a = list[i++]; polarity ^= 1;\r
2850                 } else { // a == b, drop both!\r
2851                     if (a == HIGH) break main;\r
2852                     a = list[i++]; polarity ^= 1;\r
2853                     b = other[j++]; polarity ^= 2;\r
2854                 }\r
2855                 break;\r
2856             }\r
2857         }\r
2858         buffer[k++] = HIGH;    // terminate\r
2859         len = k;\r
2860         // swap list and buffer\r
2861         int[] temp = list;\r
2862         list = buffer;\r
2863         buffer = temp;\r
2864         pat = null;\r
2865         return this;\r
2866     }\r
2867 \r
2868     // polarity = 0 is normal: x intersect y\r
2869     // polarity = 2: x intersect ~y == set-minus\r
2870     // polarity = 1: ~x intersect y\r
2871     // polarity = 3: ~x intersect ~y\r
2872 \r
2873     private UnicodeSet retain(int[] other, int otherLen, int polarity) {\r
2874         ensureBufferCapacity(len + otherLen);\r
2875         int i = 0, j = 0, k = 0;\r
2876         int a = list[i++];\r
2877         int b = other[j++];\r
2878         // change from xor is that we have to check overlapping pairs\r
2879         // polarity bit 1 means a is second, bit 2 means b is.\r
2880         main:\r
2881         while (true) {\r
2882             switch (polarity) {\r
2883               case 0: // both first; drop the smaller\r
2884                 if (a < b) { // drop a\r
2885                     a = list[i++]; polarity ^= 1;\r
2886                 } else if (b < a) { // drop b\r
2887                     b = other[j++]; polarity ^= 2;\r
2888                 } else { // a == b, take one, drop other\r
2889                     if (a == HIGH) break main;\r
2890                     buffer[k++] = a; a = list[i++]; polarity ^= 1;\r
2891                     b = other[j++]; polarity ^= 2;\r
2892                 }\r
2893                 break;\r
2894               case 3: // both second; take lower if unequal\r
2895                 if (a < b) { // take a\r
2896                     buffer[k++] = a; a = list[i++]; polarity ^= 1;\r
2897                 } else if (b < a) { // take b\r
2898                     buffer[k++] = b; b = other[j++]; polarity ^= 2;\r
2899                 } else { // a == b, take one, drop other\r
2900                     if (a == HIGH) break main;\r
2901                     buffer[k++] = a; a = list[i++]; polarity ^= 1;\r
2902                     b = other[j++]; polarity ^= 2;\r
2903                 }\r
2904                 break;\r
2905               case 1: // a second, b first;\r
2906                 if (a < b) { // NO OVERLAP, drop a\r
2907                     a = list[i++]; polarity ^= 1;\r
2908                 } else if (b < a) { // OVERLAP, take b\r
2909                     buffer[k++] = b; b = other[j++]; polarity ^= 2;\r
2910                 } else { // a == b, drop both!\r
2911                     if (a == HIGH) break main;\r
2912                     a = list[i++]; polarity ^= 1;\r
2913                     b = other[j++]; polarity ^= 2;\r
2914                 }\r
2915                 break;\r
2916               case 2: // a first, b second; if a < b, overlap\r
2917                 if (b < a) { // no overlap, drop b\r
2918                     b = other[j++]; polarity ^= 2;\r
2919                 } else  if (a < b) { // OVERLAP, take a\r
2920                     buffer[k++] = a; a = list[i++]; polarity ^= 1;\r
2921                 } else { // a == b, drop both!\r
2922                     if (a == HIGH) break main;\r
2923                     a = list[i++]; polarity ^= 1;\r
2924                     b = other[j++]; polarity ^= 2;\r
2925                 }\r
2926                 break;\r
2927             }\r
2928         }\r
2929         buffer[k++] = HIGH;    // terminate\r
2930         len = k;\r
2931         // swap list and buffer\r
2932         int[] temp = list;\r
2933         list = buffer;\r
2934         buffer = temp;\r
2935         pat = null;\r
2936         return this;\r
2937     }\r
2938 \r
2939     private static final int max(int a, int b) {\r
2940         return (a > b) ? a : b;\r
2941     }\r
2942 \r
2943     //----------------------------------------------------------------\r
2944     // Generic filter-based scanning code\r
2945     //----------------------------------------------------------------\r
2946 \r
2947     private static interface Filter {\r
2948         boolean contains(int codePoint);\r
2949     }\r
2950 \r
2951     private static class NumericValueFilter implements Filter {\r
2952         double value;\r
2953         NumericValueFilter(double value) { this.value = value; }\r
2954         public boolean contains(int ch) {\r
2955             return UCharacter.getUnicodeNumericValue(ch) == value;\r
2956         }\r
2957     }\r
2958 \r
2959     private static class GeneralCategoryMaskFilter implements Filter {\r
2960         int mask;\r
2961         GeneralCategoryMaskFilter(int mask) { this.mask = mask; }\r
2962         public boolean contains(int ch) {\r
2963             return ((1 << UCharacter.getType(ch)) & mask) != 0;\r
2964         }\r
2965     }\r
2966 \r
2967     private static class IntPropertyFilter implements Filter {\r
2968         int prop;\r
2969         int value;\r
2970         IntPropertyFilter(int prop, int value) {\r
2971             this.prop = prop;\r
2972             this.value = value;\r
2973         }\r
2974         public boolean contains(int ch) {\r
2975             return UCharacter.getIntPropertyValue(ch, prop) == value;\r
2976         }\r
2977     }\r
2978 \r
2979     // VersionInfo for unassigned characters\r
2980     static final VersionInfo NO_VERSION = VersionInfo.getInstance(0, 0, 0, 0);\r
2981 \r
2982     private static class VersionFilter implements Filter {\r
2983         VersionInfo version;\r
2984         VersionFilter(VersionInfo version) { this.version = version; }\r
2985         public boolean contains(int ch) {\r
2986             VersionInfo v = UCharacter.getAge(ch);\r
2987             // Reference comparison ok; VersionInfo caches and reuses\r
2988             // unique objects.\r
2989             return v != NO_VERSION &&\r
2990                    v.compareTo(version) <= 0;\r
2991         }\r
2992     }\r
2993 \r
2994     private static synchronized UnicodeSet getInclusions(int src) {\r
2995         if (INCLUSIONS == null) {\r
2996             INCLUSIONS = new UnicodeSet[UCharacterProperty.SRC_COUNT];\r
2997         }\r
2998         if(INCLUSIONS[src] == null) {\r
2999             UnicodeSet incl = new UnicodeSet();\r
3000             switch(src) {\r
3001             case UCharacterProperty.SRC_CHAR:\r
3002                 UCharacterProperty.getInstance().addPropertyStarts(incl);\r
3003                 break;\r
3004             case UCharacterProperty.SRC_PROPSVEC:\r
3005                 UCharacterProperty.getInstance().upropsvec_addPropertyStarts(incl);\r
3006                 break;\r
3007             case UCharacterProperty.SRC_CHAR_AND_PROPSVEC:\r
3008                 UCharacterProperty.getInstance().addPropertyStarts(incl);\r
3009                 UCharacterProperty.getInstance().upropsvec_addPropertyStarts(incl);\r
3010                 break;\r
3011             case UCharacterProperty.SRC_HST:\r
3012                 UCharacterProperty.getInstance().uhst_addPropertyStarts(incl);\r
3013                 break;\r
3014             case UCharacterProperty.SRC_NORM:\r
3015                 NormalizerImpl.addPropertyStarts(incl);\r
3016                 break;\r
3017             case UCharacterProperty.SRC_CASE:\r
3018                 try {\r
3019                     UCaseProps.getSingleton().addPropertyStarts(incl);\r
3020                 } catch(IOException e) {\r
3021                     throw new MissingResourceException(e.getMessage(),"","");\r
3022                 }\r
3023                 break;\r
3024             case UCharacterProperty.SRC_BIDI:\r
3025                 try {\r
3026                     UBiDiProps.getSingleton().addPropertyStarts(incl);\r
3027                 } catch(IOException e) {\r
3028                     throw new MissingResourceException(e.getMessage(),"","");\r
3029                 }\r
3030                 break;\r
3031             default:\r
3032                 throw new IllegalStateException("UnicodeSet.getInclusions(unknown src "+src+")");\r
3033             }\r
3034             INCLUSIONS[src] = incl;\r
3035         }\r
3036         return INCLUSIONS[src];\r
3037     }\r
3038 \r
3039     /**\r
3040      * Generic filter-based scanning code for UCD property UnicodeSets.\r
3041      */\r
3042     private UnicodeSet applyFilter(Filter filter, int src) {\r
3043         // Walk through all Unicode characters, noting the start\r
3044         // and end of each range for which filter.contain(c) is\r
3045         // true.  Add each range to a set.\r
3046         //\r
3047         // To improve performance, use the INCLUSIONS set, which\r
3048         // encodes information about character ranges that are known\r
3049         // to have identical properties, such as the CJK Ideographs\r
3050         // from U+4E00 to U+9FA5.  INCLUSIONS contains all characters\r
3051         // except the first characters of such ranges.\r
3052         //\r
3053         // TODO Where possible, instead of scanning over code points,\r
3054         // use internal property data to initialize UnicodeSets for\r
3055         // those properties.  Scanning code points is slow.\r
3056 \r
3057         clear();\r
3058 \r
3059         int startHasProperty = -1;\r
3060         UnicodeSet inclusions = getInclusions(src);\r
3061         int limitRange = inclusions.getRangeCount();\r
3062 \r
3063         for (int j=0; j<limitRange; ++j) {\r
3064             // get current range\r
3065             int start = inclusions.getRangeStart(j);\r
3066             int end = inclusions.getRangeEnd(j);\r
3067 \r
3068             // for all the code points in the range, process\r
3069             for (int ch = start; ch <= end; ++ch) {\r
3070                 // only add to the unicodeset on inflection points --\r
3071                 // where the hasProperty value changes to false\r
3072                 if (filter.contains(ch)) {\r
3073                     if (startHasProperty < 0) {\r
3074                         startHasProperty = ch;\r
3075                     }\r
3076                 } else if (startHasProperty >= 0) {\r
3077                     add_unchecked(startHasProperty, ch-1);\r
3078                     startHasProperty = -1;\r
3079                 }\r
3080             }\r
3081         }\r
3082         if (startHasProperty >= 0) {\r
3083             add_unchecked(startHasProperty, 0x10FFFF);\r
3084         }\r
3085 \r
3086         return this;\r
3087     }\r
3088 \r
3089 \r
3090     /**\r
3091      * Remove leading and trailing rule white space and compress\r
3092      * internal rule white space to a single space character.\r
3093      *\r
3094      * @see UCharacterProperty#isRuleWhiteSpace\r
3095      */\r
3096     private static String mungeCharName(String source) {\r
3097         StringBuffer buf = new StringBuffer();\r
3098         for (int i=0; i<source.length(); ) {\r
3099             int ch = UTF16.charAt(source, i);\r
3100             i += UTF16.getCharCount(ch);\r
3101             if (UCharacterProperty.isRuleWhiteSpace(ch)) {\r
3102                 if (buf.length() == 0 ||\r
3103                     buf.charAt(buf.length() - 1) == ' ') {\r
3104                     continue;\r
3105                 }\r
3106                 ch = ' '; // convert to ' '\r
3107             }\r
3108             UTF16.append(buf, ch);\r
3109         }\r
3110         if (buf.length() != 0 &&\r
3111             buf.charAt(buf.length() - 1) == ' ') {\r
3112             buf.setLength(buf.length() - 1);\r
3113         }\r
3114         return buf.toString();\r
3115     }\r
3116 \r
3117     //----------------------------------------------------------------\r
3118     // Property set API\r
3119     //----------------------------------------------------------------\r
3120 \r
3121     /**\r
3122      * Modifies this set to contain those code points which have the\r
3123      * given value for the given binary or enumerated property, as\r
3124      * returned by UCharacter.getIntPropertyValue.  Prior contents of\r
3125      * this set are lost.\r
3126      *\r
3127      * @param prop a property in the range\r
3128      * UProperty.BIN_START..UProperty.BIN_LIMIT-1 or\r
3129      * UProperty.INT_START..UProperty.INT_LIMIT-1 or.\r
3130      * UProperty.MASK_START..UProperty.MASK_LIMIT-1.\r
3131      *\r
3132      * @param value a value in the range\r
3133      * UCharacter.getIntPropertyMinValue(prop)..\r
3134      * UCharacter.getIntPropertyMaxValue(prop), with one exception.\r
3135      * If prop is UProperty.GENERAL_CATEGORY_MASK, then value should not be\r
3136      * a UCharacter.getType() result, but rather a mask value produced\r
3137      * by logically ORing (1 << UCharacter.getType()) values together.\r
3138      * This allows grouped categories such as [:L:] to be represented.\r
3139      *\r
3140      * @return a reference to this set\r
3141      *\r
3142      * @stable ICU 2.4\r
3143      */\r
3144     public UnicodeSet applyIntPropertyValue(int prop, int value) {\r
3145         checkFrozen();\r
3146         if (prop == UProperty.GENERAL_CATEGORY_MASK) {\r
3147             applyFilter(new GeneralCategoryMaskFilter(value), UCharacterProperty.SRC_CHAR);\r
3148         } else {\r
3149             applyFilter(new IntPropertyFilter(prop, value), UCharacterProperty.getInstance().getSource(prop));\r
3150         }\r
3151         return this;\r
3152     }\r
3153 \r
3154 \r
3155 \r
3156     /**\r
3157      * Modifies this set to contain those code points which have the\r
3158      * given value for the given property.  Prior contents of this\r
3159      * set are lost.\r
3160      *\r
3161      * @param propertyAlias a property alias, either short or long.\r
3162      * The name is matched loosely.  See PropertyAliases.txt for names\r
3163      * and a description of loose matching.  If the value string is\r
3164      * empty, then this string is interpreted as either a\r
3165      * General_Category value alias, a Script value alias, a binary\r
3166      * property alias, or a special ID.  Special IDs are matched\r
3167      * loosely and correspond to the following sets:\r
3168      *\r
3169      * "ANY" = [\u0000-\U0010FFFF],\r
3170      * "ASCII" = [\u0000-\u007F].\r
3171      *\r
3172      * @param valueAlias a value alias, either short or long.  The\r
3173      * name is matched loosely.  See PropertyValueAliases.txt for\r
3174      * names and a description of loose matching.  In addition to\r
3175      * aliases listed, numeric values and canonical combining classes\r
3176      * may be expressed numerically, e.g., ("nv", "0.5") or ("ccc",\r
3177      * "220").  The value string may also be empty.\r
3178      *\r
3179      * @return a reference to this set\r
3180      *\r
3181      * @stable ICU 2.4\r
3182      */\r
3183     public UnicodeSet applyPropertyAlias(String propertyAlias, String valueAlias) {\r
3184         return applyPropertyAlias(propertyAlias, valueAlias, null);\r
3185     }\r
3186 \r
3187     /**\r
3188      * Modifies this set to contain those code points which have the\r
3189      * given value for the given property.  Prior contents of this\r
3190      * set are lost.\r
3191      * @param propertyAlias\r
3192      * @param valueAlias\r
3193      * @param symbols if not null, then symbols are first called to see if a property\r
3194      * is available. If true, then everything else is skipped.\r
3195      * @return this set\r
3196      * @stable ICU 3.2\r
3197      */\r
3198     public UnicodeSet applyPropertyAlias(String propertyAlias,\r
3199                                          String valueAlias, SymbolTable symbols) {\r
3200         checkFrozen();\r
3201         int p;\r
3202         int v;\r
3203         boolean mustNotBeEmpty = false, invert = false;\r
3204 \r
3205         if (symbols != null\r
3206                 && (symbols instanceof XSymbolTable)\r
3207                 && ((XSymbolTable)symbols).applyPropertyAlias(propertyAlias, valueAlias, this)) {\r
3208                 return this;\r
3209         }\r
3210 \r
3211         if (valueAlias.length() > 0) {\r
3212             p = UCharacter.getPropertyEnum(propertyAlias);\r
3213 \r
3214             // Treat gc as gcm\r
3215             if (p == UProperty.GENERAL_CATEGORY) {\r
3216                 p = UProperty.GENERAL_CATEGORY_MASK;\r
3217             }\r
3218 \r
3219             if ((p >= UProperty.BINARY_START && p < UProperty.BINARY_LIMIT) ||\r
3220                 (p >= UProperty.INT_START && p < UProperty.INT_LIMIT) ||\r
3221                 (p >= UProperty.MASK_START && p < UProperty.MASK_LIMIT)) {\r
3222                 try {\r
3223                     v = UCharacter.getPropertyValueEnum(p, valueAlias);\r
3224                 } catch (IllegalArgumentException e) {\r
3225                     // Handle numeric CCC\r
3226                     if (p == UProperty.CANONICAL_COMBINING_CLASS ||\r
3227                         p == UProperty.LEAD_CANONICAL_COMBINING_CLASS ||\r
3228                         p == UProperty.TRAIL_CANONICAL_COMBINING_CLASS) {\r
3229                         v = Integer.parseInt(Utility.deleteRuleWhiteSpace(valueAlias));\r
3230                         // If the resultant set is empty then the numeric value\r
3231                         // was invalid.\r
3232                         //mustNotBeEmpty = true;\r
3233                         // old code was wrong; anything between 0 and 255 is valid even if unused.\r
3234                         if (v < 0 || v > 255) throw e;\r
3235                     } else {\r
3236                         throw e;\r
3237                     }\r
3238                 }\r
3239             }\r
3240 \r
3241             else {\r
3242 \r
3243                 switch (p) {\r
3244                 case UProperty.NUMERIC_VALUE:\r
3245                     {\r
3246                         double value = Double.parseDouble(Utility.deleteRuleWhiteSpace(valueAlias));\r
3247                         applyFilter(new NumericValueFilter(value), UCharacterProperty.SRC_CHAR);\r
3248                         return this;\r
3249                     }\r
3250                 case UProperty.NAME:\r
3251                 case UProperty.UNICODE_1_NAME:\r
3252                     {\r
3253                         // Must munge name, since\r
3254                         // UCharacter.charFromName() does not do\r
3255                         // 'loose' matching.\r
3256                         String buf = mungeCharName(valueAlias);\r
3257                         int ch =\r
3258                             (p == UProperty.NAME) ?\r
3259                             UCharacter.getCharFromExtendedName(buf) :\r
3260                             UCharacter.getCharFromName1_0(buf);\r
3261                         if (ch == -1) {\r
3262                             throw new IllegalArgumentException("Invalid character name");\r
3263                         }\r
3264                         clear();\r
3265                         add_unchecked(ch);\r
3266                         return this;\r
3267                     }\r
3268                 case UProperty.AGE:\r
3269                     {\r
3270                         // Must munge name, since\r
3271                         // VersionInfo.getInstance() does not do\r
3272                         // 'loose' matching.\r
3273                         VersionInfo version = VersionInfo.getInstance(mungeCharName(valueAlias));\r
3274                         applyFilter(new VersionFilter(version), UCharacterProperty.SRC_PROPSVEC);\r
3275                         return this;\r
3276                     }\r
3277                 }\r
3278 \r
3279                 // p is a non-binary, non-enumerated property that we\r
3280                 // don't support (yet).\r
3281                 throw new IllegalArgumentException("Unsupported property");\r
3282             }\r
3283         }\r
3284 \r
3285         else {\r
3286             // valueAlias is empty.  Interpret as General Category, Script,\r
3287             // Binary property, or ANY or ASCII.  Upon success, p and v will\r
3288             // be set.\r
3289             try {\r
3290                 p = UProperty.GENERAL_CATEGORY_MASK;\r
3291                 v = UCharacter.getPropertyValueEnum(p, propertyAlias);\r
3292             } catch (IllegalArgumentException e) {\r
3293                 try {\r
3294                     p = UProperty.SCRIPT;\r
3295                     v = UCharacter.getPropertyValueEnum(p, propertyAlias);\r
3296                 } catch (IllegalArgumentException e2) {\r
3297                     try {\r
3298                         p = UCharacter.getPropertyEnum(propertyAlias);\r
3299                     } catch (IllegalArgumentException e3) {\r
3300                         p = -1;\r
3301                     }\r
3302                     if (p >= UProperty.BINARY_START && p < UProperty.BINARY_LIMIT) {\r
3303                         v = 1;\r
3304                     } else if (p == -1) {\r
3305                         if (0 == UPropertyAliases.compare(ANY_ID, propertyAlias)) {\r
3306                             set(MIN_VALUE, MAX_VALUE);\r
3307                             return this;\r
3308                         } else if (0 == UPropertyAliases.compare(ASCII_ID, propertyAlias)) {\r
3309                             set(0, 0x7F);\r
3310                             return this;\r
3311                         } else if (0 == UPropertyAliases.compare(ASSIGNED, propertyAlias)) {\r
3312                             // [:Assigned:]=[:^Cn:]\r
3313                             p = UProperty.GENERAL_CATEGORY_MASK;\r
3314                             v = (1<<UCharacter.UNASSIGNED);\r
3315                             invert = true;\r
3316                         } else {\r
3317                             // Property name was never matched.\r
3318                             throw new IllegalArgumentException("Invalid property alias: " + propertyAlias + "=" + valueAlias);\r
3319                         }\r
3320                     } else {\r
3321                         // Valid propery name, but it isn't binary, so the value\r
3322                         // must be supplied.\r
3323                         throw new IllegalArgumentException("Missing property value");\r
3324                     }\r
3325                 }\r
3326             }\r
3327         }\r
3328 \r
3329         applyIntPropertyValue(p, v);\r
3330         if(invert) {\r
3331             complement();\r
3332         }\r
3333 \r
3334         if (mustNotBeEmpty && isEmpty()) {\r
3335             // mustNotBeEmpty is set to true if an empty set indicates\r
3336             // invalid input.\r
3337             throw new IllegalArgumentException("Invalid property value");\r
3338         }\r
3339 \r
3340         return this;\r
3341     }\r
3342 \r
3343     //----------------------------------------------------------------\r
3344     // Property set patterns\r
3345     //----------------------------------------------------------------\r
3346 \r
3347     /**\r
3348      * Return true if the given position, in the given pattern, appears\r
3349      * to be the start of a property set pattern.\r
3350      */\r
3351     private static boolean resemblesPropertyPattern(String pattern, int pos) {\r
3352         // Patterns are at least 5 characters long\r
3353         if ((pos+5) > pattern.length()) {\r
3354             return false;\r
3355         }\r
3356 \r
3357         // Look for an opening [:, [:^, \p, or \P\r
3358         return pattern.regionMatches(pos, "[:", 0, 2) ||\r
3359             pattern.regionMatches(true, pos, "\\p", 0, 2) ||\r
3360             pattern.regionMatches(pos, "\\N", 0, 2);\r
3361     }\r
3362 \r
3363     /**\r
3364      * Return true if the given iterator appears to point at a\r
3365      * property pattern.  Regardless of the result, return with the\r
3366      * iterator unchanged.\r
3367      * @param chars iterator over the pattern characters.  Upon return\r
3368      * it will be unchanged.\r
3369      * @param iterOpts RuleCharacterIterator options\r
3370      */\r
3371     private static boolean resemblesPropertyPattern(RuleCharacterIterator chars,\r
3372                                                     int iterOpts) {\r
3373         boolean result = false;\r
3374         iterOpts &= ~RuleCharacterIterator.PARSE_ESCAPES;\r
3375         Object pos = chars.getPos(null);\r
3376         int c = chars.next(iterOpts);\r
3377         if (c == '[' || c == '\\') {\r
3378             int d = chars.next(iterOpts & ~RuleCharacterIterator.SKIP_WHITESPACE);\r
3379             result = (c == '[') ? (d == ':') :\r
3380                      (d == 'N' || d == 'p' || d == 'P');\r
3381         }\r
3382         chars.setPos(pos);\r
3383         return result;\r
3384     }\r
3385 \r
3386     /**\r
3387      * Parse the given property pattern at the given parse position.\r
3388      * @param symbols TODO\r
3389      */\r
3390     private UnicodeSet applyPropertyPattern(String pattern, ParsePosition ppos, SymbolTable symbols) {\r
3391         int pos = ppos.getIndex();\r
3392 \r
3393         // On entry, ppos should point to one of the following locations:\r
3394 \r
3395         // Minimum length is 5 characters, e.g. \p{L}\r
3396         if ((pos+5) > pattern.length()) {\r
3397             return null;\r
3398         }\r
3399 \r
3400         boolean posix = false; // true for [:pat:], false for \p{pat} \P{pat} \N{pat}\r
3401         boolean isName = false; // true for \N{pat}, o/w false\r
3402         boolean invert = false;\r
3403 \r
3404         // Look for an opening [:, [:^, \p, or \P\r
3405         if (pattern.regionMatches(pos, "[:", 0, 2)) {\r
3406             posix = true;\r
3407             pos = Utility.skipWhitespace(pattern, pos+2);\r
3408             if (pos < pattern.length() && pattern.charAt(pos) == '^') {\r
3409                 ++pos;\r
3410                 invert = true;\r
3411             }\r
3412         } else if (pattern.regionMatches(true, pos, "\\p", 0, 2) ||\r
3413                    pattern.regionMatches(pos, "\\N", 0, 2)) {\r
3414             char c = pattern.charAt(pos+1);\r
3415             invert = (c == 'P');\r
3416             isName = (c == 'N');\r
3417             pos = Utility.skipWhitespace(pattern, pos+2);\r
3418             if (pos == pattern.length() || pattern.charAt(pos++) != '{') {\r
3419                 // Syntax error; "\p" or "\P" not followed by "{"\r
3420                 return null;\r
3421             }\r
3422         } else {\r
3423             // Open delimiter not seen\r
3424             return null;\r
3425         }\r
3426 \r
3427         // Look for the matching close delimiter, either :] or }\r
3428         int close = pattern.indexOf(posix ? ":]" : "}", pos);\r
3429         if (close < 0) {\r
3430             // Syntax error; close delimiter missing\r
3431             return null;\r
3432         }\r
3433 \r
3434         // Look for an '=' sign.  If this is present, we will parse a\r
3435         // medium \p{gc=Cf} or long \p{GeneralCategory=Format}\r
3436         // pattern.\r
3437         int equals = pattern.indexOf('=', pos);\r
3438         String propName, valueName;\r
3439         if (equals >= 0 && equals < close && !isName) {\r
3440             // Equals seen; parse medium/long pattern\r
3441             propName = pattern.substring(pos, equals);\r
3442             valueName = pattern.substring(equals+1, close);\r
3443         }\r
3444 \r
3445         else {\r
3446             // Handle case where no '=' is seen, and \N{}\r
3447             propName = pattern.substring(pos, close);\r
3448             valueName = "";\r
3449 \r
3450             // Handle \N{name}\r
3451             if (isName) {\r
3452                 // This is a little inefficient since it means we have to\r
3453                 // parse "na" back to UProperty.NAME even though we already\r
3454                 // know it's UProperty.NAME.  If we refactor the API to\r
3455                 // support args of (int, String) then we can remove\r
3456                 // "na" and make this a little more efficient.\r
3457                 valueName = propName;\r
3458                 propName = "na";\r
3459             }\r
3460         }\r
3461 \r
3462         applyPropertyAlias(propName, valueName, symbols);\r
3463 \r
3464         if (invert) {\r
3465             complement();\r
3466         }\r
3467 \r
3468         // Move to the limit position after the close delimiter\r
3469         ppos.setIndex(close + (posix ? 2 : 1));\r
3470 \r
3471         return this;\r
3472     }\r
3473 \r
3474     /**\r
3475      * Parse a property pattern.\r
3476      * @param chars iterator over the pattern characters.  Upon return\r
3477      * it will be advanced to the first character after the parsed\r
3478      * pattern, or the end of the iteration if all characters are\r
3479      * parsed.\r
3480      * @param rebuiltPat the pattern that was parsed, rebuilt or\r
3481      * copied from the input pattern, as appropriate.\r
3482      * @param symbols TODO\r
3483      */\r
3484     private void applyPropertyPattern(RuleCharacterIterator chars,\r
3485                                       StringBuffer rebuiltPat, SymbolTable symbols) {\r
3486         String patStr = chars.lookahead();\r
3487         ParsePosition pos = new ParsePosition(0);\r
3488         applyPropertyPattern(patStr, pos, symbols);\r
3489         if (pos.getIndex() == 0) {\r
3490             syntaxError(chars, "Invalid property pattern");\r
3491         }\r
3492         chars.jumpahead(pos.getIndex());\r
3493         rebuiltPat.append(patStr.substring(0, pos.getIndex()));\r
3494     }\r
3495 \r
3496     //----------------------------------------------------------------\r
3497     // Case folding API\r
3498     //----------------------------------------------------------------\r
3499 \r
3500     /**\r
3501      * Bitmask for constructor and applyPattern() indicating that\r
3502      * white space should be ignored.  If set, ignore characters for\r
3503      * which UCharacterProperty.isRuleWhiteSpace() returns true,\r
3504      * unless they are quoted or escaped.  This may be ORed together\r
3505      * with other selectors.\r
3506      * @stable ICU 3.8\r
3507      */\r
3508     public static final int IGNORE_SPACE = 1;\r
3509 \r
3510     /**\r
3511      * Bitmask for constructor, applyPattern(), and closeOver()\r
3512      * indicating letter case.  This may be ORed together with other\r
3513      * selectors.\r
3514      *\r
3515      * Enable case insensitive matching.  E.g., "[ab]" with this flag\r
3516      * will match 'a', 'A', 'b', and 'B'.  "[^ab]" with this flag will\r
3517      * match all except 'a', 'A', 'b', and 'B'. This performs a full\r
3518      * closure over case mappings, e.g. U+017F for s.\r
3519      *\r
3520      * The resulting set is a superset of the input for the code points but\r
3521      * not for the strings.\r
3522      * It performs a case mapping closure of the code points and adds\r
3523      * full case folding strings for the code points, and reduces strings of\r
3524      * the original set to their full case folding equivalents.\r
3525      *\r
3526      * This is designed for case-insensitive matches, for example\r
3527      * in regular expressions. The full code point case closure allows checking of\r
3528      * an input character directly against the closure set.\r
3529      * Strings are matched by comparing the case-folded form from the closure\r
3530      * set with an incremental case folding of the string in question.\r
3531      *\r
3532      * The closure set will also contain single code points if the original\r
3533      * set contained case-equivalent strings (like U+00DF for "ss" or "Ss" etc.).\r
3534      * This is not necessary (that is, redundant) for the above matching method\r
3535      * but results in the same closure sets regardless of whether the original\r
3536      * set contained the code point or a string.\r
3537      * @stable ICU 3.8\r
3538      */\r
3539     public static final int CASE = 2;\r
3540 \r
3541     /**\r
3542      * Alias for UnicodeSet.CASE, for ease of porting from C++ where ICU4C\r
3543      * also has both USET_CASE and USET_CASE_INSENSITIVE (see uset.h).\r
3544      * @see #CASE\r
3545      * @stable ICU 3.4\r
3546      */\r
3547     public static final int CASE_INSENSITIVE = 2;\r
3548 \r
3549     /**\r
3550      * Bitmask for constructor, applyPattern(), and closeOver()\r
3551      * indicating letter case.  This may be ORed together with other\r
3552      * selectors.\r
3553      *\r
3554      * Enable case insensitive matching.  E.g., "[ab]" with this flag\r
3555      * will match 'a', 'A', 'b', and 'B'.  "[^ab]" with this flag will\r
3556      * match all except 'a', 'A', 'b', and 'B'. This adds the lower-,\r
3557      * title-, and uppercase mappings as well as the case folding\r
3558      * of each existing element in the set.\r
3559      * @stable ICU 3.4\r
3560      */\r
3561     public static final int ADD_CASE_MAPPINGS = 4;\r
3562 \r
3563     //  add the result of a full case mapping to the set\r
3564     //  use str as a temporary string to avoid constructing one\r
3565     private static final void addCaseMapping(UnicodeSet set, int result, StringBuffer full) {\r
3566         if(result >= 0) {\r
3567             if(result > UCaseProps.MAX_STRING_LENGTH) {\r
3568                 // add a single-code point case mapping\r
3569                 set.add(result);\r
3570             } else {\r
3571                 // add a string case mapping from full with length result\r
3572                 set.add(full.toString());\r
3573                 full.setLength(0);\r
3574             }\r
3575         }\r
3576         // result < 0: the code point mapped to itself, no need to add it\r
3577         // see UCaseProps\r
3578     }\r
3579 \r
3580     /**\r
3581      * Close this set over the given attribute.  For the attribute\r
3582      * CASE, the result is to modify this set so that:\r
3583      *\r
3584      * 1. For each character or string 'a' in this set, all strings\r
3585      * 'b' such that foldCase(a) == foldCase(b) are added to this set.\r
3586      * (For most 'a' that are single characters, 'b' will have\r
3587      * b.length() == 1.)\r
3588      *\r
3589      * 2. For each string 'e' in the resulting set, if e !=\r
3590      * foldCase(e), 'e' will be removed.\r
3591      *\r
3592      * Example: [aq\u00DF{Bc}{bC}{Fi}] => [aAqQ\u00DF\uFB01{ss}{bc}{fi}]\r
3593      *\r
3594      * (Here foldCase(x) refers to the operation\r
3595      * UCharacter.foldCase(x, true), and a == b actually denotes\r
3596      * a.equals(b), not pointer comparison.)\r
3597      *\r
3598      * @param attribute bitmask for attributes to close over.\r
3599      * Currently only the CASE bit is supported.  Any undefined bits\r
3600      * are ignored.\r
3601      * @return a reference to this set.\r
3602      * @stable ICU 3.8\r
3603      */\r
3604     public UnicodeSet closeOver(int attribute) {\r
3605         checkFrozen();\r
3606         if ((attribute & (CASE | ADD_CASE_MAPPINGS)) != 0) {\r
3607             UCaseProps csp;\r
3608             try {\r
3609                 csp = UCaseProps.getSingleton();\r
3610             } catch(IOException e) {\r
3611                 return this;\r
3612             }\r
3613             UnicodeSet foldSet = new UnicodeSet(this);\r
3614             ULocale root = ULocale.ROOT;\r
3615 \r
3616             // start with input set to guarantee inclusion\r
3617             // CASE: remove strings because the strings will actually be reduced (folded);\r
3618             //       therefore, start with no strings and add only those needed\r
3619             if((attribute & CASE) != 0) {\r
3620                 foldSet.strings.clear();\r
3621             }\r
3622 \r
3623             int n = getRangeCount();\r
3624             int result;\r
3625             StringBuffer full = new StringBuffer();\r
3626             int locCache[] = new int[1];\r
3627 \r
3628             for (int i=0; i<n; ++i) {\r
3629                 int start = getRangeStart(i);\r
3630                 int end   = getRangeEnd(i);\r
3631 \r
3632                 if((attribute & CASE) != 0) {\r
3633                     // full case closure\r
3634                     for (int cp=start; cp<=end; ++cp) {\r
3635                         csp.addCaseClosure(cp, foldSet);\r
3636                     }\r
3637                 } else {\r
3638                     // add case mappings\r
3639                     // (does not add long s for regular s, or Kelvin for k, for example)\r
3640                     for (int cp=start; cp<=end; ++cp) {\r
3641                         result = csp.toFullLower(cp, null, full, root, locCache);\r
3642                         addCaseMapping(foldSet, result, full);\r
3643 \r
3644                         result = csp.toFullTitle(cp, null, full, root, locCache);\r
3645                         addCaseMapping(foldSet, result, full);\r
3646 \r
3647                         result = csp.toFullUpper(cp, null, full, root, locCache);\r
3648                         addCaseMapping(foldSet, result, full);\r
3649 \r
3650                         result = csp.toFullFolding(cp, full, 0);\r
3651                         addCaseMapping(foldSet, result, full);\r
3652                     }\r
3653                 }\r
3654             }\r
3655             if (!strings.isEmpty()) {\r
3656                 String str;\r
3657                 if ((attribute & CASE) != 0) {\r
3658                     Iterator it = strings.iterator();\r
3659                     while (it.hasNext()) {\r
3660                         str = UCharacter.foldCase((String)it.next(), 0);\r
3661                         if(!csp.addStringCaseClosure(str, foldSet)) {\r
3662                             foldSet.add(str); // does not map to code points: add the folded string itself\r
3663                         }\r
3664                     }\r
3665                 } else {\r
3666                     BreakIterator bi = BreakIterator.getWordInstance(root);\r
3667                     Iterator it = strings.iterator();\r
3668                     while (it.hasNext()) {\r
3669                         str = (String)it.next();\r
3670                         foldSet.add(UCharacter.toLowerCase(root, str));\r
3671                         foldSet.add(UCharacter.toTitleCase(root, str, bi));\r
3672                         foldSet.add(UCharacter.toUpperCase(root, str));\r
3673                         foldSet.add(UCharacter.foldCase(str, 0));\r
3674                     }\r
3675                 }\r
3676             }\r
3677             set(foldSet);\r
3678         }\r
3679         return this;\r
3680     }\r
3681 \r
3682     /**\r
3683      * Internal class for customizing UnicodeSet parsing of properties.\r
3684      * TODO: extend to allow customizing of codepoint ranges\r
3685      * @draft ICU3.8\r
3686      * @provisional This API might change or be removed in a future release.\r
3687      * @author medavis\r
3688      */\r
3689     abstract public static class XSymbolTable implements SymbolTable {\r
3690         /**\r
3691          * Default constructor\r
3692          * @draft ICU3.8\r
3693          * @provisional This API might change or be removed in a future release.\r
3694          */\r
3695         public XSymbolTable(){}\r
3696         /**\r
3697          * Supplies default implementation for SymbolTable (no action).\r
3698          * @draft ICU3.8\r
3699          * @provisional This API might change or be removed in a future release.\r
3700          */\r
3701         public UnicodeMatcher lookupMatcher(int i) {\r
3702             return null;\r
3703         }\r
3704         /**\r
3705          * Apply a new property alias. Is called when parsing [:xxx=yyy:]. Results are to put into result.\r
3706          * @param propertyName the xxx in [:xxx=yyy:]\r
3707          * @param propertyValue the yyy in [:xxx=yyy:]\r
3708          * @param result where the result is placed\r
3709          * @return true if handled\r
3710          * @draft ICU3.8\r
3711          * @provisional This API might change or be removed in a future release.\r
3712          */\r
3713         public boolean applyPropertyAlias(String propertyName, String propertyValue, UnicodeSet result) {\r
3714             return false;\r
3715         }\r
3716         /**\r
3717          * Supplies default implementation for SymbolTable (no action).\r
3718          * @draft ICU3.8\r
3719          * @provisional This API might change or be removed in a future release.\r
3720             */\r
3721         public char[] lookup(String s) {\r
3722             return null;\r
3723         }\r
3724         /**\r
3725          * Supplies default implementation for SymbolTable (no action).\r
3726          * @draft ICU3.8\r
3727          * @provisional This API might change or be removed in a future release.\r
3728          */\r
3729         public String parseReference(String text, ParsePosition pos, int limit) {\r
3730             return null;\r
3731         }\r
3732     }\r
3733 \r
3734     private boolean frozen;\r
3735     \r
3736     /**\r
3737      * Is this frozen, according to the Freezable interface?\r
3738      * @return value\r
3739      * @stable ICU 3.8\r
3740      */\r
3741     public boolean isFrozen() {\r
3742         return frozen;\r
3743     }\r
3744 \r
3745     /**\r
3746      * Freeze this class, according to the Freezable interface.\r
3747      * @return this\r
3748      * @stable ICU 3.8\r
3749      */\r
3750     public Object freeze() {\r
3751         frozen = true;\r
3752         return this;\r
3753     }\r
3754     \r
3755     /**\r
3756      * Clone a thawed version of this class, according to the Freezable interface.\r
3757      * @return this\r
3758      * @stable ICU 3.8\r
3759      */\r
3760     public Object cloneAsThawed() {\r
3761         UnicodeSet result = (UnicodeSet) clone();\r
3762         result.frozen = false;\r
3763         return result;\r
3764     }\r
3765     \r
3766     // internal function\r
3767     private void checkFrozen() {\r
3768         if (frozen) {\r
3769             throw new UnsupportedOperationException("Attempt to modify frozen object");\r
3770         }\r
3771     }\r
3772 }\r
3773 //eof\r