/*
* 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 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.util;
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;
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;
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<String> failedLoads;
private transient LFUMap<String, LibLoaderResourceBundle> successfulLoads;
private transient ResourceManager manager;
private transient ResourceKey baseKey;
private Locale locale;
private TimeZone timeZone;
public LibLoaderResourceBundleFactory() {
this.failedLoads = new HashSet<String>();
this.successfulLoads = new LFUMap<String, LibLoaderResourceBundle>( 30 );
}
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 = successfulLoads.get( propsName );
if ( bundle != null ) {
return bundle;
}
if ( manager == null ) {
manager = new ResourceManager();
}
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 ) {
if ( locale == null ) {
throw new NullPointerException();
}
this.locale = locale;
}
public void setTimeZone( final TimeZone timeZone ) {
if ( timeZone == null ) {
throw new NullPointerException();
}
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<String>) failedLoads.clone();
o.successfulLoads = (LFUMap<String, LibLoaderResourceBundle>) successfulLoads.clone();
return o;
}
private void readObject( final ObjectInputStream stream ) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
failedLoads = new HashSet<String>();
successfulLoads = new LFUMap<String, LibLoaderResourceBundle>( 30 );
}
}