
org.eigenbase.resgen.ShadowResourceBundle Maven / Gradle / Ivy
Show all versions of eigenbase-resgen Show documentation
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
package org.eigenbase.resgen;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* ShadowResourceBundle
is an abstract base class for
* {@link ResourceBundle} classes which are backed by a properties file. When
* the class is created, it loads a properties file with the same name as the
* class.
*
* In the standard scheme (see {@link ResourceBundle}),
* if you call {@link ResourceBundle#getBundle}("foo.MyResource")
,
* it first looks for a class called foo.MyResource
, then
* looks for a file called foo/MyResource.properties
. If it finds
* the file, it creates a {@link PropertyResourceBundle} and loads the class.
* The problem is if you want to load the .properties
file
* into a dedicated class; ShadowResourceBundle
helps with this
* case.
*
*
You should create a class as follows:
*
* package foo;
*class MyResource extends org.eigenbase.resgen.ShadowResourceBundle {
* public MyResource() throws java.io.IOException {
* }
*}
*
*
Then when you call
* {@link ResourceBundle#getBundle ResourceBundle.getBundle("foo.MyResource")},
* it will find the class before the properties file, but still automatically
* load the properties file based upon the name of the class.
*/
public abstract class ShadowResourceBundle extends ResourceBundle {
private PropertyResourceBundle bundle;
private static final ThreadLocal mapThreadToLocale = new ThreadLocal();
protected static final Object[] emptyObjectArray = new Object[0];
/**
* Creates a ShadowResourceBundle
, and reads resources from
* a .properties
file with the same name as the current class.
* For example, if the class is called foo.MyResource_en_US
,
* reads from foo/MyResource_en_US.properties
, then
* foo/MyResource_en.properties
, then
* foo/MyResource.properties
.
*
* @throws IOException on error
*/
protected ShadowResourceBundle() throws IOException {
super();
Class clazz = getClass();
InputStream stream = openPropertiesFile(clazz);
if (stream == null) {
throw new IOException("could not open properties file for " + getClass());
}
MyPropertyResourceBundle previousBundle =
new MyPropertyResourceBundle(stream);
bundle = previousBundle;
stream.close();
// Now load properties files for parent locales, which we deduce from
// the names of our super-class, and its super-class.
while (true) {
clazz = clazz.getSuperclass();
if (clazz == null ||
clazz == ShadowResourceBundle.class ||
!ResourceBundle.class.isAssignableFrom(clazz)) {
break;
}
stream = openPropertiesFile(clazz);
if (stream == null) {
continue;
}
MyPropertyResourceBundle newBundle =
new MyPropertyResourceBundle(stream);
stream.close();
if (previousBundle != null) {
previousBundle.setParentTrojan(newBundle);
} else {
bundle = newBundle;
}
previousBundle = newBundle;
}
}
static class MyPropertyResourceBundle extends PropertyResourceBundle {
public MyPropertyResourceBundle(InputStream stream) throws IOException {
super(stream);
}
void setParentTrojan(ResourceBundle parent) {
super.setParent(parent);
}
}
/**
* Opens the properties file corresponding to a given class. The code is
* copied from {@link ResourceBundle}.
*/
private static InputStream openPropertiesFile(Class clazz) {
final ClassLoader loader = clazz.getClassLoader();
final String resName = clazz.getName().replace('.', '/') + ".properties";
return (InputStream)java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
if (loader != null) {
return loader.getResourceAsStream(resName);
} else {
return ClassLoader.getSystemResourceAsStream(resName);
}
}
});
}
public Enumeration getKeys() {
return bundle.getKeys();
}
protected Object handleGetObject(String key)
throws MissingResourceException {
return bundle.getObject(key);
}
/**
* Returns the instance of the baseName
resource bundle for
* the current thread's locale. For example, if called with
* "mondrian.olap.MondrianResource", from a thread which has called
* {@link #setThreadLocale}({@link Locale#FRENCH}),
* will get an instance of
* "mondrian.olap.MondrianResource_FR" from the cache.
*
* This method should be called from a derived class, with the proper
* casting:
*
* class MyResource extends ShadowResourceBundle {
* ...
* /**
* * Retrieves the instance of {@link MyResource} appropriate
* * to the current locale. If this thread has specified a locale
* * by calling {@link #setThreadLocale}, this locale is used,
* * otherwise the default locale is used.
* **/
* public static MyResource instance() {
* return (MyResource) instance(MyResource.class.getName());
* }
* ...
* }
*
* @deprecated This method does not work correctly in dynamically
* loaded jars.
*
* @param baseName Base name
*
* @return Resource bundle
*/
protected static ResourceBundle instance(String baseName) {
return instance(baseName, getThreadLocale());
}
/**
* Returns the instance of the baseName
resource bundle
* for the given locale.
*
* This method should be called from a derived class, with the proper
* casting:
*
* class MyResource extends ShadowResourceBundle {
* ...
*
* /**
* * Retrieves the instance of {@link MyResource} appropriate
* * to the given locale.
* **/
* public static MyResource instance(Locale locale) {
* return (MyResource) instance(MyResource.class.getName(), locale);
* }
* ...
* }
*
* @param baseName Base name
* @param locale Locale
* @return Resource bundle
*
* @deprecated This method does not work correctly in dynamically
* loaded jars.
*/
protected static ShadowResourceBundle instance(
String baseName, Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale);
return instance(baseName, locale, bundle);
}
/**
* Returns the instance of the baseName
resource bundle
* for the given locale.
*
* This method should be called from a derived class, with the proper
* casting:
*
* class MyResource extends ShadowResourceBundle {
* ...
*
* /**
* * Retrieves the instance of {@link MyResource} appropriate
* * to the given locale.
* **/
* public static MyResource instance(Locale locale) {
* return (MyResource) instance(
* MyResource.class.getName(), locale,
* ResourceBundle.getBundle(MyResource.class.getName(), locale));
* }
* ...
* }
*
* @param baseName Base name
* @param locale Locale
* @param bundle Resource bundle
* @return Resource bundle
*/
protected static ShadowResourceBundle instance(
String baseName, Locale locale, ResourceBundle bundle)
{
if (bundle instanceof PropertyResourceBundle) {
throw new ClassCastException(
"ShadowResourceBundle.instance('" + baseName + "','" +
locale + "') found " +
baseName + "_" + locale + ".properties but not " +
baseName + "_" + locale + ".class");
}
return (ShadowResourceBundle) bundle;
}
/** Returns the preferred locale of the current thread, or
* the default locale if the current thread has not called
* {@link #setThreadLocale}.
*
* @return Locale */
protected static Locale getThreadOrDefaultLocale() {
Locale locale = getThreadLocale();
if (locale == null) {
return Locale.getDefault();
} else {
return locale;
}
}
/** Sets the locale for the current thread.
* Used by {@link #instance(String,Locale)}.
*
* @param locale Locale */
public static void setThreadLocale(Locale locale) {
mapThreadToLocale.set(locale);
}
/** Returns the preferred locale of the current thread, or null if the
* thread has not called {@link #setThreadLocale}.
*
* @return Locale */
public static Locale getThreadLocale() {
return (Locale) mapThreadToLocale.get();
}
}
// End ShadowResourceBundle.java