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:
parent
c9087beb0d
commit
078e401f7b
@ -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];
|
||||
|
@ -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]);
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:42519c352c90321d48a65a3a956596642c305477597d43b349861b6f658a2d0a
|
||||
size 7212644
|
||||
oid sha256:388fe533d5ff5855cd486aacecaa4fb031eefa29d7bffa7179507de64014bf64
|
||||
size 6407931
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dfedd3b5f82095391fc61e539fc02bc6c1862911bd6564db4964ae6a42e4e2a5
|
||||
size 773585
|
||||
oid sha256:68ced4760ccc503bedc5e7e6cd0ef39b19cc51a08d91a05f6d3658f8dfd61a75
|
||||
size 719465
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user