KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > lang > builder > ReflectionToStringBuilder


1 /*
2  * Copyright 2002-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.lang.builder;
17
18 import java.lang.reflect.AccessibleObject JavaDoc;
19 import java.lang.reflect.Field JavaDoc;
20 import java.lang.reflect.Modifier JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Set JavaDoc;
23
24 import org.apache.commons.lang.ClassUtils;
25
26 /**
27  * <p>
28  * Assists in implementing {@link Object#toString()} methods using reflection.
29  * </p>
30  *
31  * <p>
32  * This class uses reflection to determine the fields to append. Because these
33  * fields are usually private, the class uses
34  * {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)}
35  * to change the visibility of the fields. This will fail under a security
36  * manager, unless the appropriate permissions are set up correctly.
37  * </p>
38  *
39  * <p>
40  * A typical invocation for this method would look like:
41  * </p>
42  *
43  * <pre>
44  * public String toString() {
45  * return ReflectionToStringBuilder.toString(this);
46  * }</pre>
47  *
48  *
49  *
50  * <p>
51  * You can also use the builder to debug 3rd party objects:
52  * </p>
53  *
54  * <pre>
55  * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));</pre>
56  *
57  *
58  *
59  * <p>
60  * A subclass can control field output by overriding the methods:
61  * <ul>
62  * <li>{@link #accept(java.lang.reflect.Field)}</li>
63  * <li>{@link #getValue(java.lang.reflect.Field)}</li>
64  * </ul>
65  * </p>
66  * <p>
67  * For example, this method does <i>not</i> include the <code>password</code>
68  * field in the returned <code>String</code>:
69  * </p>
70  *
71  * <pre>
72  * public String toString() {
73  * return (new ReflectionToStringBuilder(this) {
74  * protected boolean accept(Field f) {
75  * return super.accept(f) && !f.getName().equals("password");
76  * }
77  * }).toString();
78  * }</pre>
79  *
80  *
81  *
82  * <p>
83  * The exact format of the <code>toString</code> is determined by the
84  * {@link ToStringStyle} passed into the constructor.
85  * </p>
86  *
87  * @author Gary Gregory
88  * @author Stephen Colebourne
89  * @author Pete Gieser
90  * @since 2.0
91  * @version $Id: ReflectionToStringBuilder.java,v 1.15 2003/12/02 19:11:58
92  * ggregory Exp $
93  */

