/* =========================================================== * 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; } }