src/share/classes/com/sun/corba/se/impl/orbutil/ObjectUtility.java

Sat, 01 Dec 2007 00:00:00 +0000

author
duke
date
Sat, 01 Dec 2007 00:00:00 +0000
changeset 1
55540e827aef
child 158
91006f157c46
permissions
-rw-r--r--

Initial load

     1 /*
     2  * Copyright 2002-2006 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.corba.se.impl.orbutil;
    28 import java.security.PrivilegedAction;
    29 import java.security.AccessController;
    30 import java.util.ArrayList;
    31 import java.util.Arrays;
    32 import java.util.Map;
    33 import java.util.List;
    34 import java.util.ListIterator;
    35 import java.util.Set;
    36 import java.util.Map.Entry;
    37 import java.util.Collection;
    38 import java.util.HashMap;
    39 import java.util.HashSet;
    40 import java.util.Hashtable;
    41 import java.util.Iterator;
    42 import java.util.Enumeration;
    43 import java.util.Properties;
    44 import java.util.IdentityHashMap;
    45 import java.lang.reflect.Array;
    46 import java.lang.reflect.Field;
    47 import java.lang.reflect.Method;
    48 import java.lang.reflect.Modifier;
    49 import java.math.BigInteger ;
    50 import java.math.BigDecimal ;
    52 public final class ObjectUtility {
    53     private boolean useToString ;
    54     private boolean isIndenting ;
    55     private int initialLevel ;
    56     private int increment ;
    57     private ClassMap classToPrinter = new ClassMap() ;
    59     private static ObjectUtility standard = new ObjectUtility( false, true,
    60         0, 4 ) ;
    61     private static ObjectUtility compact = new ObjectUtility( true, false,
    62         0, 4 ) ;
    64     private ObjectUtility( boolean useToString, boolean isIndenting,
    65         int initialLevel, int increment )
    66     {
    67         this.useToString = useToString ;
    68         this.isIndenting = isIndenting ;
    69         this.initialLevel = initialLevel ;
    70         this.increment = increment ;
    71         classToPrinter.put( Properties.class, propertiesPrinter ) ;
    72         classToPrinter.put( Collection.class, collectionPrinter ) ;
    73         classToPrinter.put( Map.class, mapPrinter ) ;
    74     }
    76     /** Construct an Utility instance with the desired objectToString
    77     * behavior.
    78     */
    79     public static ObjectUtility make( boolean useToString, boolean isIndenting,
    80         int initialLevel, int increment )
    81     {
    82         return new ObjectUtility( useToString, isIndenting, initialLevel,
    83             increment ) ;
    84     }
    86     /** Construct an Utility instance with the desired objectToString
    87     * behavior.
    88     */
    89     public static ObjectUtility make( boolean useToString, boolean isIndenting )
    90     {
    91         return new ObjectUtility( useToString, isIndenting, 0, 4 ) ;
    92     }
    94     /** Get the standard Utility object that supports objectToString with
    95     * indented display and no use of toString() methods.
    96     */
    97     public static ObjectUtility make()
    98     {
    99         return standard ;
   100     }
   102     /** A convenience method that gives the default behavior: use indenting
   103     * to display the object's structure and do not use built-in toString
   104     * methods.
   105     */
   106     public static String defaultObjectToString( java.lang.Object object )
   107     {
   108         return standard.objectToString( object ) ;
   109     }
   111     public static String compactObjectToString( java.lang.Object object )
   112     {
   113         return compact.objectToString( object ) ;
   114     }
   116     /** objectToString handles display of arbitrary objects.  It correctly
   117     * handles objects whose elements form an arbitrary graph.  It uses
   118     * reflection to display the contents of any kind of object.
   119     * An object's toString() method may optionally be used, but the default
   120     * is to ignore all toString() methods except for those defined for
   121     * primitive types, primitive type wrappers, and strings.
   122     */
   123     public String objectToString(java.lang.Object obj)
   124     {
   125         IdentityHashMap printed = new IdentityHashMap() ;
   126         ObjectWriter result = ObjectWriter.make( isIndenting, initialLevel,
   127             increment ) ;
   128         objectToStringHelper( printed, result, obj ) ;
   129         return result.toString() ;
   130     }
   132     // Perform a deep structural equality comparison of the two objects.
   133     // This handles all arrays, maps, and sets specially, otherwise
   134     // it just calls the object's equals() method.
   135     public static boolean equals( java.lang.Object obj1, java.lang.Object obj2 )
   136     {
   137         // Set of pairs of objects that have been (or are being) considered for
   138         // equality.  Such pairs are presumed to be equals.  If they are not,
   139         // this will be detected eventually and the equals method will return
   140         // false.
   141         Set considered = new HashSet() ;
   143         // Map that gives the corresponding component of obj2 for a component
   144         // of obj1.  This is used to check for the same aliasing and use of
   145         // equal objects in both objects.
   146         Map counterpart = new IdentityHashMap() ;
   148         return equalsHelper( counterpart, considered, obj1, obj2 ) ;
   149     }
   151     /** If arr1 and arr2 are both arrays of the same component type,
   152      * return an array of that component type that consists of the
   153      * elements of arr1 followed by the elements of arr2.
   154      * Throws IllegalArgumentException otherwise.
   155      */
   156     public static Object concatenateArrays( Object arr1, Object arr2 )
   157     {
   158         Class comp1 = arr1.getClass().getComponentType() ;
   159         Class comp2 = arr2.getClass().getComponentType() ;
   160         int len1 = Array.getLength( arr1 ) ;
   161         int len2 = Array.getLength( arr2 ) ;
   163         if ((comp1 == null) || (comp2 == null))
   164             throw new IllegalStateException( "Arguments must be arrays" ) ;
   165         if (!comp1.equals( comp2 ))
   166             throw new IllegalStateException(
   167                 "Arguments must be arrays with the same component type" ) ;
   169         Object result = Array.newInstance( comp1, len1 + len2 ) ;
   171         int index = 0 ;
   173         for (int ctr=0; ctr<len1; ctr++)
   174             Array.set( result, index++, Array.get( arr1, ctr ) ) ;
   176         for (int ctr=0; ctr<len2; ctr++)
   177             Array.set( result, index++, Array.get( arr2, ctr ) ) ;
   179         return result ;
   180     }
   182 //===========================================================================
   183 //  Implementation
   184 //===========================================================================
   186     private void objectToStringHelper( IdentityHashMap printed,
   187         ObjectWriter result, java.lang.Object obj)
   188     {
   189         if (obj==null) {
   190             result.append( "null" ) ;
   191             result.endElement() ;
   192         } else {
   193             Class cls = obj.getClass() ;
   194             result.startObject( obj ) ;
   196             if (printed.keySet().contains( obj )) {
   197                 result.endObject( "*VISITED*" ) ;
   198             } else {
   199                 printed.put( obj, null ) ;
   201                 if (mustUseToString(cls)) {
   202                     result.endObject( obj.toString() ) ;
   203                 } else {
   204                     // First, handle any classes that have special printer
   205                     // methods defined.  This is useful when the class
   206                     // overrides toString with something that
   207                     // is not sufficiently detailed.
   208                     ObjectPrinter printer = (ObjectPrinter)(classToPrinter.get(
   209                         cls )) ;
   210                     if (printer != null) {
   211                         printer.print( printed, result, obj ) ;
   212                         result.endObject() ;
   213                     } else {
   214                         Class compClass = cls.getComponentType() ;
   216                         if (compClass == null)
   217                             // handleObject always calls endObject
   218                             handleObject( printed, result, obj ) ;
   219                         else {
   220                             handleArray( printed, result, obj ) ;
   221                             result.endObject() ;
   222                         }
   223                     }
   224                 }
   225             }
   226         }
   227     }
   229     private static interface ObjectPrinter {
   230         void print( IdentityHashMap printed, ObjectWriter buff,
   231             java.lang.Object obj ) ;
   232     }
   234     private ObjectPrinter propertiesPrinter = new ObjectPrinter() {
   235         public void print( IdentityHashMap printed, ObjectWriter buff,
   236             java.lang.Object obj )
   237         {
   238             if (!(obj instanceof Properties))
   239                 throw new Error() ;
   241             Properties props = (Properties)obj ;
   242             Enumeration keys = props.propertyNames() ;
   243             while (keys.hasMoreElements()) {
   244                 String key = (String)(keys.nextElement()) ;
   245                 String value = props.getProperty( key ) ;
   246                 buff.startElement() ;
   247                 buff.append( key ) ;
   248                 buff.append( "=" ) ;
   249                 buff.append( value ) ;
   250                 buff.endElement() ;
   251             }
   252         }
   253     } ;
   255     private ObjectPrinter collectionPrinter = new ObjectPrinter() {
   256         public void print( IdentityHashMap printed, ObjectWriter buff,
   257             java.lang.Object obj )
   258         {
   259             if (!(obj instanceof Collection))
   260                 throw new Error() ;
   262             Collection coll = (Collection)obj ;
   263             Iterator iter = coll.iterator() ;
   264             while (iter.hasNext()) {
   265                 java.lang.Object element = iter.next() ;
   266                 buff.startElement() ;
   267                 objectToStringHelper( printed, buff, element ) ;
   268                 buff.endElement() ;
   269             }
   270         }
   271     } ;
   273     private ObjectPrinter mapPrinter = new ObjectPrinter() {
   274         public void print( IdentityHashMap printed, ObjectWriter buff,
   275             java.lang.Object obj )
   276         {
   277             if (!(obj instanceof Map))
   278                 throw new Error() ;
   280             Map map = (Map)obj ;
   281             Iterator iter = map.entrySet().iterator() ;
   282             while (iter.hasNext()) {
   283                 Entry entry = (Entry)(iter.next()) ;
   284                 buff.startElement() ;
   285                 objectToStringHelper( printed, buff, entry.getKey() ) ;
   286                 buff.append( "=>" ) ;
   287                 objectToStringHelper( printed, buff, entry.getValue() ) ;
   288                 buff.endElement() ;
   289             }
   290         }
   291     } ;
   293     private static class ClassMap {
   294         ArrayList data ;
   296         public ClassMap()
   297         {
   298             data = new ArrayList() ;
   299         }
   301         /** Return the first element of the ClassMap that is assignable to cls.
   302         * The order is determined by the order in which the put method was
   303         * called.  Returns null if there is no match.
   304         */
   305         public java.lang.Object get( Class cls )
   306         {
   307             Iterator iter = data.iterator() ;
   308             while (iter.hasNext()) {
   309                 java.lang.Object[] arr = (java.lang.Object[])(iter.next()) ;
   310                 Class key = (Class)(arr[0]) ;
   311                 if (key.isAssignableFrom( cls ))
   312                     return arr[1] ;
   313             }
   315             return null ;
   316         }
   318         /** Add obj to the map with key cls.  Note that order matters,
   319          * as the first match is returned.
   320          */
   321         public void put( Class cls, java.lang.Object obj )
   322         {
   323             java.lang.Object[] pair = { cls, obj } ;
   324             data.add( pair ) ;
   325         }
   326     }
   328     private boolean mustUseToString( Class cls )
   329     {
   330         // These probably never occur
   331         if (cls.isPrimitive())
   332             return true ;
   334         // We must use toString for all primitive wrappers, since
   335         // otherwise the code recurses endlessly (access value field
   336         // inside Integer, returns another Integer through reflection).
   337         if ((cls == Integer.class) ||
   338             (cls == BigInteger.class) ||
   339             (cls == BigDecimal.class) ||
   340             (cls == String.class) ||
   341             (cls == StringBuffer.class) ||
   342             (cls == Long.class) ||
   343             (cls == Short.class) ||
   344             (cls == Byte.class) ||
   345             (cls == Character.class) ||
   346             (cls == Float.class) ||
   347             (cls == Double.class) ||
   348             (cls == Boolean.class))
   349             return true ;
   351         if (useToString) {
   352             try {
   353                 cls.getDeclaredMethod( "toString", null ) ;
   354                 return true ;
   355             } catch (Exception exc) {
   356                 return false ;
   357             }
   358         }
   360         return false ;
   361     }
   363     private void handleObject( IdentityHashMap printed, ObjectWriter result,
   364         java.lang.Object obj )
   365     {
   366         Class cls = obj.getClass() ;
   368         try {
   369             Field[] fields;
   370             SecurityManager security = System.getSecurityManager();
   371             if (security != null && !Modifier.isPublic(cls.getModifiers())) {
   372                 fields = new Field[0];
   373             } else {
   374                 fields = cls.getDeclaredFields();
   375             }
   377             for (int ctr=0; ctr<fields.length; ctr++ ) {
   378                 final Field fld = fields[ctr] ;
   379                 int modifiers = fld.getModifiers() ;
   381                 // Do not display field if it is static, since these fields
   382                 // are always the same for every instances.  This could
   383                 // be made configurable, but I don't think it is
   384                 // useful to do so.
   385                 if (!Modifier.isStatic( modifiers )) {
   386                     if (security != null) {
   387                         if (!Modifier.isPublic(modifiers))
   388                             continue;
   389                     }
   390                     result.startElement() ;
   391                     result.append( fld.getName() ) ;
   392                     result.append( ":" ) ;
   394                     try {
   395                         // Make sure that we can read the field if it is
   396                         // not public
   397                         AccessController.doPrivileged( new PrivilegedAction() {
   398                             public Object run() {
   399                                 fld.setAccessible( true ) ;
   400                                 return null ;
   401                             }
   402                         } ) ;
   404                         java.lang.Object value = fld.get( obj ) ;
   405                         objectToStringHelper( printed, result, value ) ;
   406                     } catch (Exception exc2) {
   407                         result.append( "???" ) ;
   408                     }
   410                     result.endElement() ;
   411                 }
   412             }
   414             result.endObject() ;
   415         } catch (Exception exc2) {
   416             result.endObject( obj.toString() ) ;
   417         }
   418     }
   420     private void handleArray( IdentityHashMap printed, ObjectWriter result,
   421         java.lang.Object obj )
   422     {
   423         Class compClass = obj.getClass().getComponentType() ;
   424         if (compClass == boolean.class) {
   425             boolean[] arr = (boolean[])obj ;
   426             for (int ctr=0; ctr<arr.length; ctr++) {
   427                 result.startElement() ;
   428                 result.append( arr[ctr] ) ;
   429                 result.endElement() ;
   430             }
   431         } else if (compClass == byte.class) {
   432             byte[] arr = (byte[])obj ;
   433             for (int ctr=0; ctr<arr.length; ctr++) {
   434                 result.startElement() ;
   435                 result.append( arr[ctr] ) ;
   436                 result.endElement() ;
   437             }
   438         } else if (compClass == short.class) {
   439             short[] arr = (short[])obj ;
   440             for (int ctr=0; ctr<arr.length; ctr++) {
   441                 result.startElement() ;
   442                 result.append( arr[ctr] ) ;
   443                 result.endElement() ;
   444             }
   445         } else if (compClass == int.class) {
   446             int[] arr = (int[])obj ;
   447             for (int ctr=0; ctr<arr.length; ctr++) {
   448                 result.startElement() ;
   449                 result.append( arr[ctr] ) ;
   450                 result.endElement() ;
   451             }
   452         } else if (compClass == long.class) {
   453             long[] arr = (long[])obj ;
   454             for (int ctr=0; ctr<arr.length; ctr++) {
   455                 result.startElement() ;
   456                 result.append( arr[ctr] ) ;
   457                 result.endElement() ;
   458             }
   459         } else if (compClass == char.class) {
   460             char[] arr = (char[])obj ;
   461             for (int ctr=0; ctr<arr.length; ctr++) {
   462                 result.startElement() ;
   463                 result.append( arr[ctr] ) ;
   464                 result.endElement() ;
   465             }
   466         } else if (compClass == float.class) {
   467             float[] arr = (float[])obj ;
   468             for (int ctr=0; ctr<arr.length; ctr++) {
   469                 result.startElement() ;
   470                 result.append( arr[ctr] ) ;
   471                 result.endElement() ;
   472             }
   473         } else if (compClass == double.class) {
   474             double[] arr = (double[])obj ;
   475             for (int ctr=0; ctr<arr.length; ctr++) {
   476                 result.startElement() ;
   477                 result.append( arr[ctr] ) ;
   478                 result.endElement() ;
   479             }
   480         } else { // array of object
   481             java.lang.Object[] arr = (java.lang.Object[])obj ;
   482             for (int ctr=0; ctr<arr.length; ctr++) {
   483                 result.startElement() ;
   484                 objectToStringHelper( printed, result, arr[ctr] ) ;
   485                 result.endElement() ;
   486             }
   487         }
   488     }
   490     private static class Pair
   491     {
   492         private java.lang.Object obj1 ;
   493         private java.lang.Object obj2 ;
   495         Pair( java.lang.Object obj1, java.lang.Object obj2 )
   496         {
   497             this.obj1 = obj1 ;
   498             this.obj2 = obj2 ;
   499         }
   501         public boolean equals( java.lang.Object obj )
   502         {
   503             if (!(obj instanceof Pair))
   504                 return false ;
   506             Pair other = (Pair)obj ;
   507             return other.obj1 == obj1 && other.obj2 == obj2 ;
   508         }
   510         public int hashCode()
   511         {
   512             return System.identityHashCode( obj1 ) ^
   513                 System.identityHashCode( obj2 ) ;
   514         }
   515     }
   517     private static boolean equalsHelper( Map counterpart, Set considered,
   518         java.lang.Object obj1, java.lang.Object obj2 )
   519     {
   520         if ((obj1 == null) || (obj2 == null))
   521             return obj1 == obj2 ;
   523         java.lang.Object other2 = counterpart.get( obj1 ) ;
   524         if (other2 == null) {
   525             other2 = obj2 ;
   526             counterpart.put( obj1, other2 ) ;
   527         }
   529         if (obj1 == other2)
   530             return true ;
   532         if (obj2 != other2)
   533             return false ;
   535         Pair pair = new Pair( obj1, obj2 ) ;
   536         if (considered.contains( pair ))
   537             return true ;
   538         else
   539             considered.add( pair ) ;
   541         if (obj1 instanceof java.lang.Object[] &&
   542             obj2 instanceof java.lang.Object[])
   543             return equalArrays( counterpart, considered,
   544                 (java.lang.Object[])obj1, (java.lang.Object[])obj2 ) ;
   545         else if (obj1 instanceof Map && obj2 instanceof Map)
   546             return equalMaps( counterpart, considered,
   547                 (Map)obj1, (Map)obj2 ) ;
   548         else if (obj1 instanceof Set && obj2 instanceof Set)
   549             return equalSets( counterpart, considered,
   550                 (Set)obj1, (Set)obj2 ) ;
   551         else if (obj1 instanceof List && obj2 instanceof List)
   552             return equalLists( counterpart, considered,
   553                 (List)obj1, (List)obj2 ) ;
   554         else if (obj1 instanceof boolean[] && obj2 instanceof boolean[])
   555             return Arrays.equals( (boolean[])obj1, (boolean[])obj2 ) ;
   556         else if (obj1 instanceof byte[] && obj2 instanceof byte[])
   557             return Arrays.equals( (byte[])obj1, (byte[])obj2 ) ;
   558         else if (obj1 instanceof char[] && obj2 instanceof char[])
   559             return Arrays.equals( (char[])obj1, (char[])obj2 ) ;
   560         else if (obj1 instanceof double[] && obj2 instanceof double[])
   561             return Arrays.equals( (double[])obj1, (double[])obj2 ) ;
   562         else if (obj1 instanceof float[] && obj2 instanceof float[])
   563             return Arrays.equals( (float[])obj1, (float[])obj2 ) ;
   564         else if (obj1 instanceof int[] && obj2 instanceof int[])
   565             return Arrays.equals( (int[])obj1, (int[])obj2 ) ;
   566         else if (obj1 instanceof long[] && obj2 instanceof long[])
   567             return Arrays.equals( (long[])obj1, (long[])obj2 ) ;
   568         else {
   569             Class cls = obj1.getClass() ;
   570             if (cls != obj2.getClass())
   571                 return obj1.equals( obj2 ) ;
   572             else
   573                 return equalsObject( counterpart, considered, cls, obj1, obj2 ) ;
   574         }
   575     }
   577     private static boolean equalsObject( Map counterpart, Set considered,
   578         Class cls, java.lang.Object obj1, java.lang.Object obj2 )
   579     {
   580         Class objectClass = java.lang.Object.class ;
   581         if (cls == objectClass)
   582             return true ;
   584         Class[] equalsTypes = { objectClass } ;
   585         try {
   586             Method equalsMethod = cls.getDeclaredMethod( "equals",
   587                 equalsTypes ) ;
   588             return obj1.equals( obj2 ) ;
   589         } catch (Exception exc) {
   590             if (equalsObjectFields( counterpart, considered,
   591                     cls, obj1, obj2 ))
   592                 return equalsObject( counterpart, considered,
   593                     cls.getSuperclass(), obj1, obj2 ) ;
   594             else
   595                 return false ;
   596         }
   597     }
   599     private static boolean equalsObjectFields( Map counterpart, Set considered,
   600         Class cls, java.lang.Object obj1, java.lang.Object obj2 )
   601     {
   602         Field[] fields = cls.getDeclaredFields() ;
   603         for (int ctr=0; ctr<fields.length; ctr++) {
   604             try {
   605                 final Field field = fields[ctr] ;
   606                 // Ignore static fields
   607                 if (!Modifier.isStatic( field.getModifiers())) {
   608                     AccessController.doPrivileged(new PrivilegedAction() {
   609                         public Object run() {
   610                             field.setAccessible( true ) ;
   611                             return null ;
   612                         }
   613                     } ) ;
   615                     java.lang.Object value1 = field.get( obj1 ) ;
   616                     java.lang.Object value2 = field.get( obj2 ) ;
   617                     if (!equalsHelper( counterpart, considered, value1,
   618                         value2 ))
   619                         return false ;
   620                 }
   621             } catch (IllegalAccessException exc) {
   622                 return false ;
   623             }
   624         }
   626         return true ;
   627     }
   629     private static boolean equalArrays( Map counterpart, Set considered,
   630         java.lang.Object[] arr1, java.lang.Object[] arr2 )
   631     {
   632         int len = arr1.length ;
   633         if (len != arr2.length)
   634             return false ;
   636         for (int ctr = 0; ctr<len; ctr++ )
   637             if (!equalsHelper( counterpart, considered, arr1[ctr], arr2[ctr] ))
   638                 return false ;
   640         return true ;
   641     }
   643     private static boolean equalMaps( Map counterpart, Set considered,
   644         Map map1, Map map2 )
   645     {
   646         if (map2.size() != map1.size())
   647             return false;
   649         try {
   650             Iterator i = map1.entrySet().iterator();
   651             while (i.hasNext()) {
   652                 Entry e = (Entry) i.next();
   653                 java.lang.Object key = e.getKey();
   654                 java.lang.Object value = e.getValue();
   655                 if (value == null) {
   656                     if (!(map2.get(key)==null && map2.containsKey(key)))
   657                         return false;
   658                 } else {
   659                     if (!equalsHelper( counterpart, considered,
   660                         value, map2.get(key)))
   661                         return false;
   662                 }
   663             }
   664         } catch(ClassCastException unused)   {
   665             return false;
   666         } catch(NullPointerException unused) {
   667             return false;
   668         }
   670         return true;
   671     }
   673     // Obviously this is an inefficient quadratic algorithm.
   674     // This is taken pretty directly from AbstractSet and AbstractCollection
   675     // in the JDK.
   676     // For HashSet, an O(n) (with a good hash function) algorithm
   677     // is possible, and likewise TreeSet, since it is
   678     // ordered, is O(n).  But this is not worth the effort here.
   679     // Note that the inner loop uses equals, not equalsHelper.
   680     // This is needed because of the searching behavior of this test.
   681     // However, note that this will NOT correctly handle sets that
   682     // contain themselves as members, or that have members that reference
   683     // themselves.  These cases will cause infinite regress!
   684     private static boolean equalSets( Map counterpart, Set considered,
   685         Set set1, Set set2 )
   686     {
   687         if (set1.size() != set2.size())
   688             return false ;
   690         Iterator e1 = set1.iterator() ;
   691         while (e1.hasNext()) {
   692             java.lang.Object obj1 = e1.next() ;
   694             boolean found = false ;
   695             Iterator e2 = set2.iterator() ;
   696             while (e2.hasNext() && !found) {
   697                 java.lang.Object obj2 = e2.next() ;
   698                 found = equals( obj1, obj2 ) ;
   699             }
   701             if (!found)
   702                 return false ;
   703         }
   705         return true ;
   706     }
   708     private static boolean equalLists( Map counterpart, Set considered,
   709         List list1, List list2 )
   710     {
   711         ListIterator e1 = list1.listIterator();
   712         ListIterator e2 = list2.listIterator();
   713         while(e1.hasNext() && e2.hasNext()) {
   714             java.lang.Object o1 = e1.next();
   715             java.lang.Object o2 = e2.next();
   716             if (!(o1==null ? o2==null : equalsHelper(
   717                 counterpart, considered, o1, o2)))
   718                 return false;
   719         }
   720         return !(e1.hasNext() || e2.hasNext());
   721     }
   722 }

mercurial