001: /**
002: *******************************************************************************
003: * Copyright (C) 2001-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007:
008: /**
009: * A list resource bundle that does redirection
010: * because otherwise some of our resource class files
011: * are too big for the java runtime to handle.
012: */package com.ibm.icu.impl;
013:
014: import java.io.InputStream;
015: import java.io.InputStreamReader;
016: import java.io.IOException;
017: import java.io.UnsupportedEncodingException;
018: import java.util.ArrayList;
019: import java.util.ListResourceBundle;
020: import java.util.Locale;
021: import java.util.ResourceBundle;
022: import java.util.MissingResourceException;
023: import java.util.Hashtable;
024:
025: public class ICUListResourceBundle extends ListResourceBundle {
026: private static final String ICUDATA = "ICUDATA";
027: private static final String ICU_BUNDLE_NAME = "LocaleElements";
028: private static final String ICU_PACKAGE_NAME = "com.ibm.icu.impl.data";
029: private static final String ENCODING = "UTF-8";
030:
031: /* package */Locale icuLocale;
032:
033: /* package */void setParentX(ResourceBundle b) {
034: setParent(b);
035: }
036:
037: public Locale getLocale() {
038: return icuLocale;
039: }
040:
041: protected ICUListResourceBundle() {
042: }
043:
044: private Hashtable visited = new Hashtable();
045: /**
046: * Subclassers must statically initialize this
047: */
048: protected Object[][] contents;
049:
050: /**
051: * This is our cache
052: */
053: private Object[][] realContents;
054:
055: /**
056: * See base class description
057: */
058: protected Object[][] getContents() {
059: // we replace any redirected values with real values in a cloned array
060: if (realContents == null) {
061: realContents = contents;
062: for (int i = 0; i < contents.length; ++i) {
063: Object newValue = getRedirectedResource(
064: (String) contents[i][0], contents[i][1], -1);
065: if (newValue != null) {
066: if (realContents == contents) {
067: realContents = (Object[][]) contents.clone();
068: }
069: realContents[i] = new Object[] { contents[i][0],
070: newValue };
071: }
072: }
073: }
074: return realContents;
075: }
076:
077: /**
078: * Return null if value is already in existing contents array, otherwise fetch the
079: * real value and return it.
080: */
081: private Object getRedirectedResource(String key, Object value,
082: int index) {
083:
084: if (value instanceof Object[][]) {
085: Object[][] aValue = (Object[][]) value;
086: int i = 0;
087: while (i < aValue.length) {
088: int j = 0;
089: while (j < aValue[i].length) {
090: aValue[i][j] = getRedirectedResource(
091: (String) aValue[i][0], aValue[i][j], i);
092: j++;
093: }
094: i++;
095: }
096: } else if (value instanceof Object[]) {
097: Object[] aValue = (Object[]) value;
098: int i = 0;
099: while (i < aValue.length) {
100: aValue[i] = getRedirectedResource(key, aValue[i], i);
101: i++;
102: }
103: } else if (value instanceof Alias) {
104:
105: String cName = this .getClass().getName();
106: visited.clear();
107: visited.put(cName + key, "");
108: return ((Alias) value).getResource(cName, key, index,
109: visited);
110: } else if (value instanceof RedirectedResource) {
111: return ((RedirectedResource) value).getResource(this );
112: }
113:
114: return value;
115: }
116:
117: private static byte[] readToEOS(InputStream stream) {
118: // As of 3.0 this method reads streams of length 264..274008
119: // from the core data. We progressively double the buffer
120: // size to reduce the number of allocations required.
121: try {
122: ArrayList vec = new ArrayList();
123: int count = 0;
124: int length = 0x200; // smallest 2^n >= min stream len
125: final int MAXLENGTH = 0x8000;
126: int pos = -1;
127: for (;;) {
128: byte[] buffer = new byte[length];
129: pos = 0;
130: do {
131: int n = stream.read(buffer, pos, length - pos);
132: if (n == -1) {
133: break;
134: }
135: pos += n;
136: } while (pos < length);
137: count += pos;
138: vec.add(buffer);
139: if (pos < length) {
140: break;
141: }
142: if (length < MAXLENGTH) {
143: length <<= 1;
144: }
145: }
146:
147: // System.out.println("\ncount " + count + " bytes from " + stream);
148:
149: byte[] data = new byte[count];
150: pos = 0;
151: for (int i = 0; i < vec.size(); ++i) {
152: byte[] buf = (byte[]) vec.get(i);
153: int len = Math.min(buf.length, count - pos);
154: System.arraycopy(buf, 0, data, pos, len);
155: pos += len;
156: }
157: // assert pos==count;
158: return data;
159: } catch (IOException e) {
160: throw new MissingResourceException(e.getMessage(), "", "");
161: }
162: }
163:
164: private static char[] readToEOS(InputStreamReader stream) {
165: // As of 3.0 this method reads streams of length 41990..41994
166: // from the core data. The IBM 1.4 UTF8 converter doesn't
167: // handle buffering reliably (it throws an exception) so we
168: // are forced to read everything in one chunk.
169: try {
170: int length = 0x10000; // smallest 2^n >= max stream len
171: final int MAXLENGTH = 0x40000000;
172: int n;
173: char[] buffer;
174: for (;;) {
175: buffer = new char[length];
176: n = stream.read(buffer, 0, length);
177: if (n >= 0 && n < length) {
178: break;
179: }
180: if (length < MAXLENGTH) {
181: stream.reset();
182: length <<= 1;
183: } else {
184: throw new IllegalStateException(
185: "maximum input stream length exceeded");
186: }
187: }
188:
189: // System.out.println("\ncount " + n + " chars from " + stream);
190:
191: char[] data = new char[n];
192: System.arraycopy(buffer, 0, data, 0, n);
193: return data;
194: } catch (IOException e) {
195: throw new MissingResourceException(e.getMessage(), "", "");
196: }
197: }
198:
199: /*
200: public static class CompressedString implements RedirectedResource{
201: private String expanded=null;
202: private String compressed=null;
203: public CompressedString(String str){
204: compressed=str;
205: }
206: public Object getResource(Object obj){
207: if(compressed==null){
208: return null;
209: }
210: if(expanded==null){
211: expanded= new String(Utility.RLEStringToCharArray(compressed));
212: }
213: return expanded;
214: }
215: }
216: */
217: public static class CompressedBinary implements RedirectedResource {
218: private byte[] expanded = null;
219: private String compressed = null;
220:
221: public CompressedBinary(String str) {
222: compressed = str;
223: }
224:
225: public Object getResource(Object obj) {
226: if (compressed == null) {
227: return new byte[0];
228: }
229:
230: if (expanded == null) {
231: expanded = Utility.RLEStringToByteArray(compressed);
232: }
233: return expanded == null ? new byte[0] : expanded;
234: }
235:
236: }
237:
238: private interface RedirectedResource {
239: public Object getResource(Object obj);
240: }
241:
242: public static class ResourceBinary implements RedirectedResource {
243: private byte[] expanded = null;
244: private String resName = null;
245:
246: public ResourceBinary(String name) {
247: resName = "data/" + name;
248: }
249:
250: public Object getResource(Object obj) {
251: if (expanded == null) {
252: InputStream stream = ICUData.getStream(resName);
253: if (stream != null) {
254: //throw new MissingResourceException("",obj.getClass().getName(),resName);
255: expanded = readToEOS(stream);
256: return expanded;
257: }
258: }
259: return "";
260: }
261: }
262:
263: public static class ResourceString implements RedirectedResource {
264: private char[] expanded = null;
265: private String resName = null;
266:
267: public ResourceString(String name) {
268: resName = "data/" + name;
269: }
270:
271: public Object getResource(Object obj) {
272: if (expanded == null) {
273: // Resource strings are always UTF-8
274: InputStream stream = ICUData.getStream(resName);
275: if (stream != null) {
276: //throw new MissingResourceException("",obj.getClass().getName(),resName);
277:
278: try {
279: InputStreamReader reader = new InputStreamReader(
280: stream, ENCODING);
281: expanded = readToEOS(reader);
282: } catch (UnsupportedEncodingException ex) {
283: throw new RuntimeException(
284: "Could open converter for encoding: "
285: + ENCODING);
286: }
287: return new String(expanded);
288: }
289:
290: }
291: return "";
292: }
293: }
294:
295: private static final char RES_PATH_SEP_CHAR = '/';
296:
297: public static class Alias {
298: public Alias(String path) {
299: pathToResource = path;
300: }
301:
302: private String pathToResource;
303:
304: private Object getResource(String className, String parentKey,
305: int index, Hashtable visited) {
306: String packageName = null, bundleName = null, locale = null, keyPath = null;
307:
308: if (pathToResource.indexOf(RES_PATH_SEP_CHAR) == 0) {
309: int i = pathToResource.indexOf(RES_PATH_SEP_CHAR, 1);
310: int j = pathToResource
311: .indexOf(RES_PATH_SEP_CHAR, i + 1);
312: bundleName = pathToResource.substring(1, i);
313: locale = pathToResource.substring(i + 1);
314: if (j != -1) {
315: locale = pathToResource.substring(i + 1, j);
316: keyPath = pathToResource.substring(j + 1,
317: pathToResource.length());
318: }
319: //there is a path included
320: if (bundleName.equals(ICUDATA)) {
321: bundleName = ICU_BUNDLE_NAME;
322: packageName = ICU_PACKAGE_NAME;
323: }
324:
325: } else {
326: //no path start with locale
327: int i = pathToResource.indexOf(RES_PATH_SEP_CHAR);
328: //If this is a bundle with locale name following it
329: //then it should be of type <bundle name>_<locale>
330: //if not we donot guarantee that this will work
331: int j = className.lastIndexOf(".");
332: packageName = className.substring(0, j);
333: int underScoreIndex = className.indexOf("_");
334: if (underScoreIndex >= 0) {
335: bundleName = className.substring(j + 1, className
336: .indexOf("_"));
337: } else {
338: bundleName = className.substring(j + 1, className
339: .length());
340: }
341: keyPath = pathToResource.substring(i + 1);
342:
343: if (i != -1) {
344: locale = pathToResource.substring(0, i);
345: } else {
346: locale = keyPath;
347: keyPath = parentKey;
348: if (locale == null || locale.equals("root")) {
349: className = packageName + "." + bundleName;
350: } else {
351: className = packageName + "." + bundleName
352: + "_" + locale;
353: }
354:
355: }
356:
357: }
358:
359: ResourceBundle bundle = null;
360: // getResourceBundle guarantees that the CLASSPATH will be searched
361: // for loading the resource with name <bundleName>_<localeName>.class
362: if (locale == null || locale.equals("root")) {
363: bundle = ICULocaleData.getResourceBundle(packageName,
364: bundleName, "");
365: } else {
366: bundle = ICULocaleData.getResourceBundle(packageName,
367: bundleName, locale);
368: }
369:
370: return findResource(bundle, className, parentKey, index,
371: keyPath, visited);
372:
373: }
374:
375: private Object findResource(Object[][] contents, String key) {
376: for (int i = 0; i < contents.length; ++i) {
377: // key must be non-null String, value must be non-null
378: String tempKey = (String) contents[i][0];
379: Object value = contents[i][1];
380: if (tempKey == null || value == null) {
381: throw new NullPointerException();
382: }
383: if (tempKey.equals(key)) {
384: return value;
385: }
386: }
387: return null;
388: }
389:
390: private Object findResource(Object o, String[] keys, int start,
391: int index) {
392: Object obj = o;
393: if (start < keys.length && keys[start] != null) {
394: if (obj instanceof Object[][]) {
395: obj = findResource((Object[][]) obj, keys[start]);
396: } else if (obj instanceof Object[]
397: && isIndex(keys[start])) {
398: obj = ((Object[]) obj)[getIndex(keys[start])];
399: }
400: if (start + 1 < keys.length && keys[start + 1] != null) {
401: obj = findResource(obj, keys, start + 1, index);
402: }
403: } else {
404: //try to find the corresponding index resource
405: if (index >= 0) {
406: if (obj instanceof Object[][]) {
407: obj = findResource((Object[][]) obj, Integer
408: .toString(index));
409: } else if (obj instanceof Object[]) {
410: obj = ((Object[]) obj)[index];
411: }
412: }
413: }
414: return obj;
415: }
416:
417: private Object findResource(ResourceBundle bundle,
418: String className, String requestedKey, int index,
419: String aliasKey, Hashtable visited) {
420:
421: if (aliasKey != null
422: && visited.get(className + aliasKey) != null) {
423: throw new MissingResourceException(
424: "Circular Aliases in bundle.", bundle
425: .getClass().getName(), requestedKey);
426: }
427: if (aliasKey == null) {
428: // currently we do an implicit key lookup
429: // return ((ICUListResourceBundle)bundle).getContents();
430: aliasKey = requestedKey;
431: }
432:
433: visited.put(className + requestedKey, "");
434:
435: String[] keys = split(aliasKey, RES_PATH_SEP_CHAR);
436: Object o = null;
437: if (keys.length > 0) {
438: o = bundle.getObject(keys[0]);
439: o = findResource(o, keys, 1, index);
440: }
441: o = resolveAliases(o, className, aliasKey, visited);
442: return o;
443: }
444:
445: private Object resolveAliases(Object o, String className,
446: String key, Hashtable visited) {
447: if (o instanceof Object[][]) {
448: o = resolveAliases((Object[][]) o, className, key,
449: visited);
450: } else if (o instanceof Object[]) {
451: o = resolveAliases((Object[]) o, className, key,
452: visited);
453: } else if (o instanceof Alias) {
454: return ((Alias) o).getResource(className, key, -1,
455: visited);
456: }
457: return o;
458: }
459:
460: private Object resolveAliases(Object[][] o, String className,
461: String key, Hashtable visited) {
462: int i = 0;
463: while (i < o.length) {
464: o[i][1] = resolveAliases(o[i][1], className, key,
465: visited);
466: i++;
467: }
468: return o;
469: }
470:
471: private Object resolveAliases(Object[] o, String className,
472: String key, Hashtable visited) {
473: int i = 0;
474: while (i < o.length) {
475: o[i] = resolveAliases(o[i], className, key, visited);
476: i++;
477: }
478: return o;
479: }
480:
481: }
482:
483: private static String[] split(String source, char delimiter) {
484:
485: char[] src = source.toCharArray();
486: int index = 0;
487: int numdelimit = 0;
488: // first count the number of delimiters
489: for (int i = 0; i < source.length(); i++) {
490: if (src[i] == delimiter) {
491: numdelimit++;
492: }
493: }
494: String[] values = null;
495: values = new String[numdelimit + 2];
496: // now split
497: int old = 0;
498: for (int j = 0; j < src.length; j++) {
499: if (src[j] == delimiter) {
500: values[index++] = new String(src, old, j - old);
501: old = j + 1/* skip after the delimiter*/;
502: }
503: }
504: if (old < src.length)
505: values[index++] = new String(src, old, src.length - old);
506: return values;
507: }
508:
509: /**
510: * This method performs multilevel fallback for fetching items from the bundle
511: * e.g:
512: * If resource is in the form
513: * de__PHONEBOOK{
514: * collations{
515: * default{ "phonebook"}
516: * }
517: * }
518: * If the value of "default" key needs to be accessed, then do:
519: * <code>
520: * ResourceBundle bundle = new ResourceBundle(getLocaleFromString("de__PHONEBOOK"));
521: * Object result = null;
522: * if(bundle instanceof ICUListResourceBundle){
523: * result = ((ICUListResourceBundle) bundle).getObjectWithFallback("collations/default");
524: * }
525: * </code>
526: * @param path The path to the required resource key
527: * @return Object represented by the key
528: * @exception MissingResourceException
529: */
530: public final Object getObjectWithFallback(String path)
531: throws MissingResourceException {
532: String[] keys = split(path, RES_PATH_SEP_CHAR);
533: Object result = null;
534: ICUListResourceBundle actualBundle = this ;
535:
536: // now recuse to pick up sub levels of the items
537: result = findResourceWithFallback(keys, actualBundle);
538:
539: if (result == null) {
540: throw new MissingResourceException(
541: "Could not find the resource in ", this .getClass()
542: .getName(), path);
543: }
544: return result;
545: }
546:
547: private Object findResourceWithFallback(String[] keys,
548: ICUListResourceBundle actualBundle) {
549:
550: Object obj = null;
551:
552: while (actualBundle != null) {
553: // get the top level resource
554: // getObject is a method on the ResourceBundle class that
555: // performs the normal fallback
556: obj = actualBundle.getObject(keys[0], actualBundle);
557:
558: // now find the bundle from the actual bundle
559: // if this bundle does not contain the top level resource,
560: // then we can be sure that it does not contain the sub elements
561: obj = findResourceWithFallback(obj, keys, 1, 0);
562: // did we get the contents? the break
563: if (obj != null) {
564: break;
565: }
566: // if not try the parent bundle
567: actualBundle = (ICUListResourceBundle) actualBundle.parent;
568:
569: }
570:
571: return obj;
572: }
573:
574: private Object findResourceWithFallback(Object o, String[] keys,
575: int start, int index) {
576: Object obj = o;
577:
578: if (start < keys.length && keys[start] != null) {
579: if (obj instanceof Object[][]) {
580: obj = findResourceWithFallback((Object[][]) obj,
581: keys[start]);
582: } else if (obj instanceof Object[] && isIndex(keys[start])) {
583: obj = ((Object[]) obj)[getIndex(keys[start])];
584: }
585: if (start + 1 < keys.length && keys[start + 1] != null) {
586: obj = findResourceWithFallback(obj, keys, start + 1,
587: index);
588: }
589: } else {
590: //try to find the corresponding index resource
591: if (index >= 0) {
592: if (obj instanceof Object[][]) {
593: obj = findResourceWithFallback((Object[][]) obj,
594: Integer.toString(index));
595: } else if (obj instanceof Object[]) {
596: obj = ((Object[]) obj)[index];
597: }
598: }
599: }
600:
601: return obj;
602: }
603:
604: private Object findResourceWithFallback(Object[][] cnts, String key) {
605: Object obj = null;
606:
607: for (int i = 0; i < cnts.length; ++i) {
608: // key must be non-null String
609: String tempKey = (String) cnts[i][0];
610: obj = cnts[i][1];
611: if (tempKey != null && tempKey.equals(key)) {
612: return obj;
613: }
614: }
615:
616: return null;
617: }
618:
619: private final Object getObject(String key,
620: ICUListResourceBundle actualBundle) {
621: Object obj = handleGetObject(key);
622: if (obj == null) {
623: ICUListResourceBundle p = (ICUListResourceBundle) this .parent;
624: while (p != null) {
625: obj = p.handleGetObject(key);
626: if (obj != null) {
627: actualBundle = p;
628: break;
629: }
630: p = (ICUListResourceBundle) p.parent;
631: }
632: }
633: return obj;
634: }
635:
636: private static boolean isIndex(String s) {
637: if (s.length() == 1) {
638: char c = s.charAt(0);
639: return Character.isDigit(c);
640: }
641: return false;
642: }
643:
644: private static int getIndex(String s) {
645: if (s.length() == 1) {
646: char c = s.charAt(0);
647: if (Character.isDigit(c)) {
648: return Integer.valueOf(s).intValue();
649: }
650: }
651: return -1;
652: }
653: }
|