blob: cd2f0b599d5c400b15b5ad1223e0b0012f173abb [file] [log] [blame]
/*
* Copyright 2006 Google Inc.
*
* Licensed 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 com.google.gwt.i18n.rebind.util;
import com.google.gwt.core.ext.typeinfo.JClassType;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
/**
* Creates resources.
*/
public abstract class ResourceFactory {
static class SimplePathTree extends AbstractPathTree {
String path;
SimplePathTree(String path) {
this.path = path;
}
public AbstractPathTree getChild(int i) {
throw new UnsupportedOperationException(
"Simple paths have no children, therefore cannot get child: " + i);
}
public String getPath() {
return path;
}
public int numChildren() {
return 0;
}
}
private abstract static class AbstractPathTree {
abstract AbstractPathTree getChild(int i);
abstract String getPath();
abstract int numChildren();
}
private static class ClassPathTree extends AbstractPathTree {
Class javaInterface;
ClassPathTree(Class javaInterface) {
this.javaInterface = javaInterface;
}
AbstractPathTree getChild(int i) {
// we expect to do this at most once, so no caching is used.
return new ClassPathTree(javaInterface.getInterfaces()[i]);
}
String getPath() {
return javaInterface.getName();
}
int numChildren() {
return javaInterface.getInterfaces().length;
}
}
private static class JClassTypePathTree extends AbstractPathTree {
JClassType javaInterface;
JClassTypePathTree(JClassType javaInterface) {
this.javaInterface = javaInterface;
}
AbstractPathTree getChild(int i) {
// we expect to do this at most once, so no caching is used.
return new JClassTypePathTree(javaInterface.getImplementedInterfaces()[i]);
}
/**
* Path is equivalent to javaInterface.getQualifiedName() except for inner
* classes.
*
* @see com.google.gwt.i18n.rebind.util.ResourceFactory.AbstractPathTree#getPath()
*/
String getPath() {
String name = getResourceName(javaInterface);
String packageName = javaInterface.getPackage().getName();
return packageName + "." + name;
}
int numChildren() {
return javaInterface.getImplementedInterfaces().length;
}
}
public static final AbstractResource NOT_FOUND = new AbstractResource() {
void addToKeySet(Set s) {
throw new IllegalStateException("Not found resource");
}
Object handleGetObject(String key) {
throw new IllegalStateException("Not found resource");
}
};
private static Map cache = new HashMap();
private static List loaders = new ArrayList();
static {
loaders.add(new LocalizedPropertiesResource.Factory());
}
/**
* Clears the resource cache.
*/
public static void clearCache() {
cache.clear();
}
/**
* Gets the resource associated with the given interface.
*
* @param javaInterface interface
* @param locale locale
* @return the resource
*/
public static AbstractResource getBundle(Class javaInterface, Locale locale) {
if (javaInterface.isInterface() == false) {
throw new IllegalArgumentException(javaInterface
+ " should be an interface.");
}
ClassPathTree path = new ClassPathTree(javaInterface);
return getBundleAux(path, locale, true);
}
/**
* Gets the resource associated with the given interface.
*
* @param javaInterface interface
* @param locale locale
* @return the resource
*/
public static AbstractResource getBundle(JClassType javaInterface,
Locale locale) {
return getBundleAux(new JClassTypePathTree(javaInterface), locale, true);
}
/**
* Gets the resource associated with the given path.
*
* @param path the path
* @param locale locale
* @return the resource
*/
public static AbstractResource getBundle(String path, Locale locale) {
return getBundleAux(new SimplePathTree(path), locale, true);
}
public static String getResourceName(JClassType targetClass) {
String name = targetClass.getName();
if (targetClass.isMemberType()) {
name = name.replace('.', '$');
}
return name;
}
private static List findAlternativeParents(
ResourceFactory.AbstractPathTree tree, Locale locale) {
List altParents = null;
if (tree != null) {
altParents = new ArrayList();
for (int i = 0; i < tree.numChildren(); i++) {
ResourceFactory.AbstractPathTree child = tree.getChild(i);
AbstractResource altParent = getBundleAux(child, locale, false);
if (altParent != null) {
altParents.add(altParent);
}
}
}
return altParents;
}
private static AbstractResource findPrimaryParent(
ResourceFactory.AbstractPathTree tree, Locale locale) {
// Create bundle
AbstractResource parent = null;
// If we are not in the default case, calculate parent
if (locale != null) {
if (!("".equals(locale.getVariant()))) {
// parents, by default, share the list of alternativePaths;
parent = getBundleAux(tree, new Locale(locale.getLanguage(),
locale.getCountry()), false);
} else if (!"".equals(locale.getCountry())) {
parent = getBundleAux(tree, new Locale(locale.getLanguage()), false);
}
// If either no country was defined or no bundle was found, use null
// locale instead.
if (parent == null) {
parent = getBundleAux(tree, null, false);
}
}
return parent;
}
private static AbstractResource getBundleAux(
ResourceFactory.AbstractPathTree tree, Locale locale, boolean required) {
String targetPath = tree.getPath();
ClassLoader loader = AbstractResource.class.getClassLoader();
// Calculate baseName
String localizedPath = targetPath;
if (locale != null) {
localizedPath = targetPath + "_" + locale;
}
AbstractResource result = (AbstractResource) cache.get(localizedPath);
if (result != null) {
if (result == NOT_FOUND) {
return null;
} else {
return result;
}
}
String partualPath = localizedPath.replace('.', '/');
AbstractResource parent = findPrimaryParent(tree, locale);
List altParents = findAlternativeParents(tree, locale);
AbstractResource found = null;
for (int i = 0; i < loaders.size(); i++) {
ResourceFactory element = (ResourceFactory) loaders.get(i);
String path = partualPath + "." + element.getExt();
InputStream m = loader.getResourceAsStream(path);
if (m != null) {
found = element.load(m);
found.setPath(partualPath);
found.setPrimaryParent(parent);
found.setLocale(locale);
for (int j = 0; j < altParents.size(); j++) {
AbstractResource altParent = (AbstractResource) altParents.get(j);
found.addAlternativeParent(altParent);
}
found.checkKeys();
break;
}
}
if (found == null) {
if (parent != null) {
found = parent;
} else {
found = NOT_FOUND;
}
found = parent;
}
cache.put(localizedPath, found);
if (found == null && required) {
throw new MissingResourceException(
"Could not find any resource associated with " + tree.getPath(),
null, null);
}
return found;
}
abstract String getExt();
abstract AbstractResource load(InputStream m);
}