/* ******************************************************************************* * Copyright (C) 2004-2010, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ package com.ibm.icu.impl; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import com.ibm.icu.util.UResourceBundle; import com.ibm.icu.util.VersionInfo; /** * This class reads the *.res resource bundle format * * (For the latest version of the file format documentation see * ICU4C's source/common/uresdata.h file.) * * File format for .res resource bundle files (formatVersion=2, ICU 4.4) * * New in formatVersion 2 compared with 1.3: ------------- * * Three new resource types -- String-v2, Table16 and Array16 -- have their * values stored in a new array of 16-bit units between the table key strings * and the start of the other resources. * * genrb eliminates duplicates among Unicode string-v2 values. * Multiple Unicode strings may use the same offset and string data, * or a short string may point to the suffix of a longer string. ("Suffix sharing") * For example, one string "abc" may be reused for another string "bc" by pointing * to the second character. (Short strings-v2 are NUL-terminated * and not preceded by an explicit length value.) * * It is allowed for all resource types to share values. * The swapper code (ures_swap()) has been modified so that it swaps each item * exactly once. * * A resource bundle may use a special pool bundle. Some or all of the table key strings * of the using-bundle are omitted, and the key string offsets for such key strings refer * to offsets in the pool bundle. * The using-bundle's and the pool-bundle's indexes[URES_INDEX_POOL_CHECKSUM] values * must match. * Two bits in indexes[URES_INDEX_ATTRIBUTES] indicate whether a resource bundle * is or uses a pool bundle. * * Table key strings must be compared in ASCII order, even if they are not * stored in ASCII. * * New in formatVersion 1.3 compared with 1.2: ------------- * * genrb eliminates duplicates among key strings. * Multiple table items may share one key string, or one item may point * to the suffix of another's key string. ("Suffix sharing") * For example, one key "abc" may be reused for another key "bc" by pointing * to the second character. (Key strings are NUL-terminated.) * * ------------- * * An ICU4C resource bundle file (.res) is a binary, memory-mappable file * with nested, hierarchical data structures. * It physically contains the following: * * Resource root; -- 32-bit Resource item, root item for this bundle's tree; * currently, the root item must be a table or table32 resource item * int32_t indexes[indexes[0]]; -- array of indexes for friendly * reading and swapping; see URES_INDEX_* above * new in formatVersion 1.1 (ICU 2.8) * char keys[]; -- characters for key strings * (formatVersion 1.0: up to 65k of characters; 1.1: <2G) * (minus the space for root and indexes[]), * which consist of invariant characters (ASCII/EBCDIC) and are NUL-terminated; * padded to multiple of 4 bytes for 4-alignment of the following data * uint16_t 16BitUnits[]; -- resources that are stored entirely as sequences of 16-bit units * (new in formatVersion 2/ICU 4.4) * data is indexed by the offset values in 16-bit resource types, * with offset 0 pointing to the beginning of this array; * there is a 0 at offset 0, for empty resources; * padded to multiple of 4 bytes for 4-alignment of the following data * data; -- data directly and indirectly indexed by the root item; * the structure is determined by walking the tree * * Each resource bundle item has a 32-bit Resource handle (see typedef above) * which contains the item type number in its upper 4 bits (31..28) and either * an offset or a direct value in its lower 28 bits (27..0). * The order of items is undefined and only determined by walking the tree. * Leaves of the tree may be stored first or last or anywhere in between, * and it is in theory possible to have unreferenced holes in the file. * * 16-bit-unit values: * Starting with formatVersion 2/ICU 4.4, some resources are stored in a special * array of 16-bit units. Each resource value is a sequence of 16-bit units, * with no per-resource padding to a 4-byte boundary. * 16-bit container types (Table16 and Array16) contain Resource16 values * which are offsets to String-v2 resources in the same 16-bit-units array. * * Direct values: * - Empty Unicode strings have an offset value of 0 in the Resource handle itself. * - Starting with formatVersion 2/ICU 4.4, an offset value of 0 for * _any_ resource type indicates an empty value. * - Integer values are 28-bit values stored in the Resource handle itself; * the interpretation of unsigned vs. signed integers is up to the application. * * All other types and values use 28-bit offsets to point to the item's data. * The offset is an index to the first 32-bit word of the value, relative to the * start of the resource data (i.e., the root item handle is at offset 0). * To get byte offsets, the offset is multiplied by 4 (or shifted left by 2 bits). * All resource item values are 4-aligned. * * New in formatVersion 2/ICU 4.4: Some types use offsets into the 16-bit-units array, * indexing 16-bit units in that array. * * The structures (memory layouts) for the values for each item type are listed * in the table below. * * Nested, hierarchical structures: ------------- * * Table items contain key-value pairs where the keys are offsets to char * key strings. * The values of these pairs are either Resource handles or * offsets into the 16-bit-units array, depending on the table type. * * Array items are simple vectors of Resource handles, * or of offsets into the 16-bit-units array, depending on the array type. * * Table key string offsets: ------- * * Key string offsets are relative to the start of the resource data (of the root handle), * i.e., the first string has an offset of 4+sizeof(indexes). * (After the 4-byte root handle and after the indexes array.) * * If the resource bundle uses a pool bundle, then some key strings are stored * in the pool bundle rather than in the local bundle itself. * - In a Table or Table16, the 16-bit key string offset is local if it is * less than indexes[URES_INDEX_KEYS_TOP]<<2. * Otherwise, subtract indexes[URES_INDEX_KEYS_TOP]<<2 to get the offset into * the pool bundle key strings. * - In a Table32, the 32-bit key string offset is local if it is non-negative. * Otherwise, reset bit 31 to get the pool key string offset. * * Unlike the local offset, the pool key offset is relative to * the start of the key strings, not to the start of the bundle. * * An alias item is special (and new in ICU 2.4): -------------- * * Its memory layout is just like for a UnicodeString, but at runtime it resolves to * another resource bundle's item according to the path in the string. * This is used to share items across bundles that are in different lookup/fallback * chains (e.g., large collation data among zh_TW and zh_HK). * This saves space (for large items) and maintenance effort (less duplication of data). * * -------------------------------------------------------------------------- * * Resource types: * * Most resources have their values stored at four-byte offsets from the start * of the resource data. These values are at least 4-aligned. * Some resource values are stored directly in the offset field of the Resource itself. * See UResType in unicode/ures.h for enumeration constants for Resource types. * * Some resources have their values stored as sequences of 16-bit units, * at 2-byte offsets from the start of a contiguous 16-bit-unit array between * the table key strings and the other resources. (new in formatVersion 2/ICU 4.4) * At offset 0 of that array is a 16-bit zero value for empty 16-bit resources. * Resource16 values in Table16 and Array16 are 16-bit offsets to String-v2 * resources, with the offsets relative to the start of the 16-bit-units array. * * Type Name Memory layout of values * (in parentheses: scalar, non-offset values) * * 0 Unicode String: int32_t length, UChar[length], (UChar)0, (padding) * or (empty string ("") if offset==0) * 1 Binary: int32_t length, uint8_t[length], (padding) * - the start of the bytes is 16-aligned - * 2 Table: uint16_t count, uint16_t keyStringOffsets[count], (uint16_t padding), Resource[count] * 3 Alias: (physically same value layout as string, new in ICU 2.4) * 4 Table32: int32_t count, int32_t keyStringOffsets[count], Resource[count] * (new in formatVersion 1.1/ICU 2.8) * 5 Table16: uint16_t count, uint16_t keyStringOffsets[count], Resource16[count] * (stored in the 16-bit-units array; new in formatVersion 2/ICU 4.4) * 6 Unicode String-v2:UChar[length], (UChar)0; length determined by the first UChar: * - if first is not a trail surrogate, then the length is implicit * and u_strlen() needs to be called * - if first<0xdfef then length=first&0x3ff (and skip first) * - if first<0xdfff then length=((first-0xdfef)<<16) | second UChar * - if first==0xdfff then length=((second UChar)<<16) | third UChar * (stored in the 16-bit-units array; new in formatVersion 2/ICU 4.4) * 7 Integer: (28-bit offset is integer value) * 8 Array: int32_t count, Resource[count] * 9 Array16: uint16_t count, Resource16[count] * (stored in the 16-bit-units array; new in formatVersion 2/ICU 4.4) * 14 Integer Vector: int32_t length, int32_t[length] * 15 Reserved: This value denotes special purpose resources and is for internal use. * * Note that there are 3 types with data vector values: * - Vectors of 8-bit bytes stored as type Binary. * - Vectors of 16-bit words stored as type Unicode String or Unicode String-v2 * (no value restrictions, all values 0..ffff allowed!). * - Vectors of 32-bit words stored as type Integer Vector. */ public final class ICUResourceBundleReader implements ICUBinary.Authenticate { /** * File format version that this class understands. * "ResB" */ private static final byte DATA_FORMAT_ID[] = {(byte)0x52, (byte)0x65, (byte)0x73, (byte)0x42}; /* indexes[] value names; indexes are generally 32-bit (Resource) indexes */ private static final int URES_INDEX_LENGTH = 0; /* contains URES_INDEX_TOP==the length of indexes[]; * formatVersion==1: all bits contain the length of indexes[] * but the length is much less than 0xff; * formatVersion>1: * only bits 7..0 contain the length of indexes[], * bits 31..8 are reserved and set to 0 */ private static final int URES_INDEX_KEYS_TOP = 1; /* contains the top of the key strings, */ /* same as the bottom of resources or UTF-16 strings, rounded up */ //ivate static final int URES_INDEX_RESOURCES_TOP = 2; /* contains the top of all resources */ private static final int URES_INDEX_BUNDLE_TOP = 3; /* contains the top of the bundle, */ /* in case it were ever different from [2] */ //ivate static final int URES_INDEX_MAX_TABLE_LENGTH = 4; /* max. length of any table */ private static final int URES_INDEX_ATTRIBUTES = 5; /* attributes bit set, see URES_ATT_* (new in formatVersion 1.2) */ private static final int URES_INDEX_16BIT_TOP = 6; /* top of the 16-bit units (UTF-16 string v2 UChars, URES_TABLE16, URES_ARRAY16), * rounded up (new in formatVersion 2.0, ICU 4.4) */ private static final int URES_INDEX_POOL_CHECKSUM = 7; /* checksum of the pool bundle (new in formatVersion 2.0, ICU 4.4) */ //ivate static final int URES_INDEX_TOP = 8; /* * Nofallback attribute, attribute bit 0 in indexes[URES_INDEX_ATTRIBUTES]. * New in formatVersion 1.2 (ICU 3.6). * * If set, then this resource bundle is a standalone bundle. * If not set, then the bundle participates in locale fallback, eventually * all the way to the root bundle. * If indexes[] is missing or too short, then the attribute cannot be determined * reliably. Dependency checking should ignore such bundles, and loading should * use fallbacks. */ private static final int URES_ATT_NO_FALLBACK = 1; /* * Attributes for bundles that are, or use, a pool bundle. * A pool bundle provides key strings that are shared among several other bundles * to reduce their total size. * New in formatVersion 2 (ICU 4.4). */ private static final int URES_ATT_IS_POOL_BUNDLE = 2; private static final int URES_ATT_USES_POOL_BUNDLE = 4; private static final boolean DEBUG = false; private byte[] /* formatVersion, */ dataVersion; // See the ResourceData struct in ICU4C/source/common/uresdata.h. private String s16BitUnits; private byte[] poolBundleKeys; private String poolBundleKeysAsString; private int rootRes; private int localKeyLimit; private boolean noFallback; /* see URES_ATT_NO_FALLBACK */ private boolean isPoolBundle; private boolean usesPoolBundle; // Fields specific to the Java port. private int[] indexes; private byte[] keyStrings; private String keyStringsAsString; // null except if isPoolBundle private byte[] resourceBytes; private int resourceBottom; // File offset where the mixed-type resources start. private ICUResourceBundleReader(InputStream stream, String resolvedName){ BufferedInputStream bs = new BufferedInputStream(stream); try{ if(DEBUG) System.out.println("The InputStream class is: " + stream.getClass().getName()); if(DEBUG) System.out.println("The BufferedInputStream class is: " + bs.getClass().getName()); if(DEBUG) System.out.println("The bytes avialable in stream before reading the header: " + bs.available()); dataVersion = ICUBinary.readHeader(bs,DATA_FORMAT_ID,this); if(DEBUG) System.out.println("The bytes available in stream after reading the header: " + bs.available()); readData(bs); stream.close(); }catch(IOException ex){ throw new RuntimeException("Data file "+ resolvedName+ " is corrupt - " + ex.getMessage()); } } static ICUResourceBundleReader getReader(String resolvedName, ClassLoader root) { InputStream stream = ICUData.getStream(root,resolvedName); if(stream==null){ return null; } ICUResourceBundleReader reader = new ICUResourceBundleReader(stream, resolvedName); return reader; } void setPoolBundleKeys(ICUResourceBundleReader poolBundleReader) { if(!poolBundleReader.isPoolBundle) { throw new IllegalStateException("pool.res is not a pool bundle"); } if(poolBundleReader.indexes[URES_INDEX_POOL_CHECKSUM] != indexes[URES_INDEX_POOL_CHECKSUM]) { throw new IllegalStateException("pool.res has a different checksum than this bundle"); } poolBundleKeys = poolBundleReader.keyStrings; poolBundleKeysAsString = poolBundleReader.keyStringsAsString; } // See res_init() in ICU4C/source/common/uresdata.c. private void readData(InputStream stream) throws IOException { DataInputStream ds = new DataInputStream(stream); if(DEBUG) System.out.println("The DataInputStream class is: " + ds.getClass().getName()); if(DEBUG) System.out.println("The available bytes in the stream before reading the data: "+ds.available()); rootRes = ds.readInt(); // read the variable-length indexes[] array int indexes0 = ds.readInt(); int indexLength = indexes0 & 0xff; indexes = new int[indexLength]; indexes[URES_INDEX_LENGTH] = indexes0; for(int i=1; i URES_INDEX_ATTRIBUTES) { // determine if this resource bundle falls back to a parent bundle // along normal locale ID fallback int att = indexes[URES_INDEX_ATTRIBUTES]; noFallback = (att & URES_ATT_NO_FALLBACK) != 0; isPoolBundle = (att & URES_ATT_IS_POOL_BUNDLE) != 0; usesPoolBundle = (att & URES_ATT_USES_POOL_BUNDLE) != 0; } int length = indexes[URES_INDEX_BUNDLE_TOP]*4; if(DEBUG) System.out.println("The number of bytes in the bundle: "+length); // Read the local key strings. // The keyStrings include NUL characters corresponding to the bytes // up to the end of the indexes. if(indexes[URES_INDEX_KEYS_TOP] > (1 + indexLength)) { int keysBottom = (1 + indexLength) << 2; int keysTop = indexes[URES_INDEX_KEYS_TOP] << 2; resourceBottom = keysTop; if(isPoolBundle) { // Shift the key strings down: // Pool bundle key strings are used with a 0-based index, // unlike regular bundles' key strings for which indexes // are based on the start of the bundle data. keysTop -= keysBottom; keysBottom = 0; } else { localKeyLimit = keysTop; } keyStrings = new byte[keysTop]; ds.readFully(keyStrings, keysBottom, keysTop - keysBottom); if(isPoolBundle) { // Overwrite trailing padding bytes so that the conversion works. while(keysBottom < keysTop && keyStrings[keysTop - 1] == (byte)0xaa) { keyStrings[--keysTop] = 0; } keyStringsAsString = new String(keyStrings, "US-ASCII"); } } // Read the array of 16-bit units. // We are not using // new String(keys, "UTF-16BE") // because the 16-bit units may not be well-formed Unicode. if( indexLength > URES_INDEX_16BIT_TOP && indexes[URES_INDEX_16BIT_TOP] > indexes[URES_INDEX_KEYS_TOP] ) { int num16BitUnits = (indexes[URES_INDEX_16BIT_TOP] - indexes[URES_INDEX_KEYS_TOP]) * 2; char[] c16BitUnits = new char[num16BitUnits]; for(int i = 0; i < num16BitUnits; ++i) { c16BitUnits[i] = ds.readChar(); } s16BitUnits = new String(c16BitUnits); resourceBottom = indexes[URES_INDEX_16BIT_TOP] << 2; } else { s16BitUnits = "\0"; } // Read the block of bytes for the mixed-type resources. resourceBytes = new byte[length - resourceBottom]; ds.readFully(resourceBytes); } VersionInfo getVersion(){ return VersionInfo.getInstance(dataVersion[0],dataVersion[1],dataVersion[2],dataVersion[3]); } public boolean isDataVersionAcceptable(byte version[]){ // while ICU4C can read formatVersion 1.0 and up, // ICU4J requires 1.1 as a minimum // formatVersion = version; return ((version[0] == 1 && version[1] >= 1) || version[0] == 2); } int getRootResource() { return rootRes; } boolean getNoFallback() { return noFallback; } boolean getUsesPoolBundle() { return usesPoolBundle; } static int RES_GET_TYPE(int res) { return res >>> 28; } private static int RES_GET_OFFSET(int res) { return res & 0x0fffffff; } private int getResourceByteOffset(int offset) { return (offset << 2) - resourceBottom; } /* get signed and unsigned integer values directly from the Resource handle */ static int RES_GET_INT(int res) { return (res << 4) >> 4; } static int RES_GET_UINT(int res) { return res & 0x0fffffff; } static boolean URES_IS_TABLE(int type) { return type==UResourceBundle.TABLE || type==ICUResourceBundle.TABLE16 || type==ICUResourceBundle.TABLE32; } private static byte[] emptyBytes = new byte[0]; private static ByteBuffer emptyByteBuffer = ByteBuffer.allocate(0).asReadOnlyBuffer(); private static char[] emptyChars = new char[0]; private static int[] emptyInts = new int[0]; private static String emptyString = ""; private char getChar(int offset) { return (char)((resourceBytes[offset] << 8) | (resourceBytes[offset + 1] & 0xff)); } private char[] getChars(int offset, int count) { char[] chars = new char[count]; for(int i = 0; i < count; offset += 2, ++i) { chars[i] = (char)(((int)resourceBytes[offset] << 8) | (resourceBytes[offset + 1] & 0xff)); } return chars; } private int getInt(int offset) { return (resourceBytes[offset] << 24) | ((resourceBytes[offset+1] & 0xff) << 16) | ((resourceBytes[offset+2] & 0xff) << 8) | ((resourceBytes[offset+3] & 0xff)); } private int[] getInts(int offset, int count) { int[] ints = new int[count]; for(int i = 0; i < count; offset += 4, ++i) { ints[i] = (resourceBytes[offset] << 24) | ((resourceBytes[offset+1] & 0xff) << 16) | ((resourceBytes[offset+2] & 0xff) << 8) | ((resourceBytes[offset+3] & 0xff)); } return ints; } private char[] getTable16KeyOffsets(int offset) { int length = s16BitUnits.charAt(offset++); if(length > 0) { return s16BitUnits.substring(offset, offset + length).toCharArray(); } else { return emptyChars; } } private char[] getTableKeyOffsets(int offset) { int length = getChar(offset); if(length > 0) { return getChars(offset + 2, length); } else { return emptyChars; } } private int[] getTable32KeyOffsets(int offset) { int length = getInt(offset); if(length > 0) { return getInts(offset + 4, length); } else { return emptyInts; } } /** Refers to ASCII key string bytes, for key string matching. */ private static final class ByteSequence { private byte[] bytes; private int offset; public ByteSequence(byte[] bytes, int offset) { this.bytes = bytes; this.offset = offset; } public byte charAt(int index) { return bytes[offset + index]; } } private String makeKeyStringFromBytes(int keyOffset) { StringBuilder sb = new StringBuilder(); byte b; while((b = keyStrings[keyOffset++]) != 0) { sb.append((char)b); } return sb.toString(); } private String makeKeyStringFromString(int keyOffset) { int endOffset = keyOffset; while(poolBundleKeysAsString.charAt(endOffset) != 0) { ++endOffset; } return poolBundleKeysAsString.substring(keyOffset, endOffset); } private ByteSequence RES_GET_KEY16(char keyOffset) { if(keyOffset < localKeyLimit) { return new ByteSequence(keyStrings, keyOffset); } else { return new ByteSequence(poolBundleKeys, keyOffset - localKeyLimit); } } private String getKey16String(int keyOffset) { if(keyOffset < localKeyLimit) { return makeKeyStringFromBytes(keyOffset); } else { return makeKeyStringFromString(keyOffset - localKeyLimit); } } private ByteSequence RES_GET_KEY32(int keyOffset) { if(keyOffset >= 0) { return new ByteSequence(keyStrings, keyOffset); } else { return new ByteSequence(poolBundleKeys, keyOffset & 0x7fffffff); } } private String getKey32String(int keyOffset) { if(keyOffset >= 0) { return makeKeyStringFromBytes(keyOffset); } else { return makeKeyStringFromString(keyOffset & 0x7fffffff); } } // Compare the length-specified input key with the // NUL-terminated tableKey. private static int compareKeys(CharSequence key, ByteSequence tableKey) { int i; for(i = 0; i < key.length(); ++i) { int c2 = tableKey.charAt(i); if(c2 == 0) { return 1; // key > tableKey because key is longer. } int diff = (int)key.charAt(i) - c2; if(diff != 0) { return diff; } } return -(int)tableKey.charAt(i); } private int compareKeys(CharSequence key, char keyOffset) { return compareKeys(key, RES_GET_KEY16(keyOffset)); } private int compareKeys32(CharSequence key, int keyOffset) { return compareKeys(key, RES_GET_KEY32(keyOffset)); } String getString(int res) { int offset=RES_GET_OFFSET(res); int length; if(RES_GET_TYPE(res)==ICUResourceBundle.STRING_V2) { int first = s16BitUnits.charAt(offset); if((first&0xfffffc00)!=0xdc00) { // C: if(!U16_IS_TRAIL(first)) { if(first==0) { return emptyString; } int endOffset; for(endOffset=offset+1; s16BitUnits.charAt(endOffset)!=0; ++endOffset) {} return s16BitUnits.substring(offset, endOffset); } else if(first<0xdfef) { length=first&0x3ff; ++offset; } else if(first<0xdfff) { length=((first-0xdfef)<<16)|s16BitUnits.charAt(offset+1); offset+=2; } else { length=((int)s16BitUnits.charAt(offset+1)<<16)|s16BitUnits.charAt(offset+2); offset+=3; } return s16BitUnits.substring(offset, offset+length); } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ { if(res==0) { return emptyString; } else { offset=getResourceByteOffset(offset); length=getInt(offset); return new String(getChars(offset+4, length)); } } else { return null; } } String getAlias(int res) { int offset=RES_GET_OFFSET(res); int length; if(RES_GET_TYPE(res)==ICUResourceBundle.ALIAS) { if(offset==0) { return emptyString; } else { offset=getResourceByteOffset(offset); length=getInt(offset); return new String(getChars(offset+4, length)); } } else { return null; } } byte[] getBinary(int res, byte[] ba) { int offset=RES_GET_OFFSET(res); int length; if(RES_GET_TYPE(res)==UResourceBundle.BINARY) { if(offset==0) { return emptyBytes; } else { offset=getResourceByteOffset(offset); length=getInt(offset); if(ba==null || ba.length!=length) { ba=new byte[length]; } System.arraycopy(resourceBytes, offset+4, ba, 0, length); return ba; } } else { return null; } } ByteBuffer getBinary(int res) { int offset=RES_GET_OFFSET(res); int length; if(RES_GET_TYPE(res)==UResourceBundle.BINARY) { if(offset==0) { // Don't just // return emptyByteBuffer; // in case it matters whether the buffer's mark is defined or undefined. return emptyByteBuffer.duplicate(); } else { offset=getResourceByteOffset(offset); length=getInt(offset); return ByteBuffer.wrap(resourceBytes, offset+4, length).slice().asReadOnlyBuffer(); } } else { return null; } } int[] getIntVector(int res) { int offset=RES_GET_OFFSET(res); int length; if(RES_GET_TYPE(res)==UResourceBundle.INT_VECTOR) { if(offset==0) { return emptyInts; } else { offset=getResourceByteOffset(offset); length=getInt(offset); return getInts(offset+4, length); } } else { return null; } } Container getArray(int res) { int type=RES_GET_TYPE(res); int offset=RES_GET_OFFSET(res); switch(type) { case UResourceBundle.ARRAY: case ICUResourceBundle.ARRAY16: if(offset==0) { return new Container(this); } break; default: return null; } switch(type) { case UResourceBundle.ARRAY: return new Array(this, offset); case ICUResourceBundle.ARRAY16: return new Array16(this, offset); default: return null; } } Table getTable(int res) { int type=RES_GET_TYPE(res); int offset=RES_GET_OFFSET(res); switch(type) { case UResourceBundle.TABLE: case ICUResourceBundle.TABLE16: case ICUResourceBundle.TABLE32: if(offset==0) { return new Table(this); } break; default: return null; } switch(type) { case UResourceBundle.TABLE: return new Table1632(this, offset); case ICUResourceBundle.TABLE16: return new Table16(this, offset); case ICUResourceBundle.TABLE32: return new Table32(this, offset); default: return null; } } // Container value classes --------------------------------------------- *** static class Container { protected ICUResourceBundleReader reader; protected int size; protected int itemsOffset; int getSize() { return size; } int getContainerResource(int index) { return ICUResourceBundle.RES_BOGUS; } protected int getContainer16Resource(int index) { if (index < 0 || size <= index) { return ICUResourceBundle.RES_BOGUS; } return (ICUResourceBundle.STRING_V2 << 28) | reader.s16BitUnits.charAt(itemsOffset + index); } protected int getContainer32Resource(int index) { if (index < 0 || size <= index) { return ICUResourceBundle.RES_BOGUS; } return reader.getInt(itemsOffset + 4 * index); } Container(ICUResourceBundleReader reader) { this.reader = reader; } } private static final class Array extends Container { int getContainerResource(int index) { return getContainer32Resource(index); } Array(ICUResourceBundleReader reader, int offset) { super(reader); offset = reader.getResourceByteOffset(offset); size = reader.getInt(offset); itemsOffset = offset + 4; } } private static final class Array16 extends Container { int getContainerResource(int index) { return getContainer16Resource(index); } Array16(ICUResourceBundleReader reader, int offset) { super(reader); size = reader.s16BitUnits.charAt(offset); itemsOffset = offset + 1; } } static class Table extends Container { protected char[] keyOffsets; protected int[] key32Offsets; String getKey(int index) { if (index < 0 || size <= index) { return null; } return keyOffsets != null ? reader.getKey16String(keyOffsets[index]) : reader.getKey32String(key32Offsets[index]); } private static final int URESDATA_ITEM_NOT_FOUND = -1; int findTableItem(CharSequence key) { int mid, start, limit; int result; /* do a binary search for the key */ start=0; limit=size; while(start 0) { start = mid + 1; } else { /* We found it! */ return mid; } } return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */ } int getTableResource(String resKey) { return getContainerResource(findTableItem(resKey)); } Table(ICUResourceBundleReader reader) { super(reader); } } private static final class Table1632 extends Table { int getContainerResource(int index) { return getContainer32Resource(index); } Table1632(ICUResourceBundleReader reader, int offset) { super(reader); offset = reader.getResourceByteOffset(offset); keyOffsets = reader.getTableKeyOffsets(offset); size = keyOffsets.length; itemsOffset = offset + 2 * ((size + 2) & ~1); // Skip padding for 4-alignment. } } private static final class Table16 extends Table { int getContainerResource(int index) { return getContainer16Resource(index); } Table16(ICUResourceBundleReader reader, int offset) { super(reader); keyOffsets = reader.getTable16KeyOffsets(offset); size = keyOffsets.length; itemsOffset = offset + 1 + size; } } private static final class Table32 extends Table { int getContainerResource(int index) { return getContainer32Resource(index); } Table32(ICUResourceBundleReader reader, int offset) { super(reader); offset = reader.getResourceByteOffset(offset); key32Offsets = reader.getTable32KeyOffsets(offset); size = key32Offsets.length; itemsOffset = offset + 4 * (1 + size); } } }