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