94 public class ReflectionToStringBuilder extends ToStringBuilder {
95     /**
96      * <p>
97      * A registry of objects used by <code>reflectionToString</code> methods
98      * to detect cyclical object references and avoid infinite loops.
99      * </p>
100      */

101     private static ThreadLocal JavaDoc registry = new ThreadLocal JavaDoc() {
102         protected synchronized Object JavaDoc initialValue() {
103             // The HashSet implementation is not synchronized,
104
// which is just what we need here.
105
return new HashSet JavaDoc();
106         }
107     };
108
109     /**
110      * <p>
111      * Returns the registry of objects being traversed by the <code>reflectionToString</code>
112      * methods in the current thread.
113      * </p>
114      *
115      * @return Set the registry of objects being traversed
116      */

117     static Set JavaDoc getRegistry() {
118         return (Set JavaDoc) registry.get();
119     }
120
121     /**
122      * <p>
123      * Returns <code>true</code> if the registry contains the given object.
124      * Used by the reflection methods to avoid infinite loops.
125      * </p>
126      *
127      * @param value
128      * The object to lookup in the registry.
129      * @return boolean <code>true</code> if the registry contains the given
130      * object.
131      */

132     static boolean isRegistered(Object JavaDoc value) {
133         return getRegistry().contains(value);
134     }
135
136     /**
137      * <p>
138      * Registers the given object. Used by the reflection methods to avoid
139      * infinite loops.
140      * </p>
141      *
142      * @param value
143      * The object to register.
144      */

145     static void register(Object JavaDoc value) {
146         getRegistry().add(value);
147     }
148
149     /**
150      * <p>
151      * This method uses reflection to build a suitable <code>toString</code>
152      * using the default <code>ToStringStyle</code>.
153      *
154      * <p>
155      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
156      * private fields. This means that it will throw a security exception if
157      * run under a security manager, if the permissions are not set up
158      * correctly. It is also not as efficient as testing explicitly.
159      * </p>
160      *
161      * <p>
162      * Transient members will be not be included, as they are likely derived.
163      * Static fields will not be included. Superclass fields will be appended.
164      * </p>
165      *
166      * @param object
167      * the Object to be output
168      * @return the String result
169      * @throws IllegalArgumentException
170      * if the Object is <code>null</code>
171      */

172     public static String JavaDoc toString(Object JavaDoc object) {
173         return toString(object, null, false, false, null);
174     }
175
176     /**
177      * <p>
178      * This method uses reflection to build a suitable <code>toString</code>.
179      * </p>
180      *
181      * <p>
182      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
183      * private fields. This means that it will throw a security exception if
184      * run under a security manager, if the permissions are not set up
185      * correctly. It is also not as efficient as testing explicitly.
186      * </p>
187      *
188      * <p>
189      * Transient members will be not be included, as they are likely derived.
190      * Static fields will not be included. Superclass fields will be appended.
191      * </p>
192      *
193      * <p>
194      * If the style is <code>null</code>, the default <code>ToStringStyle</code>
195      * is used.
196      * </p>
197      *
198      * @param object
199      * the Object to be output
200      * @param style
201      * the style of the <code>toString</code> to create, may be
202      * <code>null</code>
203      * @return the String result
204      * @throws IllegalArgumentException
205      * if the Object or <code>ToStringStyle</code> is <code>null</code>
206      */

207     public static String JavaDoc toString(Object JavaDoc object, ToStringStyle style) {
208         return toString(object, style, false, false, null);
209     }
210
211     /**
212      * <p>
213      * This method uses reflection to build a suitable <code>toString</code>.
214      * </p>
215      *
216      * <p>
217      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
218      * private fields. This means that it will throw a security exception if
219      * run under a security manager, if the permissions are not set up
220      * correctly. It is also not as efficient as testing explicitly.
221      * </p>
222      *
223      * <p>
224      * If the <code>outputTransients</code> is <code>true</code>,
225      * transient members will be output, otherwise they are ignored, as they
226      * are likely derived fields, and not part of the value of the Object.
227      * </p>
228      *
229      * <p>
230      * Static fields will not be included. Superclass fields will be appended.
231      * </p>
232      *
233      * <p>
234      * If the style is <code>null</code>, the default <code>ToStringStyle</code>
235      * is used.
236      * </p>
237      *
238      * @param object
239      * the Object to be output
240      * @param style
241      * the style of the <code>toString</code> to create, may be
242      * <code>null</code>
243      * @param outputTransients
244      * whether to include transient fields
245      * @return the String result
246      * @throws IllegalArgumentException
247      * if the Object is <code>null</code>
248      */

249     public static String JavaDoc toString(Object JavaDoc object, ToStringStyle style, boolean outputTransients) {
250         return toString(object, style, outputTransients, false, null);
251     }
252
253     /**
254      * <p>
255      * This method uses reflection to build a suitable <code>toString</code>.
256      * </p>
257      *
258      * <p>
259      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
260      * private fields. This means that it will throw a security exception if
261      * run under a security manager, if the permissions are not set up
262      * correctly. It is also not as efficient as testing explicitly.
263      * </p>
264      *
265      * <p>
266      * If the <code>outputTransients</code> is <code>true</code>,
267      * transient fields will be output, otherwise they are ignored, as they are
268      * likely derived fields, and not part of the value of the Object.
269      * </p>
270      *
271      * <p>
272      * If the <code>outputStatics</code> is <code>true</code>, static
273      * fields will be output, otherwise they are ignored.
274      * </p>
275      *
276      * <p>
277      * Static fields will not be included. Superclass fields will be appended.
278      * </p>
279      *
280      * <p>
281      * If the style is <code>null</code>, the default <code>ToStringStyle</code>
282      * is used.
283      * </p>
284      *
285      * @param object
286      * the Object to be output
287      * @param style
288      * the style of the <code>toString</code> to create, may be
289      * <code>null</code>
290      * @param outputTransients
291      * whether to include transient fields
292      * @param outputStatics
293      * whether to include transient fields
294      * @return the String result
295      * @throws IllegalArgumentException
296      * if the Object is <code>null</code>
297      * @since 2.1
298      */

299     public static String JavaDoc toString(Object JavaDoc object, ToStringStyle style, boolean outputTransients, boolean outputStatics) {
300         return toString(object, style, outputTransients, outputStatics, null);
301     }
302
303     /**
304      * <p>
305      * This method uses reflection to build a suitable <code>toString</code>.
306      * </p>
307      *
308      * <p>
309      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
310      * private fields. This means that it will throw a security exception if
311      * run under a security manager, if the permissions are not set up
312      * correctly. It is also not as efficient as testing explicitly.
313      * </p>
314      *
315      * <p>
316      * If the <code>outputTransients</code> is <code>true</code>,
317      * transient fields will be output, otherwise they are ignored, as they are
318      * likely derived fields, and not part of the value of the Object.
319      * </p>
320      *
321      * <p>
322      * If the <code>outputStatics</code> is <code>true</code>, static
323      * fields will be output, otherwise they are ignored.
324      * </p>
325      *
326      * <p>
327      * Superclass fields will be appended up to and including the specified
328      * superclass. A null superclass is treated as <code>java.lang.Object</code>.
329      * </p>
330      *
331      * <p>
332      * If the style is <code>null</code>, the default <code>ToStringStyle</code>
333      * is used.
334      * </p>
335      *
336      * @param object
337      * the Object to be output
338      * @param style
339      * the style of the <code>toString</code> to create, may be
340      * <code>null</code>
341      * @param outputTransients
342      * whether to include transient fields
343      * @param outputStatics
344      * whether to include static fields
345      * @param reflectUpToClass
346      * the superclass to reflect up to (inclusive), may be <code>null</code>
347      * @return the String result
348      * @throws IllegalArgumentException
349      * if the Object is <code>null</code>
350      * @since 2.1
351      */

352     public static String JavaDoc toString(Object JavaDoc object, ToStringStyle style, boolean outputTransients, boolean outputStatics,
353             Class JavaDoc reflectUpToClass) {
354         return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics)
355                 .toString();
356     }
357
358     /**
359      * <p>
360      * This method uses reflection to build a suitable <code>toString</code>.
361      * </p>
362      *
363      * <p>
364      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
365      * private fields. This means that it will throw a security exception if
366      * run under a security manager, if the permissions are not set up
367      * correctly. It is also not as efficient as testing explicitly.
368      * </p>
369      *
370      * <p>
371      * If the <code>outputTransients</code> is <code>true</code>,
372      * transient members will be output, otherwise they are ignored, as they
373      * are likely derived fields, and not part of the value of the Object.
374      * </p>
375      *
376      * <p>
377      * Static fields will not be included. Superclass fields will be appended
378      * up to and including the specified superclass. A null superclass is
379      * treated as <code>java.lang.Object</code>.
380      * </p>
381      *
382      * <p>
383      * If the style is <code>null</code>, the default <code>ToStringStyle</code>
384      * is used.
385      * </p>
386      *
387      * @deprecated Use
388      * {@link #toString(Object,ToStringStyle,boolean,boolean,Class)}
389      *
390      * @param object
391      * the Object to be output
392      * @param style
393      * the style of the <code>toString</code> to create, may be
394      * <code>null</code>
395      * @param outputTransients
396      * whether to include transient fields
397      * @param reflectUpToClass
398      * the superclass to reflect up to (inclusive), may be <code>null</code>
399      * @return the String result
400      * @throws IllegalArgumentException
401      * if the Object is <code>null</code>
402      * @since 2.0
403      */

404     public static String JavaDoc toString(Object JavaDoc object, ToStringStyle style, boolean outputTransients,
405             Class JavaDoc reflectUpToClass) {
406         return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients).toString();
407     }
408
409     /**
410      * <p>
411      * Unregisters the given object.
412      * </p>
413      *
414      * <p>
415      * Used by the reflection methods to avoid infinite loops.
416      * </p>
417      *
418      * @param value
419      * The object to unregister.
420      */

421     static void unregister(Object JavaDoc value) {
422         getRegistry().remove(value);
423     }
424
425     /**
426      * Whether or not to append static fields.
427      */

428     private boolean appendStatics = false;
429
430     /**
431      * Whether or not to append transient fields.
432      */

433     private boolean appendTransients = false;
434
435     /**
436      * The last super class to stop appending fields for.
437      */

438     private Class JavaDoc upToClass = null;
439
440     /**
441      * <p>
442      * Constructor.
443      * </p>
444      *
445      * <p>
446      * This constructor outputs using the default style set with <code>setDefaultStyle</code>.
447      * </p>
448      *
449      * @param object
450      * the Object to build a <code>toString</code> for, must not
451      * be <code>null</code>
452      * @throws IllegalArgumentException
453      * if the Object passed in is <code>null</code>
454      */

