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). git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137353 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3bcf738220
commit
a0be4d2757
|
@ -0,0 +1,457 @@
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -53,11 +53,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.lang.builder;
|
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>
|
* <p>Builds <code>toString()</code> values.</p>
|
||||||
*
|
*
|
||||||
|
@ -122,26 +117,15 @@ import java.util.Set;
|
||||||
* @author Stephen Colebourne
|
* @author Stephen Colebourne
|
||||||
* @author Gary Gregory
|
* @author Gary Gregory
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @version $Id: ToStringBuilder.java,v 1.21 2003/05/31 22:22:49 ggregory Exp $
|
* @version $Id: ToStringBuilder.java,v 1.22 2003/06/03 03:51:56 ggregory Exp $
|
||||||
*/
|
*/
|
||||||
public class ToStringBuilder {
|
public class ToStringBuilder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default style of output to use
|
* The default style of output to use
|
||||||
*/
|
*/
|
||||||
private static ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE;
|
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 @@ public class ToStringBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the registry of objects being traversed by the
|
* Forwards to ReflectionToStringBuilder.
|
||||||
* <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.
|
|
||||||
*
|
*
|
||||||
* @param value The object to lookup in the registry.
|
* @see ReflectionToStringBuilder#toString(Object)
|
||||||
* @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>
|
|
||||||
*/
|
*/
|
||||||
public static String reflectionToString(Object 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
|
* Forwards to ReflectionToStringBuilder.
|
||||||
* <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>
|
|
||||||
*
|
*
|
||||||
* @param object the Object to be output
|
* @see ReflectionToStringBuilder#toString(Object,ToStringStyle)
|
||||||
* @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 reflectionToString(Object object, ToStringStyle style) {
|
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
|
* Forwards to ReflectionToStringBuilder.
|
||||||
* <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
|
* @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean)
|
||||||
* @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 reflectionToString(Object object, ToStringStyle style, boolean outputTransients) {
|
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
|
* Forwards to ReflectionToStringBuilder.
|
||||||
* <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>
|
|
||||||
*
|
*
|
||||||
* @param object the Object to be output
|
* @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,Class)
|
||||||
* @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 reflectionToString(
|
public static String reflectionToString(
|
||||||
Object object,
|
Object object,
|
||||||
ToStringStyle style,
|
ToStringStyle style,
|
||||||
boolean outputTransients,
|
boolean outputTransients,
|
||||||
Class reflectUpToClass) {
|
Class reflectUpToClass) {
|
||||||
if (style == null) {
|
return ReflectionToStringBuilder.toString(object, style, outputTransients, reflectUpToClass);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,27 +198,17 @@ public class ToStringBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters the given object.
|
* Current toString buffer.
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
private final StringBuffer buffer;
|
private final StringBuffer buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object being output
|
* The object being output.
|
||||||
*/
|
*/
|
||||||
private final Object object;
|
private final Object object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The style of output to use
|
* The style of output to use.
|
||||||
*/
|
*/
|
||||||
private final ToStringStyle style;
|
private final ToStringStyle style;
|
||||||
|
|
||||||
|
@ -1257,18 +1044,6 @@ public class ToStringBuilder {
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <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>Returns the built <code>toString</code>.</p>
|
||||||
*
|
*
|
||||||
|
@ -1282,4 +1057,13 @@ public class ToStringBuilder {
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the object being output.
|
||||||
|
*
|
||||||
|
* @return The object being output.
|
||||||
|
*/
|
||||||
|
public Object getObject() {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,8 +81,9 @@ import org.apache.commons.lang.SystemUtils;
|
||||||
* the array length.</p>
|
* the array length.</p>
|
||||||
*
|
*
|
||||||
* @author Stephen Colebourne
|
* @author Stephen Colebourne
|
||||||
|
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @version $Id: ToStringStyle.java,v 1.13 2003/04/18 04:57:19 bayard Exp $
|
* @version $Id: ToStringStyle.java,v 1.14 2003/06/03 03:51:56 ggregory Exp $
|
||||||
*/
|
*/
|
||||||
public abstract class ToStringStyle implements Serializable {
|
public abstract class ToStringStyle implements Serializable {
|
||||||
|
|
||||||
|
@ -153,7 +154,7 @@ public abstract class ToStringStyle implements Serializable {
|
||||||
*/
|
*/
|
||||||
private String arraySeparator = ",";
|
private String arraySeparator = ",";
|
||||||
/**
|
/**
|
||||||
* The detail for array content
|
* The detail for array content.
|
||||||
*/
|
*/
|
||||||
private boolean arrayContentDetail = true;
|
private boolean arrayContentDetail = true;
|
||||||
/**
|
/**
|
||||||
|
@ -329,7 +330,7 @@ public abstract class ToStringStyle implements Serializable {
|
||||||
* @param detail output detail or not
|
* @param detail output detail or not
|
||||||
*/
|
*/
|
||||||
protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) {
|
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)) {
|
&& !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
|
||||||
appendAsObjectToString(buffer, value);
|
appendAsObjectToString(buffer, value);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue