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

changeset 205
b2fff4b7e8cd
parent 173
032585ad970d
child 748
6845b95cba6b
equal deleted inserted replaced
198:cc67fdc4fee9 205:b2fff4b7e8cd
1 /* 1 /*
2 * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. 2 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 * 4 *
5 * This code is free software; you can redistribute it and/or modify it 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 6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this 7 * published by the Free Software Foundation. Oracle designates this
48 import java.lang.reflect.Modifier; 48 import java.lang.reflect.Modifier;
49 import java.math.BigInteger ; 49 import java.math.BigInteger ;
50 import java.math.BigDecimal ; 50 import java.math.BigDecimal ;
51 51
52 public final class ObjectUtility { 52 public final class ObjectUtility {
53 private boolean useToString ; 53 private ObjectUtility() {}
54 private boolean isIndenting ;
55 private int initialLevel ;
56 private int increment ;
57 private ClassMap classToPrinter = new ClassMap() ;
58 54
59 private static ObjectUtility standard = new ObjectUtility( false, true,
60 0, 4 ) ;
61 private static ObjectUtility compact = new ObjectUtility( true, false,
62 0, 4 ) ;
63
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 }
75
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 }
85
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 }
93
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 }
101
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 }
110
111 public static String compactObjectToString( java.lang.Object object )
112 {
113 return compact.objectToString( object ) ;
114 }
115
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 }
131
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() ;
142
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() ;
147
148 return equalsHelper( counterpart, considered, obj1, obj2 ) ;
149 }
150 55
151 /** If arr1 and arr2 are both arrays of the same component type, 56 /** If arr1 and arr2 are both arrays of the same component type,
152 * return an array of that component type that consists of the 57 * return an array of that component type that consists of the
153 * elements of arr1 followed by the elements of arr2. 58 * elements of arr1 followed by the elements of arr2.
154 * Throws IllegalArgumentException otherwise. 59 * Throws IllegalArgumentException otherwise.
177 Array.set( result, index++, Array.get( arr2, ctr ) ) ; 82 Array.set( result, index++, Array.get( arr2, ctr ) ) ;
178 83
179 return result ; 84 return result ;
180 } 85 }
181 86
182 //===========================================================================
183 // Implementation
184 //===========================================================================
185
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 ) ;
195
196 if (printed.keySet().contains( obj )) {
197 result.endObject( "*VISITED*" ) ;
198 } else {
199 printed.put( obj, null ) ;
200
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() ;
215
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 }
228
229 private static interface ObjectPrinter {
230 void print( IdentityHashMap printed, ObjectWriter buff,
231 java.lang.Object obj ) ;
232 }
233
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() ;
240
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 } ;
254
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() ;
261
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 } ;
272
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() ;
279
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 } ;
292
293 private static class ClassMap {
294 ArrayList data ;
295
296 public ClassMap()
297 {
298 data = new ArrayList() ;
299 }
300
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 }
314
315 return null ;
316 }
317
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 }
327
328 private boolean mustUseToString( Class cls )
329 {
330 // These probably never occur
331 if (cls.isPrimitive())
332 return true ;
333
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 ;
350
351 if (useToString) {
352 try {
353 cls.getDeclaredMethod( "toString", (Class[])null ) ;
354 return true ;
355 } catch (Exception exc) {
356 return false ;
357 }
358 }
359
360 return false ;
361 }
362
363 private void handleObject( IdentityHashMap printed, ObjectWriter result,
364 java.lang.Object obj )
365 {
366 Class cls = obj.getClass() ;
367
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 }
376
377 for (int ctr=0; ctr<fields.length; ctr++ ) {
378 final Field fld = fields[ctr] ;
379 int modifiers = fld.getModifiers() ;
380
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( ":" ) ;
393
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 } ) ;
403
404 java.lang.Object value = fld.get( obj ) ;
405 objectToStringHelper( printed, result, value ) ;
406 } catch (Exception exc2) {
407 result.append( "???" ) ;
408 }
409
410 result.endElement() ;
411 }
412 }
413
414 result.endObject() ;
415 } catch (Exception exc2) {
416 result.endObject( obj.toString() ) ;
417 }
418 }
419
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 }
489
490 private static class Pair
491 {
492 private java.lang.Object obj1 ;
493 private java.lang.Object obj2 ;
494
495 Pair( java.lang.Object obj1, java.lang.Object obj2 )
496 {
497 this.obj1 = obj1 ;
498 this.obj2 = obj2 ;
499 }
500
501 public boolean equals( java.lang.Object obj )
502 {
503 if (!(obj instanceof Pair))
504 return false ;
505
506 Pair other = (Pair)obj ;
507 return other.obj1 == obj1 && other.obj2 == obj2 ;
508 }
509
510 public int hashCode()
511 {
512 return System.identityHashCode( obj1 ) ^
513 System.identityHashCode( obj2 ) ;
514 }
515 }
516
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 ;
522
523 java.lang.Object other2 = counterpart.get( obj1 ) ;
524 if (other2 == null) {
525 other2 = obj2 ;
526 counterpart.put( obj1, other2 ) ;
527 }
528
529 if (obj1 == other2)
530 return true ;
531
532 if (obj2 != other2)
533 return false ;
534
535 Pair pair = new Pair( obj1, obj2 ) ;
536 if (considered.contains( pair ))
537 return true ;
538 else
539 considered.add( pair ) ;
540
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 }
576
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 ;
583
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 }
598
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 } ) ;
614
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 }
625
626 return true ;
627 }
628
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 ;
635
636 for (int ctr = 0; ctr<len; ctr++ )
637 if (!equalsHelper( counterpart, considered, arr1[ctr], arr2[ctr] ))
638 return false ;
639
640 return true ;
641 }
642
643 private static boolean equalMaps( Map counterpart, Set considered,
644 Map map1, Map map2 )
645 {
646 if (map2.size() != map1.size())
647 return false;
648
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 }
669
670 return true;
671 }
672
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 ;
689
690 Iterator e1 = set1.iterator() ;
691 while (e1.hasNext()) {
692 java.lang.Object obj1 = e1.next() ;
693
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 }
700
701 if (!found)
702 return false ;
703 }
704
705 return true ;
706 }
707
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 } 87 }

mercurial