1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/corba/se/impl/orbutil/ObjectUtility.java Sat Dec 01 00:00:00 2007 +0000 1.3 @@ -0,0 +1,722 @@ 1.4 +/* 1.5 + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Sun designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Sun in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.25 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.26 + * have any questions. 1.27 + */ 1.28 + 1.29 +package com.sun.corba.se.impl.orbutil; 1.30 + 1.31 +import java.security.PrivilegedAction; 1.32 +import java.security.AccessController; 1.33 +import java.util.ArrayList; 1.34 +import java.util.Arrays; 1.35 +import java.util.Map; 1.36 +import java.util.List; 1.37 +import java.util.ListIterator; 1.38 +import java.util.Set; 1.39 +import java.util.Map.Entry; 1.40 +import java.util.Collection; 1.41 +import java.util.HashMap; 1.42 +import java.util.HashSet; 1.43 +import java.util.Hashtable; 1.44 +import java.util.Iterator; 1.45 +import java.util.Enumeration; 1.46 +import java.util.Properties; 1.47 +import java.util.IdentityHashMap; 1.48 +import java.lang.reflect.Array; 1.49 +import java.lang.reflect.Field; 1.50 +import java.lang.reflect.Method; 1.51 +import java.lang.reflect.Modifier; 1.52 +import java.math.BigInteger ; 1.53 +import java.math.BigDecimal ; 1.54 + 1.55 +public final class ObjectUtility { 1.56 + private boolean useToString ; 1.57 + private boolean isIndenting ; 1.58 + private int initialLevel ; 1.59 + private int increment ; 1.60 + private ClassMap classToPrinter = new ClassMap() ; 1.61 + 1.62 + private static ObjectUtility standard = new ObjectUtility( false, true, 1.63 + 0, 4 ) ; 1.64 + private static ObjectUtility compact = new ObjectUtility( true, false, 1.65 + 0, 4 ) ; 1.66 + 1.67 + private ObjectUtility( boolean useToString, boolean isIndenting, 1.68 + int initialLevel, int increment ) 1.69 + { 1.70 + this.useToString = useToString ; 1.71 + this.isIndenting = isIndenting ; 1.72 + this.initialLevel = initialLevel ; 1.73 + this.increment = increment ; 1.74 + classToPrinter.put( Properties.class, propertiesPrinter ) ; 1.75 + classToPrinter.put( Collection.class, collectionPrinter ) ; 1.76 + classToPrinter.put( Map.class, mapPrinter ) ; 1.77 + } 1.78 + 1.79 + /** Construct an Utility instance with the desired objectToString 1.80 + * behavior. 1.81 + */ 1.82 + public static ObjectUtility make( boolean useToString, boolean isIndenting, 1.83 + int initialLevel, int increment ) 1.84 + { 1.85 + return new ObjectUtility( useToString, isIndenting, initialLevel, 1.86 + increment ) ; 1.87 + } 1.88 + 1.89 + /** Construct an Utility instance with the desired objectToString 1.90 + * behavior. 1.91 + */ 1.92 + public static ObjectUtility make( boolean useToString, boolean isIndenting ) 1.93 + { 1.94 + return new ObjectUtility( useToString, isIndenting, 0, 4 ) ; 1.95 + } 1.96 + 1.97 + /** Get the standard Utility object that supports objectToString with 1.98 + * indented display and no use of toString() methods. 1.99 + */ 1.100 + public static ObjectUtility make() 1.101 + { 1.102 + return standard ; 1.103 + } 1.104 + 1.105 + /** A convenience method that gives the default behavior: use indenting 1.106 + * to display the object's structure and do not use built-in toString 1.107 + * methods. 1.108 + */ 1.109 + public static String defaultObjectToString( java.lang.Object object ) 1.110 + { 1.111 + return standard.objectToString( object ) ; 1.112 + } 1.113 + 1.114 + public static String compactObjectToString( java.lang.Object object ) 1.115 + { 1.116 + return compact.objectToString( object ) ; 1.117 + } 1.118 + 1.119 + /** objectToString handles display of arbitrary objects. It correctly 1.120 + * handles objects whose elements form an arbitrary graph. It uses 1.121 + * reflection to display the contents of any kind of object. 1.122 + * An object's toString() method may optionally be used, but the default 1.123 + * is to ignore all toString() methods except for those defined for 1.124 + * primitive types, primitive type wrappers, and strings. 1.125 + */ 1.126 + public String objectToString(java.lang.Object obj) 1.127 + { 1.128 + IdentityHashMap printed = new IdentityHashMap() ; 1.129 + ObjectWriter result = ObjectWriter.make( isIndenting, initialLevel, 1.130 + increment ) ; 1.131 + objectToStringHelper( printed, result, obj ) ; 1.132 + return result.toString() ; 1.133 + } 1.134 + 1.135 + // Perform a deep structural equality comparison of the two objects. 1.136 + // This handles all arrays, maps, and sets specially, otherwise 1.137 + // it just calls the object's equals() method. 1.138 + public static boolean equals( java.lang.Object obj1, java.lang.Object obj2 ) 1.139 + { 1.140 + // Set of pairs of objects that have been (or are being) considered for 1.141 + // equality. Such pairs are presumed to be equals. If they are not, 1.142 + // this will be detected eventually and the equals method will return 1.143 + // false. 1.144 + Set considered = new HashSet() ; 1.145 + 1.146 + // Map that gives the corresponding component of obj2 for a component 1.147 + // of obj1. This is used to check for the same aliasing and use of 1.148 + // equal objects in both objects. 1.149 + Map counterpart = new IdentityHashMap() ; 1.150 + 1.151 + return equalsHelper( counterpart, considered, obj1, obj2 ) ; 1.152 + } 1.153 + 1.154 + /** If arr1 and arr2 are both arrays of the same component type, 1.155 + * return an array of that component type that consists of the 1.156 + * elements of arr1 followed by the elements of arr2. 1.157 + * Throws IllegalArgumentException otherwise. 1.158 + */ 1.159 + public static Object concatenateArrays( Object arr1, Object arr2 ) 1.160 + { 1.161 + Class comp1 = arr1.getClass().getComponentType() ; 1.162 + Class comp2 = arr2.getClass().getComponentType() ; 1.163 + int len1 = Array.getLength( arr1 ) ; 1.164 + int len2 = Array.getLength( arr2 ) ; 1.165 + 1.166 + if ((comp1 == null) || (comp2 == null)) 1.167 + throw new IllegalStateException( "Arguments must be arrays" ) ; 1.168 + if (!comp1.equals( comp2 )) 1.169 + throw new IllegalStateException( 1.170 + "Arguments must be arrays with the same component type" ) ; 1.171 + 1.172 + Object result = Array.newInstance( comp1, len1 + len2 ) ; 1.173 + 1.174 + int index = 0 ; 1.175 + 1.176 + for (int ctr=0; ctr<len1; ctr++) 1.177 + Array.set( result, index++, Array.get( arr1, ctr ) ) ; 1.178 + 1.179 + for (int ctr=0; ctr<len2; ctr++) 1.180 + Array.set( result, index++, Array.get( arr2, ctr ) ) ; 1.181 + 1.182 + return result ; 1.183 + } 1.184 + 1.185 +//=========================================================================== 1.186 +// Implementation 1.187 +//=========================================================================== 1.188 + 1.189 + private void objectToStringHelper( IdentityHashMap printed, 1.190 + ObjectWriter result, java.lang.Object obj) 1.191 + { 1.192 + if (obj==null) { 1.193 + result.append( "null" ) ; 1.194 + result.endElement() ; 1.195 + } else { 1.196 + Class cls = obj.getClass() ; 1.197 + result.startObject( obj ) ; 1.198 + 1.199 + if (printed.keySet().contains( obj )) { 1.200 + result.endObject( "*VISITED*" ) ; 1.201 + } else { 1.202 + printed.put( obj, null ) ; 1.203 + 1.204 + if (mustUseToString(cls)) { 1.205 + result.endObject( obj.toString() ) ; 1.206 + } else { 1.207 + // First, handle any classes that have special printer 1.208 + // methods defined. This is useful when the class 1.209 + // overrides toString with something that 1.210 + // is not sufficiently detailed. 1.211 + ObjectPrinter printer = (ObjectPrinter)(classToPrinter.get( 1.212 + cls )) ; 1.213 + if (printer != null) { 1.214 + printer.print( printed, result, obj ) ; 1.215 + result.endObject() ; 1.216 + } else { 1.217 + Class compClass = cls.getComponentType() ; 1.218 + 1.219 + if (compClass == null) 1.220 + // handleObject always calls endObject 1.221 + handleObject( printed, result, obj ) ; 1.222 + else { 1.223 + handleArray( printed, result, obj ) ; 1.224 + result.endObject() ; 1.225 + } 1.226 + } 1.227 + } 1.228 + } 1.229 + } 1.230 + } 1.231 + 1.232 + private static interface ObjectPrinter { 1.233 + void print( IdentityHashMap printed, ObjectWriter buff, 1.234 + java.lang.Object obj ) ; 1.235 + } 1.236 + 1.237 + private ObjectPrinter propertiesPrinter = new ObjectPrinter() { 1.238 + public void print( IdentityHashMap printed, ObjectWriter buff, 1.239 + java.lang.Object obj ) 1.240 + { 1.241 + if (!(obj instanceof Properties)) 1.242 + throw new Error() ; 1.243 + 1.244 + Properties props = (Properties)obj ; 1.245 + Enumeration keys = props.propertyNames() ; 1.246 + while (keys.hasMoreElements()) { 1.247 + String key = (String)(keys.nextElement()) ; 1.248 + String value = props.getProperty( key ) ; 1.249 + buff.startElement() ; 1.250 + buff.append( key ) ; 1.251 + buff.append( "=" ) ; 1.252 + buff.append( value ) ; 1.253 + buff.endElement() ; 1.254 + } 1.255 + } 1.256 + } ; 1.257 + 1.258 + private ObjectPrinter collectionPrinter = new ObjectPrinter() { 1.259 + public void print( IdentityHashMap printed, ObjectWriter buff, 1.260 + java.lang.Object obj ) 1.261 + { 1.262 + if (!(obj instanceof Collection)) 1.263 + throw new Error() ; 1.264 + 1.265 + Collection coll = (Collection)obj ; 1.266 + Iterator iter = coll.iterator() ; 1.267 + while (iter.hasNext()) { 1.268 + java.lang.Object element = iter.next() ; 1.269 + buff.startElement() ; 1.270 + objectToStringHelper( printed, buff, element ) ; 1.271 + buff.endElement() ; 1.272 + } 1.273 + } 1.274 + } ; 1.275 + 1.276 + private ObjectPrinter mapPrinter = new ObjectPrinter() { 1.277 + public void print( IdentityHashMap printed, ObjectWriter buff, 1.278 + java.lang.Object obj ) 1.279 + { 1.280 + if (!(obj instanceof Map)) 1.281 + throw new Error() ; 1.282 + 1.283 + Map map = (Map)obj ; 1.284 + Iterator iter = map.entrySet().iterator() ; 1.285 + while (iter.hasNext()) { 1.286 + Entry entry = (Entry)(iter.next()) ; 1.287 + buff.startElement() ; 1.288 + objectToStringHelper( printed, buff, entry.getKey() ) ; 1.289 + buff.append( "=>" ) ; 1.290 + objectToStringHelper( printed, buff, entry.getValue() ) ; 1.291 + buff.endElement() ; 1.292 + } 1.293 + } 1.294 + } ; 1.295 + 1.296 + private static class ClassMap { 1.297 + ArrayList data ; 1.298 + 1.299 + public ClassMap() 1.300 + { 1.301 + data = new ArrayList() ; 1.302 + } 1.303 + 1.304 + /** Return the first element of the ClassMap that is assignable to cls. 1.305 + * The order is determined by the order in which the put method was 1.306 + * called. Returns null if there is no match. 1.307 + */ 1.308 + public java.lang.Object get( Class cls ) 1.309 + { 1.310 + Iterator iter = data.iterator() ; 1.311 + while (iter.hasNext()) { 1.312 + java.lang.Object[] arr = (java.lang.Object[])(iter.next()) ; 1.313 + Class key = (Class)(arr[0]) ; 1.314 + if (key.isAssignableFrom( cls )) 1.315 + return arr[1] ; 1.316 + } 1.317 + 1.318 + return null ; 1.319 + } 1.320 + 1.321 + /** Add obj to the map with key cls. Note that order matters, 1.322 + * as the first match is returned. 1.323 + */ 1.324 + public void put( Class cls, java.lang.Object obj ) 1.325 + { 1.326 + java.lang.Object[] pair = { cls, obj } ; 1.327 + data.add( pair ) ; 1.328 + } 1.329 + } 1.330 + 1.331 + private boolean mustUseToString( Class cls ) 1.332 + { 1.333 + // These probably never occur 1.334 + if (cls.isPrimitive()) 1.335 + return true ; 1.336 + 1.337 + // We must use toString for all primitive wrappers, since 1.338 + // otherwise the code recurses endlessly (access value field 1.339 + // inside Integer, returns another Integer through reflection). 1.340 + if ((cls == Integer.class) || 1.341 + (cls == BigInteger.class) || 1.342 + (cls == BigDecimal.class) || 1.343 + (cls == String.class) || 1.344 + (cls == StringBuffer.class) || 1.345 + (cls == Long.class) || 1.346 + (cls == Short.class) || 1.347 + (cls == Byte.class) || 1.348 + (cls == Character.class) || 1.349 + (cls == Float.class) || 1.350 + (cls == Double.class) || 1.351 + (cls == Boolean.class)) 1.352 + return true ; 1.353 + 1.354 + if (useToString) { 1.355 + try { 1.356 + cls.getDeclaredMethod( "toString", null ) ; 1.357 + return true ; 1.358 + } catch (Exception exc) { 1.359 + return false ; 1.360 + } 1.361 + } 1.362 + 1.363 + return false ; 1.364 + } 1.365 + 1.366 + private void handleObject( IdentityHashMap printed, ObjectWriter result, 1.367 + java.lang.Object obj ) 1.368 + { 1.369 + Class cls = obj.getClass() ; 1.370 + 1.371 + try { 1.372 + Field[] fields; 1.373 + SecurityManager security = System.getSecurityManager(); 1.374 + if (security != null && !Modifier.isPublic(cls.getModifiers())) { 1.375 + fields = new Field[0]; 1.376 + } else { 1.377 + fields = cls.getDeclaredFields(); 1.378 + } 1.379 + 1.380 + for (int ctr=0; ctr<fields.length; ctr++ ) { 1.381 + final Field fld = fields[ctr] ; 1.382 + int modifiers = fld.getModifiers() ; 1.383 + 1.384 + // Do not display field if it is static, since these fields 1.385 + // are always the same for every instances. This could 1.386 + // be made configurable, but I don't think it is 1.387 + // useful to do so. 1.388 + if (!Modifier.isStatic( modifiers )) { 1.389 + if (security != null) { 1.390 + if (!Modifier.isPublic(modifiers)) 1.391 + continue; 1.392 + } 1.393 + result.startElement() ; 1.394 + result.append( fld.getName() ) ; 1.395 + result.append( ":" ) ; 1.396 + 1.397 + try { 1.398 + // Make sure that we can read the field if it is 1.399 + // not public 1.400 + AccessController.doPrivileged( new PrivilegedAction() { 1.401 + public Object run() { 1.402 + fld.setAccessible( true ) ; 1.403 + return null ; 1.404 + } 1.405 + } ) ; 1.406 + 1.407 + java.lang.Object value = fld.get( obj ) ; 1.408 + objectToStringHelper( printed, result, value ) ; 1.409 + } catch (Exception exc2) { 1.410 + result.append( "???" ) ; 1.411 + } 1.412 + 1.413 + result.endElement() ; 1.414 + } 1.415 + } 1.416 + 1.417 + result.endObject() ; 1.418 + } catch (Exception exc2) { 1.419 + result.endObject( obj.toString() ) ; 1.420 + } 1.421 + } 1.422 + 1.423 + private void handleArray( IdentityHashMap printed, ObjectWriter result, 1.424 + java.lang.Object obj ) 1.425 + { 1.426 + Class compClass = obj.getClass().getComponentType() ; 1.427 + if (compClass == boolean.class) { 1.428 + boolean[] arr = (boolean[])obj ; 1.429 + for (int ctr=0; ctr<arr.length; ctr++) { 1.430 + result.startElement() ; 1.431 + result.append( arr[ctr] ) ; 1.432 + result.endElement() ; 1.433 + } 1.434 + } else if (compClass == byte.class) { 1.435 + byte[] arr = (byte[])obj ; 1.436 + for (int ctr=0; ctr<arr.length; ctr++) { 1.437 + result.startElement() ; 1.438 + result.append( arr[ctr] ) ; 1.439 + result.endElement() ; 1.440 + } 1.441 + } else if (compClass == short.class) { 1.442 + short[] arr = (short[])obj ; 1.443 + for (int ctr=0; ctr<arr.length; ctr++) { 1.444 + result.startElement() ; 1.445 + result.append( arr[ctr] ) ; 1.446 + result.endElement() ; 1.447 + } 1.448 + } else if (compClass == int.class) { 1.449 + int[] arr = (int[])obj ; 1.450 + for (int ctr=0; ctr<arr.length; ctr++) { 1.451 + result.startElement() ; 1.452 + result.append( arr[ctr] ) ; 1.453 + result.endElement() ; 1.454 + } 1.455 + } else if (compClass == long.class) { 1.456 + long[] arr = (long[])obj ; 1.457 + for (int ctr=0; ctr<arr.length; ctr++) { 1.458 + result.startElement() ; 1.459 + result.append( arr[ctr] ) ; 1.460 + result.endElement() ; 1.461 + } 1.462 + } else if (compClass == char.class) { 1.463 + char[] arr = (char[])obj ; 1.464 + for (int ctr=0; ctr<arr.length; ctr++) { 1.465 + result.startElement() ; 1.466 + result.append( arr[ctr] ) ; 1.467 + result.endElement() ; 1.468 + } 1.469 + } else if (compClass == float.class) { 1.470 + float[] arr = (float[])obj ; 1.471 + for (int ctr=0; ctr<arr.length; ctr++) { 1.472 + result.startElement() ; 1.473 + result.append( arr[ctr] ) ; 1.474 + result.endElement() ; 1.475 + } 1.476 + } else if (compClass == double.class) { 1.477 + double[] arr = (double[])obj ; 1.478 + for (int ctr=0; ctr<arr.length; ctr++) { 1.479 + result.startElement() ; 1.480 + result.append( arr[ctr] ) ; 1.481 + result.endElement() ; 1.482 + } 1.483 + } else { // array of object 1.484 + java.lang.Object[] arr = (java.lang.Object[])obj ; 1.485 + for (int ctr=0; ctr<arr.length; ctr++) { 1.486 + result.startElement() ; 1.487 + objectToStringHelper( printed, result, arr[ctr] ) ; 1.488 + result.endElement() ; 1.489 + } 1.490 + } 1.491 + } 1.492 + 1.493 + private static class Pair 1.494 + { 1.495 + private java.lang.Object obj1 ; 1.496 + private java.lang.Object obj2 ; 1.497 + 1.498 + Pair( java.lang.Object obj1, java.lang.Object obj2 ) 1.499 + { 1.500 + this.obj1 = obj1 ; 1.501 + this.obj2 = obj2 ; 1.502 + } 1.503 + 1.504 + public boolean equals( java.lang.Object obj ) 1.505 + { 1.506 + if (!(obj instanceof Pair)) 1.507 + return false ; 1.508 + 1.509 + Pair other = (Pair)obj ; 1.510 + return other.obj1 == obj1 && other.obj2 == obj2 ; 1.511 + } 1.512 + 1.513 + public int hashCode() 1.514 + { 1.515 + return System.identityHashCode( obj1 ) ^ 1.516 + System.identityHashCode( obj2 ) ; 1.517 + } 1.518 + } 1.519 + 1.520 + private static boolean equalsHelper( Map counterpart, Set considered, 1.521 + java.lang.Object obj1, java.lang.Object obj2 ) 1.522 + { 1.523 + if ((obj1 == null) || (obj2 == null)) 1.524 + return obj1 == obj2 ; 1.525 + 1.526 + java.lang.Object other2 = counterpart.get( obj1 ) ; 1.527 + if (other2 == null) { 1.528 + other2 = obj2 ; 1.529 + counterpart.put( obj1, other2 ) ; 1.530 + } 1.531 + 1.532 + if (obj1 == other2) 1.533 + return true ; 1.534 + 1.535 + if (obj2 != other2) 1.536 + return false ; 1.537 + 1.538 + Pair pair = new Pair( obj1, obj2 ) ; 1.539 + if (considered.contains( pair )) 1.540 + return true ; 1.541 + else 1.542 + considered.add( pair ) ; 1.543 + 1.544 + if (obj1 instanceof java.lang.Object[] && 1.545 + obj2 instanceof java.lang.Object[]) 1.546 + return equalArrays( counterpart, considered, 1.547 + (java.lang.Object[])obj1, (java.lang.Object[])obj2 ) ; 1.548 + else if (obj1 instanceof Map && obj2 instanceof Map) 1.549 + return equalMaps( counterpart, considered, 1.550 + (Map)obj1, (Map)obj2 ) ; 1.551 + else if (obj1 instanceof Set && obj2 instanceof Set) 1.552 + return equalSets( counterpart, considered, 1.553 + (Set)obj1, (Set)obj2 ) ; 1.554 + else if (obj1 instanceof List && obj2 instanceof List) 1.555 + return equalLists( counterpart, considered, 1.556 + (List)obj1, (List)obj2 ) ; 1.557 + else if (obj1 instanceof boolean[] && obj2 instanceof boolean[]) 1.558 + return Arrays.equals( (boolean[])obj1, (boolean[])obj2 ) ; 1.559 + else if (obj1 instanceof byte[] && obj2 instanceof byte[]) 1.560 + return Arrays.equals( (byte[])obj1, (byte[])obj2 ) ; 1.561 + else if (obj1 instanceof char[] && obj2 instanceof char[]) 1.562 + return Arrays.equals( (char[])obj1, (char[])obj2 ) ; 1.563 + else if (obj1 instanceof double[] && obj2 instanceof double[]) 1.564 + return Arrays.equals( (double[])obj1, (double[])obj2 ) ; 1.565 + else if (obj1 instanceof float[] && obj2 instanceof float[]) 1.566 + return Arrays.equals( (float[])obj1, (float[])obj2 ) ; 1.567 + else if (obj1 instanceof int[] && obj2 instanceof int[]) 1.568 + return Arrays.equals( (int[])obj1, (int[])obj2 ) ; 1.569 + else if (obj1 instanceof long[] && obj2 instanceof long[]) 1.570 + return Arrays.equals( (long[])obj1, (long[])obj2 ) ; 1.571 + else { 1.572 + Class cls = obj1.getClass() ; 1.573 + if (cls != obj2.getClass()) 1.574 + return obj1.equals( obj2 ) ; 1.575 + else 1.576 + return equalsObject( counterpart, considered, cls, obj1, obj2 ) ; 1.577 + } 1.578 + } 1.579 + 1.580 + private static boolean equalsObject( Map counterpart, Set considered, 1.581 + Class cls, java.lang.Object obj1, java.lang.Object obj2 ) 1.582 + { 1.583 + Class objectClass = java.lang.Object.class ; 1.584 + if (cls == objectClass) 1.585 + return true ; 1.586 + 1.587 + Class[] equalsTypes = { objectClass } ; 1.588 + try { 1.589 + Method equalsMethod = cls.getDeclaredMethod( "equals", 1.590 + equalsTypes ) ; 1.591 + return obj1.equals( obj2 ) ; 1.592 + } catch (Exception exc) { 1.593 + if (equalsObjectFields( counterpart, considered, 1.594 + cls, obj1, obj2 )) 1.595 + return equalsObject( counterpart, considered, 1.596 + cls.getSuperclass(), obj1, obj2 ) ; 1.597 + else 1.598 + return false ; 1.599 + } 1.600 + } 1.601 + 1.602 + private static boolean equalsObjectFields( Map counterpart, Set considered, 1.603 + Class cls, java.lang.Object obj1, java.lang.Object obj2 ) 1.604 + { 1.605 + Field[] fields = cls.getDeclaredFields() ; 1.606 + for (int ctr=0; ctr<fields.length; ctr++) { 1.607 + try { 1.608 + final Field field = fields[ctr] ; 1.609 + // Ignore static fields 1.610 + if (!Modifier.isStatic( field.getModifiers())) { 1.611 + AccessController.doPrivileged(new PrivilegedAction() { 1.612 + public Object run() { 1.613 + field.setAccessible( true ) ; 1.614 + return null ; 1.615 + } 1.616 + } ) ; 1.617 + 1.618 + java.lang.Object value1 = field.get( obj1 ) ; 1.619 + java.lang.Object value2 = field.get( obj2 ) ; 1.620 + if (!equalsHelper( counterpart, considered, value1, 1.621 + value2 )) 1.622 + return false ; 1.623 + } 1.624 + } catch (IllegalAccessException exc) { 1.625 + return false ; 1.626 + } 1.627 + } 1.628 + 1.629 + return true ; 1.630 + } 1.631 + 1.632 + private static boolean equalArrays( Map counterpart, Set considered, 1.633 + java.lang.Object[] arr1, java.lang.Object[] arr2 ) 1.634 + { 1.635 + int len = arr1.length ; 1.636 + if (len != arr2.length) 1.637 + return false ; 1.638 + 1.639 + for (int ctr = 0; ctr<len; ctr++ ) 1.640 + if (!equalsHelper( counterpart, considered, arr1[ctr], arr2[ctr] )) 1.641 + return false ; 1.642 + 1.643 + return true ; 1.644 + } 1.645 + 1.646 + private static boolean equalMaps( Map counterpart, Set considered, 1.647 + Map map1, Map map2 ) 1.648 + { 1.649 + if (map2.size() != map1.size()) 1.650 + return false; 1.651 + 1.652 + try { 1.653 + Iterator i = map1.entrySet().iterator(); 1.654 + while (i.hasNext()) { 1.655 + Entry e = (Entry) i.next(); 1.656 + java.lang.Object key = e.getKey(); 1.657 + java.lang.Object value = e.getValue(); 1.658 + if (value == null) { 1.659 + if (!(map2.get(key)==null && map2.containsKey(key))) 1.660 + return false; 1.661 + } else { 1.662 + if (!equalsHelper( counterpart, considered, 1.663 + value, map2.get(key))) 1.664 + return false; 1.665 + } 1.666 + } 1.667 + } catch(ClassCastException unused) { 1.668 + return false; 1.669 + } catch(NullPointerException unused) { 1.670 + return false; 1.671 + } 1.672 + 1.673 + return true; 1.674 + } 1.675 + 1.676 + // Obviously this is an inefficient quadratic algorithm. 1.677 + // This is taken pretty directly from AbstractSet and AbstractCollection 1.678 + // in the JDK. 1.679 + // For HashSet, an O(n) (with a good hash function) algorithm 1.680 + // is possible, and likewise TreeSet, since it is 1.681 + // ordered, is O(n). But this is not worth the effort here. 1.682 + // Note that the inner loop uses equals, not equalsHelper. 1.683 + // This is needed because of the searching behavior of this test. 1.684 + // However, note that this will NOT correctly handle sets that 1.685 + // contain themselves as members, or that have members that reference 1.686 + // themselves. These cases will cause infinite regress! 1.687 + private static boolean equalSets( Map counterpart, Set considered, 1.688 + Set set1, Set set2 ) 1.689 + { 1.690 + if (set1.size() != set2.size()) 1.691 + return false ; 1.692 + 1.693 + Iterator e1 = set1.iterator() ; 1.694 + while (e1.hasNext()) { 1.695 + java.lang.Object obj1 = e1.next() ; 1.696 + 1.697 + boolean found = false ; 1.698 + Iterator e2 = set2.iterator() ; 1.699 + while (e2.hasNext() && !found) { 1.700 + java.lang.Object obj2 = e2.next() ; 1.701 + found = equals( obj1, obj2 ) ; 1.702 + } 1.703 + 1.704 + if (!found) 1.705 + return false ; 1.706 + } 1.707 + 1.708 + return true ; 1.709 + } 1.710 + 1.711 + private static boolean equalLists( Map counterpart, Set considered, 1.712 + List list1, List list2 ) 1.713 + { 1.714 + ListIterator e1 = list1.listIterator(); 1.715 + ListIterator e2 = list2.listIterator(); 1.716 + while(e1.hasNext() && e2.hasNext()) { 1.717 + java.lang.Object o1 = e1.next(); 1.718 + java.lang.Object o2 = e2.next(); 1.719 + if (!(o1==null ? o2==null : equalsHelper( 1.720 + counterpart, considered, o1, o2))) 1.721 + return false; 1.722 + } 1.723 + return !(e1.hasNext() || e2.hasNext()); 1.724 + } 1.725 +}