/* 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. * * */