blob: 5edf38106eec4def197c10cb611cb6501302c682 [file] [log] [blame]
/*
******************************************************************************
* Copyright (C) 2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
******************************************************************************
*/
package com.ibm.icu.impl;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.Vector;
import com.ibm.icu.util.StringTokenizer;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
import com.ibm.icu.util.UResourceTypeMismatchException;
import com.ibm.icu.util.VersionInfo;
public class ICUResourceBundle extends UResourceBundle{
/**
* Resource type constant for "no resource".
* @draft ICU 3.0
*/
public static final int NONE=-1;
/**
* Resource type constant for strings.
* @draft ICU 3.0
*/
public static final int STRING=0;
/**
* Resource type constant for binary data.
* @draft ICU 3.0
*/
public static final int BINARY=1;
/**
* Resource type constant for tables of key-value pairs.
* @draft ICU 3.0
*/
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.
* @draft ICU 3.0
* @internal
*/
private static final int ALIAS=3;
/**
* Internal use only.
* Alternative resource type constant for tables of key-value pairs.
* Never returned by getType().
* @internal
* @draft ICU 3.0
*/
private static final int TABLE32=4;
/**
* Resource type constant for a single 28-bit integer, interpreted as
* signed or unsigned by the getInt() function.
* @see #getInt
* @draft ICU 3.0
*/
public static final int INT=7;
/**
* Resource type constant for arrays of resources.
* @draft ICU 3.0
*/
public static final int ARRAY=8;
/**
* Resource type constant for vectors of 32-bit integers.
* @see #getIntVector
* @draft ICU 3.0
*/
public static final int INT_VECTOR=14;
private static final String ICU_RESOURCE_INDEX = "res_index";
private static final String DEFAULT_TAG = "default";
/**
* Return the version number associated with this UResourceBundle as an
* VersionInfo object.
* @return VersionInfo object containing the version of the bundle
* @draft ICU 3.0
*/
public VersionInfo getVersion(){
return null;
}
/**
* Returns a string from a string resource type
*
* @return a string
* @see #getBinary
* @see #getIntVector
* @see #getInt
* @throws MissingResourceException
* @throws UResourceTypeMismatchException
* @draft ICU 3.0
*/
public String getString(){
throw new UResourceTypeMismatchException("");
}
/**
* Returns a string from a string resource type
* @param key The key whose values needs to be fetched
* @return a string
* @see #getBinary
* @see #getIntVector
* @see #getInt
* @throws MissingResourceException
* @throws UResourceTypeMismatchException
* @draft ICU 3.0
*/
// public String getString(String key) {
// throw new UResourceTypeMismatchException("");
// }
/**
* Returns a binary data from a binary resource.
*
* @return a pointer to a chuck of unsigned bytes which live in a memory mapped/DLL file.
* @see #getString
* @see #getIntVector
* @see #getInt
* @throws MissingResourceException
* @throws UResourceTypeMismatchException
* @draft ICU 3.0
*/
public ByteBuffer getBinary(){
throw new UResourceTypeMismatchException("");
}
/**
* Returns a 32 bit integer array from a resource.
*
* @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.
* @see #getBinary
* @see #getString
* @see #getInt
* @throws MissingResourceException
* @throws UResourceTypeMismatchException
* @draft ICU 3.0
*/
public int[] getIntVector(){
throw new UResourceTypeMismatchException("");
}
/**
* Returns a signed integer from a resource.
*
* @return an integer value
* @see #getIntVector
* @see #getBinary
* @see #getString
* @throws MissingResourceException
* @throws UResourceTypeMismatchException
* @stable ICU 2.0
*/
public int getInt() {
throw new UResourceTypeMismatchException("");
}
/**
* Returns a unsigned integer from a resource.
* This integer is originally 28 bit and the sign gets propagated.
*
* @return an integer value
* @see #getIntVector
* @see #getBinary
* @see #getString
* @throws MissingResourceException
* @throws UResourceTypeMismatchException
* @stable ICU 2.0
*/
public int getUInt() {
throw new UResourceTypeMismatchException("");
}
/**
* Returns the size of a resource. Size for scalar types is always 1,
* and for vector/table types is the number of child resources.
* <br><b><font color='red'>Warning: </font></b> Integer array is treated as a scalar type. There are no
* APIs to access individual members of an integer array. It
* is always returned as a whole.
* @return number of resources in a given resource.
* @draft ICU 3.0
*/
public int getSize(){
return size;
}
/**
* Returns the type of a resource.
* Available types are {@link #INT INT}, {@link #ARRAY ARRAY},
* {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR},
* {@link #STRING STRING}, {@link #TABLE TABLE}.
*
* @return type of the given resource.
* @draft ICU 3.0
*/
public int getType(){
return type;
}
/**
* Returns the key associated with a given resource. Not all the resources have a key - only
* those that are members of a table.
* @return a key associated to this resource, or NULL if it doesn't have a key
* @draft ICU 3.0
*/
public String getKey(){
return key;
}
/**
* Returns the iterator which iterates over this
* resource bundle
* @draft ICU 3.0
*/
public ICUResourceBundleIterator getIterator(){
return new ICUResourceBundleIterator(this);
}
/**
* Returns the resource in a given resource at the specified index.
*
* @param index an index to the wanted resource.
* @return the sub resource UResourceBundle object
* @throws IndexOutOfBoundsException
* @draft ICU 3.0
*/
public ICUResourceBundle get(int index){
ICUResourceBundle obj = handleGet(index);
if (obj == null) {
if (parent != null) {
obj = ((ICUResourceBundle)parent).get(index);
}
if (obj == null)
throw new MissingResourceException("Can't find resource for bundle "
+this.getClass().getName()
+", key "+key,
this.getClass().getName(),
key);
}
return obj;
}
protected Object handleGetObject(String key){
if(getType()==STRING){
return getString();
}
ICUResourceBundle obj = handleGet(key);
if(obj!=null){
if(obj.getType()==STRING){
return obj.getString();
}
try{
if(obj.getType()==ARRAY){
return obj.handleGetStringArray();
}
}catch(UResourceTypeMismatchException ex){
return obj;
}
}
return obj;
}
protected ICUResourceBundle handleGet(int index){
return null;
}
protected ICUResourceBundle handleGet(String key){
return null;
}
protected String[] handleGetStringArray(){
return null;
}
// abstract UResourceBundle handleGetInt(int index);
/**
* Returns a resource in a given resource that has a given key.
*
* @param key a key associated with the wanted resource
* @return a resource bundle object representing rhe resource
* @throws MissingResourceException
* @draft ICU 3.0
*/
public ICUResourceBundle get(String key){
ICUResourceBundle obj = handleGet(key);
if (obj == null) {
if (parent != null) {
obj = ((ICUResourceBundle)parent).get(key);
}
if (obj == null){
String fullName = ICUResourceBundleReader.getFullName(baseName, localeID);
throw new MissingResourceException("Can't find resource for bundle "
+fullName
+", key "+key,
this.getClass().getName(),
key);
}
}
return obj;
}
/**
* Returns the string in a given resource at the specified index.
*
* @param indexS an index to the wanted string.
* @return a string which lives in the resource.
* @throws IndexOutOfBoundsException
* @throws UResourceTypeMismatchException
* @draft ICU 3.0
*/
public String getString(int index){
ICUResourceBundle temp = get(index);
if(temp.type==STRING){
return temp.getString();
}
throw new UResourceTypeMismatchException("");
}
/**
* Returns the parent bundle of this bundle
* @return UResourceBundle the parent of this bundle. Returns null if none
* @draft ICU 3.0
*/
public UResourceBundle getParent(){
return (UResourceBundle)parent;
}
/**
* Returns a functionally equivalent locale, considering keywords as well, for the specified keyword.
* @param BASE resource specifier
* @param resName top level resource to consider (such as "collations")
* @param keyword a particular keyword to consider (such as "collation" )
* @param locid The requested locale
* @param fillinIsAvailable If non-null, 1-element array of fillin parameter that indicates whether the
* requested locale was available. The locale is defined as 'available' if it physically
* exists within the specified tree.
* @return the locale
* @internal ICU 3.0
*/
public static final ULocale getFunctionalEquivalent(String baseName, String resName, String keyword,
ULocale locID, boolean fillinIsAvailable[]){
String kwVal = locID.getKeywordValue(keyword);
String baseLoc = locID.getBaseName();
String defStr = null;
ULocale parent = new ULocale(baseLoc);
ULocale found = locID;
ULocale defLoc = null; // locale where default (found) resource is
boolean lookForDefault = false; // true if kwVal needs to be set
ULocale fullBase = null; // base locale of found (target) resource
int defDepth = 0; // depth of 'default' marker
int resDepth = 0; // depth of found resource;
if(fillinIsAvailable != null) {
fillinIsAvailable[0] = true;
}
if((kwVal == null) || (kwVal.length()==0) || kwVal.equals(DEFAULT_TAG)) {
kwVal = ""; // default tag is treated as no keyword
lookForDefault = true;
}
// Check top level locale first
ICUResourceBundle r = null;
r = (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName,parent);
found = r.getULocale();
if(fillinIsAvailable != null) {
if (!found.equals(parent)) {
fillinIsAvailable[0] = false;
}
}
// determine in which locale (if any) the currently relevant 'default' is
do {
try {
ICUResourceBundle irb = r.get(resName);
defStr = irb.getString(DEFAULT_TAG);
if(lookForDefault == true) {
kwVal = defStr;
lookForDefault = false;
}
defLoc = r.getULocale();
} catch (MissingResourceException t) {
// Ignore error and continue search.
}
r = (ICUResourceBundle)r.getParent();
defDepth ++;
} while ((r != null) && (defLoc == null));
// Now, search for the named resource
parent = new ULocale(baseLoc);
r = (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName,parent);
// determine in which locale (if any) the named resource is located
do {
try {
ICUResourceBundle irb = r.get(resName);
UResourceBundle urb = irb.get(kwVal);
fullBase = r.getULocale(); // If the get() completed, we have the full base locale
} catch (MissingResourceException t) {
// Ignore error,
}
r = (ICUResourceBundle)r.getParent();
resDepth ++;
} while ((r != null) && (fullBase == null));
if(fullBase == null && // Could not find resource 'kwVal'
(defStr != null) && // default was defined
!defStr.equals(kwVal)) { // kwVal is not default
// couldn't find requested resource. Fall back to default.
kwVal = defStr; // Fall back to default.
parent = new ULocale(baseLoc);
r = (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName,parent);
resDepth = 0;
// determine in which locale (if any) the named resource is located
do {
try {
ICUResourceBundle irb = r.get(resName);
UResourceBundle urb = irb.get(kwVal);
// if we didn't fail before this..
fullBase = r.getULocale();
} catch (MissingResourceException t) {
// Ignore error, continue search.
}
r = (ICUResourceBundle)r.getParent();
resDepth ++;
} while ((r != null) && (fullBase == null));
}
if(fullBase == null ) {
throw new MissingResourceException("Could not find locale containing requested or default keyword.",
baseName, keyword + "=" + kwVal);
}
if(defStr.equals(kwVal) && // if default was requested and
(resDepth <= defDepth)){ // default was set in same locale or child
return fullBase; // Keyword value is default - no keyword needed in locale
} else {
return new ULocale(fullBase.toString() + "@" + keyword + "=" + kwVal);
}
}
/**
* Given a tree path and keyword, return a string enumeration of all possible values for that keyword.
* @param BASE resource specifier
* @param keyword a particular keyword to consider, must match a top level resource name
* within the tree. (i.e. "collations")
* @internal ICU 3.0
*/
public static final String[] getKeywordValues(String baseName, String keyword){
Set keywords = new HashSet();
ULocale locales[] = createULocaleList(baseName,ICU_DATA_CLASS_LOADER);
int i;
for(i=0;i<locales.length;i++) {
try {
UResourceBundle b = UResourceBundle.getBundleInstance(baseName, locales[i]);
// downcast to ICUResourceBundle?
ICUResourceBundle irb = (ICUResourceBundle)(b.getObject(keyword));
Enumeration e = irb.getKeys();
Object s;
while(e.hasMoreElements()) {
s= e.nextElement();
if ((s instanceof String) && !DEFAULT_TAG.equals(s)) { // don't add 'default' items
keywords.add(s);
}
}
} catch (Throwable t){
//System.err.println("Error in - " + new Integer(i).toString() + " - " + t.toString());
// ignore the err - just skip that resource
}
}
return (String[])keywords.toArray(new String[0]);
}
/**
*
* @param baseName
* @param localeID
* @param root
* @return
*/
public static ICUResourceBundle createBundle(String baseName, String localeID, ClassLoader root){
ICUResourceBundleReader reader = new ICUResourceBundleReader(baseName, localeID, root);
ByteBuffer rawData = reader.getData();
long rootResource = (UNSIGNED_INT_MASK)& rawData.getInt(0);
int type = RES_GET_TYPE(rootResource);
if(type==TABLE){
return ResourceTable.getTableInstance(rawData, baseName, localeID, null, rootResource, rootResource, true);
}else if(type == TABLE32){
return new ResourceTable32(rawData, baseName, localeID, null, rootResource, rootResource, true);
}else{
throw new RuntimeException("Invalid format error");
}
}
/**
* This method performs multilevel fallback for fetching items from the bundle
* e.g:
* If resource is in the form
* de__PHONEBOOK{
* collations{
* default{ "phonebook"}
* }
* }
* If the value of "default" key needs to be accessed, then do:
* <code>
* UResourceBundle bundle = UResourceBundle.getBundleInstance("de__PHONEBOOK");
* ICUResourceBundle result = null;
* if(bundle instanceof ICUListResourceBundle){
* result = ((ICUListResourceBundle) bundle).getWithFallback("collations/default");
* }
* </code>
* @param path The path to the required resource key
* @return resource represented by the key
* @exception MissingResourceException
*/
public ICUResourceBundle getWithFallback(String path) throws MissingResourceException {
ICUResourceBundle result = null;
ICUResourceBundle actualBundle = this;
// now recuse to pick up sub levels of the items
result = findResourceWithFallback(path, actualBundle);
if(result == null){
throw new MissingResourceException("Can't find resource for bundle "
+this.getClass().getName()
+", key "+key,
path,
key);
}
return result;
}
// will throw type mismatch exception if the resource is not a string
public String getStringWithFallback(String path) throws MissingResourceException {
return getWithFallback(path).getString();
}
private ICUResourceBundle findResourceWithFallback(String path, ICUResourceBundle actualBundle) {
ICUResourceBundle sub = null;
while (actualBundle != null) {
StringTokenizer st = new StringTokenizer(path, "/");
ICUResourceBundle current = actualBundle;
while (st.hasMoreTokens()) {
String subKey = st.nextToken();
sub = current.handleGet(subKey, null);
if(sub==null){
break;
}
current = sub;
}
if(sub!=null){
//we found it
break;
}
// if not try the parent bundle
actualBundle = (ICUResourceBundle) actualBundle.parent;
}
return sub;
}
/**
* Gets a resource bundle using the specified base name, locale, and class root.
*
* @param baseName the base name of the resource bundle, a fully qualified class name
* @param locale the locale for which a resource bundle is desired
* @param root the class object from which to load the resource bundle
* @param disableFallback Option to disable locale inheritence.
* If true the fallback chain will not be built.
* @exception MissingResourceException
* if no resource bundle for the specified base name can be found
* @return a resource bundle for the given base name and locale
* @draft ICU 3.0
*
*/
public static UResourceBundle getBundleInstance(String baseName, String localeName, boolean disableFallback){
return instantiateBundle(baseName, localeName, ICU_DATA_CLASS_LOADER, disableFallback);
}
/**
* Return a set of the locale names supported by a collection of resource bundles.
* @param bundlePrefix the prefix of the resource bundles to use.
*/
public static Set getAvailableLocaleNameSet(String bundlePrefix) {
return getAvailEntry(bundlePrefix).getLocaleNameSet();
}
/**
* Return a set of the locale names supported by a collection of resource bundles.
* @param bundlePrefix the prefix of the resource bundles to use.
*/
public static Set getAvailableLocaleNameSet() {
return getAvailableLocaleNameSet(UResourceBundle.ICU_BASE_NAME);
}
/**
* Get the set of Locales installed in the specified bundles.
* @return the list of available locales
* @draft ICU 3.0
*/
public static final ULocale[] getAvailableULocales(String baseName){
return getAvailEntry(baseName).getULocaleList();
}
/**
* Get the set of ULocales installed the base bundle.
* @return the list of available locales
* @draft ICU 3.0
*/
public static final ULocale[] getAvailableULocales(){
return getAvailableULocales(ICU_BASE_NAME);
}
/**
* Get the set of Locales installed in the specified bundles.
* @return the list of available locales
* @draft ICU 3.0
*/
public static final Locale[] getAvailableLocales(String baseName){
return getAvailEntry(baseName).getLocaleList();
}
/**
* Get the set of Locales installed the base bundle.
* @return the list of available locales
* @draft ICU 3.0
*/
public static final Locale[] getAvailableLocales(){
return getAvailEntry(ICU_BASE_NAME).getLocaleList();
}
/**
*
* @param ulocales
* @return
* @draft ICU 3.0
*/
public static final Locale[] getLocaleList(ULocale[] ulocales){
ArrayList list = new ArrayList();
for(int i=0; i< ulocales.length; i++){
// if the ULocale does not contain a script code
// only then convert it to a Locale object
if(ulocales[i].getScript().length()==0){
list.add(new Locale(ulocales[i].getLanguage(),
ulocales[i].getCountry(),
ulocales[i].getVariant()));
}
}
Locale[] locales = new Locale[list.size()];
for(int i=0; i<locales.length; i++){
locales[i] = (Locale)list.get(i);
}
return locales;
}
protected int type = NONE;
protected String key;
protected int size = NONE;
protected String resPath;
protected boolean isTopLevel;
protected ICUResourceBundleReader reader;
//protected byte[] version;
protected ByteBuffer rawData;
protected long resource;
protected long rootResource;
protected static final long UNSIGNED_INT_MASK = 0xffffffffL;
// ========== privates ==========
// Flag for enabling/disabling debugging code
private static final boolean DEBUG = ICUDebug.enabled("localedata");
// Cache for getAvailableLocales
private static SoftReference GET_AVAILABLE_CACHE;
private static final ULocale[] createULocaleList(String baseName, ClassLoader root){
ICUResourceBundle bundle = (ICUResourceBundle) instantiateBundle(baseName, ICU_RESOURCE_INDEX, root, true);
bundle = bundle.get(INSTALLED_LOCALES);
int length = bundle.getSize();
int i = 0;
ULocale[] locales = new ULocale[length];
ICUResourceBundleIterator iter = bundle.getIterator();
iter.reset();
while(iter.hasNext()){
locales[i++] = new ULocale(iter.next().getKey());
}
bundle = null;
return locales;
}
private static final Locale[] createLocaleList(String baseName){
ULocale[] ulocales = getAvailEntry(baseName).getULocaleList();
return getLocaleList(ulocales);
}
private static final String[] createLocaleNameArray(String baseName, ClassLoader root){
ICUResourceBundle bundle = (ICUResourceBundle) instantiateBundle(baseName, ICU_RESOURCE_INDEX, root, true);
bundle = bundle.get(INSTALLED_LOCALES);
int length = bundle.getSize();
int i = 0;
String[] locales = new String[length];
ICUResourceBundleIterator iter = bundle.getIterator();
iter.reset();
while(iter.hasNext()){
locales[i++] = iter.next().getKey();
}
bundle = null;
return locales;
}
private static Set createLocaleNameSet(String baseName) {
try {
String[] locales = createLocaleNameArray(baseName,ICU_DATA_CLASS_LOADER);
HashSet set = new HashSet();
set.addAll(Arrays.asList(locales));
return Collections.unmodifiableSet(set);
}
catch (MissingResourceException e) {
if (DEBUG) System.out.println("couldn't find index for bundleName: " + baseName);
Thread.dumpStack();
}
return Collections.EMPTY_SET;
}
/**
* Holds the prefix, and lazily creates the Locale[] list or the locale name Set as needed.
*/
private static final class AvailEntry {
private String prefix;
private ULocale[] ulocales;
private Locale[] locales;
private Set nameSet;
AvailEntry(String prefix) {
this.prefix = prefix;
}
ULocale[] getULocaleList() {
if (ulocales == null) {
ulocales = createULocaleList(prefix, ICU_DATA_CLASS_LOADER);
}
return ulocales;
}
Locale[] getLocaleList(){
if(locales == null){
locales = createLocaleList(prefix);
}
return locales;
}
Set getLocaleNameSet() {
if (nameSet == null) {
nameSet = createLocaleNameSet(prefix);
}
return nameSet;
}
}
/**
* Stores the locale information in a cache accessed by key (bundle prefix). The
* cached objects are AvailEntries. The cache is held by a SoftReference
* so it can be GC'd.
*/
private static AvailEntry getAvailEntry(String key) {
AvailEntry ae = null;
Map lcache = null;
if (GET_AVAILABLE_CACHE != null) {
lcache = (Map)GET_AVAILABLE_CACHE.get();
if (lcache != null) {
ae = (AvailEntry)lcache.get(key);
}
}
if (ae == null) {
ae = new AvailEntry(key);
if (lcache == null) {
lcache = new HashMap();
lcache.put(key, ae);
GET_AVAILABLE_CACHE = new SoftReference(lcache);
} else {
lcache.put(key, ae);
}
}
return ae;
}
private static final long RES_BOGUS = 0xffffffff;
protected 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)*4);
}
/* get signed and unsigned integer values directly from the Resource handle */
protected static final int RES_GET_INT(long res) {
return (((int)((res)<<4L))>>4L);
}
protected static final long RES_GET_UINT(long res){
long t = ((res)&0x0fffffffL);
return t;
}
protected static int countItems(final ByteBuffer rawData, final long res) {
if(res!=RES_BOGUS) {
switch(RES_GET_TYPE(res)) {
case STRING:
case BINARY:
case ALIAS:
case INT:
case INT_VECTOR:
return 1;
case ARRAY:
case TABLE32: {
int offset = RES_GET_OFFSET(res);
int value = rawData.getInt(offset);
return value;
}
case TABLE: {
int offset = RES_GET_OFFSET(res);
int value = rawData.getChar(offset);
return value;
}
default:
break;
}
}
return 0;
}
public Enumeration getKeys(){
Vector keys = new Vector();
ICUResourceBundle item =null;
for(int i=0; i<size; i++){
item = get(i);
keys.add(item.getKey());
}
return keys.elements();
}
protected ICUResourceBundle handleGet(String key, Hashtable table){
throw new UResourceTypeMismatchException("");
}
protected ICUResourceBundle handleGet(int index, Hashtable table){
throw new UResourceTypeMismatchException("");
}
protected ICUResourceBundle(ByteBuffer rawData, String baseName, String localeID,
String key, long resource,long rootResource, int type,
boolean isTopLevel ){
this.rawData = rawData;
this.key = key;
this.resource = resource;
this.rootResource = rootResource;
this.type = type;
this.isTopLevel = false;
this.size = countItems(rawData, resource);
this.baseName = baseName;
this.localeID = localeID;
//this.locale = new ULocale(localeID).toLocale();
}
public ULocale getULocale(){
return new ULocale(localeID);
}
private static StringBuffer RES_GET_KEY(ByteBuffer rawData, int keyOffset){
char ch = 0xFFFF; //sentinel
StringBuffer key = new StringBuffer();
while((ch=(char)rawData.get(keyOffset))!= 0){
key.append(ch);
keyOffset++;
}
return key;
}
private static final int getIntOffset(int offset){
return (offset*4);
}
private static final int getCharOffset(int offset){
return (offset*2);
}
private static final ICUResourceBundle createBundleObject(ByteBuffer rawData, String baseName, String localeID,
String key, long resource, long rootResource,
Hashtable table){
if(resource!=RES_BOGUS) {
switch(RES_GET_TYPE(resource)) {
case STRING:{
return new ResourceString(rawData, baseName, localeID, key, resource, rootResource);
}
case BINARY:{
return new ResourceBinary(rawData, baseName, localeID, key, resource, rootResource);
}
case ALIAS:{
return findResource(rawData, baseName, localeID, key, resource, table);
}
case INT:{
return new ResourceInt(rawData, baseName, localeID, key, resource, rootResource);
}
case INT_VECTOR:{
return new ResourceIntVector(rawData, baseName, localeID, key, resource, rootResource);
}
case ARRAY:{
return new ResourceArray(rawData, baseName, localeID, key, resource, rootResource);
}
case TABLE32:{
return new ResourceTable32(rawData, baseName, localeID, key, resource, rootResource, false);
}
case TABLE: {
return new ResourceTable(rawData, baseName, localeID, key, resource, rootResource, false);
}
default:
throw new InternalError("The resource type is unknown");
}
}
return null;
}
private static String getTableKey(ByteBuffer rawData, int size, int currentOffset, int index, int type){
switch(type){
case TABLE32:{
int charOffset = currentOffset+getIntOffset(index);
int keyOffset = rawData.getInt(charOffset);
return RES_GET_KEY(rawData,keyOffset).toString();
}
case TABLE:
default:{
int charOffset = currentOffset+getCharOffset(index);
int keyOffset = rawData.getChar(charOffset);
return RES_GET_KEY(rawData,keyOffset).toString();
}
}
}
private static int findKey(ByteBuffer rawData, int size,
int currentOffset, int type,
String target){
int mid = 0, start = 0, limit = size, rc;
int lastMid = -1;
//int myCharOffset = 0, keyOffset = 0;
for (;;) {
mid = ((start + limit) / 2);
if (lastMid == mid) { /* Have we moved? */
break; /* We haven't moved, and it wasn't found. */
}
lastMid = mid;
String comp = getTableKey(rawData, size,currentOffset,mid,type);
rc = target.compareTo(comp);
if (rc < 0) {
limit = mid;
} else if (rc > 0) {
start = mid;
} else {
return mid;
}
}
return -1;
}
private static class ResourceTable extends ICUResourceBundle{
public ICUResourceBundle handleGet(String key){
return handleGet(key, null);
}
protected ICUResourceBundle handleGet(String key, Hashtable table){
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 */
int foundOffset = findKey(rawData, size, currentOffset, type, key);
if(foundOffset == -1){
//throw new MissingResourceException(ICUResourceBundleReader.getFullName(baseName, localeID),
// localeID,
// key);
return null;
}
currentOffset += getCharOffset(size+(~size&1))+getIntOffset(foundOffset);
long resource = (UNSIGNED_INT_MASK) & rawData.getInt(currentOffset);
return createBundleObject(rawData, baseName, localeID, key, resource, rootResource, table);
}
public ICUResourceBundle handleGet(int index){
return handleGet(index, null);
}
public ICUResourceBundle handleGet(int index, Hashtable table){
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)+getCharOffset(1);
String itemKey = getTableKey(rawData, size, currentOffset, index, type);
currentOffset += getCharOffset(size+(~size&1))+ getIntOffset(index) ;
long resource = (UNSIGNED_INT_MASK) & rawData.getInt(currentOffset);
return createBundleObject(rawData, baseName, localeID, itemKey, resource, rootResource, table);
}
private ResourceTable(ByteBuffer rawData, String baseName, String localeID, String key,
long resource, long rootResource, boolean isRootResource ){
super(rawData, baseName, localeID, key, resource, rootResource, TABLE, isRootResource);
}
private static ResourceTable getTableInstance(ByteBuffer rawData, String baseName, String localeID, String key,
long resource, long rootResource, boolean isRootResource ){
// kludge: %%ALIAS is such a hack! I can understand the
// ICU4C legacy .. do we need to port it?
ResourceTable table = new ResourceTable(rawData, baseName, localeID, key, resource, rootResource, isRootResource);
//return table;
ICUResourceBundle b = table.handleGet(0);
String itemKey = b.getKey();
if(itemKey.equals("%%ALIAS")){
String locale = b.getString();
ICUResourceBundle actual = (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName, locale);
return (ResourceTable)actual;
} else {
return table;
}
}
}
private static class ResourceTable32 extends ICUResourceBundle{
public ICUResourceBundle get(String key){
return get(key, null);
}
public ICUResourceBundle get(String key, Hashtable table){
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 */
int foundOffset = findKey(rawData, size, currentOffset, TABLE32, key);
if(foundOffset == -1){
throw new MissingResourceException("Could not find resource ",
ICUResourceBundleReader.getFullName(baseName, localeID),
key);
}
currentOffset += getIntOffset(size)+getIntOffset(foundOffset);
long resource = (UNSIGNED_INT_MASK) & rawData.getInt(currentOffset);
return createBundleObject(rawData, baseName, localeID, key, resource, rootResource, table);
}
public ICUResourceBundle get(int index){
return get(index, null);
}
public ICUResourceBundle get(int index, Hashtable table){
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);
String itemKey = getTableKey(rawData, size, currentOffset, 0, TABLE32);
currentOffset += getIntOffset(size) ;
long resource = (UNSIGNED_INT_MASK) & rawData.getInt(currentOffset);
return createBundleObject(rawData, baseName, localeID, itemKey, resource, rootResource, table);
}
private ResourceTable32(ByteBuffer rawData, String baseName, String localeID,String key,
long resource, long rootResource, boolean isRootResource ){
super(rawData, key, baseName, localeID, resource, rootResource, TABLE, isRootResource);
}
}
private static class ResourceString extends ICUResourceBundle {
private String value;
public String getString(){
return value;
}
private ResourceString(ByteBuffer rawData, String baseName, String localeID,
String key, long resource, long rootResource ){
super(rawData, baseName, localeID, key, resource, rootResource, STRING, false);
value = getStringValue(rawData, resource);
}
}
private static class ResourceInt extends ICUResourceBundle{
public int getInt(){
return RES_GET_INT(resource);
}
public int getUInt(){
long ret = RES_GET_UINT(resource);
return (int)ret;
}
private ResourceInt(ByteBuffer rawData, String baseName, String localeID, String key,
long resource, long rootResource){
super(rawData, baseName, localeID, key, resource, rootResource, INT, false);
}
}
private static class ResourceArray extends ICUResourceBundle{
protected String[] handleGetStringArray(){
String[] strings = new String[size];
ICUResourceBundleIterator iter = getIterator();
int i = 0;
while(iter.hasNext()){
strings[i++] = iter.next().getString();
}
return strings;
}
protected ICUResourceBundle handleGet(String index){
return handleGet(index, null);
}
protected ICUResourceBundle handleGet(String index, Hashtable table){
int val = getIndex(index);
if(val>-1){
return handleGet(val, table);
}
throw new UResourceTypeMismatchException("");
}
protected ICUResourceBundle handleGet(int index){
return handleGet(index, null);
}
protected ICUResourceBundle handleGet(int index, Hashtable table){
if(index > size){
throw new IndexOutOfBoundsException();
}
int offset = RES_GET_OFFSET(resource);
int itemOffset = offset+getIntOffset(index+1);
long itemResource = (UNSIGNED_INT_MASK)&rawData.getInt(itemOffset);
return createBundleObject(rawData, baseName, localeID, null,itemResource, rootResource, table);
}
private ResourceArray(ByteBuffer rawData, String baseName, String localeID, String key,
long resource, long rootResource ){
super(rawData, baseName, localeID, key, resource, rootResource, ARRAY, false );
}
}
private static class ResourceBinary extends ICUResourceBundle{
private ByteBuffer value;
public ByteBuffer getBinary(){
value.rewind();
return value;
}
private ByteBuffer getValue(){
int offset = RES_GET_OFFSET(resource);
int length = rawData.getInt(offset);
int byteOffset = offset+getIntOffset(1);
ByteBuffer val = ByteBuffer.allocate(length);
for(int i=0; i<length; i++){
val.put(rawData.get(byteOffset+i));
}
return val;
}
public ResourceBinary(ByteBuffer rawData, String baseName, String localeID, String key,
long resource, long rootResource ){
super(rawData, baseName, localeID, key, resource, rootResource, BINARY, false );
value = getValue();
}
}
private static class ResourceIntVector extends ICUResourceBundle{
private int[] value;
public int[] getIntVector(){
return value;
}
private int[] getValue(){
int offset = RES_GET_OFFSET(resource);
int length = rawData.getInt(offset);
int intOffset = offset+getIntOffset(1);
int[] val = new int[length];
for(int i=0; i<length; i++){
val[i] = rawData.getInt(intOffset+getIntOffset(i));
}
return val;
}
public ResourceIntVector(ByteBuffer rawData, String baseName, String localeID, String key,
long resource, long rootResource){
super(rawData, baseName, localeID, key, resource, rootResource,INT_VECTOR, false );
value = getValue();
}
}
private static String getStringValue(ByteBuffer rawData, long resource){
int offset = RES_GET_OFFSET(resource);
int length = rawData.getInt(offset);
int stringOffset = offset+getIntOffset(1);
StringBuffer val = new StringBuffer();
for(int i=0; i<length; i++){
val.append(rawData.getChar(stringOffset+getCharOffset(i)));
}
return val.toString();
}
private static final char RES_PATH_SEP_CHAR ='/';
private static final String ICUDATA = "ICUDATA";
private static int getIndex(String s){
if(s.length()==1){
char c = s.charAt(0);
if(Character.isDigit(c)){
return Integer.valueOf(s).intValue();
}
}
return -1;
}
private static ICUResourceBundle findResource(ByteBuffer rawData, String baseName,
String localeID, String key, long resource,
Hashtable table){
String locale=null, keyPath=null;
String bundleName;
String resPath = getStringValue(rawData, resource);
if(table==null){
table = new Hashtable();
}
if(table.get(resPath)!= null){
throw new IllegalArgumentException("Circular references in the resource bundles");
}
table.put(resPath,"");
if(resPath.indexOf(RES_PATH_SEP_CHAR)==0){
int i =resPath.indexOf(RES_PATH_SEP_CHAR,1);
int j =resPath.indexOf(RES_PATH_SEP_CHAR,i+1);
bundleName=resPath.substring(1,i);
locale=resPath.substring(i+1);
if(j!=-1){
locale=resPath.substring(i+1,j);
keyPath=resPath.substring(j+1,resPath.length());
}
//there is a path included
if(bundleName.equals(ICUDATA)){
bundleName = ICU_BASE_NAME;
}
}else{
//no path start with locale
int i =resPath.indexOf(RES_PATH_SEP_CHAR);
keyPath=resPath.substring(i+1);
if(i!=-1){
locale = resPath.substring(0,i);
}else{
locale=keyPath;
keyPath = null;//keyPath.substring(i, keyPath.length());
}
bundleName = baseName;
}
ICUResourceBundle bundle = null;
if(locale==null){
bundle = (ICUResourceBundle)getBundleInstance(bundleName, "", ICU_DATA_CLASS_LOADER, false);
}else{
bundle = (ICUResourceBundle)getBundleInstance(bundleName, locale, ICU_DATA_CLASS_LOADER, false);
}
ICUResourceBundle sub = null;
if(keyPath!=null){
StringTokenizer st = new StringTokenizer(keyPath, "/");
ICUResourceBundle current = bundle;
while (st.hasMoreTokens()) {
String subKey = st.nextToken();
sub = current.handleGet(subKey, table);
if(sub==null){
break;
}
current = sub;
}
}else{
// if the sub resource is not found
// try fetching the sub resource with
// the key of this alias resource
sub = bundle.get(key);
}
if(sub == null){
throw new MissingResourceException(localeID, baseName, key);
}
sub.resPath = resPath;
return sub;
}
}