]> gitweb.fperrin.net Git - Dictionary.git/blobdiff - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/test/util/UnicodeProperty.java
go
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / test / util / UnicodeProperty.java
old mode 100755 (executable)
new mode 100644 (file)
index 0aaa6cc..d96c863
-//##header\r
-//#if defined(FOUNDATION10) || defined(J2SE13)\r
-//#else\r
-/*\r
- *******************************************************************************\r
- * Copyright (C) 1996-2009, International Business Machines Corporation and    *\r
- * others. All Rights Reserved.                                                *\r
- *******************************************************************************\r
- */\r
-package com.ibm.icu.dev.test.util;\r
-\r
-import java.io.PrintWriter;\r
-import java.io.StringWriter;\r
-import java.text.ParsePosition;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.TreeMap;\r
-import java.util.regex.Pattern;\r
-\r
-import com.ibm.icu.dev.test.util.CollectionUtilities.InverseMatcher;\r
-import com.ibm.icu.dev.test.util.CollectionUtilities.ObjectMatcher;\r
-import com.ibm.icu.impl.Utility;\r
-import com.ibm.icu.text.SymbolTable;\r
-import com.ibm.icu.text.UTF16;\r
-import com.ibm.icu.text.UnicodeMatcher;\r
-import com.ibm.icu.text.UnicodeSet;\r
-import com.ibm.icu.text.UnicodeSetIterator;\r
-\r
-public abstract class UnicodeProperty extends UnicodeLabel {\r
-\r
-    public static boolean DEBUG = false;\r
-\r
-    public static String CHECK_NAME = "FC_NFKC_Closure";\r
-\r
-    public static int CHECK_VALUE = 0x037A;\r
-\r
-    private String name;\r
-\r
-    private String firstNameAlias = null;\r
-\r
-    private int type;\r
-\r
-    private Map valueToFirstValueAlias = null;\r
-\r
-    /*\r
-     * Name: Unicode_1_Name Name: ISO_Comment Name: Name Name: Unicode_1_Name\r
-     * \r
-     */\r
-\r
-    public static final int UNKNOWN = 0, BINARY = 2, EXTENDED_BINARY = 3,\r
-            ENUMERATED = 4, EXTENDED_ENUMERATED = 5, CATALOG = 6,\r
-            EXTENDED_CATALOG = 7, MISC = 8, EXTENDED_MISC = 9, STRING = 10,\r
-            EXTENDED_STRING = 11, NUMERIC = 12, EXTENDED_NUMERIC = 13,\r
-            START_TYPE = 2, LIMIT_TYPE = 14, EXTENDED_MASK = 1,\r
-            CORE_MASK = ~EXTENDED_MASK, BINARY_MASK = (1 << BINARY)\r
-                    | (1 << EXTENDED_BINARY), STRING_MASK = (1 << STRING)\r
-                    | (1 << EXTENDED_STRING),\r
-            STRING_OR_MISC_MASK = (1 << STRING) | (1 << EXTENDED_STRING)\r
-                    | (1 << MISC) | (1 << EXTENDED_MISC),\r
-            ENUMERATED_OR_CATALOG_MASK = (1 << ENUMERATED)\r
-                    | (1 << EXTENDED_ENUMERATED) | (1 << CATALOG)\r
-                    | (1 << EXTENDED_CATALOG);\r
-\r
-    private static final String[] TYPE_NAMES = { "Unknown", "Unknown",\r
-            "Binary", "Extended Binary", "Enumerated", "Extended Enumerated",\r
-            "Catalog", "Extended Catalog", "Miscellaneous",\r
-            "Extended Miscellaneous", "String", "Extended String", "Numeric",\r
-            "Extended Numeric", };\r
-\r
-    public static String getTypeName(int propType) {\r
-        return TYPE_NAMES[propType];\r
-    }\r
-\r
-    public final String getName() {\r
-        return name;\r
-    }\r
-\r
-    public final int getType() {\r
-        return type;\r
-    }\r
-\r
-    public final boolean isType(int mask) {\r
-        return ((1 << type) & mask) != 0;\r
-    }\r
-\r
-    protected final void setName(String string) {\r
-        if (string == null)\r
-            throw new IllegalArgumentException("Name must not be null");\r
-        name = string;\r
-    }\r
-\r
-    protected final void setType(int i) {\r
-        type = i;\r
-    }\r
-\r
-    public String getVersion() {\r
-        return _getVersion();\r
-    }\r
-\r
-    public String getValue(int codepoint) {\r
-        if (DEBUG && CHECK_VALUE == codepoint && CHECK_NAME.equals(getName())) {\r
-            String value = _getValue(codepoint);\r
-            System.out.println(getName() + "(" + Utility.hex(codepoint) + "):"\r
-                    + (getType() == STRING ? Utility.hex(value) : value));\r
-            return value;\r
-        }\r
-        return _getValue(codepoint);\r
-    }\r
-\r
-    // public String getValue(int codepoint, boolean isShort) {\r
-    // return getValue(codepoint);\r
-    // }\r
-\r
-    public List getNameAliases(List result) {\r
-        if (result == null)\r
-            result = new ArrayList(1);\r
-        return _getNameAliases(result);\r
-    }\r
-\r
-    public List getValueAliases(String valueAlias, List result) {\r
-        if (result == null)\r
-            result = new ArrayList(1);\r
-        result = _getValueAliases(valueAlias, result);\r
-        if (!result.contains(valueAlias)) { // FIX && type < NUMERIC\r
-            result = _getValueAliases(valueAlias, result); // for debugging\r
-            throw new IllegalArgumentException("Internal error: " + getName()\r
-                    + " doesn't contain " + valueAlias + ": "\r
-                    + new BagFormatter().join(result));\r
-        }\r
-        return result;\r
-    }\r
-\r
-    public List getAvailableValues(List result) {\r
-        if (result == null)\r
-            result = new ArrayList(1);\r
-        return _getAvailableValues(result);\r
-    }\r
-\r
-    protected abstract String _getVersion();\r
-\r
-    protected abstract String _getValue(int codepoint);\r
-\r
-    protected abstract List _getNameAliases(List result);\r
-\r
-    protected abstract List _getValueAliases(String valueAlias, List result);\r
-\r
-    protected abstract List _getAvailableValues(List result);\r
-\r
-    // conveniences\r
-    public final List getNameAliases() {\r
-        return getNameAliases(null);\r
-    }\r
-\r
-    public final List getValueAliases(String valueAlias) {\r
-        return getValueAliases(valueAlias, null);\r
-    }\r
-\r
-    public final List getAvailableValues() {\r
-        return getAvailableValues(null);\r
-    }\r
-\r
-    public final String getValue(int codepoint, boolean getShortest) {\r
-        String result = getValue(codepoint);\r
-        if (type >= MISC || result == null || !getShortest)\r
-            return result;\r
-        return getFirstValueAlias(result);\r
-    }\r
-\r
-    public final String getFirstNameAlias() {\r
-        if (firstNameAlias == null) {\r
-            firstNameAlias = (String) getNameAliases().get(0);\r
-        }\r
-        return firstNameAlias;\r
-    }\r
-\r
-    public final String getFirstValueAlias(String value) {\r
-        if (valueToFirstValueAlias == null)\r
-            _getFirstValueAliasCache();\r
-        return (String) valueToFirstValueAlias.get(value);\r
-    }\r
-\r
-    private void _getFirstValueAliasCache() {\r
-        maxValueWidth = 0;\r
-        maxFirstValueAliasWidth = 0;\r
-        valueToFirstValueAlias = new HashMap(1);\r
-        Iterator it = getAvailableValues().iterator();\r
-        while (it.hasNext()) {\r
-            String value = (String) it.next();\r
-            String first = (String) getValueAliases(value).get(0);\r
-            if (first == null) { // internal error\r
-                throw new IllegalArgumentException(\r
-                        "Value not in value aliases: " + value);\r
-            }\r
-            if (DEBUG && CHECK_NAME.equals(getName())) {\r
-                System.out.println("First Alias: " + getName() + ": " + value\r
-                        + " => " + first\r
-                        + new BagFormatter().join(getValueAliases(value)));\r
-            }\r
-            valueToFirstValueAlias.put(value, first);\r
-            if (value.length() > maxValueWidth) {\r
-                maxValueWidth = value.length();\r
-            }\r
-            if (first.length() > maxFirstValueAliasWidth) {\r
-                maxFirstValueAliasWidth = first.length();\r
-            }\r
-        }\r
-    }\r
-\r
-    private int maxValueWidth = -1;\r
-\r
-    private int maxFirstValueAliasWidth = -1;\r
-\r
-    public int getMaxWidth(boolean getShortest) {\r
-        if (maxValueWidth < 0)\r
-            _getFirstValueAliasCache();\r
-        if (getShortest)\r
-            return maxFirstValueAliasWidth;\r
-        return maxValueWidth;\r
-    }\r
-\r
-    public final UnicodeSet getSet(String propertyValue) {\r
-        return getSet(propertyValue, null);\r
-    }\r
-\r
-    public final UnicodeSet getSet(PatternMatcher matcher) {\r
-        return getSet(matcher, null);\r
-    }\r
-\r
-    public final UnicodeSet getSet(String propertyValue, UnicodeSet result) {\r
-        return getSet(new SimpleMatcher(propertyValue,\r
-                isType(STRING_OR_MISC_MASK) ? null : PROPERTY_COMPARATOR),\r
-                result);\r
-    }\r
-\r
-    private UnicodeMap unicodeMap = null;\r
-\r
-    public static final String UNUSED = "??";\r
-\r
-    public final UnicodeSet getSet(PatternMatcher matcher, UnicodeSet result) {\r
-        if (result == null)\r
-            result = new UnicodeSet();\r
-        if (isType(STRING_OR_MISC_MASK)) {\r
-            for (int i = 0; i <= 0x10FFFF; ++i) {\r
-                String value = getValue(i);\r
-                if (value != null && matcher.matches(value)) {\r
-                    result.add(i);\r
-                }\r
-            }\r
-            return result;\r
-        }\r
-        List temp = new ArrayList(1); // to avoid reallocating...\r
-        UnicodeMap um = getUnicodeMap_internal();\r
-        Iterator it = um.getAvailableValues(null).iterator();\r
-        main: while (it.hasNext()) {\r
-            String value = (String) it.next();\r
-            temp.clear();\r
-            Iterator it2 = getValueAliases(value, temp).iterator();\r
-            while (it2.hasNext()) {\r
-                String value2 = (String) it2.next();\r
-                // System.out.println("Values:" + value2);\r
-                if (matcher.matches(value2)\r
-                        || matcher.matches(toSkeleton(value2))) {\r
-                    um.getSet(value, result);\r
-                    continue main;\r
-                }\r
-            }\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /*\r
-     * public UnicodeSet getMatchSet(UnicodeSet result) { if (result == null)\r
-     * result = new UnicodeSet(); addAll(matchIterator, result); return result; }\r
-     * \r
-     * public void setMatchSet(UnicodeSet set) { matchIterator = new\r
-     * UnicodeSetIterator(set); }\r
-     */\r
-\r
-    /**\r
-     * Utility for debugging\r
-     */\r
-    public static String getStack() {\r
-        Exception e = new Exception();\r
-        StringWriter sw = new StringWriter();\r
-        PrintWriter pw = new PrintWriter(sw);\r
-        e.printStackTrace(pw);\r
-        pw.flush();\r
-        return "Showing Stack with fake " + sw.getBuffer().toString();\r
-    }\r
-\r
-    // TODO use this instead of plain strings\r
-    public static class Name implements Comparable {\r
-        private String skeleton;\r
-\r
-        private String pretty;\r
-\r
-        public final int RAW = 0, TITLE = 1, NORMAL = 2;\r
-\r
-        public Name(String name, int style) {\r
-            if (name == null)\r
-                name = "";\r
-            if (style == RAW) {\r
-                skeleton = pretty = name;\r
-            } else {\r
-                pretty = regularize(name, style == TITLE);\r
-                skeleton = toSkeleton(pretty);\r
-            }\r
-        }\r
-\r
-        public int compareTo(Object o) {\r
-            return skeleton.compareTo(((Name) o).skeleton);\r
-        }\r
-\r
-        public boolean equals(Object o) {\r
-            return skeleton.equals(((Name) o).skeleton);\r
-        }\r
-\r
-        public int hashCode() {\r
-            return skeleton.hashCode();\r
-        }\r
-\r
-        public String toString() {\r
-            return pretty;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * @return the unicode map\r
-     */\r
-    public UnicodeMap getUnicodeMap() {\r
-        return getUnicodeMap(false);\r
-    }\r
-\r
-    /**\r
-     * @return the unicode map\r
-     */\r
-    public UnicodeMap getUnicodeMap(boolean getShortest) {\r
-        if (!getShortest)\r
-            return (UnicodeMap) getUnicodeMap_internal().cloneAsThawed();\r
-        UnicodeMap result = new UnicodeMap();\r
-        for (int i = 0; i <= 0x10FFFF; ++i) {\r
-            // if (DEBUG && i == 0x41) System.out.println(i + "\t" +\r
-            // getValue(i));\r
-            String value = getValue(i, true);\r
-            result.put(i, value);\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * @return the unicode map\r
-     */\r
-    protected UnicodeMap getUnicodeMap_internal() {\r
-        if (unicodeMap == null)\r
-            unicodeMap = _getUnicodeMap();\r
-        return unicodeMap;\r
-    }\r
-\r
-    protected UnicodeMap _getUnicodeMap() {\r
-        UnicodeMap result = new UnicodeMap();\r
-        HashMap myIntern = new HashMap();\r
-        for (int i = 0; i <= 0x10FFFF; ++i) {\r
-            // if (DEBUG && i == 0x41) System.out.println(i + "\t" +\r
-            // getValue(i));\r
-            String value = getValue(i);\r
-            String iValue = (String) myIntern.get(value);\r
-            if (iValue == null)\r
-                myIntern.put(value, iValue = value);\r
-            result.put(i, iValue);\r
-        }\r
-        if (DEBUG) {\r
-            for (int i = 0; i <= 0x10FFFF; ++i) {\r
-                // if (DEBUG && i == 0x41) System.out.println(i + "\t" +\r
-                // getValue(i));\r
-                String value = getValue(i);\r
-                String resultValue = (String) result.getValue(i);\r
-                if (!value.equals(resultValue)) {\r
-                    throw new RuntimeException("Value failure at: "\r
-                            + Utility.hex(i));\r
-                }\r
-            }\r
-        }\r
-        if (DEBUG && CHECK_NAME.equals(getName())) {\r
-            System.out.println(getName() + ":\t" + getClass().getName() + "\t"\r
-                    + getVersion());\r
-            System.out.println(getStack());\r
-            System.out.println(result);\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Really ought to create a Collection UniqueList, that forces uniqueness.\r
-     * But for now...\r
-     */\r
-    public static Collection addUnique(Object obj, Collection result) {\r
-        if (obj != null && !result.contains(obj))\r
-            result.add(obj);\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Utility for managing property & non-string value aliases\r
-     */\r
-    public static final Comparator PROPERTY_COMPARATOR = new Comparator() {\r
-        public int compare(Object o1, Object o2) {\r
-            return compareNames((String) o1, (String) o2);\r
-        }\r
-    };\r
-\r
-    /**\r
-     * Utility for managing property & non-string value aliases\r
-     * \r
-     */\r
-    // TODO optimize\r
-    public static boolean equalNames(String a, String b) {\r
-        if (a == b)\r
-            return true;\r
-        if (a == null)\r
-            return false;\r
-        return toSkeleton(a).equals(toSkeleton(b));\r
-    }\r
-\r
-    /**\r
-     * Utility for managing property & non-string value aliases\r
-     */\r
-    // TODO optimize\r
-    public static int compareNames(String a, String b) {\r
-        if (a == b)\r
-            return 0;\r
-        if (a == null)\r
-            return -1;\r
-        if (b == null)\r
-            return 1;\r
-        return toSkeleton(a).compareTo(toSkeleton(b));\r
-    }\r
-\r
-    /**\r
-     * Utility for managing property & non-string value aliases\r
-     */\r
-    // TODO account for special names, tibetan, hangul\r
-    public static String toSkeleton(String source) {\r
-        if (source == null)\r
-            return null;\r
-        StringBuffer skeletonBuffer = new StringBuffer();\r
-        boolean gotOne = false;\r
-        // remove spaces, '_', '-'\r
-        // we can do this with char, since no surrogates are involved\r
-        for (int i = 0; i < source.length(); ++i) {\r
-            char ch = source.charAt(i);\r
-            if (i > 0 && (ch == '_' || ch == ' ' || ch == '-')) {\r
-                gotOne = true;\r
-            } else {\r
-                char ch2 = Character.toLowerCase(ch);\r
-                if (ch2 != ch) {\r
-                    gotOne = true;\r
-                    skeletonBuffer.append(ch2);\r
-                } else {\r
-                    skeletonBuffer.append(ch);\r
-                }\r
-            }\r
-        }\r
-        if (!gotOne)\r
-            return source; // avoid string creation\r
-        return skeletonBuffer.toString();\r
-    }\r
-\r
-    // get the name skeleton\r
-    public static String toNameSkeleton(String source) {\r
-        if (source == null)\r
-            return null;\r
-        StringBuffer result = new StringBuffer();\r
-        // remove spaces, medial '-'\r
-        // we can do this with char, since no surrogates are involved\r
-        for (int i = 0; i < source.length(); ++i) {\r
-            char ch = source.charAt(i);\r
-            if (('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z')\r
-                    || ch == '<' || ch == '>') {\r
-                result.append(ch);\r
-            } else if (ch == ' ') {\r
-                // don't copy ever\r
-            } else if (ch == '-') {\r
-                // only copy non-medials AND trailing O-E\r
-                if (0 == i\r
-                        || i == source.length() - 1\r
-                        || source.charAt(i - 1) == ' '\r
-                        || source.charAt(i + 1) == ' '\r
-                        || (i == source.length() - 2\r
-                                && source.charAt(i - 1) == 'O' && source\r
-                                .charAt(i + 1) == 'E')) {\r
-                    System.out.println("****** EXCEPTION " + source);\r
-                    result.append(ch);\r
-                }\r
-                // otherwise don't copy\r
-            } else {\r
-                throw new IllegalArgumentException("Illegal Name Char: U+"\r
-                        + Utility.hex(ch) + ", " + ch);\r
-            }\r
-        }\r
-        return result.toString();\r
-    }\r
-\r
-    /**\r
-     * These routines use the Java functions, because they only need to act on\r
-     * ASCII Changes space, - into _, inserts _ between lower and UPPER.\r
-     */\r
-    public static String regularize(String source, boolean titlecaseStart) {\r
-        if (source == null)\r
-            return source;\r
-        /*\r
-         * if (source.equals("noBreak")) { // HACK if (titlecaseStart) return\r
-         * "NoBreak"; return source; }\r
-         */\r
-        StringBuffer result = new StringBuffer();\r
-        int lastCat = -1;\r
-        boolean haveFirstCased = true;\r
-        for (int i = 0; i < source.length(); ++i) {\r
-            char c = source.charAt(i);\r
-            if (c == ' ' || c == '-' || c == '_') {\r
-                c = '_';\r
-                haveFirstCased = true;\r
-            }\r
-            if (c == '=')\r
-                haveFirstCased = true;\r
-            int cat = Character.getType(c);\r
-            if (lastCat == Character.LOWERCASE_LETTER\r
-                    && cat == Character.UPPERCASE_LETTER) {\r
-                result.append('_');\r
-            }\r
-            if (haveFirstCased\r
-                    && (cat == Character.LOWERCASE_LETTER\r
-                            || cat == Character.TITLECASE_LETTER || cat == Character.UPPERCASE_LETTER)) {\r
-                if (titlecaseStart) {\r
-                    c = Character.toUpperCase(c);\r
-                }\r
-                haveFirstCased = false;\r
-            }\r
-            result.append(c);\r
-            lastCat = cat;\r
-        }\r
-        return result.toString();\r
-    }\r
-\r
-    /**\r
-     * Utility function for comparing codepoint to string without generating new\r
-     * string.\r
-     * \r
-     * @param codepoint\r
-     * @param other\r
-     * @return true if the codepoint equals the string\r
-     */\r
-    public static final boolean equals(int codepoint, String other) {\r
-        if (other.length() == 1) {\r
-            return codepoint == other.charAt(0);\r
-        }\r
-        if (other.length() == 2) {\r
-            return other.equals(UTF16.valueOf(codepoint));\r
-        }\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * Utility that should be on UnicodeSet\r
-     * \r
-     * @param source\r
-     * @param result\r
-     */\r
-    static public void addAll(UnicodeSetIterator source, UnicodeSet result) {\r
-        while (source.nextRange()) {\r
-            if (source.codepoint == UnicodeSetIterator.IS_STRING) {\r
-                result.add(source.string);\r
-            } else {\r
-                result.add(source.codepoint, source.codepointEnd);\r
-            }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Really ought to create a Collection UniqueList, that forces uniqueness.\r
-     * But for now...\r
-     */\r
-    public static Collection addAllUnique(Collection source, Collection result) {\r
-        for (Iterator it = source.iterator(); it.hasNext();) {\r
-            addUnique(it.next(), result);\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Really ought to create a Collection UniqueList, that forces uniqueness.\r
-     * But for now...\r
-     */\r
-    public static Collection addAllUnique(Object[] source, Collection result) {\r
-        for (int i = 0; i < source.length; ++i) {\r
-            addUnique(source[i], result);\r
-        }\r
-        return result;\r
-    }\r
-\r
-    static public class Factory {\r
-        static boolean DEBUG = false;\r
-\r
-        Map canonicalNames = new TreeMap();\r
-\r
-        Map skeletonNames = new TreeMap();\r
-\r
-        Map propertyCache = new HashMap(1);\r
-\r
-        public final Factory add(UnicodeProperty sp) {\r
-            canonicalNames.put(sp.getName(), sp);\r
-            List c = sp.getNameAliases(new ArrayList(1));\r
-            Iterator it = c.iterator();\r
-            while (it.hasNext()) {\r
-                skeletonNames.put(toSkeleton((String) it.next()), sp);\r
-            }\r
-            return this;\r
-        }\r
-\r
-        public final UnicodeProperty getProperty(String propertyAlias) {\r
-            return (UnicodeProperty) skeletonNames\r
-                    .get(toSkeleton(propertyAlias));\r
-        }\r
-\r
-        public final List getAvailableNames() {\r
-            return getAvailableNames(null);\r
-        }\r
-\r
-        public final List getAvailableNames(List result) {\r
-            if (result == null)\r
-                result = new ArrayList(1);\r
-            Iterator it = canonicalNames.keySet().iterator();\r
-            while (it.hasNext()) {\r
-                addUnique(it.next(), result);\r
-            }\r
-            return result;\r
-        }\r
-\r
-        public final List getAvailableNames(int propertyTypeMask) {\r
-            return getAvailableNames(propertyTypeMask, null);\r
-        }\r
-\r
-        public final List getAvailableNames(int propertyTypeMask, List result) {\r
-            if (result == null)\r
-                result = new ArrayList(1);\r
-            Iterator it = canonicalNames.keySet().iterator();\r
-            while (it.hasNext()) {\r
-                String item = (String) it.next();\r
-                UnicodeProperty property = getProperty(item);\r
-                if (DEBUG)\r
-                    System.out.println("Properties: " + item + ","\r
-                            + property.getType());\r
-                if (!property.isType(propertyTypeMask)) {\r
-                    // System.out.println("Masking: " + property.getType() + ","\r
-                    // + propertyTypeMask);\r
-                    continue;\r
-                }\r
-                addUnique(property.getName(), result);\r
-            }\r
-            return result;\r
-        }\r
-\r
-        InversePatternMatcher inverseMatcher = new InversePatternMatcher();\r
-\r
-        /**\r
-         * Format is: propname ('=' | '!=') propvalue ( '|' propValue )*\r
-         */\r
-        public final UnicodeSet getSet(String propAndValue,\r
-                PatternMatcher matcher, UnicodeSet result) {\r
-            int equalPos = propAndValue.indexOf('=');\r
-            String prop = propAndValue.substring(0, equalPos);\r
-            String value = propAndValue.substring(equalPos + 1);\r
-            boolean negative = false;\r
-            if (prop.endsWith("!")) {\r
-                prop = prop.substring(0, prop.length() - 1);\r
-                negative = true;\r
-            }\r
-            prop = prop.trim();\r
-            UnicodeProperty up = getProperty(prop);\r
-            if (matcher == null) {\r
-                matcher = new SimpleMatcher(value, up\r
-                        .isType(STRING_OR_MISC_MASK) ? null\r
-                        : PROPERTY_COMPARATOR);\r
-            }\r
-            if (negative) {\r
-                inverseMatcher.set(matcher);\r
-                matcher = inverseMatcher;\r
-            }\r
-            return up.getSet(matcher.set(value), result);\r
-        }\r
-\r
-        public final UnicodeSet getSet(String propAndValue,\r
-                PatternMatcher matcher) {\r
-            return getSet(propAndValue, matcher, null);\r
-        }\r
-\r
-        public final UnicodeSet getSet(String propAndValue) {\r
-            return getSet(propAndValue, null, null);\r
-        }\r
-\r
-        public final SymbolTable getSymbolTable(String prefix) {\r
-            return new PropertySymbolTable(prefix);\r
-        }\r
-\r
-        private class MyXSymbolTable extends UnicodeSet.XSymbolTable {\r
-            public boolean applyPropertyAlias(String propertyName,\r
-                    String propertyValue, UnicodeSet result) {\r
-                if (false)\r
-                    System.out.println(propertyName + "=" + propertyValue);\r
-                UnicodeProperty prop = getProperty(propertyName);\r
-                if (prop == null)\r
-                    return false;\r
-                result.clear();\r
-                UnicodeSet x = prop.getSet(propertyValue, result);\r
-                return x.size() != 0;\r
-            }\r
-        }\r
-\r
-        public final UnicodeSet.XSymbolTable getXSymbolTable() {\r
-            return new MyXSymbolTable();\r
-        }\r
-\r
-        private class PropertySymbolTable implements SymbolTable {\r
-            static final boolean DEBUG = false;\r
-\r
-            private String prefix;\r
-\r
-            RegexMatcher regexMatcher = new RegexMatcher();\r
-\r
-            PropertySymbolTable(String prefix) {\r
-                this.prefix = prefix;\r
-            }\r
-\r
-            public char[] lookup(String s) {\r
-                if (DEBUG)\r
-                    System.out.println("\t(" + prefix + ")Looking up " + s);\r
-                // ensure, again, that prefix matches\r
-                int start = prefix.length();\r
-                if (!s.regionMatches(true, 0, prefix, 0, start))\r
-                    return null;\r
-\r
-                int pos = s.indexOf(':', start);\r
-                if (pos < 0) { // should never happen\r
-                    throw new IllegalArgumentException(\r
-                            "Internal Error: missing =: " + s + "\r\n");\r
-                }\r
-                UnicodeProperty prop = getProperty(s.substring(start, pos));\r
-                if (prop == null) {\r
-                    throw new IllegalArgumentException("Invalid Property in: "\r
-                            + s + "\r\nUse " + showSet(getAvailableNames()));\r
-                }\r
-                String value = s.substring(pos + 1);\r
-                UnicodeSet set;\r
-                if (value.startsWith("\u00AB")) { // regex!\r
-                    set = prop.getSet(regexMatcher.set(value.substring(1, value\r
-                            .length() - 1)));\r
-                } else {\r
-                    set = prop.getSet(value);\r
-                }\r
-                if (set.size() == 0) {\r
-                    throw new IllegalArgumentException(\r
-                            "Empty Property-Value in: " + s + "\r\nUse "\r
-                                    + showSet(prop.getAvailableValues()));\r
-                }\r
-                if (DEBUG)\r
-                    System.out.println("\t(" + prefix + ")Returning "\r
-                            + set.toPattern(true));\r
-                return set.toPattern(true).toCharArray(); // really ugly\r
-            }\r
-\r
-            private String showSet(List list) {\r
-                StringBuffer result = new StringBuffer("[");\r
-                boolean first = true;\r
-                for (Iterator it = list.iterator(); it.hasNext();) {\r
-                    if (!first)\r
-                        result.append(", ");\r
-                    else\r
-                        first = false;\r
-                    result.append(it.next().toString());\r
-                }\r
-                result.append("]");\r
-                return result.toString();\r
-            }\r
-\r
-            public UnicodeMatcher lookupMatcher(int ch) {\r
-                return null;\r
-            }\r
-\r
-            public String parseReference(String text, ParsePosition pos,\r
-                    int limit) {\r
-                if (DEBUG)\r
-                    System.out.println("\t(" + prefix + ")Parsing <"\r
-                            + text.substring(pos.getIndex(), limit) + ">");\r
-                int start = pos.getIndex();\r
-                // ensure that it starts with 'prefix'\r
-                if (!text\r
-                        .regionMatches(true, start, prefix, 0, prefix.length()))\r
-                    return null;\r
-                start += prefix.length();\r
-                // now see if it is of the form identifier:identifier\r
-                int i = getIdentifier(text, start, limit);\r
-                if (i == start)\r
-                    return null;\r
-                String prop = text.substring(start, i);\r
-                String value = "true";\r
-                if (i < limit) {\r
-                    if (text.charAt(i) == ':') {\r
-                        int j;\r
-                        if (text.charAt(i + 1) == '\u00AB') { // regular\r
-                                                                // expression\r
-                            j = text.indexOf('\u00BB', i + 2) + 1; // include\r
-                                                                    // last\r
-                                                                    // character\r
-                            if (j <= 0)\r
-                                return null;\r
-                        } else {\r
-                            j = getIdentifier(text, i + 1, limit);\r
-                        }\r
-                        value = text.substring(i + 1, j);\r
-                        i = j;\r
-                    }\r
-                }\r
-                pos.setIndex(i);\r
-                if (DEBUG)\r
-                    System.out.println("\t(" + prefix + ")Parsed <" + prop\r
-                            + ">=<" + value + ">");\r
-                return prefix + prop + ":" + value;\r
-            }\r
-\r
-            private int getIdentifier(String text, int start, int limit) {\r
-                if (DEBUG)\r
-                    System.out.println("\tGetID <"\r
-                            + text.substring(start, limit) + ">");\r
-                int cp = 0;\r
-                int i;\r
-                for (i = start; i < limit; i += UTF16.getCharCount(cp)) {\r
-                    cp = UTF16.charAt(text, i);\r
-                    if (!com.ibm.icu.lang.UCharacter\r
-                            .isUnicodeIdentifierPart(cp)\r
-                            && cp != '.') {\r
-                        break;\r
-                    }\r
-                }\r
-                if (DEBUG)\r
-                    System.out.println("\tGotID <" + text.substring(start, i)\r
-                            + ">");\r
-                return i;\r
-            }\r
-        }\r
-    }\r
-\r
-    public static class FilteredProperty extends UnicodeProperty {\r
-        private UnicodeProperty property;\r
-\r
-        protected StringFilter filter;\r
-\r
-        protected UnicodeSetIterator matchIterator = new UnicodeSetIterator(\r
-                new UnicodeSet(0, 0x10FFFF));\r
-\r
-        protected HashMap backmap;\r
-\r
-        boolean allowValueAliasCollisions = false;\r
-\r
-        public FilteredProperty(UnicodeProperty property, StringFilter filter) {\r
-            this.property = property;\r
-            this.filter = filter;\r
-        }\r
-\r
-        public StringFilter getFilter() {\r
-            return filter;\r
-        }\r
-\r
-        public UnicodeProperty setFilter(StringFilter filter) {\r
-            this.filter = filter;\r
-            return this;\r
-        }\r
-\r
-        List temp = new ArrayList(1);\r
-\r
-        public List _getAvailableValues(List result) {\r
-            temp.clear();\r
-            return filter.addUnique(property.getAvailableValues(temp), result);\r
-        }\r
-\r
-        public List _getNameAliases(List result) {\r
-            temp.clear();\r
-            return filter.addUnique(property.getNameAliases(temp), result);\r
-        }\r
-\r
-        public String _getValue(int codepoint) {\r
-            return filter.remap(property.getValue(codepoint));\r
-        }\r
-\r
-        public List _getValueAliases(String valueAlias, List result) {\r
-            if (backmap == null) {\r
-                backmap = new HashMap(1);\r
-                temp.clear();\r
-                Iterator it = property.getAvailableValues(temp).iterator();\r
-                while (it.hasNext()) {\r
-                    String item = (String) it.next();\r
-                    String mappedItem = filter.remap(item);\r
-                    if (backmap.get(mappedItem) != null\r
-                            && !allowValueAliasCollisions) {\r
-                        throw new IllegalArgumentException(\r
-                                "Filter makes values collide! " + item + ", "\r
-                                        + mappedItem);\r
-                    }\r
-                    backmap.put(mappedItem, item);\r
-                }\r
-            }\r
-            valueAlias = (String) backmap.get(valueAlias);\r
-            temp.clear();\r
-            return filter.addUnique(property.getValueAliases(valueAlias, temp),\r
-                    result);\r
-        }\r
-\r
-        public String _getVersion() {\r
-            return property.getVersion();\r
-        }\r
-\r
-        public boolean isAllowValueAliasCollisions() {\r
-            return allowValueAliasCollisions;\r
-        }\r
-\r
-        public FilteredProperty setAllowValueAliasCollisions(boolean b) {\r
-            allowValueAliasCollisions = b;\r
-            return this;\r
-        }\r
-\r
-    }\r
-\r
-    public static abstract class StringFilter implements Cloneable {\r
-        public abstract String remap(String original);\r
-\r
-        public final List addUnique(Collection source, List result) {\r
-            if (result == null)\r
-                result = new ArrayList(1);\r
-            Iterator it = source.iterator();\r
-            while (it.hasNext()) {\r
-                UnicodeProperty.addUnique(remap((String) it.next()), result);\r
-            }\r
-            return result;\r
-        }\r
-        /*\r
-         * public Object clone() { try { return super.clone(); } catch\r
-         * (CloneNotSupportedException e) { throw new\r
-         * IllegalStateException("Should never happen."); } }\r
-         */\r
-    }\r
-\r
-    public static class MapFilter extends StringFilter {\r
-        private Map valueMap;\r
-\r
-        public MapFilter(Map valueMap) {\r
-            this.valueMap = valueMap;\r
-        }\r
-\r
-        public String remap(String original) {\r
-            Object changed = valueMap.get(original);\r
-            return changed == null ? original : (String) changed;\r
-        }\r
-\r
-        public Map getMap() {\r
-            return valueMap;\r
-        }\r
-    }\r
-\r
-    public interface PatternMatcher extends ObjectMatcher {\r
-        public PatternMatcher set(String pattern);\r
-    }\r
-\r
-    public static class InversePatternMatcher extends InverseMatcher implements\r
-            PatternMatcher {\r
-        PatternMatcher other;\r
-\r
-        public PatternMatcher set(PatternMatcher toInverse) {\r
-            other = toInverse;\r
-            return this;\r
-        }\r
-\r
-        public boolean matches(Object value) {\r
-            return !other.matches(value);\r
-        }\r
-\r
-        public PatternMatcher set(String pattern) {\r
-            other.set(pattern);\r
-            return this;\r
-        }\r
-    }\r
-\r
-    public static class SimpleMatcher implements PatternMatcher {\r
-        Comparator comparator;\r
-\r
-        String pattern;\r
-\r
-        public SimpleMatcher(String pattern, Comparator comparator) {\r
-            this.comparator = comparator;\r
-            this.pattern = pattern;\r
-        }\r
-\r
-        public boolean matches(Object value) {\r
-            if (comparator == null)\r
-                return pattern.equals(value);\r
-            return comparator.compare(pattern, value) == 0;\r
-        }\r
-\r
-        public PatternMatcher set(String pattern) {\r
-            this.pattern = pattern;\r
-            return this;\r
-        }\r
-    }\r
-\r
-    public static class RegexMatcher implements UnicodeProperty.PatternMatcher {\r
-        private java.util.regex.Matcher matcher;\r
-\r
-        public UnicodeProperty.PatternMatcher set(String pattern) {\r
-            matcher = Pattern.compile(pattern).matcher("");\r
-            return this;\r
-        }\r
-\r
-        public boolean matches(Object value) {\r
-            matcher.reset(value.toString());\r
-            return matcher.matches();\r
-        }\r
-    }\r
-\r
-    public static abstract class BaseProperty extends UnicodeProperty {\r
-        protected List propertyAliases = new ArrayList(1);\r
-\r
-        protected Map toValueAliases;\r
-\r
-        protected String version;\r
-\r
-        public BaseProperty setMain(String alias, String shortAlias,\r
-                int propertyType, String version) {\r
-            setName(alias);\r
-            setType(propertyType);\r
-            propertyAliases.add(shortAlias);\r
-            propertyAliases.add(alias);\r
-            this.version = version;\r
-            return this;\r
-        }\r
-\r
-        public String _getVersion() {\r
-            return version;\r
-        }\r
-\r
-        public List _getNameAliases(List result) {\r
-            addAllUnique(propertyAliases, result);\r
-            return result;\r
-        }\r
-\r
-        public BaseProperty addValueAliases(String[][] valueAndAlternates,\r
-                boolean errorIfCant) {\r
-            if (toValueAliases == null)\r
-                _fixValueAliases();\r
-            for (int i = 0; i < valueAndAlternates.length; ++i) {\r
-                for (int j = 1; j < valueAndAlternates[0].length; ++j) {\r
-                    addValueAlias(valueAndAlternates[i][0],\r
-                            valueAndAlternates[i][j], errorIfCant);\r
-                }\r
-            }\r
-            return this;\r
-        }\r
-\r
-        public void addValueAlias(String value, String valueAlias,\r
-                boolean errorIfCant) {\r
-            List result = (List) toValueAliases.get(value);\r
-            if (result == null && !errorIfCant)\r
-                return;\r
-            addUnique(value, result);\r
-            addUnique(valueAlias, result);\r
-        }\r
-\r
-        protected List _getValueAliases(String valueAlias, List result) {\r
-            if (toValueAliases == null)\r
-                _fixValueAliases();\r
-            List a = (List) toValueAliases.get(valueAlias);\r
-            if (a != null)\r
-                addAllUnique(a, result);\r
-            return result;\r
-        }\r
-\r
-        protected void _fixValueAliases() {\r
-            if (toValueAliases == null)\r
-                toValueAliases = new HashMap(1);\r
-            for (Iterator it = getAvailableValues().iterator(); it.hasNext();) {\r
-                Object value = it.next();\r
-                _ensureValueInAliases(value);\r
-            }\r
-        }\r
-\r
-        protected void _ensureValueInAliases(Object value) {\r
-            List result = (List) toValueAliases.get(value);\r
-            if (result == null)\r
-                toValueAliases.put(value, result = new ArrayList(1));\r
-            addUnique(value, result);\r
-        }\r
-\r
-        public BaseProperty swapFirst2ValueAliases() {\r
-            for (Iterator it = toValueAliases.keySet().iterator(); it.hasNext();) {\r
-                List list = (List) toValueAliases.get(it.next());\r
-                if (list.size() < 2)\r
-                    continue;\r
-                Object first = list.get(0);\r
-                list.set(0, list.get(1));\r
-                list.set(1, first);\r
-            }\r
-            return this;\r
-        }\r
-\r
-        /**\r
-         * @param string\r
-         * @return\r
-         */\r
-        public UnicodeProperty addName(String string) {\r
-            throw new UnsupportedOperationException();\r
-        }\r
-\r
-    }\r
-\r
-    public static abstract class SimpleProperty extends BaseProperty {\r
-        List values;\r
-\r
-        public UnicodeProperty addName(String alias) {\r
-            propertyAliases.add(alias);\r
-            return this;\r
-        }\r
-\r
-        public SimpleProperty setValues(String valueAlias) {\r
-            _addToValues(valueAlias, null);\r
-            return this;\r
-        }\r
-\r
-        public SimpleProperty setValues(String[] valueAliases,\r
-                String[] alternateValueAliases) {\r
-            for (int i = 0; i < valueAliases.length; ++i) {\r
-                if (valueAliases[i].equals(UNUSED))\r
-                    continue;\r
-                _addToValues(\r
-                        valueAliases[i],\r
-                        alternateValueAliases != null ? alternateValueAliases[i]\r
-                                : null);\r
-            }\r
-            return this;\r
-        }\r
-\r
-        public SimpleProperty setValues(List valueAliases) {\r
-            this.values = new ArrayList(valueAliases);\r
-            for (Iterator it = this.values.iterator(); it.hasNext();) {\r
-                _addToValues((String) it.next(), null);\r
-            }\r
-            return this;\r
-        }\r
-\r
-        public List _getAvailableValues(List result) {\r
-            if (values == null)\r
-                _fillValues();\r
-            result.addAll(values);\r
-            return result;\r
-        }\r
-\r
-        protected void _fillValues() {\r
-            List newvalues = (List) getUnicodeMap_internal()\r
-                    .getAvailableValues(new ArrayList());\r
-            for (Iterator it = newvalues.iterator(); it.hasNext();) {\r
-                _addToValues((String) it.next(), null);\r
-            }\r
-        }\r
-\r
-        private void _addToValues(String item, String alias) {\r
-            if (values == null)\r
-                values = new ArrayList(1);\r
-            if (toValueAliases == null)\r
-                _fixValueAliases();\r
-            addUnique(item, values);\r
-            _ensureValueInAliases(item);\r
-            addValueAlias(item, alias, true);\r
-        }\r
-        /*        public String _getVersion() {\r
-         return version;\r
-         }\r
-         */\r
-    }\r
-\r
-    public static class UnicodeMapProperty extends BaseProperty {\r
-        /*\r
-         * Example of usage:\r
-         * new UnicodeProperty.UnicodeMapProperty() {\r
-         {\r
-         unicodeMap = new UnicodeMap();\r
-         unicodeMap.setErrorOnReset(true);\r
-         unicodeMap.put(0xD, "CR");\r
-         unicodeMap.put(0xA, "LF");\r
-         UnicodeProperty cat = getProperty("General_Category");\r
-         UnicodeSet temp = cat.getSet("Line_Separator")\r
-         .addAll(cat.getSet("Paragraph_Separator"))\r
-         .addAll(cat.getSet("Control"))\r
-         .addAll(cat.getSet("Format"))\r
-         .remove(0xD).remove(0xA).remove(0x200C).remove(0x200D);\r
-         unicodeMap.putAll(temp, "Control");\r
-         UnicodeSet graphemeExtend = getProperty("Grapheme_Extend").getSet("true");\r
-         unicodeMap.putAll(graphemeExtend,"Extend");\r
-         UnicodeProperty hangul = getProperty("Hangul_Syllable_Type");\r
-         unicodeMap.putAll(hangul.getSet("L"),"L");\r
-         unicodeMap.putAll(hangul.getSet("V"),"V");\r
-         unicodeMap.putAll(hangul.getSet("T"),"T");\r
-         unicodeMap.putAll(hangul.getSet("LV"),"LV");\r
-         unicodeMap.putAll(hangul.getSet("LVT"),"LVT");\r
-         unicodeMap.setMissing("Other");\r
-         }\r
-         }.setMain("Grapheme_Cluster_Break", "GCB", UnicodeProperty.ENUMERATED, version)\r
-         */\r
-        protected UnicodeMap unicodeMap;\r
-\r
-        public UnicodeMapProperty set(UnicodeMap map) {\r
-            unicodeMap = map;\r
-            return this;\r
-        }\r
-\r
-        protected String _getValue(int codepoint) {\r
-            return (String) unicodeMap.getValue(codepoint);\r
-        }\r
-\r
-        /* protected List _getValueAliases(String valueAlias, List result) {\r
-         if (!unicodeMap.getAvailableValues().contains(valueAlias)) return result;\r
-         result.add(valueAlias);\r
-         return result; // no other aliases\r
-         }\r
-         */protected List _getAvailableValues(List result) {\r
-            return (List) unicodeMap.getAvailableValues(result);\r
-        }\r
-    }\r
-}\r
-//#endif\r
-\r
+//##header J2SE15
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2009, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ */
+package com.ibm.icu.dev.test.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+import com.ibm.icu.dev.test.util.CollectionUtilities.InverseMatcher;
+import com.ibm.icu.dev.test.util.CollectionUtilities.ObjectMatcher;
+import com.ibm.icu.impl.Utility;
+import com.ibm.icu.text.SymbolTable;
+import com.ibm.icu.text.UTF16;
+import com.ibm.icu.text.UnicodeMatcher;
+import com.ibm.icu.text.UnicodeSet;
+import com.ibm.icu.text.UnicodeSetIterator;
+
+public abstract class UnicodeProperty extends UnicodeLabel {
+
+    public static boolean DEBUG = false;
+
+    public static String CHECK_NAME = "FC_NFKC_Closure";
+
+    public static int CHECK_VALUE = 0x037A;
+
+    private String name;
+
+    private String firstNameAlias = null;
+
+    private int type;
+
+    private Map valueToFirstValueAlias = null;
+
+    /*
+     * Name: Unicode_1_Name Name: ISO_Comment Name: Name Name: Unicode_1_Name
+     * 
+     */
+
+    public static final int UNKNOWN = 0, BINARY = 2, EXTENDED_BINARY = 3,
+            ENUMERATED = 4, EXTENDED_ENUMERATED = 5, CATALOG = 6,
+            EXTENDED_CATALOG = 7, MISC = 8, EXTENDED_MISC = 9, STRING = 10,
+            EXTENDED_STRING = 11, NUMERIC = 12, EXTENDED_NUMERIC = 13,
+            START_TYPE = 2, LIMIT_TYPE = 14, EXTENDED_MASK = 1,
+            CORE_MASK = ~EXTENDED_MASK, BINARY_MASK = (1 << BINARY)
+                    | (1 << EXTENDED_BINARY), STRING_MASK = (1 << STRING)
+                    | (1 << EXTENDED_STRING),
+            STRING_OR_MISC_MASK = (1 << STRING) | (1 << EXTENDED_STRING)
+                    | (1 << MISC) | (1 << EXTENDED_MISC),
+            ENUMERATED_OR_CATALOG_MASK = (1 << ENUMERATED)
+                    | (1 << EXTENDED_ENUMERATED) | (1 << CATALOG)
+                    | (1 << EXTENDED_CATALOG);
+
+    private static final String[] TYPE_NAMES = { "Unknown", "Unknown",
+            "Binary", "Extended Binary", "Enumerated", "Extended Enumerated",
+            "Catalog", "Extended Catalog", "Miscellaneous",
+            "Extended Miscellaneous", "String", "Extended String", "Numeric",
+            "Extended Numeric", };
+
+    public static String getTypeName(int propType) {
+        return TYPE_NAMES[propType];
+    }
+
+    public final String getName() {
+        return name;
+    }
+
+    public final int getType() {
+        return type;
+    }
+
+    public final boolean isType(int mask) {
+        return ((1 << type) & mask) != 0;
+    }
+
+    protected final void setName(String string) {
+        if (string == null)
+            throw new IllegalArgumentException("Name must not be null");
+        name = string;
+    }
+
+    protected final void setType(int i) {
+        type = i;
+    }
+
+    public String getVersion() {
+        return _getVersion();
+    }
+
+    public String getValue(int codepoint) {
+        if (DEBUG && CHECK_VALUE == codepoint && CHECK_NAME.equals(getName())) {
+            String value = _getValue(codepoint);
+            System.out.println(getName() + "(" + Utility.hex(codepoint) + "):"
+                    + (getType() == STRING ? Utility.hex(value) : value));
+            return value;
+        }
+        return _getValue(codepoint);
+    }
+
+    // public String getValue(int codepoint, boolean isShort) {
+    // return getValue(codepoint);
+    // }
+
+    public List getNameAliases(List result) {
+        if (result == null)
+            result = new ArrayList(1);
+        return _getNameAliases(result);
+    }
+
+    public List getValueAliases(String valueAlias, List result) {
+        if (result == null)
+            result = new ArrayList(1);
+        result = _getValueAliases(valueAlias, result);
+        if (!result.contains(valueAlias)) { // FIX && type < NUMERIC
+            result = _getValueAliases(valueAlias, result); // for debugging
+            throw new IllegalArgumentException("Internal error: " + getName()
+                    + " doesn't contain " + valueAlias + ": "
+                    + new BagFormatter().join(result));
+        }
+        return result;
+    }
+
+    public List getAvailableValues(List result) {
+        if (result == null)
+            result = new ArrayList(1);
+        return _getAvailableValues(result);
+    }
+
+    protected abstract String _getVersion();
+
+    protected abstract String _getValue(int codepoint);
+
+    protected abstract List _getNameAliases(List result);
+
+    protected abstract List _getValueAliases(String valueAlias, List result);
+
+    protected abstract List _getAvailableValues(List result);
+
+    // conveniences
+    public final List getNameAliases() {
+        return getNameAliases(null);
+    }
+
+    public final List getValueAliases(String valueAlias) {
+        return getValueAliases(valueAlias, null);
+    }
+
+    public final List getAvailableValues() {
+        return getAvailableValues(null);
+    }
+
+    public final String getValue(int codepoint, boolean getShortest) {
+        String result = getValue(codepoint);
+        if (type >= MISC || result == null || !getShortest)
+            return result;
+        return getFirstValueAlias(result);
+    }
+
+    public final String getFirstNameAlias() {
+        if (firstNameAlias == null) {
+            firstNameAlias = (String) getNameAliases().get(0);
+        }
+        return firstNameAlias;
+    }
+
+    public final String getFirstValueAlias(String value) {
+        if (valueToFirstValueAlias == null)
+            _getFirstValueAliasCache();
+        return (String) valueToFirstValueAlias.get(value);
+    }
+
+    private void _getFirstValueAliasCache() {
+        maxValueWidth = 0;
+        maxFirstValueAliasWidth = 0;
+        valueToFirstValueAlias = new HashMap(1);
+        Iterator it = getAvailableValues().iterator();
+        while (it.hasNext()) {
+            String value = (String) it.next();
+            String first = (String) getValueAliases(value).get(0);
+            if (first == null) { // internal error
+                throw new IllegalArgumentException(
+                        "Value not in value aliases: " + value);
+            }
+            if (DEBUG && CHECK_NAME.equals(getName())) {
+                System.out.println("First Alias: " + getName() + ": " + value
+                        + " => " + first
+                        + new BagFormatter().join(getValueAliases(value)));
+            }
+            valueToFirstValueAlias.put(value, first);
+            if (value.length() > maxValueWidth) {
+                maxValueWidth = value.length();
+            }
+            if (first.length() > maxFirstValueAliasWidth) {
+                maxFirstValueAliasWidth = first.length();
+            }
+        }
+    }
+
+    private int maxValueWidth = -1;
+
+    private int maxFirstValueAliasWidth = -1;
+
+    public int getMaxWidth(boolean getShortest) {
+        if (maxValueWidth < 0)
+            _getFirstValueAliasCache();
+        if (getShortest)
+            return maxFirstValueAliasWidth;
+        return maxValueWidth;
+    }
+
+    public final UnicodeSet getSet(String propertyValue) {
+        return getSet(propertyValue, null);
+    }
+
+    public final UnicodeSet getSet(PatternMatcher matcher) {
+        return getSet(matcher, null);
+    }
+
+    public final UnicodeSet getSet(String propertyValue, UnicodeSet result) {
+        return getSet(new SimpleMatcher(propertyValue,
+                isType(STRING_OR_MISC_MASK) ? null : PROPERTY_COMPARATOR),
+                result);
+    }
+
+    private UnicodeMap unicodeMap = null;
+
+    public static final String UNUSED = "??";
+
+    public final UnicodeSet getSet(PatternMatcher matcher, UnicodeSet result) {
+        if (result == null)
+            result = new UnicodeSet();
+        if (isType(STRING_OR_MISC_MASK)) {
+            for (int i = 0; i <= 0x10FFFF; ++i) {
+                String value = getValue(i);
+                if (value != null && matcher.matches(value)) {
+                    result.add(i);
+                }
+            }
+            return result;
+        }
+        List temp = new ArrayList(1); // to avoid reallocating...
+        UnicodeMap um = getUnicodeMap_internal();
+        Iterator it = um.getAvailableValues(null).iterator();
+        main: while (it.hasNext()) {
+            String value = (String) it.next();
+            temp.clear();
+            Iterator it2 = getValueAliases(value, temp).iterator();
+            while (it2.hasNext()) {
+                String value2 = (String) it2.next();
+                // System.out.println("Values:" + value2);
+                if (matcher.matches(value2)
+                        || matcher.matches(toSkeleton(value2))) {
+                    um.getSet(value, result);
+                    continue main;
+                }
+            }
+        }
+        return result;
+    }
+
+    /*
+     * public UnicodeSet getMatchSet(UnicodeSet result) { if (result == null)
+     * result = new UnicodeSet(); addAll(matchIterator, result); return result; }
+     * 
+     * public void setMatchSet(UnicodeSet set) { matchIterator = new
+     * UnicodeSetIterator(set); }
+     */
+
+    /**
+     * Utility for debugging
+     */
+    public static String getStack() {
+        Exception e = new Exception();
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        e.printStackTrace(pw);
+        pw.flush();
+        return "Showing Stack with fake " + sw.getBuffer().toString();
+    }
+
+    // TODO use this instead of plain strings
+    public static class Name implements Comparable {
+        private String skeleton;
+
+        private String pretty;
+
+        public final int RAW = 0, TITLE = 1, NORMAL = 2;
+
+        public Name(String name, int style) {
+            if (name == null)
+                name = "";
+            if (style == RAW) {
+                skeleton = pretty = name;
+            } else {
+                pretty = regularize(name, style == TITLE);
+                skeleton = toSkeleton(pretty);
+            }
+        }
+
+        public int compareTo(Object o) {
+            return skeleton.compareTo(((Name) o).skeleton);
+        }
+
+        public boolean equals(Object o) {
+            return skeleton.equals(((Name) o).skeleton);
+        }
+
+        public int hashCode() {
+            return skeleton.hashCode();
+        }
+
+        public String toString() {
+            return pretty;
+        }
+    }
+
+    /**
+     * @return the unicode map
+     */
+    public UnicodeMap getUnicodeMap() {
+        return getUnicodeMap(false);
+    }
+
+    /**
+     * @return the unicode map
+     */
+    public UnicodeMap getUnicodeMap(boolean getShortest) {
+        if (!getShortest)
+            return (UnicodeMap) getUnicodeMap_internal().cloneAsThawed();
+        UnicodeMap result = new UnicodeMap();
+        for (int i = 0; i <= 0x10FFFF; ++i) {
+            // if (DEBUG && i == 0x41) System.out.println(i + "\t" +
+            // getValue(i));
+            String value = getValue(i, true);
+            result.put(i, value);
+        }
+        return result;
+    }
+
+    /**
+     * @return the unicode map
+     */
+    protected UnicodeMap getUnicodeMap_internal() {
+        if (unicodeMap == null)
+            unicodeMap = _getUnicodeMap();
+        return unicodeMap;
+    }
+
+    protected UnicodeMap _getUnicodeMap() {
+        UnicodeMap result = new UnicodeMap();
+        HashMap myIntern = new HashMap();
+        for (int i = 0; i <= 0x10FFFF; ++i) {
+            // if (DEBUG && i == 0x41) System.out.println(i + "\t" +
+            // getValue(i));
+            String value = getValue(i);
+            String iValue = (String) myIntern.get(value);
+            if (iValue == null)
+                myIntern.put(value, iValue = value);
+            result.put(i, iValue);
+        }
+        if (DEBUG) {
+            for (int i = 0; i <= 0x10FFFF; ++i) {
+                // if (DEBUG && i == 0x41) System.out.println(i + "\t" +
+                // getValue(i));
+                String value = getValue(i);
+                String resultValue = (String) result.getValue(i);
+                if (!value.equals(resultValue)) {
+                    throw new RuntimeException("Value failure at: "
+                            + Utility.hex(i));
+                }
+            }
+        }
+        if (DEBUG && CHECK_NAME.equals(getName())) {
+            System.out.println(getName() + ":\t" + getClass().getName() + "\t"
+                    + getVersion());
+            System.out.println(getStack());
+            System.out.println(result);
+        }
+        return result;
+    }
+
+    /**
+     * Really ought to create a Collection UniqueList, that forces uniqueness.
+     * But for now...
+     */
+    public static Collection addUnique(Object obj, Collection result) {
+        if (obj != null && !result.contains(obj))
+            result.add(obj);
+        return result;
+    }
+
+    /**
+     * Utility for managing property & non-string value aliases
+     */
+    public static final Comparator PROPERTY_COMPARATOR = new Comparator() {
+        public int compare(Object o1, Object o2) {
+            return compareNames((String) o1, (String) o2);
+        }
+    };
+
+    /**
+     * Utility for managing property & non-string value aliases
+     * 
+     */
+    // TODO optimize
+    public static boolean equalNames(String a, String b) {
+        if (a == b)
+            return true;
+        if (a == null)
+            return false;
+        return toSkeleton(a).equals(toSkeleton(b));
+    }
+
+    /**
+     * Utility for managing property & non-string value aliases
+     */
+    // TODO optimize
+    public static int compareNames(String a, String b) {
+        if (a == b)
+            return 0;
+        if (a == null)
+            return -1;
+        if (b == null)
+            return 1;
+        return toSkeleton(a).compareTo(toSkeleton(b));
+    }
+
+    /**
+     * Utility for managing property & non-string value aliases
+     */
+    // TODO account for special names, tibetan, hangul
+    public static String toSkeleton(String source) {
+        if (source == null)
+            return null;
+        StringBuffer skeletonBuffer = new StringBuffer();
+        boolean gotOne = false;
+        // remove spaces, '_', '-'
+        // we can do this with char, since no surrogates are involved
+        for (int i = 0; i < source.length(); ++i) {
+            char ch = source.charAt(i);
+            if (i > 0 && (ch == '_' || ch == ' ' || ch == '-')) {
+                gotOne = true;
+            } else {
+                char ch2 = Character.toLowerCase(ch);
+                if (ch2 != ch) {
+                    gotOne = true;
+                    skeletonBuffer.append(ch2);
+                } else {
+                    skeletonBuffer.append(ch);
+                }
+            }
+        }
+        if (!gotOne)
+            return source; // avoid string creation
+        return skeletonBuffer.toString();
+    }
+
+    // get the name skeleton
+    public static String toNameSkeleton(String source) {
+        if (source == null)
+            return null;
+        StringBuffer result = new StringBuffer();
+        // remove spaces, medial '-'
+        // we can do this with char, since no surrogates are involved
+        for (int i = 0; i < source.length(); ++i) {
+            char ch = source.charAt(i);
+            if (('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z')
+                    || ch == '<' || ch == '>') {
+                result.append(ch);
+            } else if (ch == ' ') {
+                // don't copy ever
+            } else if (ch == '-') {
+                // only copy non-medials AND trailing O-E
+                if (0 == i
+                        || i == source.length() - 1
+                        || source.charAt(i - 1) == ' '
+                        || source.charAt(i + 1) == ' '
+                        || (i == source.length() - 2
+                                && source.charAt(i - 1) == 'O' && source
+                                .charAt(i + 1) == 'E')) {
+                    System.out.println("****** EXCEPTION " + source);
+                    result.append(ch);
+                }
+                // otherwise don't copy
+            } else {
+                throw new IllegalArgumentException("Illegal Name Char: U+"
+                        + Utility.hex(ch) + ", " + ch);
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * These routines use the Java functions, because they only need to act on
+     * ASCII Changes space, - into _, inserts _ between lower and UPPER.
+     */
+    public static String regularize(String source, boolean titlecaseStart) {
+        if (source == null)
+            return source;
+        /*
+         * if (source.equals("noBreak")) { // HACK if (titlecaseStart) return
+         * "NoBreak"; return source; }
+         */
+        StringBuffer result = new StringBuffer();
+        int lastCat = -1;
+        boolean haveFirstCased = true;
+        for (int i = 0; i < source.length(); ++i) {
+            char c = source.charAt(i);
+            if (c == ' ' || c == '-' || c == '_') {
+                c = '_';
+                haveFirstCased = true;
+            }
+            if (c == '=')
+                haveFirstCased = true;
+            int cat = Character.getType(c);
+            if (lastCat == Character.LOWERCASE_LETTER
+                    && cat == Character.UPPERCASE_LETTER) {
+                result.append('_');
+            }
+            if (haveFirstCased
+                    && (cat == Character.LOWERCASE_LETTER
+                            || cat == Character.TITLECASE_LETTER || cat == Character.UPPERCASE_LETTER)) {
+                if (titlecaseStart) {
+                    c = Character.toUpperCase(c);
+                }
+                haveFirstCased = false;
+            }
+            result.append(c);
+            lastCat = cat;
+        }
+        return result.toString();
+    }
+
+    /**
+     * Utility function for comparing codepoint to string without generating new
+     * string.
+     * 
+     * @param codepoint
+     * @param other
+     * @return true if the codepoint equals the string
+     */
+    public static final boolean equals(int codepoint, String other) {
+        if (other.length() == 1) {
+            return codepoint == other.charAt(0);
+        }
+        if (other.length() == 2) {
+            return other.equals(UTF16.valueOf(codepoint));
+        }
+        return false;
+    }
+
+    /**
+     * Utility that should be on UnicodeSet
+     * 
+     * @param source
+     * @param result
+     */
+    static public void addAll(UnicodeSetIterator source, UnicodeSet result) {
+        while (source.nextRange()) {
+            if (source.codepoint == UnicodeSetIterator.IS_STRING) {
+                result.add(source.string);
+            } else {
+                result.add(source.codepoint, source.codepointEnd);
+            }
+        }
+    }
+
+    /**
+     * Really ought to create a Collection UniqueList, that forces uniqueness.
+     * But for now...
+     */
+    public static Collection addAllUnique(Collection source, Collection result) {
+        for (Iterator it = source.iterator(); it.hasNext();) {
+            addUnique(it.next(), result);
+        }
+        return result;
+    }
+
+    /**
+     * Really ought to create a Collection UniqueList, that forces uniqueness.
+     * But for now...
+     */
+    public static Collection addAllUnique(Object[] source, Collection result) {
+        for (int i = 0; i < source.length; ++i) {
+            addUnique(source[i], result);
+        }
+        return result;
+    }
+
+    static public class Factory {
+        static boolean DEBUG = false;
+
+        Map canonicalNames = new TreeMap();
+
+        Map skeletonNames = new TreeMap();
+
+        Map propertyCache = new HashMap(1);
+
+        public final Factory add(UnicodeProperty sp) {
+            canonicalNames.put(sp.getName(), sp);
+            List c = sp.getNameAliases(new ArrayList(1));
+            Iterator it = c.iterator();
+            while (it.hasNext()) {
+                skeletonNames.put(toSkeleton((String) it.next()), sp);
+            }
+            return this;
+        }
+
+        public final UnicodeProperty getProperty(String propertyAlias) {
+            return (UnicodeProperty) skeletonNames
+                    .get(toSkeleton(propertyAlias));
+        }
+
+        public final List getAvailableNames() {
+            return getAvailableNames(null);
+        }
+
+        public final List getAvailableNames(List result) {
+            if (result == null)
+                result = new ArrayList(1);
+            Iterator it = canonicalNames.keySet().iterator();
+            while (it.hasNext()) {
+                addUnique(it.next(), result);
+            }
+            return result;
+        }
+
+        public final List getAvailableNames(int propertyTypeMask) {
+            return getAvailableNames(propertyTypeMask, null);
+        }
+
+        public final List getAvailableNames(int propertyTypeMask, List result) {
+            if (result == null)
+                result = new ArrayList(1);
+            Iterator it = canonicalNames.keySet().iterator();
+            while (it.hasNext()) {
+                String item = (String) it.next();
+                UnicodeProperty property = getProperty(item);
+                if (DEBUG)
+                    System.out.println("Properties: " + item + ","
+                            + property.getType());
+                if (!property.isType(propertyTypeMask)) {
+                    // System.out.println("Masking: " + property.getType() + ","
+                    // + propertyTypeMask);
+                    continue;
+                }
+                addUnique(property.getName(), result);
+            }
+            return result;
+        }
+
+        InversePatternMatcher inverseMatcher = new InversePatternMatcher();
+
+        /**
+         * Format is: propname ('=' | '!=') propvalue ( '|' propValue )*
+         */
+        public final UnicodeSet getSet(String propAndValue,
+                PatternMatcher matcher, UnicodeSet result) {
+            int equalPos = propAndValue.indexOf('=');
+            String prop = propAndValue.substring(0, equalPos);
+            String value = propAndValue.substring(equalPos + 1);
+            boolean negative = false;
+            if (prop.endsWith("!")) {
+                prop = prop.substring(0, prop.length() - 1);
+                negative = true;
+            }
+            prop = prop.trim();
+            UnicodeProperty up = getProperty(prop);
+            if (matcher == null) {
+                matcher = new SimpleMatcher(value, up
+                        .isType(STRING_OR_MISC_MASK) ? null
+                        : PROPERTY_COMPARATOR);
+            }
+            if (negative) {
+                inverseMatcher.set(matcher);
+                matcher = inverseMatcher;
+            }
+            return up.getSet(matcher.set(value), result);
+        }
+
+        public final UnicodeSet getSet(String propAndValue,
+                PatternMatcher matcher) {
+            return getSet(propAndValue, matcher, null);
+        }
+
+        public final UnicodeSet getSet(String propAndValue) {
+            return getSet(propAndValue, null, null);
+        }
+
+        public final SymbolTable getSymbolTable(String prefix) {
+            return new PropertySymbolTable(prefix);
+        }
+
+        private class MyXSymbolTable extends UnicodeSet.XSymbolTable {
+            public boolean applyPropertyAlias(String propertyName,
+                    String propertyValue, UnicodeSet result) {
+                if (false)
+                    System.out.println(propertyName + "=" + propertyValue);
+                UnicodeProperty prop = getProperty(propertyName);
+                if (prop == null)
+                    return false;
+                result.clear();
+                UnicodeSet x = prop.getSet(propertyValue, result);
+                return x.size() != 0;
+            }
+        }
+
+        public final UnicodeSet.XSymbolTable getXSymbolTable() {
+            return new MyXSymbolTable();
+        }
+
+        private class PropertySymbolTable implements SymbolTable {
+            static final boolean DEBUG = false;
+
+            private String prefix;
+
+            RegexMatcher regexMatcher = new RegexMatcher();
+
+            PropertySymbolTable(String prefix) {
+                this.prefix = prefix;
+            }
+
+            public char[] lookup(String s) {
+                if (DEBUG)
+                    System.out.println("\t(" + prefix + ")Looking up " + s);
+                // ensure, again, that prefix matches
+                int start = prefix.length();
+                if (!s.regionMatches(true, 0, prefix, 0, start))
+                    return null;
+
+                int pos = s.indexOf(':', start);
+                if (pos < 0) { // should never happen
+                    throw new IllegalArgumentException(
+                            "Internal Error: missing =: " + s + "\r\n");
+                }
+                UnicodeProperty prop = getProperty(s.substring(start, pos));
+                if (prop == null) {
+                    throw new IllegalArgumentException("Invalid Property in: "
+                            + s + "\r\nUse " + showSet(getAvailableNames()));
+                }
+                String value = s.substring(pos + 1);
+                UnicodeSet set;
+                if (value.startsWith("\u00AB")) { // regex!
+                    set = prop.getSet(regexMatcher.set(value.substring(1, value
+                            .length() - 1)));
+                } else {
+                    set = prop.getSet(value);
+                }
+                if (set.size() == 0) {
+                    throw new IllegalArgumentException(
+                            "Empty Property-Value in: " + s + "\r\nUse "
+                                    + showSet(prop.getAvailableValues()));
+                }
+                if (DEBUG)
+                    System.out.println("\t(" + prefix + ")Returning "
+                            + set.toPattern(true));
+                return set.toPattern(true).toCharArray(); // really ugly
+            }
+
+            private String showSet(List list) {
+                StringBuffer result = new StringBuffer("[");
+                boolean first = true;
+                for (Iterator it = list.iterator(); it.hasNext();) {
+                    if (!first)
+                        result.append(", ");
+                    else
+                        first = false;
+                    result.append(it.next().toString());
+                }
+                result.append("]");
+                return result.toString();
+            }
+
+            public UnicodeMatcher lookupMatcher(int ch) {
+                return null;
+            }
+
+            public String parseReference(String text, ParsePosition pos,
+                    int limit) {
+                if (DEBUG)
+                    System.out.println("\t(" + prefix + ")Parsing <"
+                            + text.substring(pos.getIndex(), limit) + ">");
+                int start = pos.getIndex();
+                // ensure that it starts with 'prefix'
+                if (!text
+                        .regionMatches(true, start, prefix, 0, prefix.length()))
+                    return null;
+                start += prefix.length();
+                // now see if it is of the form identifier:identifier
+                int i = getIdentifier(text, start, limit);
+                if (i == start)
+                    return null;
+                String prop = text.substring(start, i);
+                String value = "true";
+                if (i < limit) {
+                    if (text.charAt(i) == ':') {
+                        int j;
+                        if (text.charAt(i + 1) == '\u00AB') { // regular
+                                                                // expression
+                            j = text.indexOf('\u00BB', i + 2) + 1; // include
+                                                                    // last
+                                                                    // character
+                            if (j <= 0)
+                                return null;
+                        } else {
+                            j = getIdentifier(text, i + 1, limit);
+                        }
+                        value = text.substring(i + 1, j);
+                        i = j;
+                    }
+                }
+                pos.setIndex(i);
+                if (DEBUG)
+                    System.out.println("\t(" + prefix + ")Parsed <" + prop
+                            + ">=<" + value + ">");
+                return prefix + prop + ":" + value;
+            }
+
+            private int getIdentifier(String text, int start, int limit) {
+                if (DEBUG)
+                    System.out.println("\tGetID <"
+                            + text.substring(start, limit) + ">");
+                int cp = 0;
+                int i;
+                for (i = start; i < limit; i += UTF16.getCharCount(cp)) {
+                    cp = UTF16.charAt(text, i);
+                    if (!com.ibm.icu.lang.UCharacter
+                            .isUnicodeIdentifierPart(cp)
+                            && cp != '.') {
+                        break;
+                    }
+                }
+                if (DEBUG)
+                    System.out.println("\tGotID <" + text.substring(start, i)
+                            + ">");
+                return i;
+            }
+        }
+    }
+
+    public static class FilteredProperty extends UnicodeProperty {
+        private UnicodeProperty property;
+
+        protected StringFilter filter;
+
+        protected UnicodeSetIterator matchIterator = new UnicodeSetIterator(
+                new UnicodeSet(0, 0x10FFFF));
+
+        protected HashMap backmap;
+
+        boolean allowValueAliasCollisions = false;
+
+        public FilteredProperty(UnicodeProperty property, StringFilter filter) {
+            this.property = property;
+            this.filter = filter;
+        }
+
+        public StringFilter getFilter() {
+            return filter;
+        }
+
+        public UnicodeProperty setFilter(StringFilter filter) {
+            this.filter = filter;
+            return this;
+        }
+
+        List temp = new ArrayList(1);
+
+        public List _getAvailableValues(List result) {
+            temp.clear();
+            return filter.addUnique(property.getAvailableValues(temp), result);
+        }
+
+        public List _getNameAliases(List result) {
+            temp.clear();
+            return filter.addUnique(property.getNameAliases(temp), result);
+        }
+
+        public String _getValue(int codepoint) {
+            return filter.remap(property.getValue(codepoint));
+        }
+
+        public List _getValueAliases(String valueAlias, List result) {
+            if (backmap == null) {
+                backmap = new HashMap(1);
+                temp.clear();
+                Iterator it = property.getAvailableValues(temp).iterator();
+                while (it.hasNext()) {
+                    String item = (String) it.next();
+                    String mappedItem = filter.remap(item);
+                    if (backmap.get(mappedItem) != null
+                            && !allowValueAliasCollisions) {
+                        throw new IllegalArgumentException(
+                                "Filter makes values collide! " + item + ", "
+                                        + mappedItem);
+                    }
+                    backmap.put(mappedItem, item);
+                }
+            }
+            valueAlias = (String) backmap.get(valueAlias);
+            temp.clear();
+            return filter.addUnique(property.getValueAliases(valueAlias, temp),
+                    result);
+        }
+
+        public String _getVersion() {
+            return property.getVersion();
+        }
+
+        public boolean isAllowValueAliasCollisions() {
+            return allowValueAliasCollisions;
+        }
+
+        public FilteredProperty setAllowValueAliasCollisions(boolean b) {
+            allowValueAliasCollisions = b;
+            return this;
+        }
+
+    }
+
+    public static abstract class StringFilter implements Cloneable {
+        public abstract String remap(String original);
+
+        public final List addUnique(Collection source, List result) {
+            if (result == null)
+                result = new ArrayList(1);
+            Iterator it = source.iterator();
+            while (it.hasNext()) {
+                UnicodeProperty.addUnique(remap((String) it.next()), result);
+            }
+            return result;
+        }
+        /*
+         * public Object clone() { try { return super.clone(); } catch
+         * (CloneNotSupportedException e) { throw new
+         * IllegalStateException("Should never happen."); } }
+         */
+    }
+
+    public static class MapFilter extends StringFilter {
+        private Map valueMap;
+
+        public MapFilter(Map valueMap) {
+            this.valueMap = valueMap;
+        }
+
+        public String remap(String original) {
+            Object changed = valueMap.get(original);
+            return changed == null ? original : (String) changed;
+        }
+
+        public Map getMap() {
+            return valueMap;
+        }
+    }
+
+    public interface PatternMatcher extends ObjectMatcher {
+        public PatternMatcher set(String pattern);
+    }
+
+    public static class InversePatternMatcher extends InverseMatcher implements
+            PatternMatcher {
+        PatternMatcher other;
+
+        public PatternMatcher set(PatternMatcher toInverse) {
+            other = toInverse;
+            return this;
+        }
+
+        public boolean matches(Object value) {
+            return !other.matches(value);
+        }
+
+        public PatternMatcher set(String pattern) {
+            other.set(pattern);
+            return this;
+        }
+    }
+
+    public static class SimpleMatcher implements PatternMatcher {
+        Comparator comparator;
+
+        String pattern;
+
+        public SimpleMatcher(String pattern, Comparator comparator) {
+            this.comparator = comparator;
+            this.pattern = pattern;
+        }
+
+        public boolean matches(Object value) {
+            if (comparator == null)
+                return pattern.equals(value);
+            return comparator.compare(pattern, value) == 0;
+        }
+
+        public PatternMatcher set(String pattern) {
+            this.pattern = pattern;
+            return this;
+        }
+    }
+
+    public static class RegexMatcher implements UnicodeProperty.PatternMatcher {
+        private java.util.regex.Matcher matcher;
+
+        public UnicodeProperty.PatternMatcher set(String pattern) {
+            matcher = Pattern.compile(pattern).matcher("");
+            return this;
+        }
+
+        public boolean matches(Object value) {
+            matcher.reset(value.toString());
+            return matcher.matches();
+        }
+    }
+
+    public static abstract class BaseProperty extends UnicodeProperty {
+        protected List propertyAliases = new ArrayList(1);
+
+        protected Map toValueAliases;
+
+        protected String version;
+
+        public BaseProperty setMain(String alias, String shortAlias,
+                int propertyType, String version) {
+            setName(alias);
+            setType(propertyType);
+            propertyAliases.add(shortAlias);
+            propertyAliases.add(alias);
+            this.version = version;
+            return this;
+        }
+
+        public String _getVersion() {
+            return version;
+        }
+
+        public List _getNameAliases(List result) {
+            addAllUnique(propertyAliases, result);
+            return result;
+        }
+
+        public BaseProperty addValueAliases(String[][] valueAndAlternates,
+                boolean errorIfCant) {
+            if (toValueAliases == null)
+                _fixValueAliases();
+            for (int i = 0; i < valueAndAlternates.length; ++i) {
+                for (int j = 1; j < valueAndAlternates[0].length; ++j) {
+                    addValueAlias(valueAndAlternates[i][0],
+                            valueAndAlternates[i][j], errorIfCant);
+                }
+            }
+            return this;
+        }
+
+        public void addValueAlias(String value, String valueAlias,
+                boolean errorIfCant) {
+            List result = (List) toValueAliases.get(value);
+            if (result == null && !errorIfCant)
+                return;
+            addUnique(value, result);
+            addUnique(valueAlias, result);
+        }
+
+        protected List _getValueAliases(String valueAlias, List result) {
+            if (toValueAliases == null)
+                _fixValueAliases();
+            List a = (List) toValueAliases.get(valueAlias);
+            if (a != null)
+                addAllUnique(a, result);
+            return result;
+        }
+
+        protected void _fixValueAliases() {
+            if (toValueAliases == null)
+                toValueAliases = new HashMap(1);
+            for (Iterator it = getAvailableValues().iterator(); it.hasNext();) {
+                Object value = it.next();
+                _ensureValueInAliases(value);
+            }
+        }
+
+        protected void _ensureValueInAliases(Object value) {
+            List result = (List) toValueAliases.get(value);
+            if (result == null)
+                toValueAliases.put(value, result = new ArrayList(1));
+            addUnique(value, result);
+        }
+
+        public BaseProperty swapFirst2ValueAliases() {
+            for (Iterator it = toValueAliases.keySet().iterator(); it.hasNext();) {
+                List list = (List) toValueAliases.get(it.next());
+                if (list.size() < 2)
+                    continue;
+                Object first = list.get(0);
+                list.set(0, list.get(1));
+                list.set(1, first);
+            }
+            return this;
+        }
+
+        /**
+         * @param string
+         * @return
+         */
+        public UnicodeProperty addName(String string) {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+    public static abstract class SimpleProperty extends BaseProperty {
+        List values;
+
+        public UnicodeProperty addName(String alias) {
+            propertyAliases.add(alias);
+            return this;
+        }
+
+        public SimpleProperty setValues(String valueAlias) {
+            _addToValues(valueAlias, null);
+            return this;
+        }
+
+        public SimpleProperty setValues(String[] valueAliases,
+                String[] alternateValueAliases) {
+            for (int i = 0; i < valueAliases.length; ++i) {
+                if (valueAliases[i].equals(UNUSED))
+                    continue;
+                _addToValues(
+                        valueAliases[i],
+                        alternateValueAliases != null ? alternateValueAliases[i]
+                                : null);
+            }
+            return this;
+        }
+
+        public SimpleProperty setValues(List valueAliases) {
+            this.values = new ArrayList(valueAliases);
+            for (Iterator it = this.values.iterator(); it.hasNext();) {
+                _addToValues((String) it.next(), null);
+            }
+            return this;
+        }
+
+        public List _getAvailableValues(List result) {
+            if (values == null)
+                _fillValues();
+            result.addAll(values);
+            return result;
+        }
+
+        protected void _fillValues() {
+            List newvalues = (List) getUnicodeMap_internal()
+                    .getAvailableValues(new ArrayList());
+            for (Iterator it = newvalues.iterator(); it.hasNext();) {
+                _addToValues((String) it.next(), null);
+            }
+        }
+
+        private void _addToValues(String item, String alias) {
+            if (values == null)
+                values = new ArrayList(1);
+            if (toValueAliases == null)
+                _fixValueAliases();
+            addUnique(item, values);
+            _ensureValueInAliases(item);
+            addValueAlias(item, alias, true);
+        }
+        /*        public String _getVersion() {
+         return version;
+         }
+         */
+    }
+
+    public static class UnicodeMapProperty extends BaseProperty {
+        /*
+         * Example of usage:
+         * new UnicodeProperty.UnicodeMapProperty() {
+         {
+         unicodeMap = new UnicodeMap();
+         unicodeMap.setErrorOnReset(true);
+         unicodeMap.put(0xD, "CR");
+         unicodeMap.put(0xA, "LF");
+         UnicodeProperty cat = getProperty("General_Category");
+         UnicodeSet temp = cat.getSet("Line_Separator")
+         .addAll(cat.getSet("Paragraph_Separator"))
+         .addAll(cat.getSet("Control"))
+         .addAll(cat.getSet("Format"))
+         .remove(0xD).remove(0xA).remove(0x200C).remove(0x200D);
+         unicodeMap.putAll(temp, "Control");
+         UnicodeSet graphemeExtend = getProperty("Grapheme_Extend").getSet("true");
+         unicodeMap.putAll(graphemeExtend,"Extend");
+         UnicodeProperty hangul = getProperty("Hangul_Syllable_Type");
+         unicodeMap.putAll(hangul.getSet("L"),"L");
+         unicodeMap.putAll(hangul.getSet("V"),"V");
+         unicodeMap.putAll(hangul.getSet("T"),"T");
+         unicodeMap.putAll(hangul.getSet("LV"),"LV");
+         unicodeMap.putAll(hangul.getSet("LVT"),"LVT");
+         unicodeMap.setMissing("Other");
+         }
+         }.setMain("Grapheme_Cluster_Break", "GCB", UnicodeProperty.ENUMERATED, version)
+         */
+        protected UnicodeMap unicodeMap;
+
+        public UnicodeMapProperty set(UnicodeMap map) {
+            unicodeMap = map;
+            return this;
+        }
+
+        protected String _getValue(int codepoint) {
+            return (String) unicodeMap.getValue(codepoint);
+        }
+
+        /* protected List _getValueAliases(String valueAlias, List result) {
+         if (!unicodeMap.getAvailableValues().contains(valueAlias)) return result;
+         result.add(valueAlias);
+         return result; // no other aliases
+         }
+         */protected List _getAvailableValues(List result) {
+            return (List) unicodeMap.getAvailableValues(result);
+        }
+    }
+}
+//#endif
+