001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.security.AccessController;
023: import java.security.PrivilegedAction;
024:
025: import org.apache.harmony.kernel.vm.VM;
026:
027: /**
028: * ResourceBundle is an abstract class which is the superclass of classes which
029: * provide locale specific resources. A bundle contains a number of named
030: * resources, where the names are Strings. A bundle may have a parent bundle,
031: * when a resource is not found in a bundle, the parent bundle is searched for
032: * the resource.
033: *
034: * @see Properties
035: * @see PropertyResourceBundle
036: * @since 1.1
037: */
038: public abstract class ResourceBundle {
039:
040: /**
041: * The parent of this ResourceBundle.
042: */
043: protected ResourceBundle parent;
044:
045: private Locale locale;
046:
047: static class MissingBundle extends ResourceBundle {
048: @Override
049: public Enumeration<String> getKeys() {
050: return null;
051: }
052:
053: @Override
054: public Object handleGetObject(String name) {
055: return null;
056: }
057: }
058:
059: private static final ResourceBundle MISSING = new MissingBundle();
060:
061: private static final ResourceBundle MISSINGBASE = new MissingBundle();
062:
063: private static final WeakHashMap<Object, Hashtable<String, ResourceBundle>> cache = new WeakHashMap<Object, Hashtable<String, ResourceBundle>>();
064:
065: /**
066: * Constructs a new instance of this class.
067: *
068: */
069: public ResourceBundle() {
070: /* empty */
071: }
072:
073: /**
074: * Finds the named resource bundle for the default locale.
075: *
076: * @param bundleName
077: * the name of the resource bundle
078: * @return ResourceBundle
079: *
080: * @exception MissingResourceException
081: * when the resource bundle cannot be found
082: */
083: public static final ResourceBundle getBundle(String bundleName)
084: throws MissingResourceException {
085: return getBundleImpl(bundleName, Locale.getDefault(), VM
086: .callerClassLoader());
087: }
088:
089: /**
090: * Finds the named resource bundle for the specified locale.
091: *
092: * @param bundleName
093: * the name of the resource bundle
094: * @param locale
095: * the locale
096: * @return ResourceBundle
097: *
098: * @exception MissingResourceException
099: * when the resource bundle cannot be found
100: */
101: public static final ResourceBundle getBundle(String bundleName,
102: Locale locale) {
103: return getBundleImpl(bundleName, locale, VM.callerClassLoader());
104: }
105:
106: /**
107: * Finds the named resource bundle for the specified locale.
108: *
109: * @param bundleName
110: * the name of the resource bundle
111: * @param locale
112: * the locale
113: * @param loader
114: * the ClassLoader to use
115: * @return ResourceBundle
116: *
117: * @exception MissingResourceException
118: * when the resource bundle cannot be found
119: */
120: public static ResourceBundle getBundle(String bundleName,
121: Locale locale, ClassLoader loader)
122: throws MissingResourceException {
123: if (loader == null) {
124: throw new NullPointerException();
125: }
126: if (bundleName != null) {
127: ResourceBundle bundle;
128: if (!locale.equals(Locale.getDefault())) {
129: if ((bundle = handleGetBundle(bundleName,
130: "_" + locale, false, //$NON-NLS-1$
131: loader)) != null) {
132: return bundle;
133: }
134: }
135: if ((bundle = handleGetBundle(bundleName,
136: "_" + Locale.getDefault(), true, loader)) != null) { //$NON-NLS-1$
137: return bundle;
138: }
139: throw new MissingResourceException(null, bundleName + '_'
140: + locale, ""); //$NON-NLS-1$
141: }
142: throw new NullPointerException();
143: }
144:
145: private static ResourceBundle getBundleImpl(String bundleName,
146: Locale locale, ClassLoader loader)
147: throws MissingResourceException {
148: if (bundleName != null) {
149: ResourceBundle bundle;
150: if (!locale.equals(Locale.getDefault())) {
151: String localeName = locale.toString();
152: if (localeName.length() > 0) {
153: localeName = "_" + localeName; //$NON-NLS-1$
154: }
155: if ((bundle = handleGetBundle(bundleName, localeName,
156: false, loader)) != null) {
157: return bundle;
158: }
159: }
160: String localeName = Locale.getDefault().toString();
161: if (localeName.length() > 0) {
162: localeName = "_" + localeName; //$NON-NLS-1$
163: }
164: if ((bundle = handleGetBundle(bundleName, localeName, true,
165: loader)) != null) {
166: return bundle;
167: }
168: throw new MissingResourceException(null, bundleName + '_'
169: + locale, ""); //$NON-NLS-1$
170: }
171: throw new NullPointerException();
172: }
173:
174: /**
175: * Answers the names of the resources contained in this ResourceBundle.
176: *
177: * @return an Enumeration of the resource names
178: */
179: public abstract Enumeration<String> getKeys();
180:
181: /**
182: * Gets the Locale of this ResourceBundle.
183: *
184: * @return the Locale of this ResourceBundle
185: */
186: public Locale getLocale() {
187: return locale;
188: }
189:
190: /**
191: * Answers the named resource from this ResourceBundle.
192: *
193: * @param key
194: * the name of the resource
195: * @return the resource object
196: *
197: * @exception MissingResourceException
198: * when the resource is not found
199: */
200: public final Object getObject(String key) {
201: ResourceBundle last, theParent = this ;
202: do {
203: Object result = theParent.handleGetObject(key);
204: if (result != null) {
205: return result;
206: }
207: last = theParent;
208: theParent = theParent.parent;
209: } while (theParent != null);
210: throw new MissingResourceException(null, last.getClass()
211: .getName(), key);
212: }
213:
214: /**
215: * Answers the named resource from this ResourceBundle.
216: *
217: * @param key
218: * the name of the resource
219: * @return the resource string
220: *
221: * @exception MissingResourceException
222: * when the resource is not found
223: */
224: public final String getString(String key) {
225: return (String) getObject(key);
226: }
227:
228: /**
229: * Answers the named resource from this ResourceBundle.
230: *
231: * @param key
232: * the name of the resource
233: * @return the resource string array
234: *
235: * @exception MissingResourceException
236: * when the resource is not found
237: */
238: public final String[] getStringArray(String key) {
239: return (String[]) getObject(key);
240: }
241:
242: private static ResourceBundle handleGetBundle(String base,
243: String locale, boolean loadBase, final ClassLoader loader) {
244: ResourceBundle bundle = null;
245: String bundleName = base + locale;
246: Object cacheKey = loader != null ? (Object) loader
247: : (Object) "null"; //$NON-NLS-1$
248: Hashtable<String, ResourceBundle> loaderCache;
249: synchronized (cache) {
250: loaderCache = cache.get(cacheKey);
251: if (loaderCache == null) {
252: loaderCache = new Hashtable<String, ResourceBundle>();
253: cache.put(cacheKey, loaderCache);
254: }
255: }
256: ResourceBundle result = loaderCache.get(bundleName);
257: if (result != null) {
258: if (result == MISSINGBASE) {
259: return null;
260: }
261: if (result == MISSING) {
262: if (!loadBase) {
263: return null;
264: }
265: String extension = strip(locale);
266: if (extension == null) {
267: return null;
268: }
269: return handleGetBundle(base, extension, loadBase,
270: loader);
271: }
272: return result;
273: }
274:
275: try {
276: Class<?> bundleClass = Class.forName(bundleName, true,
277: loader);
278:
279: if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
280: bundle = (ResourceBundle) bundleClass.newInstance();
281: }
282: } catch (LinkageError e) {
283: } catch (Exception e) {
284: }
285:
286: if (bundle != null) {
287: bundle.setLocale(locale);
288: } else {
289: final String fileName = bundleName.replace('.', '/');
290: InputStream stream = AccessController
291: .doPrivileged(new PrivilegedAction<InputStream>() {
292: public InputStream run() {
293: return loader == null ? ClassLoader
294: .getSystemResourceAsStream(fileName
295: + ".properties") : loader //$NON-NLS-1$
296: .getResourceAsStream(fileName
297: + ".properties"); //$NON-NLS-1$
298: }
299: });
300: if (stream != null) {
301: try {
302: try {
303: bundle = new PropertyResourceBundle(stream);
304: } finally {
305: stream.close();
306: }
307: bundle.setLocale(locale);
308: } catch (IOException e) {
309: }
310: }
311: }
312:
313: String extension = strip(locale);
314: if (bundle != null) {
315: if (extension != null) {
316: ResourceBundle parent = handleGetBundle(base,
317: extension, true, loader);
318: if (parent != null) {
319: bundle.setParent(parent);
320: }
321: }
322: loaderCache.put(bundleName, bundle);
323: return bundle;
324: }
325:
326: if (extension != null && (loadBase || extension.length() > 0)) {
327: bundle = handleGetBundle(base, extension, loadBase, loader);
328: if (bundle != null) {
329: loaderCache.put(bundleName, bundle);
330: return bundle;
331: }
332: }
333: loaderCache.put(bundleName, loadBase ? MISSINGBASE : MISSING);
334: return null;
335: }
336:
337: /**
338: * Answers the named resource from this ResourceBundle, or null if the
339: * resource is not found.
340: *
341: * @param key
342: * the name of the resource
343: * @return the resource object
344: */
345: protected abstract Object handleGetObject(String key);
346:
347: /**
348: * Sets the parent resource bundle of this ResourceBundle. The parent is
349: * searched for resources which are not found in this resource bundle.
350: *
351: * @param bundle
352: * the parent resource bundle
353: */
354: protected void setParent(ResourceBundle bundle) {
355: parent = bundle;
356: }
357:
358: private static String strip(String name) {
359: int index = name.lastIndexOf('_');
360: if (index != -1) {
361: return name.substring(0, index);
362: }
363: return null;
364: }
365:
366: private void setLocale(String name) {
367: String language = "", country = "", variant = ""; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
368: if (name.length() > 1) {
369: int nextIndex = name.indexOf('_', 1);
370: if (nextIndex == -1) {
371: nextIndex = name.length();
372: }
373: language = name.substring(1, nextIndex);
374: if (nextIndex + 1 < name.length()) {
375: int index = nextIndex;
376: nextIndex = name.indexOf('_', nextIndex + 1);
377: if (nextIndex == -1) {
378: nextIndex = name.length();
379: }
380: country = name.substring(index + 1, nextIndex);
381: if (nextIndex + 1 < name.length()) {
382: variant = name.substring(nextIndex + 1, name
383: .length());
384: }
385: }
386: }
387: locale = new Locale(language, country, variant);
388: }
389: }
|