/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.HashSet;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.TimeZone;
import org.pentaho.reporting.engine.classic.core.ExtendedResourceBundleFactory;
import org.pentaho.reporting.libraries.base.util.LFUMap;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
/**
* Todo: Document Me
*
* @author Thomas Morgner
*/
public class LibLoaderResourceBundleFactory implements ExtendedResourceBundleFactory
{
private static class LibLoaderResourceBundle extends PropertyResourceBundle
{
private LibLoaderResourceBundle(final InputStream stream)
throws IOException
{
super(stream);
setParent(parent);
}
/**
* Sets the parent bundle of this bundle. The parent bundle is searched by {@link #getObject getObject} when this
* bundle does not contain a particular resource.
*
* @param parent this bundle's parent bundle.
*/
public void setParent(final ResourceBundle parent)
{
super.setParent(parent);
}
}
private transient HashSet failedLoads;
private transient LFUMap successfulLoads;
private transient ResourceManager manager;
private transient ResourceKey baseKey;
private Locale locale;
private TimeZone timeZone;
public LibLoaderResourceBundleFactory()
{
this.failedLoads = new HashSet();
this.successfulLoads = new LFUMap(30);
}
public LibLoaderResourceBundleFactory(final Locale locale,
final TimeZone timeZone)
{
this(null, null, locale, timeZone);
}
public LibLoaderResourceBundleFactory(final ResourceManager manager,
final ResourceKey baseKey,
final Locale locale,
final TimeZone timeZone)
{
this();
this.manager = manager;
this.baseKey = baseKey;
this.locale = locale;
this.timeZone = timeZone;
}
/**
* Creates a resource bundle for the given key. How that key is interpreted depends on the used concrete
* implementation of this interface.
*
* @param key the key that identifies the resource bundle
* @return the created resource bundle
* @throws MissingResourceException if no resource bundle for the specified base name can be found
* @noinspection MagicCharacter
*/
public ResourceBundle getResourceBundle(final String key)
{
final String keyAsPath = key.replace('.', '/');
final Locale locale = getLocale();
final String variant = locale.getVariant();
final String country = locale.getCountry();
final String language = locale.getLanguage();
final String fullName;
if ("".equals(variant) == false)
{
fullName = locale.getLanguage() + '_' + locale.getCountry() + '_' + locale.getVariant();
}
else
{
fullName = null;
}
final String cntryName;
if ("".equals(country) == false)
{
cntryName = locale.getLanguage() + '_' + locale.getCountry();
}
else
{
cntryName = null;
}
final String langName;
if ("".equals(language) == false)
{
langName = locale.getLanguage();
}
else
{
langName = null;
}
LibLoaderResourceBundle fullProperties = null;
if (fullName != null)
{
final String propsName = keyAsPath + '_' + fullName + ".properties"; // NON-NLS
fullProperties = loadProperties(propsName);
}
LibLoaderResourceBundle cntryProperties = null;
if (cntryName != null)
{
final String propsName = keyAsPath + '_' + cntryName + ".properties"; // NON-NLS
cntryProperties = loadProperties(propsName);
}
LibLoaderResourceBundle langProperties = null;
if (langName != null)
{
final String propsName = keyAsPath + '_' + langName + ".properties"; // NON-NLS
langProperties = loadProperties(propsName);
}
final String propsName = keyAsPath + ".properties"; // NON-NLS
final LibLoaderResourceBundle defaultProperties = loadProperties(propsName);
if (langProperties == null && cntryProperties == null && fullProperties == null && defaultProperties == null)
{
throw new MissingResourceException("No such bundle: " + keyAsPath, key, null);
}
if (fullProperties != null)
{
if (cntryProperties != null)
{
fullProperties.setParent(cntryProperties);
}
else if (langProperties != null)
{
fullProperties.setParent(langProperties);
}
else if (defaultProperties != null)
{
fullProperties.setParent(defaultProperties);
}
}
if (cntryProperties != null)
{
if (langProperties != null)
{
cntryProperties.setParent(langProperties);
}
else if (defaultProperties != null)
{
cntryProperties.setParent(defaultProperties);
}
}
if (langProperties != null)
{
if (defaultProperties != null)
{
langProperties.setParent(defaultProperties);
}
}
if (fullProperties != null)
{
return fullProperties;
}
if (cntryProperties != null)
{
return cntryProperties;
}
if (langProperties != null)
{
return langProperties;
}
return defaultProperties;
}
private LibLoaderResourceBundle loadProperties(final String propsName)
{
if (failedLoads.contains(propsName))
{
return null;
}
final LibLoaderResourceBundle bundle = (LibLoaderResourceBundle) successfulLoads.get(propsName);
if (bundle != null)
{
return bundle;
}
if (manager == null)
{
manager = new ResourceManager();
manager.registerDefaults();
}
if (baseKey != null)
{
try
{
final ResourceKey resourceKey = manager.deriveKey(baseKey, propsName);
final ResourceData resource = manager.load(resourceKey);
final InputStream instream = resource.getResourceAsStream(manager);
try
{
final LibLoaderResourceBundle resourceBundle = new LibLoaderResourceBundle(instream);
successfulLoads.put(propsName, resourceBundle);
return resourceBundle;
}
finally
{
instream.close();
}
}
catch (Exception e)
{
// silently ignore ..
}
}
try
{
// try to load the bundle via classpath ...
final ResourceKey resourceKey = manager.createKey("res://" + propsName); // NON-NLS
final ResourceData resource = manager.load(resourceKey);
final InputStream instream = resource.getResourceAsStream(manager);
try
{
final LibLoaderResourceBundle resourceBundle = new LibLoaderResourceBundle(instream);
successfulLoads.put(propsName, resourceBundle);
return resourceBundle;
}
finally
{
instream.close();
}
}
catch (Exception e)
{
// ignored ..
failedLoads.add(propsName);
}
return null;
}
/**
* Returns the locale that will be used to create the resource bundle. This locale is also used to initialize the
* java.text.Format instances used by the report.
*
* @return the locale.
*/
public Locale getLocale()
{
return locale;
}
public TimeZone getTimeZone()
{
return timeZone;
}
public void setLocale(final Locale locale)
{
this.locale = locale;
}
public void setTimeZone(final TimeZone timeZone)
{
this.timeZone = timeZone;
}
public void setResourceLoader(final ResourceManager resourceManager, final ResourceKey contextKey)
{
this.manager = resourceManager;
this.baseKey = contextKey;
this.failedLoads.clear();
this.successfulLoads.clear();
}
public Object clone() throws CloneNotSupportedException
{
final LibLoaderResourceBundleFactory o = (LibLoaderResourceBundleFactory) super.clone();
o.failedLoads = (HashSet) failedLoads.clone();
o.successfulLoads = (LFUMap) successfulLoads.clone();
return o;
}
private void readObject(final ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
failedLoads = new HashSet();
successfulLoads = new LFUMap(30);
}
}