ICU-6945 merge into trunk: changes for .res formatVersion 2 (#6945) and quick fix for race in using UResourceBundle.addToCache() (#7054), from -r 26334:26353 icu4j/branches/markus/smallres2

X-SVN-Rev: 26376
This commit is contained in:
Markus Scherer 2009-07-17 23:17:34 +00:00
parent c9087beb0d
commit 078e401f7b
15 changed files with 1222 additions and 846 deletions

View File

@ -37,7 +37,7 @@ public abstract class CharsetDecoderICU extends CharsetDecoder{
int invalidCharLength;
/* maximum number of indexed bytes */
private static final int EXT_MAX_BYTES = 0x1f;
protected static final int EXT_MAX_BYTES = 0x1f;
/* store previous UChars/chars to continue partial matches */
byte[] preToUArray = new byte[EXT_MAX_BYTES];

View File

@ -1650,7 +1650,7 @@ class CharsetMBCS extends CharsetICU {
}
/*
* this works like natchFromU() except - the first character is in pre - no trie is used - the returned
* this works like matchFromU() except - the first character is in pre - no trie is used - the returned
* matchLength is not offset by 2
*/
private int matchToU(byte sisoState, byte[] preArray, int preArrayBegin, int preLength, ByteBuffer source,
@ -1910,9 +1910,23 @@ class CharsetMBCS extends CharsetICU {
}
/* try to match */
match = matchToU((byte) -1, source.array(), source.position(), source.limit(), null, value, useFallback, true);
byte[] sourceArray;
int sourcePosition, sourceLimit;
if (source.isReadOnly()) {
// source.array() would throw an exception
sourcePosition = source.position(); // relative to source.array()
sourceArray = new byte[Math.min(source.remaining(), EXT_MAX_BYTES)];
source.get(sourceArray).position(sourcePosition);
sourcePosition = 0; // relative to sourceArray
sourceLimit = sourceArray.length;
} else {
sourceArray = source.array();
sourcePosition = source.position();
sourceLimit = source.limit();
}
match = matchToU((byte) -1, sourceArray, sourcePosition, sourceLimit, null, value, useFallback, true);
if (match == (source.limit() - source.position())) {
if (match == source.remaining()) {
/* write result for simple, single-character conversion */
if (TO_U_IS_CODE_POINT(value[0])) {
return TO_U_GET_CODE_POINT(value[0]);

View File

@ -7,10 +7,10 @@
package com.ibm.icu.text;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import com.ibm.icu.impl.ICUBinary;
import com.ibm.icu.impl.ICUData;
@ -44,14 +44,31 @@ final class CollatorReader
b.close();
return result;
}
static void initRBC(RuleBasedCollator rbc, byte[] data) throws IOException {
public static InputStream makeByteBufferInputStream(final ByteBuffer buf) {
return new InputStream() {
public int read() throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
return buf.get() & 0xff;
}
public int read(byte[] bytes, int off, int len) throws IOException {
len = Math.min(len, buf.remaining());
buf.get(bytes, off, len);
return len;
}
};
}
static void initRBC(RuleBasedCollator rbc, ByteBuffer data) throws IOException {
final int MIN_BINARY_DATA_SIZE_ = (42 + 25) << 2;
InputStream i = new ByteArrayInputStream(data);
BufferedInputStream b = new BufferedInputStream(i);
CollatorReader reader = new CollatorReader(b, false);
if (data.length > MIN_BINARY_DATA_SIZE_) {
int dataLength = data.remaining();
// TODO: Change the rest of this class to use the ByteBuffer directly, rather than
// a DataInputStream, except for passing an InputStream to ICUBinary.readHeader().
// Consider changing ICUBinary to also work with a ByteBuffer.
CollatorReader reader = new CollatorReader(makeByteBufferInputStream(data), false);
if (dataLength > MIN_BINARY_DATA_SIZE_) {
reader.readImp(rbc, null);
} else {
reader.readHeader(rbc);

View File

@ -1834,8 +1834,7 @@ public final class RuleBasedCollator extends Collator
// %%CollationBin
if(buf!=null){
// m_rules_ = (String)rules[1][1];
byte map[] = buf.array();
CollatorReader.initRBC(this, map);
CollatorReader.initRBC(this, buf);
/*
BufferedInputStream input =
new BufferedInputStream(

View File

@ -79,8 +79,6 @@ public class ICUResourceBundle extends UResourceBundle {
*/
protected String resPath;
protected static final long UNSIGNED_INT_MASK = 0xffffffffL;
/**
* The class loader constant to be used with getBundleInstance API
*/
@ -127,7 +125,6 @@ public class ICUResourceBundle extends UResourceBundle {
}
}
/**
* Returns the respath of this bundle
* @return
@ -703,6 +700,9 @@ public class ICUResourceBundle extends UResourceBundle {
return sub;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof ICUResourceBundle) {
ICUResourceBundle o = (ICUResourceBundle) other;
if (getBaseName().equals(o.getBaseName())
@ -729,7 +729,7 @@ public class ICUResourceBundle extends UResourceBundle {
if(localeName.indexOf('@')>0){
localeName = ULocale.getBaseName(localeID);
}
String fullName = ICUResourceBundleReader.getFullName(baseName, localeName);
String fullName = getFullName(baseName, localeName);
ICUResourceBundle b = (ICUResourceBundle)loadFromCache(root, fullName, defaultLocale);
// here we assume that java type resource bundle organization
@ -740,7 +740,7 @@ public class ICUResourceBundle extends UResourceBundle {
// com.mycompany.data.MyLocaleElements.res
//
final String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";
final String defaultID = ULocale.getDefault().toString();
final String defaultID = defaultLocale.toString();
if(localeName.equals("")){
localeName = rootLocale;
@ -751,9 +751,8 @@ public class ICUResourceBundle extends UResourceBundle {
if(DEBUG)System.out.println("The bundle created is: "+b+" and disableFallback="+disableFallback+" and bundle.getNoFallback="+(b!=null && b.getNoFallback()));
if(disableFallback || (b!=null && b.getNoFallback())){
addToCache(root, fullName, defaultLocale, b);
// no fallback because the caller said so or because the bundle says so
return b;
return addToCache(root, fullName, defaultLocale, b);
}
// fallback to locale ID parent
@ -783,7 +782,7 @@ public class ICUResourceBundle extends UResourceBundle {
localeName = b.getLocaleID();
int i = localeName.lastIndexOf('_');
addToCache(root, fullName, defaultLocale, b);
b = (ICUResourceBundle)addToCache(root, fullName, defaultLocale, b);
if (i != -1) {
parent = instantiateBundle(baseName, localeName.substring(0, i), root, disableFallback);
@ -807,8 +806,7 @@ public class ICUResourceBundle extends UResourceBundle {
obj = (ICUResourceBundle)obj.get(aKey, table, requested);
}
if (obj == null) {
String fullName = ICUResourceBundleReader.getFullName(
getBaseName(), getLocaleID());
String fullName = getFullName(getBaseName(), getLocaleID());
throw new MissingResourceException(
"Can't find resource for bundle " + fullName + ", key "
+ aKey, this.getClass().getName(), aKey);
@ -817,17 +815,85 @@ public class ICUResourceBundle extends UResourceBundle {
((ICUResourceBundle)obj).setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());
return obj;
}
//protected byte[] version;
protected byte[] rawData;
protected long rootResource;
protected boolean noFallback;
private static final String ICU_RESOURCE_SUFFIX = ".res";
/**
* Gets the full name of the resource with suffix.
*/
public static String getFullName(String baseName, String localeName){
if(baseName==null || baseName.length()==0){
if(localeName.length()==0){
return localeName=ULocale.getDefault().toString();
}
return localeName+ICU_RESOURCE_SUFFIX;
}else{
if(baseName.indexOf('.')==-1){
if(baseName.charAt(baseName.length()-1)!= '/'){
return baseName+"/"+localeName+ICU_RESOURCE_SUFFIX;
}else{
return baseName+localeName+ICU_RESOURCE_SUFFIX;
}
}else{
baseName = baseName.replace('.','/');
if(localeName.length()==0){
return baseName+ICU_RESOURCE_SUFFIX;
}else{
return baseName+"_"+localeName+ICU_RESOURCE_SUFFIX;
}
}
}
}
protected String localeID;
protected String baseName;
protected ULocale ulocale;
protected ClassLoader loader;
//protected static final boolean ASSERT = false;
/**
* Access to the bits and bytes of the resource bundle.
* Hides low-level details.
*/
protected ICUResourceBundleReader reader;
/** Data member where the subclasses store the key. */
protected String key;
/** Data member where the subclasses store the offset within resource data. */
protected int resource;
/**
* A resource word value that means "no resource".
* Note: 0xffffffff == -1
* This has the same value as UResourceBundle.NONE, but they are semantically
* different and should be used appropriately according to context:
* NONE means "no type".
* (The type of RES_BOGUS is RES_RESERVED=15 which was defined in ICU4C ures.h.)
*/
public static final int RES_BOGUS = 0xffffffff;
/**
* Resource type constant for aliases;
* internally stores a string which identifies the actual resource
* storing the data (can be in a different resource bundle).
* Resolved internally before delivering the actual resource through the API.
*/
public static final int ALIAS = 3;
/** Resource type constant for tables with 32-bit count, key offsets and values. */
public static final int TABLE32 = 4;
/**
* Resource type constant for tables with 16-bit count, key offsets and values.
* All values are STRING_V2 strings.
*/
public static final int TABLE16 = 5;
/** Resource type constant for 16-bit Unicode strings in formatVersion 2. */
public static final int STRING_V2 = 6;
/**
* Resource type constant for arrays with 16-bit count and values.
* All values are STRING_V2 strings.
*/
public static final int ARRAY16 = 9;
/**
*
@ -838,8 +904,8 @@ public class ICUResourceBundle extends UResourceBundle {
*/
public static ICUResourceBundle createBundle(String baseName,
String localeID, ClassLoader root) {
ICUResourceBundleReader reader = ICUResourceBundleReader.getReader( baseName, localeID, root);
String resolvedName = getFullName(baseName, localeID);
ICUResourceBundleReader reader = ICUResourceBundleReader.getReader(resolvedName, root);
// could not open the .res file so return null
if (reader == null) {
return null;
@ -867,218 +933,88 @@ public class ICUResourceBundle extends UResourceBundle {
this.parent = parent;
}
public String getKey() {
return key;
}
private static final int[] gPublicTypes = new int[] {
STRING,
BINARY,
TABLE,
ALIAS,
TABLE, /* TABLE32 */
TABLE, /* TABLE16 */
STRING, /* STRING_V2 */
INT,
ARRAY,
ARRAY, /* ARRAY16 */
NONE,
NONE,
NONE,
NONE,
INT_VECTOR,
NONE
};
public int getType() {
return gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(resource)];
}
/**
* Get the noFallback flag specified in the loaded bundle.
* @return The noFallback flag.
*/
protected boolean getNoFallback() {
return noFallback;
private boolean getNoFallback() {
return reader.getNoFallback();
}
private static ICUResourceBundle getBundle(ICUResourceBundleReader reader, String baseName, String localeID, ClassLoader loader) {
long rootResource = (UNSIGNED_INT_MASK) & reader.getRootResource();
int type = RES_GET_TYPE(rootResource);
if (type == TABLE) {
ICUResourceBundleImpl.ResourceTable table = new ICUResourceBundleImpl.ResourceTable(reader, baseName, localeID, loader);
if(table.getSize()>=1){ // ticket#5683 ICU4J 3.6 data for zh_xx contains an entry other than %%ALIAS
UResourceBundle b = table.handleGetImpl(0, null, table, null); // handleGet will cache the bundle with no parent set
String itemKey = b.getKey();
// %%ALIAS is such a hack!
if (itemKey.equals("%%ALIAS")) {
String locale = b.getString();
UResourceBundle actual = UResourceBundle.getBundleInstance(baseName, locale);
return (ICUResourceBundleImpl.ResourceTable) actual;
}else{
return table;
}
}else {
return table;
}
} else if (type == TABLE32) {
// genrb does not generate Table32 with %%ALIAS
return new ICUResourceBundleImpl.ResourceTable32(reader, baseName, localeID, loader);
private static ICUResourceBundle getBundle(ICUResourceBundleReader reader,
String baseName, String localeID,
ClassLoader loader) {
ICUResourceBundleImpl bundle;
int rootRes = reader.getRootResource();
if(gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(rootRes)] == TABLE) {
bundle = new ICUResourceBundleImpl.ResourceTable(reader, null, "", rootRes, null);
} else {
throw new IllegalStateException("Invalid format error");
}
}
// private constructor for inner classes
protected ICUResourceBundle(){}
public static final int RES_GET_TYPE(long res) {
return (int) ((res) >> 28L);
}
protected static final int RES_GET_OFFSET(long res) {
return (int) ((res & 0x0fffffff) << 2); // * 4
}
/* get signed and unsigned integer values directly from the Resource handle */
protected static final int RES_GET_INT(long res) {
return (((int) ((res) << 4)) >> 4);
}
static final long RES_GET_UINT(long res) {
long t = ((res) & 0x0fffffffL);
return t;
}
static final StringBuffer RES_GET_KEY(byte[] rawData,
int keyOffset) {
char ch;
StringBuffer key = new StringBuffer();
while ((ch = (char) rawData[keyOffset]) != 0) {
key.append(ch);
keyOffset++;
bundle.baseName = baseName;
bundle.localeID = localeID;
bundle.ulocale = new ULocale(localeID);
bundle.loader = loader;
if(bundle.reader.getUsesPoolBundle()) {
bundle.reader.setPoolBundleKeys(
((ICUResourceBundleImpl)getBundleInstance(baseName, "pool", loader, true)).reader);
}
return key;
}
protected static final int getIntOffset(int offset) {
return (offset << 2); // * 4
}
static final int getCharOffset(int offset) {
return (offset << 1); // * 2
}
protected final ICUResourceBundle createBundleObject(String _key,
long _resource, String _resPath, HashMap<String, String> table,
UResourceBundle requested, ICUResourceBundle bundle, boolean[] isAlias) {
if (isAlias != null) {
isAlias[0] = false;
UResourceBundle alias = bundle.handleGetImpl("%%ALIAS", null, bundle, null, null); // handleGet will cache the bundle with no parent set
if(alias != null) {
return (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName, alias.getString());
} else {
return bundle;
}
//if (resource != RES_BOGUS) {
switch (RES_GET_TYPE(_resource)) {
case STRING : {
return new ICUResourceBundleImpl.ResourceString(_key, _resPath, _resource, this);
}
case BINARY : {
return new ICUResourceBundleImpl.ResourceBinary(_key, _resPath, _resource, this);
}
case ALIAS : {
if (isAlias != null) {
isAlias[0] = true;
}
return findResource(_key, _resource, table, requested);
}
case INT : {
return new ICUResourceBundleImpl.ResourceInt(_key, _resPath, _resource, this);
}
case INT_VECTOR : {
return new ICUResourceBundleImpl.ResourceIntVector(_key, _resPath, _resource, this);
}
case ARRAY : {
return new ICUResourceBundleImpl.ResourceArray(_key, _resPath, _resource, this);
}
case TABLE32 : {
return new ICUResourceBundleImpl.ResourceTable32(_key, _resPath, _resource, this);
}
case TABLE : {
return new ICUResourceBundleImpl.ResourceTable(_key, _resPath, _resource, this);
}
default :
throw new IllegalStateException("The resource type is unknown");
}
// constructor for inner classes
protected ICUResourceBundle(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundle container) {
this.reader = reader;
this.key = key;
this.resPath = resPath;
this.resource = resource;
if(container != null) {
baseName = container.baseName;
localeID = container.localeID;
ulocale = container.ulocale;
loader = container.loader;
this.parent = container.parent;
}
//}
//return null;
}
static final void assign(ICUResourceBundle b1, ICUResourceBundle b2){
b1.rawData = b2.rawData;
b1.rootResource = b2.rootResource;
b1.noFallback = b2.noFallback;
b1.baseName = b2.baseName;
b1.localeID = b2.localeID;
b1.ulocale = b2.ulocale;
b1.loader = b2.loader;
b1.parent = b2.parent;
}
int findKey(int siz, int currentOffset, ICUResourceBundle res, String target) {
int mid = 0, start = 0, limit = siz;
int lastMid = -1;
int targetLength = target.length();
char targetChar;
char actualChar;
int offset;
//int myCharOffset = 0, keyOffset = 0;
outer: for (;;) {
mid = ((start + limit) >> 1); // compute average
if (lastMid == mid) { /* Have we moved? */
break; /* We haven't moved, and it wasn't found. */
}
lastMid = mid;
offset = res.getOffset(currentOffset, mid);
// compare a segment of rawData with targetArray
for (int i=0; i<targetLength; i++) {
targetChar = target.charAt(i);
actualChar = (char)rawData[offset];
if (actualChar == 0 || targetChar > actualChar ) {
// target > data
start = mid;
continue outer;
}
if (targetChar < actualChar) {
// target < data
limit = mid;
continue outer;
}
// target == data so far...
offset++;
}
actualChar = (char)rawData[offset];
if (actualChar != 0) {
// target < data
limit = mid;
continue outer;
}
// target == data, we're sure now
return mid;
}
return -1;
}
public int getOffset(int currentOfset, int index){
return -1;
}
private static final char makeChar(byte[] data, int offset) {
return (char)((data[offset++] << 8) | (data[offset] & 0xff));
}
static char getChar(byte[]data, int offset){
return makeChar(data, offset);
}
private static final int makeInt(byte[] data, int offset) {
// | is left-associative
return (int) ((data[offset++] << 24) | ((data[offset++] & 0xff) << 16) | ((data[offset++] & 0xff) << 8) | ((data[offset] & 0xff)));
}
protected static int getInt(byte[] data, int offset){
//if (ASSERT) Assert.assrt("offset < data.length", offset < data.length);
return makeInt(data, offset);
}
String getStringValue(long res) {
if (res == 0) {
/*
* The data structure is documented as supporting resource==0 for empty strings.
* Return a fixed pointer in such a case.
* This was dropped in uresdata.c 1.17 as part of Jitterbug 1005 work
* on code coverage for ICU 2.0.
* Re-added for consistency with the design and with other code.
*/
return "";
}
int offset = RES_GET_OFFSET(res);
int length = getInt(rawData,offset);
int stringOffset = offset + getIntOffset(1);
char[] dst = new char[length];
//if (ASSERT) Assert.assrt("(stringOffset+getCharOffset(length)) < rawData.length", (stringOffset+getCharOffset(length)) < rawData.length);
for(int i=0; i<length; i++){
dst[i]=getChar(rawData, stringOffset+getCharOffset(i));
}
return new String(dst);
private String getAliasValue(int res) {
String result = reader.getAlias(res);
return result != null ? result : "";
}
private static final char RES_PATH_SEP_CHAR = '/';
private static final String RES_PATH_SEP_STR = "/";
@ -1086,19 +1022,13 @@ public class ICUResourceBundle extends UResourceBundle {
private static final char HYPHEN = '-';
private static final String LOCALE = "LOCALE";
protected static final int getIndex(String s) {
if (s.length() >= 1) {
return Integer.valueOf(s).intValue();
}
return -1;
}
private ICUResourceBundle findResource(String _key, long _resource,
HashMap<String, String> table,
UResourceBundle requested) {
protected ICUResourceBundle findResource(String _key, int _resource,
HashMap<String, String> table,
UResourceBundle requested) {
ClassLoader loaderToUse = loader;
String locale = null, keyPath = null;
String bundleName;
String rpath = getStringValue(_resource);
String rpath = getAliasValue(_resource);
if (table == null) {
table = new HashMap<String, String>();
}
@ -1111,8 +1041,9 @@ public class ICUResourceBundle extends UResourceBundle {
int i = rpath.indexOf(RES_PATH_SEP_CHAR, 1);
int j = rpath.indexOf(RES_PATH_SEP_CHAR, i + 1);
bundleName = rpath.substring(1, i);
locale = rpath.substring(i + 1);
if (j != -1) {
if (j < 0) {
locale = rpath.substring(i + 1);
} else {
locale = rpath.substring(i + 1, j);
keyPath = rpath.substring(j + 1, rpath.length());
}
@ -1147,7 +1078,9 @@ public class ICUResourceBundle extends UResourceBundle {
keyPath = rpath.substring(LOCALE.length() + 2/* prepending and appending / */, rpath.length());
locale = ((ICUResourceBundle)requested).getLocaleID();
sub = ICUResourceBundle.findResourceWithFallback(keyPath, requested, null);
sub.resPath = "/" + sub.getLocaleID() + "/" + keyPath;
if (sub != null) {
sub.resPath = "/" + sub.getLocaleID() + "/" + keyPath;
}
}else{
if (locale == null) {
// {dlf} must use requestor's class loader to get resources from same jar
@ -1174,7 +1107,9 @@ public class ICUResourceBundle extends UResourceBundle {
// the key of this alias resource
sub = (ICUResourceBundle)bundle.get(_key);
}
sub.resPath = rpath;
if (sub != null) {
sub.resPath = rpath;
}
}
if (sub == null) {
throw new MissingResourceException(localeID, baseName, _key);
@ -1188,7 +1123,7 @@ public class ICUResourceBundle extends UResourceBundle {
private static final int MAX_INITIAL_LOOKUP_SIZE = 64;
protected void createLookupCache() {
lookup = new SimpleCache<Object, UResourceBundle>(ICUCache.WEAK, Math.max(size*2, MAX_INITIAL_LOOKUP_SIZE));
lookup = new SimpleCache<Object, UResourceBundle>(ICUCache.WEAK, Math.max(getSize()*2, MAX_INITIAL_LOOKUP_SIZE));
}
protected UResourceBundle handleGet(String resKey, HashMap<String, String> table, UResourceBundle requested) {
@ -1239,8 +1174,8 @@ public class ICUResourceBundle extends UResourceBundle {
boolean[] isAlias) {
return null;
}
// TODO Below is a set of workarounds created for org.unicode.cldr.icu.ICU2LDMLWriter
/*
* Calling getKeys() on a table that has alias's can throw a NullPointerException if parent is not set,
@ -1249,35 +1184,19 @@ public class ICUResourceBundle extends UResourceBundle {
*/
/**
* Returns the resource handle for the given index within the calling resource table.
* Returns the resource handle for the given key within the calling resource table.
*
* @internal
* @deprecated This API is ICU internal only and a workaround see ticket #6514.
* @author Brian Rower
*/
private long getResourceHandle(int index)
{
//TODO this is part of a workaround for ticket #6514
//if it's out of range, return -1
if(index > this.size)
{
return -1;
}
//get the offset of the calling tables resource
int offset = RES_GET_OFFSET(resource);
//move past the 2 byte count number
offset += getCharOffset(1);
//move past the array of 2 byte key string offsets
offset += getCharOffset(size);
//move past the padding if it exists...it's either 2 bytes or no bytes
offset += getCharOffset(~size & 1);
//and then to the proper int in the array of resources
offset += getIntOffset(index);
return (UNSIGNED_INT_MASK) & ICUResourceBundle.getInt(rawData, offset);
protected int getTableResource(String resKey) {
return RES_BOGUS;
}
protected int getTableResource(int index) {
return RES_BOGUS;
}
/**
* Determines if the object at the specified index of the calling resource table
* is an alias. If it is, returns true
@ -1293,19 +1212,9 @@ public class ICUResourceBundle extends UResourceBundle {
{
//TODO this is part of a workaround for ticket #6514
//if index is out of the resource, return false.
if(index > size)
{
return false;
}
//parent resource must be a table to call this
if(RES_GET_TYPE(this.resource) != TABLE)
{
return false;
}
long res = getResourceHandle(index);
return RES_GET_TYPE(res) == ALIAS ? true : false;
return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(index)) == ALIAS;
}
/**
*
* @internal
@ -1315,9 +1224,9 @@ public class ICUResourceBundle extends UResourceBundle {
public boolean isAlias()
{
//TODO this is part of a workaround for ticket #6514
return RES_GET_TYPE(this.resource) == ALIAS;
return ICUResourceBundleReader.RES_GET_TYPE(resource) == ALIAS;
}
/**
* Determines if the object with the specified key
* is an alias. If it is, returns true
@ -1325,7 +1234,7 @@ public class ICUResourceBundle extends UResourceBundle {
* @param key The key of the resource to check
* @returns True if the resource with 'key' is an alias, false otherwise.
*
* @internal
* @internal
* @deprecated This API is ICU internal only and part of a workaround see ticket #6514.
* @author Brian Rower
*/
@ -1333,37 +1242,9 @@ public class ICUResourceBundle extends UResourceBundle {
{
//TODO this is part of a workaround for ticket #6514
//this only applies to tables
if(RES_GET_TYPE(this.resource) != TABLE)
{
return false;
}
int i = getIndexOfKey(k);
if(i > size || i < 0)
{
return false;
}
return isAlias(i);
return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(k)) == ALIAS;
}
private int getIndexOfKey(String k)
{
//TODO this is part of a workaround for ticket #6514
if(RES_GET_TYPE(this.resource) != TABLE)
{
return -1;
}
int index;
for(index = 0; index < size; index++)
{
String curKey = getKey(index);
if(k.equals(curKey))
{
return index;
}
}
return -1;
}
/**
* This method can be used to retrieve the underlying alias path (aka where the alias points to)
* This method was written to allow conversion from ICU back to LDML format.
@ -1377,16 +1258,9 @@ public class ICUResourceBundle extends UResourceBundle {
*/
public String getAliasPath(int index)
{
//TODO cannot allow alias path to to end up in public API
if(!isAlias(index) || index > this.size)
{
return "";
}
return getStringValue(getResourceHandle(index));
return getAliasValue(getTableResource(index));
}
/**
*
* @internal
@ -1395,10 +1269,10 @@ public class ICUResourceBundle extends UResourceBundle {
*/
public String getAliasPath()
{
//TODO cannot allow alias path to to end up in public API
return getStringValue(resource);
//TODO cannot allow alias path to end up in public API
return getAliasValue(resource);
}
/**
*
* @internal
@ -1407,32 +1281,17 @@ public class ICUResourceBundle extends UResourceBundle {
*/
public String getAliasPath(String k)
{
//TODO cannot allow alias path to to end up in public API
return getAliasPath(getIndexOfKey(k));
//TODO cannot allow alias path to end up in public API
return getAliasValue(getTableResource(k));
}
/*
* Helper method for getKeysSafe
*/
private String getKey(int index)
{
//TODO this is part of a workaround for ticket #6514
if(index > this.size)
{
return "";
}
//the offset of the table
int offset = RES_GET_OFFSET(resource);
//move past the 2 byte number for the count
offset += getCharOffset(1);
//grab the key string offset from the array
offset = getOffset(offset, index);
return RES_GET_KEY(rawData, offset).toString();
protected String getKey(int index) {
return null;
}
/**
* Returns an Enumeration of the keys belonging to this table or array.
* This method differs from the getKeys() method by not following alias paths. This method exposes
@ -1447,17 +1306,31 @@ public class ICUResourceBundle extends UResourceBundle {
{
//TODO this is part of a workaround for ticket #6514
//the safeness only applies to tables, so use the other method if it's not a table
if(RES_GET_TYPE(this.resource) != TABLE)
if(!ICUResourceBundleReader.URES_IS_TABLE(resource))
{
return getKeys();
}
Vector<String> v = new Vector<String>();
int index;
for(index = 0; index < size; index++)
int size = getSize();
for(int index = 0; index < size; index++)
{
String curKey = getKey(index);
v.add(curKey);
}
return v.elements();
}
// This is the worker function for the public getKeys().
// TODO: Now that UResourceBundle uses handleKeySet(), this function is obsolete.
// It is also not inherited from ResourceBundle, and it is not implemented
// by ResourceBundleWrapper despite its documentation requiring all subclasses to
// implement it.
// Consider deprecating UResourceBundle.handleGetKeys(), and consider making it always return null.
protected Enumeration<String> handleGetKeys() {
return Collections.enumeration(handleKeySet());
}
protected boolean isTopLevelResource() {
return resPath.length() == 0;
}
}

View File

@ -8,18 +8,130 @@ package com.ibm.icu.impl;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.TreeSet;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
import com.ibm.icu.util.UResourceBundleIterator;
import com.ibm.icu.util.UResourceTypeMismatchException;
class ICUResourceBundleImpl {
class ICUResourceBundleImpl extends ICUResourceBundle {
protected ICUResourceBundleImpl(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
}
protected final ICUResourceBundle createBundleObject(String _key,
int _resource,
HashMap<String, String> table,
UResourceBundle requested,
boolean[] isAlias) {
if (isAlias != null) {
isAlias[0] = false;
}
String _resPath = resPath + "/" + _key;
switch(ICUResourceBundleReader.RES_GET_TYPE(_resource)) {
case STRING :
case STRING_V2:
return new ICUResourceBundleImpl.ResourceString(reader, _key, _resPath, _resource, this);
case BINARY:
return new ICUResourceBundleImpl.ResourceBinary(reader, _key, _resPath, _resource, this);
case ALIAS:
if (isAlias != null) {
isAlias[0] = true;
}
return findResource(_key, _resource, table, requested);
case INT:
return new ICUResourceBundleImpl.ResourceInt(reader, _key, _resPath, _resource, this);
case INT_VECTOR:
return new ICUResourceBundleImpl.ResourceIntVector(reader, _key, _resPath, _resource, this);
case ARRAY:
case ARRAY16:
return new ICUResourceBundleImpl.ResourceArray(reader, _key, _resPath, _resource, this);
case TABLE:
case TABLE16:
case TABLE32:
return new ICUResourceBundleImpl.ResourceTable(reader, _key, _resPath, _resource, this);
default :
throw new IllegalStateException("The resource type is unknown");
}
}
static final class ResourceArray extends ICUResourceBundle {
// Scalar values ------------------------------------------------------- ***
private static final class ResourceBinary extends ICUResourceBundleImpl {
public ByteBuffer getBinary() {
return reader.getBinary(resource);
}
public byte [] getBinary(byte []ba) {
return reader.getBinary(resource, ba);
}
ResourceBinary(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
}
}
private static final class ResourceInt extends ICUResourceBundleImpl {
public int getInt() {
return ICUResourceBundleReader.RES_GET_INT(resource);
}
public int getUInt() {
return ICUResourceBundleReader.RES_GET_UINT(resource);
}
ResourceInt(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
}
}
private static final class ResourceString extends ICUResourceBundleImpl {
private String value;
public String getString() {
return value;
}
ResourceString(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
value = reader.getString(resource);
}
}
private static final class ResourceIntVector extends ICUResourceBundleImpl {
private int[] value;
public int[] getIntVector() {
return value;
}
ResourceIntVector(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
value = reader.getIntVector(resource);
}
}
// Container values ---------------------------------------------------- ***
private static class ResourceContainer extends ICUResourceBundleImpl {
protected ICUResourceBundleReader.Container value;
public int getSize() {
return value.getSize();
}
protected int getContainerResource(int index) {
return value.getContainerResource(index);
}
protected UResourceBundle createBundleObject(int index, String resKey, HashMap<String, String> table,
UResourceBundle requested, boolean[] isAlias) {
int item = getContainerResource(index);
if (item == RES_BOGUS) {
throw new IndexOutOfBoundsException();
}
return createBundleObject(resKey, item, table, requested, isAlias);
}
ResourceContainer(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
}
}
private static class ResourceArray extends ResourceContainer {
protected String[] handleGetStringArray() {
String[] strings = new String[size];
String[] strings = new String[value.getSize()];
UResourceBundleIterator iter = getIterator();
int i = 0;
while (iter.hasNext()) {
@ -27,300 +139,75 @@ class ICUResourceBundleImpl {
}
return strings;
}
/**
* @internal ICU 3.0
*/
public String[] getStringArray() {
return handleGetStringArray();
}
protected UResourceBundle handleGetImpl(String indexStr, HashMap<String, String> table, UResourceBundle requested,
int[] index, boolean[] isAlias) {
index[0] = getIndex(indexStr);
if (index[0] > -1) {
return handleGetImpl(index[0], table, requested, isAlias);
protected UResourceBundle handleGetImpl(String indexStr, HashMap<String, String> table,
UResourceBundle requested,
int[] index, boolean[] isAlias) {
int i = indexStr.length() > 0 ? Integer.valueOf(indexStr).intValue() : -1;
if(index != null) {
index[0] = i;
}
throw new UResourceTypeMismatchException("Could not get the correct value for index: "+ index);
}
protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table, UResourceBundle requested,
boolean[] isAlias) {
if (index > size) {
throw new IndexOutOfBoundsException();
if (i < 0) {
throw new UResourceTypeMismatchException("Could not get the correct value for index: "+ index);
}
int offset = RES_GET_OFFSET(resource);
int itemOffset = offset + getIntOffset(index + 1);
long itemResource = (UNSIGNED_INT_MASK) & ICUResourceBundle.getInt(rawData,itemOffset);
String path = (isTopLevel == true) ? Integer.toString(index) : resPath + "/" + index;
return createBundleObject(Integer.toString(index), itemResource, path, table, requested, this, isAlias);
return createBundleObject(i, indexStr, table, requested, isAlias);
}
private int countItems() {
int offset = RES_GET_OFFSET(resource);
int value = getInt(rawData,offset);
return value;
protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table,
UResourceBundle requested, boolean[] isAlias) {
return createBundleObject(index, Integer.toString(index), table, requested, isAlias);
}
ResourceArray(String key, String resPath, long resource, ICUResourceBundle bundle) {
assign(this, bundle);
this.resource = resource;
this.key = key;
this.size = countItems();
this.resPath = resPath;
ResourceArray(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
value = reader.getArray(resource);
createLookupCache(); // Use bundle cache to access array entries
}
}
static final class ResourceBinary extends ICUResourceBundle {
private byte[] value;
public ByteBuffer getBinary() {
return ByteBuffer.wrap(value);
static class ResourceTable extends ResourceContainer {
protected String getKey(int index) {
return ((ICUResourceBundleReader.Table)value).getKey(index);
}
public byte [] getBinary(byte []ba) {
return value;
}
private byte[] getValue() {
int offset = RES_GET_OFFSET(resource);
int length = ICUResourceBundle.getInt(rawData,offset);
int byteOffset = offset + getIntOffset(1);
byte[] dst = new byte[length];
//if (ASSERT) Assert.assrt("byteOffset+length < rawData.length", byteOffset+length < rawData.length);
System.arraycopy(rawData, byteOffset, dst, 0, length);
return dst;
}
ResourceBinary(String key, String resPath, long resource, ICUResourceBundle bundle) {
assign(this, bundle);
this.resource = resource;
this.key = key;
this.resPath = resPath;
value = getValue();
}
}
static final class ResourceInt extends ICUResourceBundle {
public int getInt() {
return RES_GET_INT(resource);
}
public int getUInt() {
long ret = RES_GET_UINT(resource);
return (int) ret;
}
ResourceInt(String key, String resPath, long resource, ICUResourceBundle bundle) {
assign(this, bundle);
this.key = key;
this.resource = resource;
this.resPath = resPath;
}
}
static final class ResourceString extends ICUResourceBundle {
private String value;
public String getString() {
return value;
}
ResourceString(String key, String resPath, long resource, ICUResourceBundle bundle) {
assign(this, bundle);
value = getStringValue(resource);
this.key = key;
this.resource = resource;
this.resPath = resPath;
}
}
static final class ResourceIntVector extends ICUResourceBundle {
private int[] value;
public int[] getIntVector() {
return value;
}
private int[] getValue() {
int offset = RES_GET_OFFSET(resource);
int length = ICUResourceBundle.getInt(rawData,offset);
int intOffset = offset + getIntOffset(1);
int[] val = new int[length];
//int byteLength = getIntOffset(length);
//if (ASSERT) Assert.assrt("(intOffset+byteLength)<rawData.length", (intOffset+byteLength)<rawData.length);
for(int i=0; i<length;i++){
val[i]=ICUResourceBundle.getInt(rawData, intOffset+getIntOffset(i));
protected Set<String> handleKeySet() {
TreeSet<String> keySet = new TreeSet<String>();
ICUResourceBundleReader.Table table = (ICUResourceBundleReader.Table)value;
for (int i = 0; i < table.getSize(); ++i) {
keySet.add(table.getKey(i));
}
return val;
return keySet;
}
ResourceIntVector(String key, String resPath, long resource, ICUResourceBundle bundle) {
assign(this, bundle);
this.key = key;
this.resource = resource;
this.size = 1;
this.resPath = resPath;
value = getValue();
protected int getTableResource(String resKey) {
return ((ICUResourceBundleReader.Table)value).getTableResource(resKey);
}
}
static final class ResourceTable extends ICUResourceBundle {
protected UResourceBundle handleGetImpl(String resKey, HashMap<String, String> table, UResourceBundle requested,
int[] index, boolean[] isAlias) {
if(size<=0){
protected int getTableResource(int index) {
return getContainerResource(index);
}
protected UResourceBundle handleGetImpl(String resKey, HashMap<String, String> table,
UResourceBundle requested,
int[] index, boolean[] isAlias) {
int i = ((ICUResourceBundleReader.Table)value).findTableItem(resKey);
if(index != null) {
index[0] = i;
}
if (i < 0) {
return null;
}
int offset = RES_GET_OFFSET(resource);
// offset+0 contains number of entries
// offset+1 contains the keyOffset
int currentOffset = (offset) + getCharOffset(1);
//int keyOffset = rawData.getChar(currentOffset);
/* do a binary search for the key */
index[0] = findKey(size, currentOffset, this, resKey);
if (index[0] == -1) {
//throw new MissingResourceException(ICUResourceBundleReader.getFullName(baseName, localeID),
// localeID,
// key);
return null;
}
currentOffset += getCharOffset(size + (~size & 1))
+ getIntOffset(index[0]);
long resOffset = (UNSIGNED_INT_MASK) & ICUResourceBundle.getInt(rawData, currentOffset);
String path = (isTopLevel == true) ? resKey : resPath + "/" + resKey;
return createBundleObject(resKey, resOffset, path, table, requested, this, isAlias);
return createBundleObject(i, resKey, table, requested, isAlias);
}
public int getOffset(int currentOffset, int index) {
return getChar(rawData, currentOffset + getCharOffset(index));
}
protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table, UResourceBundle requested,
boolean[] isAlias) {
if (index > size) {
protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table,
UResourceBundle requested, boolean[] isAlias) {
String itemKey = ((ICUResourceBundleReader.Table)value).getKey(index);
if (itemKey == null) {
throw new IndexOutOfBoundsException();
}
int offset = RES_GET_OFFSET(resource);
// offset+0 contains number of entries
// offset+1 contains the keyOffset
int currentOffset = (offset) + getCharOffset(1);
int betterOffset = getOffset(currentOffset, index);
String itemKey = RES_GET_KEY(rawData, betterOffset).toString();
currentOffset += getCharOffset(size + (~size & 1))
+ getIntOffset(index);
long resOffset = (UNSIGNED_INT_MASK) & ICUResourceBundle.getInt(rawData,currentOffset);
String path = (isTopLevel == true) ? itemKey : resPath + "/" + itemKey;
return createBundleObject(itemKey, resOffset, path, table, requested, this, isAlias);
return createBundleObject(index, itemKey, table, requested, isAlias);
}
private int countItems() {
int offset = RES_GET_OFFSET(resource);
int value = getChar(rawData,offset);
return value;
}
ResourceTable(String key, String resPath, long resource, ICUResourceBundle bundle) {
this(key, resPath, resource, bundle, false);
}
ResourceTable(ICUResourceBundleReader reader, String baseName, String localeID, ClassLoader loader) {
this.rawData = reader.getData();
this.rootResource = (UNSIGNED_INT_MASK) & reader.getRootResource();
this.noFallback = reader.getNoFallback();
this.baseName = baseName;
this.localeID = localeID;
this.ulocale = new ULocale(localeID);
this.loader = loader;
initialize(null, "", rootResource, null, isTopLevel);
}
void initialize(String resKey, String resourcePath, long resOffset,
ICUResourceBundle bundle, boolean topLevel){
if(bundle!=null){
assign(this, bundle);
}
key = resKey;
resource = resOffset;
isTopLevel = topLevel;
size = countItems();
resPath = resourcePath;
createLookupCache(); // Use bundle cache to access nested resources
}
ResourceTable(String key, String resPath, long resource,
ICUResourceBundle bundle, boolean isTopLevel) {
initialize(key, resPath, resource, bundle, isTopLevel);
}
}
static final class ResourceTable32 extends ICUResourceBundle{
protected UResourceBundle handleGetImpl(String resKey, HashMap<String, String> table, UResourceBundle requested,
int[] index, boolean[] isAlias) {
int offset = RES_GET_OFFSET(resource);
// offset+0 contains number of entries
// offset+1 contains the keyOffset
int currentOffset = (offset) + getIntOffset(1);
//int keyOffset = rawData.getChar(currentOffset);
/* do a binary search for the key */
index[0] = findKey(size, currentOffset, this, resKey);
if (index[0] == -1) {
throw new MissingResourceException(
"Could not find resource ",
ICUResourceBundleReader.getFullName(baseName, localeID),
resKey);
}
currentOffset += getIntOffset(size) + getIntOffset(index[0]);
long resOffset = (UNSIGNED_INT_MASK) & ICUResourceBundle.getInt(rawData,currentOffset);
String path = (isTopLevel == true) ? resKey : resPath + "/" + resKey;
return createBundleObject(resKey, resOffset, path, table, requested, this, isAlias);
}
public int getOffset(int currentOffset, int index) {
return ICUResourceBundle.getInt(rawData, currentOffset + getIntOffset(index));
}
protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table, UResourceBundle requested,
boolean[] isAlias) {
if(size<=0){
return null;
}
if (index > size) {
throw new IndexOutOfBoundsException();
}
int offset = RES_GET_OFFSET(resource);
// offset+0 contains number of entries
// offset+1 contains the keyOffset
int currentOffset = (offset) + getIntOffset(1)
+ getIntOffset(index);
int betterOffset = getOffset(currentOffset, 0);
String itemKey = RES_GET_KEY(rawData, betterOffset).toString();
currentOffset += getIntOffset(size);
long resOffset = (UNSIGNED_INT_MASK) & ICUResourceBundle.getInt(rawData,currentOffset);
String path = (isTopLevel == true) ? Integer.toString(index) : resPath + "/" + index;
return createBundleObject(itemKey, resOffset, path, table, requested, this, isAlias);
}
private int countItems() {
int offset = RES_GET_OFFSET(resource);
int value = ICUResourceBundle.getInt(rawData, offset);
return value;
}
ResourceTable32(String key, String resPath, long resource, ICUResourceBundle bundle) {
this(key, resPath, resource, bundle, false);
}
ResourceTable32(ICUResourceBundleReader reader, String baseName, String localeID, ClassLoader loader) {
this.rawData = reader.getData();
this.rootResource = (UNSIGNED_INT_MASK) & reader.getRootResource();
this.noFallback = reader.getNoFallback();
this.baseName = baseName;
this.localeID = localeID;
this.ulocale = new ULocale(localeID);
this.loader = loader;
initialize(null, "", rootResource, null, isTopLevel);
}
void initialize(String resKey, String resourcePath, long resOffset,
ICUResourceBundle bundle, boolean topLevel){
if(bundle!=null){
assign(this, bundle);
}
key = resKey;
resource = resOffset;
isTopLevel = topLevel;
size = countItems();
resPath = resourcePath;
createLookupCache(); // Use bundle cache to access nested resources
}
ResourceTable32(String key, String resPath, long resource,
ICUResourceBundle bundle, boolean isTopLevel) {
initialize(key, resPath, resource, bundle, isTopLevel);
ResourceTable(ICUResourceBundleReader reader, String key, String resPath, int resource,
ICUResourceBundleImpl container) {
super(reader, key, resPath, resource, container);
value = reader.getTable(resource);
createLookupCache(); // Use bundle cache to access table entries
}
}
}

View File

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2004-2008, International Business Machines Corporation and *
* Copyright (C) 2004-2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@ -10,19 +10,56 @@ 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.ULocale;
import com.ibm.icu.util.UResourceBundle; // For resource type constants.
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=1.2)
* 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.
@ -38,6 +75,12 @@ import com.ibm.icu.util.VersionInfo;
* (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
*
@ -48,8 +91,17 @@ import com.ibm.icu.util.VersionInfo;
* 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.
*
@ -59,18 +111,38 @@ import com.ibm.icu.util.VersionInfo;
* 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 above.
* in the table below.
*
* Nested, hierarchical structures: -------------
*
* Table items contain key-value pairs where the keys are 16-bit offsets to char * key strings.
* Key string offsets are also relative to the start of the resource data (of the root handle),
* i.e., the first string has an offset of 4 (after the 4-byte root handle).
* 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.
*
* The values of these pairs are Resource handles.
* Array items are simple vectors of Resource handles,
* or of offsets into the 16-bit-units array, depending on the array type.
*
* Array items are simple vectors of Resource handles.
* 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): --------------
*
@ -89,34 +161,47 @@ import com.ibm.icu.util.VersionInfo;
* 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)
* - this value should be 32-aligned -
* - 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
* - 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{
public final class ICUResourceBundleReader implements ICUBinary.Authenticate {
/**
* File format version that this class understands.
* "ResB"
@ -124,20 +209,24 @@ public final class ICUResourceBundleReader implements ICUBinary.Authenticate{
private static final byte DATA_FORMAT_ID[] = {(byte)0x52, (byte)0x65,
(byte)0x73, (byte)0x42};
private static final String ICU_RESOURCE_SUFFIX = ".res";
/* indexes[] value names; indexes are generally 32-bit (Resource) indexes */
private static final int URES_INDEX_LENGTH = 0; /* [0] contains URES_INDEX_TOP==the length of indexes[] */
//private static final int URES_INDEX_STRINGS_TOP = 1; /* [1] contains the top of the strings, */
/* same as the bottom of resources, rounded up */
//private static final int URES_INDEX_RESOURCES_TOP = 2; /* [2] contains the top of all resources */
private static final int URES_INDEX_BUNDLE_TOP = 3; /* [3] contains the top of the bundle, */
/* in case it were ever different from [2] */
//private static final int URES_INDEX_MAX_TABLE_LENGTH = 4; /* [4] max. length of any table */
private static final int URES_INDEX_ATTRIBUTES = 5; /* [5] attributes bit set, see URES_ATT_* (new in formatVersion 1.2) */
//private static final int URES_INDEX_TOP = 6;
//private static final int URES_STRINGS_BOTTOM=(1+URES_INDEX_TOP)*4;
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].
@ -152,18 +241,37 @@ public final class ICUResourceBundleReader implements ICUBinary.Authenticate{
*/
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[] indexes;
private int localKeyLimit;
private boolean noFallback; /* see URES_ATT_NO_FALLBACK */
private boolean isPoolBundle;
private boolean usesPoolBundle;
private byte[] data;
// 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());
@ -180,8 +288,7 @@ public final class ICUResourceBundleReader implements ICUBinary.Authenticate{
throw new RuntimeException("Data file "+ resolvedName+ " is corrupt - " + ex.getMessage());
}
}
public static ICUResourceBundleReader getReader(String baseName, String localeName, ClassLoader root){
String resolvedName = getFullName(baseName, localeName);
static ICUResourceBundleReader getReader(String resolvedName, ClassLoader root) {
InputStream stream = ICUData.getStream(root,resolvedName);
if(stream==null){
@ -190,109 +297,561 @@ public final class ICUResourceBundleReader implements ICUBinary.Authenticate{
ICUResourceBundleReader reader = new ICUResourceBundleReader(stream, resolvedName);
return reader;
}
private static void writeInt(int i, byte[] bytes, int offset) {
bytes[offset++]=(byte)(i>>24);
bytes[offset++]=(byte)(i>>16);
bytes[offset++]=(byte)(i>>8);
bytes[offset]=(byte)i;
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;
}
private void readData(InputStream stream)
throws IOException{
// 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());
/*
* The following will read two integers before ds.mark().
* Later, the two integers need to be placed into data[],
* then ds.reset(), then ds.readFully(into rest of data[]).
*
* This is necessary because we don't know the readLimit for ds.mark()
* until we have read the second integer (indexLength).
*/
rootRes = ds.readInt();
// read the variable-length indexes[] array
int indexLength = ds.readInt();
ds.mark((indexLength-1)*4);
int indexes0 = ds.readInt();
int indexLength = indexes0 & 0xff;
indexes = new int[indexLength];
indexes[URES_INDEX_LENGTH] = indexLength;
indexes[URES_INDEX_LENGTH] = indexes0;
for(int i=1; i<indexLength; i++){
indexes[i] = ds.readInt();
}
resourceBottom = (1 + indexLength) << 2;
// determine if this resource bundle falls back to a parent bundle
// along normal locale ID fallback
noFallback =
indexLength > URES_INDEX_ATTRIBUTES &&
(indexes[URES_INDEX_ATTRIBUTES]&URES_ATT_NO_FALLBACK)!=0;
if(indexLength > 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;
}
// read the entire bundle (after the header) into data[]
// put rootRes and indexLength into data[0..7]
// and the rest of the data into data[8..length-1]
int length = indexes[URES_INDEX_BUNDLE_TOP]*4;
if(DEBUG) System.out.println("The number of bytes in the bundle: "+length);
data = new byte[length];
writeInt(rootRes, data, 0);
writeInt(indexLength, data, 4);
// now reset to the mark, which was set after reading rootRes and indexLength
ds.reset();
ds.readFully(data, 8, length-8);
}
/**
* Gets the full name of the resource with suffix.
*/
public static String getFullName(String baseName, String localeName){
if(baseName==null || baseName.length()==0){
if(localeName.length()==0){
return ULocale.getDefault().toString()+ICU_RESOURCE_SUFFIX;
}else{
return localeName+ICU_RESOURCE_SUFFIX;
// 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;
}
}else{
if(baseName.indexOf('.')==-1){
if(baseName.charAt(baseName.length()-1)!= '/'){
return baseName+"/"+localeName+ICU_RESOURCE_SUFFIX;
}else{
return baseName+localeName+ICU_RESOURCE_SUFFIX;
}
}else{
baseName = baseName.replace('.','/');
if(localeName.length()==0){
return baseName+ICU_RESOURCE_SUFFIX;
}else{
return baseName+"_"+localeName+ICU_RESOURCE_SUFFIX;
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);
}
public VersionInfo getVersion(){
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;
return ((version[0] == 1 && version[1] >= 1) || version[0] == 2);
}
public byte[] getData(){
return data;
}
public int getRootResource() {
int getRootResource() {
return rootRes;
}
public boolean getNoFallback() {
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 (int)((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] = (int)((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<limit) {
mid = (start + limit) / 2;
if (keyOffsets != null) {
result = reader.compareKeys(key, keyOffsets[mid]);
} else {
result = reader.compareKeys32(key, key32Offsets[mid]);
}
if (result < 0) {
limit = mid;
} else if (result > 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);
}
}
}

View File

@ -459,7 +459,7 @@ public class OlsonTimeZone extends BasicTimeZone {
// Type data must be of the same size as the transitions list
r = res.get(2);
typeData = r.getBinary().array();
typeData = r.getBinary(null);
if (typeData.length != transitionCount) {
throw new IllegalArgumentException("Invalid Format");
}

View File

@ -206,7 +206,7 @@ public class ResourceBundleWrapper extends UResourceBundle {
System.out.println(e);
}
addToCache(cl, name, defaultLocale, b);
b = (ResourceBundleWrapper)addToCache(cl, name, defaultLocale, b);
}
if(b!=null){
b.initKeysVector();

View File

@ -9,17 +9,20 @@ package com.ibm.icu.util;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.ICUResourceBundleReader;
import com.ibm.icu.impl.ResourceBundleWrapper;
import com.ibm.icu.impl.SimpleCache;
@ -318,20 +321,24 @@ public abstract class UResourceBundle extends ResourceBundle{
//TODO figure a way around this method(see method comment)
BUNDLE_CACHE = new SimpleCache<ResourceCacheKey, UResourceBundle>();
}
private static void addToCache(ResourceCacheKey key, UResourceBundle b) {
BUNDLE_CACHE.put(key, b);
}
/**
* Method used by subclasses to add the a particular resource bundle object to the managed cache
* Method used by subclasses to add a resource bundle object to the managed cache.
* Works like a putIfAbsent(): If the cache already contains a matching bundle,
* that one will be retained and returned.
* @internal revisit for ICU 3.6
* @deprecated This API is ICU internal only.
*/
protected static void addToCache(ClassLoader cl, String fullName, ULocale defaultLocale, UResourceBundle b){
protected static UResourceBundle addToCache(ClassLoader cl, String fullName,
ULocale defaultLocale, UResourceBundle b) {
synchronized(cacheKey){
cacheKey.setKeyValues(cl, fullName, defaultLocale);
addToCache((ResourceCacheKey)cacheKey.clone(), b);
UResourceBundle cachedBundle = BUNDLE_CACHE.get(cacheKey);
if (cachedBundle != null) {
return cachedBundle;
}
BUNDLE_CACHE.put((ResourceCacheKey)cacheKey.clone(), b);
return b;
}
}
/**
@ -342,12 +349,9 @@ public abstract class UResourceBundle extends ResourceBundle{
protected static UResourceBundle loadFromCache(ClassLoader cl, String fullName, ULocale defaultLocale){
synchronized(cacheKey){
cacheKey.setKeyValues(cl, fullName, defaultLocale);
return loadFromCache(cacheKey);
return BUNDLE_CACHE.get(cacheKey);
}
}
private static UResourceBundle loadFromCache(ResourceCacheKey key) {
return BUNDLE_CACHE.get(key);
}
/**
* Key used for cached resource bundles. The key checks
@ -495,7 +499,7 @@ public abstract class UResourceBundle extends ResourceBundle{
}
/**
* Loads a new resource bundle for the give base name, locale and class loader.
* Loads a new resource bundle for the given base name, locale and class loader.
* Optionally will disable loading of fallback bundles.
* @param baseName the base name of the resource bundle, a fully qualified class name
* @param localeName the locale for which a resource bundle is desired
@ -517,16 +521,10 @@ public abstract class UResourceBundle extends ResourceBundle{
{
case ROOT_ICU:
if(disableFallback) {
String fullName = ICUResourceBundleReader.getFullName(baseName, localeName);
synchronized(cacheKey){
cacheKey.setKeyValues(root, fullName, defaultLocale);
b = loadFromCache(cacheKey);
}
String fullName = ICUResourceBundle.getFullName(baseName, localeName);
b = loadFromCache(root, fullName, defaultLocale);
if (b == null) {
b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);
//cacheKey.setKeyValues(root, fullName, defaultLocale);
addToCache(cacheKey, b);
}
} else {
b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);
@ -549,11 +547,10 @@ public abstract class UResourceBundle extends ResourceBundle{
}
}
/**
* Returns a binary data from a binary resource.
* Returns a binary data item from a binary resource, as a read-only ByteBuffer.
*
* @return a pointer to a chuck of unsigned bytes which live in a memory mapped/DLL file.
* @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.
* @see #getIntVector
* @see #getInt
* @throws MissingResourceException
@ -594,10 +591,11 @@ public abstract class UResourceBundle extends ResourceBundle{
}
/**
* Returns a binary data from a binary resource.
* Returns a binary data from a binary resource, as a byte array with a copy
* of the bytes from the resource bundle.
*
* @param ba The byte array to write the bytes to. A null variable is OK.
* @return an array bytes containing the binary data from the resource.
* @return an array of bytes containing the binary data from the resource.
* @see #getIntVector
* @see #getInt
* @throws MissingResourceException
@ -668,8 +666,7 @@ public abstract class UResourceBundle extends ResourceBundle{
obj = res.handleGet(aKey, null, this);
}
if (obj == null) {
String fullName = ICUResourceBundleReader.getFullName(
getBaseName(), getLocaleID());
String fullName = ICUResourceBundle.getFullName(getBaseName(), getLocaleID());
throw new MissingResourceException(
"Can't find resource for bundle " + fullName + ", key "
+ aKey, this.getClass().getName(), aKey);
@ -723,28 +720,56 @@ public abstract class UResourceBundle extends ResourceBundle{
}
/**
* Returns the keys in this bundle as an enumeration
* @return an enumeration containing key strings
* @return an enumeration containing key strings,
* which is empty if this is not a bundle or a table resource
* @stable ICU 3.8
*/
public Enumeration<String> getKeys() {
initKeysVector();
return keys.elements();
return Collections.enumeration(keySet());
}
private Vector<String> keys = null;
private synchronized void initKeysVector(){
if(keys != null){
return;
}
//ICUResourceBundle current = this;
keys = new Vector<String>();
Enumeration<String> e = this.handleGetKeys();
while(e.hasMoreElements()){
String elem = e.nextElement();
if(!keys.contains(elem)){
keys.add(elem);
/**
* Returns a Set of all keys contained in this ResourceBundle and its parent bundles.
* @return a Set of all keys contained in this ResourceBundle and its parent bundles,
* which is empty if this is not a bundle or a table resource
* @internal
* @deprecated This API is ICU internal only.
*/
public Set<String> keySet() {
if(keys == null) {
if(isTopLevelResource()) {
TreeSet<String> newKeySet;
if(parent == null) {
newKeySet = new TreeSet<String>();
} else if(parent instanceof UResourceBundle) {
newKeySet = new TreeSet<String>(((UResourceBundle)parent).keySet());
} else {
// TODO: Java 6 ResourceBundle has keySet(); use it when we upgrade to Java 6
// and remove this else branch.
newKeySet = new TreeSet<String>();
Enumeration<String> parentKeys = parent.getKeys();
while(parentKeys.hasMoreElements()) {
newKeySet.add(parentKeys.nextElement());
}
}
newKeySet.addAll(handleKeySet());
keys = Collections.unmodifiableSet(newKeySet);
} else {
return handleKeySet();
}
}
return keys;
}
private Set<String> keys = null;
/**
* Returns a Set of the keys contained <i>only</i> in this ResourceBundle.
* This does not include further keys from parent bundles.
* @return a Set of the keys contained only in this ResourceBundle,
* which is empty if this is not a bundle or a table resource
* @internal
* @deprecated This API is ICU internal only.
*/
protected Set<String> handleKeySet() {
return Collections.emptySet();
}
/**
@ -757,7 +782,7 @@ public abstract class UResourceBundle extends ResourceBundle{
* @stable ICU 3.8
*/
public int getSize() {
return size;
return 1;
}
/**
@ -770,11 +795,7 @@ public abstract class UResourceBundle extends ResourceBundle{
* @stable ICU 3.8
*/
public int getType() {
int type = ICUResourceBundle.RES_GET_TYPE(resource);
if(type==TABLE32){
return TABLE; //Mask the table32's real type
}
return type;
return NONE;
}
/**
@ -803,7 +824,7 @@ public abstract class UResourceBundle extends ResourceBundle{
* @stable ICU 3.8
*/
public String getKey() {
return key;
return null;
}
/**
* Resource type constant for "no resource".
@ -829,25 +850,6 @@ public abstract class UResourceBundle extends ResourceBundle{
*/
public static final int TABLE = 2;
/**
* Resource type constant for aliases;
* internally stores a string which identifies the actual resource
* storing the data (can be in a different resource bundle).
* Resolved internally before delivering the actual resource through the API.
* @internal ICU 3.8
* @deprecated This API is ICU internal only.
*/
protected static final int ALIAS = 3;
/**
* Internal use only.
* Alternative resource type constant for tables of key-value pairs.
* Never returned by getType().
* @internal ICU 3.8
* @deprecated This API is ICU internal only.
*/
protected static final int TABLE32 = 4;
/**
* Resource type constant for a single 28-bit integer, interpreted as
* signed or unsigned by the getInt() function.
@ -870,32 +872,6 @@ public abstract class UResourceBundle extends ResourceBundle{
public static final int INT_VECTOR = 14;
//====== protected members ==============
/**
* Data member where the subclasses store the key
* @internal
* @deprecated This API is ICU internal only.
*/
protected String key;
/**
* Data member where the subclasses store the size of resources
* @internal
* @deprecated This API is ICU internal only.
*/
protected int size = 1;
/**
* Data member where the subclasses store the offset within resource data
* @internal
* @deprecated This API is ICU internal only.
*/
protected long resource = RES_BOGUS;
/**
* Data member where the subclasses store whether the resource is top level
* @internal
* @deprecated This API is ICU internal only.
*/
protected boolean isTopLevel = false;
private static final long RES_BOGUS = 0xffffffff;
/**
* Actual worker method for fetching a resource based on the given key.
@ -945,13 +921,7 @@ public abstract class UResourceBundle extends ResourceBundle{
* @stable ICU 3.8
*/
protected Enumeration<String> handleGetKeys(){
Vector<String> resKeys = new Vector<String>();
UResourceBundle item = null;
for (int i = 0; i < size; i++) {
item = get(i);
resKeys.add(item.getKey());
}
return resKeys.elements();
return null;
}
/**
@ -1019,4 +989,14 @@ public abstract class UResourceBundle extends ResourceBundle{
* @deprecated This API is ICU internal only.
*/
protected abstract void setLoadingStatus(int newStatus);
/**
* Is this a top-level resource, that is, a whole bundle?
* @return true if this is a top-level resource
* @internal
* @deprecated This API is ICU internal only.
*/
protected boolean isTopLevelResource() {
return true;
}
}

View File

@ -130,7 +130,7 @@ public final class VersionInfo implements Comparable<VersionInfo>
* @internal
* @deprecated This API is ICU internal only.
*/
public static final String ICU_DATA_VERSION = "42b";
public static final String ICU_DATA_VERSION = "43b";
/**
* ICU4J collator runtime version

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:42519c352c90321d48a65a3a956596642c305477597d43b349861b6f658a2d0a
size 7212644
oid sha256:388fe533d5ff5855cd486aacecaa4fb031eefa29d7bffa7179507de64014bf64
size 6407931

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dfedd3b5f82095391fc61e539fc02bc6c1862911bd6564db4964ae6a42e4e2a5
size 773585
oid sha256:68ced4760ccc503bedc5e7e6cd0ef39b19cc51a08d91a05f6d3658f8dfd61a75
size 719465

View File

@ -589,7 +589,8 @@ public class TestConversion extends ModuleTest {
CharBuffer out = null;
try {
out = decoder.decode(ByteBuffer.wrap(cc.bytes.array()));
cc.bytes.rewind();
out = decoder.decode(cc.bytes);
out.position(out.limit());
if (out.limit() < cc.unicode.length()) {
int pos = out.position();

View File

@ -15,6 +15,8 @@ import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import com.ibm.icu.dev.test.TestFmwk;
@ -593,7 +595,7 @@ public final class ICUResourceBundleTest extends TestFmwk {
if(b.getSize()>0){
logln("%%ALIAS mechanism works");
}else{
errln("%%ALIAS mechanism failed for iw_IL collations");
errln("%%ALIAS mechanism failed for iw_IL NumberPatterns");
}
}else{
errln("%%ALIAS mechanism failed for iw_IL");
@ -1001,4 +1003,48 @@ public final class ICUResourceBundleTest extends TestFmwk {
warnln("Failed to load data for abbreviated month names");
}
}
private Set<String> setFromEnumeration(Enumeration<String> e) {
TreeSet<String> set = new TreeSet<String>();
while (e.hasMoreElements()) {
set.add(e.nextElement());
}
return set;
}
/**
* Test ICUResourceBundle.getKeys() for a whole bundle (top-level resource).
* JDK JavaDoc for ResourceBundle.getKeys() says that it returns
* "an Enumeration of the keys contained in this ResourceBundle and its parent bundles."
*/
public void TestICUGetKeysAtTopLevel() {
String baseName="com/ibm/icu/dev/data/testdata";
UResourceBundle te_IN = UResourceBundle.getBundleInstance(baseName, "te_IN", testLoader);
UResourceBundle te = UResourceBundle.getBundleInstance(baseName, "te", testLoader);
Set<String> te_set = setFromEnumeration(te.getKeys());
Set<String> te_IN_set = setFromEnumeration(te_IN.getKeys());
assertTrue("te.getKeys().contains(string_only_in_Root)", te_set.contains("string_only_in_Root"));
assertTrue("te.getKeys().contains(string_only_in_te)", te_set.contains("string_only_in_te"));
assertFalse("te.getKeys().contains(string_only_in_te_IN)", te_set.contains("string_only_in_te_IN"));
assertTrue("te_IN.getKeys().contains(string_only_in_Root)", te_IN_set.contains("string_only_in_Root"));
assertTrue("te_IN.getKeys().contains(string_only_in_te)", te_IN_set.contains("string_only_in_te"));
assertTrue("te_IN.getKeys().contains(string_only_in_te_IN)", te_IN_set.contains("string_only_in_te_IN"));
// TODO: Check for keys of alias resource items
}
/**
* Test ICUResourceBundle.getKeys() for a resource item (not a whole bundle/top-level resource).
* This does not take parent bundles into account.
*/
public void TestICUGetKeysForResourceItem() {
String baseName="com/ibm/icu/dev/data/testdata";
UResourceBundle te = UResourceBundle.getBundleInstance(baseName, "te", testLoader);
UResourceBundle tagged_array_in_Root_te = te.get("tagged_array_in_Root_te");
Set<String> keys = setFromEnumeration(tagged_array_in_Root_te.getKeys());
assertTrue("tagged_array_in_Root_te.getKeys().contains(tag0)", keys.contains("tag0"));
assertTrue("tagged_array_in_Root_te.getKeys().contains(tag1)", keys.contains("tag1"));
assertFalse("tagged_array_in_Root_te.getKeys().contains(tag7)", keys.contains("tag7"));
assertFalse("tagged_array_in_Root_te.getKeys().contains(tag12)", keys.contains("tag12"));
UResourceBundle array_in_Root_te = te.get("array_in_Root_te");
assertFalse("array_in_Root_te.getKeys().hasMoreElements()", array_in_Root_te.getKeys().hasMoreElements());
UResourceBundle string_in_Root_te = te.get("string_in_Root_te");
assertFalse("string_in_Root_te.getKeys().hasMoreElements()", string_in_Root_te.getKeys().hasMoreElements());
}
}