455     public ReflectionToStringBuilder(Object JavaDoc object) {
456         super(object);
457     }
458
459     /**
460      * <p>
461      * Constructor.
462      * </p>
463      *
464      * <p>
465      * If the style is <code>null</code>, the default style is used.
466      * </p>
467      *
468      * @param object
469      * the Object to build a <code>toString</code> for, must not
470      * be <code>null</code>
471      * @param style
472      * the style of the <code>toString</code> to create, may be
473      * <code>null</code>
474      * @throws IllegalArgumentException
475      * if the Object passed in is <code>null</code>
476      */

477     public ReflectionToStringBuilder(Object JavaDoc object, ToStringStyle style) {
478         super(object, style);
479     }
480
481     /**
482      * <p>
483      * Constructor.
484      * </p>
485      *
486      * <p>
487      * If the style is <code>null</code>, the default style is used.
488      * </p>
489      *
490      * <p>
491      * If the buffer is <code>null</code>, a new one is created.
492      * </p>
493      *
494      * @param object
495      * the Object to build a <code>toString</code> for
496      * @param style
497      * the style of the <code>toString</code> to create, may be
498      * <code>null</code>
499      * @param buffer
500      * the <code>StringBuffer</code> to populate, may be <code>null</code>
501      * @throws IllegalArgumentException
502      * if the Object passed in is <code>null</code>
503      */

504     public ReflectionToStringBuilder(Object JavaDoc object, ToStringStyle style, StringBuffer JavaDoc buffer) {
505         super(object, style, buffer);
506     }
507
508     /**
509      * Constructor.
510      *
511      * @deprecated Use
512      * {@link #ReflectionToStringBuilder(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)}.
513      *
514      * @param object
515      * the Object to build a <code>toString</code> for
516      * @param style
517      * the style of the <code>toString</code> to create, may be
518      * <code>null</code>
519      * @param buffer
520      * the <code>StringBuffer</code> to populate, may be <code>null</code>
521      * @param reflectUpToClass
522      * the superclass to reflect up to (inclusive), may be <code>null</code>
523      * @param outputTransients
524      * whether to include transient fields
525      */

