From aedc34d55462a75e329bbf342251ff6504cd117e Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Sun, 19 May 2024 17:56:33 -0400 Subject: Initial import from SVN --- .../foundation/internal/ValueConverter.java | 718 +++++++++++++++++++++ 1 file changed, 718 insertions(+) create mode 100644 projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java (limited to 'projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java') diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java new file mode 100644 index 0000000..d6bc797 --- /dev/null +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ValueConverter.java @@ -0,0 +1,718 @@ +/* +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. + * + * + */ + -- cgit v1.2.3