17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert **********************************************************************
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Copyright (c) 2002-2014, International Business Machines
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Corporation and others.  All Rights Reserved.
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert **********************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Author: Alan Liu
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Created: November 5 2002
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Since: ICU 2.4
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2010nov19 Markus Scherer  Rewrite for formatVersion 2.
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert **********************************************************************
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.nio.ByteBuffer;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.lang.UProperty;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.BytesTrie;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Wrapper for the pnames.icu binary data file.  This data file is
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * imported from icu4c.  It contains property and property value
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * aliases from the UCD files PropertyAliases.txt and
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * PropertyValueAliases.txt.  The file is built by the icu4c tool
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * genpname.  It must be an ASCII big-endian file to be
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * usable in icu4j.
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This class performs two functions.
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (1) It can import the flat binary data into usable objects.
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (2) It provides an API to access the tree of objects.
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Needless to say, this class is tightly coupled to the binary format
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * of icu4c's pnames.icu file.
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Each time a UPropertyAliases is constructed, the pnames.icu file is
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * read, parsed, and data structures assembled.  Clients should create one
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * singleton instance and cache it.
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author Alan Liu
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @since ICU 2.4
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic final class UPropertyAliases {
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Byte offsets from the start of the data, after the generic header.
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int IX_VALUE_MAPS_OFFSET=0;
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int IX_BYTE_TRIES_OFFSET=1;
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int IX_NAME_GROUPS_OFFSET=2;
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int IX_RESERVED3_OFFSET=3;
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // private static final int IX_RESERVED4_OFFSET=4;
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // private static final int IX_TOTAL_SIZE=5;
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Other values.
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // private static final int IX_MAX_NAME_LENGTH=6;
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // private static final int IX_RESERVED7=7;
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // private static final int IX_COUNT=8;
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //----------------------------------------------------------------
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Runtime data.  This is an unflattened representation of the
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // data in pnames.icu.
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int[] valueMaps;
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private byte[] bytesTries;
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String nameGroups;
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final class IsAcceptable implements ICUBinary.Authenticate {
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // @Override when we switch to Java 6
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isDataVersionAcceptable(byte version[]) {
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return version[0]==2;
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final IsAcceptable IS_ACCEPTABLE=new IsAcceptable();
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int DATA_FORMAT=0x706E616D;  // "pnam"
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void load(ByteBuffer bytes) throws IOException {
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //dataVersion=ICUBinary.readHeaderAndDataVersion(bytes, DATA_FORMAT, IS_ACCEPTABLE);
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ICUBinary.readHeader(bytes, DATA_FORMAT, IS_ACCEPTABLE);
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int indexesLength=bytes.getInt()/4;  // inIndexes[IX_VALUE_MAPS_OFFSET]/4
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(indexesLength<8) {  // formatVersion 2 initially has 8 indexes
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IOException("pnames.icu: not enough indexes");
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] inIndexes=new int[indexesLength];
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        inIndexes[0]=indexesLength*4;
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(int i=1; i<indexesLength; ++i) {
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            inIndexes[i]=bytes.getInt();
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Read the valueMaps.
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int offset=inIndexes[IX_VALUE_MAPS_OFFSET];
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int nextOffset=inIndexes[IX_BYTE_TRIES_OFFSET];
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int numInts=(nextOffset-offset)/4;
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        valueMaps=new int[numInts];
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(int i=0; i<numInts; ++i) {
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            valueMaps[i]=bytes.getInt();
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Read the bytesTries.
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        offset=nextOffset;
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        nextOffset=inIndexes[IX_NAME_GROUPS_OFFSET];
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int numBytes=nextOffset-offset;
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        bytesTries=new byte[numBytes];
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        bytes.get(bytesTries);
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Read the nameGroups and turn them from ASCII bytes into a Java String.
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        offset=nextOffset;
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        nextOffset=inIndexes[IX_RESERVED3_OFFSET];
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        numBytes=nextOffset-offset;
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder sb=new StringBuilder(numBytes);
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(int i=0; i<numBytes; ++i) {
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            sb.append((char)bytes.get());
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        nameGroups=sb.toString();
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private UPropertyAliases() throws IOException {
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ByteBuffer bytes = ICUBinary.getRequiredData("pnames.icu");
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        load(bytes);
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int findProperty(int property) {
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i=1;  // valueMaps index, initially after numRanges
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(int numRanges=valueMaps[0]; numRanges>0; --numRanges) {
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Read and skip the start and limit of this range.
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int start=valueMaps[i];
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int limit=valueMaps[i+1];
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            i+=2;
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if(property<start) {
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if(property<limit) {
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return i+(property-start)*2;
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            i+=(limit-start)*2;  // Skip all entries for this range.
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return 0;
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int findPropertyValueNameGroup(int valueMapIndex, int value) {
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(valueMapIndex==0) {
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return 0;  // The property does not have named values.
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ++valueMapIndex;  // Skip the BytesTrie offset.
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int numRanges=valueMaps[valueMapIndex++];
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(numRanges<0x10) {
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Ranges of values.
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for(; numRanges>0; --numRanges) {
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Read and skip the start and limit of this range.
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int start=valueMaps[valueMapIndex];
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int limit=valueMaps[valueMapIndex+1];
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                valueMapIndex+=2;
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(value<start) {
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(value<limit) {
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return valueMaps[valueMapIndex+value-start];
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                valueMapIndex+=limit-start;  // Skip all entries for this range.
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // List of values.
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int valuesStart=valueMapIndex;
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int nameGroupOffsetsStart=valueMapIndex+numRanges-0x10;
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int v=valueMaps[valueMapIndex];
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(value<v) {
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(value==v) {
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return valueMaps[nameGroupOffsetsStart+valueMapIndex-valuesStart];
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while(++valueMapIndex<nameGroupOffsetsStart);
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return 0;
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String getName(int nameGroupsIndex, int nameIndex) {
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int numNames=nameGroups.charAt(nameGroupsIndex++);
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(nameIndex<0 || numNames<=nameIndex) {
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalIcuArgumentException("Invalid property (value) name choice");
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Skip nameIndex names.
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(; nameIndex>0; --nameIndex) {
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while(0!=nameGroups.charAt(nameGroupsIndex++)) {}
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Find the end of this name.
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int nameStart=nameGroupsIndex;
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while(0!=nameGroups.charAt(nameGroupsIndex)) {
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ++nameGroupsIndex;
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(nameStart==nameGroupsIndex) {
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;  // no name (Property[Value]Aliases.txt has "n/a")
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return nameGroups.substring(nameStart, nameGroupsIndex);
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static int asciiToLowercase(int c) {
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return 'A'<=c && c<='Z' ? c+0x20 : c;
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean containsName(BytesTrie trie, CharSequence name) {
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BytesTrie.Result result=BytesTrie.Result.NO_VALUE;
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(int i=0; i<name.length(); ++i) {
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int c=name.charAt(i);
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Ignore delimiters '-', '_', and ASCII White_Space.
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if(c=='-' || c=='_' || c==' ' || (0x09<=c && c<=0x0d)) {
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if(!result.hasNext()) {
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            c=asciiToLowercase(c);
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result=trie.next(c);
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.hasValue();
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //----------------------------------------------------------------
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Public API
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final UPropertyAliases INSTANCE;
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static {
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            INSTANCE = new UPropertyAliases();
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch(IOException e) {
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:OFF
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MissingResourceException mre = new MissingResourceException(
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    "Could not construct UPropertyAliases. Missing pnames.icu", "", "");
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            mre.initCause(e);
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw mre;
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:ON
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a property name given a property enum.
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Multiple names may be available for each property;
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the nameChoice selects among them.
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getPropertyName(int property, int nameChoice) {
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int valueMapIndex=findProperty(property);
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(valueMapIndex==0) {
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException(
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    "Invalid property enum "+property+" (0x"+Integer.toHexString(property)+")");
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getName(valueMaps[valueMapIndex], nameChoice);
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a value name given a property enum and a value enum.
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Multiple names may be available for each value;
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the nameChoice selects among them.
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getPropertyValueName(int property, int value, int nameChoice) {
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int valueMapIndex=findProperty(property);
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(valueMapIndex==0) {
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException(
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    "Invalid property enum "+property+" (0x"+Integer.toHexString(property)+")");
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int nameGroupOffset=findPropertyValueNameGroup(valueMaps[valueMapIndex+1], value);
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(nameGroupOffset==0) {
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException(
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    "Property "+property+" (0x"+Integer.toHexString(property)+
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ") does not have named values");
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getName(nameGroupOffset, nameChoice);
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int getPropertyOrValueEnum(int bytesTrieOffset, CharSequence alias) {
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BytesTrie trie=new BytesTrie(bytesTries, bytesTrieOffset);
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(containsName(trie, alias)) {
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return trie.getValue();
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return UProperty.UNDEFINED;
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a property enum given one of its property names.
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If the property name is not known, this method returns
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * UProperty.UNDEFINED.
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getPropertyEnum(CharSequence alias) {
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getPropertyOrValueEnum(0, alias);
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a value enum given a property enum and one of its value names.
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getPropertyValueEnum(int property, CharSequence alias) {
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int valueMapIndex=findProperty(property);
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(valueMapIndex==0) {
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException(
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    "Invalid property enum "+property+" (0x"+Integer.toHexString(property)+")");
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        valueMapIndex=valueMaps[valueMapIndex+1];
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(valueMapIndex==0) {
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException(
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    "Property "+property+" (0x"+Integer.toHexString(property)+
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ") does not have named values");
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // valueMapIndex is the start of the property's valueMap,
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // where the first word is the BytesTrie offset.
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getPropertyOrValueEnum(valueMaps[valueMapIndex], alias);
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a value enum given a property enum and one of its value names. Does not throw.
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return value enum, or UProperty.UNDEFINED if not defined for that property
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getPropertyValueEnumNoThrow(int property, CharSequence alias) {
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int valueMapIndex=findProperty(property);
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(valueMapIndex==0) {
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return UProperty.UNDEFINED;
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        valueMapIndex=valueMaps[valueMapIndex+1];
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(valueMapIndex==0) {
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return UProperty.UNDEFINED;
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // valueMapIndex is the start of the property's valueMap,
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // where the first word is the BytesTrie offset.
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getPropertyOrValueEnum(valueMaps[valueMapIndex], alias);
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Compare two property names, returning <0, 0, or >0.  The
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * comparison is that described as "loose" matching in the
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Property*Aliases.txt files.
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static int compare(String stra, String strb) {
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Note: This implementation is a literal copy of
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // uprv_comparePropertyNames.  It can probably be improved.
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int istra=0, istrb=0, rc;
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int cstra=0, cstrb=0;
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (;;) {
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /* Ignore delimiters '-', '_', and ASCII White_Space */
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while (istra<stra.length()) {
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cstra = stra.charAt(istra);
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                switch (cstra) {
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case '-':  case '_':  case ' ':  case '\t':
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case '\n': case 0xb/*\v*/: case '\f': case '\r':
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ++istra;
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while (istrb<strb.length()) {
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cstrb = strb.charAt(istrb);
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                switch (cstrb) {
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case '-':  case '_':  case ' ':  case '\t':
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case '\n': case 0xb/*\v*/: case '\f': case '\r':
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ++istrb;
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /* If we reach the ends of both strings then they match */
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean endstra = istra==stra.length();
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean endstrb = istrb==strb.length();
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (endstra) {
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (endstrb) return 0;
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cstra = 0;
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (endstrb) {
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cstrb = 0;
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            rc = asciiToLowercase(cstra) - asciiToLowercase(cstrb);
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (rc != 0) {
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return rc;
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ++istra;
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ++istrb;
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
381