001: /**
002: *******************************************************************************
003: * Copyright (C) 2001-2004, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */package com.ibm.icu.util;
007:
008: import java.util.*;
009:
010: /**
011: * A ResourceBundle that overlays one hierarchy atop another. This is
012: * best explained by example. Suppose one wants to use the
013: * resource hiararchy (in JDK 1.2 and 1.3, but not 1.4) at
014: * "java.text.resources.LocaleElements", but one wants to use
015: * a modified version of the "NumberPatterns" resource in the
016: * fr_FR locale. One way to do this is to add special case code
017: * to the lookup operation to check for fr_FR and the key
018: * "NumberPatterns", and in that case, load up custom data. However,
019: * this becomes unwieldy and places some information about the
020: * effective resource hierarchy into the code.
021: *
022: * The OverlayBundle solves this problem by layering another
023: * hierarchy, e.g, "com.acme.resources.LocaleElements", on top of a
024: * base hierarchy. When a resource is requested, it is first sought
025: * in the overlay hierarchy, and if not found there, it is sought in
026: * the base hierarchy. Multiple overlays are supported, but in
027: * practice one is usually sufficient.
028: *
029: * The OverlayBundle also addresses the problem of country-oriented
030: * data. To specify the default data for a language, one just sets
031: * the language resource bundle data. However, specifying the default
032: * data for a country using the standard ResourceBundle mechanism is
033: * impossible. The OverlayBundle recognizes "wildcard" locales with
034: * the special language code "xx". When looking up data for a locale
035: * with a non-empty country, if an exact locale match cannot be found,
036: * the OverlayBundle looks for data in the locale xx_YY, where YY is
037: * the country being sought. This effectively adds another entry in
038: * the fallback sequence for a locale aa_BB: aa_BB, xx_BB, aa, root.
039: * Wildcard locales are not implemented for the base hierarchy, only
040: * for overlays.
041: *
042: * The OverlayBundle is implemented as an array of n ResourceBundle
043: * base names. The base names are searched from 0 to n-1. Base name
044: * n-1 is special; it is the base hierarchy. This should be a
045: * well-populated hierarchy with most of the default data, typically,
046: * the icu or sun core hierarchies. The base hierarchy is
047: * treated differently from the overlays above it. It does not get
048: * wildcard resolution, and the getKeys() framework method is
049: * delegated to the base hierarchy bundle.
050: *
051: * Usage: Instantiate an OverlayBundle directly (not via a factory
052: * method as in ResourceBundle). Instead of specifying a single base
053: * name, pass it an array of 2 or more base names. After that, use it
054: * exactly as you would use ResourceBundle.
055: *
056: * @see java.util.ResourceBundle
057: * @author Alan Liu
058: * @internal
059: * @deprecated ICU 2.4. This class may be removed or modified.
060: */
061: // prepare to deprecate in next release
062: ///CLOVER:OFF
063: public class OverlayBundle extends ResourceBundle {
064:
065: /**
066: * The array of base names, with the length-1 entry being the base
067: * hierarchy, typically "sun.text.resources.LocaleElements".
068: */
069: private String[] baseNames;
070:
071: /**
072: * The requested locale.
073: */
074: private Locale locale;
075:
076: /**
077: * Loaded bundles. These will be null until they are loaded on
078: * demand.
079: */
080: private ResourceBundle[] bundles;
081:
082: /**
083: * Construct an overlay bundle given a sequence of base names and
084: * a locale.
085: * @internal
086: * @deprecated ICU 2.4. This class may be removed or modified.
087: */
088: public OverlayBundle(String[] baseNames, Locale locale) {
089: this .baseNames = baseNames;
090: this .locale = locale;
091: bundles = new ResourceBundle[baseNames.length];
092: }
093:
094: /**
095: * ResourceBundle framework method. Delegates to
096: * bundles[i].getObject().
097: * @internal
098: * @deprecated ICU 2.4. This class may be removed or modified.
099: */
100: protected Object handleGetObject(String key)
101: throws MissingResourceException {
102:
103: Object o = null;
104:
105: for (int i = 0; i < bundles.length; ++i) {
106: load(i);
107: try {
108: o = bundles[i].getObject(key);
109: } catch (MissingResourceException e) {
110: if (i == bundles.length - 1) {
111: throw e;
112: }
113: }
114: if (o != null) {
115: break;
116: }
117: }
118:
119: return o;
120: }
121:
122: /**
123: * ResourceBundle framework method. Delegates to
124: * bundles[bundles.length-1].getKeys().
125: * @internal
126: * @deprecated ICU 2.4. This class may be removed or modified.
127: */
128: public Enumeration getKeys() {
129: // Return the enumeration of the last bundle, which is the base
130: // of our hierarchy stack.
131: int i = bundles.length - 1;
132: load(i);
133: return bundles[i].getKeys();
134: }
135:
136: /**
137: * Load the i-th bundle and implement wildcard resolution.
138: */
139: private void load(int i) throws MissingResourceException {
140:
141: if (bundles[i] == null) {
142: boolean tryWildcard = false;
143: try {
144: bundles[i] = ResourceBundle.getBundle(baseNames[i],
145: locale);
146: if (bundles[i].getLocale().equals(locale)) {
147: return;
148: }
149: if (locale.getCountry().length() != 0
150: && i != bundles.length - 1) {
151: tryWildcard = true;
152: }
153: } catch (MissingResourceException e) {
154: if (i == bundles.length - 1) {
155: throw e;
156: }
157: tryWildcard = true;
158: }
159: if (tryWildcard) {
160: Locale wildcard = new Locale("xx", locale.getCountry(),
161: locale.getVariant());
162: try {
163: bundles[i] = ResourceBundle.getBundle(baseNames[i],
164: wildcard);
165: } catch (MissingResourceException e) {
166: if (bundles[i] == null) {
167: throw e;
168: }
169: }
170: }
171: }
172: }
173: }
|