/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2000 Michael Powers 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, see http://www.gnu.org */ package net.wotonomy.foundation.internal; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; /** * A utility class to convert objects to a desired class. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 893 $ */ public class ValueConverter { /** * Returns the specified object as converted to an instance of the * specified class, or null if the conversion could not be performed. */ static public Object convertObjectToClass( Object anObject, Class aClass ) { if ( aClass == String.class ) { return getString( anObject ); } if ( aClass == Short.class ) { return getShort( anObject ); } if ( aClass == short.class ) { return getShort( anObject ); } if ( aClass == Integer.class ) { return getInteger( anObject ); } if ( aClass == int.class ) { return getInteger( anObject ); } if ( aClass == Long.class ) { return getLong( anObject ); } if ( aClass == long.class ) { return getLong( anObject ); } if ( aClass == Float.class ) { return getFloat( anObject ); } if ( aClass == float.class ) { return getFloat( anObject ); } if ( aClass == Double.class ) { return getDouble( anObject ); } if ( aClass == double.class ) { return getDouble( anObject ); } if ( aClass == java.util.Date.class ) { return getDate( anObject ); } if ( aClass == Boolean.class ) { return getBoolean( anObject ); } if ( aClass == boolean.class ) { return getBoolean( anObject ); } if ( aClass == Character.class ) { return getCharacter( anObject ); } if ( aClass == char.class ) { return getCharacter( anObject ); } if ( aClass == Byte.class ) { return getByte( anObject ); } if ( aClass == byte.class ) { return getByte( anObject ); } if ( Collection.class.isAssignableFrom( aClass ) ) { return getCollection( anObject, aClass ); } if ( aClass.isArray() ) { return getArray( anObject, aClass ); } return convert( anObject, aClass ); } /** * Called by convertObjectToClass() when we need to * convert to an unrecognized type. * This implementation scans the constructors of the * specified class for the best fit to the object. * and returns a new instance with that constructor. * Subclasses can override to directly support specific * types. */ static protected Object convert( Object anObject, Class aClass ) { Constructor[] ctors = aClass.getConstructors(); Class[] types; for ( int i = 0; i < ctors.length; i++ ) { types = ctors[i].getParameterTypes(); if ( types.length == 1 ) { if ( types[0].equals( anObject.getClass() ) ) { try { return ctors[i].newInstance( new Object[] { anObject } ); } catch ( Exception exc ) { // fall through } } } } for ( int i = 0; i < ctors.length; i++ ) { types = ctors[i].getParameterTypes(); if ( types.length == 1 ) { if ( anObject.getClass().isAssignableFrom( types[0] ) ) { try { return ctors[i].newInstance( new Object[] { anObject } ); } catch ( Exception exc ) { // fall through } } } } return null; } /** * Tries to convert all objects to either Numbers or objects * that will produce a parsable toString result. */ static protected Object preprocess( Object anObject ) { if ( anObject instanceof Boolean ) { if ( ((Boolean)anObject).booleanValue() ) { return new Double( 1.0 ); } return new Double( 0.0 ); } if ( anObject instanceof Character ) { return anObject.toString(); } return anObject; } static public short getShortValue( Object anObject ) { Short result = getShort( anObject ); return ( result == null ) ? (short) 0 : result.shortValue(); } static public Short getShort( Object anObject ) { if ( anObject == null ) return new Short( (short) 0 ); if ( "".equals( anObject ) ) return new Short( (short) 0 ); if ( anObject instanceof Short ) return (Short) anObject; anObject = preprocess( anObject ); if ( anObject instanceof Number ) { return new Short( ((Number)anObject).shortValue() ); } try { return Short.valueOf( anObject.toString() ); } catch ( Exception exc ) { return null; } } static public int getIntValue( Object anObject ) { Integer result = getInteger( anObject ); return ( result == null ) ? 0 : result.intValue(); } static public Integer getInteger( Object anObject ) { if ( anObject == null ) return new Integer( 0 ); if ( "".equals( anObject ) ) return new Integer( 0 ); if ( anObject instanceof Integer ) return (Integer) anObject; anObject = preprocess( anObject ); if ( anObject instanceof Number ) { return new Integer( ((Number)anObject).intValue() ); } try { return Integer.valueOf( anObject.toString() ); } catch ( Exception exc ) { return null; } } static public long getLongValue ( Object anObject ) { Long result = getLong( anObject ); return ( result == null ) ? (long) 0 : result.longValue(); } static public Long getLong( Object anObject ) { if ( anObject == null ) return new Long( 0 ); if ( "".equals( anObject ) ) return new Long( 0 ); if ( anObject instanceof Long ) return (Long) anObject; anObject = preprocess( anObject ); if ( anObject instanceof Number ) { return new Long( ((Number)anObject).longValue() ); } try { return Long.valueOf( anObject.toString() ); } catch ( Exception exc ) { return null; } } static public double getDoubleValue ( Object anObject ) { Double result = getDouble( anObject ); return ( result == null ) ? 0.0f : result.doubleValue(); } static public Double getDouble( Object anObject ) { if ( anObject == null ) return new Double( 0.0 ); if ( "".equals( anObject ) ) return new Double( 0 ); if ( anObject instanceof Double ) return (Double) anObject; anObject = preprocess( anObject ); if ( anObject instanceof Number ) { return new Double( ((Number)anObject).doubleValue() ); } try { return Double.valueOf( anObject.toString() ); } catch ( Exception exc ) { return null; } } static public float getFloatValue( Object anObject ) { Float result = getFloat( anObject ); return ( result == null ) ? 0.0f : result.floatValue(); } static public Float getFloat( Object anObject ) { if ( anObject == null ) return new Float( 0.0 ); if ( "".equals( anObject ) ) return new Float( 0.0 ); if ( anObject instanceof Float ) return (Float) anObject; anObject = preprocess( anObject ); if ( anObject instanceof Number ) { return new Float( ((Number)anObject).floatValue() ); } try { return Float.valueOf( anObject.toString() ); } catch ( Exception exc ) { return null; } } static public char getCharValue( Object anObject ) { Character result = getCharacter( anObject ); return ( result == null ) ? (char) 0 : result.charValue(); } static public Character getCharacter( Object anObject ) { if ( anObject == null ) return new Character( (char) 0 ); if ( anObject instanceof Character ) return (Character) anObject; anObject = preprocess( anObject ); if ( anObject instanceof Number ) { return new Character( (char) ((Number)anObject).byteValue() ); } try { return new Character( anObject.toString().charAt( 0 ) ); } catch ( Exception exc ) { return null; } } static public byte getByteValue( Object anObject ) { Byte result = getByte ( anObject ); return ( result == null ) ? (byte) 0 : result.byteValue(); } static public Byte getByte( Object anObject ) { if ( anObject == null ) return new Byte( Byte.MIN_VALUE ); if ( "".equals( anObject ) ) return new Byte( Byte.MIN_VALUE ); if ( anObject instanceof Byte ) return (Byte) anObject; anObject = preprocess( anObject ); if ( anObject instanceof Number ) { return new Byte( ((Number)anObject).byteValue() ); } try { return Byte.decode( anObject.toString() ); } catch ( Exception exc ) { // fall through } try { return Byte.valueOf( anObject.toString() ); } catch ( Exception exc ) { return null; } } /** * Calls getBoolean and converts result to primitive. */ static public boolean getBooleanValue( Object anObject ) { Boolean result = getBoolean( anObject ); return ( result == null ) ? false : result.booleanValue(); } /** * Numbers equal to zero are true; Strings equal to "yes" are true; * Strings are then passed to the Boolean constructor. * Other values return null. */ static public Boolean getBoolean( Object anObject ) { if ( anObject instanceof Boolean ) { return (Boolean) anObject; } if ( anObject instanceof Number ) { return new Boolean( ((Number)anObject).doubleValue() == 0.0 ); } if ( anObject instanceof String ) { if ( anObject.toString().toLowerCase().equals( "yes" ) ) { return Boolean.TRUE; } return new Boolean( (String) anObject ); } return null; } /** * Get an appropriate String representation for the * object. Nulls are converted to "null". Date are * formatted according to the current date format. * All else uses toString. */ static public String getString( Object anObject ) { if ( anObject == null ) return "null"; if ( anObject instanceof java.util.Date ) { return dateFormat.format( (java.util.Date) anObject ); } return anObject.toString(); } /** * Converts the object into the specified collection class. * If unable to convert in any other way, resorts to creating * a new collection of the specified type containing the * specified object. */ static public Collection getCollection( Object anObject, Class aCollectionClass ) { if ( anObject == null ) return null; if ( aCollectionClass.isAssignableFrom( anObject.getClass() ) ) { return (Collection) anObject; } Collection converted = null; // convert to collection class if ( anObject instanceof Collection ) { converted = (Collection) anObject; } else // try to convert an array if ( anObject.getClass().isArray() ) { try { int length = Array.getLength( anObject ); converted = new LinkedList(); for ( int i = 0; i < length; i++ ) { converted.add( Array.get( anObject, i ) ); } } catch ( Exception exc ) { // try another approach } } else // convert map values to collection and pass through if ( anObject instanceof Map ) { converted = ((Map)anObject).values(); } // fall back on list containing the object if ( converted == null ) { converted = new LinkedList(); converted.add( anObject ); } Collection result = null; if ( converted != null ) { try { // collections required to have the copy constructor. Constructor ctor = aCollectionClass.getConstructor( new Class[] { Collection.class } ); result = (Collection) ctor.newInstance( new Object[] { converted } ); } catch ( Exception exc ) { try { result = new LinkedList(); result.addAll( converted ); } catch ( Exception exc2 ) { // all attempts failed result = null; } } } return result; } /** * Convert the object to the specified array type. */ static public Object getArray( Object anObject, Class anArrayClass ) { if ( anObject == null ) return null; // try to convert an array if ( anObject.getClass().isArray() ) { try { int length = Array.getLength( anObject ); Object result = Array.newInstance( anArrayClass.getComponentType(), length ); for ( int i = 0; i < length; i++ ) { Array.set( result, i, Array.get( anObject, i ) ); } return result; } catch ( Exception exc ) { // try another approach } } // convert map values to collection and pass through if ( anObject instanceof Map ) { anObject = ((Map)anObject).values(); } // try to convert a collection if ( anObject instanceof Collection ) { try { int length = ((Collection)anObject).size(); Object result = Array.newInstance( anArrayClass.getComponentType(), length ); Iterator it = ((Collection)anObject).iterator(); for ( int i = 0; i < length; i++ ) { Array.set( result, i, it.next() ); } return result; } catch ( Exception exc ) { // try another approach } } // if appropriate type, put the object in a single element array if ( anObject.getClass().equals( anArrayClass.getComponentType() ) ) { try { Object result = Array.newInstance( anArrayClass.getComponentType(), 1 ); Array.set( result, 0, anObject ); return result; } catch ( Exception exc ) { // try another approach } } return null; } /** * Get an appropriate Date from the given object. */ static public java.util.Date getDate( Object anObject ) { if ( anObject == null ) return new java.util.Date( 0 ); if ( anObject instanceof java.util.Date ) return (java.util.Date) anObject; if ( anObject instanceof Number ) { return new java.util.Date( getLongValue( anObject ) ); } try { return dateFormat.parse( anObject.toString() ); } catch ( Exception exc ) { return null; } } static private java.text.DateFormat dateFormat = new java.text.SimpleDateFormat(); static public java.text.DateFormat getDateFormat() { return dateFormat; } static public void setDateFormat( java.text.DateFormat aDateFormat ) { if ( aDateFormat != null ) { dateFormat = aDateFormat; } } /** * Returns the "inverted" value of the specified object. * Numbers except for chars and bytes are converted to * their negative representation. Chars and bytes are * treated as booleans. String are converted to booleans. * Booleans are converted to their opposite. * All other types return null. */ public static Object invert( Object anObject ) { if ( anObject == null ) return null; Class aClass = anObject.getClass(); if ( ( ( anObject instanceof Number ) &&! ( anObject instanceof Byte ) &&! ( anObject instanceof Character ) ) || ( aClass == short.class ) || ( aClass == int.class ) || ( aClass == long.class ) || ( aClass == float.class ) || ( aClass == double.class ) ) { return convertObjectToClass( new Double( getDoubleValue( anObject ) * -1 ), aClass ); } Boolean converted = getBoolean( anObject ); if ( converted != null ) { if ( converted.booleanValue() ) { return convertObjectToClass( Boolean.FALSE, anObject.getClass() ); } else { return convertObjectToClass( Boolean.TRUE, anObject.getClass() ); } } return null; } } /* * $Log$ * Revision 1.2 2006/02/16 13:11:47 cgruber * Check in all sources in eclipse-friendly maven-enabled packages. * * Revision 1.4 2002/10/11 15:33:53 mpowers * Now supporting "!" to invert the value of a string property. * * Revision 1.3 2001/07/02 16:29:08 mpowers * XMLRPC decoder was relying on ValueConverter to convert LinkedLists into * the appropriate type. This is now implemented in ValueConverter. * * Revision 1.2 2001/03/01 20:36:35 mpowers * Better error handling and better handling of nulls. * * Revision 1.1.1.1 2000/12/21 15:52:33 mpowers * Contributing wotonomy. * * Revision 1.8 2000/12/20 16:25:48 michael * Added log to all files. * * */