/*
* 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) 2006 - 2013 Pentaho Corporation and Contributors. All rights reserved.
*/
package org.pentaho.reporting.libraries.fonts.encoding;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.fonts.LibFontBoot;
import org.pentaho.reporting.libraries.fonts.encoding.manual.Ascii;
import org.pentaho.reporting.libraries.fonts.encoding.manual.BuiltInJavaEncoding;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.libraries.resourceloader.factory.property.PropertiesResourceFactory;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* A global registry for all supported encodings. This offers the option to fall back to Java - which is enabled by
* default.
*
* @author Thomas Morgner.
*/
public final class EncodingRegistry {
private static final Log logger = LogFactory.getLog( EncodingRegistry.class );
/**
* Implementation doc: This class uses several sources to load the encodings.
* The primary source are the known manually written encodings; they provide
* the base set (namely UFT-16LE and ISO-8859-1).
*
* The 'encodingsMapping' hashmap contains the name of the encoding along
* with the classname. For now, we do not cache these encodings; we should
* to it, but we dont.
*
* The second set are the generated mappings; the hashmap contains the name
* of the encoding along with the resourcekey pointing to the .ser files.
* These files must be serialized objects of the type "EncodingData". For
* now, only External8BitEncodingData objects are accepted.
*
* And finally, we use the JDK for all not-known encodings.
*/
/**
* the string that should be encoded. This is a simple space, and should be supported by all encodings - but will
* cause InvalidEncodingExceptions if the encoding does not exist.
*/
private static final String TEST_STRING = " ";
private HashMap encodingsMapping;
private HashMap generatedMapping;
private HashMap fallbackMapping;
private HashMap aliases;
private ResourceManager manager;
private static EncodingRegistry instance;
public static final String ENCODING_ALIAS_PREFIX = "org.pentaho.reporting.libraries.fonts.encoding.alias.";
public static synchronized EncodingRegistry getInstance() {
if ( instance == null ) {
instance = new EncodingRegistry();
instance.registerDefaults();
}
return instance;
}
private EncodingRegistry() {
this.manager = new ResourceManager();
this.manager.registerFactoryCache();
this.manager.registerDataCache();
this.manager.registerDefaultLoaders();
this.manager.registerFactory( new EncodingFactory() );
this.manager.registerFactory( new PropertiesResourceFactory() );
this.encodingsMapping = new HashMap();
this.generatedMapping = new HashMap();
this.fallbackMapping = new HashMap();
this.aliases = new HashMap();
}
private void registerDefaults() {
final Configuration config = LibFontBoot.getInstance().getGlobalConfig();
final Iterator encodings = config.findPropertyKeys( "org.pentaho.reporting.libraries.fonts.encoding.manual." );
while ( encodings.hasNext() ) {
final String key = (String) encodings.next();
final String encodingClass = config.getConfigProperty( key );
final Object maybeEncoding = ObjectUtilities.loadAndInstantiate
( encodingClass, EncodingRegistry.class, Encoding.class );
if ( maybeEncoding != null ) {
// ok, loaded perfectly ..
final Encoding encoding = (Encoding) maybeEncoding;
final String name = encoding.getName();
//Log.debug ("Manual mapping added. " + name);
encodingsMapping.put( normalizeEncodingName( name ), encodingClass );
}
}
final Iterator generateDirs =
config.findPropertyKeys( "org.pentaho.reporting.libraries.fonts.encoding.generated." );
while ( generateDirs.hasNext() ) {
final String key = (String) generateDirs.next();
final String dataLocation = config.getConfigProperty( key );
try {
final ResourceKey resKey = manager.createKey( dataLocation );
final Resource res = manager.create( resKey, null, Properties.class );
final Properties props = (Properties) res.getResource();
final Iterator vals = props.entrySet().iterator();
while ( vals.hasNext() ) {
final Map.Entry entry = (Map.Entry) vals.next();
final String encName = (String) entry.getKey();
final String encPath = (String) entry.getValue();
final ResourceKey encKey = manager.deriveKey( resKey, encPath );
//Log.debug ("Generated mapping added. " + encName);
generatedMapping.put( normalizeEncodingName( encName ), encKey );
}
} catch ( Exception e ) {
// ignore that key, it is behaving badly ..
e.printStackTrace();
}
}
final Iterator aliasesIt = config.findPropertyKeys
( ENCODING_ALIAS_PREFIX );
while ( aliasesIt.hasNext() ) {
final String key = (String) aliasesIt.next();
final String alias = key.substring( ENCODING_ALIAS_PREFIX.length() );
final String target = config.getConfigProperty( key );
aliases.put( normalizeEncodingName( alias ), normalizeEncodingName( target ) );
}
}
/**
* Returns <code>true</code> if the encoding is valid, and <code>false</code> otherwise.
*
* @param encoding the encoding (name).
* @return A boolean.
*/
public boolean isSupportedEncoding( final String encoding ) {
if ( encoding == null ) {
throw new NullPointerException();
}
final String key = normalizeEncodingName( encoding );
if ( encodingsMapping.containsKey( key ) ) {
return true;
}
if ( generatedMapping.containsKey( key ) ) {
return true;
}
if ( fallbackMapping.containsKey( key ) ) {
return true;
}
return isEncodingSupportedJVM( encoding );
}
private String normalizeEncodingName( final String name ) {
final String lcName = name.toLowerCase();
final String retval = lcName.replace( '_', '-' );
final String alias = (String) aliases.get( retval );
if ( alias != null ) {
return alias;
}
return retval;
}
private boolean isEncodingSupportedJVM( final String encoding ) {
try {
//noinspection ResultOfMethodCallIgnored
TEST_STRING.getBytes( encoding );
fallbackMapping.put( encoding, Boolean.TRUE );
return true;
} catch ( Exception ue ) {
// There is a known JDK bug in JDK 1.5 with Cp903 encoding
logger.info( "Encoding " + encoding + " is not supported." );
return false;
}
}
/**
* Helper method to read the platform default encoding from the VM's system properties.
*
* @return the contents of the system property "file.encoding".
*/
public static String getPlatformDefaultEncoding() {
return LibFontBoot.getInstance().getGlobalConfig().getConfigProperty( "file.encoding", "Cp1252" );
}
public Encoding getEncoding( final String name ) {
final String key = normalizeEncodingName( name );
final String manual = (String) encodingsMapping.get( key );
if ( manual != null ) {
// we tested the instantiation during the initialization,
// so we can be sure that no errors appear here
return (Encoding) ObjectUtilities.loadAndInstantiate
( manual, EncodingRegistry.class, Encoding.class );
}
final ResourceKey generated = (ResourceKey) generatedMapping.get( key );
if ( generated != null ) {
try {
final Resource res = manager.create( generated, null, Encoding.class );
final Object o = res.getResource();
if ( o instanceof EncodingCore ) {
return new ExternalEncoding( name, (EncodingCore) o, res );
}
} catch ( Exception e ) {
// fall back ...
logger.warn( "Failed to create external-encoding instance for key " + key, e );
}
}
if ( isEncodingSupportedJVM( name ) ) {
return new BuiltInJavaEncoding( name, true );
} else if ( isEncodingSupportedJVM( key ) ) {
return new BuiltInJavaEncoding( key, true );
} else {
return new Ascii();
}
}
//
// public static void main(String[] args) throws EncodingException
// {
// LibFontBoot.getInstance().start();
// final EncodingRegistry reg = EncodingRegistry.getInstance();
// final Encoding encoding = reg.getEncoding("MacRoman");
// System.out.println ("MacRoman: " + encoding);
// byte[] text = new byte[] { 'A', 'b', 'c' };
// final ByteBuffer byteBuffer = new ByteBuffer(text);
// final CodePointBuffer cp = encoding.decode(byteBuffer, null);
// System.out.println (Utf16LE.getInstance().encodeString(cp));
//
// }
}