KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > impl > UPropertyAliases


1 /*
2 **********************************************************************
3 * Copyright (c) 2002-2006, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
6 * Author: Alan Liu
7 * Created: November 5 2002
8 * Since: ICU 2.4
9 **********************************************************************
10 */

11 package com.ibm.icu.impl;
12
13 import java.io.*;
14
15 import com.ibm.icu.lang.*;
16
17 /**
18  * Wrapper for the pnames.icu binary data file. This data file is
19  * imported from icu4c. It contains property and property value
20  * aliases from the UCD files PropertyAliases.txt and
21  * PropertyValueAliases.txt. The file is built by the icu4c tool
22  * genpname. It must be built on an ASCII big-endian platform to be
23  * usable in icu4j.
24  *
25  * This class performs two functions.
26  *
27  * (1) It can import the flat binary data into a tree of usable
28  * objects.
29  *
30  * (2) It provides an API to access the tree of objects.
31  *
32  * Needless to say, this class is tightly coupled to the binary format
33  * of icu4c's pnames.icu file.
34  *
35  * Each time a UPropertyAliases is constructed, the pnames.icu file is
36  * read, parsed, and a data tree assembled. Clients should create one
37  * singleton instance and cache it.
38  *
39  * @author Alan Liu
40  * @since ICU 2.4
41  */

