]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/tests/translit/src/com/ibm/icu/dev/util/UnicodeProperty.java
Upgrade ICU4J.
[Dictionary.git] / jars / icu4j-52_1 / main / tests / translit / src / com / ibm / icu / dev / util / UnicodeProperty.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2013, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.dev.util;
8
9 import java.io.PrintWriter;
10 import java.io.StringWriter;
11 import java.text.ParsePosition;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.Comparator;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.LinkedHashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.TreeMap;
22 import java.util.regex.Pattern;
23
24 import com.ibm.icu.dev.util.CollectionUtilities.InverseMatcher;
25 import com.ibm.icu.dev.util.CollectionUtilities.ObjectMatcher;
26 import com.ibm.icu.impl.Utility;
27 import com.ibm.icu.text.SymbolTable;
28 import com.ibm.icu.text.UFormat;
29 import com.ibm.icu.text.UTF16;
30 import com.ibm.icu.text.UnicodeMatcher;
31 import com.ibm.icu.text.UnicodeSet;
32 import com.ibm.icu.text.UnicodeSetIterator;
33
34 public abstract class UnicodeProperty extends UnicodeLabel {
35
36     public static final UnicodeSet NONCHARACTERS = new UnicodeSet("[:noncharactercodepoint:]").freeze();
37     public static final UnicodeSet PRIVATE_USE = new UnicodeSet("[:gc=privateuse:]").freeze();
38     public static final UnicodeSet SURROGATE = new UnicodeSet("[:gc=surrogate:]").freeze();
39
40     public static final UnicodeSet HIGH_SURROGATES = new UnicodeSet("[\\uD800-\\uDB7F]").freeze();
41     public static final int SAMPLE_HIGH_SURROGATE = HIGH_SURROGATES.charAt(0);
42     public static final UnicodeSet HIGH_PRIVATE_USE_SURROGATES = new UnicodeSet("[\\uDB80-\\uDBFF]").freeze();
43     public static final int SAMPLE_HIGH_PRIVATE_USE_SURROGATE = HIGH_PRIVATE_USE_SURROGATES.charAt(0);
44     public static final UnicodeSet LOW_SURROGATES = new UnicodeSet("[\\uDC00-\\uDFFF]").freeze();
45     public static final int SAMPLE_LOW_SURROGATE = LOW_SURROGATES.charAt(0);
46
47     public static final UnicodeSet PRIVATE_USE_AREA = new UnicodeSet("[\\uE000-\\uF8FF]").freeze();
48     public static final int SAMPLE_PRIVATE_USE_AREA = PRIVATE_USE_AREA.charAt(0);
49     public static final UnicodeSet PRIVATE_USE_AREA_A = new UnicodeSet("[\\U000F0000-\\U000FFFFD]").freeze();
50     public static final int SAMPLE_PRIVATE_USE_AREA_A = PRIVATE_USE_AREA_A.charAt(0);
51     public static final UnicodeSet PRIVATE_USE_AREA_B = new UnicodeSet("[\\U00100000-\\U0010FFFD]").freeze();
52     public static final int SAMPLE_PRIVATE_USE_AREA_B = PRIVATE_USE_AREA_B.charAt(0);
53
54     // The following are special. They are used for performance, but must be changed if the version of Unicode for the UnicodeProperty changes.
55     private static UnicodeSet UNASSIGNED;
56     private static int SAMPLE_UNASSIGNED;
57     private static UnicodeSet SPECIALS;
58     private static UnicodeSet STUFF_TO_TEST;
59     private static UnicodeSet STUFF_TO_TEST_WITH_UNASSIGNED;
60
61     public static synchronized UnicodeSet getUNASSIGNED() {
62         if (UNASSIGNED == null) {
63             UNASSIGNED = new UnicodeSet("[:gc=unassigned:]").freeze();
64         }
65         return UNASSIGNED;
66     }
67
68     public static synchronized UnicodeSet contractUNASSIGNED(UnicodeSet toBeUnassigned) {
69         UnicodeSet temp = UNASSIGNED;
70         ResetCacheProperties();
71         UNASSIGNED = temp == null ? toBeUnassigned.freeze() : new UnicodeSet(temp).retainAll(toBeUnassigned).freeze();
72         return UNASSIGNED;
73     }
74
75     public static synchronized int getSAMPLE_UNASSIGNED() {
76         if (SAMPLE_UNASSIGNED == 0) {
77             SAMPLE_UNASSIGNED = getUNASSIGNED().charAt(0);
78         }
79         return SAMPLE_UNASSIGNED;
80     }
81
82     public static synchronized UnicodeSet getSPECIALS() {
83         if (SPECIALS == null) {
84             SPECIALS = new UnicodeSet(getUNASSIGNED()).addAll(PRIVATE_USE).addAll(SURROGATE).freeze();
85         }
86         return SPECIALS;
87     }
88
89     public static synchronized UnicodeSet getSTUFF_TO_TEST() {
90         if (STUFF_TO_TEST == null) {
91             STUFF_TO_TEST = new UnicodeSet(getSPECIALS()).complement()
92             .addAll(NONCHARACTERS)
93             .add(getSAMPLE_UNASSIGNED())
94             .add(SAMPLE_HIGH_SURROGATE)
95             .add(SAMPLE_HIGH_PRIVATE_USE_SURROGATE)
96             .add(SAMPLE_LOW_SURROGATE)
97             .add(SAMPLE_PRIVATE_USE_AREA)
98             .add(SAMPLE_PRIVATE_USE_AREA_A)
99             .add(SAMPLE_PRIVATE_USE_AREA_B)
100             .freeze();
101         }
102         return STUFF_TO_TEST;
103     }
104
105     public static synchronized UnicodeSet getSTUFF_TO_TEST_WITH_UNASSIGNED() {
106         if (STUFF_TO_TEST_WITH_UNASSIGNED == null) {
107             STUFF_TO_TEST_WITH_UNASSIGNED = new UnicodeSet(getSTUFF_TO_TEST()).addAll(getUNASSIGNED()).freeze();
108         }
109         return STUFF_TO_TEST_WITH_UNASSIGNED;
110     }
111
112     /**
113      * Reset the cache properties. Must be done if the version of Unicode is different than the ICU one, AND any UnicodeProperty has already been instantiated.
114      * TODO make this a bit more robust.
115      * @internal
116      */
117     public static synchronized void ResetCacheProperties() {
118         UNASSIGNED = null;
119         SAMPLE_UNASSIGNED = 0;
120         SPECIALS = null;
121         STUFF_TO_TEST = null;
122         STUFF_TO_TEST_WITH_UNASSIGNED = null;
123     }
124
125     public static boolean DEBUG = false;
126
127     public static String CHECK_NAME = "FC_NFKC_Closure";
128
129     public static int CHECK_VALUE = 0x037A;
130
131     private String name;
132
133     private String firstNameAlias = null;
134
135     private int type;
136
137     private Map valueToFirstValueAlias = null;
138
139     private boolean hasUniformUnassigned = true;
140
141     /*
142      * Name: Unicode_1_Name Name: ISO_Comment Name: Name Name: Unicode_1_Name
143      * 
144      */
145
146     public static final int UNKNOWN = 0, BINARY = 2, EXTENDED_BINARY = 3,
147     ENUMERATED = 4, EXTENDED_ENUMERATED = 5, CATALOG = 6,
148     EXTENDED_CATALOG = 7, MISC = 8, EXTENDED_MISC = 9, STRING = 10,
149     EXTENDED_STRING = 11, NUMERIC = 12, EXTENDED_NUMERIC = 13,
150     START_TYPE = 2, LIMIT_TYPE = 14, EXTENDED_MASK = 1,
151     CORE_MASK = ~EXTENDED_MASK, BINARY_MASK = (1 << BINARY)
152     | (1 << EXTENDED_BINARY), STRING_MASK = (1 << STRING)
153     | (1 << EXTENDED_STRING),
154     STRING_OR_MISC_MASK = (1 << STRING) | (1 << EXTENDED_STRING)
155     | (1 << MISC) | (1 << EXTENDED_MISC),
156     ENUMERATED_OR_CATALOG_MASK = (1 << ENUMERATED)
157     | (1 << EXTENDED_ENUMERATED) | (1 << CATALOG)
158     | (1 << EXTENDED_CATALOG);
159
160     private static final String[] TYPE_NAMES = { "Unknown", "Unknown",
161         "Binary", "Extended Binary", "Enumerated", "Extended Enumerated",
162         "Catalog", "Extended Catalog", "Miscellaneous",
163         "Extended Miscellaneous", "String", "Extended String", "Numeric",
164         "Extended Numeric", };
165
166     public static String getTypeName(int propType) {
167         return TYPE_NAMES[propType];
168     }
169
170     public final String getName() {
171         return name;
172     }
173
174     public final int getType() {
175         return type;
176     }
177
178     public String getTypeName() {
179         return TYPE_NAMES[type];
180     }
181
182     public final boolean isType(int mask) {
183         return ((1 << type) & mask) != 0;
184     }
185
186     protected final void setName(String string) {
187         if (string == null)
188             throw new IllegalArgumentException("Name must not be null");
189         name = string;
190     }
191
192     protected final void setType(int i) {
193         type = i;
194     }
195
196     public String getVersion() {
197         return _getVersion();
198     }
199
200     public String getValue(int codepoint) {
201         if (DEBUG && CHECK_VALUE == codepoint && CHECK_NAME.equals(getName())) {
202             String value = _getValue(codepoint);
203             System.out.println(getName() + "(" + Utility.hex(codepoint) + "):"
204                     + (getType() == STRING ? Utility.hex(value) : value));
205             return value;
206         }
207         return _getValue(codepoint);
208     }
209
210     // public String getValue(int codepoint, boolean isShort) {
211     // return getValue(codepoint);
212     // }
213
214     public List<String> getNameAliases(List<String> result) {
215         if (result == null)
216             result = new ArrayList(1);
217         return _getNameAliases(result);
218     }
219
220     public List<String> getValueAliases(String valueAlias, List<String> result) {
221         if (result == null)
222             result = new ArrayList(1);
223         result = _getValueAliases(valueAlias, result);
224         if (!result.contains(valueAlias)) { // FIX && type < NUMERIC
225             result = _getValueAliases(valueAlias, result); // for debugging
226             throw new IllegalArgumentException("Internal error: " + getName()
227                     + " doesn't contain " + valueAlias + ": "
228                     + new BagFormatter().join(result));
229         }
230         return result;
231     }
232
233     public List<String> getAvailableValues(List<String> result) {
234         if (result == null)
235             result = new ArrayList(1);
236         return _getAvailableValues(result);
237     }
238
239     protected abstract String _getVersion();
240
241     protected abstract String _getValue(int codepoint);
242
243     protected abstract List<String> _getNameAliases(List<String> result);
244
245     protected abstract List<String> _getValueAliases(String valueAlias, List<String> result);
246
247     protected abstract List<String> _getAvailableValues(List<String> result);
248
249     // conveniences
250     public final List<String> getNameAliases() {
251         return getNameAliases(null);
252     }
253
254     public final List<String> getValueAliases(String valueAlias) {
255         return getValueAliases(valueAlias, null);
256     }
257
258     public final List<String> getAvailableValues() {
259         return getAvailableValues(null);
260     }
261
262     public final String getValue(int codepoint, boolean getShortest) {
263         String result = getValue(codepoint);
264         if (type >= MISC || result == null || !getShortest)
265             return result;
266         return getFirstValueAlias(result);
267     }
268
269     public final String getFirstNameAlias() {
270         if (firstNameAlias == null) {
271             firstNameAlias = (String) getNameAliases().get(0);
272         }
273         return firstNameAlias;
274     }
275
276     public final String getFirstValueAlias(String value) {
277         if (valueToFirstValueAlias == null)
278             _getFirstValueAliasCache();
279         return valueToFirstValueAlias.get(value).toString();
280     }
281
282     private void _getFirstValueAliasCache() {
283         maxValueWidth = 0;
284         maxFirstValueAliasWidth = 0;
285         valueToFirstValueAlias = new HashMap(1);
286         Iterator it = getAvailableValues().iterator();
287         while (it.hasNext()) {
288             String value = (String) it.next();
289             String first = (String) getValueAliases(value).get(0);
290             if (first == null) { // internal error
291                 throw new IllegalArgumentException(
292                         "Value not in value aliases: " + value);
293             }
294             if (DEBUG && CHECK_NAME.equals(getName())) {
295                 System.out.println("First Alias: " + getName() + ": " + value
296                         + " => " + first
297                         + new BagFormatter().join(getValueAliases(value)));
298             }
299             valueToFirstValueAlias.put(value, first);
300             if (value.length() > maxValueWidth) {
301                 maxValueWidth = value.length();
302             }
303             if (first.length() > maxFirstValueAliasWidth) {
304                 maxFirstValueAliasWidth = first.length();
305             }
306         }
307     }
308
309     private int maxValueWidth = -1;
310
311     private int maxFirstValueAliasWidth = -1;
312
313     public int getMaxWidth(boolean getShortest) {
314         if (maxValueWidth < 0)
315             _getFirstValueAliasCache();
316         if (getShortest)
317             return maxFirstValueAliasWidth;
318         return maxValueWidth;
319     }
320
321     public final UnicodeSet getSet(String propertyValue) {
322         return getSet(propertyValue, null);
323     }
324
325     public final UnicodeSet getSet(PatternMatcher matcher) {
326         return getSet(matcher, null);
327     }
328
329     /** Adds the property value set to the result. Clear the result first if you don't want to keep the original contents.
330      */
331     public final UnicodeSet getSet(String propertyValue, UnicodeSet result) {
332         return getSet(new SimpleMatcher(propertyValue,
333                 isType(STRING_OR_MISC_MASK) ? null : PROPERTY_COMPARATOR),
334                 result);
335     }
336
337     private UnicodeMap unicodeMap = null;
338
339     public static final String UNUSED = "??";
340
341     public UnicodeSet getSet(PatternMatcher matcher, UnicodeSet result) {
342         if (result == null)
343             result = new UnicodeSet();
344         boolean uniformUnassigned = hasUniformUnassigned();
345         if (isType(STRING_OR_MISC_MASK)) {
346             for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
347                 int i = usi.codepoint;
348                 String value = getValue(i);
349                 if (value != null && matcher.matches(value)) {
350                     result.add(i);
351                 }
352             }
353             return addUntested(result, uniformUnassigned);
354         }
355         List temp = new ArrayList(1); // to avoid reallocating...
356         UnicodeMap um = getUnicodeMap_internal();
357         Iterator it = um.getAvailableValues(null).iterator();
358         main: while (it.hasNext()) {
359             String value = (String) it.next();
360             temp.clear();
361             Iterator it2 = getValueAliases(value, temp).iterator();
362             while (it2.hasNext()) {
363                 String value2 = (String) it2.next();
364                 // System.out.println("Values:" + value2);
365                 if (matcher.matches(value2)
366                         || matcher.matches(toSkeleton(value2))) {
367                     um.keySet(value, result);
368                     continue main;
369                 }
370             }
371         }
372         return result;
373     }
374
375     /*
376      * public UnicodeSet getMatchSet(UnicodeSet result) { if (result == null)
377      * result = new UnicodeSet(); addAll(matchIterator, result); return result; }
378      * 
379      * public void setMatchSet(UnicodeSet set) { matchIterator = new
380      * UnicodeSetIterator(set); }
381      */
382
383     /**
384      * Utility for debugging
385      */
386     public static String getStack() {
387         Exception e = new Exception();
388         StringWriter sw = new StringWriter();
389         PrintWriter pw = new PrintWriter(sw);
390         e.printStackTrace(pw);
391         pw.flush();
392         return "Showing Stack with fake " + sw.getBuffer().toString();
393     }
394
395     // TODO use this instead of plain strings
396     public static class Name implements Comparable {
397         private String skeleton;
398
399         private String pretty;
400
401         public final int RAW = 0, TITLE = 1, NORMAL = 2;
402
403         public Name(String name, int style) {
404             if (name == null)
405                 name = "";
406             if (style == RAW) {
407                 skeleton = pretty = name;
408             } else {
409                 pretty = regularize(name, style == TITLE);
410                 skeleton = toSkeleton(pretty);
411             }
412         }
413
414         public int compareTo(Object o) {
415             return skeleton.compareTo(((Name) o).skeleton);
416         }
417
418         public boolean equals(Object o) {
419             return skeleton.equals(((Name) o).skeleton);
420         }
421
422         public int hashCode() {
423             return skeleton.hashCode();
424         }
425
426         public String toString() {
427             return pretty;
428         }
429     }
430
431     /**
432      * @return the unicode map
433      */
434     public UnicodeMap getUnicodeMap() {
435         return getUnicodeMap(false);
436     }
437
438     /**
439      * @return the unicode map
440      */
441     public UnicodeMap getUnicodeMap(boolean getShortest) {
442         if (!getShortest)
443             return (UnicodeMap) getUnicodeMap_internal().cloneAsThawed();
444         UnicodeMap result = new UnicodeMap();
445         boolean uniformUnassigned = hasUniformUnassigned();
446
447         for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
448             int i = usi.codepoint;
449             // if (DEBUG && i == 0x41) System.out.println(i + "\t" +
450             // getValue(i));
451             String value = getValue(i, true);
452             result.put(i, value);
453         }
454         return addUntested(result, uniformUnassigned);
455     }
456
457     /**
458      * @return the unicode map
459      */
460     public UnicodeMap getUnicodeMap_internal() {
461         if (unicodeMap == null)
462             unicodeMap = _getUnicodeMap();
463         return unicodeMap;
464     }
465
466     protected UnicodeMap _getUnicodeMap() {
467         UnicodeMap result = new UnicodeMap();
468         HashMap myIntern = new HashMap();
469         boolean uniformUnassigned = hasUniformUnassigned();
470
471         for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
472             int i = usi.codepoint;
473             // if (DEBUG && i == 0x41) System.out.println(i + "\t" +
474             // getValue(i));
475             String value = getValue(i);
476             String iValue = (String) myIntern.get(value);
477             if (iValue == null)
478                 myIntern.put(value, iValue = value);
479             result.put(i, iValue);
480         }
481         addUntested(result, uniformUnassigned);
482
483         if (DEBUG) {
484             for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
485                 int i = usi.codepoint;
486                 // if (DEBUG && i == 0x41) System.out.println(i + "\t" +
487                 // getValue(i));
488                 String value = getValue(i);
489                 String resultValue = (String) result.getValue(i);
490                 if (!value.equals(resultValue)) {
491                     throw new RuntimeException("Value failure at: "
492                             + Utility.hex(i));
493                 }
494             }
495         }
496         if (DEBUG && CHECK_NAME.equals(getName())) {
497             System.out.println(getName() + ":\t" + getClass().getName() + "\t"
498                     + getVersion());
499             System.out.println(getStack());
500             System.out.println(result);
501         }
502         return result;
503     }
504
505     private static UnicodeSetIterator getStuffToTest(boolean uniformUnassigned) {
506         return new UnicodeSetIterator(uniformUnassigned ? getSTUFF_TO_TEST() : getSTUFF_TO_TEST_WITH_UNASSIGNED());
507     }
508
509     /**
510      * Really ought to create a Collection UniqueList, that forces uniqueness.
511      * But for now...
512      */
513     public static Collection addUnique(Object obj, Collection result) {
514         if (obj != null && !result.contains(obj))
515             result.add(obj);
516         return result;
517     }
518
519     /**
520      * Utility for managing property & non-string value aliases
521      */
522     public static final Comparator PROPERTY_COMPARATOR = new Comparator() {
523         public int compare(Object o1, Object o2) {
524             return compareNames((String) o1, (String) o2);
525         }
526     };
527
528     /**
529      * Utility for managing property & non-string value aliases
530      * 
531      */
532     // TODO optimize
533     public static boolean equalNames(String a, String b) {
534         if (a == b)
535             return true;
536         if (a == null)
537             return false;
538         return toSkeleton(a).equals(toSkeleton(b));
539     }
540
541     /**
542      * Utility for managing property & non-string value aliases
543      */
544     // TODO optimize
545     public static int compareNames(String a, String b) {
546         if (a == b)
547             return 0;
548         if (a == null)
549             return -1;
550         if (b == null)
551             return 1;
552         return toSkeleton(a).compareTo(toSkeleton(b));
553     }
554
555     /**
556      * Utility for managing property & non-string value aliases
557      */
558     // TODO account for special names, tibetan, hangul
559     public static String toSkeleton(String source) {
560         if (source == null)
561             return null;
562         StringBuffer skeletonBuffer = new StringBuffer();
563         boolean gotOne = false;
564         // remove spaces, '_', '-'
565         // we can do this with char, since no surrogates are involved
566         for (int i = 0; i < source.length(); ++i) {
567             char ch = source.charAt(i);
568             if (i > 0 && (ch == '_' || ch == ' ' || ch == '-')) {
569                 gotOne = true;
570             } else {
571                 char ch2 = Character.toLowerCase(ch);
572                 if (ch2 != ch) {
573                     gotOne = true;
574                     skeletonBuffer.append(ch2);
575                 } else {
576                     skeletonBuffer.append(ch);
577                 }
578             }
579         }
580         if (!gotOne)
581             return source; // avoid string creation
582         return skeletonBuffer.toString();
583     }
584
585     // get the name skeleton
586     public static String toNameSkeleton(String source) {
587         if (source == null)
588             return null;
589         StringBuffer result = new StringBuffer();
590         // remove spaces, medial '-'
591         // we can do this with char, since no surrogates are involved
592         for (int i = 0; i < source.length(); ++i) {
593             char ch = source.charAt(i);
594             if (('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z')
595                     || ch == '<' || ch == '>') {
596                 result.append(ch);
597             } else if (ch == ' ') {
598                 // don't copy ever
599             } else if (ch == '-') {
600                 // only copy non-medials AND trailing O-E
601                 if (0 == i
602                         || i == source.length() - 1
603                         || source.charAt(i - 1) == ' '
604                             || source.charAt(i + 1) == ' '
605                                 || (i == source.length() - 2
606                                         && source.charAt(i - 1) == 'O' && source
607                                         .charAt(i + 1) == 'E')) {
608                     System.out.println("****** EXCEPTION " + source);
609                     result.append(ch);
610                 }
611                 // otherwise don't copy
612             } else {
613                 throw new IllegalArgumentException("Illegal Name Char: U+"
614                         + Utility.hex(ch) + ", " + ch);
615             }
616         }
617         return result.toString();
618     }
619
620     /**
621      * These routines use the Java functions, because they only need to act on
622      * ASCII Changes space, - into _, inserts _ between lower and UPPER.
623      */
624     public static String regularize(String source, boolean titlecaseStart) {
625         if (source == null)
626             return source;
627         /*
628          * if (source.equals("noBreak")) { // HACK if (titlecaseStart) return
629          * "NoBreak"; return source; }
630          */
631         StringBuffer result = new StringBuffer();
632         int lastCat = -1;
633         boolean haveFirstCased = true;
634         for (int i = 0; i < source.length(); ++i) {
635             char c = source.charAt(i);
636             if (c == ' ' || c == '-' || c == '_') {
637                 c = '_';
638                 haveFirstCased = true;
639             }
640             if (c == '=')
641                 haveFirstCased = true;
642             int cat = Character.getType(c);
643             if (lastCat == Character.LOWERCASE_LETTER
644                     && cat == Character.UPPERCASE_LETTER) {
645                 result.append('_');
646             }
647             if (haveFirstCased
648                     && (cat == Character.LOWERCASE_LETTER
649                             || cat == Character.TITLECASE_LETTER || cat == Character.UPPERCASE_LETTER)) {
650                 if (titlecaseStart) {
651                     c = Character.toUpperCase(c);
652                 }
653                 haveFirstCased = false;
654             }
655             result.append(c);
656             lastCat = cat;
657         }
658         return result.toString();
659     }
660
661     /**
662      * Utility function for comparing codepoint to string without generating new
663      * string.
664      * 
665      * @param codepoint
666      * @param other
667      * @return true if the codepoint equals the string
668      */
669     public static final boolean equals(int codepoint, String other) {
670         if (other == null) return false;
671         if (other.length() == 1) {
672             return codepoint == other.charAt(0);
673         }
674         if (other.length() == 2) {
675             return other.equals(UTF16.valueOf(codepoint));
676         }
677         return false;
678     }
679
680     /**
681      * Utility function for comparing objects that may be null
682      * string.
683      */
684     public static final <T extends Object> boolean equals(T a, T b) {
685         return a == null ? b == null
686                 : b == null ? false
687                         : a.equals(b);
688     }
689
690     /**
691      * Utility that should be on UnicodeSet
692      * 
693      * @param source
694      * @param result
695      */
696     static public void addAll(UnicodeSetIterator source, UnicodeSet result) {
697         while (source.nextRange()) {
698             if (source.codepoint == UnicodeSetIterator.IS_STRING) {
699                 result.add(source.string);
700             } else {
701                 result.add(source.codepoint, source.codepointEnd);
702             }
703         }
704     }
705
706     /**
707      * Really ought to create a Collection UniqueList, that forces uniqueness.
708      * But for now...
709      */
710     public static Collection addAllUnique(Collection source, Collection result) {
711         for (Iterator it = source.iterator(); it.hasNext();) {
712             addUnique(it.next(), result);
713         }
714         return result;
715     }
716
717     /**
718      * Really ought to create a Collection UniqueList, that forces uniqueness.
719      * But for now...
720      */
721     public static Collection addAllUnique(Object[] source, Collection result) {
722         for (int i = 0; i < source.length; ++i) {
723             addUnique(source[i], result);
724         }
725         return result;
726     }
727
728     static public class Factory {
729         static boolean DEBUG = false;
730
731         Map<String, UnicodeProperty> canonicalNames = new TreeMap<String, UnicodeProperty>();
732
733         Map skeletonNames = new TreeMap();
734
735         Map propertyCache = new HashMap(1);
736
737         public final Factory add(UnicodeProperty sp) {
738             String name2 = sp.getName();
739             if (name2.length() == 0) {
740                 throw new IllegalArgumentException();
741             }
742             canonicalNames.put(name2, sp);
743             skeletonNames.put(toSkeleton(name2), sp);
744             List c = sp.getNameAliases(new ArrayList(1));
745             Iterator it = c.iterator();
746             while (it.hasNext()) {
747                 skeletonNames.put(toSkeleton((String) it.next()), sp);
748             }
749             return this;
750         }
751
752         public final UnicodeProperty getProperty(String propertyAlias) {
753             return (UnicodeProperty) skeletonNames
754             .get(toSkeleton(propertyAlias));
755         }
756
757         public final List<String> getAvailableNames() {
758             return getAvailableNames(null);
759         }
760
761         public final List<String> getAvailableNames(List<String> result) {
762             if (result == null)
763                 result = new ArrayList(1);
764             Iterator it = canonicalNames.keySet().iterator();
765             while (it.hasNext()) {
766                 addUnique(it.next(), result);
767             }
768             return result;
769         }
770
771         public final List getAvailableNames(int propertyTypeMask) {
772             return getAvailableNames(propertyTypeMask, null);
773         }
774
775         public final List getAvailableNames(int propertyTypeMask, List result) {
776             if (result == null)
777                 result = new ArrayList(1);
778             Iterator it = canonicalNames.keySet().iterator();
779             while (it.hasNext()) {
780                 String item = (String) it.next();
781                 UnicodeProperty property = getProperty(item);
782                 if (DEBUG)
783                     System.out.println("Properties: " + item + ","
784                             + property.getType());
785                 if (!property.isType(propertyTypeMask)) {
786                     // System.out.println("Masking: " + property.getType() + ","
787                     // + propertyTypeMask);
788                     continue;
789                 }
790                 addUnique(property.getName(), result);
791             }
792             return result;
793         }
794
795         InversePatternMatcher inverseMatcher = new InversePatternMatcher();
796
797         /**
798          * Format is: propname ('=' | '!=') propvalue ( '|' propValue )*
799          */
800         public final UnicodeSet getSet(String propAndValue,
801                 PatternMatcher matcher, UnicodeSet result) {
802             int equalPos = propAndValue.indexOf('=');
803             String prop = propAndValue.substring(0, equalPos);
804             String value = propAndValue.substring(equalPos + 1);
805             boolean negative = false;
806             if (prop.endsWith("!")) {
807                 prop = prop.substring(0, prop.length() - 1);
808                 negative = true;
809             }
810             prop = prop.trim();
811             UnicodeProperty up = getProperty(prop);
812             if (matcher == null) {
813                 matcher = new SimpleMatcher(value, up
814                         .isType(STRING_OR_MISC_MASK) ? null
815                                 : PROPERTY_COMPARATOR);
816             }
817             if (negative) {
818                 inverseMatcher.set(matcher);
819                 matcher = inverseMatcher;
820             }
821             return up.getSet(matcher.set(value), result);
822         }
823
824         public final UnicodeSet getSet(String propAndValue,
825                 PatternMatcher matcher) {
826             return getSet(propAndValue, matcher, null);
827         }
828
829         public final UnicodeSet getSet(String propAndValue) {
830             return getSet(propAndValue, null, null);
831         }
832
833         public final SymbolTable getSymbolTable(String prefix) {
834             return new PropertySymbolTable(prefix);
835         }
836
837         private class MyXSymbolTable extends UnicodeSet.XSymbolTable {
838             public boolean applyPropertyAlias(String propertyName,
839                     String propertyValue, UnicodeSet result) {
840                 if (false)
841                     System.out.println(propertyName + "=" + propertyValue);
842                 UnicodeProperty prop = getProperty(propertyName);
843                 if (prop == null)
844                     return false;
845                 result.clear();
846                 UnicodeSet x = prop.getSet(propertyValue, result);
847                 return x.size() != 0;
848             }
849         }
850
851         public final UnicodeSet.XSymbolTable getXSymbolTable() {
852             return new MyXSymbolTable();
853         }
854
855         private class PropertySymbolTable implements SymbolTable {
856             static final boolean DEBUG = false;
857
858             private String prefix;
859
860             RegexMatcher regexMatcher = new RegexMatcher();
861
862             PropertySymbolTable(String prefix) {
863                 this.prefix = prefix;
864             }
865
866             public char[] lookup(String s) {
867                 if (DEBUG)
868                     System.out.println("\t(" + prefix + ")Looking up " + s);
869                 // ensure, again, that prefix matches
870                 int start = prefix.length();
871                 if (!s.regionMatches(true, 0, prefix, 0, start))
872                     return null;
873
874                 int pos = s.indexOf(':', start);
875                 if (pos < 0) { // should never happen
876                     throw new IllegalArgumentException(
877                             "Internal Error: missing =: " + s + "\r\n");
878                 }
879                 UnicodeProperty prop = getProperty(s.substring(start, pos));
880                 if (prop == null) {
881                     throw new IllegalArgumentException("Invalid Property in: "
882                             + s + "\r\nUse " + showSet(getAvailableNames()));
883                 }
884                 String value = s.substring(pos + 1);
885                 UnicodeSet set;
886                 if (value.startsWith("\u00AB")) { // regex!
887                     set = prop.getSet(regexMatcher.set(value.substring(1, value
888                             .length() - 1)));
889                 } else {
890                     set = prop.getSet(value);
891                 }
892                 if (set.size() == 0) {
893                     throw new IllegalArgumentException(
894                             "Empty Property-Value in: " + s + "\r\nUse "
895                             + showSet(prop.getAvailableValues()));
896                 }
897                 if (DEBUG)
898                     System.out.println("\t(" + prefix + ")Returning "
899                             + set.toPattern(true));
900                 return set.toPattern(true).toCharArray(); // really ugly
901             }
902
903             private String showSet(List list) {
904                 StringBuffer result = new StringBuffer("[");
905                 boolean first = true;
906                 for (Iterator it = list.iterator(); it.hasNext();) {
907                     if (!first)
908                         result.append(", ");
909                     else
910                         first = false;
911                     result.append(it.next().toString());
912                 }
913                 result.append("]");
914                 return result.toString();
915             }
916
917             public UnicodeMatcher lookupMatcher(int ch) {
918                 return null;
919             }
920
921             public String parseReference(String text, ParsePosition pos,
922                     int limit) {
923                 if (DEBUG)
924                     System.out.println("\t(" + prefix + ")Parsing <"
925                             + text.substring(pos.getIndex(), limit) + ">");
926                 int start = pos.getIndex();
927                 // ensure that it starts with 'prefix'
928                 if (!text
929                         .regionMatches(true, start, prefix, 0, prefix.length()))
930                     return null;
931                 start += prefix.length();
932                 // now see if it is of the form identifier:identifier
933                 int i = getIdentifier(text, start, limit);
934                 if (i == start)
935                     return null;
936                 String prop = text.substring(start, i);
937                 String value = "true";
938                 if (i < limit) {
939                     if (text.charAt(i) == ':') {
940                         int j;
941                         if (text.charAt(i + 1) == '\u00AB') { // regular
942                             // expression
943                             j = text.indexOf('\u00BB', i + 2) + 1; // include
944                             // last
945                             // character
946                             if (j <= 0)
947                                 return null;
948                         } else {
949                             j = getIdentifier(text, i + 1, limit);
950                         }
951                         value = text.substring(i + 1, j);
952                         i = j;
953                     }
954                 }
955                 pos.setIndex(i);
956                 if (DEBUG)
957                     System.out.println("\t(" + prefix + ")Parsed <" + prop
958                             + ">=<" + value + ">");
959                 return prefix + prop + ":" + value;
960             }
961
962             private int getIdentifier(String text, int start, int limit) {
963                 if (DEBUG)
964                     System.out.println("\tGetID <"
965                             + text.substring(start, limit) + ">");
966                 int cp = 0;
967                 int i;
968                 for (i = start; i < limit; i += UTF16.getCharCount(cp)) {
969                     cp = UTF16.charAt(text, i);
970                     if (!com.ibm.icu.lang.UCharacter
971                             .isUnicodeIdentifierPart(cp)
972                             && cp != '.') {
973                         break;
974                     }
975                 }
976                 if (DEBUG)
977                     System.out.println("\tGotID <" + text.substring(start, i)
978                             + ">");
979                 return i;
980             }
981         }
982     }
983
984     public static class FilteredProperty extends UnicodeProperty {
985         private UnicodeProperty property;
986
987         protected StringFilter filter;
988
989         protected UnicodeSetIterator matchIterator = new UnicodeSetIterator(
990                 new UnicodeSet(0, 0x10FFFF));
991
992         protected HashMap backmap;
993
994         boolean allowValueAliasCollisions = false;
995
996         public FilteredProperty(UnicodeProperty property, StringFilter filter) {
997             this.property = property;
998             this.filter = filter;
999         }
1000
1001         public StringFilter getFilter() {
1002             return filter;
1003         }
1004
1005         public UnicodeProperty setFilter(StringFilter filter) {
1006             this.filter = filter;
1007             return this;
1008         }
1009
1010         List temp = new ArrayList(1);
1011
1012         public List _getAvailableValues(List result) {
1013             temp.clear();
1014             return filter.addUnique(property.getAvailableValues(temp), result);
1015         }
1016
1017         public List _getNameAliases(List result) {
1018             temp.clear();
1019             return filter.addUnique(property.getNameAliases(temp), result);
1020         }
1021
1022         public String _getValue(int codepoint) {
1023             return filter.remap(property.getValue(codepoint));
1024         }
1025
1026         public List _getValueAliases(String valueAlias, List result) {
1027             if (backmap == null) {
1028                 backmap = new HashMap(1);
1029                 temp.clear();
1030                 Iterator it = property.getAvailableValues(temp).iterator();
1031                 while (it.hasNext()) {
1032                     String item = (String) it.next();
1033                     String mappedItem = filter.remap(item);
1034                     if (backmap.get(mappedItem) != null
1035                             && !allowValueAliasCollisions) {
1036                         throw new IllegalArgumentException(
1037                                 "Filter makes values collide! " + item + ", "
1038                                 + mappedItem);
1039                     }
1040                     backmap.put(mappedItem, item);
1041                 }
1042             }
1043             valueAlias = (String) backmap.get(valueAlias);
1044             temp.clear();
1045             return filter.addUnique(property.getValueAliases(valueAlias, temp),
1046                     result);
1047         }
1048
1049         public String _getVersion() {
1050             return property.getVersion();
1051         }
1052
1053         public boolean isAllowValueAliasCollisions() {
1054             return allowValueAliasCollisions;
1055         }
1056
1057         public FilteredProperty setAllowValueAliasCollisions(boolean b) {
1058             allowValueAliasCollisions = b;
1059             return this;
1060         }
1061
1062     }
1063
1064     public static abstract class StringFilter implements Cloneable {
1065         public abstract String remap(String original);
1066
1067         public final List addUnique(Collection source, List result) {
1068             if (result == null)
1069                 result = new ArrayList(1);
1070             Iterator it = source.iterator();
1071             while (it.hasNext()) {
1072                 UnicodeProperty.addUnique(remap((String) it.next()), result);
1073             }
1074             return result;
1075         }
1076         /*
1077          * public Object clone() { try { return super.clone(); } catch
1078          * (CloneNotSupportedException e) { throw new
1079          * IllegalStateException("Should never happen."); } }
1080          */
1081     }
1082
1083     public static class MapFilter extends StringFilter {
1084         private Map valueMap;
1085
1086         public MapFilter(Map valueMap) {
1087             this.valueMap = valueMap;
1088         }
1089
1090         public String remap(String original) {
1091             Object changed = valueMap.get(original);
1092             return changed == null ? original : (String) changed;
1093         }
1094
1095         public Map getMap() {
1096             return valueMap;
1097         }
1098     }
1099
1100     public interface PatternMatcher extends ObjectMatcher {
1101         public PatternMatcher set(String pattern);
1102     }
1103
1104     public static class InversePatternMatcher extends InverseMatcher implements
1105     PatternMatcher {
1106         PatternMatcher other;
1107
1108         public PatternMatcher set(PatternMatcher toInverse) {
1109             other = toInverse;
1110             return this;
1111         }
1112
1113         public boolean matches(Object value) {
1114             return !other.matches(value);
1115         }
1116
1117         public PatternMatcher set(String pattern) {
1118             other.set(pattern);
1119             return this;
1120         }
1121     }
1122
1123     public static class SimpleMatcher implements PatternMatcher {
1124         Comparator comparator;
1125
1126         String pattern;
1127
1128         public SimpleMatcher(String pattern, Comparator comparator) {
1129             this.comparator = comparator;
1130             this.pattern = pattern;
1131         }
1132
1133         public boolean matches(Object value) {
1134             if (comparator == null)
1135                 return pattern.equals(value);
1136             return comparator.compare(pattern, value) == 0;
1137         }
1138
1139         public PatternMatcher set(String pattern) {
1140             this.pattern = pattern;
1141             return this;
1142         }
1143     }
1144
1145     public static class RegexMatcher implements UnicodeProperty.PatternMatcher {
1146         private java.util.regex.Matcher matcher;
1147
1148         public UnicodeProperty.PatternMatcher set(String pattern) {
1149             matcher = Pattern.compile(pattern).matcher("");
1150             return this;
1151         }
1152         UFormat foo;
1153         public boolean matches(Object value) {
1154             matcher.reset(value.toString());
1155             return matcher.find();
1156         }
1157     }
1158
1159     public enum AliasAddAction {IGNORE_IF_MISSING, REQUIRE_MAIN_ALIAS, ADD_MAIN_ALIAS}
1160
1161     public static abstract class BaseProperty extends UnicodeProperty {
1162         private static final String[] NO_VALUES = {"No", "N", "F", "False"};
1163
1164         private static final String[] YES_VALUES = {"Yes", "Y", "T", "True"};
1165
1166         /**
1167          * 
1168          */
1169         private static final String[][] YES_NO_ALIASES = new String[][] {YES_VALUES, NO_VALUES};
1170
1171         protected List propertyAliases = new ArrayList(1);
1172
1173         protected Map toValueAliases;
1174
1175         protected String version;
1176
1177         public BaseProperty setMain(String alias, String shortAlias,
1178                 int propertyType, String version) {
1179             setName(alias);
1180             setType(propertyType);
1181             propertyAliases.add(shortAlias);
1182             propertyAliases.add(alias);
1183             if (propertyType == BINARY) {
1184                 addValueAliases(YES_NO_ALIASES, AliasAddAction.ADD_MAIN_ALIAS);
1185             }
1186             this.version = version;
1187             return this;
1188         }
1189
1190         public String _getVersion() {
1191             return version;
1192         }
1193
1194         public List _getNameAliases(List result) {
1195             addAllUnique(propertyAliases, result);
1196             return result;
1197         }
1198
1199         public BaseProperty addValueAliases(String[][] valueAndAlternates,
1200                 AliasAddAction aliasAddAction) {
1201             if (toValueAliases == null)
1202                 _fixValueAliases();
1203             for (int i = 0; i < valueAndAlternates.length; ++i) {
1204                 for (int j = 1; j < valueAndAlternates[0].length; ++j) {
1205                     addValueAlias(valueAndAlternates[i][0],
1206                             valueAndAlternates[i][j], aliasAddAction);
1207                 }
1208             }
1209             return this;
1210         }
1211
1212         public void addValueAlias(String value, String valueAlias,
1213                 AliasAddAction aliasAddAction) {
1214             List result = (List) toValueAliases.get(value);
1215             if (result == null) {
1216                 switch(aliasAddAction) {
1217                 case IGNORE_IF_MISSING: return;
1218                 case REQUIRE_MAIN_ALIAS: throw new IllegalArgumentException("Can't add alias for mising value: " + value);
1219                 case ADD_MAIN_ALIAS: 
1220                     toValueAliases.put(value, result = new ArrayList(0));
1221                     break;
1222                 }
1223             }
1224             addUnique(value, result);
1225             addUnique(valueAlias, result);
1226         }
1227
1228         protected List _getValueAliases(String valueAlias, List result) {
1229             if (toValueAliases == null)
1230                 _fixValueAliases();
1231             List a = (List) toValueAliases.get(valueAlias);
1232             if (a != null)
1233                 addAllUnique(a, result);
1234             return result;
1235         }
1236
1237         protected void _fixValueAliases() {
1238             if (toValueAliases == null)
1239                 toValueAliases = new HashMap(1);
1240             for (Iterator it = getAvailableValues().iterator(); it.hasNext();) {
1241                 Object value = it.next();
1242                 _ensureValueInAliases(value);
1243             }
1244         }
1245
1246         protected void _ensureValueInAliases(Object value) {
1247             List result = (List) toValueAliases.get(value);
1248             if (result == null)
1249                 toValueAliases.put(value, result = new ArrayList(1));
1250             addUnique(value, result);
1251         }
1252
1253         public BaseProperty swapFirst2ValueAliases() {
1254             for (Iterator it = toValueAliases.keySet().iterator(); it.hasNext();) {
1255                 List list = (List) toValueAliases.get(it.next());
1256                 if (list.size() < 2)
1257                     continue;
1258                 Object first = list.get(0);
1259                 list.set(0, list.get(1));
1260                 list.set(1, first);
1261             }
1262             return this;
1263         }
1264
1265         /**
1266          * @param string
1267          * @return
1268          */
1269         public UnicodeProperty addName(String string) {
1270             throw new UnsupportedOperationException();
1271         }
1272
1273     }
1274
1275     public static abstract class SimpleProperty extends BaseProperty {
1276         LinkedHashSet values;
1277
1278         public UnicodeProperty addName(String alias) {
1279             propertyAliases.add(alias);
1280             return this;
1281         }
1282
1283         public SimpleProperty setValues(String valueAlias) {
1284             _addToValues(valueAlias, null);
1285             return this;
1286         }
1287
1288         public SimpleProperty addAliases(String valueAlias, String... aliases) {
1289             _addToValues(valueAlias, null);
1290             return this;
1291         }
1292
1293         public SimpleProperty setValues(String[] valueAliases,
1294                 String[] alternateValueAliases) {
1295             for (int i = 0; i < valueAliases.length; ++i) {
1296                 if (valueAliases[i].equals(UNUSED))
1297                     continue;
1298                 _addToValues(
1299                         valueAliases[i],
1300                         alternateValueAliases != null ? alternateValueAliases[i]
1301                                                                               : null);
1302             }
1303             return this;
1304         }
1305
1306         public SimpleProperty setValues(List valueAliases) {
1307             this.values = new LinkedHashSet(valueAliases);
1308             for (Iterator it = this.values.iterator(); it.hasNext();) {
1309                 _addToValues((String) it.next(), null);
1310             }
1311             return this;
1312         }
1313
1314         public List _getAvailableValues(List result) {
1315             if (values == null)
1316                 _fillValues();
1317             result.addAll(values);
1318             return result;
1319         }
1320
1321         protected void _fillValues() {
1322             List newvalues = (List) getUnicodeMap_internal()
1323             .getAvailableValues(new ArrayList());
1324             for (Iterator it = newvalues.iterator(); it.hasNext();) {
1325                 _addToValues((String) it.next(), null);
1326             }
1327         }
1328
1329         private void _addToValues(String item, String alias) {
1330             if (values == null)
1331                 values = new LinkedHashSet();
1332             if (toValueAliases == null)
1333                 _fixValueAliases();
1334             addUnique(item, values);
1335             _ensureValueInAliases(item);
1336             addValueAlias(item, alias, AliasAddAction.REQUIRE_MAIN_ALIAS);
1337         }
1338         /*        public String _getVersion() {
1339          return version;
1340          }
1341          */
1342     }
1343
1344     public static class UnicodeMapProperty extends BaseProperty {
1345         /*
1346          * Example of usage:
1347          * new UnicodeProperty.UnicodeMapProperty() {
1348          {
1349          unicodeMap = new UnicodeMap();
1350          unicodeMap.setErrorOnReset(true);
1351          unicodeMap.put(0xD, "CR");
1352          unicodeMap.put(0xA, "LF");
1353          UnicodeProperty cat = getProperty("General_Category");
1354          UnicodeSet temp = cat.getSet("Line_Separator")
1355          .addAll(cat.getSet("Paragraph_Separator"))
1356          .addAll(cat.getSet("Control"))
1357          .addAll(cat.getSet("Format"))
1358          .remove(0xD).remove(0xA).remove(0x200C).remove(0x200D);
1359          unicodeMap.putAll(temp, "Control");
1360          UnicodeSet graphemeExtend = getProperty("Grapheme_Extend").getSet("true");
1361          unicodeMap.putAll(graphemeExtend,"Extend");
1362          UnicodeProperty hangul = getProperty("Hangul_Syllable_Type");
1363          unicodeMap.putAll(hangul.getSet("L"),"L");
1364          unicodeMap.putAll(hangul.getSet("V"),"V");
1365          unicodeMap.putAll(hangul.getSet("T"),"T");
1366          unicodeMap.putAll(hangul.getSet("LV"),"LV");
1367          unicodeMap.putAll(hangul.getSet("LVT"),"LVT");
1368          unicodeMap.setMissing("Other");
1369          }
1370          }.setMain("Grapheme_Cluster_Break", "GCB", UnicodeProperty.ENUMERATED, version)
1371          */
1372         protected UnicodeMap unicodeMap;
1373
1374         protected UnicodeMap _getUnicodeMap() {
1375             return unicodeMap;
1376         }
1377
1378         public UnicodeMapProperty set(UnicodeMap map) {
1379             unicodeMap = map.freeze();
1380             return this;
1381         }
1382
1383         protected String _getValue(int codepoint) {
1384             return (String) unicodeMap.getValue(codepoint);
1385         }
1386
1387         /* protected List _getValueAliases(String valueAlias, List result) {
1388          if (!unicodeMap.getAvailableValues().contains(valueAlias)) return result;
1389          result.add(valueAlias);
1390          return result; // no other aliases
1391          }
1392          */protected List _getAvailableValues(List result) {
1393              unicodeMap.getAvailableValues(result);
1394              if (toValueAliases != null) {
1395                  for (Object s : toValueAliases.keySet()) {
1396                      if (!result.contains(s)) {
1397                          result.add(s);
1398                      }
1399                  }
1400              }
1401              return result;
1402          }
1403     }
1404
1405     public boolean isValidValue(String propertyValue) {
1406         if (isType(STRING_OR_MISC_MASK)) {
1407             return true;
1408         }
1409         Collection<String> values = (Collection<String>) getAvailableValues();
1410         for (String valueAlias : values) {
1411             if (UnicodeProperty.compareNames(valueAlias, propertyValue) == 0) {
1412                 return true;
1413             }
1414             for (String valueAlias2 : (Collection<String>) getValueAliases(valueAlias)) {
1415                 if (UnicodeProperty.compareNames(valueAlias2, propertyValue) == 0) {
1416                     return true;
1417                 }
1418             }
1419         }
1420         return false;
1421     }
1422
1423     public List<String> getValueAliases() {
1424         List<String> result = new ArrayList();
1425         if (isType(STRING_OR_MISC_MASK)) {
1426             return result;
1427         }
1428         Collection<String> values = (Collection<String>) getAvailableValues();
1429         for (String valueAlias : values) {
1430             UnicodeProperty.addAllUnique(getValueAliases(valueAlias), result);
1431         }
1432         result.removeAll(values);
1433         return result;
1434     }
1435
1436
1437     public static UnicodeSet addUntested(UnicodeSet result, boolean uniformUnassigned) {
1438         if (uniformUnassigned && result.contains(UnicodeProperty.getSAMPLE_UNASSIGNED())) {
1439             result.addAll(UnicodeProperty.getUNASSIGNED());
1440         }
1441
1442         if (result.contains(UnicodeProperty.SAMPLE_HIGH_SURROGATE)) {
1443             result.addAll(UnicodeProperty.HIGH_SURROGATES);
1444         }
1445         if (result.contains(UnicodeProperty.SAMPLE_HIGH_PRIVATE_USE_SURROGATE)) {
1446             result.addAll(UnicodeProperty.HIGH_PRIVATE_USE_SURROGATES);
1447         }
1448         if (result.contains(UnicodeProperty.SAMPLE_LOW_SURROGATE)) {
1449             result.addAll(UnicodeProperty.LOW_SURROGATES);
1450         }
1451
1452         if (result.contains(UnicodeProperty.SAMPLE_PRIVATE_USE_AREA)) {
1453             result.addAll(UnicodeProperty.PRIVATE_USE_AREA);
1454         }
1455         if (result.contains(UnicodeProperty.SAMPLE_PRIVATE_USE_AREA_A)) {
1456             result.addAll(UnicodeProperty.PRIVATE_USE_AREA_A);
1457         }
1458         if (result.contains(UnicodeProperty.SAMPLE_PRIVATE_USE_AREA_B)) {
1459             result.addAll(UnicodeProperty.PRIVATE_USE_AREA_B);
1460         }
1461
1462         return result;
1463     }
1464
1465     public static UnicodeMap addUntested(UnicodeMap result, boolean uniformUnassigned) {
1466         Object temp;
1467         if (uniformUnassigned && null != (temp = result.get(UnicodeProperty.getSAMPLE_UNASSIGNED()))) {
1468             result.putAll(UnicodeProperty.getUNASSIGNED(), temp);
1469         }
1470
1471         if (null != (temp = result.get(UnicodeProperty.SAMPLE_HIGH_SURROGATE))) {
1472             result.putAll(UnicodeProperty.HIGH_SURROGATES, temp);
1473         }
1474         if (null != (temp = result.get(UnicodeProperty.SAMPLE_HIGH_PRIVATE_USE_SURROGATE))) {
1475             result.putAll(UnicodeProperty.HIGH_PRIVATE_USE_SURROGATES, temp);
1476         }
1477         if (null != (temp = result.get(UnicodeProperty.SAMPLE_LOW_SURROGATE))) {
1478             result.putAll(UnicodeProperty.LOW_SURROGATES, temp);
1479         }
1480
1481         if (null != (temp = result.get(UnicodeProperty.SAMPLE_PRIVATE_USE_AREA))) {
1482             result.putAll(UnicodeProperty.PRIVATE_USE_AREA, temp);
1483         }
1484         if (null != (temp = result.get(UnicodeProperty.SAMPLE_PRIVATE_USE_AREA_A))) {
1485             result.putAll(UnicodeProperty.PRIVATE_USE_AREA_A, temp);
1486         }
1487         if (null != (temp = result.get(UnicodeProperty.SAMPLE_PRIVATE_USE_AREA_B))) {
1488             result.putAll(UnicodeProperty.PRIVATE_USE_AREA_B, temp);
1489         }
1490         return result;
1491     }
1492
1493     public boolean isDefault(int cp) {
1494         String value = getValue(cp);
1495         if (isType(STRING_OR_MISC_MASK)) {
1496             return equals(cp, value);
1497         }
1498         String defaultValue = getValue(getSAMPLE_UNASSIGNED());
1499         return defaultValue == null ? value == null : defaultValue.equals(value);   
1500     }
1501
1502     public boolean hasUniformUnassigned() {
1503         return hasUniformUnassigned;
1504     }
1505     protected UnicodeProperty setUniformUnassigned(boolean hasUniformUnassigned) {
1506         this.hasUniformUnassigned = hasUniformUnassigned;
1507         return this;
1508     }
1509
1510     public static class UnicodeSetProperty extends BaseProperty {
1511         protected UnicodeSet unicodeSet;
1512         private static final String[] YESNO_ARRAY = new String[]{"Yes", "No"};
1513         private static final List YESNO = Arrays.asList(YESNO_ARRAY);
1514
1515         public UnicodeSetProperty set(UnicodeSet set) {
1516             unicodeSet = set.freeze();
1517             return this;
1518         }
1519
1520         public UnicodeSetProperty set(String string) {
1521             // TODO Auto-generated method stub
1522             return set(new UnicodeSet(string).freeze());
1523         }
1524
1525         protected String _getValue(int codepoint) {
1526             return YESNO_ARRAY[unicodeSet.contains(codepoint) ? 0 : 1];
1527         }
1528
1529         protected List _getAvailableValues(List result) {
1530             return YESNO;
1531         }
1532     }
1533
1534     //    private static class StringTransformProperty extends SimpleProperty {
1535     //        Transform<String,String> transform;
1536     //
1537     //        public StringTransformProperty(Transform<String,String> transform, boolean hasUniformUnassigned) {
1538     //            this.transform = transform;
1539     //            setUniformUnassigned(hasUniformUnassigned);
1540     //        }
1541     //        protected String _getValue(int codepoint) {
1542     //            return transform.transform(UTF16.valueOf(codepoint));
1543     //        }
1544     //    }
1545     //
1546     //    private static class CodepointTransformProperty extends SimpleProperty {
1547     //        Transform<Integer,String> transform;
1548     //
1549     //        public CodepointTransformProperty(Transform<Integer,String> transform, boolean hasUniformUnassigned) {
1550     //            this.transform = transform;
1551     //            setUniformUnassigned(hasUniformUnassigned);
1552     //        }
1553     //        protected String _getValue(int codepoint) {
1554     //            return transform.transform(codepoint);
1555     //        }
1556     //    }
1557 }
1558