blob: f6058114c5bcbd71669f188de07b9bee0ec8164f [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 2001-2005, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
/**
* A list resource bundle that does redirection
* because otherwise some of our resource class files
* are too big for the java runtime to handle.
*/
package com.ibm.icu.impl;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.ListResourceBundle;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
import java.util.Hashtable;
public class ICUListResourceBundle extends ListResourceBundle {
private static final String ICUDATA = "ICUDATA";
private static final String ICU_BUNDLE_NAME = "LocaleElements";
private static final String ICU_PACKAGE_NAME ="com.ibm.icu.impl.data";
private static final String ENCODING="UTF-8";
/* package */ Locale icuLocale;
/* package */ void setParentX(ResourceBundle b) {
setParent(b);
}
public Locale getLocale() {
return icuLocale;
}
protected ICUListResourceBundle() {
}
private Hashtable visited = new Hashtable();
/**
* Subclassers must statically initialize this
*/
protected Object[][] contents;
/**
* This is our cache
*/
private Object[][] realContents;
/**
* See base class description
*/
protected Object[][] getContents(){
// we replace any redirected values with real values in a cloned array
if (realContents == null) {
realContents = contents;
for (int i = 0; i < contents.length; ++i) {
Object newValue = getRedirectedResource((String)contents[i][0],contents[i][1], -1);
if (newValue != null) {
if (realContents == contents) {
realContents = (Object[][])contents.clone();
}
realContents[i] = new Object[] { contents[i][0], newValue };
}
}
}
return realContents;
}
/**
* Return null if value is already in existing contents array, otherwise fetch the
* real value and return it.
*/
private Object getRedirectedResource(String key, Object value, int index) {
if (value instanceof Object[][]) {
Object[][] aValue = (Object[][])value;
int i=0;
while(i < aValue.length){
int j=0;
while(j < aValue[i].length){
aValue[i][j] = getRedirectedResource((String)aValue[i][0],aValue[i][j], i);
j++;
}
i++;
}
}else if (value instanceof Object[]){
Object[] aValue = (Object[]) value;
int i=0;
while( i < aValue.length){
aValue[i] = getRedirectedResource(key,aValue[i], i);
i++;
}
}else if(value instanceof Alias){
String cName = this.getClass().getName();
visited.clear();
visited.put(cName+key,"");
return ((Alias)value).getResource(cName,key,index, visited);
}else if(value instanceof RedirectedResource){
return ((RedirectedResource)value).getResource(this);
}
return value;
}
private static byte[] readToEOS(InputStream stream) {
// As of 3.0 this method reads streams of length 264..274008
// from the core data. We progressively double the buffer
// size to reduce the number of allocations required.
try {
ArrayList vec = new ArrayList();
int count = 0;
int length = 0x200; // smallest 2^n >= min stream len
final int MAXLENGTH = 0x8000;
int pos = -1;
for (;;) {
byte[] buffer = new byte[length];
pos = 0;
do {
int n = stream.read(buffer, pos, length - pos);
if (n == -1) {
break;
}
pos += n;
} while (pos < length);
count += pos;
vec.add(buffer);
if (pos < length) {
break;
}
if (length < MAXLENGTH) {
length <<= 1;
}
}
// System.out.println("\ncount " + count + " bytes from " + stream);
byte[] data = new byte[count];
pos = 0;
for (int i = 0; i < vec.size(); ++i) {
byte[] buf = (byte[])vec.get(i);
int len = Math.min(buf.length, count - pos);
System.arraycopy(buf, 0, data, pos, len);
pos += len;
}
// assert pos==count;
return data;
} catch (IOException e) {
throw new MissingResourceException(e.getMessage(),"","");
}
}
private static char[] readToEOS(InputStreamReader stream) {
// As of 3.0 this method reads streams of length 41990..41994
// from the core data. The IBM 1.4 UTF8 converter doesn't
// handle buffering reliably (it throws an exception) so we
// are forced to read everything in one chunk.
try {
int length = 0x10000; // smallest 2^n >= max stream len
final int MAXLENGTH = 0x40000000;
int n;
char[] buffer;
for (;;) {
buffer = new char[length];
n = stream.read(buffer, 0, length);
if (n >= 0 && n < length) {
break;
}
if (length < MAXLENGTH) {
stream.reset();
length <<= 1;
} else {
throw new InternalError("maximum input stream length exceeded");
}
}
// System.out.println("\ncount " + n + " chars from " + stream);
char[] data = new char[n];
System.arraycopy(buffer, 0, data, 0, n);
return data;
} catch (IOException e) {
throw new MissingResourceException(e.getMessage(),"","");
}
}
/*
public static class CompressedString implements RedirectedResource{
private String expanded=null;
private String compressed=null;
public CompressedString(String str){
compressed=str;
}
public Object getResource(Object obj){
if(compressed==null){
return null;
}
if(expanded==null){
expanded= new String(Utility.RLEStringToCharArray(compressed));
}
return expanded;
}
}
*/
public static class CompressedBinary implements RedirectedResource{
private byte[] expanded=null;
private String compressed=null;
public CompressedBinary(String str){
compressed = str;
}
public Object getResource(Object obj){
if(compressed==null){
return null;
}
if(expanded==null){
expanded= Utility.RLEStringToByteArray(compressed);
}
return expanded;
}
}
private interface RedirectedResource{
public Object getResource(Object obj);
}
public static class ResourceBinary implements RedirectedResource{
private byte[] expanded=null;
private String resName=null;
public ResourceBinary(String name){
resName="data/" + name;
}
public Object getResource(Object obj) {
if(expanded==null){
InputStream stream = ICUData.getStream(resName);
if(stream!=null){
//throw new MissingResourceException("",obj.getClass().getName(),resName);
expanded = readToEOS(stream);
return expanded;
}
}
return "";
}
}
public static class ResourceString implements RedirectedResource{
private char[] expanded=null;
private String resName=null;
public ResourceString(String name){
resName="data/"+name;
}
public Object getResource(Object obj) {
if(expanded==null){
// Resource strings are always UTF-8
InputStream stream = ICUData.getStream(resName);
if(stream!=null){
//throw new MissingResourceException("",obj.getClass().getName(),resName);
try{
InputStreamReader reader = new InputStreamReader(stream,ENCODING);
expanded = readToEOS(reader);
}catch(UnsupportedEncodingException ex){
throw new RuntimeException("Could open converter for encoding: " +ENCODING);
}
return new String(expanded);
}
}
return "";
}
}
private static final char RES_PATH_SEP_CHAR = '/';
public static class Alias{
public Alias(String path){
pathToResource = path;
};
private String pathToResource;
private Object getResource(String className, String parentKey, int index, Hashtable visited){
String packageName=null,bundleName=null, locale=null, keyPath=null;
if(pathToResource.indexOf(RES_PATH_SEP_CHAR)==0){
int i =pathToResource.indexOf(RES_PATH_SEP_CHAR,1);
int j =pathToResource.indexOf(RES_PATH_SEP_CHAR,i+1);
bundleName=pathToResource.substring(1,i);
locale=pathToResource.substring(i+1);
if(j!=-1){
locale=pathToResource.substring(i+1,j);
keyPath=pathToResource.substring(j+1,pathToResource.length());
}
//there is a path included
if(bundleName.equals(ICUDATA)){
bundleName = ICU_BUNDLE_NAME;
packageName = ICU_PACKAGE_NAME;
}
}else{
//no path start with locale
int i =pathToResource.indexOf(RES_PATH_SEP_CHAR);
//If this is a bundle with locale name following it
//then it should be of type <bundle name>_<locale>
//if not we donot guarantee that this will work
int j = className.lastIndexOf(".");
packageName=className.substring(0,j);
int underScoreIndex = className.indexOf("_");
if(underScoreIndex>=0){
bundleName=className.substring(j+1,className.indexOf("_"));
}else{
bundleName = className.substring(j+1,className.length());
}
keyPath=pathToResource.substring(i+1);
if(i!=-1){
locale = pathToResource.substring(0,i);
}else{
locale=keyPath;
keyPath=parentKey;
if(locale==null || locale.equals("root")){
className=packageName+"."+bundleName;
}else{
className=packageName+"."+bundleName+"_"+ locale;
}
}
}
ResourceBundle bundle = null;
// getResourceBundle guarantees that the CLASSPATH will be searched
// for loading the resource with name <bundleName>_<localeName>.class
if(locale==null || locale.equals("root")){
bundle = ICULocaleData.getResourceBundle(packageName,bundleName,"");
}else{
bundle = ICULocaleData.getResourceBundle(packageName,bundleName,locale);
}
return findResource(bundle, className, parentKey, index, keyPath, visited);
}
private Object findResource(Object[][] contents, String key){
for (int i = 0; i < contents.length; ++i) {
// key must be non-null String, value must be non-null
String tempKey = (String) contents[i][0];
Object value = contents[i][1];
if (tempKey == null || value == null) {
throw new NullPointerException();
}
if(tempKey.equals(key)){
return value;
}
}
return null;
}
private Object findResource(Object o , String[] keys, int start, int index){
Object obj = o;
if( start < keys.length && keys[start] !=null){
if(obj instanceof Object[][]){
obj = findResource((Object[][])obj,keys[start]);
}else if(obj instanceof Object[] && isIndex(keys[start])){
obj = ((Object[])obj)[getIndex(keys[start])];
}
if(start+1 < keys.length && keys[start+1] !=null){
obj = findResource(obj,keys,start+1, index);
}
}else{
//try to find the corresponding index resource
if(index>=0){
if(obj instanceof Object[][]){
obj = findResource((Object[][])obj,Integer.toString(index));
}else if(obj instanceof Object[]){
obj = ((Object[])obj)[index];
}
}
}
return obj;
}
private Object findResource(ResourceBundle bundle, String className, String requestedKey, int index, String aliasKey, Hashtable visited){
if(aliasKey != null && visited.get(className+aliasKey)!=null){
throw new MissingResourceException("Circular Aliases in bundle.",bundle.getClass().getName(),requestedKey);
}
if(aliasKey==null){
// currently we do an implicit key lookup
// return ((ICUListResourceBundle)bundle).getContents();
aliasKey = requestedKey;
}
visited.put(className+requestedKey,"");
String[] keys = split(aliasKey,RES_PATH_SEP_CHAR);
Object o =null;
if(keys.length>0){
o = bundle.getObject(keys[0]);
o = findResource(o, keys, 1, index);
}
o=resolveAliases(o,className,aliasKey,visited);
return o;
}
private Object resolveAliases(Object o,String className,String key, Hashtable visited){
if(o instanceof Object[][]){
o = resolveAliases((Object[][])o,className,key, visited);
}else if(o instanceof Object[]){
o = resolveAliases((Object[])o,className,key, visited);
}else if(o instanceof Alias){
return ((Alias)o).getResource(className,key, -1, visited);
}
return o;
}
private Object resolveAliases(Object[][] o,String className, String key,Hashtable visited){
int i =0;
while(i<o.length){
o[i][1]=resolveAliases(o[i][1],className,key,visited);
i++;
}
return o;
}
private Object resolveAliases(Object[] o,String className, String key,Hashtable visited){
int i =0;
while(i<o.length){
o[i]=resolveAliases(o[i],className,key,visited);
i++;
}
return o;
}
}
private static String[] split(String source, char delimiter){
char[] src = source.toCharArray();
int index = 0;
int numdelimit=0;
// first count the number of delimiters
for(int i=0;i<source.length();i++){
if(src[i]==delimiter){
numdelimit++;
}
}
String[] values =null;
values = new String[numdelimit+2];
// now split
int old=0;
for(int j=0;j<src.length;j++){
if(src[j]==delimiter){
values[index++] = new String(src,old,j-old);
old=j+1/* skip after the delimiter*/;
}
}
if(old <src.length)
values[index++]=new String(src,old,src.length-old);
return values;
}
/**
* 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>
* ResourceBundle bundle = new ResourceBundle(getLocaleFromString("de__PHONEBOOK"));
* Object result = null;
* if(bundle instanceof ICUListResourceBundle){
* result = ((ICUListResourceBundle) bundle).getObjectWithFallback("collations/default");
* }
* </code>
* @param path The path to the required resource key
* @return Object represented by the key
* @exception MissingResourceException
*/
public final Object getObjectWithFallback(String path)
throws MissingResourceException{
String[] keys = split(path, RES_PATH_SEP_CHAR);
Object result = null;
ICUListResourceBundle actualBundle = this;
// now recuse to pick up sub levels of the items
result = findResourceWithFallback(keys, actualBundle);
if(result == null){
throw new MissingResourceException("Could not find the resource in ",this.getClass().getName(),path);
}
return result;
}
private Object findResourceWithFallback(String[] keys,
ICUListResourceBundle actualBundle){
Object obj = null;
while(actualBundle != null){
// get the top level resource
// getObject is a method on the ResourceBundle class that
// performs the normal fallback
obj = actualBundle.getObject(keys[0], actualBundle);
// now find the bundle from the actual bundle
// if this bundle does not contain the top level resource,
// then we can be sure that it does not contain the sub elements
obj = findResourceWithFallback(obj, keys, 1, 0);
// did we get the contents? the break
if(obj != null){
break;
}
// if not try the parent bundle
actualBundle = (ICUListResourceBundle) actualBundle.parent;
}
return obj;
}
private Object findResourceWithFallback(Object o , String[] keys, int start,
int index){
Object obj = o;
if( start < keys.length && keys[start] !=null){
if(obj instanceof Object[][]){
obj = findResourceWithFallback((Object[][])obj,keys[start]);
}else if(obj instanceof Object[] && isIndex(keys[start])){
obj = ((Object[])obj)[getIndex(keys[start])];
}
if(start+1 < keys.length && keys[start+1] !=null){
obj = findResourceWithFallback(obj,keys,start+1, index);
}
}else{
//try to find the corresponding index resource
if(index>=0){
if(obj instanceof Object[][]){
obj = findResourceWithFallback((Object[][])obj,
Integer.toString(index));
}else if(obj instanceof Object[]){
obj = ((Object[])obj)[index];
}
}
}
return obj;
}
private Object findResourceWithFallback(Object[][] cnts, String key){
Object obj = null;
for (int i = 0; i < cnts.length; ++i) {
// key must be non-null String
String tempKey = (String) cnts[i][0];
obj = cnts[i][1];
if(tempKey != null && tempKey.equals(key)){
return obj;
}
}
return null;
}
private final Object getObject(String key,
ICUListResourceBundle actualBundle) {
Object obj = handleGetObject(key);
if (obj == null) {
ICUListResourceBundle p = (ICUListResourceBundle) this.parent;
while( p!=null){
obj = p.handleGetObject(key);
if(obj != null){
actualBundle = p;
break;
}
p = (ICUListResourceBundle) p.parent;
}
}
return obj;
}
private static boolean isIndex(String s){
if(s.length()==1){
char c = s.charAt(0);
return Character.isDigit(c);
}
return false;
}
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;
}
}