001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.axis2.jaxws.i18n;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024:
025: import java.util.Enumeration;
026: import java.util.HashSet;
027: import java.util.Hashtable;
028: import java.util.Iterator;
029: import java.util.Locale;
030: import java.util.MissingResourceException;
031: import java.util.ResourceBundle;
032:
033: /**
034: * <p>Wrapper class for resource bundles. Property files are used to store resource strings, which
035: * are the only types of resources available. Property files can inherit properties from other files
036: * so that a base property file can be used and a small number of properties can be over-ridden by
037: * another property file. For example you may create an english version of a resource file named
038: * "resource.properties". You then decide that the British English version of all of the properties
039: * except one are the same, so there is no need to redefine all of the properties in
040: * "resource_en_GB", just the one that is different.</p> <p>The basename is the name of the property
041: * file without the ".properties" extension.</p> <p>Properties will be cached for performance.<p>
042: * <p>Property values stored in the property files can also contain dynamic variables. Any dynamic
043: * variable defined in PropertiesUtil.getVariableValue() can be used (such as {date}), as well as
044: * arguments in the form {0}, {1}, etc. Argument values are specified in the various overloaded
045: * getString() methods.</p>
046: */
047: public class ProjectResourceBundle extends ResourceBundle {
048: private static final Log log = LogFactory
049: .getLog(ProjectResourceBundle.class);
050:
051: // The static cache of ResourceBundles.
052: // The key is the 'basename + locale + default locale'
053: // The element is a ResourceBundle object
054: private static final Hashtable bundleCache = new Hashtable();
055:
056: private static final Locale defaultLocale = Locale.getDefault();
057:
058: private final ResourceBundle resourceBundle;
059: private final String resourceName;
060:
061: protected Object handleGetObject(String key)
062: throws MissingResourceException {
063: if (log.isDebugEnabled()) {
064: log.debug(this .toString() + "::handleGetObject(" + key
065: + ")");
066: }
067: Object obj;
068: try {
069: obj = resourceBundle.getObject(key);
070: } catch (MissingResourceException e) {
071: /* catch missing resource, ignore, & return null
072: * if this method doesn't return null, then parents
073: * are not searched
074: */
075: obj = null;
076: }
077: return obj;
078: }
079:
080: public Enumeration getKeys() {
081: Enumeration myKeys = resourceBundle.getKeys();
082: if (parent == null) {
083: return myKeys;
084: } else {
085: final HashSet set = new HashSet();
086: while (myKeys.hasMoreElements()) {
087: set.add(myKeys.nextElement());
088: }
089:
090: Enumeration pKeys = parent.getKeys();
091: while (pKeys.hasMoreElements()) {
092: set.add(pKeys.nextElement());
093: }
094:
095: return new Enumeration() {
096: private Iterator it = set.iterator();
097:
098: public boolean hasMoreElements() {
099: return it.hasNext();
100: }
101:
102: public Object nextElement() {
103: return it.next();
104: }
105: };
106: }
107: }
108:
109: /**
110: * Construct a new ProjectResourceBundle
111: *
112: * @param projectName The name of the project to which the class belongs. It must be a proper
113: * prefix of the caller's package.
114: * @param packageName The package name to further construct the basename.
115: * @param resourceName The name of the resource without the ".properties" extension
116: * @throws MissingResourceException if projectName is not a prefix of the caller's package name,
117: * or if the resource could not be found/loaded.
118: */
119: public static ProjectResourceBundle getBundle(String projectName,
120: String packageName, String resourceName)
121: throws MissingResourceException {
122: return getBundle(projectName, packageName, resourceName, null,
123: null, null);
124: }
125:
126: /**
127: * Construct a new ProjectResourceBundle
128: *
129: * @param projectName The name of the project to which the class belongs. It must be a proper
130: * prefix of the caller's package.
131: * @param caller The calling class.
132: * @param resourceName The name of the resource without the ".properties" extension
133: * @throws MissingResourceException if projectName is not a prefix of the caller's package name,
134: * or if the resource could not be found/loaded.
135: */
136: public static ProjectResourceBundle getBundle(String projectName,
137: Class caller, String resourceName, Locale locale)
138: throws MissingResourceException {
139: return getBundle(projectName, caller, resourceName, locale,
140: null);
141: }
142:
143: /**
144: * Construct a new ProjectResourceBundle
145: *
146: * @param projectName The name of the project to which the class belongs. It must be a proper
147: * prefix of the caller's package.
148: * @param packageName The package name to construct base name.
149: * @param resourceName The name of the resource without the ".properties" extension
150: * @param locale The locale
151: * @throws MissingResourceException if projectName is not a prefix of the caller's package name,
152: * or if the resource could not be found/loaded.
153: */
154: public static ProjectResourceBundle getBundle(String projectName,
155: String packageName, String resourceName, Locale locale,
156: ClassLoader loader) throws MissingResourceException {
157: return getBundle(projectName, packageName, resourceName,
158: locale, loader, null);
159: }
160:
161: /**
162: * Construct a new ProjectResourceBundle
163: *
164: * @param projectName The name of the project to which the class belongs. It must be a proper
165: * prefix of the caller's package.
166: * @param caller The calling class. This is used to get the package name to further
167: * construct the basename as well as to get the proper ClassLoader.
168: * @param resourceName The name of the resource without the ".properties" extension
169: * @param locale The locale
170: * @param extendsBundle If non-null, then this ExtendMessages will default to extendsBundle.
171: * @throws MissingResourceException if projectName is not a prefix of the caller's package name,
172: * or if the resource could not be found/loaded.
173: */
174: public static ProjectResourceBundle getBundle(String projectName,
175: Class caller, String resourceName, Locale locale,
176: ResourceBundle extendsBundle)
177: throws MissingResourceException {
178: return getBundle(projectName, getPackage(caller.getClass()
179: .getName()), resourceName, locale, caller.getClass()
180: .getClassLoader(), extendsBundle);
181: }
182:
183: /**
184: * Construct a new ProjectResourceBundle
185: *
186: * @param projectName The name of the project to which the class belongs. It must be a proper
187: * prefix of the caller's package.
188: * @param packageName The package name to further construct the basename.
189: * @param resourceName The name of the resource without the ".properties" extension
190: * @param locale The locale
191: * @param extendsBundle If non-null, then this ExtendMessages will default to extendsBundle.
192: * @throws MissingResourceException if projectName is not a prefix of the caller's package name,
193: * or if the resource could not be found/loaded.
194: */
195: public static ProjectResourceBundle getBundle(String projectName,
196: String packageName, String resourceName, Locale locale,
197: ClassLoader loader, ResourceBundle extendsBundle)
198: throws MissingResourceException {
199: if (log.isDebugEnabled()) {
200: log.debug("getBundle(" + projectName + "," + packageName
201: + "," + resourceName + "," + String.valueOf(locale)
202: + ",...)");
203: }
204:
205: Context context = new Context();
206: context.setLocale(locale);
207: context.setLoader(loader);
208: context.setProjectName(projectName);
209: context.setResourceName(resourceName);
210: context.setParentBundle(extendsBundle);
211:
212: packageName = context.validate(packageName);
213:
214: ProjectResourceBundle bundle = null;
215: try {
216: bundle = getBundle(context, packageName);
217: } catch (RuntimeException e) {
218: log.debug("Exception: ", e);
219: throw e;
220: }
221:
222: if (bundle == null) {
223: throw new MissingResourceException("Cannot find resource '"
224: + packageName + '.' + resourceName + "'",
225: resourceName, "");
226: }
227:
228: return bundle;
229: }
230:
231: /**
232: * get bundle... - check cache - try up hierarchy - if at top of hierarchy, use (link to)
233: * context.getParentBundle()
234: */
235: private static synchronized ProjectResourceBundle getBundle(
236: Context context, String packageName)
237: throws MissingResourceException {
238: String cacheKey = context.getCacheKey(packageName);
239:
240: ProjectResourceBundle prb = (ProjectResourceBundle) bundleCache
241: .get(cacheKey);
242:
243: if (prb == null) {
244: String name = packageName + '.' + context.getResourceName();
245: ResourceBundle rb = context.loadBundle(packageName);
246: ResourceBundle parent = context
247: .getParentBundle(packageName);
248:
249: if (rb != null) {
250: prb = new ProjectResourceBundle(name, rb);
251: prb.setParent(parent);
252: if (log.isDebugEnabled()) {
253: log.debug("Created " + prb + ", linked to parent "
254: + String.valueOf(parent));
255: }
256: } else {
257: if (parent != null) {
258: if (parent instanceof ProjectResourceBundle) {
259: prb = (ProjectResourceBundle) parent;
260: } else {
261: prb = new ProjectResourceBundle(name, parent);
262: }
263: if (log.isDebugEnabled()) {
264: log
265: .debug("Root package not found, cross link to "
266: + parent);
267: }
268: }
269: }
270:
271: if (prb != null) {
272: // Cache the resource
273: bundleCache.put(cacheKey, prb);
274: }
275: }
276:
277: return prb;
278: }
279:
280: private static String getPackage(String name) {
281: return name.substring(0, name.lastIndexOf('.')).intern();
282: }
283:
284: /** Construct a new ProjectResourceBundle */
285: private ProjectResourceBundle(String name, ResourceBundle bundle)
286: throws MissingResourceException {
287: this .resourceBundle = bundle;
288: this .resourceName = name;
289: }
290:
291: public String getResourceName() {
292: return resourceName;
293: }
294:
295: /** Clears the internal cache */
296: // public static void clearCache() {
297: // bundleCache.clear();
298: // }
299: public String toString() {
300: return resourceName;
301: }
302:
303: private static class Context {
304: private Locale _locale;
305: private ClassLoader _loader;
306: private String _projectName;
307: private String _resourceName;
308: private ResourceBundle _parent;
309:
310: void setLocale(Locale l) {
311: /* 1. Docs indicate that if locale is not specified,
312: * then the default local is used in it's place.
313: * 2. A null value for locale is invalid.
314: *
315: * Therefore, default...
316: */
317: _locale = (l == null) ? defaultLocale : l;
318: }
319:
320: void setLoader(ClassLoader l) {
321: _loader = (l != null) ? l : this .getClass()
322: .getClassLoader();
323: // START FIX: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16868
324: if (_loader == null) {
325: _loader = ClassLoader.getSystemClassLoader();
326: }
327: // END FIX: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16868
328: }
329:
330: void setProjectName(String name) {
331: _projectName = name.intern();
332: }
333:
334: void setResourceName(String name) {
335: _resourceName = name.intern();
336: }
337:
338: void setParentBundle(ResourceBundle b) {
339: _parent = b;
340: }
341:
342: Locale getLocale() {
343: return _locale;
344: }
345:
346: ClassLoader getLoader() {
347: return _loader;
348: }
349:
350: String getProjectName() {
351: return _projectName;
352: }
353:
354: String getResourceName() {
355: return _resourceName;
356: }
357:
358: ResourceBundle getParentBundle() {
359: return _parent;
360: }
361:
362: String getCacheKey(String packageName) {
363: String loaderName = (_loader == null) ? "" : (":" + _loader
364: .hashCode());
365: return packageName + "." + _resourceName + ":" + _locale
366: + ":" + defaultLocale + loaderName;
367: }
368:
369: ResourceBundle loadBundle(String packageName) {
370: try {
371: return ResourceBundle.getBundle(packageName + '.'
372: + _resourceName, _locale, _loader);
373: } catch (MissingResourceException e) {
374: // Deliberately surpressing print stack.. just the string for info.
375: log
376: .debug("loadBundle: Ignoring MissingResourceException: "
377: + e.getMessage());
378: }
379: return null;
380: }
381:
382: ResourceBundle getParentBundle(String packageName) {
383: ResourceBundle p;
384: if (!packageName.equals(_projectName)) {
385: p = getBundle(this , getPackage(packageName));
386: } else {
387: p = _parent;
388: _parent = null;
389: }
390: return p;
391: }
392:
393: String validate(String packageName)
394: throws MissingResourceException {
395: if (_projectName == null || _projectName.length() == 0) {
396: log.debug("Project name not specified");
397: throw new MissingResourceException(
398: "Project name not specified", "", "");
399: }
400:
401: if (packageName == null || packageName.length() == 0) {
402: log.debug("Package name not specified");
403: throw new MissingResourceException(
404: "Package not specified", packageName, "");
405: }
406: packageName = packageName.intern();
407:
408: /* Ensure that project is a proper prefix of class.
409: * Terminate project name with '.' to ensure proper match.
410: */
411: if (!packageName.equals(_projectName)
412: && !packageName.startsWith(_projectName + '.')) {
413: log.debug("Project not a prefix of Package");
414: throw new MissingResourceException("Project '"
415: + _projectName
416: + "' must be a prefix of Package '"
417: + packageName + "'", packageName + '.'
418: + _resourceName, "");
419: }
420:
421: return packageName;
422: }
423: }
424: }
|