You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by gg...@apache.org on 2003/06/03 05:52:03 UTC

cvs commit: jakarta-commons/lang/src/java/org/apache/commons/lang/builder ReflectionToStringBuilder.java ToStringStyle.java ToStringBuilder.java

ggregory    2003/06/02 20:52:03

  Modified:    lang/src/java/org/apache/commons/lang/builder
                        ToStringStyle.java ToStringBuilder.java
  Added:       lang/src/java/org/apache/commons/lang/builder
                        ReflectionToStringBuilder.java
  Log:
  Refactor code in ToStringBuilder.reflectionToString(...) into a new subclass called ReflectionToStringBuilder.
  All of the ToStringBuilder.reflectionToString(...) forward their calls to equivalent methods in ReflectionToStringBuilde.
  ReflectionToStringBuilder can  be subclassed to provide Field or value filtering.
  Since the unit tests exercis ToStringBuilder.reflectionToString(...) which then forwards those calls to ReflectionToStringBuilder, and ReflectionToStringBuilder does not provide new features (yet), there are no new unit test cases (yet).
  
  Revision  Changes    Path
  1.14      +4 -3      jakarta-commons/lang/src/java/org/apache/commons/lang/builder/ToStringStyle.java
  
  Index: ToStringStyle.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/builder/ToStringStyle.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- ToStringStyle.java	18 Apr 2003 04:57:19 -0000	1.13
  +++ ToStringStyle.java	3 Jun 2003 03:51:56 -0000	1.14
  @@ -81,6 +81,7 @@
    * the array length.</p>
    *
    * @author Stephen Colebourne
  + * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
    * @since 1.0
    * @version $Id$
    */
  @@ -153,7 +154,7 @@
        */
       private String arraySeparator = ",";
       /**
  -     * The detail for array content
  +     * The detail for array content.
        */
       private boolean arrayContentDetail = true;
       /**
  @@ -329,7 +330,7 @@
        * @param detail  output detail or not
        */
       protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) {
  -        if (ToStringBuilder.isRegistered(value) 
  +        if (ReflectionToStringBuilder.isRegistered(value) 
                   && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
               appendAsObjectToString(buffer, value);
               
  
  
  
  1.22      +28 -244   jakarta-commons/lang/src/java/org/apache/commons/lang/builder/ToStringBuilder.java
  
  Index: ToStringBuilder.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/builder/ToStringBuilder.java,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- ToStringBuilder.java	31 May 2003 22:22:49 -0000	1.21
  +++ ToStringBuilder.java	3 Jun 2003 03:51:56 -0000	1.22
  @@ -53,11 +53,6 @@
    */
   package org.apache.commons.lang.builder;
   
  -import java.lang.reflect.Field;
  -import java.lang.reflect.Modifier;
  -import java.util.HashSet;
  -import java.util.Set;
  -
   /**
    * <p>Builds <code>toString()</code> values.</p>
    *
  @@ -125,23 +120,12 @@
    * @version $Id$
    */
   public class ToStringBuilder {
  -    
  +
       /**
        * The default style of output to use
        */
       private static ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE;
   
  -    /**
  -     * A registry of objects used by <code>reflectionToString</code> methods to detect cyclical object references 
  -     * and avoid infinite loops.
  -     */
  -    private static ThreadLocal reflectionRegistry = new ThreadLocal() {
  -        protected synchronized Object initialValue() {
  -            // The HashSet implementation is not synchronized, which is just what we need here. 
  -            return new HashSet();
  -        }
  -    };
  -
       //----------------------------------------------------------------------------
   
       /**
  @@ -161,230 +145,43 @@
       }
   
       /**
  -     * Returns the registry of objects being traversed by the 
  -     * <code>reflectionToString</code> methods in the current thread.
  -     * @return Set the registry of objects being traversed 
  -     */
  -    static Set getReflectionRegistry() {
  -        return (Set) reflectionRegistry.get();
  -    }
  -
  -    /**
  -     * Returns <code>true</code> if the registry contains the given object.
  -     * Used by the reflection methods to avoid infinite loops.
  +     * Forwards to ReflectionToStringBuilder.
        * 
  -     * @param value The object to lookup in the registry.
  -     * @return boolean <code>true</code> if the registry contains the given object.
  -     */
  -    static boolean isRegistered(Object value) {
  -        return getReflectionRegistry().contains(value);
  -    }
  -
  -    /**
  -     * Appends the fields and values defined by the given object of the
  -     * given Class. If a cycle is detected as an objects is "toString()'ed",
  -     * such an object is rendered as if <code>Object.toString()</code> 
  -     * had been called and not implemented by the object.
  -     * 
  -     * @param object  the object to append details of
  -     * @param clazz  the class of object parameter
  -     * @param builder  the builder to append to
  -     * @param useTransients  whether to output transient fields
  -     */
  -    private static void reflectionAppend(Object object, Class clazz, ToStringBuilder builder, boolean useTransients) {
  -        if (isRegistered(object)) {
  -            // The object has already been appended, therefore we have an object cycle. 
  -            // Append a simple Object.toString style string. The field name is already appended at this point.
  -            builder.appendAsObjectToString(object);
  -            return;
  -        }
  -        try {
  -            register(object);
  -            if (clazz.isArray()) {
  -                builder.reflectionAppendArray(object);
  -                return;
  -            }
  -            Field[] fields = clazz.getDeclaredFields();
  -            Field.setAccessible(fields, true);
  -            for (int i = 0; i < fields.length; i++) {
  -                Field f = fields[i];
  -                String fieldName = f.getName();
  -                if ((fieldName.indexOf('$') == -1)
  -                    && (useTransients || !Modifier.isTransient(f.getModifiers()))
  -                    && (!Modifier.isStatic(f.getModifiers()))) {
  -                    try {
  -                        // Warning: Field.get(Object) creates wrappers objects for primitive types.
  -                        Object fieldValue = f.get(object);
  -                        if (isRegistered(fieldValue)
  -                            && !f.getType().isPrimitive()) {
  -                            // A known field value has already been appended, therefore we have an object cycle, 
  -                            // append a simple Object.toString style string.
  -                            builder.getStyle().appendFieldStart(builder.getStringBuffer(), fieldName);
  -                            builder.appendAsObjectToString(fieldValue);
  -                            // The recursion out of 
  -                            //    builder.append(fieldName, fieldValue); 
  -                            // below will append the field 
  -                            // end marker.
  -                        } else {
  -                            try {
  -                                register(object);
  -                                builder.append(fieldName, fieldValue);
  -                            } finally {
  -                                unregister(object);
  -                            }
  -                        }
  -                    } catch (IllegalAccessException ex) {
  -                        //this can't happen. Would get a Security exception instead
  -                        //throw a runtime exception in case the impossible happens.
  -                        throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
  -                    }
  -                }
  -            }
  -        } finally {
  -            unregister(object);
  -        }
  -    }
  -
  -    //-------------------------------------------------------------------------
  -
  -    /**
  -     * <p>This method uses reflection to build a suitable
  -     * <code>toString</code> using the default <code>ToStringStyle</code>.
  -     *
  -     * <p>It uses <code>Field.setAccessible</code> to gain access to private
  -     * fields. This means that it will throw a security exception if run
  -     * under a security manger, if the permissions are not set up correctly.
  -     * It is also not as efficient as testing explicitly.</p>
  -     *
  -     * <p>Transient members will be not be included, as they are likely derived.</p>
  -     *
  -     * <p>Static fields will not be included. Superclass fields will be appended.</p>
  -     *
  -     * @param object  the Object to be output
  -     * @return the String result
  -     * @throws IllegalArgumentException if the Object is <code>null</code>
  +     * @see ReflectionToStringBuilder#toString(Object)
        */
       public static String reflectionToString(Object object) {
  -        return reflectionToString(object, null, false, null);
  +        return ReflectionToStringBuilder.toString(object);
       }
   
       /**
  -     * <p>This method uses reflection to build a suitable
  -     * <code>toString</code>.</p>
  -     *
  -     * <p>It uses <code>Field.setAccessible</code> to gain access to private
  -     * fields. This means that it will throw a security exception if run
  -     * under a security manger, if the permissions are not set up correctly.
  -     * It is also not as efficient as testing explicitly.</p>
  -     *
  -     * <p>Transient members will be not be included, as they are likely
  -     * derived.</p>
  -     *
  -     * <p>Static fields will not be included. Superclass fields will be appended.</p>
  -     *
  -     * <p>If the style is <code>null</code>, the default
  -     * <code>ToStringStyle</code> is used.</p>
  +     * Forwards to ReflectionToStringBuilder.
        * 
  -     * @param object  the Object to be output
  -     * @param style  the style of the <code>toString</code> to create,
  -     *  may be <code>null</code>
  -     * @return the String result
  -     * @throws IllegalArgumentException if the Object or
  -     *  <code>ToStringStyle</code> is <code>null</code>
  +     * @see ReflectionToStringBuilder#toString(Object,ToStringStyle)
        */
       public static String reflectionToString(Object object, ToStringStyle style) {
  -        return reflectionToString(object, style, false, null);
  +        return ReflectionToStringBuilder.toString(object, style);
       }
   
       /**
  -     * <p>This method uses reflection to build a suitable
  -     * <code>toString</code>.</p>
  -     *
  -     * <p>It uses <code>Field.setAccessible</code> to gain access to private
  -     * fields. This means that it will throw a security exception if run
  -     * under a security manger, if the permissions are not set up correctly.
  -     * It is also not as efficient as testing explicitly. </p>
  -     *
  -     * <p>If the <code>outputTransients</code> is <code>true</code>,
  -     * transient members will be output, otherwise they are ignored,
  -     * as they are likely derived fields, and not part of the value of the
  -     * Object.</p>
  -     *
  -     * <p>Static fields will not be included. Superclass fields will be appended.</p>
  -     *
  -     * <p>
  -     * If the style is <code>null</code>, the default
  -     * <code>ToStringStyle</code> is used.</p>
  +     * Forwards to ReflectionToStringBuilder.
        * 
  -     * @param object  the Object to be output
  -     * @param style  the style of the <code>toString</code> to create,
  -     *  may be <code>null</code>
  -     * @param outputTransients  whether to include transient fields
  -     * @return the String result
  -     * @throws IllegalArgumentException if the Object is <code>null</code>
  +     * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean)
        */
       public static String reflectionToString(Object object, ToStringStyle style, boolean outputTransients) {
  -        return reflectionToString(object, style, outputTransients, null);
  +        return ReflectionToStringBuilder.toString(object, style, outputTransients, null);
       }
   
       /**
  -     * <p>This method uses reflection to build a suitable
  -     * <code>toString</code>.</p>
  -     *
  -     * <p>It uses <code>Field.setAccessible</code> to gain access to private
  -     * fields. This means that it will throw a security exception if run
  -     * under a security manger, if the permissions are not set up correctly.
  -     * It is also not as efficient as testing explicitly. </p>
  -     *
  -     * <p>If the <code>outputTransients</code> is <code>true</code>,
  -     * transient members will be output, otherwise they are ignored,
  -     * as they are likely derived fields, and not part of the value of the
  -     * Object.</p>
  -     *
  -     * <p>Static fields will not be included. Superclass fields will be appended
  -     * up to and including the specified superclass. A null superclass is treated
  -     * as java.lang.Object.</p>
  -     *
  -     * <p>If the style is <code>null</code>, the default
  -     * <code>ToStringStyle</code> is used.</p>
  +     * Forwards to ReflectionToStringBuilder.
        * 
  -     * @param object  the Object to be output
  -     * @param style  the style of the <code>toString</code> to create,
  -     *  may be <code>null</code>
  -     * @param outputTransients  whether to include transient fields
  -     * @param reflectUpToClass  the superclass to reflect up to (inclusive), may be null
  -     * @return the String result
  -     * @throws IllegalArgumentException if the Object is <code>null</code>
  +     * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,Class)
        */
       public static String reflectionToString(
           Object object,
           ToStringStyle style,
           boolean outputTransients,
           Class reflectUpToClass) {
  -        if (style == null) {
  -            style = getDefaultStyle();
  -        }
  -        if (object == null) {
  -            return style.getNullText();
  -        }
  -        ToStringBuilder builder = new ToStringBuilder(object, style);
  -        Class clazz = object.getClass();
  -        reflectionAppend(object, clazz, builder, outputTransients);
  -        while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
  -            clazz = clazz.getSuperclass();
  -            reflectionAppend(object, clazz, builder, outputTransients);
  -        }
  -        return builder.toString();
  -    }
  -
  -    /**
  -     * Registers the given object.
  -     * Used by the reflection methods to avoid infinite loops.
  -     * 
  -     * @param value The object to register.
  -     */
  -    static void register(Object value) {
  -        getReflectionRegistry().add(value);
  +        return ReflectionToStringBuilder.toString(object, style, outputTransients, reflectUpToClass);
       }
   
       /**
  @@ -401,27 +198,17 @@
       }
   
       /**
  -     * Unregisters the given object.
  -     * Used by the reflection methods to avoid infinite loops.
  -     * 
  -     * @param value The object to unregister.
  -     */
  -    static void unregister(Object value) {
  -        getReflectionRegistry().remove(value);
  -    }
  -    
  -    /**
  -     * Current toString buffer
  +     * Current toString buffer.
        */
       private final StringBuffer buffer;
  -    
  +
       /**
  -     * The object being output
  +     * The object being output.
        */
       private final Object object;
  -    
  +
       /**
  -     * The style of output to use
  +     * The style of output to use.
        */
       private final ToStringStyle style;
   
  @@ -1258,18 +1045,6 @@
       }
   
       /**
  -     * <p>Append to the <code>toString</code> an <code>Object</code>
  -     * array.</p>
  -     *
  -     * @param array  the array to add to the <code>toString</code>
  -     * @return this
  -     */
  -    public ToStringBuilder reflectionAppendArray(Object array) {
  -        style.reflectionAppendArrayDetail(buffer, null, array);
  -        return this;
  -    }
  -
  -    /**
        * <p>Returns the built <code>toString</code>.</p>
        * 
        * <p>This method appends the end of the buffer, and can only be called once.
  @@ -1280,6 +1055,15 @@
       public String toString() {
           style.appendEnd(buffer, object);
           return buffer.toString();
  +    }
  +
  +    /**
  +     * Returns the object being output.
  +     * 
  +     * @return The object being output.
  +     */
  +    public Object getObject() {
  +        return object;
       }
   
   }
  
  
  
  1.1                  jakarta-commons/lang/src/java/org/apache/commons/lang/builder/ReflectionToStringBuilder.java
  
  Index: ReflectionToStringBuilder.java
  ===================================================================
  package org.apache.commons.lang.builder;
  
  import java.lang.reflect.Field;
  import java.lang.reflect.Modifier;
  import java.util.HashSet;
  import java.util.Set;
  
  import org.apache.commons.lang.ClassUtils;
  
  /**
   * <p>Builds <code>toString()</code> values using reflection.</p>
   *
   * <p>This class uses reflection to determine the fields to append. 
   * Because these fields are usually private, the class, 
   * uses <code>Field.setAccessible</code> to
   * change the visibility of the fields. This will fail under a security manager,
   * unless the appropriate permissions are set up correctly.</p>
   *
   * <p>A typical invocation for this method would look like:</p>
   * <pre>
   * public String toString() {
   *   return ReflectionToStringBuilder.toString(this);
   * }
   * </pre>
   *
   * <p>You can also use the builder to debug 3rd party objects:</p>
   * <pre>
   * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));
   * </pre>
   * 
   * <p>A subclass can control field output by overriding the methods:
   * <ul> 
   * <li>{@link #accept(java.lang.reflect.Field)}</li>
   * <li>{@link #getValue(java.lang.reflect.Field)}</li>
   * </ul>
   * </p>
   * 
   * <p>The exact format of the <code>toString</code> is determined by
   * the {@link ToStringStyle} passed into the constructor.</p>
   *
   * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
   * @author Stephen Colebourne
   * @since 2.0
   * @version $Id: ReflectionToStringBuilder.java,v 1.1 2003/06/03 03:51:56 ggregory Exp $
   */
  public class ReflectionToStringBuilder extends ToStringBuilder {
  
      /**
       * A registry of objects used by <code>reflectionToString</code> methods to detect cyclical object references 
       * and avoid infinite loops.
       */
      private static ThreadLocal registry = new ThreadLocal() {
          protected synchronized Object initialValue() {
              // The HashSet implementation is not synchronized, 
              // which is just what we need here. 
              return new HashSet();
          }
      };
  
      /**
       * Returns the registry of objects being traversed by the 
       * <code>reflectionToString</code> methods in the current thread.
       * @return Set the registry of objects being traversed 
       */
      static Set getRegistry() {
          return (Set) registry.get();
      }
  
      /**
       * Returns <code>true</code> if the registry contains the given object.
       * Used by the reflection methods to avoid infinite loops.
       * 
       * @param value The object to lookup in the registry.
       * @return boolean <code>true</code> if the registry contains the given object.
       */
      static boolean isRegistered(Object value) {
          return getRegistry().contains(value);
      }
  
      /**
       * Registers the given object.
       * Used by the reflection methods to avoid infinite loops.
       * 
       * @param value The object to register.
       */
      static void register(Object value) {
          getRegistry().add(value);
      }
      
      /**
       * <p>This method uses reflection to build a suitable
       * <code>toString</code> using the default <code>ToStringStyle</code>.
       *
       * <p>It uses <code>Field.setAccessible</code> to gain access to private
       * fields. This means that it will throw a security exception if run
       * under a security manger, if the permissions are not set up correctly.
       * It is also not as efficient as testing explicitly.</p>
       *
       * <p>Transient members will be not be included, as they are likely derived.
       * Static fields will not be included. Superclass fields will be appended.</p>
       *
       * @param object  the Object to be output
       * @return the String result
       * @throws IllegalArgumentException if the Object is <code>null</code>
       */
      public static String toString(Object object) {
          return toString(object, null, false, null);
      }
  
      /**
       * <p>This method uses reflection to build a suitable
       * <code>toString</code>.</p>
       *
       * <p>It uses <code>Field.setAccessible</code> to gain access to private
       * fields. This means that it will throw a security exception if run
       * under a security manger, if the permissions are not set up correctly.
       * It is also not as efficient as testing explicitly.</p>
       *
       * <p>Transient members will be not be included, as they are likely derived.
       * Static fields will not be included. Superclass fields will be appended.</p>
       *
       * <p>If the style is <code>null</code>, the default
       * <code>ToStringStyle</code> is used.</p>
       * 
       * @param object  the Object to be output
       * @param style  the style of the <code>toString</code> to create,
       *  may be <code>null</code>
       * @return the String result
       * @throws IllegalArgumentException if the Object or
       *  <code>ToStringStyle</code> is <code>null</code>
       */
      public static String toString(Object object, ToStringStyle style) {
          return toString(object, style, false, null);
      }
  
      /**
       * <p>This method uses reflection to build a suitable
       * <code>toString</code>.</p>
       *
       * <p>It uses <code>Field.setAccessible</code> to gain access to private
       * fields. This means that it will throw a security exception if run
       * under a security manger, if the permissions are not set up correctly.
       * It is also not as efficient as testing explicitly. </p>
       *
       * <p>If the <code>outputTransients</code> is <code>true</code>,
       * transient members will be output, otherwise they are ignored,
       * as they are likely derived fields, and not part of the value of the
       * Object.</p>
       *
       * <p>Static fields will not be included. Superclass fields will be appended.</p>
       *
       * <p>
       * If the style is <code>null</code>, the default
       * <code>ToStringStyle</code> is used.</p>
       * 
       * @param object  the Object to be output
       * @param style  the style of the <code>toString</code> to create,
       *  may be <code>null</code>
       * @param outputTransients  whether to include transient fields
       * @return the String result
       * @throws IllegalArgumentException if the Object is <code>null</code>
       */
      public static String toString(Object object, ToStringStyle style, boolean outputTransients) {
          return toString(object, style, outputTransients, null);
      }
  
      /**
       * <p>This method uses reflection to build a suitable
       * <code>toString</code>.</p>
       *
       * <p>It uses <code>Field.setAccessible</code> to gain access to private
       * fields. This means that it will throw a security exception if run
       * under a security manger, if the permissions are not set up correctly.
       * It is also not as efficient as testing explicitly. </p>
       *
       * <p>If the <code>outputTransients</code> is <code>true</code>,
       * transient members will be output, otherwise they are ignored,
       * as they are likely derived fields, and not part of the value of the
       * Object.</p>
       *
       * <p>Static fields will not be included. Superclass fields will be appended
       * up to and including the specified superclass. A null superclass is treated
       * as <code>java.lang.Object</code>.</p>
       *
       * <p>If the style is <code>null</code>, the default
       * <code>ToStringStyle</code> is used.</p>
       * 
       * @param object  the Object to be output
       * @param style  the style of the <code>toString</code> to create,
       *  may be <code>null</code>
       * @param outputTransients  whether to include transient fields
       * @param reflectUpToClass  the superclass to reflect up to (inclusive), may be null
       * @return the String result
       * @throws IllegalArgumentException if the Object is <code>null</code>
       */
      public static String toString(
          Object object,
          ToStringStyle style,
          boolean outputTransients,
          Class reflectUpToClass) {
          return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients).toString();
      }
  
      /**
       * Unregisters the given object.
       * Used by the reflection methods to avoid infinite loops.
       * 
       * @param value The object to unregister.
       */
      static void unregister(Object value) {
          getRegistry().remove(value);
      }
  
      /**
       * Whether or not to append transient fields.
       */
      private boolean appendTransients = false;
  
      /**
       * The last super class to stop appending fields for.
       */
      private Class upToClass = null;
  
      /**
       * <p>Constructs a new instance.</p>
       *
       * <p>This constructor outputs using the default style set with
       * <code>setDefaultStyle</code>.</p>
       * 
       * @param object  the Object to build a <code>toString</code> for,
       *  must not be <code>null</code>
       * @throws IllegalArgumentException  if the Object passed in is
       *  <code>null</code>
       */
      public ReflectionToStringBuilder(Object object) {
          super(object);
      }
  
      /**
       * <p>Constructor specifying the output style.</p>
       *
       * <p>If the style is <code>null</code>, the default style is used.</p>
       * 
       * @param object  the Object to build a <code>toString</code> for,
       *  must not be <code>null</code>
       * @param style  the style of the <code>toString</code> to create,
       *  may be <code>null</code>
       * @throws IllegalArgumentException  if the Object passed in is
       *  <code>null</code>
       */
      public ReflectionToStringBuilder(Object object, ToStringStyle style) {
          super(object, style);
      }
  
      /**
       * <p>Constructors a new instance.</p>
       *
       * <p>If the style is <code>null</code>, the default style is used.</p>
       *
       * <p>If the buffer is <code>null</code>, a new one is created.</p>
       * 
       * @param object  the Object to build a <code>toString</code> for,
       *  must not be <code>null</code>
       * @param style  the style of the <code>toString</code> to create,
       *  may be <code>null</code>
       * @param buffer  the <code>StringBuffer</code> to populate, may be
       *  <code>null</code>
       * @throws IllegalArgumentException  if the Object passed in is
       *  <code>null</code>
       */
      public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
          super(object, style, buffer);
      }
  
      /**
       * Constructs a new instance.
       * 
       * @param object  the Object to build a <code>toString</code> for,
       *  must not be <code>null</code>
       * @param style  the style of the <code>toString</code> to create,
       *  may be <code>null</code>
       * @param buffer  the <code>StringBuffer</code> to populate, may be
       *  <code>null</code>
       */
      public ReflectionToStringBuilder(
          Object object,
          ToStringStyle style,
          StringBuffer buffer,
          Class reflectUpToClass,
          boolean outputTransients) {
          super(object, style, buffer);
          this.setUpToClass(reflectUpToClass);
          this.setAppendTransients(outputTransients);
      }
  
      /**
       * Returns whether or not to append the given <code>Field</code>.
       * <ul>
       * <li>Static fields are not appended.</li>
       * <li>Transient fields are appended only if {@link #isAppendTransients()} returns <code>true</code>.
       * <li>Inner class fields are not appened.</li>
       * </ul>
       * @param field The Field to test.
       * @return Whether or not to append the given <code>Field</code>.
       */
      protected boolean accept(Field field) {
          String fieldName = field.getName();
          return (fieldName.indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) == -1)
              && (this.isAppendTransients() || !Modifier.isTransient(field.getModifiers()))
              && (!Modifier.isStatic(field.getModifiers()));
      }
  
      /**
       * Appends the fields and values defined by the given object of the
       * given Class. If a cycle is detected as an objects is "toString()'ed",
       * such an object is rendered as if <code>Object.toString()</code> 
       * had been called and not implemented by the object.
       * 
       * @param clazz The class of object parameter
       */
      protected void appendFieldsIn(Class clazz) {
          if (isRegistered(this.getObject())) {
              // The object has already been appended, therefore we have an object cycle. 
              // Append a simple Object.toString style string. The field name is already appended at this point.
              this.appendAsObjectToString(this.getObject());
              return;
          }
          try {
              this.registerObject();
              if (clazz.isArray()) {
                  this.reflectionAppendArray(this.getObject());
                  return;
              }
              Field[] fields = clazz.getDeclaredFields();
              Field.setAccessible(fields, true);
              for (int i = 0; i < fields.length; i++) {
                  Field field = fields[i];
                  String fieldName = field.getName();
                  if (this.accept(field)) {
                      try {
                          // Warning: Field.get(Object) creates wrappers objects for primitive types.
                          Object fieldValue = this.getValue(field);
                          if (isRegistered(fieldValue) && !field.getType().isPrimitive()) {
                              // A known field value has already been appended, therefore we have an object cycle, 
                              // append a simple Object.toString style string.
                              this.getStyle().appendFieldStart(this.getStringBuffer(), fieldName);
                              this.appendAsObjectToString(fieldValue);
                              // The recursion out of 
                              //    builder.append(fieldName, fieldValue); 
                              // below will append the field 
                              // end marker.
                          } else {
                              try {
                                  this.registerObject();
                                  this.append(fieldName, fieldValue);
                              } finally {
                                  this.unregisterObject();
                              }
                          }
                      } catch (IllegalAccessException ex) {
                          //this can't happen. Would get a Security exception instead
                          //throw a runtime exception in case the impossible happens.
                          throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
                      }
                  }
              }
          } finally {
              this.unregisterObject();
          }
      }
      
      /**
       * Gets the last super class to stop appending fields for.
       * 
       * @return The last super class to stop appending fields for.
       */
      public Class getUpToClass() {
          return this.upToClass;
      }
  
      /**
       * Calls <code>java.lang.reflect.Field.get(Object)</code>
       * @see java.lang.reflect.Field#get(Object)
       * @throws IllegalArgumentException
       * @throws IllegalAccessException
       */
      protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException {
          return field.get(this.getObject());
      }
  
      /**
       * Returns whether or not to append transient fields.
       * 
       * @return Whether or not to append transient fields.
       */
      public boolean isAppendTransients() {
          return this.appendTransients;
      }
      
      /**
       * <p>Append to the <code>toString</code> an <code>Object</code>
       * array.</p>
       *
       * @param array  the array to add to the <code>toString</code>
       * @return this
       */
      public ToStringBuilder reflectionAppendArray(Object array) {
          this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
          return this;
      }
  
      /**
       * Registers this builder's source object to avoid infinite loops processing circular object references.
       */
      void registerObject() {
          register(this.getObject());
      }
  
      /**
       * Sets whether or not to append transient fields.
       * 
       * @param appendTransients Whether or not to append transient fields.
       */
      public void setAppendTransients(boolean appendTransients) {
          this.appendTransients = appendTransients;
      }
  
      /**
       * Sets the last super class to stop appending fields for.
       * 
       * @param clazz The last super class to stop appending fields for.
       */
      public void setUpToClass(Class clazz) {
          this.upToClass = clazz;
      }
  
      public String toString() {
          if (this.getObject() == null) {
              return this.getStyle().getNullText();
          }
          Class clazz = this.getObject().getClass();
          this.appendFieldsIn(clazz);
          while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
              clazz = clazz.getSuperclass();
              this.appendFieldsIn(clazz);
          }
          return super.toString();
      }
  
      /**
       * Unegisters this builder's source object to avoid infinite loops processing circular object references.
       */
      void unregisterObject() {
          unregister(this.getObject());
      }
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org