ICU-1982 New implementation of Alias and redirected data
X-SVN-Rev: 9122
This commit is contained in:
parent
61ba0a2fdf
commit
9a1ac0fce6
@ -6,31 +6,25 @@
|
||||
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListResourceBundle;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
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";
|
||||
|
||||
protected ICUListResourceBundle() {
|
||||
}
|
||||
|
||||
// protected static final Integer COMPRESSED_BINARY = new Integer(1);
|
||||
protected static final Integer RESOURCE_BINARY = new Integer(2);
|
||||
// protected static final Integer COMPRESSED_STRING = new Integer(3);
|
||||
// protected static final Integer COMPRESSED_BINARY_STRING = new Integer(4);
|
||||
protected static final Integer RESOURCE_UNICODE = new Integer(5);
|
||||
|
||||
/**
|
||||
* Subclassers must statically initialize this
|
||||
@ -46,151 +40,385 @@ public class ICUListResourceBundle extends ListResourceBundle {
|
||||
* 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 = getRedirectedValue(contents[i][1]);
|
||||
if (newValue != null) {
|
||||
if (realContents == contents) {
|
||||
realContents = (Object[][])contents.clone();
|
||||
// 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 = getRedirectedValue((String)contents[i][0],contents[i][1]);
|
||||
if (newValue != null) {
|
||||
if (realContents == contents) {
|
||||
realContents = (Object[][])contents.clone();
|
||||
}
|
||||
realContents[i] = new Object[] { contents[i][0], newValue };
|
||||
}
|
||||
}
|
||||
realContents[i] = new Object[] { contents[i][0], newValue };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return realContents;
|
||||
|
||||
return realContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return null if value is already in existing contents array, otherwise fetch the
|
||||
* real value and return it.
|
||||
*/
|
||||
private Object getRedirectedValue(Object value) {
|
||||
|
||||
// what we really want is:
|
||||
// if (value instanceof ICUListResourceBundle.Redirect) {
|
||||
// return ((ICUListResourcBundle.Redirect)value).getValue();
|
||||
// }
|
||||
// return null;
|
||||
// if we really want to support multiple encoding types, then we just have
|
||||
// different instances of ICUListResourceBundle.Redirect.
|
||||
|
||||
// value is always an array of one object which is an array of two objects,
|
||||
// encoding type and resource name.
|
||||
// encoding type is always RESOURCE_BINARY or RESOURCE_UNICODE
|
||||
|
||||
if (value instanceof Object[][]) {
|
||||
Object[][] aValue = (Object[][])value;
|
||||
if (aValue.length == 1) {
|
||||
Object[] bValue = (Object[])aValue[0];
|
||||
String resName = (String)bValue[1];
|
||||
try {
|
||||
if (bValue[0] == RESOURCE_BINARY) {
|
||||
// this code would be in RedirectByteArray.getValue, for example, and the resource
|
||||
// value would be an instance of this class
|
||||
// sigh, isn't there a better way to read the whole stream?
|
||||
// could get url instead, I suppose, and get the length of the data
|
||||
InputStream stream = this.getClass().getResourceAsStream(resName);
|
||||
byte[] result = readToEOS(stream);
|
||||
return result;
|
||||
}
|
||||
else if (bValue[0] == RESOURCE_UNICODE) {
|
||||
// temporarily disable because of BreakDictionaryData_th.problem
|
||||
// LocaleElements_th is the only resource currently using this.
|
||||
|
||||
return null;
|
||||
/*
|
||||
InputStream stream = this.getClass().getResourceAsStream(resName);
|
||||
InputStreamReader reader = new InputStreamReader(stream, "UTF-16LE");
|
||||
char[] result = readToEOS(reader);
|
||||
return result;
|
||||
*/
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
private Object getRedirectedValue(String key, Object value) {
|
||||
|
||||
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] = getRedirectedValue((String)aValue[i][0],aValue[i][j]);
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}else if(value instanceof Alias){
|
||||
Hashtable visited = new Hashtable();
|
||||
String cName = this.getClass().getName();
|
||||
visited.put(cName+key,"");
|
||||
return ((Alias)value).getResource(cName,key,visited);
|
||||
}else if(value instanceof CompressedString){
|
||||
return ((CompressedString)value).getResource();
|
||||
}else if(value instanceof CompressedBinary){
|
||||
return ((CompressedBinary)value).getResource();
|
||||
}else if(value instanceof ResourceBinary){
|
||||
return ((ResourceBinary)value).getResource(this);
|
||||
}else if(value instanceof ResourceString){
|
||||
try{
|
||||
return ((ResourceString)value).getResource(this);
|
||||
}catch(UnsupportedEncodingException e){
|
||||
throw new RuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static byte[] readToEOS(InputStream stream) {
|
||||
|
||||
ArrayList vec = new ArrayList();
|
||||
int count = 0;
|
||||
int pos = 0;
|
||||
final int MAXLENGTH = 0x8000; // max buffer size - 32K
|
||||
int length = 0x80; // start with small buffers and work up
|
||||
do {
|
||||
pos = 0;
|
||||
length = length >= MAXLENGTH ? MAXLENGTH : length * 2;
|
||||
byte[] buffer = new byte[length];
|
||||
try {
|
||||
ArrayList vec = new ArrayList();
|
||||
int count = 0;
|
||||
int pos = 0;
|
||||
final int MAXLENGTH = 0x8000; // max buffer size - 32K
|
||||
int length = 0x80; // start with small buffers and work up
|
||||
do {
|
||||
int n = stream.read(buffer, pos, length - pos);
|
||||
if (n == -1) {
|
||||
break;
|
||||
pos = 0;
|
||||
length = length >= MAXLENGTH ? MAXLENGTH : length * 2;
|
||||
byte[] buffer = new byte[length];
|
||||
try {
|
||||
do {
|
||||
int n = stream.read(buffer, pos, length - pos);
|
||||
if (n == -1) {
|
||||
break;
|
||||
}
|
||||
pos += n;
|
||||
} while (pos < length);
|
||||
}
|
||||
pos += n;
|
||||
} while (pos < length);
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
vec.add(buffer);
|
||||
count += pos;
|
||||
} while (pos == length);
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
return data;
|
||||
catch (IOException e) {
|
||||
}
|
||||
vec.add(buffer);
|
||||
count += pos;
|
||||
} while (pos == length);
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private static char[] readToEOS(InputStreamReader stream) {
|
||||
ArrayList vec = new ArrayList();
|
||||
int count = 0;
|
||||
int pos = 0;
|
||||
final int MAXLENGTH = 0x8000; // max buffer size - 32K
|
||||
int length = 0x80; // start with small buffers and work up
|
||||
do {
|
||||
pos = 0;
|
||||
length = length >= MAXLENGTH ? MAXLENGTH : length * 2;
|
||||
char[] buffer = new char[length];
|
||||
try {
|
||||
ArrayList vec = new ArrayList();
|
||||
int count = 0;
|
||||
int pos = 0;
|
||||
final int MAXLENGTH = 0x8000; // max buffer size - 32K
|
||||
int length = 0x80; // start with small buffers and work up
|
||||
do {
|
||||
int n = stream.read(buffer, pos, length - pos);
|
||||
if (n == -1) {
|
||||
break;
|
||||
pos = 0;
|
||||
length = length >= MAXLENGTH ? MAXLENGTH : length * 2;
|
||||
char[] buffer = new char[length];
|
||||
try {
|
||||
do {
|
||||
int n = stream.read(buffer, pos, length - pos);
|
||||
if (n == -1) {
|
||||
break;
|
||||
}
|
||||
pos += n;
|
||||
} while (pos < length);
|
||||
}
|
||||
pos += n;
|
||||
} while (pos < length);
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
vec.add(buffer);
|
||||
count += pos;
|
||||
} while (pos == length);
|
||||
|
||||
char[] data = new char[count];
|
||||
pos = 0;
|
||||
for (int i = 0; i < vec.size(); ++i) {
|
||||
char[] buf = (char[])vec.get(i);
|
||||
int len = Math.min(buf.length, count - pos);
|
||||
System.arraycopy(buf, 0, data, pos, len);
|
||||
pos += len;
|
||||
}
|
||||
return data;
|
||||
catch (IOException e) {
|
||||
}
|
||||
vec.add(buffer);
|
||||
count += pos;
|
||||
} while (pos == length);
|
||||
|
||||
char[] data = new char[count];
|
||||
pos = 0;
|
||||
for (int i = 0; i < vec.size(); ++i) {
|
||||
char[] buf = (char[])vec.get(i);
|
||||
int len = Math.min(buf.length, count - pos);
|
||||
System.arraycopy(buf, 0, data, pos, len);
|
||||
pos += len;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static class CompressedString{
|
||||
private String expanded=null;
|
||||
private String compressed=null;
|
||||
public CompressedString(String str){
|
||||
compressed=str;
|
||||
}
|
||||
private Object getResource(){
|
||||
if(compressed==null){
|
||||
return null;
|
||||
}
|
||||
if(expanded==null){
|
||||
expanded= new String(Utility.RLEStringToCharArray(compressed));
|
||||
}
|
||||
return expanded;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CompressedBinary{
|
||||
private byte[] expanded=null;
|
||||
private String compressed=null;
|
||||
public CompressedBinary(String str){
|
||||
compressed = str;
|
||||
}
|
||||
private Object getResource(){
|
||||
if(compressed==null){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(expanded==null){
|
||||
expanded= Utility.RLEStringToByteArray(compressed);
|
||||
}
|
||||
return expanded;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ResourceBinary{
|
||||
private byte[] expanded=null;
|
||||
private String resName=null;
|
||||
public ResourceBinary(String name){
|
||||
resName=name;
|
||||
}
|
||||
public Object getResource(Object obj){
|
||||
if(expanded==null){
|
||||
InputStream stream = obj.getClass().getResourceAsStream(resName);
|
||||
if(stream==null){
|
||||
throw new MissingResourceException("",obj.getClass().getName(),resName);
|
||||
}
|
||||
expanded = readToEOS(stream);
|
||||
}
|
||||
return expanded;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResourceString{
|
||||
private char[] expanded=null;
|
||||
private String resName=null;
|
||||
public ResourceString(String name){
|
||||
resName=name;
|
||||
}
|
||||
public Object getResource(Object obj) throws UnsupportedEncodingException{
|
||||
if(expanded==null){
|
||||
// Resource strings are always UTF-8
|
||||
InputStream stream = obj.getClass().getResourceAsStream(resName);
|
||||
if(stream==null){
|
||||
throw new MissingResourceException("",obj.getClass().getName(),resName);
|
||||
}
|
||||
InputStreamReader reader = new InputStreamReader(stream,ENCODING);
|
||||
expanded = readToEOS(reader);
|
||||
}
|
||||
return new String(expanded);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Alias{
|
||||
public Alias(String path){
|
||||
pathToResource = path;
|
||||
};
|
||||
private final char RES_PATH_SEP_CHAR = '/';
|
||||
private String pathToResource;
|
||||
|
||||
|
||||
private Object getResource(String className,String parentKey, 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);
|
||||
bundleName=className.substring(j+1,className.indexOf("_"));
|
||||
keyPath=pathToResource.substring(i+1);
|
||||
|
||||
if(i!=-1){
|
||||
locale = pathToResource.substring(0,i);
|
||||
}else{
|
||||
locale=keyPath;
|
||||
keyPath=parentKey;
|
||||
className=packageName+"."+bundleName+"_"+locale;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ResourceBundle bundle = null;
|
||||
// getResourceBundle guarantees that the CLASSPATH will be searched
|
||||
// for loading the resource with name <bundleName>_<localeName>.class
|
||||
bundle = ICULocaleData.getResourceBundle(packageName,bundleName,locale);
|
||||
|
||||
return findResource(bundle,className,keyPath, visited);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean isIndex(String s){
|
||||
if(s.length()==1){
|
||||
char c = s.charAt(0);
|
||||
return Character.isDigit(c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private 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 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){
|
||||
Object obj = o;
|
||||
int i=0;
|
||||
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);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
private Object findResource(ResourceBundle bundle,String className,String key, Hashtable visited){
|
||||
if(key==null){
|
||||
return ((ICUListResourceBundle)bundle).getContents();
|
||||
}
|
||||
if(visited.get(className+key)!=null){
|
||||
throw new MissingResourceException("Circular Aliases in bundle.",bundle.getClass().getName(),key);
|
||||
}
|
||||
|
||||
visited.put(className+key,"");
|
||||
|
||||
String[] keys = split(key,RES_PATH_SEP_CHAR);
|
||||
Object o =null;
|
||||
if(keys.length>0){
|
||||
o = bundle.getObject(keys[0]);
|
||||
o = findResource(o,keys,1);
|
||||
}
|
||||
o=resolveAliases(o,className,key,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,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((Object)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((Object)o[i],className,key,visited);
|
||||
i++;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,10 @@
|
||||
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
|
||||
/**
|
||||
* Provides information about and access to resource bundles in the
|
||||
@ -134,6 +126,49 @@ public class ICULocaleData {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource bundle from the lookup chain.
|
||||
*/
|
||||
public static ResourceBundle getResourceBundle(String[] packages, String bundleName, String localeName) {
|
||||
Locale locale = LocaleUtility.getLocaleFromName(localeName);
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
for (int i = 0; i < packages.length; ++i) {
|
||||
try {
|
||||
String path = packages[i] + "." + bundleName;
|
||||
if (debug) System.out.println("calling instantiateBundle: " + path + "_" + locale);
|
||||
ResourceBundle rb = instantiateBundle(path, locale);
|
||||
return rb;
|
||||
}
|
||||
catch (MissingResourceException e) {
|
||||
if (debug) System.out.println(bundleName + "_" + locale + " not found in " + packages[i]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource bundle from the lookup chain.
|
||||
*/
|
||||
public static ResourceBundle getResourceBundle(String packageName, String bundleName, String localeName) {
|
||||
Locale locale = LocaleUtility.getLocaleFromName(localeName);
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
|
||||
try {
|
||||
String path = packageName + "." + bundleName;
|
||||
if (debug) System.out.println("calling instantiateBundle: " + path + "_" + locale);
|
||||
ResourceBundle rb = instantiateBundle(path, locale);
|
||||
return rb;
|
||||
}
|
||||
catch (MissingResourceException e) {
|
||||
if (debug) System.out.println(bundleName + "_" + locale + " not found in " + packageName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Get a resource bundle from the resource bundle path. Unlike getResourceBundle, this
|
||||
* returns an 'unparented' bundle that exactly matches the bundle name and locale name.
|
||||
|
Loading…
Reference in New Issue
Block a user