42 public final class UPropertyAliases implements ICUBinary.Authenticate {
43
44     //----------------------------------------------------------------
45
// Runtime data. This is an unflattened representation of the
46
// data in pnames.icu.
47

48     /**
49      * Map from property enum value to nameGroupPool[] index
50      */

51     private NonContiguousEnumToShort enumToName;
52
53     /**
54      * Map from property alias to property enum value
55      */

56     private NameToEnum nameToEnum;
57
58     /**
59      * Map from property enum value to valueMapArray[] index
60      */

61     private NonContiguousEnumToShort enumToValue;
62
63     /**
64      * Each entry represents a binary or enumerated property
65      */

66     private ValueMap valueMapArray[];
67
68     /**
69      * Pool of concatenated integer runs. Each run contains one
70      * or more entries. The last entry of the run is negative.
71      * A zero entry indicates "n/a" in the Property*Aliases.txt.
72      * Each entry is a stringPool[] index.
73      */

74     private short nameGroupPool[];
75
76     /**
77      * Pool of strings.
78      */

79     private String JavaDoc stringPool[];
80
81     //----------------------------------------------------------------
82
// Constants
83

84     /**
85      * Debug flag (not really constant)
86      */

87     private static boolean DEBUG = ICUDebug.enabled("pnames");
88
89     /**
90      * File format that this class understands.
91      * See icu4c/src/common/propname.h.
92      */

93     private static final byte DATA_FORMAT_ID[] = {'p', 'n', 'a', 'm'};
94
95     /**
96      * File version that this class understands.
97      * See icu4c/src/common/propname.h.
98      */

99     private static final byte DATA_FORMAT_VERSION = 1;
100
101     /**
102      * Name of the datafile
103      */

104     private static final String JavaDoc DATA_FILE_NAME = ICUResourceBundle.ICU_BUNDLE+"/pnames.icu";
105
106     /**
107      * Buffer size of datafile. The whole file is < 16k.
108      */

109     private static final int DATA_BUFFER_SIZE = 8192;
110
111     //----------------------------------------------------------------
112
// Constructor
113

114     /**
115      * Constructs a UPropertyAliases object. The binary file
116      * DATA_FILE_NAME is read from the jar/classpath and unflattened
117      * into member variables of this object.
118      */

119     public UPropertyAliases() throws IOException {
120
121         // Open the .icu file from the jar/classpath
122
InputStream is = ICUData.getRequiredStream(DATA_FILE_NAME);
123         BufferedInputStream b = new BufferedInputStream(is, DATA_BUFFER_SIZE);
124         // Read and discard Unicode version...
125
/* byte unicodeVersion[] = */ICUBinary.readHeader(b, DATA_FORMAT_ID, this);
126         DataInputStream d = new DataInputStream(b);
127
128         // Record the origin position of the file. Keep enough around
129
// to seek back to the start of the header.
130
d.mark(256);
131
132         short enumToName_offset = d.readShort();
133         short nameToEnum_offset = d.readShort();
134         short enumToValue_offset = d.readShort();
135         short total_size = d.readShort();
136         short valueMap_offset = d.readShort();
137         short valueMap_count = d.readShort();
138         short nameGroupPool_offset = d.readShort();
139         short nameGroupPool_count = d.readShort();
140         short stringPool_offset = d.readShort();
141         short stringPool_count = d.readShort();
142
143         if (DEBUG) {
144             System.out.println(
145                "enumToName_offset=" + enumToName_offset + "\n" +
146                "nameToEnum_offset=" + nameToEnum_offset + "\n" +
147                "enumToValue_offset=" + enumToValue_offset + "\n" +
148                "total_size=" + total_size + "\n" +
149                "valueMap_offset=" + valueMap_offset + "\n" +
150                "valueMap_count=" + valueMap_count + "\n" +
151                "nameGroupPool_offset=" + nameGroupPool_offset + "\n" +
152                "nameGroupPool_count=" + nameGroupPool_count + "\n" +
153                "stringPool_offset=" + stringPool_offset + "\n" +
154                "stringPool_count=" + stringPool_count);
155         }
156
157         // Read it all (less than 32k). Seeking around (using
158
// mark/reset/skipBytes) doesn't work directly on the file,
159
// but it works fine if we read everything into a byte[] array
160
// first.
161
byte raw[] = new byte[total_size];
162         d.reset();
163         d.readFully(raw);
164         d.close();
165
166         Builder builder = new Builder(raw);
167
168         stringPool = builder.readStringPool(stringPool_offset,
169                                             stringPool_count);
170
171         nameGroupPool = builder.readNameGroupPool(nameGroupPool_offset,
172                                                   nameGroupPool_count);
173
174         builder.setupValueMap_map(valueMap_offset, valueMap_count);
175
176         // Some of the following data structures have to be set up
177
// here, _not_ in Builder. That's because they are instances
178
// of non-static inner classes, and they contain implicit
179
// references to this.
180

181         builder.seek(enumToName_offset);
182         enumToName = new NonContiguousEnumToShort(builder);
183         builder.nameGroupOffsetToIndex(enumToName.offsetArray);
184
185         builder.seek(nameToEnum_offset);
186         nameToEnum = new NameToEnum(builder);
187
188         builder.seek(enumToValue_offset);
189         enumToValue = new NonContiguousEnumToShort(builder);
190         builder.valueMapOffsetToIndex(enumToValue.offsetArray);
191
192         valueMapArray = new ValueMap[valueMap_count];
193         for (int i=0; i<valueMap_count; ++i) {
194             // Must seek to the start of each entry.
195
builder.seek(builder.valueMap_map[i]);
196             valueMapArray[i] = new ValueMap(builder);
197         }
198
199         builder.close();
200     }
201
202     //----------------------------------------------------------------
203
// Public API
204

205     /**
206      * Return a property name given a property enum. Multiple
207      * names may be available for each property; the nameChoice
208      * selects among them.
209      */

210     public String JavaDoc getPropertyName(int property,
211                                   int nameChoice) {
212         short nameGroupIndex = enumToName.getShort(property);
213         return chooseNameInGroup(nameGroupIndex, nameChoice);
214     }
215
216     /**
217      * Return a property enum given one of its property names.
218      */

219     public int getPropertyEnum(String JavaDoc propertyAlias) {
220         return nameToEnum.getEnum(propertyAlias);
221     }
222
223     /**
224      * Return a value name given a property enum and a value enum.
225      * Multiple names may be available for each value; the nameChoice
226      * selects among them.
227      */

228     public String JavaDoc getPropertyValueName(int property,
229                                        int value,
230                                        int nameChoice) {
231         ValueMap vm = getValueMap(property);
232         short nameGroupIndex = vm.enumToName.getShort(value);
233         return chooseNameInGroup(nameGroupIndex, nameChoice);
234     }
235
236     /**
237      * Return a value enum given one of its value names and the
238      * corresponding property alias.
239      */

240     public int getPropertyValueEnum(int property,
241                                     String JavaDoc valueAlias) {
242         ValueMap vm = getValueMap(property);
243         return vm.nameToEnum.getEnum(valueAlias);
244     }
245
246     //----------------------------------------------------------------
247
// Data structures
248

249     /**
250      * A map for the legal values of a binary or enumerated properties.
251      */

252     private class ValueMap {
253
254         /**
255          * Maps value enum to index into the nameGroupPool[]
256          */

257         EnumToShort enumToName; // polymorphic
258

259         /**
260          * Maps value name to value enum.
261          */

262         NameToEnum nameToEnum;
263
264         ValueMap(Builder b) throws IOException {
265             short enumToName_offset = b.readShort();
266             short ncEnumToName_offset = b.readShort();
267             short nameToEnum_offset = b.readShort();
268             if (enumToName_offset != 0) {
269                 b.seek(enumToName_offset);
270                 ContiguousEnumToShort x = new ContiguousEnumToShort(b);
271                 b.nameGroupOffsetToIndex(x.offsetArray);
272                 enumToName = x;
273             } else {
274                 b.seek(ncEnumToName_offset);
275                 NonContiguousEnumToShort x = new NonContiguousEnumToShort(b);
276                 b.nameGroupOffsetToIndex(x.offsetArray);
277                 enumToName = x;
278             }
279             b.seek(nameToEnum_offset);
280             nameToEnum = new NameToEnum(b);
281         }
282     }
283
284     /**
285      * Abstract map from enum values to integers.
286      */

287     private interface EnumToShort {
288         short getShort(int enumProbe);
289     }
290
291     /**
292      * Generic map from enum values to offsets. Enum values are
293      * contiguous.
294      */

295     private static class ContiguousEnumToShort implements EnumToShort {
296         int enumStart;
297         int enumLimit;
298         short offsetArray[];
299
300         public short getShort(int enumProbe) {
301             if (enumProbe < enumStart || enumProbe >= enumLimit) {
302                 throw new IllegalArgumentException JavaDoc("Invalid enum. enumStart = " +enumStart +
303                                                    " enumLimit = " + enumLimit +
304                                                    " enumProbe = " + enumProbe );
305             }
306             return offsetArray[enumProbe - enumStart];
307         }
308
309         ContiguousEnumToShort(ICUBinaryStream s) throws IOException {
310             enumStart = s.readInt();
311             enumLimit = s.readInt();
312             int count = enumLimit - enumStart;
313             offsetArray = new short[count];
314             for (int i=0; i<count; ++i) {
315                 offsetArray[i] = s.readShort();
316             }
317         }
318     }
319
320     /**
321      * Generic map from enum values to offsets. Enum values need not
322      * be contiguous.
323      */

324     private static class NonContiguousEnumToShort implements EnumToShort {
325         int enumArray[];
326         short offsetArray[];
327
328         public short getShort(int enumProbe) {
329             for (int i=0; i<enumArray.length; ++i) {
330                 if (enumArray[i] < enumProbe) continue;
331                 if (enumArray[i] > enumProbe) break;
332                 return offsetArray[i];
333             }
334             throw new IllegalArgumentException JavaDoc("Invalid enum");
335         }
336
337         NonContiguousEnumToShort(ICUBinaryStream s) throws IOException {
338             int i;
339             int count = s.readInt();
340             enumArray = new int[count];
341             offsetArray = new short[count];
342             for (i=0; i<count; ++i) {
343                 enumArray[i] = s.readInt();
344             }
345             for (i=0; i<count; ++i) {
346                 offsetArray[i] = s.readShort();
347             }
348         }
349     }
350
351     /**
352      * Map from names to enum values.
353      */

354     private class NameToEnum {
355         int enumArray[];
356         short nameArray[];
357
358         int getEnum(String JavaDoc nameProbe) {
359             for (int i=0; i<nameArray.length; ++i) {
360                 int c = UPropertyAliases.compare(nameProbe,
361                                                  stringPool[nameArray[i]]);
362                 if (c > 0) continue;
363                 if (c < 0) break;
364                 return enumArray[i];
365             }
366             throw new IllegalArgumentException JavaDoc("Invalid name: " + nameProbe);
367         }
368
369         NameToEnum(Builder b) throws IOException {
370             int i;
371             int count = b.readInt();
372             enumArray = new int[count];
373             nameArray = new short[count];
374             for (i=0; i<count; ++i) {
375                 enumArray[i] = b.readInt();
376             }
377             for (i=0; i<count; ++i) {
378                 nameArray[i] = b.stringOffsetToIndex(b.readShort());
379             }
380         }
381     }
382
383     //----------------------------------------------------------------
384
// Runtime implementation
385

386     /**
387      * Compare two property names, returning <0, 0, or >0. The
388      * comparison is that described as "loose" matching in the
389      * Property*Aliases.txt files.
390      */

391     public static int compare(String JavaDoc stra, String JavaDoc strb) {
392         // Note: This implementation is a literal copy of
393
// uprv_comparePropertyNames. It can probably be improved.
394
int istra=0, istrb=0, rc;
395         int cstra=0, cstrb=0;
396         for (;;) {
397             /* Ignore delimiters '-', '_', and ASCII White_Space */
398             while (istra<stra.length()) {
399                 cstra = stra.charAt(istra);
400                 switch (cstra) {
401                 case '-': case '_': case ' ': case '\t':
402                 case '\n': case 0xb/*\v*/: case '\f': case '\r':
403                     ++istra;
404                     continue;
405                 }
406                 break;
407             }
408
409             while (istrb<strb.length()) {
410                 cstrb = strb.charAt(istrb);
411                 switch (cstrb) {
412                 case '-': case '_': case ' ': case '\t':
413                 case '\n': case 0xb/*\v*/: case '\f': case '\r':
414                     ++istrb;
415                     continue;
416                 }
417                 break;
418             }
419
420             /* If we reach the ends of both strings then they match */
421             boolean endstra = istra==stra.length();
422             boolean endstrb = istrb==strb.length();
423             if (endstra) {
424                 if (endstrb) return 0;
425                 cstra = 0;
426             } else if (endstrb) {
427                 cstrb = 0;
428             }
429
430             rc = UCharacter.toLowerCase(cstra) - UCharacter.toLowerCase(cstrb);
431             if (rc != 0) {
432                 return rc;
433             }
434
435             ++istra;
436             ++istrb;
437         }
438     }
439
440     /**
441      * Given an index to a run within the nameGroupPool[], and a
442      * nameChoice (0,1,...), select the nameChoice-th entry of the run.
443      */

444     private String JavaDoc chooseNameInGroup(short nameGroupIndex, int nameChoice) {
445         if (nameChoice < 0) {
446             throw new IllegalArgumentException JavaDoc("Invalid name choice");
447         }
448         while (nameChoice-- > 0) {
449             if (nameGroupPool[nameGroupIndex++] < 0) {
450                 throw new IllegalArgumentException JavaDoc("Invalid name choice");
451             }
452         }
453         short a = nameGroupPool[nameGroupIndex];
454         return stringPool[(a < 0) ? -a : a];
455     }
456
457     /**
458      * Return the valueMap[] entry for a given property.
459      */

460     private ValueMap getValueMap(int property) {
461         int valueMapIndex = enumToValue.getShort(property);
462         return valueMapArray[valueMapIndex];
463     }
464
465     //----------------------------------------------------------------
466
// ICUBinary API
467

468     /**
469      * Return true if the given data version can be used.
470      */

471     public boolean isDataVersionAcceptable(byte version[]) {
472         return version[0] == DATA_FORMAT_VERSION;
473     }
474
475     //----------------------------------------------------------------
476
// Builder
477

478     /**
479      * A specialized ICUBinaryStream that can map between offsets and
480      * index values into various arrays (stringPool, nameGroupPool,
481      * and valueMap). It also knows how to read various structures.
482      */

483     static class Builder extends ICUBinaryStream {
484
485         // map[i] = offset of object i. We need maps for all of our
486
// arrays. The arrays are indexed by offset in the raw binary
487
// file; we need to translate that to index.
488

489         private short stringPool_map[];
490
491         private short valueMap_map[];
492
493         private short nameGroup_map[];
494
495         public Builder(byte raw[]) {
496             super(raw);
497         }
498
499         /**
500          * The valueMap_map[] must be setup in advance. This method
501          * does that.
502          */

503         public void setupValueMap_map(short offset, short count) {
504             valueMap_map = new short[count];
505             for (int i=0; i<count; ++i) {
506                 // Start of each entry. Each entry is 6 bytes long.
507
valueMap_map[i] = (short) (offset + i * 6);
508             }
509         }
510
511         /**
512          * Read stringPool[]. Build up translation table from offsets
513          * to string indices (stringPool_map[]).
514          */

515         public String JavaDoc[] readStringPool(short offset, short count)
516             throws IOException {
517             seek(offset);
518             // Allocate one more stringPool entry than needed. Use this
519
// to store a "no string" entry in the pool, at index 0. This
520
// maps to offset 0, so let stringPool_map[0] = 0.
521
String JavaDoc stringPool[] = new String JavaDoc[count + 1];
522             stringPool_map = new short[count + 1];
523             short pos = offset;
524             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
525             stringPool_map[0] = 0;
526             for (int i=1; i<=count; ++i) {
527                 buf.setLength(0);
528                 for (;;) {
529                     // This works because the name is invariant-ASCII
530
char c = (char) readUnsignedByte();
531                     if (c == 0) break;
532                     buf.append(c);
533                 }
534                 stringPool_map[i] = pos;
535                 stringPool[i] = buf.toString();
536                 pos += stringPool[i].length() + 1;
537             }
538             if (DEBUG) {
539                 System.out.println("read stringPool x " + count +
540                                    ": " + stringPool[1] + ", " +
541                                    stringPool[2] + ", " +
542                                    stringPool[3] + ",...");
543             }
544             return stringPool;
545         }
546
547         /**
548          * Read the nameGroupPool[], and build up the offset->index
549          * map (nameGroupPool_map[]).
550          */

551         public short[] readNameGroupPool(short offset, short count)
552             throws IOException {
553             // Read nameGroupPool[]. This contains offsets from start of
554
// header. We translate these into indices into stringPool[]
555
// on the fly. The offset 0, which indicates "no entry", we
556
// translate into index 0, which contains a null String
557
// pointer.
558
seek(offset);
559             short pos = offset;
560             short nameGroupPool[] = new short[count];
561             nameGroup_map = new short[count];
562             for (int i=0; i<count; ++i) {
563                 nameGroup_map[i] = pos;
564                 nameGroupPool[i] = stringOffsetToIndex(readShort());
565                 pos += 2;
566             }
567             if (DEBUG) {
568                 System.out.println("read nameGroupPool x " + count +
569                                    ": " + nameGroupPool[0] + ", " +
570                                    nameGroupPool[1] + ", " +
571                                    nameGroupPool[2] + ",...");
572             }
573             return nameGroupPool;
574         }
575
576         /**
577          * Convert an offset into the string pool into a stringPool[]
578          * index.
579          */

580         private short stringOffsetToIndex(short offset) {
581             int probe = offset;
582             if (probe < 0) probe = -probe;
583             for (int i=0; i<stringPool_map.length; ++i) {
584                 if (stringPool_map[i] == probe) {
585                     return (short) ((offset < 0) ? -i : i);
586                 }
587             }
588             throw new IllegalStateException JavaDoc("Can't map string pool offset " +
589                                             offset + " to index");
590         }
591
592         /**
593          * Convert an array of offsets into the string pool into an
594          * array of stringPool[] indices. MODIFIES THE ARRAY IN
595          * PLACE.
596          */

597         ///CLOVER:OFF
598
private void stringOffsetToIndex(short array[]) {
599             for (int i=0; i<array.length; ++i) {
600                 array[i] = stringOffsetToIndex(array[i]);
601             }
602         }
603         ///CLOVER:ON
604

605         /**
606          * Convert an offset into the value map into a valueMap[]
607          * index.
608          */

609         private short valueMapOffsetToIndex(short offset) {
610             for (short i=0; i<valueMap_map.length; ++i) {
611                 if (valueMap_map[i] == offset) {
612                     return i;
613                 }
614             }
615             throw new IllegalStateException JavaDoc("Can't map value map offset " +
616                                             offset + " to index");
617         }
618
619         /**
620          * Convert an array of offsets into the value map array into
621          * an array of valueMap[] indices. MODIFIES THE ARRAY IN
622          * PLACE.
623          */

624         private void valueMapOffsetToIndex(short array[]) {
625             for (int i=0; i<array.length; ++i) {
626                 array[i] = valueMapOffsetToIndex(array[i]);
627             }
628         }
629
630         /**
631          * Convert an offset into the name group pool into a
632          * nameGroupPool[] index.
633          */

634         private short nameGroupOffsetToIndex(short offset) {
635             for (short i=0; i<nameGroup_map.length; ++i) {
636                 if (nameGroup_map[i] == offset) {
637                     return i;
638                 }
639             }
640             throw new RuntimeException JavaDoc("Can't map name group offset " + offset +
641                                        " to index");
642         }
643
644         /**
645          * Convert an array of offsets into the name group pool into an
646          * array of nameGroupPool[] indices. MODIFIES THE ARRAY IN
647          * PLACE.
648          */

649         private void nameGroupOffsetToIndex(short array[]) {
650             for (int i=0; i<array.length; ++i) {
651                 array[i] = nameGroupOffsetToIndex(array[i]);
652             }
653         }
654     }
655 }
656
Popular Tags