526     public ReflectionToStringBuilder(Object JavaDoc object, ToStringStyle style, StringBuffer JavaDoc buffer, Class JavaDoc reflectUpToClass,
527             boolean outputTransients) {
528         super(object, style, buffer);
529         this.setUpToClass(reflectUpToClass);
530         this.setAppendTransients(outputTransients);
531     }
532
533     /**
534      * Constructor.
535      *
536      * @param object
537      * the Object to build a <code>toString</code> for
538      * @param style
539      * the style of the <code>toString</code> to create, may be
540      * <code>null</code>
541      * @param buffer
542      * the <code>StringBuffer</code> to populate, may be <code>null</code>
543      * @param reflectUpToClass
544      * the superclass to reflect up to (inclusive), may be <code>null</code>
545      * @param outputTransients
546      * whether to include transient fields
547      * @param outputStatics
548      * whether to include static fields
549      * @since 2.1
550      */

551     public ReflectionToStringBuilder(Object JavaDoc object, ToStringStyle style, StringBuffer JavaDoc buffer, Class JavaDoc reflectUpToClass,
552             boolean outputTransients, boolean outputStatics) {
553         super(object, style, buffer);
554         this.setUpToClass(reflectUpToClass);
555         this.setAppendTransients(outputTransients);
556         this.setAppendStatics(outputStatics);
557     }
558
559     /**
560      * Returns whether or not to append the given <code>Field</code>.
561      * <ul>
562      * <li>Transient fields are appended only if {@link #isAppendTransients()}
563      * returns <code>true</code>.
564      * <li>Static fields are appended only if {@link #isAppendStatics()}
565      * returns <code>true</code>.
566      * <li>Inner class fields are not appened.</li>
567      * </ul>
568      *
569      * @param field
570      * The Field to test.
571      * @return Whether or not to append the given <code>Field</code>.
572      */

573     protected boolean accept(Field JavaDoc field) {
574         if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
575             // Reject field from inner class.
576
return false;
577         }
578         if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) {
579             // transients.
580
return false;
581         }
582         if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) {
583             // transients.
584
return false;
585         }
586         return true;
587     }
588
589     /**
590      * <p>
591      * Appends the fields and values defined by the given object of the given
592      * Class.
593      * </p>
594      *
595      * <p>
596      * If a cycle is detected as an object is &quot;toString()'ed&quot;, such
597      * an object is rendered as if <code>Object.toString()</code> had been
598      * called and not implemented by the object.
599      * </p>
600      *
601      * @param clazz
602      * The class of object parameter
603      */

604     protected void appendFieldsIn(Class JavaDoc clazz) {
605         if (isRegistered(this.getObject())) {
606             // The object has already been appended, therefore we have an
607
// object cycle.
608
// Append a simple Object.toString style string. The field name is
609
// already appended at this point.
610
this.appendAsObjectToString(this.getObject());
611             return;
612         }
613         try {
614             this.registerObject();
615             if (clazz.isArray()) {
616                 this.reflectionAppendArray(this.getObject());
617                 return;
618             }
619             Field JavaDoc[] fields = clazz.getDeclaredFields();
620             AccessibleObject.setAccessible(fields, true);
621             for (int i = 0; i < fields.length; i++) {
622                 Field JavaDoc field = fields[i];
623                 String JavaDoc fieldName = field.getName();
624                 if (this.accept(field)) {
625                     try {
626                         // Warning: Field.get(Object) creates wrappers objects
627
// for primitive types.
628
Object JavaDoc fieldValue = this.getValue(field);
629                         if (isRegistered(fieldValue) && !field.getType().isPrimitive()) {
630                             // A known field value has already been appended,
631
// therefore we have an object cycle,
632
// append a simple Object.toString style string.
633
this.getStyle().appendFieldStart(this.getStringBuffer(), fieldName);
634                             this.appendAsObjectToString(fieldValue);
635                             this.getStyle().appendFieldEnd(this.getStringBuffer(), fieldName);
636                             // The recursion out of
637
// builder.append(fieldName, fieldValue);
638
// below will append the field
639
// end marker.
640
} else {
641                             try {
642                                 this.registerObject();
643                                 this.append(fieldName, fieldValue);
644                             } finally {
645                                 this.unregisterObject();
646                             }
647                         }
648                     } catch (IllegalAccessException JavaDoc ex) {
649                         //this can't happen. Would get a Security exception
650
// instead
651
//throw a runtime exception in case the impossible
652
// happens.
653
throw new InternalError JavaDoc("Unexpected IllegalAccessException: " + ex.getMessage());
654                     }
655                 }
656             }
657         } finally {
658             this.unregisterObject();
659         }
660     }
661
662     /**
663      * <p>
664      * Gets the last super class to stop appending fields for.
665      * </p>
666      *
667      * @return The last super class to stop appending fields for.
668      */

