]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundleReader.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / main / classes / core / src / com / ibm / icu / impl / ICUResourceBundleReader.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 2004-2011, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.impl;
8
9 import java.io.BufferedInputStream;
10 import java.io.DataInputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.nio.ByteBuffer;
14
15 import com.ibm.icu.util.UResourceBundle;
16 import com.ibm.icu.util.VersionInfo;
17
18 /**
19  * This class reads the *.res resource bundle format
20  *
21  * (For the latest version of the file format documentation see
22  * ICU4C's source/common/uresdata.h file.)
23  *
24  * File format for .res resource bundle files (formatVersion=2, ICU 4.4)
25  *
26  * New in formatVersion 2 compared with 1.3: -------------
27  *
28  * Three new resource types -- String-v2, Table16 and Array16 -- have their
29  * values stored in a new array of 16-bit units between the table key strings
30  * and the start of the other resources.
31  *
32  * genrb eliminates duplicates among Unicode string-v2 values.
33  * Multiple Unicode strings may use the same offset and string data,
34  * or a short string may point to the suffix of a longer string. ("Suffix sharing")
35  * For example, one string "abc" may be reused for another string "bc" by pointing
36  * to the second character. (Short strings-v2 are NUL-terminated
37  * and not preceded by an explicit length value.)
38  *
39  * It is allowed for all resource types to share values.
40  * The swapper code (ures_swap()) has been modified so that it swaps each item
41  * exactly once.
42  *
43  * A resource bundle may use a special pool bundle. Some or all of the table key strings
44  * of the using-bundle are omitted, and the key string offsets for such key strings refer
45  * to offsets in the pool bundle.
46  * The using-bundle's and the pool-bundle's indexes[URES_INDEX_POOL_CHECKSUM] values
47  * must match.
48  * Two bits in indexes[URES_INDEX_ATTRIBUTES] indicate whether a resource bundle
49  * is or uses a pool bundle.
50  *
51  * Table key strings must be compared in ASCII order, even if they are not
52  * stored in ASCII.
53  *
54  * New in formatVersion 1.3 compared with 1.2: -------------
55  *
56  * genrb eliminates duplicates among key strings.
57  * Multiple table items may share one key string, or one item may point
58  * to the suffix of another's key string. ("Suffix sharing")
59  * For example, one key "abc" may be reused for another key "bc" by pointing
60  * to the second character. (Key strings are NUL-terminated.)
61  *
62  * -------------
63  *
64  * An ICU4C resource bundle file (.res) is a binary, memory-mappable file
65  * with nested, hierarchical data structures.
66  * It physically contains the following:
67  *
68  *   Resource root; -- 32-bit Resource item, root item for this bundle's tree;
69  *                     currently, the root item must be a table or table32 resource item
70  *   int32_t indexes[indexes[0]]; -- array of indexes for friendly
71  *                                   reading and swapping; see URES_INDEX_* above
72  *                                   new in formatVersion 1.1 (ICU 2.8)
73  *   char keys[]; -- characters for key strings
74  *                   (formatVersion 1.0: up to 65k of characters; 1.1: <2G)
75  *                   (minus the space for root and indexes[]),
76  *                   which consist of invariant characters (ASCII/EBCDIC) and are NUL-terminated;
77  *                   padded to multiple of 4 bytes for 4-alignment of the following data
78  *   uint16_t 16BitUnits[]; -- resources that are stored entirely as sequences of 16-bit units
79  *                             (new in formatVersion 2/ICU 4.4)
80  *                             data is indexed by the offset values in 16-bit resource types,
81  *                             with offset 0 pointing to the beginning of this array;
82  *                             there is a 0 at offset 0, for empty resources;
83  *                             padded to multiple of 4 bytes for 4-alignment of the following data
84  *   data; -- data directly and indirectly indexed by the root item;
85  *            the structure is determined by walking the tree
86  *
87  * Each resource bundle item has a 32-bit Resource handle (see typedef above)
88  * which contains the item type number in its upper 4 bits (31..28) and either
89  * an offset or a direct value in its lower 28 bits (27..0).
90  * The order of items is undefined and only determined by walking the tree.
91  * Leaves of the tree may be stored first or last or anywhere in between,
92  * and it is in theory possible to have unreferenced holes in the file.
93  *
94  * 16-bit-unit values:
95  * Starting with formatVersion 2/ICU 4.4, some resources are stored in a special
96  * array of 16-bit units. Each resource value is a sequence of 16-bit units,
97  * with no per-resource padding to a 4-byte boundary.
98  * 16-bit container types (Table16 and Array16) contain Resource16 values
99  * which are offsets to String-v2 resources in the same 16-bit-units array.
100  *
101  * Direct values:
102  * - Empty Unicode strings have an offset value of 0 in the Resource handle itself.
103  * - Starting with formatVersion 2/ICU 4.4, an offset value of 0 for
104  *   _any_ resource type indicates an empty value.
105  * - Integer values are 28-bit values stored in the Resource handle itself;
106  *   the interpretation of unsigned vs. signed integers is up to the application.
107  *
108  * All other types and values use 28-bit offsets to point to the item's data.
109  * The offset is an index to the first 32-bit word of the value, relative to the
110  * start of the resource data (i.e., the root item handle is at offset 0).
111  * To get byte offsets, the offset is multiplied by 4 (or shifted left by 2 bits).
112  * All resource item values are 4-aligned.
113  *
114  * New in formatVersion 2/ICU 4.4: Some types use offsets into the 16-bit-units array,
115  * indexing 16-bit units in that array.
116  *
117  * The structures (memory layouts) for the values for each item type are listed
118  * in the table below.
119  *
120  * Nested, hierarchical structures: -------------
121  *
122  * Table items contain key-value pairs where the keys are offsets to char * key strings.
123  * The values of these pairs are either Resource handles or
124  * offsets into the 16-bit-units array, depending on the table type.
125  *
126  * Array items are simple vectors of Resource handles,
127  * or of offsets into the 16-bit-units array, depending on the array type.
128  *
129  * Table key string offsets: -------
130  *
131  * Key string offsets are relative to the start of the resource data (of the root handle),
132  * i.e., the first string has an offset of 4+sizeof(indexes).
133  * (After the 4-byte root handle and after the indexes array.)
134  *
135  * If the resource bundle uses a pool bundle, then some key strings are stored
136  * in the pool bundle rather than in the local bundle itself.
137  * - In a Table or Table16, the 16-bit key string offset is local if it is
138  *   less than indexes[URES_INDEX_KEYS_TOP]<<2.
139  *   Otherwise, subtract indexes[URES_INDEX_KEYS_TOP]<<2 to get the offset into
140  *   the pool bundle key strings.
141  * - In a Table32, the 32-bit key string offset is local if it is non-negative.
142  *   Otherwise, reset bit 31 to get the pool key string offset.
143  *
144  * Unlike the local offset, the pool key offset is relative to
145  * the start of the key strings, not to the start of the bundle.
146  *
147  * An alias item is special (and new in ICU 2.4): --------------
148  *
149  * Its memory layout is just like for a UnicodeString, but at runtime it resolves to
150  * another resource bundle's item according to the path in the string.
151  * This is used to share items across bundles that are in different lookup/fallback
152  * chains (e.g., large collation data among zh_TW and zh_HK).
153  * This saves space (for large items) and maintenance effort (less duplication of data).
154  *
155  * --------------------------------------------------------------------------
156  *
157  * Resource types:
158  *
159  * Most resources have their values stored at four-byte offsets from the start
160  * of the resource data. These values are at least 4-aligned.
161  * Some resource values are stored directly in the offset field of the Resource itself.
162  * See UResType in unicode/ures.h for enumeration constants for Resource types.
163  *
164  * Some resources have their values stored as sequences of 16-bit units,
165  * at 2-byte offsets from the start of a contiguous 16-bit-unit array between
166  * the table key strings and the other resources. (new in formatVersion 2/ICU 4.4)
167  * At offset 0 of that array is a 16-bit zero value for empty 16-bit resources.
168  * Resource16 values in Table16 and Array16 are 16-bit offsets to String-v2
169  * resources, with the offsets relative to the start of the 16-bit-units array.
170  *
171  * Type Name            Memory layout of values
172  *                      (in parentheses: scalar, non-offset values)
173  *
174  * 0  Unicode String:   int32_t length, UChar[length], (UChar)0, (padding)
175  *                  or  (empty string ("") if offset==0)
176  * 1  Binary:           int32_t length, uint8_t[length], (padding)
177  *                      - the start of the bytes is 16-aligned -
178  * 2  Table:            uint16_t count, uint16_t keyStringOffsets[count], (uint16_t padding), Resource[count]
179  * 3  Alias:            (physically same value layout as string, new in ICU 2.4)
180  * 4  Table32:          int32_t count, int32_t keyStringOffsets[count], Resource[count]
181  *                      (new in formatVersion 1.1/ICU 2.8)
182  * 5  Table16:          uint16_t count, uint16_t keyStringOffsets[count], Resource16[count]
183  *                      (stored in the 16-bit-units array; new in formatVersion 2/ICU 4.4)
184  * 6  Unicode String-v2:UChar[length], (UChar)0; length determined by the first UChar:
185  *                      - if first is not a trail surrogate, then the length is implicit
186  *                        and u_strlen() needs to be called
187  *                      - if first<0xdfef then length=first&0x3ff (and skip first)
188  *                      - if first<0xdfff then length=((first-0xdfef)<<16) | second UChar
189  *                      - if first==0xdfff then length=((second UChar)<<16) | third UChar
190  *                      (stored in the 16-bit-units array; new in formatVersion 2/ICU 4.4)
191  * 7  Integer:          (28-bit offset is integer value)
192  * 8  Array:            int32_t count, Resource[count]
193  * 9  Array16:          uint16_t count, Resource16[count]
194  *                      (stored in the 16-bit-units array; new in formatVersion 2/ICU 4.4)
195  * 14 Integer Vector:   int32_t length, int32_t[length]
196  * 15 Reserved:         This value denotes special purpose resources and is for internal use.
197  *
198  * Note that there are 3 types with data vector values:
199  * - Vectors of 8-bit bytes stored as type Binary.
200  * - Vectors of 16-bit words stored as type Unicode String or Unicode String-v2
201  *                     (no value restrictions, all values 0..ffff allowed!).
202  * - Vectors of 32-bit words stored as type Integer Vector.
203  */
204 public final class ICUResourceBundleReader implements ICUBinary.Authenticate {
205     /**
206      * File format version that this class understands.
207      * "ResB"
208      */
209     private static final byte DATA_FORMAT_ID[] = {(byte)0x52, (byte)0x65, 
210                                                      (byte)0x73, (byte)0x42};
211
212     /* indexes[] value names; indexes are generally 32-bit (Resource) indexes */
213     private static final int URES_INDEX_LENGTH           = 0;   /* contains URES_INDEX_TOP==the length of indexes[];
214                                                                  * formatVersion==1: all bits contain the length of indexes[]
215                                                                  *   but the length is much less than 0xff;
216                                                                  * formatVersion>1:
217                                                                  *   only bits  7..0 contain the length of indexes[],
218                                                                  *        bits 31..8 are reserved and set to 0 */
219     private static final int URES_INDEX_KEYS_TOP         = 1;   /* contains the top of the key strings, */
220                                                                 /* same as the bottom of resources or UTF-16 strings, rounded up */
221     //ivate static final int URES_INDEX_RESOURCES_TOP    = 2;   /* contains the top of all resources */
222     private static final int URES_INDEX_BUNDLE_TOP       = 3;   /* contains the top of the bundle, */
223                                                                 /* in case it were ever different from [2] */
224     //ivate static final int URES_INDEX_MAX_TABLE_LENGTH = 4;   /* max. length of any table */
225     private static final int URES_INDEX_ATTRIBUTES       = 5;   /* attributes bit set, see URES_ATT_* (new in formatVersion 1.2) */
226     private static final int URES_INDEX_16BIT_TOP        = 6;   /* top of the 16-bit units (UTF-16 string v2 UChars, URES_TABLE16, URES_ARRAY16),
227                                                                  * rounded up (new in formatVersion 2.0, ICU 4.4) */
228     private static final int URES_INDEX_POOL_CHECKSUM    = 7;   /* checksum of the pool bundle (new in formatVersion 2.0, ICU 4.4) */
229     //ivate static final int URES_INDEX_TOP              = 8;
230
231     /*
232      * Nofallback attribute, attribute bit 0 in indexes[URES_INDEX_ATTRIBUTES].
233      * New in formatVersion 1.2 (ICU 3.6).
234      *
235      * If set, then this resource bundle is a standalone bundle.
236      * If not set, then the bundle participates in locale fallback, eventually
237      * all the way to the root bundle.
238      * If indexes[] is missing or too short, then the attribute cannot be determined
239      * reliably. Dependency checking should ignore such bundles, and loading should
240      * use fallbacks.
241      */
242     private static final int URES_ATT_NO_FALLBACK = 1;
243
244     /*
245      * Attributes for bundles that are, or use, a pool bundle.
246      * A pool bundle provides key strings that are shared among several other bundles
247      * to reduce their total size.
248      * New in formatVersion 2 (ICU 4.4).
249      */
250     private static final int URES_ATT_IS_POOL_BUNDLE = 2;
251     private static final int URES_ATT_USES_POOL_BUNDLE = 4;
252
253     private static final boolean DEBUG = false;
254     
255     private byte[] /* formatVersion, */ dataVersion;
256
257     // See the ResourceData struct in ICU4C/source/common/uresdata.h.
258     private String s16BitUnits;
259     private byte[] poolBundleKeys;
260     private String poolBundleKeysAsString;
261     private int rootRes;
262     private int localKeyLimit;
263     private boolean noFallback; /* see URES_ATT_NO_FALLBACK */
264     private boolean isPoolBundle;
265     private boolean usesPoolBundle;
266
267     // Fields specific to the Java port.
268     private int[] indexes;
269     private byte[] keyStrings;
270     private String keyStringsAsString;  // null except if isPoolBundle
271     private byte[] resourceBytes;
272     private int resourceBottom;  // File offset where the mixed-type resources start.
273
274     private ICUResourceBundleReader(InputStream stream, String resolvedName){
275         BufferedInputStream bs = new BufferedInputStream(stream);
276         try{
277             if(DEBUG) System.out.println("The InputStream class is: " + stream.getClass().getName());
278             if(DEBUG) System.out.println("The BufferedInputStream class is: " + bs.getClass().getName());
279             if(DEBUG) System.out.println("The bytes avialable in stream before reading the header: " + bs.available());
280             
281             dataVersion = ICUBinary.readHeader(bs,DATA_FORMAT_ID,this);
282
283             if(DEBUG) System.out.println("The bytes available in stream after reading the header: " + bs.available());
284                  
285             readData(bs);
286             stream.close();
287         }catch(IOException ex){
288             throw new RuntimeException("Data file "+ resolvedName+ " is corrupt - " + ex.getMessage());   
289         }
290     }
291     static ICUResourceBundleReader getReader(String resolvedName, ClassLoader root) {
292         InputStream stream = ICUData.getStream(root,resolvedName);
293         
294         if(stream==null){
295             return null;
296         }
297         ICUResourceBundleReader reader = new ICUResourceBundleReader(stream, resolvedName);
298         return reader;
299     }
300     
301     void setPoolBundleKeys(ICUResourceBundleReader poolBundleReader) {
302         if(!poolBundleReader.isPoolBundle) {
303             throw new IllegalStateException("pool.res is not a pool bundle");
304         }
305         if(poolBundleReader.indexes[URES_INDEX_POOL_CHECKSUM] != indexes[URES_INDEX_POOL_CHECKSUM]) {
306             throw new IllegalStateException("pool.res has a different checksum than this bundle");
307         }
308         poolBundleKeys = poolBundleReader.keyStrings;
309         poolBundleKeysAsString = poolBundleReader.keyStringsAsString;
310     }
311
312     // See res_init() in ICU4C/source/common/uresdata.c.
313     private void readData(InputStream stream) throws IOException {
314         DataInputStream ds = new DataInputStream(stream);
315
316         if(DEBUG) System.out.println("The DataInputStream class is: " + ds.getClass().getName());
317         if(DEBUG) System.out.println("The available bytes in the stream before reading the data: "+ds.available());
318
319         rootRes = ds.readInt();
320
321         // read the variable-length indexes[] array
322         int indexes0 = ds.readInt();
323         int indexLength = indexes0 & 0xff;
324         indexes = new int[indexLength];
325         indexes[URES_INDEX_LENGTH] = indexes0;
326         for(int i=1; i<indexLength; i++){
327             indexes[i] = ds.readInt();   
328         }
329         resourceBottom = (1 + indexLength) << 2;
330
331         if(indexLength > URES_INDEX_ATTRIBUTES) {
332             // determine if this resource bundle falls back to a parent bundle
333             // along normal locale ID fallback
334             int att = indexes[URES_INDEX_ATTRIBUTES];
335             noFallback = (att & URES_ATT_NO_FALLBACK) != 0;
336             isPoolBundle = (att & URES_ATT_IS_POOL_BUNDLE) != 0;
337             usesPoolBundle = (att & URES_ATT_USES_POOL_BUNDLE) != 0;
338         }
339
340         int length = indexes[URES_INDEX_BUNDLE_TOP]*4;
341         if(DEBUG) System.out.println("The number of bytes in the bundle: "+length);
342
343         // Read the local key strings.
344         // The keyStrings include NUL characters corresponding to the bytes
345         // up to the end of the indexes.
346         if(indexes[URES_INDEX_KEYS_TOP] > (1 + indexLength)) {
347             int keysBottom = (1 + indexLength) << 2;
348             int keysTop = indexes[URES_INDEX_KEYS_TOP] << 2;
349             resourceBottom = keysTop;
350             if(isPoolBundle) {
351                 // Shift the key strings down:
352                 // Pool bundle key strings are used with a 0-based index,
353                 // unlike regular bundles' key strings for which indexes
354                 // are based on the start of the bundle data.
355                 keysTop -= keysBottom;
356                 keysBottom = 0;
357             } else {
358                 localKeyLimit = keysTop;
359             }
360             keyStrings = new byte[keysTop];
361             ds.readFully(keyStrings, keysBottom, keysTop - keysBottom);
362             if(isPoolBundle) {
363                 // Overwrite trailing padding bytes so that the conversion works.
364                 while(keysBottom < keysTop && keyStrings[keysTop - 1] == (byte)0xaa) {
365                     keyStrings[--keysTop] = 0;
366                 }
367                 keyStringsAsString = new String(keyStrings, "US-ASCII");
368             }
369         }
370
371         // Read the array of 16-bit units.
372         // We are not using
373         //   new String(keys, "UTF-16BE")
374         // because the 16-bit units may not be well-formed Unicode.
375         if( indexLength > URES_INDEX_16BIT_TOP &&
376             indexes[URES_INDEX_16BIT_TOP] > indexes[URES_INDEX_KEYS_TOP]
377         ) {
378             int num16BitUnits = (indexes[URES_INDEX_16BIT_TOP] -
379                                  indexes[URES_INDEX_KEYS_TOP]) * 2;
380             char[] c16BitUnits = new char[num16BitUnits];
381             // Note: Calling readFully() to read data into byte[] and copy
382             // the data to char[] is faster than calling readChar() one by one
383             // for large data
384             byte[] c16BitUnitsBytes = new byte[num16BitUnits * 2];
385             ds.readFully(c16BitUnitsBytes);
386             for (int i = 0; i < num16BitUnits; i++) {
387                 c16BitUnits[i] = (char)((c16BitUnitsBytes[i*2] << 8) | (c16BitUnitsBytes[i*2 + 1] & 0xFF));
388             }
389             s16BitUnits = new String(c16BitUnits);
390             resourceBottom = indexes[URES_INDEX_16BIT_TOP] << 2;
391         } else {
392             s16BitUnits = "\0";
393         }
394
395         // Read the block of bytes for the mixed-type resources.
396         resourceBytes = new byte[length - resourceBottom];
397         ds.readFully(resourceBytes);
398     }
399
400     VersionInfo getVersion(){
401         return VersionInfo.getInstance(dataVersion[0],dataVersion[1],dataVersion[2],dataVersion[3]);   
402     }
403     public boolean isDataVersionAcceptable(byte version[]){
404         // while ICU4C can read formatVersion 1.0 and up,
405         // ICU4J requires 1.1 as a minimum
406         // formatVersion = version;
407         return ((version[0] == 1 && version[1] >= 1) || version[0] == 2);
408     }
409     
410     int getRootResource() {
411         return rootRes;
412     }
413     boolean getNoFallback() {
414         return noFallback;
415     }
416     boolean getUsesPoolBundle() {
417         return usesPoolBundle;
418     }
419
420     static int RES_GET_TYPE(int res) {
421         return res >>> 28;
422     }
423     private static int RES_GET_OFFSET(int res) {
424         return res & 0x0fffffff;
425     }
426     private int getResourceByteOffset(int offset) {
427         return (offset << 2) - resourceBottom;
428     }
429     /* get signed and unsigned integer values directly from the Resource handle */
430     static int RES_GET_INT(int res) {
431         return (res << 4) >> 4;
432     }
433     static int RES_GET_UINT(int res) {
434         return res & 0x0fffffff;
435     }
436     static boolean URES_IS_TABLE(int type) {
437         return type==UResourceBundle.TABLE || type==ICUResourceBundle.TABLE16 || type==ICUResourceBundle.TABLE32;
438     }
439
440     private static byte[] emptyBytes = new byte[0];
441     private static ByteBuffer emptyByteBuffer = ByteBuffer.allocate(0).asReadOnlyBuffer();
442     private static char[] emptyChars = new char[0];
443     private static int[] emptyInts = new int[0];
444     private static String emptyString = "";
445
446     private char getChar(int offset) {
447         return (char)((resourceBytes[offset] << 8) | (resourceBytes[offset + 1] & 0xff));
448     }
449     private char[] getChars(int offset, int count) {
450         char[] chars = new char[count];
451         for(int i = 0; i < count; offset += 2, ++i) {
452             chars[i] = (char)(((int)resourceBytes[offset] << 8) | (resourceBytes[offset + 1] & 0xff));
453         }
454         return chars;
455     }
456     private int getInt(int offset) {
457         return (resourceBytes[offset] << 24) |
458                 ((resourceBytes[offset+1] & 0xff) << 16) |
459                 ((resourceBytes[offset+2] & 0xff) << 8) |
460                 ((resourceBytes[offset+3] & 0xff));
461     }
462     private int[] getInts(int offset, int count) {
463         int[] ints = new int[count];
464         for(int i = 0; i < count; offset += 4, ++i) {
465             ints[i] = (resourceBytes[offset] << 24) |
466                         ((resourceBytes[offset+1] & 0xff) << 16) |
467                         ((resourceBytes[offset+2] & 0xff) << 8) |
468                         ((resourceBytes[offset+3] & 0xff));
469         }
470         return ints;
471     }
472     private char[] getTable16KeyOffsets(int offset) {
473         int length = s16BitUnits.charAt(offset++);
474         if(length > 0) {
475             return s16BitUnits.substring(offset, offset + length).toCharArray();
476         } else {
477             return emptyChars;
478         }
479     }
480     private char[] getTableKeyOffsets(int offset) {
481         int length = getChar(offset);
482         if(length > 0) {
483             return getChars(offset + 2, length);
484         } else {
485             return emptyChars;
486         }
487     }
488     private int[] getTable32KeyOffsets(int offset) {
489         int length = getInt(offset);
490         if(length > 0) {
491             return getInts(offset + 4, length);
492         } else {
493             return emptyInts;
494         }
495     }
496
497     /** Refers to ASCII key string bytes, for key string matching. */
498     private static final class ByteSequence {
499         private byte[] bytes;
500         private int offset;
501         public ByteSequence(byte[] bytes, int offset) {
502             this.bytes = bytes;
503             this.offset = offset;
504         }
505         public byte charAt(int index) {
506             return bytes[offset + index];
507         }
508     }
509     private String makeKeyStringFromBytes(int keyOffset) {
510         StringBuilder sb = new StringBuilder();
511         byte b;
512         while((b = keyStrings[keyOffset++]) != 0) {
513             sb.append((char)b);
514         }
515         return sb.toString();
516     }
517     private String makeKeyStringFromString(int keyOffset) {
518         int endOffset = keyOffset;
519         while(poolBundleKeysAsString.charAt(endOffset) != 0) {
520             ++endOffset;
521         }
522         return poolBundleKeysAsString.substring(keyOffset, endOffset);
523     }
524     private ByteSequence RES_GET_KEY16(char keyOffset) {
525         if(keyOffset < localKeyLimit) {
526             return new ByteSequence(keyStrings, keyOffset);
527         } else {
528             return new ByteSequence(poolBundleKeys, keyOffset - localKeyLimit);
529         }
530     }
531     private String getKey16String(int keyOffset) {
532         if(keyOffset < localKeyLimit) {
533             return makeKeyStringFromBytes(keyOffset);
534         } else {
535             return makeKeyStringFromString(keyOffset - localKeyLimit);
536         }
537     }
538     private ByteSequence RES_GET_KEY32(int keyOffset) {
539         if(keyOffset >= 0) {
540             return new ByteSequence(keyStrings, keyOffset);
541         } else {
542             return new ByteSequence(poolBundleKeys, keyOffset & 0x7fffffff);
543         }
544     }
545     private String getKey32String(int keyOffset) {
546         if(keyOffset >= 0) {
547             return makeKeyStringFromBytes(keyOffset);
548         } else {
549             return makeKeyStringFromString(keyOffset & 0x7fffffff);
550         }
551     }
552     // Compare the length-specified input key with the
553     // NUL-terminated tableKey.
554     private static int compareKeys(CharSequence key, ByteSequence tableKey) {
555         int i;
556         for(i = 0; i < key.length(); ++i) {
557             int c2 = tableKey.charAt(i);
558             if(c2 == 0) {
559                 return 1;  // key > tableKey because key is longer.
560             }
561             int diff = (int)key.charAt(i) - c2;
562             if(diff != 0) {
563                 return diff;
564             }
565         }
566         return -(int)tableKey.charAt(i);
567     }
568     private int compareKeys(CharSequence key, char keyOffset) {
569         return compareKeys(key, RES_GET_KEY16(keyOffset));
570     }
571     private int compareKeys32(CharSequence key, int keyOffset) {
572         return compareKeys(key, RES_GET_KEY32(keyOffset));
573     }
574
575     String getString(int res) {
576         int offset=RES_GET_OFFSET(res);
577         int length;
578         if(RES_GET_TYPE(res)==ICUResourceBundle.STRING_V2) {
579             int first = s16BitUnits.charAt(offset);
580             if((first&0xfffffc00)!=0xdc00) {  // C: if(!U16_IS_TRAIL(first)) {
581                 if(first==0) {
582                     return emptyString;
583                 }
584                 int endOffset;
585                 for(endOffset=offset+1; s16BitUnits.charAt(endOffset)!=0; ++endOffset) {}
586                 return s16BitUnits.substring(offset, endOffset);
587             } else if(first<0xdfef) {
588                 length=first&0x3ff;
589                 ++offset;
590             } else if(first<0xdfff) {
591                 length=((first-0xdfef)<<16)|s16BitUnits.charAt(offset+1);
592                 offset+=2;
593             } else {
594                 length=((int)s16BitUnits.charAt(offset+1)<<16)|s16BitUnits.charAt(offset+2);
595                 offset+=3;
596             }
597             return s16BitUnits.substring(offset, offset+length);
598         } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ {
599             if(res==0) {
600                 return emptyString;
601             } else {
602                 offset=getResourceByteOffset(offset);
603                 length=getInt(offset);
604                 return new String(getChars(offset+4, length));
605             }
606         } else {
607             return null;
608         }
609     }
610
611     String getAlias(int res) {
612         int offset=RES_GET_OFFSET(res);
613         int length;
614         if(RES_GET_TYPE(res)==ICUResourceBundle.ALIAS) {
615             if(offset==0) {
616                 return emptyString;
617             } else {
618                 offset=getResourceByteOffset(offset);
619                 length=getInt(offset);
620                 return new String(getChars(offset+4, length));
621             }
622         } else {
623             return null;
624         }
625     }
626
627     byte[] getBinary(int res, byte[] ba) {
628         int offset=RES_GET_OFFSET(res);
629         int length;
630         if(RES_GET_TYPE(res)==UResourceBundle.BINARY) {
631             if(offset==0) {
632                 return emptyBytes;
633             } else {
634                 offset=getResourceByteOffset(offset);
635                 length=getInt(offset);
636                 if(ba==null || ba.length!=length) {
637                     ba=new byte[length];
638                 }
639                 System.arraycopy(resourceBytes, offset+4, ba, 0, length);
640                 return ba;
641             }
642         } else {
643             return null;
644         }
645     }
646
647     ByteBuffer getBinary(int res) {
648         int offset=RES_GET_OFFSET(res);
649         int length;
650         if(RES_GET_TYPE(res)==UResourceBundle.BINARY) {
651             if(offset==0) {
652                 // Don't just
653                 //   return emptyByteBuffer;
654                 // in case it matters whether the buffer's mark is defined or undefined.
655                 return emptyByteBuffer.duplicate();
656             } else {
657                 offset=getResourceByteOffset(offset);
658                 length=getInt(offset);
659                 return ByteBuffer.wrap(resourceBytes, offset+4, length).slice().asReadOnlyBuffer();
660             }
661         } else {
662             return null;
663         }
664     }
665
666     int[] getIntVector(int res) {
667         int offset=RES_GET_OFFSET(res);
668         int length;
669         if(RES_GET_TYPE(res)==UResourceBundle.INT_VECTOR) {
670             if(offset==0) {
671                 return emptyInts;
672             } else {
673                 offset=getResourceByteOffset(offset);
674                 length=getInt(offset);
675                 return getInts(offset+4, length);
676             }
677         } else {
678             return null;
679         }
680     }
681
682     Container getArray(int res) {
683         int type=RES_GET_TYPE(res);
684         int offset=RES_GET_OFFSET(res);
685         switch(type) {
686         case UResourceBundle.ARRAY:
687         case ICUResourceBundle.ARRAY16:
688             if(offset==0) {
689                 return new Container(this);
690             }
691             break;
692         default:
693             return null;
694         }
695         switch(type) {
696         case UResourceBundle.ARRAY:
697             return new Array(this, offset);
698         case ICUResourceBundle.ARRAY16:
699             return new Array16(this, offset);
700         default:
701             return null;
702         }
703     }
704
705     Table getTable(int res) {
706         int type=RES_GET_TYPE(res);
707         int offset=RES_GET_OFFSET(res);
708         switch(type) {
709         case UResourceBundle.TABLE:
710         case ICUResourceBundle.TABLE16:
711         case ICUResourceBundle.TABLE32:
712             if(offset==0) {
713                 return new Table(this);
714             }
715             break;
716         default:
717             return null;
718         }
719         switch(type) {
720         case UResourceBundle.TABLE:
721             return new Table1632(this, offset);
722         case ICUResourceBundle.TABLE16:
723             return new Table16(this, offset);
724         case ICUResourceBundle.TABLE32:
725             return new Table32(this, offset);
726         default:
727             return null;
728         }
729     }
730
731     // Container value classes --------------------------------------------- ***
732
733     static class Container {
734         protected ICUResourceBundleReader reader;
735         protected int size;
736         protected int itemsOffset;
737
738         int getSize() {
739             return size;
740         }
741         int getContainerResource(int index) {
742             return ICUResourceBundle.RES_BOGUS;
743         }
744         protected int getContainer16Resource(int index) {
745             if (index < 0 || size <= index) {
746                 return ICUResourceBundle.RES_BOGUS;
747             }
748             return (ICUResourceBundle.STRING_V2 << 28) |
749                    reader.s16BitUnits.charAt(itemsOffset + index);
750         }
751         protected int getContainer32Resource(int index) {
752             if (index < 0 || size <= index) {
753                 return ICUResourceBundle.RES_BOGUS;
754             }
755             return reader.getInt(itemsOffset + 4 * index);
756         }
757         Container(ICUResourceBundleReader reader) {
758             this.reader = reader;
759         }
760     }
761     private static final class Array extends Container {
762         int getContainerResource(int index) {
763             return getContainer32Resource(index);
764         }
765         Array(ICUResourceBundleReader reader, int offset) {
766             super(reader);
767             offset = reader.getResourceByteOffset(offset);
768             size = reader.getInt(offset);
769             itemsOffset = offset + 4;
770         }
771     }
772     private static final class Array16 extends Container {
773         int getContainerResource(int index) {
774             return getContainer16Resource(index);
775         }
776         Array16(ICUResourceBundleReader reader, int offset) {
777             super(reader);
778             size = reader.s16BitUnits.charAt(offset);
779             itemsOffset = offset + 1;
780         }
781     }
782     static class Table extends Container {
783         protected char[] keyOffsets;
784         protected int[] key32Offsets;
785
786         String getKey(int index) {
787             if (index < 0 || size <= index) {
788                 return null;
789             }
790             return keyOffsets != null ?
791                         reader.getKey16String(keyOffsets[index]) :
792                         reader.getKey32String(key32Offsets[index]);
793         }
794         private static final int URESDATA_ITEM_NOT_FOUND = -1;
795         int findTableItem(CharSequence key) {
796             int mid, start, limit;
797             int result;
798
799             /* do a binary search for the key */
800             start=0;
801             limit=size;
802             while(start<limit) {
803                 mid = (start + limit) / 2;
804                 if (keyOffsets != null) {
805                     result = reader.compareKeys(key, keyOffsets[mid]);
806                 } else {
807                     result = reader.compareKeys32(key, key32Offsets[mid]);
808                 }
809                 if (result < 0) {
810                     limit = mid;
811                 } else if (result > 0) {
812                     start = mid + 1;
813                 } else {
814                     /* We found it! */
815                     return mid;
816                 }
817             }
818             return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
819         }
820         int getTableResource(String resKey) {
821             return getContainerResource(findTableItem(resKey));
822         }
823         Table(ICUResourceBundleReader reader) {
824             super(reader);
825         }
826     }
827     private static final class Table1632 extends Table {
828         int getContainerResource(int index) {
829             return getContainer32Resource(index);
830         }
831         Table1632(ICUResourceBundleReader reader, int offset) {
832             super(reader);
833             offset = reader.getResourceByteOffset(offset);
834             keyOffsets = reader.getTableKeyOffsets(offset);
835             size = keyOffsets.length;
836             itemsOffset = offset + 2 * ((size + 2) & ~1);  // Skip padding for 4-alignment.
837         }
838     }
839     private static final class Table16 extends Table {
840         int getContainerResource(int index) {
841             return getContainer16Resource(index);
842         }
843         Table16(ICUResourceBundleReader reader, int offset) {
844             super(reader);
845             keyOffsets = reader.getTable16KeyOffsets(offset);
846             size = keyOffsets.length;
847             itemsOffset = offset + 1 + size;
848         }
849     }
850     private static final class Table32 extends Table {
851         int getContainerResource(int index) {
852             return getContainer32Resource(index);
853         }
854         Table32(ICUResourceBundleReader reader, int offset) {
855             super(reader);
856             offset = reader.getResourceByteOffset(offset);
857             key32Offsets = reader.getTable32KeyOffsets(offset);
858             size = key32Offsets.length;
859             itemsOffset = offset + 4 * (1 + size);
860         }
861     }
862 }