/* ===========================================================
* TradeManager : An application to trade strategies for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Project Info: org.trade
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Oracle, Inc.
* in the United States and other countries.]
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Original Author: Simon Allen;
* Contributor(s): -;
*
* Changes
* -------
*
*/
package org.trade.core.conversion;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* This class is used to convert values of one type to another type. It
* delegates conversion to registered converter instances which implement a
* common interface: JavaTypeConverter.
*
* To make a conversion, execute something along the lines of the following:
*
* <code>
* <p>NewClass newValue = (NewClass) JavaTypeTranslator(NewClass.class, oldValue);
* </code>
*
* New converters can be registered with the JavaTypeTranslator to either change
* existing conversion behaviour or to enable previously unsupported
* conversions. To register a new converter simply execute the following:
*
* <code>
* <p>JavaTypeTranslator.registerConverter(new MyConverter());
* </code>
*
* @author Simon Allen
*/
public class JavaTypeTranslator {
/**
* A hashtable of hashtables. The outer hashtable consists of a set of keys
* representing target types. The values at these keys consist of inner
* hashtables whose keys represent source types. The inner hashtables'
* values are the actual converter instances used by the JavaTypeTranslator.
*/
private static Hashtable<Class<?>, Hashtable<Class<?>, JavaTypeConverter>> m_converters = new Hashtable<Class<?>, Hashtable<Class<?>, JavaTypeConverter>>();
private static Hashtable<?, ?> m_noBaseConverters = new Hashtable<Object, Object>();
private static Vector<Object> m_dynConverters = new Vector<Object>();
/**
* Flag indicating whether supertype conversions are supported by the
* JavaTypeTranslator. True is the default.
*/
private static boolean m_allowSupertypeConversions = true;
static {
// when the class is loaded register a number of
// default converter instances
registerConverter(new StringToBooleanConverter());
registerConverter(new NumberToByteConverter());
registerConverter(new StringToByteConverter());
registerConverter(new StringToCharacterConverter());
registerConverter(new StringToDateConverter());
registerConverter(new StringToBigDecimalConverter());
registerConverter(new BigDecimalToStringConverter());
registerConverter(new NumberToDoubleConverter());
registerConverter(new StringToDoubleConverter());
registerConverter(new NumberToFloatConverter());
registerConverter(new StringToFloatConverter());
registerConverter(new NumberToIntegerConverter());
registerConverter(new StringToIntegerConverter());
registerConverter(new NumberToLongConverter());
registerConverter(new StringToLongConverter());
registerConverter(new NumberToShortConverter());
registerConverter(new StringToShortConverter());
registerConverter(new StringToSQLDateConverter());
registerConverter(new StringToSQLTimeConverter());
registerConverter(new StringToSQLTimestampConverter());
registerConverter(new ObjectToStringConverter());
registerConverter(new DateToStringConverter());
registerConverter(new SQLDateToStringConverter());
registerConverter(new SQLTimeToStringConverter());
registerConverter(new SQLTimestampToStringConverter());
}
/**
* This method may be used to convert a specified object to the specified
* class type.
*
* @param targetType
* the type to be converted to
* @param sourceValue
* the object value to convert
*
*
* @return Object the converted object * @exception
* JavaTypeTranslatorException thrown if any exception occurs
*/
public static Object convert(Class<?> targetType, Object sourceValue) throws JavaTypeTranslatorException {
// before: simply return the value if it is already of the correct type
// (meaning that null simply returns null, which is always the correct
// type
// now: return an empty object of the target type, created by
// converting "" to the type.
// (if that doesn't work, just create a new istance and don't set the
// value
if (targetType.isInstance(sourceValue)) {
return sourceValue;
}
if (null == sourceValue) {
try {
return convert(targetType, "");
} catch (JavaTypeTranslatorException x) {
try {
return targetType.newInstance();
} catch (Exception illegalX) // ille
{
throw new JavaTypeTranslatorException(illegalX,
"permission is denied to create an instance of " + targetType.toString());
}
}
}
// Perform an additional check for JavaFormatForObject source instances
// if the object it is representing is null then return null as null by
// default should not be formatted
if (sourceValue instanceof JavaFormatForObject) {
if (null == ((JavaFormatForObject) sourceValue).getForObject()) {
return (null);
}
}
// locate the proper converter
Hashtable<?, ?> innerTable = m_converters.get(targetType);
// No base converters registered to deal with the conversion
if (innerTable == null) {
innerTable = m_noBaseConverters;
}
boolean topClass = true;
Class<?> sourceType = sourceValue.getClass();
while (sourceType != null) {
JavaTypeConverter converter = (JavaTypeConverter) innerTable.get(sourceType);
if (converter != null) {
// there is a converter for the given targetType and sourceType,
// try converting the source value with it
try {
return converter.convert(sourceValue);
} catch (IllegalArgumentException iae) {
throw new JavaTypeTranslatorException("The source value, of type "
+ sourceValue.getClass().getName() + ", cannot be converted to " + targetType.getName()
+ " because it is not in the proper format");
}
}
if (m_allowSupertypeConversions) {
// Try the registered dynamic converters
if (topClass) {
// Check the Dynamic Converters
Enumeration<Object> en = m_dynConverters.elements();
while (en.hasMoreElements()) {
JavaDynamicTypeConverter dc = (JavaDynamicTypeConverter) en.nextElement();
if (dc.supportsConversion(targetType, sourceValue)) {
// The first matching dynamic converter will be used
return dc.convert(targetType, sourceValue);
}
}
}
// there is no converter for the given targetType and sourceType
// so see if there is one for the given targetType and
// sourceType
// superclass
sourceType = sourceType.getSuperclass();
// Have already tried the dynamic converters
topClass = false;
} else {
// there is no converter for the given targetType and sourceType
// so bail and throw an exception
sourceType = null;
}
}
// If I get to here - there are no registered converters to handle the
// conversion
throw new JavaTypeTranslatorException("There is no converter for converting " + sourceValue.getClass().getName()
+ " values to " + targetType.getName());
}
/**
* This method may be used to register a converter with the
* JavaTypeTranslator. Any converter which has previously been registered
* with the JavaTypeTranslator that has the same source and target types
* will be replaced and the new converter being registered will be used for
* those types instead.
*
* @param theConverter
* the converter instance to register
*/
public static void registerConverter(JavaTypeConverter theConverter) {
Class<?> targetType = theConverter.getTargetType();
Class<?> sourceType = theConverter.getSourceType();
Hashtable<Class<?>, JavaTypeConverter> innerTable = m_converters.get(targetType);
if (innerTable != null) {
// add the converter to the existing list of
// converters for its targetType, replacing
// any converter which has the same sourceType
// as well
innerTable.put(sourceType, theConverter);
} else {
// there is no list of existing converters for
// this converter's targetType, create one and
// add the converter to it
innerTable = new Hashtable<Class<?>, JavaTypeConverter>(10);
innerTable.put(sourceType, theConverter);
m_converters.put(targetType, innerTable);
}
}
/**
* This method may be used to register a dynamic converter with the
* JavaTypeTranslator.
*
* @param theConverter
* the converter instance to register
*/
public static void registerDynamicTypeConverter(JavaDynamicTypeConverter theConverter) {
if (!m_dynConverters.contains(theConverter)) {
m_dynConverters.addElement(theConverter);
}
}
/**
* This method may be used to set whether the JavaTypeTranslator will use a
* converter whose source type is a superclass of a source type being
* converted from, if there is no converter available for that specific
* source type. For example, it determines whether a Number to String
* converter will be used if no Double to String converter is available.
*
* @param allowSupertypes
* flag setting whether supertype conversions are supported. True
* indicates supertype conversions are supported, false indicates
* they are not.
*/
public static void setAllowSupertypeConversions(boolean allowSupertypes) {
m_allowSupertypeConversions = allowSupertypes;
}
/**
* This method may be used to verify whether the JavaTypeTranslator will use
* a converter whose source type is a superclass of a source type being
* converted from if there is no converter available for that specific
* source type. For example, it indicates whether a Number to String
* converter will be used if no Double to String converter is available.
*
*
* @return boolean Flag indicating whether supertype conversions are
* supported. True indicates supertype conversions are supported,
* false indicates they are not. True is the default.
*/
public static boolean isAllowSupertypeConversions() {
return m_allowSupertypeConversions;
}
}