/************************************************************************** * Parts copyright (c) 2001 by Punch Telematix. All rights reserved. * * Parts copyright (c) 2008, 2009 by Chris Gray, /k/ Embedded Java * * Solutions. All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. Neither the name of Punch Telematix or of /k/ Embedded Java Solutions* * nor the names of other contributors may be used to endorse or promote* * products derived from this software without specific prior written * * permission. * * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL PUNCH TELEMATIX, /K/ EMBEDDED JAVA SOLUTIONS OR OTHER * * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ package java.util; import java.io.IOException; import java.io.InputStream; public abstract class ResourceBundle { private Locale locale; protected ResourceBundle parent=null; protected void setParent(ResourceBundle p) { parent = p; } protected abstract Object handleGetObject(String key) throws MissingResourceException; private static final Hashtable cache = new Hashtable(); private static final char underscore = '_'; public abstract Enumeration getKeys(); public Locale getLocale(){ return locale; } public final Object getObject(String key) throws MissingResourceException { ResourceBundle check = this; Object o = null; while (check != null) { o = check.handleGetObject(key); if (o != null ) { return o; } check = check.parent; } throw new MissingResourceException(this + ": key '" + key + "' not found",this.getClass().getName(),key); } public final String getString(String key) throws MissingResourceException { return (String) getObject(key); } public final String[] getStringArray(String key) throws MissingResourceException { return (String[]) getObject(key); } public static final ResourceBundle getBundle(String baseName) throws MissingResourceException { return getBundle(baseName, Locale.getDefault(), getCallingClassLoader(), true); } public static final ResourceBundle getBundle(String baseName, Locale locale) throws MissingResourceException { return getBundle(baseName, locale, getCallingClassLoader(), locale.equals(Locale.getDefault())); } public static final ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader) throws MissingResourceException { return getBundle(baseName, locale, loader, locale.equals(Locale.getDefault())); } /** ** this method calls a function in ClassLoader.c. This against the convention but avoids code duplication, ** has better performance, ... */ private static native ClassLoader getCallingClassLoader(); private static String cutEnd(String name) { //System.out.println("cutting String ..."+name); int i = name.lastIndexOf(underscore); return ( i == -1 ? "" : name.substring(0,i)); } /** ** Find or create the resource bundle corresponding to a given 'baseName'. ** We search for bundles with the following names: ** <ul><li>basename_lang_country_variant ** <li>basename_lang_country ** <li>basename_lang ** <li>if the locale is not the default locale, the same three names ** but for the default locale ** <li>basename ** </ul> ** For each name we try to find first a class ans then a .properties file. ** <p>[CG 20090108] Added negative caching to avoid repeated attempts ** to load the same non-existent class or file. Note that this may not be ** the Right Thing to do if users expect to be able to dynamically add ** new resource bundles to the classpath. (In my view it would be better ** to define a separate, OSGi-friendly API for this). */ private static final ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader, boolean defaultLoc) throws MissingResourceException { boolean inNegativeCache; if (baseName == null){ throw new NullPointerException(); } Class rbClass=null; ResourceBundle newBundle=null; String local = baseName+"_"+locale.toString(); while (true) { inNegativeCache = false; Object cached = cache.get(local); if (cached != null) { if (cached instanceof String) { inNegativeCache = true; } else { newBundle = (ResourceBundle)cached; return newBundle; // found in cache } } else { try { rbClass = Class.forName(local, true, loader); newBundle = (ResourceBundle)rbClass.newInstance(); break; } catch(Exception cnfe){ } } // Not found in positive cache; either found in negative cache or we // tried to load the class and couldn't. Time to try something else ... // How about a properties file ? InputStream in = (loader == null ? ClassLoader.getSystemResourceAsStream(local.replace('.','/')+".properties") : loader.getResourceAsStream(local.replace('.','/')+".properties")); if (in == null) { // no properties file --> we cut down our string if (!inNegativeCache) { cache.put(local, baseName); } local = cutEnd(local); if ((local.equals(baseName)&& !defaultLoc)||local.equals("")) { if (!defaultLoc) { defaultLoc = true; local = baseName+"_"+Locale.getDefault().toString(); } else { throw new MissingResourceException("couldn't find resourceBundle '" + baseName + "' using locale '" + locale + (defaultLoc ? "' or default" : "'"), baseName,locale.toString()); } } continue; } // we have a properties file ... try { newBundle = new PropertyResourceBundle(in); break; //leave the WHILE- loop } catch(IOException ioe) { // something went wrong ??? --> lets look for something else ... continue; } } // We found a ResourceBundle now search for the parents ... newBundle.setParent(findParent(cutEnd(local), loader)); newBundle.locale = locale; cache.put(local,newBundle); return newBundle; } private static ResourceBundle findParent(String name, ClassLoader loader) { //System.out.println("Looking for parent ..."+name); if (name.equals("")) return null; ResourceBundle rb = (ResourceBundle) cache.get(name); if (rb != null) { //System.out.println("found parent in the cache ..."+name); return rb; } try { rb = (ResourceBundle)Class.forName(name, true, loader).newInstance(); } catch (Exception e) { // we don't seem to able to load the direct parent --> maybe in a prop. file try { rb = new PropertyResourceBundle(loader == null ? ClassLoader.getSystemResourceAsStream(name.replace('.','/')+".properties"): loader.getResourceAsStream(name.replace('.','/')+".properties")); } // no class or properties file ==> no parent (we return null) catch (Exception pe) { return null; } } rb.setParent(findParent(cutEnd(name), loader)); cache.put(name,rb); return rb; } }