- /*
- * @(#)ORBUtility.java 1.32 02/08/13
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package com.sun.corba.se.impl.orbutil;
-
- import java.security.PrivilegedAction;
- import java.security.AccessController;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Map;
- import java.util.List;
- import java.util.ListIterator;
- import java.util.Set;
- import java.util.Map.Entry;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Hashtable;
- import java.util.Iterator;
- import java.util.Enumeration;
- import java.util.Properties;
- import java.util.IdentityHashMap;
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.math.BigInteger ;
- import java.math.BigDecimal ;
-
- public final class ObjectUtility {
- private boolean useToString ;
- private boolean isIndenting ;
- private int initialLevel ;
- private int increment ;
- private ClassMap classToPrinter = new ClassMap() ;
-
- private static ObjectUtility standard = new ObjectUtility( false, true,
- 0, 4 ) ;
- private static ObjectUtility compact = new ObjectUtility( true, false,
- 0, 4 ) ;
-
- private ObjectUtility( boolean useToString, boolean isIndenting,
- int initialLevel, int increment )
- {
- this.useToString = useToString ;
- this.isIndenting = isIndenting ;
- this.initialLevel = initialLevel ;
- this.increment = increment ;
- classToPrinter.put( Properties.class, propertiesPrinter ) ;
- classToPrinter.put( Collection.class, collectionPrinter ) ;
- classToPrinter.put( Map.class, mapPrinter ) ;
- }
-
- /** Construct an Utility instance with the desired objectToString
- * behavior.
- */
- public static ObjectUtility make( boolean useToString, boolean isIndenting,
- int initialLevel, int increment )
- {
- return new ObjectUtility( useToString, isIndenting, initialLevel,
- increment ) ;
- }
-
- /** Construct an Utility instance with the desired objectToString
- * behavior.
- */
- public static ObjectUtility make( boolean useToString, boolean isIndenting )
- {
- return new ObjectUtility( useToString, isIndenting, 0, 4 ) ;
- }
-
- /** Get the standard Utility object that supports objectToString with
- * indented display and no use of toString() methods.
- */
- public static ObjectUtility make()
- {
- return standard ;
- }
-
- /** A convenience method that gives the default behavior: use indenting
- * to display the object's structure and do not use built-in toString
- * methods.
- */
- public static String defaultObjectToString( java.lang.Object object )
- {
- return standard.objectToString( object ) ;
- }
-
- public static String compactObjectToString( java.lang.Object object )
- {
- return compact.objectToString( object ) ;
- }
-
- /** objectToString handles display of arbitrary objects. It correctly
- * handles objects whose elements form an arbitrary graph. It uses
- * reflection to display the contents of any kind of object.
- * An object's toString() method may optionally be used, but the default
- * is to ignore all toString() methods except for those defined for
- * primitive types, primitive type wrappers, and strings.
- */
- public String objectToString(java.lang.Object obj)
- {
- IdentityHashMap printed = new IdentityHashMap() ;
- ObjectWriter result = ObjectWriter.make( isIndenting, initialLevel,
- increment ) ;
- objectToStringHelper( printed, result, obj ) ;
- return result.toString() ;
- }
-
- // Perform a deep structural equality comparison of the two objects.
- // This handles all arrays, maps, and sets specially, otherwise
- // it just calls the object's equals() method.
- public static boolean equals( java.lang.Object obj1, java.lang.Object obj2 )
- {
- // Set of pairs of objects that have been (or are being) considered for
- // equality. Such pairs are presumed to be equals. If they are not,
- // this will be detected eventually and the equals method will return
- // false.
- Set considered = new HashSet() ;
-
- // Map that gives the corresponding component of obj2 for a component
- // of obj1. This is used to check for the same aliasing and use of
- // equal objects in both objects.
- Map counterpart = new IdentityHashMap() ;
-
- return equalsHelper( counterpart, considered, obj1, obj2 ) ;
- }
-
- /** If arr1 and arr2 are both arrays of the same component type,
- * return an array of that component type that consists of the
- * elements of arr1 followed by the elements of arr2.
- * Throws IllegalArgumentException otherwise.
- */
- public static Object concatenateArrays( Object arr1, Object arr2 )
- {
- Class comp1 = arr1.getClass().getComponentType() ;
- Class comp2 = arr2.getClass().getComponentType() ;
- int len1 = Array.getLength( arr1 ) ;
- int len2 = Array.getLength( arr2 ) ;
-
- if ((comp1 == null) || (comp2 == null))
- throw new IllegalStateException( "Arguments must be arrays" ) ;
- if (!comp1.equals( comp2 ))
- throw new IllegalStateException(
- "Arguments must be arrays with the same component type" ) ;
-
- Object result = Array.newInstance( comp1, len1 + len2 ) ;
-
- int index = 0 ;
-
- for (int ctr=0; ctr<len1; ctr++)
- Array.set( result, index++, Array.get( arr1, ctr ) ) ;
-
- for (int ctr=0; ctr<len2; ctr++)
- Array.set( result, index++, Array.get( arr2, ctr ) ) ;
-
- return result ;
- }
-
- //===========================================================================
- // Implementation
- //===========================================================================
-
- private void objectToStringHelper( IdentityHashMap printed,
- ObjectWriter result, java.lang.Object obj)
- {
- if (obj==null) {
- result.append( "null" ) ;
- result.endElement() ;
- } else {
- Class cls = obj.getClass() ;
- result.startObject( obj ) ;
-
- if (printed.keySet().contains( obj )) {
- result.endObject( "*VISITED*" ) ;
- } else {
- printed.put( obj, null ) ;
-
- if (mustUseToString(cls)) {
- result.endObject( obj.toString() ) ;
- } else {
- // First, handle any classes that have special printer
- // methods defined. This is useful when the class
- // overrides toString with something that
- // is not sufficiently detailed.
- ObjectPrinter printer = (ObjectPrinter)(classToPrinter.get(
- cls )) ;
- if (printer != null) {
- printer.print( printed, result, obj ) ;
- result.endObject() ;
- } else {
- Class compClass = cls.getComponentType() ;
-
- if (compClass == null)
- // handleObject always calls endObject
- handleObject( printed, result, obj ) ;
- else {
- handleArray( printed, result, obj ) ;
- result.endObject() ;
- }
- }
- }
- }
- }
- }
-
- private static interface ObjectPrinter {
- void print( IdentityHashMap printed, ObjectWriter buff,
- java.lang.Object obj ) ;
- }
-
- private ObjectPrinter propertiesPrinter = new ObjectPrinter() {
- public void print( IdentityHashMap printed, ObjectWriter buff,
- java.lang.Object obj )
- {
- if (!(obj instanceof Properties))
- throw new Error() ;
-
- Properties props = (Properties)obj ;
- Enumeration keys = props.propertyNames() ;
- while (keys.hasMoreElements()) {
- String key = (String)(keys.nextElement()) ;
- String value = props.getProperty( key ) ;
- buff.startElement() ;
- buff.append( key ) ;
- buff.append( "=" ) ;
- buff.append( value ) ;
- buff.endElement() ;
- }
- }
- } ;
-
- private ObjectPrinter collectionPrinter = new ObjectPrinter() {
- public void print( IdentityHashMap printed, ObjectWriter buff,
- java.lang.Object obj )
- {
- if (!(obj instanceof Collection))
- throw new Error() ;
-
- Collection coll = (Collection)obj ;
- Iterator iter = coll.iterator() ;
- while (iter.hasNext()) {
- java.lang.Object element = iter.next() ;
- buff.startElement() ;
- objectToStringHelper( printed, buff, element ) ;
- buff.endElement() ;
- }
- }
- } ;
-
- private ObjectPrinter mapPrinter = new ObjectPrinter() {
- public void print( IdentityHashMap printed, ObjectWriter buff,
- java.lang.Object obj )
- {
- if (!(obj instanceof Map))
- throw new Error() ;
-
- Map map = (Map)obj ;
- Iterator iter = map.entrySet().iterator() ;
- while (iter.hasNext()) {
- Entry entry = (Entry)(iter.next()) ;
- buff.startElement() ;
- objectToStringHelper( printed, buff, entry.getKey() ) ;
- buff.append( "=>" ) ;
- objectToStringHelper( printed, buff, entry.getValue() ) ;
- buff.endElement() ;
- }
- }
- } ;
-
- private static class ClassMap {
- ArrayList data ;
-
- public ClassMap()
- {
- data = new ArrayList() ;
- }
-
- /** Return the first element of the ClassMap that is assignable to cls.
- * The order is determined by the order in which the put method was
- * called. Returns null if there is no match.
- */
- public java.lang.Object get( Class cls )
- {
- Iterator iter = data.iterator() ;
- while (iter.hasNext()) {
- java.lang.Object[] arr = (java.lang.Object[])(iter.next()) ;
- Class key = (Class)(arr[0]) ;
- if (key.isAssignableFrom( cls ))
- return arr[1] ;
- }
-
- return null ;
- }
-
- /** Add obj to the map with key cls. Note that order matters,
- * as the first match is returned.
- */
- public void put( Class cls, java.lang.Object obj )
- {
- java.lang.Object[] pair = { cls, obj } ;
- data.add( pair ) ;
- }
- }
-
- private boolean mustUseToString( Class cls )
- {
- // These probably never occur
- if (cls.isPrimitive())
- return true ;
-
- // We must use toString for all primitive wrappers, since
- // otherwise the code recurses endlessly (access value field
- // inside Integer, returns another Integer through reflection).
- if ((cls == Integer.class) ||
- (cls == BigInteger.class) ||
- (cls == BigDecimal.class) ||
- (cls == String.class) ||
- (cls == StringBuffer.class) ||
- (cls == Long.class) ||
- (cls == Short.class) ||
- (cls == Byte.class) ||
- (cls == Character.class) ||
- (cls == Float.class) ||
- (cls == Double.class) ||
- (cls == Boolean.class))
- return true ;
-
- if (useToString) {
- try {
- cls.getDeclaredMethod( "toString", null ) ;
- return true ;
- } catch (Exception exc) {
- return false ;
- }
- }
-
- return false ;
- }
-
- private void handleObject( IdentityHashMap printed, ObjectWriter result,
- java.lang.Object obj )
- {
- Class cls = obj.getClass() ;
-
- try {
- Field[] fields = cls.getDeclaredFields() ;
-
- for (int ctr=0; ctr<fields.length; ctr++ ) {
- final Field fld = fields[ctr] ;
- int modifiers = fld.getModifiers() ;
-
- // Do not display field if it is static, since these fields
- // are always the same for every instances. This could
- // be made configurable, but I don't think it is
- // useful to do so.
- if (!Modifier.isStatic( modifiers )) {
- result.startElement() ;
- result.append( fld.getName() ) ;
- result.append( ":" ) ;
-
- try {
- // Make sure that we can read the field if it is
- // not public
- AccessController.doPrivileged( new PrivilegedAction() {
- public Object run() {
- fld.setAccessible( true ) ;
- return null ;
- }
- } ) ;
-
- java.lang.Object value = fld.get( obj ) ;
- objectToStringHelper( printed, result, value ) ;
- } catch (Exception exc2) {
- result.append( "???" ) ;
- }
-
- result.endElement() ;
- }
- }
-
- result.endObject() ;
- } catch (Exception exc2) {
- result.endObject( obj.toString() ) ;
- }
- }
-
- private void handleArray( IdentityHashMap printed, ObjectWriter result,
- java.lang.Object obj )
- {
- Class compClass = obj.getClass().getComponentType() ;
- if (compClass == boolean.class) {
- boolean[] arr = (boolean[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else if (compClass == byte.class) {
- byte[] arr = (byte[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else if (compClass == short.class) {
- short[] arr = (short[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else if (compClass == int.class) {
- int[] arr = (int[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else if (compClass == long.class) {
- long[] arr = (long[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else if (compClass == char.class) {
- char[] arr = (char[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else if (compClass == float.class) {
- float[] arr = (float[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else if (compClass == double.class) {
- double[] arr = (double[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- result.append( arr[ctr] ) ;
- result.endElement() ;
- }
- } else { // array of object
- java.lang.Object[] arr = (java.lang.Object[])obj ;
- for (int ctr=0; ctr<arr.length; ctr++) {
- result.startElement() ;
- objectToStringHelper( printed, result, arr[ctr] ) ;
- result.endElement() ;
- }
- }
- }
-
- private static class Pair
- {
- private java.lang.Object obj1 ;
- private java.lang.Object obj2 ;
-
- Pair( java.lang.Object obj1, java.lang.Object obj2 )
- {
- this.obj1 = obj1 ;
- this.obj2 = obj2 ;
- }
-
- public boolean equals( java.lang.Object obj )
- {
- if (!(obj instanceof Pair))
- return false ;
-
- Pair other = (Pair)obj ;
- return other.obj1 == obj1 && other.obj2 == obj2 ;
- }
-
- public int hashCode()
- {
- return System.identityHashCode( obj1 ) ^
- System.identityHashCode( obj2 ) ;
- }
- }
-
- private static boolean equalsHelper( Map counterpart, Set considered,
- java.lang.Object obj1, java.lang.Object obj2 )
- {
- if ((obj1 == null) || (obj2 == null))
- return obj1 == obj2 ;
-
- java.lang.Object other2 = counterpart.get( obj1 ) ;
- if (other2 == null) {
- other2 = obj2 ;
- counterpart.put( obj1, other2 ) ;
- }
-
- if (obj1 == other2)
- return true ;
-
- if (obj2 != other2)
- return false ;
-
- Pair pair = new Pair( obj1, obj2 ) ;
- if (considered.contains( pair ))
- return true ;
- else
- considered.add( pair ) ;
-
- if (obj1 instanceof java.lang.Object[] &&
- obj2 instanceof java.lang.Object[])
- return equalArrays( counterpart, considered,
- (java.lang.Object[])obj1, (java.lang.Object[])obj2 ) ;
- else if (obj1 instanceof Map && obj2 instanceof Map)
- return equalMaps( counterpart, considered,
- (Map)obj1, (Map)obj2 ) ;
- else if (obj1 instanceof Set && obj2 instanceof Set)
- return equalSets( counterpart, considered,
- (Set)obj1, (Set)obj2 ) ;
- else if (obj1 instanceof List && obj2 instanceof List)
- return equalLists( counterpart, considered,
- (List)obj1, (List)obj2 ) ;
- else if (obj1 instanceof boolean[] && obj2 instanceof boolean[])
- return Arrays.equals( (boolean[])obj1, (boolean[])obj2 ) ;
- else if (obj1 instanceof byte[] && obj2 instanceof byte[])
- return Arrays.equals( (byte[])obj1, (byte[])obj2 ) ;
- else if (obj1 instanceof char[] && obj2 instanceof char[])
- return Arrays.equals( (char[])obj1, (char[])obj2 ) ;
- else if (obj1 instanceof double[] && obj2 instanceof double[])
- return Arrays.equals( (double[])obj1, (double[])obj2 ) ;
- else if (obj1 instanceof float[] && obj2 instanceof float[])
- return Arrays.equals( (float[])obj1, (float[])obj2 ) ;
- else if (obj1 instanceof int[] && obj2 instanceof int[])
- return Arrays.equals( (int[])obj1, (int[])obj2 ) ;
- else if (obj1 instanceof long[] && obj2 instanceof long[])
- return Arrays.equals( (long[])obj1, (long[])obj2 ) ;
- else {
- Class cls = obj1.getClass() ;
- if (cls != obj2.getClass())
- return obj1.equals( obj2 ) ;
- else
- return equalsObject( counterpart, considered, cls, obj1, obj2 ) ;
- }
- }
-
- private static boolean equalsObject( Map counterpart, Set considered,
- Class cls, java.lang.Object obj1, java.lang.Object obj2 )
- {
- Class objectClass = java.lang.Object.class ;
- if (cls == objectClass)
- return true ;
-
- Class[] equalsTypes = { objectClass } ;
- try {
- Method equalsMethod = cls.getDeclaredMethod( "equals",
- equalsTypes ) ;
- return obj1.equals( obj2 ) ;
- } catch (Exception exc) {
- if (equalsObjectFields( counterpart, considered,
- cls, obj1, obj2 ))
- return equalsObject( counterpart, considered,
- cls.getSuperclass(), obj1, obj2 ) ;
- else
- return false ;
- }
- }
-
- private static boolean equalsObjectFields( Map counterpart, Set considered,
- Class cls, java.lang.Object obj1, java.lang.Object obj2 )
- {
- Field[] fields = cls.getDeclaredFields() ;
- for (int ctr=0; ctr<fields.length; ctr++) {
- try {
- final Field field = fields[ctr] ;
- // Ignore static fields
- if (!Modifier.isStatic( field.getModifiers())) {
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- field.setAccessible( true ) ;
- return null ;
- }
- } ) ;
-
- java.lang.Object value1 = field.get( obj1 ) ;
- java.lang.Object value2 = field.get( obj2 ) ;
- if (!equalsHelper( counterpart, considered, value1,
- value2 ))
- return false ;
- }
- } catch (IllegalAccessException exc) {
- return false ;
- }
- }
-
- return true ;
- }
-
- private static boolean equalArrays( Map counterpart, Set considered,
- java.lang.Object[] arr1, java.lang.Object[] arr2 )
- {
- int len = arr1.length ;
- if (len != arr2.length)
- return false ;
-
- for (int ctr = 0; ctr<len; ctr++ )
- if (!equalsHelper( counterpart, considered, arr1[ctr], arr2[ctr] ))
- return false ;
-
- return true ;
- }
-
- private static boolean equalMaps( Map counterpart, Set considered,
- Map map1, Map map2 )
- {
- if (map2.size() != map1.size())
- return false;
-
- try {
- Iterator i = map1.entrySet().iterator();
- while (i.hasNext()) {
- Entry e = (Entry) i.next();
- java.lang.Object key = e.getKey();
- java.lang.Object value = e.getValue();
- if (value == null) {
- if (!(map2.get(key)==null && map2.containsKey(key)))
- return false;
- } else {
- if (!equalsHelper( counterpart, considered,
- value, map2.get(key)))
- return false;
- }
- }
- } catch(ClassCastException unused) {
- return false;
- } catch(NullPointerException unused) {
- return false;
- }
-
- return true;
- }
-
- // Obviously this is an inefficient quadratic algorithm.
- // This is taken pretty directly from AbstractSet and AbstractCollection
- // in the JDK.
- // For HashSet, an O(n) (with a good hash function) algorithm
- // is possible, and likewise TreeSet, since it is
- // ordered, is O(n). But this is not worth the effort here.
- // Note that the inner loop uses equals, not equalsHelper.
- // This is needed because of the searching behavior of this test.
- // However, note that this will NOT correctly handle sets that
- // contain themselves as members, or that have members that reference
- // themselves. These cases will cause infinite regress!
- private static boolean equalSets( Map counterpart, Set considered,
- Set set1, Set set2 )
- {
- if (set1.size() != set2.size())
- return false ;
-
- Iterator e1 = set1.iterator() ;
- while (e1.hasNext()) {
- java.lang.Object obj1 = e1.next() ;
-
- boolean found = false ;
- Iterator e2 = set2.iterator() ;
- while (e2.hasNext() && !found) {
- java.lang.Object obj2 = e2.next() ;
- found = equals( obj1, obj2 ) ;
- }
-
- if (!found)
- return false ;
- }
-
- return true ;
- }
-
- private static boolean equalLists( Map counterpart, Set considered,
- List list1, List list2 )
- {
- ListIterator e1 = list1.listIterator();
- ListIterator e2 = list2.listIterator();
- while(e1.hasNext() && e2.hasNext()) {
- java.lang.Object o1 = e1.next();
- java.lang.Object o2 = e2.next();
- if (!(o1==null ? o2==null : equalsHelper(
- counterpart, considered, o1, o2)))
- return false;
- }
- return !(e1.hasNext() || e2.hasNext());
- }
- }