669     public Class JavaDoc getUpToClass() {
670         return this.upToClass;
671     }
672
673     /**
674      * <p>
675      * Calls <code>java.lang.reflect.Field.get(Object)</code>.
676      * </p>
677      *
678      * @param field
679      * The Field to query.
680      * @return The Object from the given Field.
681      *
682      * @throws IllegalArgumentException
683      * see {@link java.lang.reflect.Field#get(Object)}
684      * @throws IllegalAccessException
685      * see {@link java.lang.reflect.Field#get(Object)}
686      *
687      * @see java.lang.reflect.Field#get(Object)
688      */

689     protected Object JavaDoc getValue(Field JavaDoc field) throws IllegalArgumentException JavaDoc, IllegalAccessException JavaDoc {
690         return field.get(this.getObject());
691     }
692
693     /**
694      * <p>
695      * Gets whether or not to append static fields.
696      * </p>
697      *
698      * @return Whether or not to append static fields.
699      * @since 2.1
700      */

701     public boolean isAppendStatics() {
702         return this.appendStatics;
703     }
704
705     /**
706      * <p>
707      * Gets whether or not to append transient fields.
708      * </p>
709      *
710      * @return Whether or not to append transient fields.
711      */

712     public boolean isAppendTransients() {
713         return this.appendTransients;
714     }
715
716     /**
717      * <p>
718      * Append to the <code>toString</code> an <code>Object</code> array.
719      * </p>
720      *
721      * @param array
722      * the array to add to the <code>toString</code>
723      * @return this
724      */

725     public ToStringBuilder reflectionAppendArray(Object JavaDoc array) {
726         this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
727         return this;
728     }
729
730     /**
731      * <p>
732      * Registers this builder's source object to avoid infinite loops when
733      * processing circular object references.
734      * </p>
735      */

736     void registerObject() {
737         register(this.getObject());
738     }
739
740     /**
741      * <p>
742      * Sets whether or not to append static fields.
743      * </p>
744      *
745      * @param appendStatics
746      * Whether or not to append static fields.
747      * @since 2.1
748      */

749     public void setAppendStatics(boolean appendStatics) {
750         this.appendStatics = appendStatics;
751     }
752
753     /**
754      * <p>
755      * Sets whether or not to append transient fields.
756      * </p>
757      *
758      * @param appendTransients
759      * Whether or not to append transient fields.
760      */

761     public void setAppendTransients(boolean appendTransients) {
762         this.appendTransients = appendTransients;
763     }
764
765     /**
766      * <p>
767      * Sets the last super class to stop appending fields for.
768      * </p>
769      *
770      * @param clazz
771      * The last super class to stop appending fields for.
772      */

773     public void setUpToClass(Class JavaDoc clazz) {
774         this.upToClass = clazz;
775     }
776
777     /**
778      * <p>
779      * Gets the String built by this builder.
780      * </p>
781      *
782      * @return the built string
783      */

784     public String JavaDoc toString() {
785         if (this.getObject() == null) {
786             return this.getStyle().getNullText();
787         }
788         Class JavaDoc clazz = this.getObject().getClass();
789         this.appendFieldsIn(clazz);
790         while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
791             clazz = clazz.getSuperclass();
792             this.appendFieldsIn(clazz);
793         }
794         return super.toString();
795     }
796
797     /**
798      * <p>
799      * Unregisters this builder's source object to avoid infinite loops when
800      * processing circular object references.
801      * </p>
802      */

803     void unregisterObject() {
804         unregister(this.getObject());
805     }
806 }
807
Popular Tags