diff options
Diffstat (limited to 'projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal')
12 files changed, 2887 insertions, 3351 deletions
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java index ddf347d..16ef393 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Duplicator.java @@ -26,274 +26,219 @@ import java.io.*; import java.util.*; //collections /** -* Duplicator makes use of Introspector to duplicate objects, -* either by shallow copy, deep copy, or by copying properties -* from one object to apply to another object. You may find this -* class useful because java.lang.Object.clone() only supports -* shallow copying. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 895 $ -*/ + * Duplicator makes use of Introspector to duplicate objects, either by shallow + * copy, deep copy, or by copying properties from one object to apply to another + * object. You may find this class useful because java.lang.Object.clone() only + * supports shallow copying. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 895 $ + */ + +public class Duplicator { + /** + * Used to represent null values for properties in the maps returned by + * readProperties and cloneProperties and in the parameter to writeProperties. + * This actually references the NSNull instance. + */ + public static final Object NULL = NSNull.nullValue(); + private static NSSelector clone = new NSSelector("clone"); + + /** + * Returns a list of properties for the specified class that are both readable + * and writable. + */ + static public List editablePropertiesForObject(Object anObject) { + List readProperties = new ArrayList(); + String[] read = Introspector.getReadPropertiesForObject(anObject); + for (int i = 0; i < read.length; i++) { + readProperties.add(read[i]); + } + + List properties = new ArrayList(); + String[] write = Introspector.getWritePropertiesForObject(anObject); + for (int i = 0; i < write.length; i++) { + properties.add(write[i]); + } + + // only use properties on both lists: read/write + properties.retainAll(readProperties); + + return properties; + } -public class Duplicator -{ - /** - * Used to represent null values for properties in the - * maps returned by readProperties and cloneProperties - * and in the parameter to writeProperties. - * This actually references the NSNull instance. - */ - public static final Object NULL = NSNull.nullValue(); - private static NSSelector clone = new NSSelector( "clone" ); + /** + * Returns a Map containing only the mutable properties for the specified object + * and their values. Any null values for properties will be represented with the + * NULL object. + */ + static public Map readPropertiesForObject(Object anObject) { + NSMutableDictionary result = new NSMutableDictionary(); - /** - * Returns a list of properties for the specified class - * that are both readable and writable. - */ - static public List editablePropertiesForObject( - Object anObject ) - { - List readProperties = new ArrayList(); - String[] read = Introspector.getReadPropertiesForObject( anObject ); - for ( int i = 0; i < read.length; i++ ) - { - readProperties.add( read[i] ); - } + String key; + Object value; + Iterator it = editablePropertiesForObject(anObject).iterator(); + while (it.hasNext()) { + key = it.next().toString(); + value = Introspector.get(anObject, key); + if (value == null) + value = NULL; + result.setObjectForKey(value, key); + } + return result; + } - List properties = new ArrayList(); - String[] write = Introspector.getWritePropertiesForObject( anObject ); - for ( int i = 0; i < write.length; i++ ) - { - properties.add( write[i] ); - } - - // only use properties on both lists: read/write - properties.retainAll( readProperties ); + /** + * Returns a Map containing only the mutable properties for the specified object + * and deep clones of their values. Nulls are represented by the NULL object. + */ + static public Map clonePropertiesForObject(Object anObject) { + Object key, value; + Map result = readPropertiesForObject(anObject); + Iterator it = result.keySet().iterator(); + while (it.hasNext()) { + key = it.next(); + value = result.get(key); + value = deepClone(value); + result.put(key, value); + } + return result; + } - return properties; - } - - /** - * Returns a Map containing only the mutable properties - * for the specified object and their values. - * Any null values for properties will be represented with - * the NULL object. - */ - static public Map readPropertiesForObject( - Object anObject ) - { - NSMutableDictionary result = new NSMutableDictionary(); - - String key; - Object value; - Iterator it = editablePropertiesForObject( anObject ).iterator(); - while ( it.hasNext() ) - { - key = it.next().toString(); - value = Introspector.get( anObject, key ); - if ( value == null ) value = NULL; - result.setObjectForKey( value, key ); - } - return result; - } - - /** - * Returns a Map containing only the mutable properties - * for the specified object and deep clones of their values. - * Nulls are represented by the NULL object. - */ - static public Map clonePropertiesForObject( - Object anObject ) - { - Object key, value; - Map result = readPropertiesForObject( anObject ); - Iterator it = result.keySet().iterator(); - while ( it.hasNext() ) - { - key = it.next(); - value = result.get( key ); - value = deepClone( value ); - result.put( key, value ); - } - return result; - } - - /** - * Applies the map of properties and values to the - * specified object. Null values for properties must - * be represented by the NULL object. - */ - static public void writePropertiesForObject( - Map aMap, Object anObject ) - { - String key; - Object value; - Iterator it = aMap.keySet().iterator(); - while ( it.hasNext() ) - { - key = it.next().toString(); - value = aMap.get( key ); - if ( NULL.equals( value ) ) value = null; - Introspector.set( anObject, key, value ); - } - } - - /** - * Creates a new copy of the specified object. - * This implementation tries to call clone(), - * and failing that, calls newInstance - * and then calls copy() to transfer the values. - * @throws WotonomyException if any operation fails. - */ - static public Object clone( - Object aSource ) - { - Object result = null; - if ( clone.implementedByObject( aSource ) ) - { - try - { - result = clone.invoke( aSource ); - return result; - } - catch ( Exception exc ) - { - // fall back on newInstance() - } - } - - Class c = aSource.getClass(); - try - { - result = c.newInstance(); - } - catch ( Exception exc ) - { - throw new WotonomyException( exc ); - } - return copy( aSource, result ); - } + /** + * Applies the map of properties and values to the specified object. Null values + * for properties must be represented by the NULL object. + */ + static public void writePropertiesForObject(Map aMap, Object anObject) { + String key; + Object value; + Iterator it = aMap.keySet().iterator(); + while (it.hasNext()) { + key = it.next().toString(); + value = aMap.get(key); + if (NULL.equals(value)) + value = null; + Introspector.set(anObject, key, value); + } + } - /** - * Creates a deep copy of the specified object. - * Every object in this objects graph will be - * duplicated with new instances. - * @throws WotonomyException if any operation fails. - */ - static public Object deepClone( - Object aSource ) - { - // the only known way to deep copy in - // java without native code is serialization - - try - { - ByteArrayOutputStream byteOutput = - new ByteArrayOutputStream(); - ObjectOutputStream objectOutput = - new ObjectOutputStream( byteOutput ); - - objectOutput.writeObject( aSource ); - objectOutput.flush(); - objectOutput.close(); - - ByteArrayInputStream byteInput = - new ByteArrayInputStream( byteOutput.toByteArray() ); - ObjectInputStream objectInput = - new ObjectInputStream( byteInput ); - return objectInput.readObject(); - } - catch ( Exception exc ) - { - throw new WotonomyException( "Error cloning object: " + aSource, exc ); - } - } + /** + * Creates a new copy of the specified object. This implementation tries to call + * clone(), and failing that, calls newInstance and then calls copy() to + * transfer the values. + * + * @throws WotonomyException if any operation fails. + */ + static public Object clone(Object aSource) { + Object result = null; + if (clone.implementedByObject(aSource)) { + try { + result = clone.invoke(aSource); + return result; + } catch (Exception exc) { + // fall back on newInstance() + } + } - /** - * Copies values from one object to another. - * Returns the destination object. - * @throws WotonomyException if any operation fails. - */ - static public Object copy( - Object aSource, Object aDestination ) - { - try - { - writePropertiesForObject( - readPropertiesForObject( aSource ), aDestination ); - } - catch ( RuntimeException exc ) - { - throw new WotonomyException( exc ); - } - return aDestination; - } - - /** - * Deeply clones the values from one object and applies them - * to another object. - * Returns the destination object. - * @throws WotonomyException if any operation fails. - */ - static public Object deepCopy( - Object aSource, Object aDestination ) - { - try - { - writePropertiesForObject( - clonePropertiesForObject( aSource ), aDestination ); - } - catch ( RuntimeException exc ) - { - throw new WotonomyException( exc ); - } - return aDestination; - } + Class c = aSource.getClass(); + try { + result = c.newInstance(); + } catch (Exception exc) { + throw new WotonomyException(exc); + } + return copy(aSource, result); + } + + /** + * Creates a deep copy of the specified object. Every object in this objects + * graph will be duplicated with new instances. + * + * @throws WotonomyException if any operation fails. + */ + static public Object deepClone(Object aSource) { + // the only known way to deep copy in + // java without native code is serialization + + try { + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + ObjectOutputStream objectOutput = new ObjectOutputStream(byteOutput); + + objectOutput.writeObject(aSource); + objectOutput.flush(); + objectOutput.close(); + + ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); + ObjectInputStream objectInput = new ObjectInputStream(byteInput); + return objectInput.readObject(); + } catch (Exception exc) { + throw new WotonomyException("Error cloning object: " + aSource, exc); + } + } + + /** + * Copies values from one object to another. Returns the destination object. + * + * @throws WotonomyException if any operation fails. + */ + static public Object copy(Object aSource, Object aDestination) { + try { + writePropertiesForObject(readPropertiesForObject(aSource), aDestination); + } catch (RuntimeException exc) { + throw new WotonomyException(exc); + } + return aDestination; + } + + /** + * Deeply clones the values from one object and applies them to another object. + * Returns the destination object. + * + * @throws WotonomyException if any operation fails. + */ + static public Object deepCopy(Object aSource, Object aDestination) { + try { + writePropertiesForObject(clonePropertiesForObject(aSource), aDestination); + } catch (RuntimeException exc) { + throw new WotonomyException(exc); + } + return aDestination; + } } /* - * $Log$ - * Revision 1.1 2006/02/16 16:52:12 cgruber - * Add cvsignore crap to find off checking in binary crap. + * $Log$ Revision 1.1 2006/02/16 16:52:12 cgruber Add cvsignore crap to find off + * checking in binary crap. * - * Revision 1.1 2006/02/16 13:22:22 cgruber - * Check in all sources in eclipse-friendly maven-enabled packages. + * Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in + * eclipse-friendly maven-enabled packages. * - * Revision 1.11 2001/08/22 19:24:26 mpowers - * Providing a more helpful error message for cloning exceptions. + * Revision 1.11 2001/08/22 19:24:26 mpowers Providing a more helpful error + * message for cloning exceptions. * - * Revision 1.10 2001/03/29 03:30:36 mpowers - * Refactored duplicator a bit. + * Revision 1.10 2001/03/29 03:30:36 mpowers Refactored duplicator a bit. * Disabled MissingPropertyExceptions for now. * - * Revision 1.9 2001/03/28 14:11:23 mpowers - * Removed debugging printlns. + * Revision 1.9 2001/03/28 14:11:23 mpowers Removed debugging printlns. * - * Revision 1.8 2001/03/27 23:25:48 mpowers - * Basically reverting to the previous version. + * Revision 1.8 2001/03/27 23:25:48 mpowers Basically reverting to the previous + * version. * - * Revision 1.7 2001/03/06 23:18:13 mpowers - * Clarified some comments. + * Revision 1.7 2001/03/06 23:18:13 mpowers Clarified some comments. * - * Revision 1.6 2001/03/01 20:36:35 mpowers - * Better error handling and better handling of nulls. + * Revision 1.6 2001/03/01 20:36:35 mpowers Better error handling and better + * handling of nulls. * - * Revision 1.5 2001/02/27 21:43:40 mpowers - * Removed NullMarker class in favor of NSNull. + * Revision 1.5 2001/02/27 21:43:40 mpowers Removed NullMarker class in favor of + * NSNull. * - * Revision 1.4 2001/02/26 22:41:51 mpowers - * Implemented null placeholder classes. - * Duplicator now uses NSNull. - * No longer catching base exception class. + * Revision 1.4 2001/02/26 22:41:51 mpowers Implemented null placeholder + * classes. Duplicator now uses NSNull. No longer catching base exception class. * - * Revision 1.3 2001/02/23 21:07:46 mpowers - * Documented the NULL object. + * Revision 1.3 2001/02/23 21:07:46 mpowers Documented the NULL object. * - * Revision 1.1 2001/02/16 22:51:29 mpowers - * Now deep-cloning objects passed between editing contexts. + * Revision 1.1 2001/02/16 22:51:29 mpowers Now deep-cloning objects passed + * between editing contexts. * * */ - diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java index 2b313d0..09687e2 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/Introspector.java @@ -30,912 +30,750 @@ import java.util.Map; import java.util.Set; /** -* This Introspector is a static utility class written to work -* around limitations in PropertyDescriptor and Introspector.<br><br> -* -* Of particular note are the get() and set() methods, which will attempt -* to get and set artibrary values on arbitrary objects to the best of its -* ability, converting values as appropriate. Properties of the form -* "property.nestedproperty.anotherproperty" are supported to get and set -* values on property values directly.<br><br> -* -* Note that for naming getter methods, this class supports "get", "is", -* and also the property name itself, which supports NeXT-style properties. -* Introspector supports Maps by treating the keys a property names, -* supports Lists by treating the indexes as property names. <br><br> -* -* Numeric and boolean types can be inverted by prepending a "!" before -* the name of the property, like "manager.!active" or "task.!lag". -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ + * This Introspector is a static utility class written to work around + * limitations in PropertyDescriptor and Introspector.<br> + * <br> + * + * Of particular note are the get() and set() methods, which will attempt to get + * and set artibrary values on arbitrary objects to the best of its ability, + * converting values as appropriate. Properties of the form + * "property.nestedproperty.anotherproperty" are supported to get and set values + * on property values directly.<br> + * <br> + * + * Note that for naming getter methods, this class supports "get", "is", and + * also the property name itself, which supports NeXT-style properties. + * Introspector supports Maps by treating the keys a property names, supports + * Lists by treating the indexes as property names. <br> + * <br> + * + * Numeric and boolean types can be inverted by prepending a "!" before the name + * of the property, like "manager.!active" or "task.!lag". + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ + +public class Introspector { + // allows "hasProperty" or "property" forms + public static boolean strict = false; -public class Introspector -{ - // allows "hasProperty" or "property" forms - public static boolean strict = false; - // print exception stack traces private static boolean debug = true; - + // path separator public static final String SEPARATOR = "."; - // method cache - use hashtables for thread safety - private static Map getterMethods = new Hashtable(); - private static Map setterMethods = new Hashtable(); - - // wildcard value - using this class to represent a "wildcard" generic class. - // we have to do this when matching methods by parameter types and a - // null value is passed in - can't tell what class the null should be. - public static Class WILD = Introspector.class; - - // empty class array - prevents having to create one every time - private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; - - // use OGNL for property access - private static boolean useOGNL; - - static - { - try - { - useOGNL = ( Class.forName( "ognl.Ognl" ) != null ); - } - catch ( ClassNotFoundException t ) - { - useOGNL = false; - } - } + // method cache - use hashtables for thread safety + private static Map getterMethods = new Hashtable(); + private static Map setterMethods = new Hashtable(); -/** -* Utility method to get the read method for a property belonging to a class. -* Will search for methods in the form of "getProperty" and failing that -* "isProperty" (to handle booleans). -* @param objectClass the class whose property methods will be retrieved. -* @param aProperty The property whose method will be retrieved. -* @param paramTypes An array of class objects representing the types of parameters. -* @return The appropriate method for the class, or null if not found. -*/ - static public Method getPropertyReadMethod( - Class objectClass, String aProperty, Class[] paramTypes ) - { - Method result = null; - - result = getMethodFromClass( objectClass, aProperty, paramTypes, true ); + // wildcard value - using this class to represent a "wildcard" generic class. + // we have to do this when matching methods by parameter types and a + // null value is passed in - can't tell what class the null should be. + public static Class WILD = Introspector.class; - return result; - } + // empty class array - prevents having to create one every time + private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; -/** -* Utility method to get the write method for a property belonging to a class. -* Will search for methods in the form of "setProperty". -* @param objectClass the class whose property methods will be retrieved. -* @param aProperty The property whose method will be retrieved. -* @param paramTypes An array of class objects representing the types of parameters. -* @return The appropriate method for the class, or null if not found. -*/ - static public Method getPropertyWriteMethod( - Class objectClass, String aProperty, Class[] paramTypes ) - { - Method result = null; - - result = getMethodFromClass( objectClass, aProperty, paramTypes, false ); + // use OGNL for property access + private static boolean useOGNL; - return result; - } + static { + try { + useOGNL = (Class.forName("ognl.Ognl") != null); + } catch (ClassNotFoundException t) { + useOGNL = false; + } + } -/** -* Gets a named method from a class. Using this method is preferred because -* the results are cached and should be faster than calling Class.getMethod(). -* Note that if an object has a "get" getter method and an "is" getter method -* with the same signature defined for a given property. The "get" method -* is called. -* @param objectClass the Class whose property methods will be retrieved. -* @param aMethodName A String containing the name of the desired method. -* @param paramTypes An array of class objects representing the types of parameters. -* @return The appropriate Method from the Class, or null if not found. -*/ - static private Method getMethodFromClass( - Class objectClass, String aProperty, Class[] paramTypes, boolean doGetter) - { // System.out.print( "Introspector.getMethodFromClass: " + aMethodName + " : " ); - - Map classesToMethods = (doGetter ? - getterMethods : - setterMethods); - - Map allMethods = (Map) classesToMethods.get( objectClass ); - if (allMethods == null) - { - // need to build maps for this class - mapPropertiesForClass( objectClass ); - // now the map should exist - allMethods = (Map) classesToMethods.get( objectClass ); - } - - Method[] methods = (Method[]) allMethods.get( aProperty ); - if ( methods == null ) - { - return null; // property doesn't exist - } - - methods_loop: // walks through all methods for name - for ( int i = 0; i < methods.length; i++ ) - { - Class[] types = methods[i].getParameterTypes(); - - // if parameter lengths don't match - if ( types.length != paramTypes.length ) - { - // System.out.println( aMethodName + " : " + types.length + " != " + paramTypes.length ); - continue methods_loop; // continue with outer loop - } - - // match up each parameter - for ( int j = 0; j < types.length; j++ ) - { - // convert primitives so they'll match - ugly - // (would have thought isAssignableFrom() would catch this) - if ( types[j].isPrimitive() ) - { - if ( types[j] == Boolean.TYPE ) - { - types[j] = Boolean.class; - } - else - if ( types[j] == Character.TYPE ) - { - types[j] = Character.class; - } - else - if ( types[j] == Byte.TYPE ) - { - types[j] = Byte.class; - } - else - if ( types[j] == Short.TYPE ) - { - types[j] = Short.class; - } - else - if ( types[j] == Integer.TYPE ) - { - types[j] = Integer.class; - } - else - if ( types[j] == Long.TYPE ) - { - types[j] = Long.class; - } - else - if ( types[j] == Float.TYPE ) - { - types[j] = Float.class; - } - else - if ( types[j] == Double.TYPE ) - { - types[j] = Double.class; - } - } - - // if parameters don't match - if ( ( paramTypes[j] != WILD ) && ( ! types[j].isAssignableFrom( paramTypes[j] ) ) ) - { + /** + * Utility method to get the read method for a property belonging to a class. + * Will search for methods in the form of "getProperty" and failing that + * "isProperty" (to handle booleans). + * + * @param objectClass the class whose property methods will be retrieved. + * @param aProperty The property whose method will be retrieved. + * @param paramTypes An array of class objects representing the types of + * parameters. + * @return The appropriate method for the class, or null if not found. + */ + static public Method getPropertyReadMethod(Class objectClass, String aProperty, Class[] paramTypes) { + Method result = null; + + result = getMethodFromClass(objectClass, aProperty, paramTypes, true); + + return result; + } + + /** + * Utility method to get the write method for a property belonging to a class. + * Will search for methods in the form of "setProperty". + * + * @param objectClass the class whose property methods will be retrieved. + * @param aProperty The property whose method will be retrieved. + * @param paramTypes An array of class objects representing the types of + * parameters. + * @return The appropriate method for the class, or null if not found. + */ + static public Method getPropertyWriteMethod(Class objectClass, String aProperty, Class[] paramTypes) { + Method result = null; + + result = getMethodFromClass(objectClass, aProperty, paramTypes, false); + + return result; + } + + /** + * Gets a named method from a class. Using this method is preferred because the + * results are cached and should be faster than calling Class.getMethod(). Note + * that if an object has a "get" getter method and an "is" getter method with + * the same signature defined for a given property. The "get" method is called. + * + * @param objectClass the Class whose property methods will be retrieved. + * @param aMethodName A String containing the name of the desired method. + * @param paramTypes An array of class objects representing the types of + * parameters. + * @return The appropriate Method from the Class, or null if not found. + */ + static private Method getMethodFromClass(Class objectClass, String aProperty, Class[] paramTypes, + boolean doGetter) { // System.out.print( "Introspector.getMethodFromClass: " + aMethodName + " : " + // ); + + Map classesToMethods = (doGetter ? getterMethods : setterMethods); + + Map allMethods = (Map) classesToMethods.get(objectClass); + if (allMethods == null) { + // need to build maps for this class + mapPropertiesForClass(objectClass); + // now the map should exist + allMethods = (Map) classesToMethods.get(objectClass); + } + + Method[] methods = (Method[]) allMethods.get(aProperty); + if (methods == null) { + return null; // property doesn't exist + } + + methods_loop: // walks through all methods for name + for (int i = 0; i < methods.length; i++) { + Class[] types = methods[i].getParameterTypes(); + + // if parameter lengths don't match + if (types.length != paramTypes.length) { + // System.out.println( aMethodName + " : " + types.length + " != " + + // paramTypes.length ); + continue methods_loop; // continue with outer loop + } + + // match up each parameter + for (int j = 0; j < types.length; j++) { + // convert primitives so they'll match - ugly + // (would have thought isAssignableFrom() would catch this) + if (types[j].isPrimitive()) { + if (types[j] == Boolean.TYPE) { + types[j] = Boolean.class; + } else if (types[j] == Character.TYPE) { + types[j] = Character.class; + } else if (types[j] == Byte.TYPE) { + types[j] = Byte.class; + } else if (types[j] == Short.TYPE) { + types[j] = Short.class; + } else if (types[j] == Integer.TYPE) { + types[j] = Integer.class; + } else if (types[j] == Long.TYPE) { + types[j] = Long.class; + } else if (types[j] == Float.TYPE) { + types[j] = Float.class; + } else if (types[j] == Double.TYPE) { + types[j] = Double.class; + } + } + + // if parameters don't match + if ((paramTypes[j] != WILD) && (!types[j].isAssignableFrom(paramTypes[j]))) { // System.out.println( "Introspector.getMethodFromClass: " + // aProperty + " : " + types[j] + " != " + paramTypes[j] ); - continue methods_loop; // continue with outer loop - } - } - - // all params match - return methods[i]; - } - - // no match - return null; - } - - static private final Method[] getAllMethodsForClass( Class aClass ) - { - Method[] local = aClass.getDeclaredMethods(); // only local - Method[] all = aClass.getMethods(); // all public - Method[] result = new Method[ local.length + all.length ]; - System.arraycopy( local, 0, result, 0, local.length ); - System.arraycopy( all, 0, result, local.length, all.length ); - return result; - } - - /** - * Generates a map of properties to both getter or setter methods for the given class. - * Then assigned those maps into the appropriate getterMethods and setterMethods maps - * keyed by the specified class. Even on error, this method will at least place empty - * property maps into each of the methods maps. - */ - static private void mapPropertiesForClass( Class objectClass ) - { - try - { - Map readProperties = new HashMap(); - getterMethods.put( objectClass, readProperties ); - Map writeProperties = new HashMap(); - setterMethods.put( objectClass, writeProperties ); - - String name, property; - Method[] methods = getAllMethodsForClass( objectClass ); // throws SecurityException - for ( int i = 0; i < methods.length; i++ ) - { - name = methods[i].getName(); - methods[i].setAccessible( true ); // throws SecurityException - if ( name.startsWith( "set" ) ) - { - name = name.substring( 3 ); - if ( ! "".equals( name ) ) // excludes "set()" - { - putMethodIntoPropertyMap( name, methods[i], writeProperties ); - } - } - else - if ( methods[i].getReturnType() != void.class ) - { - String fullname = name; - if ( name.startsWith( "get" ) ) - { - name = name.substring( 3 ); - } - else - if ( name.startsWith( "is" ) ) - { - name = name.substring( 2 ); - } - else - if ( name.startsWith( "has" ) && ( !strict ) ) // what about hashCode()? - { - name = name.substring( 3 ); - } - - if ( ! "".equals( name ) && ( !strict ) ) // excludes "get()", "has()", and "is()" - { - putMethodIntoPropertyMap( name, methods[i], readProperties ); - if ( fullname != name ) - { // allows us to match properties that include the get/set prefix as well - putMethodIntoPropertyMap( fullname, methods[i], readProperties ); - } - } - } - } - } - catch ( SecurityException se ) - { - System.out.println( "Introspector.getMethodFromClass: " + se ); - // this class will show up with empty getter/setter maps - } - } - - /** - * Places a property-method pair into one of the properties maps. - * This in effect maps a property to an array of methods. - */ - private static void putMethodIntoPropertyMap( String aProperty, Method aMethod, Map aMap ) - { - // ensure first character is lower case - StringBuffer buffer = new StringBuffer( aProperty ); - buffer.setCharAt(0, Character.toLowerCase(buffer.charAt(0))); - String key = buffer.toString(); - - // build array of methods for property - Method[] result = (Method[]) aMap.get( key ); - if ( result == null ) - { - result = new Method[] { aMethod }; - } - else - { - // create new array that's larger by one and copy - int i; - Method[] enlarged = new Method[ result.length + 1 ]; - for ( i = 0; i < result.length; i ++ ) - { - enlarged[i] = result[i]; - } - // add the new method to end - enlarged[i] = aMethod; - result = enlarged; - } - aMap.put( key, result ); - } + continue methods_loop; // continue with outer loop + } + } -/** -* Utility method to get a method for a property belonging to a class. -* Use this if you don't feel like making the Class array from the parameters -* you will be using - pass in the parameters themselves. -* @param objectClass the Class whose property methods will be retrieved. -* @param aProperty The property whose method will be retrieved. -* @param params An array of parameters to be used. -* @return The appropriate method for the class, or null if not found. -*/ - static public Method getPropertyReadMethod( - Class objectClass, String aProperty, Object[] params ) - { - // optimization: avoid allocating class array for common case - if ( params.length == 0 ) - { - return getPropertyReadMethod( - objectClass, aProperty, EMPTY_CLASS_ARRAY ); - } - - Class[] paramList = new Class[ params.length ]; - for ( int i = 0; i < params.length; i++ ) - { - if ( params[i] != null ) - { - paramList[i] = params[i].getClass(); - } - else - { - paramList[i] = WILD; - } - } - return getPropertyReadMethod( objectClass, aProperty, paramList ); - } + // all params match + return methods[i]; + } -/** -* Utility method to get a method for a property belonging to a class. -* Use this if you don't feel like making the Class array from the parameters -* you will be using - pass in the parameters themselves. -* @param objectClass the Class whose property methods will be retrieved. -* @param aProperty The property whose method will be retrieved. -* @param params An array of parameters to be used. -* @return The appropriate method for the class, or null if not found. -*/ - static public Method getPropertyWriteMethod( - Class objectClass, String aProperty, Object[] params ) - { - Class[] paramList = new Class[ params.length ]; - for ( int i = 0; i < params.length; i++ ) - { - if ( params[i] != null ) - { - paramList[i] = params[i].getClass(); - } - else - { - paramList[i] = WILD; - } - } - return getPropertyWriteMethod( objectClass, aProperty, paramList ); - } - - /** - * Gets a list of the readable properties for the given class. - * Note that readable properties may not be writable - see getWriteProperties(). - * @return An array of property names in no particular order - * where each name is a string with the first character in lower case. - */ - public static String[] getReadPropertiesForClass( Class objectClass ) - { - Map properties = (Map) getterMethods.get( objectClass ); - if ( properties == null ) - { - // need to build maps for this class - mapPropertiesForClass( objectClass ); - // now the map should exist - properties = (Map) getterMethods.get( objectClass ); - } - - // put property names into string array - Set keys = properties.keySet(); - Iterator it = keys.iterator(); - int len = keys.size(); - String[] result = new String[ len ]; - for ( int i = 0; i < len; i++ ) - { - result[i] = (String) it.next(); - } - return result; - } - - /** - * Gets a list of the writable properties for the given class. - * Note that writable properties may not be writable - see getReadProperties(). - * @return An array of property names in no particular order - * where each name is a string with the first character in lower case. - */ - public static String[] getWritePropertiesForClass( Class objectClass ) - { - Map properties = (Map) setterMethods.get( objectClass ); - if ( properties == null ) - { - // need to build maps for this class - mapPropertiesForClass( objectClass ); - // now the map should exist - properties = (Map) setterMethods.get( objectClass ); - } - - // put property names into string array - Set keys = properties.keySet(); - Iterator it = keys.iterator(); - int len = keys.size(); - String[] result = new String[ len ]; - for ( int i = 0; i < len; i++ ) - { - result[i] = (String) it.next(); - } - return result; - } - - /** - * Gets a list of the readable properties for the given object, which may - * not be null. This method is more useful than getReadPropertiesForClass - * in that Maps will return their keys as properties and Lists will return - * their element indices as properties. - * Note that readable properties may not be writable - see getWriteProperties(). - * @return An array of property names in no particular order - * where each name is a string with the first character in lower case. - */ - public static String[] getReadPropertiesForObject( Object anObject ) - { - List properties = new ArrayList(); - String[] classProperties = - getReadPropertiesForClass( anObject.getClass() ); - if ( anObject instanceof List ) - { - properties.addAll( getPropertiesForList( (List) anObject ) ); - } - if ( anObject instanceof Map ) - { - properties.addAll( getPropertiesForMap( (Map) anObject ) ); - } - int i; - int len = classProperties.length + properties.size(); - String[] result = new String[ len ]; - for ( i = 0; i < classProperties.length; i++ ) - { - result[i] = classProperties[i]; - } - Iterator it = properties.iterator(); - while ( it.hasNext() ) - { - result[i++] = it.next().toString(); - } - return result; - } - - /** - * Gets a list of the writable properties for the given object, which may - * not be null. This method is more useful than getWritePropertiesForClass - * in that Maps will return their keys as properties and Lists will return - * their element indices as properties. - * Note that writable properties may not be writable - see getReadProperties(). - * @return An array of property names in no particular order - * where each name is a string with the first character in lower case. - */ - public static String[] getWritePropertiesForObject( Object anObject ) - { - List properties = new ArrayList(); - String[] classProperties = - getWritePropertiesForClass( anObject.getClass() ); - if ( anObject instanceof List ) - { - properties.addAll( getPropertiesForList( (List) anObject ) ); - } - if ( anObject instanceof Map ) - { - properties.addAll( getPropertiesForMap( (Map) anObject ) ); - } - - int i; - int len = classProperties.length + properties.size(); - String[] result = new String[ len ]; - for ( i = 0; i < classProperties.length; i++ ) - { - result[i] = classProperties[i]; - } - Iterator it = properties.iterator(); - while ( it.hasNext() ) - { - result[i++] = it.next().toString(); - } - return result; - } - - private static List getPropertiesForList( List aList ) - { - List result = new ArrayList(); - int len = aList.size(); - for ( int i = 0; i < len; i++ ) - { - result.add( new Integer( i ).toString() ); - } - return result; - } - - private static List getPropertiesForMap( Map aMap ) - { - List result = new ArrayList(); - Iterator it = ((Map)aMap).keySet().iterator(); - while ( it.hasNext() ) - { - result.add( it.next().toString() ); - } - return result; - } - - private static Object[] EMPTY_ARRAY = new Object[0]; - - /** - * Convenience to get a value for a property from an object. - * An empty property string is considered the identity property - * and simply returns the object. - * @throws MissingPropertyException if the property cannot be - * found on the object. - */ - public static Object getValueForObject( Object anObject, String aProperty ) - { - if ( ( aProperty == null ) || ( "".equals( aProperty ) ) ) - { - return anObject; - } - - if ( useOGNL && aProperty.startsWith( "ognl:" ) ) - { - try - { - return ognl.Ognl.getValue( aProperty, anObject ); - } - catch ( Throwable t ) - { - if ( debug ) - { - System.err.println( - "Introspector.getValueForObject: " - + anObject + "' ( " + anObject.getClass() + " )" - + ", ognl:" + aProperty ); - System.err.println( t ); - } - return null; - } - } - - boolean invert = false; - if ( aProperty.startsWith( "!" ) ) - { - aProperty = aProperty.substring(1); - invert = true; - } - - Object result = null; - try - { - Method m = Introspector.getPropertyReadMethod( - anObject.getClass(), aProperty, EMPTY_ARRAY ); - if ( m != null ) - { - result = m.invoke( anObject, EMPTY_ARRAY ); + // no match + return null; + } + + static private final Method[] getAllMethodsForClass(Class aClass) { + Method[] local = aClass.getDeclaredMethods(); // only local + Method[] all = aClass.getMethods(); // all public + Method[] result = new Method[local.length + all.length]; + System.arraycopy(local, 0, result, 0, local.length); + System.arraycopy(all, 0, result, local.length, all.length); + return result; + } + + /** + * Generates a map of properties to both getter or setter methods for the given + * class. Then assigned those maps into the appropriate getterMethods and + * setterMethods maps keyed by the specified class. Even on error, this method + * will at least place empty property maps into each of the methods maps. + */ + static private void mapPropertiesForClass(Class objectClass) { + try { + Map readProperties = new HashMap(); + getterMethods.put(objectClass, readProperties); + Map writeProperties = new HashMap(); + setterMethods.put(objectClass, writeProperties); + + String name, property; + Method[] methods = getAllMethodsForClass(objectClass); // throws SecurityException + for (int i = 0; i < methods.length; i++) { + name = methods[i].getName(); + methods[i].setAccessible(true); // throws SecurityException + if (name.startsWith("set")) { + name = name.substring(3); + if (!"".equals(name)) // excludes "set()" + { + putMethodIntoPropertyMap(name, methods[i], writeProperties); + } + } else if (methods[i].getReturnType() != void.class) { + String fullname = name; + if (name.startsWith("get")) { + name = name.substring(3); + } else if (name.startsWith("is")) { + name = name.substring(2); + } else if (name.startsWith("has") && (!strict)) // what about hashCode()? + { + name = name.substring(3); + } + + if (!"".equals(name) && (!strict)) // excludes "get()", "has()", and "is()" + { + putMethodIntoPropertyMap(name, methods[i], readProperties); + if (fullname != name) { // allows us to match properties that include the get/set prefix as well + putMethodIntoPropertyMap(fullname, methods[i], readProperties); + } + } + } } - else // no method, try for field - { - try - { - Field field = anObject.getClass().getDeclaredField( aProperty ); - if ( field != null ) - { - field.setAccessible( true ); // throws SecurityException - result = field.get( anObject ); - } - } - catch ( Throwable t ) - { - // ignore for now - } - } - - if ( result == null ) - { - if ( anObject instanceof Map ) - { - result = ((Map)anObject).get( aProperty ); - } - else - if ( anObject instanceof List ) - { - result = ((List)anObject).get( Integer.parseInt( aProperty ) ); - } - } - - if ( invert ) - { - Object inverted = ValueConverter.invert( result ); - if ( inverted != null ) result = inverted; - } - //System.out.println( "getValueForObject: " + anObject + " : " + aProperty + " : " + result ); - return result; - } - catch ( Throwable exc ) - { - if ( exc instanceof InvocationTargetException ) - { - exc = ((InvocationTargetException)exc).getTargetException(); - } - if ( exc instanceof RuntimeException ) - { - throw (RuntimeException)exc; - } - if ( debug ) + } catch (SecurityException se) { + System.out.println("Introspector.getMethodFromClass: " + se); + // this class will show up with empty getter/setter maps + } + } + + /** + * Places a property-method pair into one of the properties maps. This in effect + * maps a property to an array of methods. + */ + private static void putMethodIntoPropertyMap(String aProperty, Method aMethod, Map aMap) { + // ensure first character is lower case + StringBuffer buffer = new StringBuffer(aProperty); + buffer.setCharAt(0, Character.toLowerCase(buffer.charAt(0))); + String key = buffer.toString(); + + // build array of methods for property + Method[] result = (Method[]) aMap.get(key); + if (result == null) { + result = new Method[] { aMethod }; + } else { + // create new array that's larger by one and copy + int i; + Method[] enlarged = new Method[result.length + 1]; + for (i = 0; i < result.length; i++) { + enlarged[i] = result[i]; + } + // add the new method to end + enlarged[i] = aMethod; + result = enlarged; + } + aMap.put(key, result); + } + + /** + * Utility method to get a method for a property belonging to a class. Use this + * if you don't feel like making the Class array from the parameters you will be + * using - pass in the parameters themselves. + * + * @param objectClass the Class whose property methods will be retrieved. + * @param aProperty The property whose method will be retrieved. + * @param params An array of parameters to be used. + * @return The appropriate method for the class, or null if not found. + */ + static public Method getPropertyReadMethod(Class objectClass, String aProperty, Object[] params) { + // optimization: avoid allocating class array for common case + if (params.length == 0) { + return getPropertyReadMethod(objectClass, aProperty, EMPTY_CLASS_ARRAY); + } + + Class[] paramList = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + if (params[i] != null) { + paramList[i] = params[i].getClass(); + } else { + paramList[i] = WILD; + } + } + return getPropertyReadMethod(objectClass, aProperty, paramList); + } + + /** + * Utility method to get a method for a property belonging to a class. Use this + * if you don't feel like making the Class array from the parameters you will be + * using - pass in the parameters themselves. + * + * @param objectClass the Class whose property methods will be retrieved. + * @param aProperty The property whose method will be retrieved. + * @param params An array of parameters to be used. + * @return The appropriate method for the class, or null if not found. + */ + static public Method getPropertyWriteMethod(Class objectClass, String aProperty, Object[] params) { + Class[] paramList = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + if (params[i] != null) { + paramList[i] = params[i].getClass(); + } else { + paramList[i] = WILD; + } + } + return getPropertyWriteMethod(objectClass, aProperty, paramList); + } + + /** + * Gets a list of the readable properties for the given class. Note that + * readable properties may not be writable - see getWriteProperties(). + * + * @return An array of property names in no particular order where each name is + * a string with the first character in lower case. + */ + public static String[] getReadPropertiesForClass(Class objectClass) { + Map properties = (Map) getterMethods.get(objectClass); + if (properties == null) { + // need to build maps for this class + mapPropertiesForClass(objectClass); + // now the map should exist + properties = (Map) getterMethods.get(objectClass); + } + + // put property names into string array + Set keys = properties.keySet(); + Iterator it = keys.iterator(); + int len = keys.size(); + String[] result = new String[len]; + for (int i = 0; i < len; i++) { + result[i] = (String) it.next(); + } + return result; + } + + /** + * Gets a list of the writable properties for the given class. Note that + * writable properties may not be writable - see getReadProperties(). + * + * @return An array of property names in no particular order where each name is + * a string with the first character in lower case. + */ + public static String[] getWritePropertiesForClass(Class objectClass) { + Map properties = (Map) setterMethods.get(objectClass); + if (properties == null) { + // need to build maps for this class + mapPropertiesForClass(objectClass); + // now the map should exist + properties = (Map) setterMethods.get(objectClass); + } + + // put property names into string array + Set keys = properties.keySet(); + Iterator it = keys.iterator(); + int len = keys.size(); + String[] result = new String[len]; + for (int i = 0; i < len; i++) { + result[i] = (String) it.next(); + } + return result; + } + + /** + * Gets a list of the readable properties for the given object, which may not be + * null. This method is more useful than getReadPropertiesForClass in that Maps + * will return their keys as properties and Lists will return their element + * indices as properties. Note that readable properties may not be writable - + * see getWriteProperties(). + * + * @return An array of property names in no particular order where each name is + * a string with the first character in lower case. + */ + public static String[] getReadPropertiesForObject(Object anObject) { + List properties = new ArrayList(); + String[] classProperties = getReadPropertiesForClass(anObject.getClass()); + if (anObject instanceof List) { + properties.addAll(getPropertiesForList((List) anObject)); + } + if (anObject instanceof Map) { + properties.addAll(getPropertiesForMap((Map) anObject)); + } + int i; + int len = classProperties.length + properties.size(); + String[] result = new String[len]; + for (i = 0; i < classProperties.length; i++) { + result[i] = classProperties[i]; + } + Iterator it = properties.iterator(); + while (it.hasNext()) { + result[i++] = it.next().toString(); + } + return result; + } + + /** + * Gets a list of the writable properties for the given object, which may not be + * null. This method is more useful than getWritePropertiesForClass in that Maps + * will return their keys as properties and Lists will return their element + * indices as properties. Note that writable properties may not be writable - + * see getReadProperties(). + * + * @return An array of property names in no particular order where each name is + * a string with the first character in lower case. + */ + public static String[] getWritePropertiesForObject(Object anObject) { + List properties = new ArrayList(); + String[] classProperties = getWritePropertiesForClass(anObject.getClass()); + if (anObject instanceof List) { + properties.addAll(getPropertiesForList((List) anObject)); + } + if (anObject instanceof Map) { + properties.addAll(getPropertiesForMap((Map) anObject)); + } + + int i; + int len = classProperties.length + properties.size(); + String[] result = new String[len]; + for (i = 0; i < classProperties.length; i++) { + result[i] = classProperties[i]; + } + Iterator it = properties.iterator(); + while (it.hasNext()) { + result[i++] = it.next().toString(); + } + return result; + } + + private static List getPropertiesForList(List aList) { + List result = new ArrayList(); + int len = aList.size(); + for (int i = 0; i < len; i++) { + result.add(new Integer(i).toString()); + } + return result; + } + + private static List getPropertiesForMap(Map aMap) { + List result = new ArrayList(); + Iterator it = ((Map) aMap).keySet().iterator(); + while (it.hasNext()) { + result.add(it.next().toString()); + } + return result; + } + + private static Object[] EMPTY_ARRAY = new Object[0]; + + /** + * Convenience to get a value for a property from an object. An empty property + * string is considered the identity property and simply returns the object. + * + * @throws MissingPropertyException if the property cannot be found on the + * object. + */ + public static Object getValueForObject(Object anObject, String aProperty) { + if ((aProperty == null) || ("".equals(aProperty))) { + return anObject; + } + + if (useOGNL && aProperty.startsWith("ognl:")) { + try { + return ognl.Ognl.getValue(aProperty, anObject); + } catch (Throwable t) { + if (debug) { + System.err.println("Introspector.getValueForObject: " + anObject + "' ( " + anObject.getClass() + + " )" + ", ognl:" + aProperty); + System.err.println(t); + } + return null; + } + } + + boolean invert = false; + if (aProperty.startsWith("!")) { + aProperty = aProperty.substring(1); + invert = true; + } + + Object result = null; + try { + Method m = Introspector.getPropertyReadMethod(anObject.getClass(), aProperty, EMPTY_ARRAY); + if (m != null) { + result = m.invoke(anObject, EMPTY_ARRAY); + } else // no method, try for field { - System.out.println( - "Introspector.getValueForObject: " - + anObject + "' ( " + anObject.getClass() + " )" - + ", " + aProperty + ": " ); - } - throw new WotonomyException( exc ); - } + try { + Field field = anObject.getClass().getDeclaredField(aProperty); + if (field != null) { + field.setAccessible(true); // throws SecurityException + result = field.get(anObject); + } + } catch (Throwable t) { + // ignore for now + } + } + + if (result == null) { + if (anObject instanceof Map) { + result = ((Map) anObject).get(aProperty); + } else if (anObject instanceof List) { + result = ((List) anObject).get(Integer.parseInt(aProperty)); + } + } + + if (invert) { + Object inverted = ValueConverter.invert(result); + if (inverted != null) + result = inverted; + } + // System.out.println( "getValueForObject: " + anObject + " : " + aProperty + " + // : " + result ); + return result; + } catch (Throwable exc) { + if (exc instanceof InvocationTargetException) { + exc = ((InvocationTargetException) exc).getTargetException(); + } + if (exc instanceof RuntimeException) { + throw (RuntimeException) exc; + } + if (debug) { + System.out.println("Introspector.getValueForObject: " + anObject + "' ( " + anObject.getClass() + " )" + + ", " + aProperty + ": "); + } + throw new WotonomyException(exc); + } //! throw new MissingPropertyException(); - } - - /** - * Convenience to set a value for a property from an object. - * Returns the return value from executing the specified method, - * or null if the method returns type void. - * @throws MissingPropertyException if the property cannot be - * found on the object. - * @throws NullPrimitiveException if the property is of primitive - * type and the value is null. - */ - public static Object setValueForObject( - Object anObject, String aProperty, Object aValue ) - { - if ( useOGNL && aProperty.startsWith( "ognl:" ) ) - { - try - { - ognl.Ognl.setValue( aProperty, anObject, aValue ); - } - catch ( Throwable t ) - { - if ( debug ) - { - System.err.println( - "Introspector.setValueForObject: " - + anObject + "' ( " + anObject.getClass() + " )" - + ", ognl:" + aProperty + " : " + aValue ); - System.err.println( t ); - } - } - return null; - } - - try - { - if ( aProperty.startsWith( "!" ) ) - { - aProperty = aProperty.substring(1); - Object inverted = ValueConverter.invert( aValue ); - if ( inverted != null ) aValue = inverted; - } - - Method m = null; - if ( aValue != null ) - { - m = Introspector.getPropertyWriteMethod( - anObject.getClass(), aProperty, new Class[] { aValue.getClass() } ); - } - if ( m == null ) - { - m = Introspector.getPropertyWriteMethod( - anObject.getClass(), aProperty, new Class[] { WILD } ); - if ( ( m != null ) && ( aValue != null ) ) - { - // check for null primitive - if ( ( aValue == null ) && - ( m.getParameterTypes()[0].isPrimitive() ) ) - { - throw new NullPrimitiveException(); - } - - // convert if possible - Object o = ValueConverter.convertObjectToClass( - aValue, m.getParameterTypes()[0] ); - if ( o != null ) - { - aValue = o; - } - } - } - if ( m != null ) - { - return m.invoke( anObject, new Object[] { aValue } ); + } + + /** + * Convenience to set a value for a property from an object. Returns the return + * value from executing the specified method, or null if the method returns type + * void. + * + * @throws MissingPropertyException if the property cannot be found on the + * object. + * @throws NullPrimitiveException if the property is of primitive type and the + * value is null. + */ + public static Object setValueForObject(Object anObject, String aProperty, Object aValue) { + if (useOGNL && aProperty.startsWith("ognl:")) { + try { + ognl.Ognl.setValue(aProperty, anObject, aValue); + } catch (Throwable t) { + if (debug) { + System.err.println("Introspector.setValueForObject: " + anObject + "' ( " + anObject.getClass() + + " )" + ", ognl:" + aProperty + " : " + aValue); + System.err.println(t); + } } - else // no method, try for field - { - try - { - Field field = anObject.getClass().getDeclaredField( aProperty ); - if ( field != null ) - { - field.setAccessible( true ); // throws SecurityException - field.set( anObject, aValue ); - return null; - } - } - catch ( Throwable t ) - { - // ignore for now - } - } - - if ( anObject instanceof Map ) - { - return ((Map)anObject).put( aProperty, aValue ); + return null; + } + + try { + if (aProperty.startsWith("!")) { + aProperty = aProperty.substring(1); + Object inverted = ValueConverter.invert(aValue); + if (inverted != null) + aValue = inverted; + } + + Method m = null; + if (aValue != null) { + m = Introspector.getPropertyWriteMethod(anObject.getClass(), aProperty, + new Class[] { aValue.getClass() }); } - if ( anObject instanceof List ) + if (m == null) { + m = Introspector.getPropertyWriteMethod(anObject.getClass(), aProperty, new Class[] { WILD }); + if ((m != null) && (aValue != null)) { + // check for null primitive + if ((aValue == null) && (m.getParameterTypes()[0].isPrimitive())) { + throw new NullPrimitiveException(); + } + + // convert if possible + Object o = ValueConverter.convertObjectToClass(aValue, m.getParameterTypes()[0]); + if (o != null) { + aValue = o; + } + } + } + if (m != null) { + return m.invoke(anObject, new Object[] { aValue }); + } else // no method, try for field { - List list = (List) anObject; - int i = Integer.parseInt( aProperty ); - if ( list.size() < i+1 ) - { - // expand list as necessary - for ( int j = list.size(); j <= i; j++ ) - { - list.add( new Object() ); // placeholder - } - } - return list.set( i, aValue ); + try { + Field field = anObject.getClass().getDeclaredField(aProperty); + if (field != null) { + field.setAccessible(true); // throws SecurityException + field.set(anObject, aValue); + return null; + } + } catch (Throwable t) { + // ignore for now + } + } + + if (anObject instanceof Map) { + return ((Map) anObject).put(aProperty, aValue); + } + if (anObject instanceof List) { + List list = (List) anObject; + int i = Integer.parseInt(aProperty); + if (list.size() < i + 1) { + // expand list as necessary + for (int j = list.size(); j <= i; j++) { + list.add(new Object()); // placeholder + } + } + return list.set(i, aValue); } - } - catch ( Throwable exc ) - { - if ( exc instanceof IllegalArgumentException ) - { + } catch (Throwable exc) { + if (exc instanceof IllegalArgumentException) { System.out.println( - "Introspector.setValueForObject: " - + anObject + " , " + aProperty + " , '" - + aValue + "' ):" ); - System.out.println( exc ); - } - else - if ( exc instanceof InvocationTargetException ) - { - exc = ((InvocationTargetException)exc).getTargetException(); - } - if ( exc instanceof RuntimeException ) - { - throw (RuntimeException)exc; - } - if ( debug ) - { + "Introspector.setValueForObject: " + anObject + " , " + aProperty + " , '" + aValue + "' ):"); + System.out.println(exc); + } else if (exc instanceof InvocationTargetException) { + exc = ((InvocationTargetException) exc).getTargetException(); + } + if (exc instanceof RuntimeException) { + throw (RuntimeException) exc; + } + if (debug) { System.out.println( - "Introspector.setValueForObject: " - + anObject + " , " + aProperty + " , '" - + aValue + "' ):" ); + "Introspector.setValueForObject: " + anObject + " , " + aProperty + " , '" + aValue + "' ):"); } - throw new WotonomyException( exc ); - } - return null; + throw new WotonomyException(exc); + } + return null; //! throw new MissingPropertyException(); - } - - /** - * Gets a value from an object or any of its child objects. - * This will parse the property string for "."'s and get - * values for each successive object's property in the path. - * An empty property string is considered the identity property - * and simply returns the object. - */ - public static Object get( Object anObject, String aProperty ) - { - int i = aProperty.indexOf( SEPARATOR ); - if ( i == -1 ) return getValueForObject( anObject, aProperty ); - - String pathElement = aProperty.substring( 0, i ); - String remainder = aProperty.substring( i+1 ); - - Object result = getValueForObject( anObject, pathElement ); - if ( result == null ) return null; - return get( result, remainder ); - } - - /** - * Sets a value in an object or any of its child objects. - * This will parse the property string for "."'s and set - * values for each successive object's property in the path.<br><br> - * - * If a property is not found, this method will try to - * implicitly create hash maps (if possible) to fill out the path. - * This is useful when dealing with trees of nested maps. - */ - public static Object set( Object anObject, String aProperty, Object aValue ) - { - int i = aProperty.indexOf( SEPARATOR ); - if ( i == -1 ) return setValueForObject( anObject, aProperty, aValue ); - - String pathElement = aProperty.substring( 0, i ); - String remainder = aProperty.substring( i+1 ); - - Object result = getValueForObject( anObject, pathElement ); - if ( result == null ) - { + } + + /** + * Gets a value from an object or any of its child objects. This will parse the + * property string for "."'s and get values for each successive object's + * property in the path. An empty property string is considered the identity + * property and simply returns the object. + */ + public static Object get(Object anObject, String aProperty) { + int i = aProperty.indexOf(SEPARATOR); + if (i == -1) + return getValueForObject(anObject, aProperty); + + String pathElement = aProperty.substring(0, i); + String remainder = aProperty.substring(i + 1); + + Object result = getValueForObject(anObject, pathElement); + if (result == null) + return null; + return get(result, remainder); + } + + /** + * Sets a value in an object or any of its child objects. This will parse the + * property string for "."'s and set values for each successive object's + * property in the path.<br> + * <br> + * + * If a property is not found, this method will try to implicitly create hash + * maps (if possible) to fill out the path. This is useful when dealing with + * trees of nested maps. + */ + public static Object set(Object anObject, String aProperty, Object aValue) { + int i = aProperty.indexOf(SEPARATOR); + if (i == -1) + return setValueForObject(anObject, aProperty, aValue); + + String pathElement = aProperty.substring(0, i); + String remainder = aProperty.substring(i + 1); + + Object result = getValueForObject(anObject, pathElement); + if (result == null) { result = new HashMap(2); - setValueForObject( anObject, pathElement, result ); + setValueForObject(anObject, pathElement, result); } - return set( result, remainder, aValue ); - } - + return set(result, remainder, aValue); + } + /** - * If set to true, exceptions printed to System.out.println. - * Defaults to true. - */ - public void setDebug( boolean isDebug ) - { + * If set to true, exceptions printed to System.out.println. Defaults to true. + */ + public void setDebug(boolean isDebug) { debug = isDebug; - } + } } /* - * $Log$ - * Revision 1.2 2006/02/16 13:11:47 cgruber - * Check in all sources in eclipse-friendly maven-enabled packages. + * $Log$ Revision 1.2 2006/02/16 13:11:47 cgruber Check in all sources in + * eclipse-friendly maven-enabled packages. * - * Revision 1.19 2004/02/05 02:20:34 mpowers - * Added experimental ognl support (if ognl is present). + * Revision 1.19 2004/02/05 02:20:34 mpowers Added experimental ognl support (if + * ognl is present). * - * Revision 1.18 2003/03/26 16:44:35 mpowers - * Now correctly reflecting on all methods, not just locally declared ones. + * Revision 1.18 2003/03/26 16:44:35 mpowers Now correctly reflecting on all + * methods, not just locally declared ones. * - * Revision 1.17 2003/02/21 21:10:51 mpowers - * Now reaching package, protected, and private methods and fields. + * Revision 1.17 2003/02/21 21:10:51 mpowers Now reaching package, protected, + * and private methods and fields. * - * Revision 1.16 2003/01/28 22:11:59 mpowers - * Now more lenient in resolving properties starting with "is" "get" or "has". + * Revision 1.16 2003/01/28 22:11:59 mpowers Now more lenient in resolving + * properties starting with "is" "get" or "has". * - * Revision 1.15 2003/01/27 15:10:54 mpowers - * Better handling for illegal argument exceptions. + * Revision 1.15 2003/01/27 15:10:54 mpowers Better handling for illegal + * argument exceptions. * - * Revision 1.14 2003/01/18 23:30:42 mpowers - * WODisplayGroup now compiles. + * Revision 1.14 2003/01/18 23:30:42 mpowers WODisplayGroup now compiles. * - * Revision 1.13 2002/10/11 15:35:12 mpowers - * Removed printlns. + * Revision 1.13 2002/10/11 15:35:12 mpowers Removed printlns. * - * Revision 1.11 2001/05/02 17:58:41 mpowers - * Removed debugging code, added comments. + * Revision 1.11 2001/05/02 17:58:41 mpowers Removed debugging code, added + * comments. * - * Revision 1.10 2001/04/08 21:00:54 mpowers - * Changes to support new objectsForFetchSpecification scheme. + * Revision 1.10 2001/04/08 21:00:54 mpowers Changes to support new + * objectsForFetchSpecification scheme. * - * Revision 1.9 2001/03/29 03:30:36 mpowers - * Refactored duplicator a bit. + * Revision 1.9 2001/03/29 03:30:36 mpowers Refactored duplicator a bit. * Disabled MissingPropertyExceptions for now. * - * Revision 1.8 2001/03/28 17:52:45 mpowers - * Corrected the throws in the docs. + * Revision 1.8 2001/03/28 17:52:45 mpowers Corrected the throws in the docs. * - * Revision 1.7 2001/03/28 17:49:13 mpowers - * Better exception handling in Introspector. + * Revision 1.7 2001/03/28 17:49:13 mpowers Better exception handling in + * Introspector. * - * Revision 1.6 2001/03/13 21:40:20 mpowers - * Improved handling of runtime exceptions. + * Revision 1.6 2001/03/13 21:40:20 mpowers Improved handling of runtime + * exceptions. * - * Revision 1.5 2001/03/09 22:06:35 mpowers - * Now extracting the wrapped exception from InvocationTargetExceptions. + * Revision 1.5 2001/03/09 22:06:35 mpowers Now extracting the wrapped exception + * from InvocationTargetExceptions. * - * Revision 1.4 2001/03/01 20:36:35 mpowers - * Better error handling and better handling of nulls. + * Revision 1.4 2001/03/01 20:36:35 mpowers Better error handling and better + * handling of nulls. * - * Revision 1.3 2001/01/17 16:20:57 mpowers - * Introspector now handles the identity property. + * Revision 1.3 2001/01/17 16:20:57 mpowers Introspector now handles the + * identity property. * - * Revision 1.2 2001/01/09 20:08:17 mpowers - * Slight optimization. + * Revision 1.2 2001/01/09 20:08:17 mpowers Slight optimization. * - * Revision 1.1.1.1 2000/12/21 15:52:04 mpowers - * Contributing wotonomy. + * Revision 1.1.1.1 2000/12/21 15:52:04 mpowers Contributing wotonomy. * - * Revision 1.5 2000/12/20 16:25:46 michael - * Added log to all files. + * Revision 1.5 2000/12/20 16:25:46 michael Added log to all files. * * */ - diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java index b1ad824..45080a2 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/IntrospectorException.java @@ -19,14 +19,13 @@ License along with this library; if not, see http://www.gnu.org package net.wotonomy.foundation.internal; /** -* A WotonomyException that is thrown by Introspector. -* This class serves as a base class for other exceptions. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ + * A WotonomyException that is thrown by Introspector. This class serves as a + * base class for other exceptions. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ -public class IntrospectorException extends WotonomyException -{ +public class IntrospectorException extends WotonomyException { } diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java index c1e30d3..c89742f 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/MissingPropertyException.java @@ -19,14 +19,13 @@ License along with this library; if not, see http://www.gnu.org package net.wotonomy.foundation.internal; /** -* A IntrospectorException that is thrown by Introspector when -* a property does not exist for an object. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ + * A IntrospectorException that is thrown by Introspector when a property does + * not exist for an object. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ -public class MissingPropertyException extends IntrospectorException -{ +public class MissingPropertyException extends IntrospectorException { } diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java index 43c14a5..7761876 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NetworkClassLoader.java @@ -47,7 +47,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== - */ + */ package net.wotonomy.foundation.internal; @@ -59,310 +59,284 @@ import java.util.Enumeration; import java.util.Hashtable; /** - * The correct name for this class should be URLClassLoader. - * But there is already a class by that name in JDK1.2. + * The correct name for this class should be URLClassLoader. But there is + * already a class by that name in JDK1.2. * - * I have had quite a few problems with URLClassLoader in - * past, so I ended up writing this ClassLoader. I found that - * the Java 2's URLClassLoader, does not close the Jar file once - * opened. It is a pretty good optimization step, but if you - * modify the class in the jar file, it does not pick it up. Some - * operating systems may not let you modify the jar file while it is - * still open. IMHO, it does make sense to close the jar file - * after you are done reading the class data. But this approach may not - * get you the performance of the URLClassLoader, but it works in all - * cases and also runs on JDK1.1. I have enhanced this class loader - * to read all the zip/jar entries once & cache the data, so that - * there is no overhead of opening/closing jar file to pick up + * I have had quite a few problems with URLClassLoader in past, so I ended up + * writing this ClassLoader. I found that the Java 2's URLClassLoader, does not + * close the Jar file once opened. It is a pretty good optimization step, but if + * you modify the class in the jar file, it does not pick it up. Some operating + * systems may not let you modify the jar file while it is still open. IMHO, it + * does make sense to close the jar file after you are done reading the class + * data. But this approach may not get you the performance of the + * URLClassLoader, but it works in all cases and also runs on JDK1.1. I have + * enhanced this class loader to read all the zip/jar entries once & cache the + * data, so that there is no overhead of opening/closing jar file to pick up * each entry. * * * @author Harish Prabandham */ public class NetworkClassLoader extends ClassLoader { - private ClassLoader parent = null; // parent classloader - private Hashtable classCache = new Hashtable(); - private Hashtable urlset = new Hashtable(); - - /** - * Creates a new instance of the class loader. - * @param delegate/parent class loader. - */ - public NetworkClassLoader(ClassLoader parent) { - setParent(parent); - } - - /** - * Sets the parent/delegate class loader. - * @param delegate/parent class loader. - */ - protected final void setParent(ClassLoader parent) { - this.parent = parent; - } - - /** - * Adds the given URL to this class loader. If the URL - * ends with "/", then it is assumed to be a directory - * otherwise, it is assumed to be a zip/jar file. If the - * same URL is added again, the URL is re-opened and this - * zip/jar file is used for serving any future class requests. - * @param URL where to look for the classes. - */ - public synchronized void addURL(URL url) { - // System.out.println("Adding url: " + url); - if(!urlset.containsKey(url)) { - try { - urlset.put(url, new URLResourceReader(url)); - }catch(IOException ioe){ - // Probably a bad url... - } - } else { - // remove the old one & add a new one... - try{ - URLResourceReader newu = new URLResourceReader(url); - URLResourceReader oldu = (URLResourceReader) urlset.get(url); - oldu.close(); - urlset.remove(url); - urlset.put(url, newu); - } catch (IOException ioe) { - } - } - } - - /** - * @return An enumeration of URLs where this class loader - * looks for classes. - */ - public Enumeration getURLs() { - return urlset.keys(); - } - - /** - * Call this to bypass the implementation of loadClass. - */ - public Class findClass(String name) { - byte[] b = loadClassData(name); - if ( b == null ) return null; - return defineClass(name, b, 0, b.length); - } - - protected byte[] loadResource(URL url, String resourceName) - throws IOException { - URLResourceReader urr = (URLResourceReader) urlset.get(url); - // System.out.println("Loading from " + urr + " " + resourceName); - if(urr != null) { - return urr.getResource(resourceName); - } - - return null; - } - - protected byte[] loadResource(String resource) { - byte[] barray = null; - for(Enumeration e = urlset.keys(); e.hasMoreElements();) { - URL url = (URL) e.nextElement(); - - try { - barray = loadResource(url, resource); - } catch(Exception ex) { - } finally { - if(barray != null) - break; - } - } - - return barray; - } - - protected byte[] loadClassData(String classname) { - String resourceName = classname.replace('.', '/') + ".class"; - return loadResource(resourceName); - } - - /** - * Overridden to search for a resource and return - * a "jar"-style URL or normal "file" URL as necessary. - */ - protected URL findResource(String name) - { //System.out.println( "findResource: " + name ); - URL url; - byte[] barray = null; - - for ( Enumeration e = urlset.keys(); e.hasMoreElements(); ) - { - url = (URL) e.nextElement(); - try - { - barray = loadResource(url, name); // loads fully: wasteful - } - catch(Exception ex) - { - // do nothing - } - if( barray != null ) - { - try - { - String ref = url.toString(); - if ( ref.endsWith( ".jar" ) ) - { - //System.out.println( "jar:" + ref + "!/" + name ); - return new URL( "jar:" + ref + "!/" + name ); - } - else - { - //System.out.println( new URL( url, name ).toString() ); - return new URL( url, name ); - } - } - catch ( Throwable t ) - { - t.printStackTrace(); - } - } - } - - return null; - } - - /** - * @return The resource as the input stream if such a resource - * exists, otherwise returns null. - */ - public InputStream getResourceAsStream(String name) { - //System.out.println( "getResourceAsStream: " + name ); - InputStream istream = null; - - // Algorithm: - // - // 1. first check the system path for the resource - // 2. next check the delegate/parent class loader for the resource - // 3. then attempt to get the resource from the url set. - // - - // Lets check the system path for the resource. - istream = getSystemResourceAsStream(name); - if(istream != null) - return istream; - - // Lets check the parent/delegate class loader for the resource. - if(parent != null) { - istream = parent.getResourceAsStream(name); - if(istream != null) - return istream; - } - - // Lets load it ourselves. - byte[] data = loadResource(name); - if(data != null) { - istream = new ByteArrayInputStream(data); - } - - return istream; - } - - /** - * java.lang.ClassLoader's defineClass method is final, so the - * its subclasses cannot override this method. But, this class - * calls this method in the loadClass() instead. - * @param The name of the class without ".class" extension. - * @param The class data bytes. - * @return The class object. - */ - protected Class defineClass(String classname, byte[] classdata) { - return defineClass(classname, classdata, 0, classdata.length); - } - - public synchronized Class loadClass(String name, boolean resolve) - throws ClassNotFoundException { - Class c = null; - - // Algorithm: (Please do not change the order; unless you - // have a good reason to do so). - // - // 1. first check the system class loader. - // 2. next check the delegate/parent class loader. - // 3. next check the class cache - // 4. then attempt to load classes from the URL set. - // - - // Lets see if the class is in system class loader. - try { - c = findSystemClass(name); - }catch(ClassNotFoundException cnfe) { - }finally { - if(c != null) - return c; - } - - // Lets see if the class is in parent class loader. - try { - if(parent != null) - c = parent.loadClass(name); - }catch(ClassNotFoundException cnfe) { - }finally { - if(c != null) - return c; - } - - // Lets see if the class is in the cache.. - c = (Class) classCache.get(name); - - if(c != null) - return c; - - - // Lets see if we find the class all by ourselves. - byte[] data = loadClassData(name); - - if(data != null) { - // we did !! - c = defineClass(name, data); - classCache.put(name, c); - if(resolve) - resolveClass(c); - } else { - // We are out of luck at this point... - throw new ClassNotFoundException(name); - } - - return c; - } - - /** - * This method resets this ClassLoader's state. It completely - * removes all the URLs and classes in this class loader cache. - */ - public final void clear() { - urlset.clear(); - classCache.clear(); - } - - /** - * This method resets this ClassLoader's state and resets the - * references for garbage collection. - */ - protected void finalize() throws Throwable { - // Cleanup real well. Otherwise, this can be - // a major source of memory leaks... - - // remove all the urls & class entries. - clear(); - - parent = null; - urlset = null; - classCache = null; - } + private ClassLoader parent = null; // parent classloader + private Hashtable classCache = new Hashtable(); + private Hashtable urlset = new Hashtable(); + + /** + * Creates a new instance of the class loader. + * + * @param delegate/parent class loader. + */ + public NetworkClassLoader(ClassLoader parent) { + setParent(parent); + } + + /** + * Sets the parent/delegate class loader. + * + * @param delegate/parent class loader. + */ + protected final void setParent(ClassLoader parent) { + this.parent = parent; + } + + /** + * Adds the given URL to this class loader. If the URL ends with "/", then it is + * assumed to be a directory otherwise, it is assumed to be a zip/jar file. If + * the same URL is added again, the URL is re-opened and this zip/jar file is + * used for serving any future class requests. + * + * @param URL where to look for the classes. + */ + public synchronized void addURL(URL url) { + // System.out.println("Adding url: " + url); + if (!urlset.containsKey(url)) { + try { + urlset.put(url, new URLResourceReader(url)); + } catch (IOException ioe) { + // Probably a bad url... + } + } else { + // remove the old one & add a new one... + try { + URLResourceReader newu = new URLResourceReader(url); + URLResourceReader oldu = (URLResourceReader) urlset.get(url); + oldu.close(); + urlset.remove(url); + urlset.put(url, newu); + } catch (IOException ioe) { + } + } + } + + /** + * @return An enumeration of URLs where this class loader looks for classes. + */ + public Enumeration getURLs() { + return urlset.keys(); + } + + /** + * Call this to bypass the implementation of loadClass. + */ + public Class findClass(String name) { + byte[] b = loadClassData(name); + if (b == null) + return null; + return defineClass(name, b, 0, b.length); + } + + protected byte[] loadResource(URL url, String resourceName) throws IOException { + URLResourceReader urr = (URLResourceReader) urlset.get(url); + // System.out.println("Loading from " + urr + " " + resourceName); + if (urr != null) { + return urr.getResource(resourceName); + } + + return null; + } + + protected byte[] loadResource(String resource) { + byte[] barray = null; + for (Enumeration e = urlset.keys(); e.hasMoreElements();) { + URL url = (URL) e.nextElement(); + + try { + barray = loadResource(url, resource); + } catch (Exception ex) { + } finally { + if (barray != null) + break; + } + } + + return barray; + } + + protected byte[] loadClassData(String classname) { + String resourceName = classname.replace('.', '/') + ".class"; + return loadResource(resourceName); + } + + /** + * Overridden to search for a resource and return a "jar"-style URL or normal + * "file" URL as necessary. + */ + protected URL findResource(String name) { // System.out.println( "findResource: " + name ); + URL url; + byte[] barray = null; + + for (Enumeration e = urlset.keys(); e.hasMoreElements();) { + url = (URL) e.nextElement(); + try { + barray = loadResource(url, name); // loads fully: wasteful + } catch (Exception ex) { + // do nothing + } + if (barray != null) { + try { + String ref = url.toString(); + if (ref.endsWith(".jar")) { + // System.out.println( "jar:" + ref + "!/" + name ); + return new URL("jar:" + ref + "!/" + name); + } else { + // System.out.println( new URL( url, name ).toString() ); + return new URL(url, name); + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + + return null; + } + + /** + * @return The resource as the input stream if such a resource exists, otherwise + * returns null. + */ + public InputStream getResourceAsStream(String name) { + // System.out.println( "getResourceAsStream: " + name ); + InputStream istream = null; + + // Algorithm: + // + // 1. first check the system path for the resource + // 2. next check the delegate/parent class loader for the resource + // 3. then attempt to get the resource from the url set. + // + + // Lets check the system path for the resource. + istream = getSystemResourceAsStream(name); + if (istream != null) + return istream; + + // Lets check the parent/delegate class loader for the resource. + if (parent != null) { + istream = parent.getResourceAsStream(name); + if (istream != null) + return istream; + } + + // Lets load it ourselves. + byte[] data = loadResource(name); + if (data != null) { + istream = new ByteArrayInputStream(data); + } + + return istream; + } + + /** + * java.lang.ClassLoader's defineClass method is final, so the its subclasses + * cannot override this method. But, this class calls this method in the + * loadClass() instead. + * + * @param The name of the class without ".class" extension. + * @param The class data bytes. + * @return The class object. + */ + protected Class defineClass(String classname, byte[] classdata) { + return defineClass(classname, classdata, 0, classdata.length); + } + + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class c = null; + + // Algorithm: (Please do not change the order; unless you + // have a good reason to do so). + // + // 1. first check the system class loader. + // 2. next check the delegate/parent class loader. + // 3. next check the class cache + // 4. then attempt to load classes from the URL set. + // + + // Lets see if the class is in system class loader. + try { + c = findSystemClass(name); + } catch (ClassNotFoundException cnfe) { + } finally { + if (c != null) + return c; + } + + // Lets see if the class is in parent class loader. + try { + if (parent != null) + c = parent.loadClass(name); + } catch (ClassNotFoundException cnfe) { + } finally { + if (c != null) + return c; + } + + // Lets see if the class is in the cache.. + c = (Class) classCache.get(name); + + if (c != null) + return c; + + // Lets see if we find the class all by ourselves. + byte[] data = loadClassData(name); + + if (data != null) { + // we did !! + c = defineClass(name, data); + classCache.put(name, c); + if (resolve) + resolveClass(c); + } else { + // We are out of luck at this point... + throw new ClassNotFoundException(name); + } + + return c; + } + + /** + * This method resets this ClassLoader's state. It completely removes all the + * URLs and classes in this class loader cache. + */ + public final void clear() { + urlset.clear(); + classCache.clear(); + } + + /** + * This method resets this ClassLoader's state and resets the references for + * garbage collection. + */ + protected void finalize() throws Throwable { + // Cleanup real well. Otherwise, this can be + // a major source of memory leaks... + + // remove all the urls & class entries. + clear(); + + parent = null; + urlset = null; + classCache = null; + } } - - - - - - - - - - - - diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java index e367211..bf1dffd 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/NullPrimitiveException.java @@ -19,14 +19,13 @@ License along with this library; if not, see http://www.gnu.org package net.wotonomy.foundation.internal; /** -* A IntrospectorException that is thrown by Introspector when -* trying to set a primitive type to null. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ + * A IntrospectorException that is thrown by Introspector when trying to set a + * primitive type to null. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ -public class NullPrimitiveException extends IntrospectorException -{ +public class NullPrimitiveException extends IntrospectorException { } diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java index abdc82f..c804893 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyComparator.java @@ -21,80 +21,61 @@ package net.wotonomy.foundation.internal; import java.io.Serializable; import java.util.Comparator; - /** -* A Comparator that will sort elements based on the -* property specified in the constructor. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ -public class PropertyComparator implements Comparator, Serializable -{ - private String property; + * A Comparator that will sort elements based on the property specified in the + * constructor. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ +public class PropertyComparator implements Comparator, Serializable { + private String property; -/** -* Standard constructor to configure the comparator. -* @param aProperty A property whose value is used to sort elements. -*/ - public PropertyComparator( String aProperty ) - { + /** + * Standard constructor to configure the comparator. + * + * @param aProperty A property whose value is used to sort elements. + */ + public PropertyComparator(String aProperty) { property = aProperty; - } + } - // interface Comparator + // interface Comparator - public int compare(Object o1, Object o2) - { - Object v1 = Introspector.get( o1, property ); - Object v2 = Introspector.get( o2, property ); - if ( v1 instanceof Comparable ) - { - return ((Comparable)v1).compareTo( v2 ); - } - else - if ( v2 instanceof Comparable ) - { - return ((Comparable)v2).compareTo( v1 ); - } - else - { - if ( v1 == null ) - { - if ( v2 == null ) - { + public int compare(Object o1, Object o2) { + Object v1 = Introspector.get(o1, property); + Object v2 = Introspector.get(o2, property); + if (v1 instanceof Comparable) { + return ((Comparable) v1).compareTo(v2); + } else if (v2 instanceof Comparable) { + return ((Comparable) v2).compareTo(v1); + } else { + if (v1 == null) { + if (v2 == null) { return 0; // both nulls are equal } return -1; // null is less than any object - } - else - if ( v2 == null ) - { + } else if (v2 == null) { return 1; // any object is greater than null } } // last resort: compare string conversions - return v1.toString().compareTo( v2.toString() ); + return v1.toString().compareTo(v2.toString()); } - - public boolean equals( Object obj ) - { - return ( obj instanceof PropertyComparator ); + + public boolean equals(Object obj) { + return (obj instanceof PropertyComparator); } } /* - * $Log$ - * Revision 1.2 2006/02/16 13:11:47 cgruber - * Check in all sources in eclipse-friendly maven-enabled packages. + * $Log$ Revision 1.2 2006/02/16 13:11:47 cgruber Check in all sources in + * eclipse-friendly maven-enabled packages. * - * Revision 1.1.1.1 2000/12/21 15:52:07 mpowers - * Contributing wotonomy. + * Revision 1.1.1.1 2000/12/21 15:52:07 mpowers Contributing wotonomy. * - * Revision 1.2 2000/12/20 16:25:46 michael - * Added log to all files. + * Revision 1.2 2000/12/20 16:25:46 michael Added log to all files. * * */ - diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java index 03231c7..3698325 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/PropertyListParser.java @@ -23,23 +23,26 @@ import java.io.*; /** * PropertyListParser can parse a property list (plist) file or string, and - * return the top-level object represented by the plist. <p> + * return the top-level object represented by the plist. + * <p> * - * A property list is a heirarchical data structure containing only Maps, - * Lists, and Strings -- nothing else. In other words, a property list is - * either a Map, List, or String instance, with the restrictions that the - * collections may only contain Map, List, or String instances. <p> + * A property list is a heirarchical data structure containing only Maps, Lists, + * and Strings -- nothing else. In other words, a property list is either a Map, + * List, or String instance, with the restrictions that the collections may only + * contain Map, List, or String instances. + * <p> * - * This class can read a particularly-formatted string or file, and create - * the property list structure described. It provides a convenient means - * for having a structured data file, letting programs simply deal with the - * structure rather than having to do a lot of string parsing work as well. - * The concept is similar to Properties files, except that the values can - * be nested Maps or Lists instead of only Strings. <p> + * This class can read a particularly-formatted string or file, and create the + * property list structure described. It provides a convenient means for having + * a structured data file, letting programs simply deal with the structure + * rather than having to do a lot of string parsing work as well. The concept is + * similar to Properties files, except that the values can be nested Maps or + * Lists instead of only Strings. + * <p> * * A Map is specified in a file by key/value pairs surrounded by brace - * characters. An equal sign (=) must be between the key and value, and - * there must be a semicolon (;) following the value. + * characters. An equal sign (=) must be between the key and value, and there + * must be a semicolon (;) following the value. * * <pre> * { @@ -49,35 +52,42 @@ import java.io.*; * } * </pre> * - * A List is specified by a comma-separated list of values surrounded by parentheses, like: + * A List is specified by a comma-separated list of values surrounded by + * parentheses, like: + * * <pre> * ( value1, value2, value3, etc... ) * </pre> * - * A String can either be quoted in the manner of a constant string in - * Java, or unquoted. If unquoted, the string can only contain - * alphanumerics, underscores (_), periods (.), dollar signs ($), colons - * (:), or forward slashes (/). If any other character appears in the - * string, it must be quoted (i.e., surrounded by " characters). - * Quoted strings may also contain \n, \t, \f, \v, \b, and \a escapes, - * octal escapes of the form \000, and unicode escapes of the form of \U - * followed by four hexadecimal characters. Any other character escaped - * by a backslash will be treated as that character, and the escaping - * backslash character will be omitted. Thus, to represent an actual - * backslash, it must appear as \\ in the quoted string. <p> + * A String can either be quoted in the manner of a constant string in Java, or + * unquoted. If unquoted, the string can only contain alphanumerics, underscores + * (_), periods (.), dollar signs ($), colons (:), or forward slashes (/). If + * any other character appears in the string, it must be quoted (i.e., + * surrounded by " characters). Quoted strings may also contain \n, \t, \f, + * \v, \b, and \a escapes, octal escapes of the form \000, and unicode escapes + * of the form of \U followed by four hexadecimal characters. Any other + * character escaped by a backslash will be treated as that character, and the + * escaping backslash character will be omitted. Thus, to represent an actual + * backslash, it must appear as \\ in the quoted string. + * <p> + * + * All whitespace between elements is ignored, and both //-style and /*-style + * comments are allowed to appear anywhere between elements. + * <p> * - * All whitespace between elements is ignored, and both //-style and - * /*-style comments are allowed to appear anywhere between elements. <p> + * If there are any syntax errors encountered while parsing, RuntimeExceptions + * are thrown with the line number and column of the problem. + * <p> * - * If there are any syntax errors encountered while parsing, - * RuntimeExceptions are thrown with the line number and column of the - * problem. <p> + * Currenty, HashMaps and ArrayLists are the actual Map and List classes used + * when creating the property list. + * <p> * - * Currenty, HashMaps and ArrayLists are the actual Map and List classes - * used when creating the property list. <p> + * Examples: + * <p> + * <blockquote> * - * Examples: <p><blockquote> - <pre> + * <pre> // This plist file represents a Map, since it starts with a '{'. { Map1 = { subkey1 = "foo"; }; @@ -104,443 +114,425 @@ import java.io.*; } ); } - </pre> - </blockquote> - * For those wondering, this is essentially a re-implementation of + * </pre> + * + * </blockquote> For those wondering, this is essentially a re-implementation of * NeXT/Apple's property lists, except that data values are not supported. * * @author clindberg@blacksmith.com * @version $Revision: 899 $ */ -public class PropertyListParser -{ - private char buffer[]; - private int currIndex; - private int lineNumber; - private int currLineStartIndex; - - /** Reads an object (String, List, or Map) from plistString and returns it. - * RuntimeExceptions are raised if there are parse problems. - */ - public static Object propertyListFromString(String plistString) - { - PropertyListParser parser = new PropertyListParser(plistString); - return parser.readTopLevelObject(); - } - - /** - * Reads all remaining characters from the Reader, and returns the - * result of propertyListFromString(). RuntimeExceptions are raised if - * there are parse problems - */ - public static Object propertyListFromReader(Reader reader) throws IOException - { - char charBuffer[] = new char[2048]; - StringBuffer stringBuffer = new StringBuffer(); - int numRead = 0; - - while (numRead >= 0) - { - numRead = reader.read(charBuffer); - if (numRead > 0) stringBuffer.append(charBuffer, 0, numRead); - } - - return propertyListFromString(stringBuffer.toString()); - } - - /** - * Reads the contents of the specified file, and parses the contents. - * If any error occurs, prints out a message using System.out.println() - * and returns null. - */ - public static Object propertyListFromFile(String filename) - { - try { - FileInputStream stream = new FileInputStream(filename); - return propertyListFromReader(new InputStreamReader(stream)); - } catch (Exception exception) { - String errorMessage = exception.getMessage(); - System.out.println("Error parsing property list from "+filename+": "+errorMessage); - } - - return null; - } - - /** - * Creates a new PropertyListParser to parse the contents of the - * specified String. - */ - public PropertyListParser(String plistString) - { - this(plistString.toCharArray()); - } - - /** - * Creates a new PropertyListParser to parse the specified char array. - */ - public PropertyListParser(char[] charArray) - { - buffer = charArray; - lineNumber = 1; - currLineStartIndex = 1; - currIndex = 0; - } - - public Object readTopLevelObject() - { - Object plist = readObject(); - - skipCommentWhitespace(); - if (!isAtEnd()) - { - throwParseException("Extra characters in plist string after parsing object. A plist should only contain one top-level object."); - } - - return plist; - } - - private void throwParseException(String errorMessage) - { - int column = currIndex - currLineStartIndex + 1; - throw new RuntimeException(errorMessage + " (Line " + lineNumber + ", column " + column + ")"); - } - - private void updateLineNumberWithIndex(int lineStartIndex) - { - lineNumber++; - currLineStartIndex = lineStartIndex; - } - - private boolean isAtEnd() - { - return currIndex >= buffer.length; - } - - private void skipDoubleslashComment() - { - while (!isAtEnd() && buffer[currIndex] != '\n') { - currIndex++; - } - } - - private void skipStandardCComment() - { - currIndex++; //skip over the starting '/' - - while (!isAtEnd()) - { - if (buffer[currIndex] == '\n') - updateLineNumberWithIndex(currIndex+1); - - currIndex++; - - if (buffer[currIndex-2] == '*' && buffer[currIndex-1] == '/') - { - return; - } - } - - throwParseException("Input exhausted while parsing comment"); - } - - private void skipWhitespace() - { - while (!isAtEnd() && isWhitespace(buffer[currIndex])) - { - if (buffer[currIndex] == '\n') - updateLineNumberWithIndex(currIndex+1); - currIndex++; - } - } - - private void skipCommentWhitespace() - { - boolean done = false; - - while (!done) - { - done = true; - - skipWhitespace(); - if ((buffer.length - currIndex) > 1 && buffer[currIndex] == '/') - { - if (buffer[currIndex+1] == '/') { - done = false; //iterate again - skipDoubleslashComment(); - } - else if (buffer[currIndex+1] == '*') { - done = false; //iterate again - skipStandardCComment(); - } - } - } - } - - private Object readObject() - { - skipCommentWhitespace(); - if (isAtEnd()) return null; - - // Data (i.e. byte[]) not supported - if (buffer[currIndex] == '"') - return readQuotedString(); - if (buffer[currIndex] == '(') - return readList(); - if (buffer[currIndex] == '{') - return readMap(); - - return readUnquotedString(); - } - - private static final byte valueForHexDigit(char c) - { - if(c >= '0' && c <= '9') return (byte)(c - '0'); - if(c >= 'a' && c <= 'f') return (byte)((c - 'a') + 10); - if(c >= 'A' && c <= 'F') return (byte)((c - 'A') + 10); - - return 0; - } - - private static final boolean isOctalDigit(char c) - { - return c >= '0' && c <= '7'; - } - - private static final boolean isHexDigit(char c) - { - return (c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); - } - - private static String unquotedStringChars = "._$:/"; // chars allowed in unquoted strings - private static String whitespaceChars = " \t\n\r\f"; - - private static final boolean isWhitespace(char c) - { - return whitespaceChars.indexOf(c) >= 0; - } - - private static final boolean isValidUnquotedStringChar(char c) - { - return ((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - unquotedStringChars.indexOf(c) >= 0); - } - - private String readUnquotedString() - { - int startIndex = currIndex; - - while (!isAtEnd() && isValidUnquotedStringChar(buffer[currIndex])) - currIndex++; - - if (startIndex == currIndex) - throwParseException("No allowable characters found to parse unquoted string"); - - return new String(buffer, startIndex, currIndex - startIndex); - } - - private String readQuotedString() - { - currIndex++; //skip over '"' - - StringBuffer stringBuffer = new StringBuffer(); - int startIndex = currIndex; - - while (!isAtEnd() && buffer[currIndex] != '"') - { - if (buffer[currIndex] != '\\') - { - if (buffer[currIndex] == '\n') - updateLineNumberWithIndex(currIndex+1); - - /* - * Just increment the index -- all these characters will be - * appended in chunks, either before an escape sequence or - * at the end. - */ - currIndex++; - } - else // it's an escape - { - /* Append anything scanned past before the '\\' */ - if (startIndex < currIndex) - stringBuffer.append(buffer, startIndex, currIndex - startIndex); - currIndex++; // skip over '\\' - - if (isAtEnd()) - throwParseException("Input exhausted while parsing escape sequence"); - - switch (buffer[currIndex]) - { - case 't': stringBuffer.append('\t'); currIndex++; break; // tab - case 'n': stringBuffer.append('\n'); currIndex++; break; // newline - case 'r': stringBuffer.append('\r'); currIndex++; break; // carriage return - case 'f': stringBuffer.append('\f'); currIndex++; break; // form feed - case 'b': stringBuffer.append('\b'); currIndex++; break; // backspace - case 'a': stringBuffer.append('\007'); currIndex++; break; // bell - case 'v': stringBuffer.append('\013'); currIndex++; break; // vertical tab - case 'U': - case 'u': - { - /* A Unicode escape. Always followed by 4 hex digits. */ - currIndex++; // skip past the 'U' - if ((currIndex+4) > buffer.length) - throwParseException("Not enough chars to parse \\U sequence"); - - if(!isHexDigit(buffer[currIndex]) || !isHexDigit(buffer[currIndex+1]) || - !isHexDigit(buffer[currIndex+2]) || !isHexDigit(buffer[currIndex+3])) - { - throwParseException("Four hex digits not found for \\U sequence"); - } - - byte byte3 = valueForHexDigit(buffer[currIndex]); - byte byte2 = valueForHexDigit(buffer[currIndex+1]); - byte byte1 = valueForHexDigit(buffer[currIndex+2]); - byte byte0 = valueForHexDigit(buffer[currIndex+3]); - char theChar = (char)((byte3 << 12) + (byte2 << 8) + (byte1 << 4) + byte0); - stringBuffer.append(theChar); - currIndex += 4; - break; - } - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - { - /* An octal escape. Expect 1, 2, or 3 octal digits. */ - int digits = 0; - int value = 0; - - do { - value *= 8; - value += (int)(buffer[currIndex] - '0'); - currIndex++; - digits++; - } while (digits <= 3 && !isAtEnd() && isOctalDigit(buffer[currIndex])); - - if (value > 255) - throwParseException("Value too large in octal escape sequence (> 0377)"); - - // This assumes value is in ISO Latin 1 encoding - stringBuffer.append((char)value); - break; - } - /* I guess plists can't have the \x{HEX}{HEX} escapes */ - default: - { - // Unknown escape sequence, just add the character. - // GCC warns if this isn't a '"', '\'', or '\\'... - stringBuffer.append(buffer[currIndex]); - if (buffer[currIndex] == '\n') - updateLineNumberWithIndex(currIndex+1); - currIndex++; - break; - } - } // end case - - /* Reset startIndex, so a verbatim copy will now start from this index */ - startIndex = currIndex; - - } //end '\\' escape - } - - if (isAtEnd()) - throwParseException("Input exhausted while parsing quoted string"); - if (startIndex < currIndex) - stringBuffer.append(buffer, startIndex, currIndex - startIndex); - currIndex++; //skip past '"' - - return stringBuffer.toString(); - } - - private List readList() - { - List newList = new ArrayList(); - - currIndex++; //skip over '(' - skipCommentWhitespace(); - while (!isAtEnd() && buffer[currIndex] != ')') - { - /* A comma is required between list elements */ - if (newList.size() > 0) - { - if (buffer[currIndex] != ',') - throwParseException("List parsing failed: expecting ','"); - currIndex++; - skipCommentWhitespace(); - if (isAtEnd()) - throwParseException("Input exhausted while parsing list"); - } - - if (buffer[currIndex] != ')') - { - Object plistObject = readObject(); - if (plistObject == null) - throwParseException("List parsing failed: could not read contained object."); - newList.add(plistObject); - skipCommentWhitespace(); - } - } - - if (isAtEnd()) - throwParseException("Input exhausted while parsing list"); - currIndex++; //skip past ')' - - return newList; - } - - private Map readMap() - { - HashMap newMap = new HashMap(); - - currIndex++; // skip over open brace - skipCommentWhitespace(); - - while (!isAtEnd() && buffer[currIndex] != '}') - { - Object key; - Object value; - - key = readObject(); - if (key == null || !(key instanceof String)) - throwParseException("Map parsing failed: could not parse key or key is not a String"); - - skipCommentWhitespace(); - if (isAtEnd() || buffer[currIndex] != '=') - throwParseException("Map parsing failed: expecting '='"); - currIndex++; //skip over '=' - skipCommentWhitespace(); - if (isAtEnd()) - throwParseException("Input exhausted while parsing map"); - - value = readObject(); - if (value == null) - throwParseException("Map parsing failed: could not parse value object"); - - skipCommentWhitespace(); - if (isAtEnd() || buffer[currIndex] != ';') - throwParseException("Map parsing failed: expecting ';'"); - currIndex++; //skip over ';' - skipCommentWhitespace(); - - newMap.put(key, value); - } - - if (isAtEnd()) - throwParseException("Input exhausted while parsing map"); - currIndex++; //skip past '}' - - return newMap; - } - - - public static void main(String[] args) - { - String filename = args[0]; - Object plist = PropertyListParser.propertyListFromFile(filename); - System.out.println(plist); - } +public class PropertyListParser { + private char buffer[]; + private int currIndex; + private int lineNumber; + private int currLineStartIndex; + + /** + * Reads an object (String, List, or Map) from plistString and returns it. + * RuntimeExceptions are raised if there are parse problems. + */ + public static Object propertyListFromString(String plistString) { + PropertyListParser parser = new PropertyListParser(plistString); + return parser.readTopLevelObject(); + } + + /** + * Reads all remaining characters from the Reader, and returns the result of + * propertyListFromString(). RuntimeExceptions are raised if there are parse + * problems + */ + public static Object propertyListFromReader(Reader reader) throws IOException { + char charBuffer[] = new char[2048]; + StringBuffer stringBuffer = new StringBuffer(); + int numRead = 0; + + while (numRead >= 0) { + numRead = reader.read(charBuffer); + if (numRead > 0) + stringBuffer.append(charBuffer, 0, numRead); + } + + return propertyListFromString(stringBuffer.toString()); + } + + /** + * Reads the contents of the specified file, and parses the contents. If any + * error occurs, prints out a message using System.out.println() and returns + * null. + */ + public static Object propertyListFromFile(String filename) { + try { + FileInputStream stream = new FileInputStream(filename); + return propertyListFromReader(new InputStreamReader(stream)); + } catch (Exception exception) { + String errorMessage = exception.getMessage(); + System.out.println("Error parsing property list from " + filename + ": " + errorMessage); + } + + return null; + } + + /** + * Creates a new PropertyListParser to parse the contents of the specified + * String. + */ + public PropertyListParser(String plistString) { + this(plistString.toCharArray()); + } + + /** + * Creates a new PropertyListParser to parse the specified char array. + */ + public PropertyListParser(char[] charArray) { + buffer = charArray; + lineNumber = 1; + currLineStartIndex = 1; + currIndex = 0; + } + + public Object readTopLevelObject() { + Object plist = readObject(); + + skipCommentWhitespace(); + if (!isAtEnd()) { + throwParseException( + "Extra characters in plist string after parsing object. A plist should only contain one top-level object."); + } + + return plist; + } + + private void throwParseException(String errorMessage) { + int column = currIndex - currLineStartIndex + 1; + throw new RuntimeException(errorMessage + " (Line " + lineNumber + ", column " + column + ")"); + } + + private void updateLineNumberWithIndex(int lineStartIndex) { + lineNumber++; + currLineStartIndex = lineStartIndex; + } + + private boolean isAtEnd() { + return currIndex >= buffer.length; + } + + private void skipDoubleslashComment() { + while (!isAtEnd() && buffer[currIndex] != '\n') { + currIndex++; + } + } + + private void skipStandardCComment() { + currIndex++; // skip over the starting '/' + + while (!isAtEnd()) { + if (buffer[currIndex] == '\n') + updateLineNumberWithIndex(currIndex + 1); + + currIndex++; + + if (buffer[currIndex - 2] == '*' && buffer[currIndex - 1] == '/') { + return; + } + } + + throwParseException("Input exhausted while parsing comment"); + } + + private void skipWhitespace() { + while (!isAtEnd() && isWhitespace(buffer[currIndex])) { + if (buffer[currIndex] == '\n') + updateLineNumberWithIndex(currIndex + 1); + currIndex++; + } + } + + private void skipCommentWhitespace() { + boolean done = false; + + while (!done) { + done = true; + + skipWhitespace(); + if ((buffer.length - currIndex) > 1 && buffer[currIndex] == '/') { + if (buffer[currIndex + 1] == '/') { + done = false; // iterate again + skipDoubleslashComment(); + } else if (buffer[currIndex + 1] == '*') { + done = false; // iterate again + skipStandardCComment(); + } + } + } + } + + private Object readObject() { + skipCommentWhitespace(); + if (isAtEnd()) + return null; + + // Data (i.e. byte[]) not supported + if (buffer[currIndex] == '"') + return readQuotedString(); + if (buffer[currIndex] == '(') + return readList(); + if (buffer[currIndex] == '{') + return readMap(); + + return readUnquotedString(); + } + + private static final byte valueForHexDigit(char c) { + if (c >= '0' && c <= '9') + return (byte) (c - '0'); + if (c >= 'a' && c <= 'f') + return (byte) ((c - 'a') + 10); + if (c >= 'A' && c <= 'F') + return (byte) ((c - 'A') + 10); + + return 0; + } + + private static final boolean isOctalDigit(char c) { + return c >= '0' && c <= '7'; + } + + private static final boolean isHexDigit(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } + + private static String unquotedStringChars = "._$:/"; // chars allowed in unquoted strings + private static String whitespaceChars = " \t\n\r\f"; + + private static final boolean isWhitespace(char c) { + return whitespaceChars.indexOf(c) >= 0; + } + + private static final boolean isValidUnquotedStringChar(char c) { + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') + || unquotedStringChars.indexOf(c) >= 0); + } + + private String readUnquotedString() { + int startIndex = currIndex; + + while (!isAtEnd() && isValidUnquotedStringChar(buffer[currIndex])) + currIndex++; + + if (startIndex == currIndex) + throwParseException("No allowable characters found to parse unquoted string"); + + return new String(buffer, startIndex, currIndex - startIndex); + } + + private String readQuotedString() { + currIndex++; // skip over '"' + + StringBuffer stringBuffer = new StringBuffer(); + int startIndex = currIndex; + + while (!isAtEnd() && buffer[currIndex] != '"') { + if (buffer[currIndex] != '\\') { + if (buffer[currIndex] == '\n') + updateLineNumberWithIndex(currIndex + 1); + + /* + * Just increment the index -- all these characters will be appended in chunks, + * either before an escape sequence or at the end. + */ + currIndex++; + } else // it's an escape + { + /* Append anything scanned past before the '\\' */ + if (startIndex < currIndex) + stringBuffer.append(buffer, startIndex, currIndex - startIndex); + currIndex++; // skip over '\\' + + if (isAtEnd()) + throwParseException("Input exhausted while parsing escape sequence"); + + switch (buffer[currIndex]) { + case 't': + stringBuffer.append('\t'); + currIndex++; + break; // tab + case 'n': + stringBuffer.append('\n'); + currIndex++; + break; // newline + case 'r': + stringBuffer.append('\r'); + currIndex++; + break; // carriage return + case 'f': + stringBuffer.append('\f'); + currIndex++; + break; // form feed + case 'b': + stringBuffer.append('\b'); + currIndex++; + break; // backspace + case 'a': + stringBuffer.append('\007'); + currIndex++; + break; // bell + case 'v': + stringBuffer.append('\013'); + currIndex++; + break; // vertical tab + case 'U': + case 'u': { + /* A Unicode escape. Always followed by 4 hex digits. */ + currIndex++; // skip past the 'U' + if ((currIndex + 4) > buffer.length) + throwParseException("Not enough chars to parse \\U sequence"); + + if (!isHexDigit(buffer[currIndex]) || !isHexDigit(buffer[currIndex + 1]) + || !isHexDigit(buffer[currIndex + 2]) || !isHexDigit(buffer[currIndex + 3])) { + throwParseException("Four hex digits not found for \\U sequence"); + } + + byte byte3 = valueForHexDigit(buffer[currIndex]); + byte byte2 = valueForHexDigit(buffer[currIndex + 1]); + byte byte1 = valueForHexDigit(buffer[currIndex + 2]); + byte byte0 = valueForHexDigit(buffer[currIndex + 3]); + char theChar = (char) ((byte3 << 12) + (byte2 << 8) + (byte1 << 4) + byte0); + stringBuffer.append(theChar); + currIndex += 4; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + /* An octal escape. Expect 1, 2, or 3 octal digits. */ + int digits = 0; + int value = 0; + + do { + value *= 8; + value += (int) (buffer[currIndex] - '0'); + currIndex++; + digits++; + } while (digits <= 3 && !isAtEnd() && isOctalDigit(buffer[currIndex])); + + if (value > 255) + throwParseException("Value too large in octal escape sequence (> 0377)"); + + // This assumes value is in ISO Latin 1 encoding + stringBuffer.append((char) value); + break; + } + /* I guess plists can't have the \x{HEX}{HEX} escapes */ + default: { + // Unknown escape sequence, just add the character. + // GCC warns if this isn't a '"', '\'', or '\\'... + stringBuffer.append(buffer[currIndex]); + if (buffer[currIndex] == '\n') + updateLineNumberWithIndex(currIndex + 1); + currIndex++; + break; + } + } // end case + + /* Reset startIndex, so a verbatim copy will now start from this index */ + startIndex = currIndex; + + } // end '\\' escape + } + + if (isAtEnd()) + throwParseException("Input exhausted while parsing quoted string"); + if (startIndex < currIndex) + stringBuffer.append(buffer, startIndex, currIndex - startIndex); + currIndex++; // skip past '"' + + return stringBuffer.toString(); + } + + private List readList() { + List newList = new ArrayList(); + + currIndex++; // skip over '(' + skipCommentWhitespace(); + while (!isAtEnd() && buffer[currIndex] != ')') { + /* A comma is required between list elements */ + if (newList.size() > 0) { + if (buffer[currIndex] != ',') + throwParseException("List parsing failed: expecting ','"); + currIndex++; + skipCommentWhitespace(); + if (isAtEnd()) + throwParseException("Input exhausted while parsing list"); + } + + if (buffer[currIndex] != ')') { + Object plistObject = readObject(); + if (plistObject == null) + throwParseException("List parsing failed: could not read contained object."); + newList.add(plistObject); + skipCommentWhitespace(); + } + } + + if (isAtEnd()) + throwParseException("Input exhausted while parsing list"); + currIndex++; // skip past ')' + + return newList; + } + + private Map readMap() { + HashMap newMap = new HashMap(); + + currIndex++; // skip over open brace + skipCommentWhitespace(); + + while (!isAtEnd() && buffer[currIndex] != '}') { + Object key; + Object value; + + key = readObject(); + if (key == null || !(key instanceof String)) + throwParseException("Map parsing failed: could not parse key or key is not a String"); + + skipCommentWhitespace(); + if (isAtEnd() || buffer[currIndex] != '=') + throwParseException("Map parsing failed: expecting '='"); + currIndex++; // skip over '=' + skipCommentWhitespace(); + if (isAtEnd()) + throwParseException("Input exhausted while parsing map"); + + value = readObject(); + if (value == null) + throwParseException("Map parsing failed: could not parse value object"); + + skipCommentWhitespace(); + if (isAtEnd() || buffer[currIndex] != ';') + throwParseException("Map parsing failed: expecting ';'"); + currIndex++; // skip over ';' + skipCommentWhitespace(); + + newMap.put(key, value); + } + + if (isAtEnd()) + throwParseException("Input exhausted while parsing map"); + currIndex++; // skip past '}' + + return newMap; + } + + public static void main(String[] args) { + String filename = args[0]; + Object plist = PropertyListParser.propertyListFromFile(filename); + System.out.println(plist); + } } - diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java index 6d35b7b..59104e5 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/QueueMap.java @@ -21,518 +21,488 @@ package net.wotonomy.foundation.internal; import java.util.*; //collections /** -* A queue based implementation of the Map interface. This class provides for -* all the opertions of a map, but keeps the entries in the same sequence as -* originally added. The first entry placed in the map will be the first -* entry retreived during iteration: first-in, first-out (FIFO). <BR><BR> -* -* Keys cannot be duplicated. If an entry is made using a key that already -* exists, the value for that key will be replaced with that new value. There -* are no such restrictions on the values. The values may be null. <BR><BR> -* -* Some convenience methods are provided for the queue type operations. The -* first key can be retreived as well as the last key. A key can be used -* to retreive its corresponding value from the map. A value can also be used -* to retreive its key from the map, however, since there may be multiple values -* of the same equality, the first key found will be returned. <BR><BR> -* -* @author rglista@blacksmith.com -* @author mpowers@blacksmith.com -* @date $Date: 2006-02-18 17:41:36 -0500 (Sat, 18 Feb 2006) $ -* @revision $Revision: 899 $ -*/ -public class QueueMap implements Map -{ - List values; - List keys; - Map keyToValue; - -/** -* Creates an empty QueueMap. -*/ - public QueueMap() - { - values = new LinkedList(); - keys = new LinkedList(); - keyToValue = new HashMap(); - } - -/** -* Creates a QueueMap and places the entries from the passed in map into this map. -* The order of the entries is based on however the iterator iteratated through -* the map entries. -* @param t A map object. -*/ - public QueueMap( Map t ) - { - values = new ArrayList(); - keys = new ArrayList(); - keyToValue = new HashMap(); - - putAll( t ); - } - -/** -* Removes all the entries from this map. -*/ - public void clear() - { - values.clear(); - keys.clear(); - keyToValue.clear(); - } - -/** -* Tests to see if the key is contained in the map. If the key is present in -* the map, then TRUE is returned, otherwise FALSE is returned. The equals() -* operation is used to determine equality. -* @return True if the map contains the given key, false otherwise. -*/ - public boolean containsKey( Object key ) - { - return keyToValue.containsKey( key ); - } - -/** -* Tests to see if the value is contained in the map. If the value is present in -* the map, then TRUE is returned, otherwise FALSE is returned. The equals() -* operation is used to determine equality. The value can be null and will -* return TRUE if null is a value in the map. If TRUE is returned, then there -* may be more than one values in the map. -* @return True if the map contains the given value, false otherwise. -*/ - public boolean containsValue( Object value ) - { - return keyToValue.containsValue( value ); - } - -/** -* Returns a set view of the mappings contained in this map. Each element -* in the returned set is a <tt>Map.Entry</tt>. The returned set is NOT backed -* by the map, so changes to the map are NOT reflected in the set, and vice-versa. -* The returned set is independent of this Map and its underlying structure. -* -* @return a set view of the mappings contained in this map. -*/ - public Set entrySet() - { - Set result = new HashSet(keys.size(), 1F); - - for ( int i = 0; i < keys.size(); i++ ) - { - result.add( new KeyValuePair( keys.get(i), values.get(i) ) ); - } - - return result; - } - -/** -* Compares the specified object with this map for equality. Returns -* <tt>true</tt> if the given object is also a map and the two Maps -* represent the same mappings. More formally, two maps <tt>t1</tt> and -* <tt>t2</tt> represent the same mappings if -* <tt>t1.entrySet().equals(t2.entrySet())</tt>. This ensures that the -* <tt>equals</tt> method works properly across different implementations -* of the <tt>Map</tt> interface. -* -* @param o object to be compared for equality with this map. -* @return <tt>true</tt> if the specified object is equal to this map. -*/ - public boolean equals( Object o ) - { - return keyToValue.equals( o ); - } - -/** -* Returns the corresponding value for the given key. The returned value may be -* null as that is a legal value in this map. However, if the key is not -* contained in this map then null is also returned. Use the containsKey() -* method to distinguish between the two cases. -* @param key A key into the map. -* @return The value corresponding to the key (can be null), or null if the key -* is not contained in the map. -*/ - public Object get( Object key ) - { - return keyToValue.get( key ); - } - -/** -* Returns the hash code value for this map. The hash code of a map -* is defined to be the sum of the hashCodes of each entry in the map's -* entrySet view. This ensures that <tt>t1.equals(t2)</tt> implies -* that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps -* <tt>t1</tt> and <tt>t2</tt>, as required by the general -* contract of Object.hashCode. -* -* @return the hash code value for this map. -*/ - public int hashCode() - { - return keyToValue.hashCode(); - } - -/** -* Returns true is this map contains no key-value mappings. -* @return True is this map contains no entries, false otherwise. -*/ - public boolean isEmpty() - { - return keyToValue.isEmpty(); - } - -/** -* Returns the keys used in the map. There is no order implied in the returned -* set and may be different than the the order in which they were inserted. -* @return A Set containing all the keys used in the map. -*/ - public Set keySet() - { - Set result = new HashSet(keys.size(), 1F); - - for ( int i = 0; i < keys.size(); i++ ) - { - result.add( keys.get(i) ); - } - - return result; - } - -/** -* Places the key/value entry into the map at the end position. If the key -* already exists in the map, then its value is replaced by the new given -* value. The mapping does not change order in this case. The specified -* key cannot be null. -* @param key The key to place into the map, cannot be null. -* @param value The value to associate with the key, may be null. -* @return Null if the key is new, the replaced value if the key already -* existed. (Note: If the key was null, then null is returned.) -*/ - public Object put( Object key, Object value ) - { - if ( key == null ) return null; - - if ( keys.contains( key ) ) - { - values.remove( keys.indexOf( key ) ); - values.add( keys.indexOf( key ), value ); - } - else - { - values.add( value ); - keys.add( key ); - } - - return keyToValue.put( key, value ); - } - -/** -* Places all the entries from this given map into this map. If the keys -* already exist, then there values are replaced. -* @param t A map of key/value entries. -*/ - public void putAll( Map t ) - { - if ( t == null ) - { - // Nothing to do! - return; - } - - // Place the entries from the passed in map into this map. - for ( Iterator it = t.keySet().iterator(); it.hasNext(); ) - { - Object aKey = it.next(); - put( aKey, t.get( aKey ) ); - } - } - -/** -* Remove the mapping of the key and associated value from this map. -* Note: null is a valid value in the map. -* @param key A key to remove from the map, cannot be null. -* @return The value of the removed mapping, null if the mapping did not exist. -* Null could also be returned if the associated value of the key was null. -*/ - public Object remove( Object key ) - { - if ( key == null ) return null; - - Object value = null; - - if ( containsKey( key ) ) - { - value = keyToValue.remove( key ); - int i = values.indexOf( value ); - if ( i != -1 ) - { - keys.remove( i ); - values.remove( i ); - } - } - - return value; - } - -/** -* Returns the number of key/value pairs in this map. -* @return The number of pairs. -*/ - public int size() - { - return values.size(); - } - -/** -* Returns an ordered list of keys from the map. The order is the same -* as the added order of the mapped items.<br> -* NOTE: The returned list is the underlying keys list used by this class. -* If modification are to be made to this list, it should be cloned first. -* @return An ordered list of keys. -*/ - public List keys() - { - return keys; - } - -/** -* Returns an ordered list of values from the map. The order is the same -* as the added order of the mapped items. -* NOTE: The returned list is the underlying keys list used by this class. -* If modification are to be made to this list, it should be cloned first. -* @return An ordered list of values. -*/ - public Collection values() - { - return values; - } - -/** -* Returns the corresponding value for the given key. The returned value may be -* null as that is a legal value in this map. However, if the key is not -* contained in this map then null is also returned. Use the containsKey() -* method to distinguish between the two cases. -* @param key A key into the map. -* @return The value corresponding to the key (can be null), or null if the key -* is not contained in the map. -*/ - public Object getValueForKey( Object key ) - { - return keyToValue.get( key ); - } - -/** -* Returns the associated key for this value. Since there may be more than one -* of the same value in the map, this returns the first key associated with this -* value. Null is returned if the value is not in the map. -* @param value A value that is contained in this map. -* @return A first key that corresponds to this value. -*/ - public Object getKeyForValue( Object value ) - { - int i = values.indexOf( value ); - if ( i != -1 ) - { - return keys.get( i ); - } - return null; - } - -/** -* Returns the first key in the map. If the map is empty, then null is -* returned. -* @return The first key in the map, null if there are no mappings. -*/ - public Object getFirstKey() - { - if ( keys.size() < 1 ) - { - return null; - } - return keys.get(0); - } - -/** -* Returns the last key in the map. If the map is empty, then null is -* returned. -* @return The last key in the map, null if there are no mappings. -*/ - public Object getLastKey() - { - if ( keys.size() < 1 ) - { - return null; - } - return keys.get( keys.size() -1 ); - } - -/** -* This class contains a key/value pair. The key must be a valid object, -* it cannot be null. The value can be a valid object or null. -*/ - static public class KeyValuePair implements Map.Entry - { - Object key; - Object value; - - /** - * Default constructor. The constructor takes the key and value as parameters. - * Since the key cannot be null, it must be specified during initialization. - * The value can be null. - * @param key The key object of this pair. The key cannot be null. - * @param value The value object of this pair. The value can be null. - */ - public KeyValuePair( Object aKey, Object aValue ) - { - key = aKey; - value = aValue; - } - - /** - * Returns the key object of this pair. - * @return The key object. - */ - public Object getKey() - { - return key; - } - - /** - * Returns the the value object of this pair. May be null. - * @return The value object. - */ - public Object getValue() - { - return value; - } - - /** - * Sets the value object of this pair. May be an object or null. - * @param aValue The value object to place into this pair. - */ - public Object setValue( Object aValue ) - { - Object result = value; - value = aValue; - return result; - } - - public boolean equals( Object o ) - { - if ( o instanceof KeyValuePair ) - { - KeyValuePair p = (KeyValuePair) o; - if ( ( key.equals( p.getKey() ) ) && ( value.equals( p.getValue() ) ) ) - { - return true; - } - } - return false; - } - } - - /** - * Returns a string reprsentation of this class. The contents of the - * map are placed in the string in its proper order. - */ - public String toString() - { - int max = size() - 1; - StringBuffer buf = new StringBuffer(); - - buf.append("{"); - for (int j = 0; j <= max; j++) - { - buf.append(keys.get(j) + "=" + values.get(j)); - if (j < max) - { - buf.append(", "); - } - } - buf.append("}"); - - return buf.toString(); - } - - // unit test - public static void main( String[] argv ) - { - QueueMap qMap; - - qMap = new QueueMap(); - for (int i = 0; i < 5; i++) - { - qMap.put(Integer.toString(i), Integer.toString(i)); - } - System.out.println("\nMap = " + qMap); - System.out.println("Keys = " + qMap.keys()); - System.out.println("Values = " + qMap.values()); - - qMap = new QueueMap(); - for (int i = 0; i < 5; i++) - { - qMap.put(Integer.toString(i), "A"); - } - System.out.println("\nMap = " + qMap); - System.out.println("Keys = " + qMap.keys()); - System.out.println("Values = " + qMap.values()); - - qMap = new QueueMap(); - for (int i = 0; i < 5; i++) - { - qMap.put(Integer.toString(i), null); - } - System.out.println("\nMap = " + qMap); - System.out.println("Keys = " + qMap.keys()); - System.out.println("Values = " + qMap.values()); - - Map aMap = new HashMap(); - for (int i = 0; i < 5; i++) - { - aMap.put(Integer.toString(i), Integer.toString(i)); - } - qMap = new QueueMap( aMap ); - System.out.println("\nHashMap = " + aMap); - System.out.println("Map = " + qMap); - System.out.println("Keys = " + qMap.keys()); - System.out.println("Values = " + qMap.values()); - - qMap = new QueueMap(); - qMap.put( "Test1", "String1" ); - qMap.put( "Test2", "String2" ); - qMap.put( "Test3", "String3" ); - qMap.put( "Test4", "String4" ); - qMap.put( "Test5", "String5" ); - System.out.println("\nStandard Test, Map = " + qMap); - qMap.put( "Test6", "String6" ); - qMap.put( "Test7", "String7" ); - System.out.println("Put New Test, Map = " + qMap); - qMap.put( "Test2", "New String2" ); - qMap.put( "Test6", "New String6" ); - System.out.println("Put Existing Test, Map = " + qMap); - qMap.put( "Test5", null ); - qMap.put( "Test8", null ); - System.out.println("Put Null Test, Map = " + qMap); - qMap.remove( "Test1" ); - qMap.remove( "Test3" ); - qMap.remove( "Test5" ); - qMap.remove( "Test9" ); - System.out.println("Remove Test, Map = " + qMap); - System.out.println(" Keys = " + qMap.keys()); - System.out.println(" Values = " + qMap.values()); - qMap.clear(); - qMap.put( "Test10", "String10" ); - qMap.put( "Test11", "String11" ); - qMap.put( "Test12", "String12" ); - System.out.println("Clear Test, Map = " + qMap); - - aMap = new HashMap(); - aMap.put( "Test10", "String10" ); - aMap.put( "Test11", "String11" ); - aMap.put( "Test12", "String12" ); - System.out.println("Equality Test, Equal = " + qMap.equals( aMap )); - } + * A queue based implementation of the Map interface. This class provides for + * all the opertions of a map, but keeps the entries in the same sequence as + * originally added. The first entry placed in the map will be the first entry + * retreived during iteration: first-in, first-out (FIFO). <BR> + * <BR> + * + * Keys cannot be duplicated. If an entry is made using a key that already + * exists, the value for that key will be replaced with that new value. There + * are no such restrictions on the values. The values may be null. <BR> + * <BR> + * + * Some convenience methods are provided for the queue type operations. The + * first key can be retreived as well as the last key. A key can be used to + * retreive its corresponding value from the map. A value can also be used to + * retreive its key from the map, however, since there may be multiple values of + * the same equality, the first key found will be returned. <BR> + * <BR> + * + * @author rglista@blacksmith.com + * @author mpowers@blacksmith.com + * @date $Date: 2006-02-18 17:41:36 -0500 (Sat, 18 Feb 2006) $ + * @revision $Revision: 899 $ + */ +public class QueueMap implements Map { + List values; + List keys; + Map keyToValue; + + /** + * Creates an empty QueueMap. + */ + public QueueMap() { + values = new LinkedList(); + keys = new LinkedList(); + keyToValue = new HashMap(); + } + + /** + * Creates a QueueMap and places the entries from the passed in map into this + * map. The order of the entries is based on however the iterator iteratated + * through the map entries. + * + * @param t A map object. + */ + public QueueMap(Map t) { + values = new ArrayList(); + keys = new ArrayList(); + keyToValue = new HashMap(); + + putAll(t); + } + + /** + * Removes all the entries from this map. + */ + public void clear() { + values.clear(); + keys.clear(); + keyToValue.clear(); + } + + /** + * Tests to see if the key is contained in the map. If the key is present in the + * map, then TRUE is returned, otherwise FALSE is returned. The equals() + * operation is used to determine equality. + * + * @return True if the map contains the given key, false otherwise. + */ + public boolean containsKey(Object key) { + return keyToValue.containsKey(key); + } + + /** + * Tests to see if the value is contained in the map. If the value is present in + * the map, then TRUE is returned, otherwise FALSE is returned. The equals() + * operation is used to determine equality. The value can be null and will + * return TRUE if null is a value in the map. If TRUE is returned, then there + * may be more than one values in the map. + * + * @return True if the map contains the given value, false otherwise. + */ + public boolean containsValue(Object value) { + return keyToValue.containsValue(value); + } + + /** + * Returns a set view of the mappings contained in this map. Each element in the + * returned set is a <tt>Map.Entry</tt>. The returned set is NOT backed by the + * map, so changes to the map are NOT reflected in the set, and vice-versa. The + * returned set is independent of this Map and its underlying structure. + * + * @return a set view of the mappings contained in this map. + */ + public Set entrySet() { + Set result = new HashSet(keys.size(), 1F); + + for (int i = 0; i < keys.size(); i++) { + result.add(new KeyValuePair(keys.get(i), values.get(i))); + } + + return result; + } + + /** + * Compares the specified object with this map for equality. Returns + * <tt>true</tt> if the given object is also a map and the two Maps represent + * the same mappings. More formally, two maps <tt>t1</tt> and <tt>t2</tt> + * represent the same mappings if <tt>t1.entrySet().equals(t2.entrySet())</tt>. + * This ensures that the <tt>equals</tt> method works properly across different + * implementations of the <tt>Map</tt> interface. + * + * @param o object to be compared for equality with this map. + * @return <tt>true</tt> if the specified object is equal to this map. + */ + public boolean equals(Object o) { + return keyToValue.equals(o); + } + + /** + * Returns the corresponding value for the given key. The returned value may be + * null as that is a legal value in this map. However, if the key is not + * contained in this map then null is also returned. Use the containsKey() + * method to distinguish between the two cases. + * + * @param key A key into the map. + * @return The value corresponding to the key (can be null), or null if the key + * is not contained in the map. + */ + public Object get(Object key) { + return keyToValue.get(key); + } + + /** + * Returns the hash code value for this map. The hash code of a map is defined + * to be the sum of the hashCodes of each entry in the map's entrySet view. This + * ensures that <tt>t1.equals(t2)</tt> implies that + * <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps <tt>t1</tt> and + * <tt>t2</tt>, as required by the general contract of Object.hashCode. + * + * @return the hash code value for this map. + */ + public int hashCode() { + return keyToValue.hashCode(); + } + + /** + * Returns true is this map contains no key-value mappings. + * + * @return True is this map contains no entries, false otherwise. + */ + public boolean isEmpty() { + return keyToValue.isEmpty(); + } + + /** + * Returns the keys used in the map. There is no order implied in the returned + * set and may be different than the the order in which they were inserted. + * + * @return A Set containing all the keys used in the map. + */ + public Set keySet() { + Set result = new HashSet(keys.size(), 1F); + + for (int i = 0; i < keys.size(); i++) { + result.add(keys.get(i)); + } + + return result; + } + + /** + * Places the key/value entry into the map at the end position. If the key + * already exists in the map, then its value is replaced by the new given value. + * The mapping does not change order in this case. The specified key cannot be + * null. + * + * @param key The key to place into the map, cannot be null. + * @param value The value to associate with the key, may be null. + * @return Null if the key is new, the replaced value if the key already + * existed. (Note: If the key was null, then null is returned.) + */ + public Object put(Object key, Object value) { + if (key == null) + return null; + + if (keys.contains(key)) { + values.remove(keys.indexOf(key)); + values.add(keys.indexOf(key), value); + } else { + values.add(value); + keys.add(key); + } + + return keyToValue.put(key, value); + } + + /** + * Places all the entries from this given map into this map. If the keys already + * exist, then there values are replaced. + * + * @param t A map of key/value entries. + */ + public void putAll(Map t) { + if (t == null) { + // Nothing to do! + return; + } + + // Place the entries from the passed in map into this map. + for (Iterator it = t.keySet().iterator(); it.hasNext();) { + Object aKey = it.next(); + put(aKey, t.get(aKey)); + } + } + + /** + * Remove the mapping of the key and associated value from this map. Note: null + * is a valid value in the map. + * + * @param key A key to remove from the map, cannot be null. + * @return The value of the removed mapping, null if the mapping did not exist. + * Null could also be returned if the associated value of the key was + * null. + */ + public Object remove(Object key) { + if (key == null) + return null; + + Object value = null; + + if (containsKey(key)) { + value = keyToValue.remove(key); + int i = values.indexOf(value); + if (i != -1) { + keys.remove(i); + values.remove(i); + } + } + + return value; + } + + /** + * Returns the number of key/value pairs in this map. + * + * @return The number of pairs. + */ + public int size() { + return values.size(); + } + + /** + * Returns an ordered list of keys from the map. The order is the same as the + * added order of the mapped items.<br> + * NOTE: The returned list is the underlying keys list used by this class. If + * modification are to be made to this list, it should be cloned first. + * + * @return An ordered list of keys. + */ + public List keys() { + return keys; + } + + /** + * Returns an ordered list of values from the map. The order is the same as the + * added order of the mapped items. NOTE: The returned list is the underlying + * keys list used by this class. If modification are to be made to this list, it + * should be cloned first. + * + * @return An ordered list of values. + */ + public Collection values() { + return values; + } + + /** + * Returns the corresponding value for the given key. The returned value may be + * null as that is a legal value in this map. However, if the key is not + * contained in this map then null is also returned. Use the containsKey() + * method to distinguish between the two cases. + * + * @param key A key into the map. + * @return The value corresponding to the key (can be null), or null if the key + * is not contained in the map. + */ + public Object getValueForKey(Object key) { + return keyToValue.get(key); + } + + /** + * Returns the associated key for this value. Since there may be more than one + * of the same value in the map, this returns the first key associated with this + * value. Null is returned if the value is not in the map. + * + * @param value A value that is contained in this map. + * @return A first key that corresponds to this value. + */ + public Object getKeyForValue(Object value) { + int i = values.indexOf(value); + if (i != -1) { + return keys.get(i); + } + return null; + } + + /** + * Returns the first key in the map. If the map is empty, then null is returned. + * + * @return The first key in the map, null if there are no mappings. + */ + public Object getFirstKey() { + if (keys.size() < 1) { + return null; + } + return keys.get(0); + } + + /** + * Returns the last key in the map. If the map is empty, then null is returned. + * + * @return The last key in the map, null if there are no mappings. + */ + public Object getLastKey() { + if (keys.size() < 1) { + return null; + } + return keys.get(keys.size() - 1); + } + + /** + * This class contains a key/value pair. The key must be a valid object, it + * cannot be null. The value can be a valid object or null. + */ + static public class KeyValuePair implements Map.Entry { + Object key; + Object value; + + /** + * Default constructor. The constructor takes the key and value as parameters. + * Since the key cannot be null, it must be specified during initialization. The + * value can be null. + * + * @param key The key object of this pair. The key cannot be null. + * @param value The value object of this pair. The value can be null. + */ + public KeyValuePair(Object aKey, Object aValue) { + key = aKey; + value = aValue; + } + + /** + * Returns the key object of this pair. + * + * @return The key object. + */ + public Object getKey() { + return key; + } + + /** + * Returns the the value object of this pair. May be null. + * + * @return The value object. + */ + public Object getValue() { + return value; + } + + /** + * Sets the value object of this pair. May be an object or null. + * + * @param aValue The value object to place into this pair. + */ + public Object setValue(Object aValue) { + Object result = value; + value = aValue; + return result; + } + + public boolean equals(Object o) { + if (o instanceof KeyValuePair) { + KeyValuePair p = (KeyValuePair) o; + if ((key.equals(p.getKey())) && (value.equals(p.getValue()))) { + return true; + } + } + return false; + } + } + + /** + * Returns a string reprsentation of this class. The contents of the map are + * placed in the string in its proper order. + */ + public String toString() { + int max = size() - 1; + StringBuffer buf = new StringBuffer(); + + buf.append("{"); + for (int j = 0; j <= max; j++) { + buf.append(keys.get(j) + "=" + values.get(j)); + if (j < max) { + buf.append(", "); + } + } + buf.append("}"); + + return buf.toString(); + } + + // unit test + public static void main(String[] argv) { + QueueMap qMap; + + qMap = new QueueMap(); + for (int i = 0; i < 5; i++) { + qMap.put(Integer.toString(i), Integer.toString(i)); + } + System.out.println("\nMap = " + qMap); + System.out.println("Keys = " + qMap.keys()); + System.out.println("Values = " + qMap.values()); + + qMap = new QueueMap(); + for (int i = 0; i < 5; i++) { + qMap.put(Integer.toString(i), "A"); + } + System.out.println("\nMap = " + qMap); + System.out.println("Keys = " + qMap.keys()); + System.out.println("Values = " + qMap.values()); + + qMap = new QueueMap(); + for (int i = 0; i < 5; i++) { + qMap.put(Integer.toString(i), null); + } + System.out.println("\nMap = " + qMap); + System.out.println("Keys = " + qMap.keys()); + System.out.println("Values = " + qMap.values()); + + Map aMap = new HashMap(); + for (int i = 0; i < 5; i++) { + aMap.put(Integer.toString(i), Integer.toString(i)); + } + qMap = new QueueMap(aMap); + System.out.println("\nHashMap = " + aMap); + System.out.println("Map = " + qMap); + System.out.println("Keys = " + qMap.keys()); + System.out.println("Values = " + qMap.values()); + + qMap = new QueueMap(); + qMap.put("Test1", "String1"); + qMap.put("Test2", "String2"); + qMap.put("Test3", "String3"); + qMap.put("Test4", "String4"); + qMap.put("Test5", "String5"); + System.out.println("\nStandard Test, Map = " + qMap); + qMap.put("Test6", "String6"); + qMap.put("Test7", "String7"); + System.out.println("Put New Test, Map = " + qMap); + qMap.put("Test2", "New String2"); + qMap.put("Test6", "New String6"); + System.out.println("Put Existing Test, Map = " + qMap); + qMap.put("Test5", null); + qMap.put("Test8", null); + System.out.println("Put Null Test, Map = " + qMap); + qMap.remove("Test1"); + qMap.remove("Test3"); + qMap.remove("Test5"); + qMap.remove("Test9"); + System.out.println("Remove Test, Map = " + qMap); + System.out.println(" Keys = " + qMap.keys()); + System.out.println(" Values = " + qMap.values()); + qMap.clear(); + qMap.put("Test10", "String10"); + qMap.put("Test11", "String11"); + qMap.put("Test12", "String12"); + System.out.println("Clear Test, Map = " + qMap); + + aMap = new HashMap(); + aMap.put("Test10", "String10"); + aMap.put("Test11", "String11"); + aMap.put("Test12", "String12"); + System.out.println("Equality Test, Equal = " + qMap.equals(aMap)); + } } - - diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java index 9133a8d..8694564 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/URLResourceReader.java @@ -47,7 +47,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== - */ + */ package net.wotonomy.foundation.internal; @@ -61,147 +61,134 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** - * This implementation of URL Resource Reader assumes 2 types - * of base urls. A base url that ends with / is considered a - * resource folder, whereas a resource that does not end with - * / is considered a zip/jar resource folder. + * This implementation of URL Resource Reader assumes 2 types of base urls. A + * base url that ends with / is considered a resource folder, whereas a resource + * that does not end with / is considered a zip/jar resource folder. * - * If the resource folder happens is a zip/jar archive, the - * entries are always cached. - * For non-zip base urls, one could specify whether or not it should - * be cached. + * If the resource folder happens is a zip/jar archive, the entries are always + * cached. For non-zip base urls, one could specify whether or not it should be + * cached. * * @author Harish Prabandham */ -public class URLResourceReader { - private Hashtable resourceCache = new Hashtable(); - private boolean iszip = true; - private URL url = null; - private boolean cache = true; - - /** - * Creates a new URLResourceReader object. You can either give - * the URL of the zip/jar file or a base url where to - * look for additional resources. If the url ends with - * "/" then it is assumed to be a Base URL. - * @param The base url to look for the resources. - * @param If the base url is not a zip/jar, then true indicates - * that entries should be cached, false otherwise. - */ - public URLResourceReader(URL baseurl, boolean cache) throws IOException { - this.url = baseurl; - this.cache = cache; - this.iszip = !url.getFile().endsWith("/"); - if(this.iszip) - this.cache = true; - initialize(); - } - - /** - * equivalent to URLResourceReader(baseurl, false) - */ - public URLResourceReader(URL baseurl) throws IOException { - this(baseurl, false); - } - - /** - * Creates a new URLResourceReader object with the given - * input stream. The stream is assumed to be a zip/jar - * stream. - */ - public URLResourceReader(InputStream is) throws IOException { - init(is); - } - - private void initialize() throws IOException { - if(iszip) { - InputStream is = url.openStream(); - init(is); - is.close(); - } - } - - private byte[] readFully(InputStream is) throws IOException { - byte[] buf = new byte[1024]; - int num = 0; - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - - while( (num = is.read(buf)) != -1) { - bout.write(buf, 0, num); - } - - return bout.toByteArray(); - } - - private void init(InputStream is) throws IOException { - ZipInputStream zstream = new ZipInputStream(is); - ZipEntry entry; - - while( (entry = zstream.getNextEntry()) != null) { - byte[] entryData = readFully(zstream); - if(cache) - resourceCache.put(entry.getName(), entryData); - zstream.closeEntry(); - } - - zstream.close(); - } - - /** - * Returns an Enumeration of all "known" resource names. - */ - public Enumeration getResourceNames() { - return resourceCache.keys(); - } - - /** - * Returns an array of bytes read for this resource if the - * resource exists. This method blocks until the resource - * has been fully read. If the resource does not exist, - * this method returns null. - */ - public byte[] getResource(String resource) { - // lookup the data in the cache... - byte[] data = (byte[]) resourceCache.get(resource); - if(data != null) { - return data; - } - - // if the data was to come from a zip file that we - // already read fully & cached , then it is probably - // not there. - if(iszip) { - return null; - } - - // Now the only choice left is to make a url connection. - try { - URL realURL = new URL(url.getProtocol(), url.getHost(), - url.getFile() + resource); - data = readFully(realURL.openStream()); - // add it to cache if needed... - if(cache) - resourceCache.put(resource, data); - return data; - } catch(Exception e) { - return null; - } - } - - public void close() { - resourceCache.clear(); - resourceCache = null; - } - - public String toString() { - return url.toString(); - } +public class URLResourceReader { + private Hashtable resourceCache = new Hashtable(); + private boolean iszip = true; + private URL url = null; + private boolean cache = true; + + /** + * Creates a new URLResourceReader object. You can either give the URL of the + * zip/jar file or a base url where to look for additional resources. If the url + * ends with "/" then it is assumed to be a Base URL. + * + * @param The base url to look for the resources. + * @param If the base url is not a zip/jar, then true indicates that entries + * should be cached, false otherwise. + */ + public URLResourceReader(URL baseurl, boolean cache) throws IOException { + this.url = baseurl; + this.cache = cache; + this.iszip = !url.getFile().endsWith("/"); + if (this.iszip) + this.cache = true; + initialize(); + } + + /** + * equivalent to URLResourceReader(baseurl, false) + */ + public URLResourceReader(URL baseurl) throws IOException { + this(baseurl, false); + } + + /** + * Creates a new URLResourceReader object with the given input stream. The + * stream is assumed to be a zip/jar stream. + */ + public URLResourceReader(InputStream is) throws IOException { + init(is); + } + + private void initialize() throws IOException { + if (iszip) { + InputStream is = url.openStream(); + init(is); + is.close(); + } + } + + private byte[] readFully(InputStream is) throws IOException { + byte[] buf = new byte[1024]; + int num = 0; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + while ((num = is.read(buf)) != -1) { + bout.write(buf, 0, num); + } + + return bout.toByteArray(); + } + + private void init(InputStream is) throws IOException { + ZipInputStream zstream = new ZipInputStream(is); + ZipEntry entry; + + while ((entry = zstream.getNextEntry()) != null) { + byte[] entryData = readFully(zstream); + if (cache) + resourceCache.put(entry.getName(), entryData); + zstream.closeEntry(); + } + + zstream.close(); + } + + /** + * Returns an Enumeration of all "known" resource names. + */ + public Enumeration getResourceNames() { + return resourceCache.keys(); + } + + /** + * Returns an array of bytes read for this resource if the resource exists. This + * method blocks until the resource has been fully read. If the resource does + * not exist, this method returns null. + */ + public byte[] getResource(String resource) { + // lookup the data in the cache... + byte[] data = (byte[]) resourceCache.get(resource); + if (data != null) { + return data; + } + + // if the data was to come from a zip file that we + // already read fully & cached , then it is probably + // not there. + if (iszip) { + return null; + } + + // Now the only choice left is to make a url connection. + try { + URL realURL = new URL(url.getProtocol(), url.getHost(), url.getFile() + resource); + data = readFully(realURL.openStream()); + // add it to cache if needed... + if (cache) + resourceCache.put(resource, data); + return data; + } catch (Exception e) { + return null; + } + } + + public void close() { + resourceCache.clear(); + resourceCache = null; + } + + public String toString() { + return url.toString(); + } } - - - - - - - - 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 index d6bc797..7f28e06 100644 --- 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 @@ -17,6 +17,7 @@ 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; @@ -25,694 +26,562 @@ 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; - } - } + * 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(); - } - + * 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 ) - { + * 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" ) ) - { + 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; - } + 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. + * $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.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.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.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.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. + * Revision 1.8 2000/12/20 16:25:48 michael Added log to all files. * * */ - diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java index e2210d0..21b47b3 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/WotonomyException.java @@ -22,113 +22,96 @@ import java.io.PrintStream; import java.io.PrintWriter; /** -* This is a simple RuntimeException that can encapsulate -* another throwable. Behaves as a normal RuntimeException -* except that it prints a stack trace of the wrapped -* throwable if one is specified. -* -* Intended to be used anytime you'd -* report an exception with System.out.println: that is, -* for debugging and non-user-visible exceptions. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ + * This is a simple RuntimeException that can encapsulate another throwable. + * Behaves as a normal RuntimeException except that it prints a stack trace of + * the wrapped throwable if one is specified. + * + * Intended to be used anytime you'd report an exception with + * System.out.println: that is, for debugging and non-user-visible exceptions. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ -public class WotonomyException extends RuntimeException -{ +public class WotonomyException extends RuntimeException { protected String message; protected Throwable wrappedThrowable; - + /** - * Default constructor. - */ - public WotonomyException() - { + * Default constructor. + */ + public WotonomyException() { super(); message = null; - wrappedThrowable = null; + wrappedThrowable = null; } - + /** - * Standard constructor with message. - */ - public WotonomyException( String aMessage ) - { - super( aMessage ); + * Standard constructor with message. + */ + public WotonomyException(String aMessage) { + super(aMessage); message = aMessage; wrappedThrowable = null; } - + /** - * Specifies a throwable to wrap. - */ - public WotonomyException( Throwable aThrowable ) - { + * Specifies a throwable to wrap. + */ + public WotonomyException(Throwable aThrowable) { super(); message = null; wrappedThrowable = aThrowable; } - + /** - * Specifies a message and a throwable to wrap. - */ - public WotonomyException( String aMessage, Throwable aThrowable ) - { - super( aMessage ); + * Specifies a message and a throwable to wrap. + */ + public WotonomyException(String aMessage, Throwable aThrowable) { + super(aMessage); message = aMessage; wrappedThrowable = aThrowable; } - - /** - * Returns the wrapped throwable. - */ - public Throwable getWrappedThrowable() - { - return wrappedThrowable; - } - - public void printStackTrace(PrintWriter s) - { - if ( message != null ) - { - s.println( "Exception: " + message ); + + /** + * Returns the wrapped throwable. + */ + public Throwable getWrappedThrowable() { + return wrappedThrowable; + } + + public void printStackTrace(PrintWriter s) { + if (message != null) { + s.println("Exception: " + message); } - if ( wrappedThrowable != null ) - { - wrappedThrowable.printStackTrace( s ); + if (wrappedThrowable != null) { + wrappedThrowable.printStackTrace(s); return; } - super.printStackTrace( s ); + super.printStackTrace(s); } - - public void printStackTrace(PrintStream s) - { - if ( message != null ) - { - s.println( "Exception: " + message ); + + public void printStackTrace(PrintStream s) { + if (message != null) { + s.println("Exception: " + message); } - if ( wrappedThrowable != null ) - { - wrappedThrowable.printStackTrace( s ); + if (wrappedThrowable != null) { + wrappedThrowable.printStackTrace(s); return; } - super.printStackTrace( s ); + super.printStackTrace(s); } - - public void printStackTrace() - { - if ( message != null ) - { - System.err.println( "Exception: " + message ); + + public void printStackTrace() { + if (message != null) { + System.err.println("Exception: " + message); } - if ( wrappedThrowable != null ) - { - wrappedThrowable.printStackTrace(); + if (wrappedThrowable != null) { + wrappedThrowable.printStackTrace(); return; } super.printStackTrace(); } - + } |
