0001: //##header
0002: /*
0003: * *****************************************************************************
0004: * Copyright (C) 2005-2006, International Business Machines Corporation and * others.
0005: * All Rights Reserved. *
0006: * *****************************************************************************
0007: */
0008:
0009: package com.ibm.icu.impl;
0010:
0011: import java.io.BufferedReader;
0012: import java.io.InputStream;
0013: import java.io.InputStreamReader;
0014: import java.io.IOException;
0015: import java.lang.ref.SoftReference;
0016: import java.net.URL;
0017: import java.util.ArrayList;
0018: import java.util.Arrays;
0019: import java.util.Collections;
0020: import java.util.Enumeration;
0021: import java.util.HashMap;
0022: import java.util.HashSet;
0023: import java.util.Locale;
0024: import java.util.Map;
0025: import java.util.MissingResourceException;
0026: import java.util.Set;
0027: import java.util.Vector;
0028:
0029: //#ifndef FOUNDATION
0030: import java.nio.ByteBuffer; //#else
0031: //##import com.ibm.icu.impl.ByteBuffer;
0032: //#endif
0033: import com.ibm.icu.impl.URLHandler.URLVisitor;
0034: import com.ibm.icu.util.StringTokenizer;
0035: import com.ibm.icu.util.ULocale;
0036: import com.ibm.icu.util.UResourceBundle;
0037: import com.ibm.icu.util.UResourceTypeMismatchException;
0038: import com.ibm.icu.util.VersionInfo;
0039:
0040: public abstract class ICUResourceBundle extends UResourceBundle {
0041: /**
0042: * The data path to be used with getBundleInstance API
0043: * @draft ICU 3.0
0044: */
0045: protected static final String ICU_DATA_PATH = "com/ibm/icu/impl/";
0046: /**
0047: * The data path to be used with getBundleInstance API
0048: * @draft ICU 3.0
0049: */
0050: public static final String ICU_BUNDLE = "data/icudt"
0051: + VersionInfo.ICU_DATA_VERSION;
0052:
0053: /**
0054: * The base name of ICU data to be used with getBundleInstance API
0055: * @draft ICU 3.0
0056: */
0057: public static final String ICU_BASE_NAME = ICU_DATA_PATH
0058: + ICU_BUNDLE;
0059:
0060: /**
0061: * The base name of collation data to be used with getBundleInstance API
0062: * @draft ICU 3.0
0063: */
0064: public static final String ICU_COLLATION_BASE_NAME = ICU_BASE_NAME
0065: + "/coll";
0066:
0067: /**
0068: * The base name of rbbi data to be used with getData API
0069: * @draft ICU 3.6
0070: */
0071: public static final String ICU_BRKITR_NAME = "/brkitr";
0072:
0073: /**
0074: * The base name of rbbi data to be used with getBundleInstance API
0075: * @draft ICU 3.6
0076: */
0077: public static final String ICU_BRKITR_BASE_NAME = ICU_BASE_NAME
0078: + ICU_BRKITR_NAME;
0079:
0080: /**
0081: * The base name of rbnf data to be used with getBundleInstance API
0082: * @draft ICU 3.0
0083: */
0084: public static final String ICU_RBNF_BASE_NAME = ICU_BASE_NAME
0085: + "/rbnf";
0086:
0087: /**
0088: * The base name of transliterator data to be used with getBundleInstance API
0089: * @draft ICU 3.0
0090: */
0091: public static final String ICU_TRANSLIT_BASE_NAME = ICU_BASE_NAME
0092: + "/translit";
0093:
0094: /**
0095: * The class loader constant to be used with getBundleInstance API
0096: * @draft ICU 3.0
0097: */
0098: public static final ClassLoader ICU_DATA_CLASS_LOADER;
0099: static {
0100: ClassLoader loader = ICUData.class.getClassLoader();
0101: if (loader == null) { // boot class loader
0102: loader = ClassLoader.getSystemClassLoader();
0103: }
0104: ICU_DATA_CLASS_LOADER = loader;
0105: }
0106:
0107: /**
0108: * The name of the resource containing the installed locales
0109: * @draft ICU 3.0
0110: */
0111: protected static final String INSTALLED_LOCALES = "InstalledLocales";
0112:
0113: /**
0114: * Resource type constant for "no resource".
0115: * @draft ICU 3.0
0116: */
0117: public static final int NONE = -1;
0118:
0119: /**
0120: * Resource type constant for strings.
0121: * @draft ICU 3.0
0122: */
0123: public static final int STRING = 0;
0124:
0125: /**
0126: * Resource type constant for binary data.
0127: * @draft ICU 3.0
0128: */
0129: public static final int BINARY = 1;
0130:
0131: /**
0132: * Resource type constant for tables of key-value pairs.
0133: * @draft ICU 3.0
0134: */
0135: public static final int TABLE = 2;
0136:
0137: /**
0138: * Resource type constant for aliases;
0139: * internally stores a string which identifies the actual resource
0140: * storing the data (can be in a different resource bundle).
0141: * Resolved internally before delivering the actual resource through the API.
0142: * @draft ICU 3.0
0143: * @internal
0144: */
0145: protected static final int ALIAS = 3;
0146:
0147: /**
0148: * Internal use only.
0149: * Alternative resource type constant for tables of key-value pairs.
0150: * Never returned by getType().
0151: * @internal
0152: * @draft ICU 3.0
0153: */
0154: protected static final int TABLE32 = 4;
0155:
0156: /**
0157: * Resource type constant for a single 28-bit integer, interpreted as
0158: * signed or unsigned by the getInt() function.
0159: * @see #getInt
0160: * @draft ICU 3.0
0161: */
0162: public static final int INT = 7;
0163:
0164: /**
0165: * Resource type constant for arrays of resources.
0166: * @draft ICU 3.0
0167: */
0168: public static final int ARRAY = 8;
0169:
0170: /**
0171: * Resource type constant for vectors of 32-bit integers.
0172: * @see #getIntVector
0173: * @draft ICU 3.0
0174: */
0175: public static final int INT_VECTOR = 14;
0176:
0177: public static final int FROM_FALLBACK = 1, FROM_ROOT = 2,
0178: FROM_DEFAULT = 3, FROM_LOCALE = 4;
0179:
0180: private int loadingStatus = -1;
0181:
0182: public void setLoadingStatus(int newStatus) {
0183: loadingStatus = newStatus;
0184: }
0185:
0186: /**
0187: * Returns the loading status of a particular resource.
0188: *
0189: * @return FROM_FALLBACK if the resource is fetched from fallback bundle
0190: * FROM_ROOT if the resource is fetched from root bundle.
0191: * FROM_DEFAULT if the resource is fetched from the default locale.
0192: */
0193: public int getLoadingStatus() {
0194: return loadingStatus;
0195: }
0196:
0197: /**
0198: * Get the noFallback flag specified in the loaded bundle.
0199: * @return The noFallback flag.
0200: */
0201: protected boolean getNoFallback() {
0202: return false;
0203: }
0204:
0205: /**
0206: * Return the version number associated with this UResourceBundle as an
0207: * VersionInfo object.
0208: * @return VersionInfo object containing the version of the bundle
0209: * @draft ICU 3.0
0210: */
0211: public VersionInfo getVersion() {
0212: return null;
0213: }
0214:
0215: /**
0216: * Returns a string from a string resource type
0217: *
0218: * @return a string
0219: * @see #getBinary
0220: * @see #getIntVector
0221: * @see #getInt
0222: * @throws MissingResourceException
0223: * @throws UResourceTypeMismatchException
0224: * @draft ICU 3.0
0225: */
0226: public String getString() {
0227: throw new UResourceTypeMismatchException("");
0228: }
0229:
0230: /**
0231: * @internal ICU 3.0
0232: */
0233: public String[] getStringArray() {
0234: throw new UResourceTypeMismatchException("");
0235: }
0236:
0237: /**
0238: * Returns a string from a string resource type
0239: * @param key The key whose values needs to be fetched
0240: * @return a string
0241: * @see #getBinary
0242: * @see #getIntVector
0243: * @see #getInt
0244: * @throws MissingResourceException
0245: * @throws UResourceTypeMismatchException
0246: * @draft ICU 3.0
0247: */
0248: // public String getString(String key) {
0249: // throw new UResourceTypeMismatchException("");
0250: // }
0251: /**
0252: * Returns a binary data from a binary resource.
0253: *
0254: * @return a pointer to a chuck of unsigned bytes which live in a memory mapped/DLL file.
0255: * @see #getIntVector
0256: * @see #getInt
0257: * @throws MissingResourceException
0258: * @throws UResourceTypeMismatchException
0259: * @draft ICU 3.0
0260: */
0261: public ByteBuffer getBinary() {
0262: throw new UResourceTypeMismatchException("");
0263: }
0264:
0265: /**
0266: * Returns a binary data from a binary resource.
0267: *
0268: * @param ba Establish the return type from this function. The value of the parameter
0269: * is not used; a null variable is OK. The thing that matters is the type
0270: * of the parameter
0271: * @return an array unsigned bytes containing the binary data from the resource.
0272: * @see #getIntVector
0273: * @see #getInt
0274: * @throws MissingResourceException
0275: * @throws UResourceTypeMismatchException
0276: * @draft ICU 3.6
0277: */
0278: public byte[] getBinary(byte[] ba) {
0279: throw new UResourceTypeMismatchException("");
0280: }
0281:
0282: /**
0283: * Returns a 32 bit integer array from a resource.
0284: *
0285: * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.
0286: * @see #getBinary
0287: * @see #getInt
0288: * @throws MissingResourceException
0289: * @throws UResourceTypeMismatchException
0290: * @draft ICU 3.0
0291: */
0292: public int[] getIntVector() {
0293: throw new UResourceTypeMismatchException("");
0294: }
0295:
0296: /**
0297: * Returns a signed integer from a resource.
0298: *
0299: * @return an integer value
0300: * @see #getIntVector
0301: * @see #getBinary
0302: * @throws MissingResourceException
0303: * @throws UResourceTypeMismatchException
0304: * @stable ICU 2.0
0305: */
0306: public int getInt() {
0307: throw new UResourceTypeMismatchException("");
0308: }
0309:
0310: /**
0311: * Returns a unsigned integer from a resource.
0312: * This integer is originally 28 bit and the sign gets propagated.
0313: *
0314: * @return an integer value
0315: * @see #getIntVector
0316: * @see #getBinary
0317: * @throws MissingResourceException
0318: * @throws UResourceTypeMismatchException
0319: * @stable ICU 2.0
0320: */
0321: public int getUInt() {
0322: throw new UResourceTypeMismatchException("");
0323: }
0324:
0325: /**
0326: * Returns the size of a resource. Size for scalar types is always 1,
0327: * and for vector/table types is the number of child resources.
0328: * <br><b><font color='red'>Warning: </font></b> Integer array is treated as a scalar type. There are no
0329: * APIs to access individual members of an integer array. It
0330: * is always returned as a whole.
0331: * @return number of resources in a given resource.
0332: * @draft ICU 3.0
0333: */
0334: public int getSize() {
0335: return size;
0336: }
0337:
0338: /**
0339: * Returns the type of a resource.
0340: * Available types are {@link #INT INT}, {@link #ARRAY ARRAY},
0341: * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR},
0342: * {@link #STRING STRING}, {@link #TABLE TABLE}.
0343: *
0344: * @return type of the given resource.
0345: * @draft ICU 3.0
0346: */
0347: public int getType() {
0348: int type = ICUResourceBundleImpl.RES_GET_TYPE(resource);
0349: if (type == TABLE32) {
0350: return TABLE; //Mask the table32's real type
0351: }
0352: return type;
0353: }
0354:
0355: /**
0356: * Returns the key associated with a given resource. Not all the resources have a key - only
0357: * those that are members of a table.
0358: * @return a key associated to this resource, or NULL if it doesn't have a key
0359: * @draft ICU 3.0
0360: */
0361: public String getKey() {
0362: return key;
0363: }
0364:
0365: /**
0366: * Returns the iterator which iterates over this
0367: * resource bundle
0368: * @draft ICU 3.0
0369: */
0370: public ICUResourceBundleIterator getIterator() {
0371: return new ICUResourceBundleIterator(this );
0372: }
0373:
0374: /**
0375: * Returns the resource in a given resource at the specified index.
0376: *
0377: * @param index an index to the wanted resource.
0378: * @return the sub resource UResourceBundle object
0379: * @throws IndexOutOfBoundsException
0380: * @draft ICU 3.0
0381: */
0382: public ICUResourceBundle get(int index) {
0383: return getImpl(index, null, this );
0384: }
0385:
0386: protected ICUResourceBundle getImpl(int index, HashMap table,
0387: ICUResourceBundle requested) {
0388: ICUResourceBundle obj = handleGet(index, table, requested);
0389: if (obj == null) {
0390: obj = (ICUResourceBundle) getParent();
0391: if (obj != null) {
0392: obj = obj.getImpl(index, table, requested);
0393: }
0394: if (obj == null)
0395: throw new MissingResourceException(
0396: "Can't find resource for bundle "
0397: + this .getClass().getName() + ", key "
0398: + getKey(), this .getClass().getName(),
0399: getKey());
0400: }
0401: setLoadingStatus(obj, requested.getLocaleID());
0402: return obj;
0403: }
0404:
0405: // abstract UResourceBundle handleGetInt(int index);
0406:
0407: /**
0408: * Returns a resource in a given resource that has a given key.
0409: *
0410: * @param key a key associated with the wanted resource
0411: * @return a resource bundle object representing rhe resource
0412: * @throws MissingResourceException
0413: * @draft ICU 3.0
0414: */
0415: public ICUResourceBundle get(String key) {
0416: return getImpl(key, null, this );
0417: }
0418:
0419: protected ICUResourceBundle getImpl(String key, HashMap table,
0420: ICUResourceBundle requested) {
0421: ICUResourceBundle obj = handleGet(key, table, requested);
0422: if (obj == null) {
0423: obj = (ICUResourceBundle) getParent();
0424: if (obj != null) {
0425: //call the get method to recursively fetch the resource
0426: obj = obj.getImpl(key, table, requested);
0427: }
0428: if (obj == null) {
0429: String fullName = ICUResourceBundleReader.getFullName(
0430: getBaseName(), getLocaleID());
0431: throw new MissingResourceException(
0432: "Can't find resource for bundle " + fullName
0433: + ", key " + key, this .getClass()
0434: .getName(), key);
0435: }
0436: }
0437: setLoadingStatus(obj, requested.getLocaleID());
0438: return obj;
0439: }
0440:
0441: private static void setLoadingStatus(ICUResourceBundle bundle,
0442: String requestedLocale) {
0443: String locale = bundle.getLocaleID();
0444: if (locale.equals("root")) {
0445: bundle.setLoadingStatus(FROM_ROOT);
0446: return;
0447: }
0448: if (locale.equals(requestedLocale)) {
0449: bundle.setLoadingStatus(FROM_LOCALE);
0450: } else {
0451: bundle.setLoadingStatus(FROM_FALLBACK);
0452: }
0453: }
0454:
0455: /**
0456: * Returns the string in a given resource at the specified index.
0457: *
0458: * @param index an index to the wanted string.
0459: * @return a string which lives in the resource.
0460: * @throws IndexOutOfBoundsException
0461: * @throws UResourceTypeMismatchException
0462: * @draft ICU 3.0
0463: */
0464: public String getString(int index) {
0465: ICUResourceBundle temp = get(index);
0466: if (temp.getType() == STRING) {
0467: return temp.getString();
0468: }
0469: throw new UResourceTypeMismatchException("");
0470: }
0471:
0472: /**
0473: * Returns the parent bundle of this bundle
0474: * @return UResourceBundle the parent of this bundle. Returns null if none
0475: * @draft ICU 3.0
0476: */
0477: public abstract UResourceBundle getParent();
0478:
0479: /**
0480: * Returns the locale id of this bundle as String
0481: * @return String locale id
0482: * @draft ICU 3.4
0483: */
0484: protected abstract String getLocaleID();
0485:
0486: /**
0487: * Returns a functionally equivalent locale, considering keywords as well, for the specified keyword.
0488: * @param baseName resource specifier
0489: * @param resName top level resource to consider (such as "collations")
0490: * @param keyword a particular keyword to consider (such as "collation" )
0491: * @param locID The requested locale
0492: * @param fillinIsAvailable If non-null, 1-element array of fillin parameter that indicates whether the
0493: * requested locale was available. The locale is defined as 'available' if it physically
0494: * exists within the specified tree.
0495: * @return the locale
0496: * @internal ICU 3.0
0497: */
0498: public static final ULocale getFunctionalEquivalent(
0499: String baseName, String resName, String keyword,
0500: ULocale locID, boolean fillinIsAvailable[]) {
0501: String kwVal = locID.getKeywordValue(keyword);
0502: String baseLoc = locID.getBaseName();
0503: String defStr = null;
0504: ULocale parent = new ULocale(baseLoc);
0505: ULocale found = locID;
0506: ULocale defLoc = null; // locale where default (found) resource is
0507: boolean lookForDefault = false; // true if kwVal needs to be set
0508: ULocale fullBase = null; // base locale of found (target) resource
0509: int defDepth = 0; // depth of 'default' marker
0510: int resDepth = 0; // depth of found resource;
0511: if (fillinIsAvailable != null) {
0512: fillinIsAvailable[0] = true;
0513: }
0514:
0515: if ((kwVal == null) || (kwVal.length() == 0)
0516: || kwVal.equals(DEFAULT_TAG)) {
0517: kwVal = ""; // default tag is treated as no keyword
0518: lookForDefault = true;
0519: }
0520:
0521: // Check top level locale first
0522: ICUResourceBundle r = null;
0523:
0524: r = (ICUResourceBundle) UResourceBundle.getBundleInstance(
0525: baseName, parent);
0526: found = r.getULocale();
0527: if (fillinIsAvailable != null) {
0528: if (!found.equals(parent)) {
0529: fillinIsAvailable[0] = false;
0530: }
0531: }
0532: // determine in which locale (if any) the currently relevant 'default' is
0533: do {
0534: try {
0535: ICUResourceBundle irb = r.get(resName);
0536: defStr = irb.getString(DEFAULT_TAG);
0537: if (lookForDefault == true) {
0538: kwVal = defStr;
0539: lookForDefault = false;
0540: }
0541: defLoc = r.getULocale();
0542: } catch (MissingResourceException t) {
0543: // Ignore error and continue search.
0544: }
0545: if (defLoc == null) {
0546: r = (ICUResourceBundle) r.getParent();
0547: defDepth++;
0548: }
0549: } while ((r != null) && (defLoc == null));
0550:
0551: // Now, search for the named resource
0552: parent = new ULocale(baseLoc);
0553: r = (ICUResourceBundle) UResourceBundle.getBundleInstance(
0554: baseName, parent);
0555: // determine in which locale (if any) the named resource is located
0556: do {
0557: try {
0558: ICUResourceBundle irb = r.get(resName);
0559: /* UResourceBundle urb = */irb.get(kwVal);
0560: fullBase = irb.getULocale();
0561: // If the get() completed, we have the full base locale
0562: // If we fell back to an ancestor of the old 'default',
0563: // we need to re calculate the "default" keyword.
0564: if ((fullBase != null) && ((resDepth) > defDepth)) {
0565: defStr = irb.getString(DEFAULT_TAG);
0566: defLoc = r.getULocale();
0567: defDepth = resDepth;
0568: }
0569: } catch (MissingResourceException t) {
0570: // Ignore error,
0571: }
0572: if (fullBase == null) {
0573: r = (ICUResourceBundle) r.getParent();
0574: resDepth++;
0575: }
0576: } while ((r != null) && (fullBase == null));
0577:
0578: if (fullBase == null && // Could not find resource 'kwVal'
0579: (defStr != null) && // default was defined
0580: !defStr.equals(kwVal)) { // kwVal is not default
0581: // couldn't find requested resource. Fall back to default.
0582: kwVal = defStr; // Fall back to default.
0583: parent = new ULocale(baseLoc);
0584: r = (ICUResourceBundle) UResourceBundle.getBundleInstance(
0585: baseName, parent);
0586: resDepth = 0;
0587: // determine in which locale (if any) the named resource is located
0588: do {
0589: try {
0590: ICUResourceBundle irb = r.get(resName);
0591: UResourceBundle urb = irb.get(kwVal);
0592:
0593: // if we didn't fail before this..
0594: fullBase = r.getULocale();
0595:
0596: // If the fetched item (urb) is in a different locale than our outer locale (r/fullBase)
0597: // then we are in a 'fallback' situation. treat as a missing resource situation.
0598: if (!fullBase.toString().equals(
0599: urb.getLocale().toString())) {
0600: fullBase = null; // fallback condition. Loop and try again.
0601: }
0602:
0603: // If we fell back to an ancestor of the old 'default',
0604: // we need to re calculate the "default" keyword.
0605: if ((fullBase != null) && ((resDepth) > defDepth)) {
0606: defStr = irb.getString(DEFAULT_TAG);
0607: defLoc = r.getULocale();
0608: defDepth = resDepth;
0609: }
0610: } catch (MissingResourceException t) {
0611: // Ignore error, continue search.
0612: }
0613: if (fullBase == null) {
0614: r = (ICUResourceBundle) r.getParent();
0615: resDepth++;
0616: }
0617: } while ((r != null) && (fullBase == null));
0618: }
0619:
0620: if (fullBase == null) {
0621: throw new MissingResourceException(
0622: "Could not find locale containing requested or default keyword.",
0623: baseName, keyword + "=" + kwVal);
0624: }
0625:
0626: if (defStr.equals(kwVal) // if default was requested and
0627: && resDepth <= defDepth) { // default was set in same locale or child
0628: return fullBase; // Keyword value is default - no keyword needed in locale
0629: } else {
0630: return new ULocale(fullBase.toString() + "@" + keyword
0631: + "=" + kwVal);
0632: }
0633: }
0634:
0635: /**
0636: * Given a tree path and keyword, return a string enumeration of all possible values for that keyword.
0637: * @param baseName resource specifier
0638: * @param keyword a particular keyword to consider, must match a top level resource name
0639: * within the tree. (i.e. "collations")
0640: * @internal ICU 3.0
0641: */
0642: public static final String[] getKeywordValues(String baseName,
0643: String keyword) {
0644: Set keywords = new HashSet();
0645: ULocale locales[] = createULocaleList(baseName,
0646: ICU_DATA_CLASS_LOADER);
0647: int i;
0648:
0649: for (i = 0; i < locales.length; i++) {
0650: try {
0651: UResourceBundle b = UResourceBundle.getBundleInstance(
0652: baseName, locales[i]);
0653: // downcast to ICUResourceBundle?
0654: ICUResourceBundle irb = (ICUResourceBundle) (b
0655: .getObject(keyword));
0656: Enumeration e = irb.getKeys();
0657: Object s;
0658: while (e.hasMoreElements()) {
0659: s = e.nextElement();
0660: if ((s instanceof String) && !DEFAULT_TAG.equals(s)) {
0661: // don't add 'default' items
0662: keywords.add(s);
0663: }
0664: }
0665: } catch (Throwable t) {
0666: //System.err.println("Error in - " + new Integer(i).toString()
0667: // + " - " + t.toString());
0668: // ignore the err - just skip that resource
0669: }
0670: }
0671: return (String[]) keywords.toArray(new String[0]);
0672: }
0673:
0674: /**
0675: * This method performs multilevel fallback for fetching items from the
0676: * bundle e.g: If resource is in the form de__PHONEBOOK{ collations{
0677: * default{ "phonebook"} } } If the value of "default" key needs to be
0678: * accessed, then do: <code>
0679: * UResourceBundle bundle = UResourceBundle.getBundleInstance("de__PHONEBOOK");
0680: * ICUResourceBundle result = null;
0681: * if(bundle instanceof ICUListResourceBundle){
0682: * result = ((ICUListResourceBundle) bundle).getWithFallback("collations/default");
0683: * }
0684: * </code>
0685: *
0686: * @param path
0687: * The path to the required resource key
0688: * @return resource represented by the key
0689: * @exception MissingResourceException
0690: */
0691: public ICUResourceBundle getWithFallback(String path)
0692: throws MissingResourceException {
0693: ICUResourceBundle result = null;
0694: ICUResourceBundle actualBundle = this ;
0695:
0696: // now recuse to pick up sub levels of the items
0697: result = findResourceWithFallback(path, actualBundle, null);
0698:
0699: if (result == null) {
0700: throw new MissingResourceException(
0701: "Can't find resource for bundle "
0702: + this .getClass().getName() + ", key "
0703: + getType(), path, getKey());
0704: }
0705: return result;
0706: }
0707:
0708: // will throw type mismatch exception if the resource is not a string
0709: public String getStringWithFallback(String path)
0710: throws MissingResourceException {
0711: return getWithFallback(path).getString();
0712: }
0713:
0714: /**
0715: * Return a set of the locale names supported by a collection of resource
0716: * bundles.
0717: *
0718: * @param bundlePrefix the prefix of the resource bundles to use.
0719: */
0720: public static Set getAvailableLocaleNameSet(String bundlePrefix) {
0721: return getAvailEntry(bundlePrefix).getLocaleNameSet();
0722: }
0723:
0724: /**
0725: * Return a set of all the locale names supported by a collection of
0726: * resource bundles.
0727: */
0728: public static Set getFullLocaleNameSet() {
0729: return getFullLocaleNameSet(ICU_BASE_NAME);
0730: }
0731:
0732: /**
0733: * Return a set of all the locale names supported by a collection of
0734: * resource bundles.
0735: *
0736: * @param bundlePrefix the prefix of the resource bundles to use.
0737: */
0738: public static Set getFullLocaleNameSet(String bundlePrefix) {
0739: return getAvailEntry(bundlePrefix).getFullLocaleNameSet();
0740: }
0741:
0742: /**
0743: * Return a set of the locale names supported by a collection of resource
0744: * bundles.
0745: */
0746: public static Set getAvailableLocaleNameSet() {
0747: return getAvailableLocaleNameSet(ICU_BASE_NAME);
0748: }
0749:
0750: /**
0751: * Get the set of Locales installed in the specified bundles.
0752: * @return the list of available locales
0753: * @draft ICU 3.0
0754: */
0755: public static final ULocale[] getAvailableULocales(String baseName) {
0756: return getAvailEntry(baseName).getULocaleList();
0757: }
0758:
0759: /**
0760: * Get the set of ULocales installed the base bundle.
0761: * @return the list of available locales
0762: * @draft ICU 3.0
0763: */
0764: public static final ULocale[] getAvailableULocales() {
0765: return getAvailableULocales(ICU_BASE_NAME);
0766: }
0767:
0768: /**
0769: * Get the set of Locales installed in the specified bundles.
0770: * @return the list of available locales
0771: * @draft ICU 3.0
0772: */
0773: public static final Locale[] getAvailableLocales(String baseName) {
0774: return getAvailEntry(baseName).getLocaleList();
0775: }
0776:
0777: /**
0778: * Get the set of Locales installed the base bundle.
0779: * @return the list of available locales
0780: * @draft ICU 3.0
0781: */
0782: public static final Locale[] getAvailableLocales() {
0783: return getAvailEntry(ICU_BASE_NAME).getLocaleList();
0784: }
0785:
0786: /**
0787: * Convert a list of ULocales to a list of Locales. ULocales with a script code will not be converted
0788: * since they cannot be represented as a Locale. This means that the two lists will <b>not</b> match
0789: * one-to-one, and that the returned list might be shorter than the input list.
0790: * @param ulocales a list of ULocales to convert to a list of Locales.
0791: * @return the list of converted ULocales
0792: * @draft ICU 3.0
0793: */
0794: public static final Locale[] getLocaleList(ULocale[] ulocales) {
0795: ArrayList list = new ArrayList();
0796: for (int i = 0; i < ulocales.length; i++) {
0797: // if the ULocale does not contain a script code
0798: // only then convert it to a Locale object
0799: if (ulocales[i].getScript().length() == 0) {
0800: list.add(ulocales[i].toLocale());
0801: }
0802: }
0803: return (Locale[]) list.toArray(new Locale[list.size()]);
0804: }
0805:
0806: public Enumeration getKeys() {
0807: initKeysVector();
0808: return keys.elements();
0809: }
0810:
0811: private Vector keys = null;
0812:
0813: private synchronized void initKeysVector() {
0814: if (keys != null) {
0815: return;
0816: }
0817: //ICUResourceBundle current = this;
0818: keys = new Vector();
0819: //while(current!=null){
0820: Enumeration e = this .handleGetKeys();
0821: while (e.hasMoreElements()) {
0822: String elem = (String) e.nextElement();
0823: if (!keys.contains(elem)) {
0824: keys.add(elem);
0825: }
0826: }
0827: // current = (ICUResourceBundle)current.getParent();
0828: //}
0829: }
0830:
0831: protected Enumeration handleGetKeys() {
0832: Vector keys = new Vector();
0833: ICUResourceBundle item = null;
0834: for (int i = 0; i < size; i++) {
0835: item = get(i);
0836: keys.add(item.getKey());
0837: }
0838: return keys.elements();
0839: }
0840:
0841: public static ICUResourceBundle createBundle(String baseName,
0842: String localeID, ClassLoader root) {
0843: ICUResourceBundle b = ICUResourceBundleImpl.createBundle(
0844: baseName, localeID, root);
0845: if (b == null) {
0846: throw new MissingResourceException(
0847: "Could not find the bundle " + baseName + "/"
0848: + localeID + ".res", "", "");
0849: }
0850: return b;
0851: }
0852:
0853: //====== protected members ==============
0854: protected String key;
0855: protected int size = 1;
0856: protected String resPath;
0857: protected long resource = RES_BOGUS;
0858: protected boolean isTopLevel = false;
0859:
0860: protected static final long UNSIGNED_INT_MASK = 0xffffffffL;
0861:
0862: protected static final long RES_BOGUS = 0xffffffff;
0863:
0864: protected ICUResourceBundle handleGet(String key, HashMap table,
0865: ICUResourceBundle requested) {
0866: throw new UResourceTypeMismatchException("");
0867: }
0868:
0869: protected ICUResourceBundle handleGet(int index, HashMap table,
0870: ICUResourceBundle requested) {
0871: throw new UResourceTypeMismatchException("");
0872: }
0873:
0874: /**
0875: * Returns the locale of this resource bundle. This method can be used after
0876: * a call to getBundle() to determine whether the resource bundle returned
0877: * really corresponds to the requested locale or is a fallback.
0878: *
0879: * @return the locale of this resource bundle
0880: */
0881: public Locale getLocale() {
0882: return getULocale().toLocale();
0883: }
0884:
0885: // this method is declared in ResourceBundle class
0886: // so cannot change the signature
0887: protected Object handleGetObject(String key) {
0888: return handleGetObjectImpl(key, this );
0889: }
0890:
0891: // To facilitate XPath style aliases we need a way to pass the reference
0892: // to requested locale. The only way I could figure out is to implement
0893: // the look up logic here. This has a disadvantage that if the client
0894: // loads an ICUResourceBundle, calls ResourceBundle.getObject method
0895: // with a key that does not exist in the bundle then the lookup is
0896: // done twice before throwing a MissingResourceExpection.
0897: private Object handleGetObjectImpl(String key,
0898: ICUResourceBundle requested) {
0899: Object obj = resolveObject(key, requested);
0900: if (obj == null) {
0901: ICUResourceBundle parent = (ICUResourceBundle) getParent();
0902: if (parent != null) {
0903: obj = parent.handleGetObjectImpl(key, requested);
0904: }
0905: if (obj == null)
0906: throw new MissingResourceException(
0907: "Can't find resource for bundle "
0908: + this .getClass().getName() + ", key "
0909: + key, this .getClass().getName(), key);
0910: }
0911: return obj;
0912: }
0913:
0914: private Object resolveObject(String key, ICUResourceBundle requested) {
0915: if (getType() == STRING) {
0916: return getString();
0917: }
0918: ICUResourceBundle obj = handleGet(key, requested);
0919: if (obj != null) {
0920: if (obj.getType() == STRING) {
0921: return obj.getString();
0922: }
0923: try {
0924: if (obj.getType() == ARRAY) {
0925: return obj.handleGetStringArray();
0926: }
0927: } catch (UResourceTypeMismatchException ex) {
0928: return obj;
0929: }
0930: }
0931: return obj;
0932: }
0933:
0934: protected ICUResourceBundle handleGet(int index,
0935: ICUResourceBundle requested) {
0936: return null;
0937: }
0938:
0939: protected ICUResourceBundle handleGet(String key,
0940: ICUResourceBundle requested) {
0941: return null;
0942: }
0943:
0944: protected String[] handleGetStringArray() {
0945: return null;
0946: }
0947:
0948: // ========== privates ==========
0949: private static final String ICU_RESOURCE_INDEX = "res_index";
0950:
0951: private static final String DEFAULT_TAG = "default";
0952:
0953: // Flag for enabling/disabling debugging code
0954: private static final boolean DEBUG = ICUDebug.enabled("localedata");
0955:
0956: // Cache for getAvailableLocales
0957: private static SoftReference GET_AVAILABLE_CACHE;
0958:
0959: private static final ULocale[] createULocaleList(String baseName,
0960: ClassLoader root) {
0961: // the canned list is a subset of all the available .res files, the idea
0962: // is we don't export them
0963: // all. gotta be a better way to do this, since to add a locale you have
0964: // to update this list,
0965: // and it's embedded in our binary resources.
0966: ICUResourceBundle bundle = (ICUResourceBundle) createBundle(
0967: baseName, ICU_RESOURCE_INDEX, root);
0968:
0969: bundle = bundle.get(INSTALLED_LOCALES);
0970: int length = bundle.getSize();
0971: int i = 0;
0972: ULocale[] locales = new ULocale[length];
0973: ICUResourceBundleIterator iter = bundle.getIterator();
0974: iter.reset();
0975: while (iter.hasNext()) {
0976: locales[i++] = new ULocale(iter.next().getKey());
0977: }
0978: bundle = null;
0979: return locales;
0980: }
0981:
0982: private static final Locale[] createLocaleList(String baseName) {
0983: ULocale[] ulocales = getAvailEntry(baseName).getULocaleList();
0984: return getLocaleList(ulocales);
0985: }
0986:
0987: private static final String[] createLocaleNameArray(
0988: String baseName, ClassLoader root) {
0989: ICUResourceBundle bundle = (ICUResourceBundle) createBundle(
0990: baseName, ICU_RESOURCE_INDEX, root);
0991: bundle = bundle.get(INSTALLED_LOCALES);
0992: int length = bundle.getSize();
0993: int i = 0;
0994: String[] locales = new String[length];
0995: ICUResourceBundleIterator iter = bundle.getIterator();
0996: iter.reset();
0997: while (iter.hasNext()) {
0998: locales[i++] = iter.next().getKey();
0999: }
1000: bundle = null;
1001: return locales;
1002: }
1003:
1004: private static final ArrayList createFullLocaleNameArray(
1005: final String baseName, final ClassLoader root) {
1006:
1007: ArrayList list = (ArrayList) java.security.AccessController
1008: .doPrivileged(new java.security.PrivilegedAction() {
1009: public Object run() {
1010: // WebSphere class loader will return null for a raw
1011: // directory name without trailing slash
1012: String bn = baseName.endsWith("/") ? baseName
1013: : baseName + "/";
1014:
1015: // look for prebuilt indices first
1016: try {
1017: InputStream s = root.getResourceAsStream(bn
1018: + ICU_RESOURCE_INDEX + ".txt");
1019: if (s != null) {
1020: ArrayList list = new ArrayList();
1021: BufferedReader br = new BufferedReader(
1022: new InputStreamReader(s,
1023: "ASCII"));
1024: String line;
1025: while ((line = br.readLine()) != null) {
1026: if (line.length() != 0
1027: && !line.startsWith("#")) {
1028: list.add(line);
1029: }
1030: }
1031: return list;
1032: }
1033: } catch (IOException e) {
1034: // swallow it
1035: }
1036:
1037: URL url = root.getResource(bn);
1038: URLHandler handler = URLHandler.get(url);
1039: if (handler != null) {
1040: final ArrayList list = new ArrayList();
1041: URLVisitor v = new URLVisitor() {
1042: public void visit(String s) {
1043: if (s.endsWith(".res")
1044: && !"res_index.res"
1045: .equals(s)) {
1046: list.add(s.substring(0, s
1047: .length() - 4)); // strip '.res'
1048: }
1049: }
1050: };
1051: handler.guide(v, false);
1052: return list;
1053: }
1054:
1055: return null;
1056: }
1057: });
1058:
1059: return list;
1060: }
1061:
1062: private static Set createFullLocaleNameSet(String baseName) {
1063: ArrayList list = createFullLocaleNameArray(baseName,
1064: ICU_DATA_CLASS_LOADER);
1065: HashSet set = new HashSet();
1066: if (list == null) {
1067: throw new MissingResourceException("Could not find "
1068: + ICU_RESOURCE_INDEX, "", "");
1069: }
1070: set.addAll(list);
1071: return Collections.unmodifiableSet(set);
1072: }
1073:
1074: private static Set createLocaleNameSet(String baseName) {
1075: try {
1076: String[] locales = createLocaleNameArray(baseName,
1077: ICU_DATA_CLASS_LOADER);
1078:
1079: HashSet set = new HashSet();
1080: set.addAll(Arrays.asList(locales));
1081: return Collections.unmodifiableSet(set);
1082: } catch (MissingResourceException e) {
1083: if (DEBUG) {
1084: System.out
1085: .println("couldn't find index for bundleName: "
1086: + baseName);
1087: Thread.dumpStack();
1088: }
1089: }
1090: return Collections.EMPTY_SET;
1091: }
1092:
1093: /**
1094: * Holds the prefix, and lazily creates the Locale[] list or the locale name
1095: * Set as needed.
1096: */
1097: private static final class AvailEntry {
1098: private String prefix;
1099: private ULocale[] ulocales;
1100: private Locale[] locales;
1101: private Set nameSet;
1102: private Set fullNameSet;
1103:
1104: AvailEntry(String prefix) {
1105: this .prefix = prefix;
1106: }
1107:
1108: ULocale[] getULocaleList() {
1109: if (ulocales == null) {
1110: ulocales = createULocaleList(prefix,
1111: ICU_DATA_CLASS_LOADER);
1112: }
1113: return ulocales;
1114: }
1115:
1116: Locale[] getLocaleList() {
1117: if (locales == null) {
1118: locales = createLocaleList(prefix);
1119: }
1120: return locales;
1121: }
1122:
1123: Set getLocaleNameSet() {
1124: if (nameSet == null) {
1125: nameSet = createLocaleNameSet(prefix);
1126: }
1127: return nameSet;
1128: }
1129:
1130: Set getFullLocaleNameSet() {
1131: if (fullNameSet == null) {
1132: fullNameSet = createFullLocaleNameSet(prefix);
1133: }
1134: return fullNameSet;
1135: }
1136: }
1137:
1138: /**
1139: * Stores the locale information in a cache accessed by key (bundle prefix).
1140: * The cached objects are AvailEntries. The cache is held by a SoftReference
1141: * so it can be GC'd.
1142: */
1143: private static AvailEntry getAvailEntry(String key) {
1144: AvailEntry ae = null;
1145: Map lcache = null;
1146: if (GET_AVAILABLE_CACHE != null) {
1147: lcache = (Map) GET_AVAILABLE_CACHE.get();
1148: if (lcache != null) {
1149: ae = (AvailEntry) lcache.get(key);
1150: }
1151: }
1152:
1153: if (ae == null) {
1154: ae = new AvailEntry(key);
1155: if (lcache == null) {
1156: lcache = new HashMap();
1157: lcache.put(key, ae);
1158: GET_AVAILABLE_CACHE = new SoftReference(lcache);
1159: } else {
1160: lcache.put(key, ae);
1161: }
1162: }
1163:
1164: return ae;
1165: }
1166:
1167: protected static final ICUResourceBundle findResourceWithFallback(
1168: String path, ICUResourceBundle actualBundle,
1169: ICUResourceBundle requested) {
1170: ICUResourceBundle sub = null;
1171: if (requested == null) {
1172: requested = actualBundle;
1173: }
1174: while (actualBundle != null) {
1175: StringTokenizer st = new StringTokenizer(path, "/");
1176: ICUResourceBundle current = actualBundle;
1177: while (st.hasMoreTokens()) {
1178: String subKey = st.nextToken();
1179: sub = current.handleGet(subKey, requested);
1180: if (sub == null) {
1181: break;
1182: }
1183: current = sub;
1184: }
1185: if (sub != null) {
1186: //we found it
1187: break;
1188: }
1189: if (actualBundle.resPath.length() != 0) {
1190: path = actualBundle.resPath + "/" + path;
1191: }
1192: // if not try the parent bundle
1193: actualBundle = (ICUResourceBundle) actualBundle.getParent();
1194:
1195: }
1196: if (sub != null) {
1197: setLoadingStatus(sub, requested.getLocaleID());
1198: }
1199: return sub;
1200: }
1201:
1202: public boolean equals(Object other) {
1203: if (other instanceof ICUResourceBundle) {
1204: ICUResourceBundle o = (ICUResourceBundle) other;
1205: if (getBaseName().equals(o.getBaseName())
1206: && getLocaleID().equals(o.getLocaleID())) {
1207: return true;
1208: }
1209: }
1210: return false;
1211: }
1212:
1213: // This method is for super class's instantiateBundle method
1214: public static UResourceBundle getBundleInstance(String baseName,
1215: String localeID, ClassLoader root, boolean disableFallback) {
1216: UResourceBundle b = instantiateBundle(baseName, localeID, root,
1217: disableFallback);
1218: if (b == null) {
1219: throw new MissingResourceException(
1220: "Could not find the bundle " + baseName + "/"
1221: + localeID + ".res", "", "");
1222: }
1223: return b;
1224: }
1225:
1226: // recursively build bundle .. over-ride super class method.
1227: protected synchronized static UResourceBundle instantiateBundle(
1228: String baseName, String localeID, ClassLoader root,
1229: boolean disableFallback) {
1230: ULocale defaultLocale = ULocale.getDefault();
1231: String localeName = localeID;
1232: if (localeName.indexOf('@') > 0) {
1233: localeName = ULocale.getBaseName(localeID);
1234: }
1235: String fullName = ICUResourceBundleReader.getFullName(baseName,
1236: localeName);
1237: ICUResourceBundle b = (ICUResourceBundle) loadFromCache(root,
1238: fullName, defaultLocale);
1239:
1240: // here we assume that java type resource bundle organization
1241: // is required then the base name contains '.' else
1242: // the resource organization is of ICU type
1243: // so clients can instantiate resources of the type
1244: // com.mycompany.data.MyLocaleElements_en.res and
1245: // com.mycompany.data.MyLocaleElements.res
1246: //
1247: final String rootLocale = (baseName.indexOf('.') == -1) ? "root"
1248: : "";
1249: final String defaultID = ULocale.getDefault().toString();
1250:
1251: if (localeName.equals("")) {
1252: localeName = rootLocale;
1253: }
1254: if (DEBUG)
1255: System.out.println("Creating " + fullName
1256: + " currently b is " + b);
1257: if (b == null) {
1258: b = ICUResourceBundleImpl.createBundle(baseName,
1259: localeName, root);
1260:
1261: if (DEBUG)
1262: System.out.println("The bundle created is: " + b
1263: + " and disableFallback=" + disableFallback
1264: + " and bundle.getNoFallback="
1265: + (b != null && b.getNoFallback()));
1266: if (disableFallback || (b != null && b.getNoFallback())) {
1267: addToCache(root, fullName, defaultLocale, b);
1268: // no fallback because the caller said so or because the bundle says so
1269: return b;
1270: }
1271:
1272: // fallback to locale ID parent
1273: if (b == null) {
1274: int i = localeName.lastIndexOf('_');
1275: if (i != -1) {
1276: String temp = localeName.substring(0, i);
1277: b = (ICUResourceBundle) instantiateBundle(baseName,
1278: temp, root, disableFallback);
1279: if (b != null && b.getULocale().equals(temp)) {
1280: b
1281: .setLoadingStatus(ICUResourceBundle.FROM_FALLBACK);
1282: }
1283: } else {
1284: if (defaultID.indexOf(localeName) == -1) {
1285: b = (ICUResourceBundle) instantiateBundle(
1286: baseName, defaultID, root,
1287: disableFallback);
1288: if (b != null) {
1289: b
1290: .setLoadingStatus(ICUResourceBundle.FROM_DEFAULT);
1291: }
1292: } else if (rootLocale.length() != 0) {
1293: b = ICUResourceBundleImpl.createBundle(
1294: baseName, rootLocale, root);
1295: if (b != null) {
1296: b
1297: .setLoadingStatus(ICUResourceBundle.FROM_ROOT);
1298: }
1299: }
1300: }
1301: } else {
1302: UResourceBundle parent = null;
1303: localeName = b.getLocaleID();
1304: int i = localeName.lastIndexOf('_');
1305:
1306: addToCache(root, fullName, defaultLocale, b);
1307:
1308: if (i != -1) {
1309: parent = instantiateBundle(baseName, localeName
1310: .substring(0, i), root, disableFallback);
1311: } else if (!localeName.equals(rootLocale)) {
1312: parent = instantiateBundle(baseName, rootLocale,
1313: root, true);
1314: }
1315:
1316: if (!b.equals(parent)) {
1317: b.setParent(parent);
1318: }
1319: }
1320: }
1321: return b;
1322: }
1323: }
|