]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/impl/UPropertyAliases.java
go
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / impl / UPropertyAliases.java
1 /*\r
2  **********************************************************************\r
3  * Copyright (c) 2002-2009, International Business Machines\r
4  * Corporation and others.  All Rights Reserved.\r
5  **********************************************************************\r
6  * Author: Alan Liu\r
7  * Created: November 5 2002\r
8  * Since: ICU 2.4\r
9  **********************************************************************\r
10  */\r
11 \r
12 package com.ibm.icu.impl;\r
13 \r
14 import java.io.*;\r
15 \r
16 import com.ibm.icu.lang.*;\r
17 \r
18 /**\r
19  * Wrapper for the pnames.icu binary data file.  This data file is\r
20  * imported from icu4c.  It contains property and property value\r
21  * aliases from the UCD files PropertyAliases.txt and\r
22  * PropertyValueAliases.txt.  The file is built by the icu4c tool\r
23  * genpname.  It must be built on an ASCII big-endian platform to be\r
24  * usable in icu4j.\r
25  *\r
26  * This class performs two functions.\r
27  *\r
28  * (1) It can import the flat binary data into a tree of usable\r
29  * objects.\r
30  *\r
31  * (2) It provides an API to access the tree of objects.\r
32  *\r
33  * Needless to say, this class is tightly coupled to the binary format\r
34  * of icu4c's pnames.icu file.\r
35  *\r
36  * Each time a UPropertyAliases is constructed, the pnames.icu file is\r
37  * read, parsed, and a data tree assembled.  Clients should create one\r
38  * singleton instance and cache it.\r
39  *\r
40  * @author Alan Liu\r
41  * @since ICU 2.4\r
42  */\r
43 public final class UPropertyAliases implements ICUBinary.Authenticate {\r
44 \r
45     //----------------------------------------------------------------\r
46     // Runtime data.  This is an unflattened representation of the\r
47     // data in pnames.icu.\r
48 \r
49     /**\r
50      * Map from property enum value to nameGroupPool[] index\r
51      */\r
52     private NonContiguousEnumToShort enumToName;\r
53 \r
54     /**\r
55      * Map from property alias to property enum value\r
56      */\r
57     private NameToEnum nameToEnum;\r
58 \r
59     /**\r
60      * Map from property enum value to valueMapArray[] index\r
61      */\r
62     private NonContiguousEnumToShort enumToValue;\r
63 \r
64     /**\r
65      * Each entry represents a binary or enumerated property\r
66      */\r
67     private ValueMap valueMapArray[];\r
68 \r
69     /**\r
70      * Pool of concatenated integer runs.  Each run contains one\r
71      * or more entries.  The last entry of the run is negative.\r
72      * A zero entry indicates "n/a" in the Property*Aliases.txt.\r
73      * Each entry is a stringPool[] index.\r
74      */\r
75     private short nameGroupPool[];\r
76 \r
77     /**\r
78      * Pool of strings.\r
79      */\r
80     private String stringPool[];\r
81 \r
82     //----------------------------------------------------------------\r
83     // Constants\r
84 \r
85     /**\r
86      * Debug flag (not really constant)\r
87      */\r
88     private static boolean DEBUG = ICUDebug.enabled("pnames");\r
89 \r
90     /**\r
91      * File format that this class understands.\r
92      * See icu4c/src/common/propname.h.\r
93      */\r
94     private static final byte DATA_FORMAT_ID[] = {'p', 'n', 'a', 'm'};\r
95 \r
96     /**\r
97      * File version that this class understands.\r
98      * See icu4c/src/common/propname.h.\r
99      */\r
100     private static final byte DATA_FORMAT_VERSION = 1;\r
101 \r
102     /**\r
103      * Name of the datafile\r
104      */\r
105     private static final String DATA_FILE_NAME = ICUResourceBundle.ICU_BUNDLE+"/pnames.icu";\r
106 \r
107     /**\r
108      * Buffer size of datafile.  The whole file is < 16k.\r
109      */\r
110     private static final int DATA_BUFFER_SIZE = 8192;\r
111 \r
112     //----------------------------------------------------------------\r
113     // Constructor\r
114 \r
115     /**\r
116      * Constructs a UPropertyAliases object.  The binary file\r
117      * DATA_FILE_NAME is read from the jar/classpath and unflattened\r
118      * into member variables of this object.\r
119      */\r
120     public UPropertyAliases() throws IOException {\r
121 \r
122         // Open the .icu file from the jar/classpath\r
123         InputStream is = ICUData.getRequiredStream(DATA_FILE_NAME);\r
124         BufferedInputStream b = new BufferedInputStream(is, DATA_BUFFER_SIZE);\r
125         // Read and discard Unicode version...\r
126        /* byte unicodeVersion[] = */ICUBinary.readHeader(b, DATA_FORMAT_ID, this);\r
127         DataInputStream d = new DataInputStream(b);\r
128 \r
129         // Record the origin position of the file.  Keep enough around\r
130         // to seek back to the start of the header.\r
131         d.mark(256);\r
132 \r
133         short enumToName_offset = d.readShort();\r
134         short nameToEnum_offset = d.readShort();\r
135         short enumToValue_offset = d.readShort();\r
136         short total_size = d.readShort();\r
137         short valueMap_offset = d.readShort();\r
138         short valueMap_count = d.readShort();\r
139         short nameGroupPool_offset = d.readShort();\r
140         short nameGroupPool_count = d.readShort();\r
141         short stringPool_offset = d.readShort();\r
142         short stringPool_count = d.readShort();\r
143 \r
144         if (DEBUG) {\r
145             System.out.println(\r
146                "enumToName_offset=" + enumToName_offset + "\n" +\r
147                "nameToEnum_offset=" + nameToEnum_offset + "\n" +\r
148                "enumToValue_offset=" + enumToValue_offset + "\n" +\r
149                "total_size=" + total_size + "\n" +\r
150                "valueMap_offset=" + valueMap_offset + "\n" +\r
151                "valueMap_count=" + valueMap_count + "\n" +\r
152                "nameGroupPool_offset=" + nameGroupPool_offset + "\n" +\r
153                "nameGroupPool_count=" + nameGroupPool_count + "\n" +\r
154                "stringPool_offset=" + stringPool_offset + "\n" +\r
155                "stringPool_count=" + stringPool_count);\r
156         }\r
157 \r
158         // Read it all (less than 32k).  Seeking around (using\r
159         // mark/reset/skipBytes) doesn't work directly on the file,\r
160         // but it works fine if we read everything into a byte[] array\r
161         // first.\r
162         byte raw[] = new byte[total_size];\r
163         d.reset();\r
164         d.readFully(raw);\r
165         d.close();\r
166 \r
167         Builder builder = new Builder(raw);\r
168 \r
169         stringPool = builder.readStringPool(stringPool_offset,\r
170                                             stringPool_count);\r
171 \r
172         nameGroupPool = builder.readNameGroupPool(nameGroupPool_offset,\r
173                                                   nameGroupPool_count);\r
174 \r
175         builder.setupValueMap_map(valueMap_offset, valueMap_count);\r
176 \r
177         // Some of the following data structures have to be set up\r
178         // here, _not_ in Builder.  That's because they are instances\r
179         // of non-static inner classes, and they contain implicit\r
180         // references to this.\r
181 \r
182         builder.seek(enumToName_offset);\r
183         enumToName = new NonContiguousEnumToShort(builder);\r
184         builder.nameGroupOffsetToIndex(enumToName.offsetArray);\r
185 \r
186         builder.seek(nameToEnum_offset);\r
187         nameToEnum = new NameToEnum(builder);\r
188 \r
189         builder.seek(enumToValue_offset);\r
190         enumToValue = new NonContiguousEnumToShort(builder);\r
191         builder.valueMapOffsetToIndex(enumToValue.offsetArray);\r
192 \r
193         valueMapArray = new ValueMap[valueMap_count];\r
194         for (int i=0; i<valueMap_count; ++i) {\r
195             // Must seek to the start of each entry.\r
196             builder.seek(builder.valueMap_map[i]);\r
197             valueMapArray[i] = new ValueMap(builder);\r
198         }\r
199 \r
200         builder.close();\r
201     }\r
202 \r
203     //----------------------------------------------------------------\r
204     // Public API\r
205 \r
206     /**\r
207      * Return a property name given a property enum.  Multiple\r
208      * names may be available for each property; the nameChoice\r
209      * selects among them.\r
210      */\r
211     public String getPropertyName(int property,\r
212                                   int nameChoice) {\r
213         short nameGroupIndex = enumToName.getShort(property);\r
214         return chooseNameInGroup(nameGroupIndex, nameChoice);\r
215     }\r
216 \r
217     /**\r
218      * Return a property enum given one of its property names.\r
219      */\r
220     public int getPropertyEnum(String propertyAlias) {\r
221         return nameToEnum.getEnum(propertyAlias);\r
222     }\r
223 \r
224     /**\r
225      * Return a value name given a property enum and a value enum.\r
226      * Multiple names may be available for each value; the nameChoice\r
227      * selects among them.\r
228      */\r
229     public String getPropertyValueName(int property,\r
230                                        int value,\r
231                                        int nameChoice) {\r
232         ValueMap vm = getValueMap(property);\r
233         short nameGroupIndex = vm.enumToName.getShort(value);\r
234         return chooseNameInGroup(nameGroupIndex, nameChoice);\r
235     }\r
236 \r
237     /**\r
238      * Return a value enum given one of its value names and the\r
239      * corresponding property alias.\r
240      */\r
241     public int getPropertyValueEnum(int property,\r
242                                     String valueAlias) {\r
243         ValueMap vm = getValueMap(property);\r
244         return vm.nameToEnum.getEnum(valueAlias);\r
245     }\r
246 \r
247     //----------------------------------------------------------------\r
248     // Data structures\r
249 \r
250     /**\r
251      * A map for the legal values of a binary or enumerated properties.\r
252      */\r
253     private class ValueMap {\r
254 \r
255         /**\r
256          * Maps value enum to index into the nameGroupPool[]\r
257          */\r
258         EnumToShort enumToName; // polymorphic\r
259 \r
260         /**\r
261          * Maps value name to value enum.\r
262          */\r
263         NameToEnum nameToEnum;\r
264 \r
265         ValueMap(Builder b) throws IOException {\r
266             short enumToName_offset = b.readShort();\r
267             short ncEnumToName_offset = b.readShort();\r
268             short nameToEnum_offset = b.readShort();\r
269             if (enumToName_offset != 0) {\r
270                 b.seek(enumToName_offset);\r
271                 ContiguousEnumToShort x = new ContiguousEnumToShort(b);\r
272                 b.nameGroupOffsetToIndex(x.offsetArray);\r
273                 enumToName = x;\r
274             } else {\r
275                 b.seek(ncEnumToName_offset);\r
276                 NonContiguousEnumToShort x = new NonContiguousEnumToShort(b);\r
277                 b.nameGroupOffsetToIndex(x.offsetArray);\r
278                 enumToName = x;\r
279             }\r
280             b.seek(nameToEnum_offset);\r
281             nameToEnum = new NameToEnum(b);\r
282         }\r
283     }\r
284 \r
285     /**\r
286      * Abstract map from enum values to integers.\r
287      */\r
288     private interface EnumToShort {\r
289         short getShort(int enumProbe);\r
290     }\r
291 \r
292     /**\r
293      * Generic map from enum values to offsets.  Enum values are\r
294      * contiguous.\r
295      */\r
296     private static class ContiguousEnumToShort implements EnumToShort {\r
297         int enumStart;\r
298         int enumLimit;\r
299         short offsetArray[];\r
300 \r
301         public short getShort(int enumProbe) {\r
302             if (enumProbe < enumStart || enumProbe >= enumLimit) {\r
303                 throw new IllegalIcuArgumentException("Invalid enum. enumStart = " +enumStart +\r
304                                                    " enumLimit = " + enumLimit +\r
305                                                    " enumProbe = " + enumProbe );\r
306             }\r
307             return offsetArray[enumProbe - enumStart];\r
308         }\r
309 \r
310         ContiguousEnumToShort(ICUBinaryStream s) throws IOException  {\r
311             enumStart = s.readInt();\r
312             enumLimit = s.readInt();\r
313             int count = enumLimit - enumStart;\r
314             offsetArray = new short[count];\r
315             for (int i=0; i<count; ++i) {\r
316                 offsetArray[i] = s.readShort();\r
317             }\r
318         }\r
319     }\r
320 \r
321     /**\r
322      * Generic map from enum values to offsets.  Enum values need not\r
323      * be contiguous.\r
324      */\r
325     private static class NonContiguousEnumToShort implements EnumToShort {\r
326         int enumArray[];\r
327         short offsetArray[];\r
328 \r
329         public short getShort(int enumProbe) {\r
330             for (int i=0; i<enumArray.length; ++i) {\r
331                 if (enumArray[i] < enumProbe) continue;\r
332                 if (enumArray[i] > enumProbe) break;\r
333                 return offsetArray[i];\r
334             }\r
335             throw new IllegalIcuArgumentException("Invalid enum");\r
336         }\r
337 \r
338         NonContiguousEnumToShort(ICUBinaryStream s) throws IOException  {\r
339             int i;\r
340             int count = s.readInt();\r
341             enumArray = new int[count];\r
342             offsetArray = new short[count];\r
343             for (i=0; i<count; ++i) {\r
344                 enumArray[i] = s.readInt();\r
345             }\r
346             for (i=0; i<count; ++i) {\r
347                 offsetArray[i] = s.readShort();\r
348             }\r
349         }\r
350     }\r
351 \r
352     /**\r
353      * Map from names to enum values.\r
354      */\r
355     private class NameToEnum {\r
356         int enumArray[];\r
357         short nameArray[];\r
358 \r
359         int getEnum(String nameProbe) {\r
360             for (int i=0; i<nameArray.length; ++i) {\r
361                 int c = UPropertyAliases.compare(nameProbe,\r
362                                                  stringPool[nameArray[i]]);\r
363                 if (c > 0) continue;\r
364                 if (c < 0) break;\r
365                 return enumArray[i];\r
366             }\r
367             throw new IllegalIcuArgumentException("Invalid name: " + nameProbe);\r
368         }\r
369 \r
370         NameToEnum(Builder b) throws IOException {\r
371             int i;\r
372             int count = b.readInt();\r
373             enumArray = new int[count];\r
374             nameArray = new short[count];\r
375             for (i=0; i<count; ++i) {\r
376                 enumArray[i] = b.readInt();\r
377             }\r
378             for (i=0; i<count; ++i) {\r
379                 nameArray[i] = b.stringOffsetToIndex(b.readShort());\r
380             }\r
381         }\r
382     }\r
383 \r
384     //----------------------------------------------------------------\r
385     // Runtime implementation\r
386 \r
387     /**\r
388      * Compare two property names, returning <0, 0, or >0.  The\r
389      * comparison is that described as "loose" matching in the\r
390      * Property*Aliases.txt files.\r
391      */\r
392     public static int compare(String stra, String strb) {\r
393         // Note: This implementation is a literal copy of\r
394         // uprv_comparePropertyNames.  It can probably be improved.\r
395         int istra=0, istrb=0, rc;\r
396         int cstra=0, cstrb=0;\r
397         for (;;) {\r
398             /* Ignore delimiters '-', '_', and ASCII White_Space */\r
399             while (istra<stra.length()) {\r
400                 cstra = stra.charAt(istra);\r
401                 switch (cstra) {\r
402                 case '-':  case '_':  case ' ':  case '\t':\r
403                 case '\n': case 0xb/*\v*/: case '\f': case '\r':\r
404                     ++istra;\r
405                     continue;\r
406                 }\r
407                 break;\r
408             }\r
409 \r
410             while (istrb<strb.length()) {\r
411                 cstrb = strb.charAt(istrb);\r
412                 switch (cstrb) {\r
413                 case '-':  case '_':  case ' ':  case '\t':\r
414                 case '\n': case 0xb/*\v*/: case '\f': case '\r':\r
415                     ++istrb;\r
416                     continue;\r
417                 }\r
418                 break;\r
419             }\r
420 \r
421             /* If we reach the ends of both strings then they match */\r
422             boolean endstra = istra==stra.length();\r
423             boolean endstrb = istrb==strb.length();\r
424             if (endstra) {\r
425                 if (endstrb) return 0;\r
426                 cstra = 0;\r
427             } else if (endstrb) {\r
428                 cstrb = 0;\r
429             }\r
430 \r
431             rc = UCharacter.toLowerCase(cstra) - UCharacter.toLowerCase(cstrb);\r
432             if (rc != 0) {\r
433                 return rc;\r
434             }\r
435 \r
436             ++istra;\r
437             ++istrb;\r
438         }\r
439     }\r
440 \r
441     /**\r
442      * Given an index to a run within the nameGroupPool[], and a\r
443      * nameChoice (0,1,...), select the nameChoice-th entry of the run.\r
444      */\r
445     private String chooseNameInGroup(short nameGroupIndex, int nameChoice) {\r
446         if (nameChoice < 0) {\r
447             throw new IllegalIcuArgumentException("Invalid name choice");\r
448         }\r
449         while (nameChoice-- > 0) {\r
450             if (nameGroupPool[nameGroupIndex++] < 0) {\r
451                 throw new IllegalIcuArgumentException("Invalid name choice");\r
452             }\r
453         }\r
454         short a = nameGroupPool[nameGroupIndex];\r
455         return stringPool[(a < 0) ? -a : a];\r
456     }\r
457 \r
458     /**\r
459      * Return the valueMap[] entry for a given property.\r
460      */\r
461     private ValueMap getValueMap(int property) {\r
462         int valueMapIndex = enumToValue.getShort(property);\r
463         return valueMapArray[valueMapIndex];\r
464     }\r
465 \r
466     //----------------------------------------------------------------\r
467     // ICUBinary API\r
468 \r
469     /**\r
470      * Return true if the given data version can be used.\r
471      */\r
472     public boolean isDataVersionAcceptable(byte version[])   {\r
473         return version[0] == DATA_FORMAT_VERSION;\r
474     }\r
475 \r
476     //----------------------------------------------------------------\r
477     // Builder\r
478 \r
479     /**\r
480      * A specialized ICUBinaryStream that can map between offsets and\r
481      * index values into various arrays (stringPool, nameGroupPool,\r
482      * and valueMap).  It also knows how to read various structures.\r
483      */\r
484     static class Builder extends ICUBinaryStream {\r
485 \r
486         // map[i] = offset of object i.  We need maps for all of our\r
487         // arrays.  The arrays are indexed by offset in the raw binary\r
488         // file; we need to translate that to index.\r
489 \r
490         private short stringPool_map[];\r
491 \r
492         private short valueMap_map[];\r
493 \r
494         private short nameGroup_map[];\r
495 \r
496         public Builder(byte raw[]) {\r
497             super(raw);\r
498         }\r
499 \r
500         /**\r
501          * The valueMap_map[] must be setup in advance.  This method\r
502          * does that.\r
503          */\r
504         public void setupValueMap_map(short offset, short count) {\r
505             valueMap_map = new short[count];\r
506             for (int i=0; i<count; ++i) {\r
507                 // Start of each entry.  Each entry is 6 bytes long.\r
508                 valueMap_map[i] = (short) (offset + i * 6);\r
509             }\r
510         }\r
511 \r
512         /**\r
513          * Read stringPool[].  Build up translation table from offsets\r
514          * to string indices (stringPool_map[]).\r
515          */\r
516         public String[] readStringPool(short offset, short count)\r
517             throws IOException {\r
518             seek(offset);\r
519             // Allocate one more stringPool entry than needed.  Use this\r
520             // to store a "no string" entry in the pool, at index 0.  This\r
521             // maps to offset 0, so let stringPool_map[0] = 0.\r
522             String stringPool[] = new String[count + 1];\r
523             stringPool_map = new short[count + 1];\r
524             short pos = offset;\r
525             StringBuffer buf = new StringBuffer();\r
526             stringPool_map[0] = 0;\r
527             for (int i=1; i<=count; ++i) {\r
528                 buf.setLength(0);\r
529                 for (;;) {\r
530                     // This works because the name is invariant-ASCII\r
531                     char c = (char) readUnsignedByte();\r
532                     if (c == 0) break;\r
533                     buf.append(c);\r
534                 }\r
535                 stringPool_map[i] = pos;\r
536                 stringPool[i] = buf.toString();\r
537                 pos += stringPool[i].length() + 1;\r
538             }\r
539             if (DEBUG) {\r
540                 System.out.println("read stringPool x " + count +\r
541                                    ": " + stringPool[1] + ", " +\r
542                                    stringPool[2] + ", " +\r
543                                    stringPool[3] + ",...");\r
544             }\r
545             return stringPool;\r
546         }\r
547 \r
548         /**\r
549          * Read the nameGroupPool[], and build up the offset->index\r
550          * map (nameGroupPool_map[]).\r
551          */\r
552         public short[] readNameGroupPool(short offset, short count)\r
553             throws IOException {\r
554             // Read nameGroupPool[].  This contains offsets from start of\r
555             // header.  We translate these into indices into stringPool[]\r
556             // on the fly.  The offset 0, which indicates "no entry", we\r
557             // translate into index 0, which contains a null String\r
558             // pointer.\r
559             seek(offset);\r
560             short pos = offset;\r
561             short nameGroupPool[] = new short[count];\r
562             nameGroup_map = new short[count];\r
563             for (int i=0; i<count; ++i) {\r
564                 nameGroup_map[i] = pos;\r
565                 nameGroupPool[i] = stringOffsetToIndex(readShort());\r
566                 pos += 2;\r
567             }\r
568             if (DEBUG) {\r
569                 System.out.println("read nameGroupPool x " + count +\r
570                                    ": " + nameGroupPool[0] + ", " +\r
571                                    nameGroupPool[1] + ", " +\r
572                                    nameGroupPool[2] + ",...");\r
573             }\r
574             return nameGroupPool;\r
575         }\r
576 \r
577         /**\r
578          * Convert an offset into the string pool into a stringPool[]\r
579          * index.\r
580          */\r
581         private short stringOffsetToIndex(short offset) {\r
582             int probe = offset;\r
583             if (probe < 0) probe = -probe;\r
584             for (int i=0; i<stringPool_map.length; ++i) {\r
585                 if (stringPool_map[i] == probe) {\r
586                     return (short) ((offset < 0) ? -i : i);\r
587                 }\r
588             }\r
589             throw new IllegalStateException("Can't map string pool offset " + \r
590                                             offset + " to index");\r
591         }\r
592 \r
593         /**\r
594          * Convert an array of offsets into the string pool into an\r
595          * array of stringPool[] indices.  MODIFIES THE ARRAY IN\r
596          * PLACE.\r
597          */\r
598 /*        private void stringOffsetToIndex(short array[]) {\r
599             for (int i=0; i<array.length; ++i) {\r
600                 array[i] = stringOffsetToIndex(array[i]);\r
601             }\r
602         }*/\r
603 \r
604         /**\r
605          * Convert an offset into the value map into a valueMap[]\r
606          * index.\r
607          */\r
608         private short valueMapOffsetToIndex(short offset) {\r
609             for (short i=0; i<valueMap_map.length; ++i) {\r
610                 if (valueMap_map[i] == offset) {\r
611                     return i;\r
612                 }\r
613             }\r
614             throw new IllegalStateException("Can't map value map offset " + \r
615                                             offset + " to index");\r
616         }\r
617 \r
618         /**\r
619          * Convert an array of offsets into the value map array into\r
620          * an array of valueMap[] indices.  MODIFIES THE ARRAY IN\r
621          * PLACE.\r
622          */\r
623         private void valueMapOffsetToIndex(short array[]) {\r
624             for (int i=0; i<array.length; ++i) {\r
625                 array[i] = valueMapOffsetToIndex(array[i]);\r
626             }\r
627         }\r
628 \r
629         /**\r
630          * Convert an offset into the name group pool into a\r
631          * nameGroupPool[] index.\r
632          */\r
633         private short nameGroupOffsetToIndex(short offset) {\r
634             for (short i=0; i<nameGroup_map.length; ++i) {\r
635                 if (nameGroup_map[i] == offset) {\r
636                     return i;\r
637                 }\r
638             }\r
639             throw new RuntimeException("Can't map name group offset " + offset +\r
640                                        " to index");\r
641         }\r
642 \r
643         /**\r
644          * Convert an array of offsets into the name group pool into an\r
645          * array of nameGroupPool[] indices.  MODIFIES THE ARRAY IN\r
646          * PLACE.\r
647          */\r
648         private void nameGroupOffsetToIndex(short array[]) {\r
649             for (int i=0; i<array.length; ++i) {\r
650                 array[i] = nameGroupOffsetToIndex(array[i]);\r
651             }\r
652         }\r
653     }\r
654 }\r