mirror of
https://github.com/apache/commons-lang.git
synced 2025-02-07 02:28:25 +00:00
Fix bug 16676: StackOverflow due to ToStringBuilder
(http://issues.apache.org/bugzilla/show_bug.cgi?id=16676) git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137276 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ab7745f98e
commit
edb0e8d284
@ -54,8 +54,9 @@
|
||||
package org.apache.commons.lang.builder;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p><code>ToString</code> generation routine.</p>
|
||||
@ -115,10 +116,21 @@
|
||||
* @author Stephen Colebourne
|
||||
* @author Gary Gregory
|
||||
* @since 1.0
|
||||
* @version $Id: ToStringBuilder.java,v 1.16 2003/03/23 17:54:16 scolebourne Exp $
|
||||
* @version $Id: ToStringBuilder.java,v 1.17 2003/03/27 08:54:31 ggregory Exp $
|
||||
*/
|
||||
public class ToStringBuilder {
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The default style of output to use
|
||||
*/
|
||||
@ -136,6 +148,40 @@ public class ToStringBuilder {
|
||||
*/
|
||||
private final Object object;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return boolean <code>true</code> if the registry contains the given object.
|
||||
*/
|
||||
static boolean isRegistered(Object value) {
|
||||
return getReflectionRegistry().contains(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given object.
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
*/
|
||||
static void register(Object value) {
|
||||
getReflectionRegistry().add(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given object.
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
*/
|
||||
static void unregister(Object value) {
|
||||
getReflectionRegistry().remove(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructor for <code>ToStringBuilder</code>.</p>
|
||||
*
|
||||
@ -351,9 +397,6 @@ public static String reflectionToString(Object object, ToStringStyle style, bool
|
||||
if (object == null) {
|
||||
return style.getNullText();
|
||||
}
|
||||
if (style == null) {
|
||||
style = getDefaultStyle();
|
||||
}
|
||||
ToStringBuilder builder = new ToStringBuilder(object, style);
|
||||
Class clazz = object.getClass();
|
||||
reflectionAppend(object, clazz, builder, outputTransients);
|
||||
@ -366,7 +409,9 @@ public static String reflectionToString(Object object, ToStringStyle style, bool
|
||||
|
||||
/**
|
||||
* Appends the fields and values defined by the given object of the
|
||||
* given Class.
|
||||
* 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
|
||||
@ -374,61 +419,54 @@ public static String reflectionToString(Object object, ToStringStyle style, bool
|
||||
* @param useTransients whether to output transient fields
|
||||
*/
|
||||
private static void reflectionAppend(Object object, Class clazz, ToStringBuilder builder, boolean useTransients) {
|
||||
if (clazz.isArray()) {
|
||||
reflectionAppendArray(object, clazz, builder);
|
||||
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;
|
||||
}
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
Field.setAccessible(fields, true);
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
Field f = fields[i];
|
||||
if ((f.getName().indexOf('$') == -1)
|
||||
&& (useTransients || !Modifier.isTransient(f.getModifiers()))
|
||||
&& (!Modifier.isStatic(f.getModifiers()))) {
|
||||
try {
|
||||
builder.append(f.getName(), f.get(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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the array elements in the given <code>Object</code> of the
|
||||
* given <code>Class</code> to a <code>ToStringBuilder</code>.
|
||||
*
|
||||
* @param object the array object to append details of
|
||||
* @param clazz the array class of the object parameter
|
||||
* @param builder the builder to append to
|
||||
*/
|
||||
private static void reflectionAppendArray(Object object, Class clazz, ToStringBuilder builder) {
|
||||
try {
|
||||
// A multi-dimension array invokes the append(Object) method.
|
||||
// A single-dimension array of primitive type pt invokes the append(pt[]) method.
|
||||
builder.getClass().getDeclaredMethod("append", new Class[] { clazz.getComponentType().isArray() ? Object.class : clazz }).invoke(
|
||||
builder,
|
||||
new Object[] { object });
|
||||
} catch (SecurityException e) {
|
||||
// "This cannot happen"
|
||||
throw new InternalError("Unexpected SecurityException: " + e.getMessage());
|
||||
} catch (NoSuchMethodException e) {
|
||||
// "This cannot happen"
|
||||
throw new InternalError("Unexpected NoSuchMethodException: " + e.getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Method.invoke exception
|
||||
// "This cannot happen"
|
||||
throw new InternalError("Unexpected IllegalArgumentException: " + e.getMessage());
|
||||
} catch (IllegalAccessException e) {
|
||||
// Method.invoke exception
|
||||
// "This cannot happen"
|
||||
throw new InternalError("Unexpected IllegalAccessException: " + e.getMessage());
|
||||
} catch (InvocationTargetException e) {
|
||||
// Method.invoke exception
|
||||
// "This cannot happen"
|
||||
throw new InternalError("Unexpected InvocationTargetException: " + e.getMessage());
|
||||
} finally {
|
||||
unregister(object);
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,6 +523,18 @@ public ToStringBuilder appendToString(String toString) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Appends with the same format as the default <code>Object toString()
|
||||
* </code> method. Appends the class name followed by
|
||||
* {@link System#identityHashCode(java.lang.Object)}.</p>
|
||||
*
|
||||
* @param object the <code>Object</code> whose class name and id to output
|
||||
*/
|
||||
public ToStringBuilder appendAsObjectToString(Object object) {
|
||||
this.getStyle().appendAsObjectToString(this.getStringBuffer(), object);
|
||||
return this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@ -757,6 +807,18 @@ public ToStringBuilder append(Object[] array) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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>Append to the <code>toString</code> an <code>Object</code>
|
||||
* array.</p>
|
||||
|
@ -54,6 +54,7 @@
|
||||
package org.apache.commons.lang.builder;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@ -81,7 +82,7 @@
|
||||
*
|
||||
* @author Stephen Colebourne
|
||||
* @since 1.0
|
||||
* @version $Id: ToStringStyle.java,v 1.10 2003/03/23 17:54:16 scolebourne Exp $
|
||||
* @version $Id: ToStringStyle.java,v 1.11 2003/03/27 08:54:31 ggregory Exp $
|
||||
*/
|
||||
public abstract class ToStringStyle implements Serializable {
|
||||
|
||||
@ -319,6 +320,8 @@ public void append(StringBuffer buffer, String fieldName, Object value, Boolean
|
||||
*
|
||||
* <p>Either detail or summary views can be specified.</p>
|
||||
*
|
||||
* <p>If a cycle is detected, an object will be appended with the Object.toString() format.</p>
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param fieldName the field name, typically not used as already appended
|
||||
* @param value the value to add to the <code>toString</code>,
|
||||
@ -326,7 +329,12 @@ public void append(StringBuffer buffer, String fieldName, Object value, Boolean
|
||||
* @param detail output detail or not
|
||||
*/
|
||||
protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) {
|
||||
if (value instanceof Collection) {
|
||||
if (ToStringBuilder.isRegistered(value)
|
||||
&& !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
|
||||
appendAsObjectToString(buffer, value);
|
||||
|
||||
}
|
||||
else if (value instanceof Collection) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (Collection) value);
|
||||
} else {
|
||||
@ -742,6 +750,32 @@ protected void appendDetail(StringBuffer buffer, String fieldName, Object[] arra
|
||||
buffer.append(arrayEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the detail of an any array type.</p>
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param fieldName the field name, typically not used as already appended
|
||||
* @param array the array to add to the <code>toString</code>,
|
||||
* not <code>null</code>
|
||||
*/
|
||||
protected void reflectionAppendArrayDetail(StringBuffer buffer, String fieldName, Object array) {
|
||||
buffer.append(arrayStart);
|
||||
int length = Array.getLength(array);
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object item = Array.get(array, i);
|
||||
if (i > 0) {
|
||||
buffer.append(arraySeparator);
|
||||
}
|
||||
if (item == null) {
|
||||
appendNullText(buffer, fieldName);
|
||||
|
||||
} else {
|
||||
appendInternal(buffer, fieldName, item, arrayContentDetail);
|
||||
}
|
||||
}
|
||||
buffer.append(arrayEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> a summary of an
|
||||
* <code>Object</code> array.</p>
|
||||
@ -1274,6 +1308,19 @@ protected void appendIdentityHashCode(StringBuffer buffer, Object object) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Appends with the same format as the default <code>Object toString()
|
||||
* </code> method. Appends the class name followed by
|
||||
* {@link System#identityHashCode(java.lang.Object)}.</p>
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param object the <code>Object</code> whose class name and id to output
|
||||
*/
|
||||
protected void appendAsObjectToString(StringBuffer buffer, Object object) {
|
||||
this.appendClassName(buffer, object);
|
||||
this.appendIdentityHashCode(buffer, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Append the content start to the buffer.</p>
|
||||
*
|
||||
|
@ -63,10 +63,12 @@
|
||||
import junit.framework.TestSuite;
|
||||
import junit.textui.TestRunner;
|
||||
/**
|
||||
* Unit tests {@link org.apache.commons.lang.ToStringBuilder}.
|
||||
* Unit tests for {@link org.apache.commons.lang.ToStringBuilder}.
|
||||
*
|
||||
* @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
|
||||
* @version $Id: ToStringBuilderTest.java,v 1.6 2003/03/23 17:35:51 scolebourne Exp $
|
||||
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
|
||||
* @author <a href="mailto:alex@apache.org">Alex Chaffee</a>
|
||||
* @version $Id: ToStringBuilderTest.java,v 1.7 2003/03/27 08:55:22 ggregory Exp $
|
||||
*/
|
||||
public class ToStringBuilderTest extends TestCase {
|
||||
|
||||
@ -164,10 +166,37 @@ public void testBlank() {
|
||||
assertEquals(baseStr + "[]", new ToStringBuilder(base).toString());
|
||||
}
|
||||
|
||||
public void testReflection() {
|
||||
assertEquals(baseStr + "[value=5]", ToStringBuilder.reflectionToString(base));
|
||||
}
|
||||
/**
|
||||
* Test wrapper for int primitive.
|
||||
*/
|
||||
public void testReflectionInteger() {
|
||||
assertEquals(baseStr + "[value=5]", ToStringBuilder.reflectionToString(base));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test wrapper for char primitive.
|
||||
*/
|
||||
public void testReflectionCharacter() {
|
||||
Character c = new Character('A');
|
||||
assertEquals(this.toBaseString(c) + "[value=A]", ToStringBuilder.reflectionToString(c));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test wrapper for char boolean.
|
||||
*/
|
||||
public void testReflectionBoolean() {
|
||||
Boolean b;
|
||||
b = Boolean.TRUE;
|
||||
assertEquals(this.toBaseString(b) + "[value=true]", ToStringBuilder.reflectionToString(b));
|
||||
b = Boolean.FALSE;
|
||||
assertEquals(this.toBaseString(b) + "[value=false]", ToStringBuilder.reflectionToString(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the same toString() as Object.toString().
|
||||
* @param o the object to create the string for.
|
||||
* @return a String in the Object.toString format.
|
||||
*/
|
||||
private String toBaseString(Object o) {
|
||||
return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o));
|
||||
}
|
||||
@ -200,6 +229,7 @@ public void testReflectionObjectArray() {
|
||||
assertEquals(baseStr + "[{<null>,5,{3,6}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionLongArray() {
|
||||
@ -208,6 +238,7 @@ public void testReflectionLongArray() {
|
||||
assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionIntArray() {
|
||||
@ -216,6 +247,7 @@ public void testReflectionIntArray() {
|
||||
assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionShortArray() {
|
||||
@ -224,6 +256,7 @@ public void testReflectionShortArray() {
|
||||
assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionyteArray() {
|
||||
@ -232,6 +265,7 @@ public void testReflectionyteArray() {
|
||||
assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionCharArray() {
|
||||
@ -240,6 +274,7 @@ public void testReflectionCharArray() {
|
||||
assertEquals(baseStr + "[{A,2,_,D}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionDoubleArray() {
|
||||
@ -248,6 +283,7 @@ public void testReflectionDoubleArray() {
|
||||
assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionFloatArray() {
|
||||
@ -256,6 +292,7 @@ public void testReflectionFloatArray() {
|
||||
assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionBooleanArray() {
|
||||
@ -264,6 +301,7 @@ public void testReflectionBooleanArray() {
|
||||
assertEquals(baseStr + "[{true,false,false}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
// Reflection Array Array tests
|
||||
@ -274,6 +312,7 @@ public void testReflectionFloatArrayArray() {
|
||||
assertEquals(baseStr + "[{{1.0,2.29686},<null>,{NaN}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
|
||||
@ -283,6 +322,7 @@ public void testReflectionLongArrayArray() {
|
||||
assertEquals(baseStr + "[{{1,2},<null>,{5}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionIntArrayArray() {
|
||||
@ -291,6 +331,7 @@ public void testReflectionIntArrayArray() {
|
||||
assertEquals(baseStr + "[{{1,2},<null>,{5}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionhortArrayArray() {
|
||||
@ -299,6 +340,7 @@ public void testReflectionhortArrayArray() {
|
||||
assertEquals(baseStr + "[{{1,2},<null>,{5}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionByteArrayArray() {
|
||||
@ -307,6 +349,7 @@ public void testReflectionByteArrayArray() {
|
||||
assertEquals(baseStr + "[{{1,2},<null>,{5}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionCharArrayArray() {
|
||||
@ -315,6 +358,7 @@ public void testReflectionCharArrayArray() {
|
||||
assertEquals(baseStr + "[{{A,B},<null>,{p}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionDoubleArrayArray() {
|
||||
@ -323,6 +367,7 @@ public void testReflectionDoubleArrayArray() {
|
||||
assertEquals(baseStr + "[{{1.0,2.29686},<null>,{NaN}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionBooleanArrayArray() {
|
||||
@ -332,6 +377,7 @@ public void testReflectionBooleanArrayArray() {
|
||||
assertEquals(baseStr + "[{{true,false},<null>,{false}}]", ToStringBuilder.reflectionToString(array));
|
||||
array = null;
|
||||
assertReflectionArray("<null>", array);
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
// Reflection hierarchy tests
|
||||
@ -341,6 +387,7 @@ public void testReflectionHierarchyArrayList() {
|
||||
String baseStr = this.toBaseString(base);
|
||||
assertEquals(baseStr + "[elementData={<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>},size=0,modCount=0]", ToStringBuilder.reflectionToString(base, null, true));
|
||||
assertEquals(baseStr + "[size=0]", ToStringBuilder.reflectionToString(base, null, false));
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionHierarchy() {
|
||||
@ -367,6 +414,7 @@ public void testReflectionHierarchy() {
|
||||
assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB, null, false, List.class));
|
||||
assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB, null, false, ReflectionTestFixtureA.class));
|
||||
assertEquals(baseStr + "[b=b]", ToStringBuilder.reflectionToString(baseB, null, false, ReflectionTestFixtureB.class));
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
static class ReflectionTestFixtureA {
|
||||
@ -396,6 +444,161 @@ public String toString() {
|
||||
}
|
||||
}
|
||||
|
||||
// Reflection cycle tests
|
||||
|
||||
/**
|
||||
* Test an array element pointing to its container.
|
||||
*/
|
||||
public void testReflectionArrayCycle() throws Exception {
|
||||
Object[] objects = new Object[1];
|
||||
objects[0] = objects;
|
||||
assertEquals(
|
||||
this.toBaseString(objects) + "[{" + this.toBaseString(objects) + "}]",
|
||||
ToStringBuilder.reflectionToString(objects));
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an array element pointing to its container.
|
||||
*/
|
||||
public void testReflectionArrayCycleLevel2() throws Exception {
|
||||
Object[] objects = new Object[1];
|
||||
Object[] objectsLevel2 = new Object[1];
|
||||
objects[0] = objectsLevel2;
|
||||
objectsLevel2[0] = (Object) objects;
|
||||
assertEquals(
|
||||
this.toBaseString(objects) + "[{{" + this.toBaseString(objects) + "}}]",
|
||||
ToStringBuilder.reflectionToString(objects));
|
||||
assertEquals(
|
||||
this.toBaseString(objectsLevel2) + "[{{" + this.toBaseString(objectsLevel2) + "}}]",
|
||||
ToStringBuilder.reflectionToString(objectsLevel2));
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
public void testReflectionArrayArrayCycle() throws Exception {
|
||||
Object[][] objects = new Object[2][2];
|
||||
objects[0][0] = objects;
|
||||
objects[0][1] = objects;
|
||||
objects[1][0] = objects;
|
||||
objects[1][1] = objects;
|
||||
String basicToString = this.toBaseString(objects);
|
||||
assertEquals(
|
||||
basicToString
|
||||
+ "[{{"
|
||||
+ basicToString
|
||||
+ ","
|
||||
+ basicToString
|
||||
+ "},{"
|
||||
+ basicToString
|
||||
+ ","
|
||||
+ basicToString
|
||||
+ "}}]",
|
||||
ToStringBuilder.reflectionToString(objects));
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reflection test fixture.
|
||||
*/
|
||||
static class ReflectionTestCycleA {
|
||||
ReflectionTestCycleB b;
|
||||
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reflection test fixture.
|
||||
*/
|
||||
static class ReflectionTestCycleB {
|
||||
ReflectionTestCycleA a;
|
||||
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reflection test fixture.
|
||||
*/
|
||||
static class SimpleReflectionTestFixture {
|
||||
Object o;
|
||||
|
||||
public SimpleReflectionTestFixture() {
|
||||
}
|
||||
|
||||
public SimpleReflectionTestFixture(Object o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an Object pointing to itself, the simplest test.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testSimpleReflectionObjectCycle() throws Exception {
|
||||
SimpleReflectionTestFixture simple = new SimpleReflectionTestFixture();
|
||||
simple.o = simple;
|
||||
assertTrue(ToStringBuilder.getReflectionRegistry().isEmpty());
|
||||
assertEquals(this.toBaseString(simple) + "[o=" + this.toBaseString(simple) + "]", simple.toString());
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Objects pointing to each other.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testReflectionObjectCycle() throws Exception {
|
||||
ReflectionTestCycleA a = new ReflectionTestCycleA();
|
||||
ReflectionTestCycleB b = new ReflectionTestCycleB();
|
||||
a.b = b;
|
||||
b.a = a;
|
||||
assertEquals(
|
||||
this.toBaseString(a) + "[b=" + this.toBaseString(b) + "[a=" + this.toBaseString(a) + "]]",
|
||||
a.toString());
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a nasty combination of arrays and Objects pointing to each other.
|
||||
* objects[0] -> SimpleReflectionTestFixture[ o -> objects ]
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testReflectionArrayAndObjectCycle() throws Exception {
|
||||
Object[] objects = new Object[1];
|
||||
SimpleReflectionTestFixture simple = new SimpleReflectionTestFixture(objects);
|
||||
objects[0] = (Object) simple;
|
||||
assertEquals(
|
||||
this.toBaseString(objects)
|
||||
+ "[{"
|
||||
+ this.toBaseString(simple)
|
||||
+ "[o="
|
||||
+ this.toBaseString(objects)
|
||||
+ "]"
|
||||
+ "}]",
|
||||
ToStringBuilder.reflectionToString(objects));
|
||||
assertEquals(
|
||||
this.toBaseString(simple)
|
||||
+ "[o={"
|
||||
+ this.toBaseString(simple)
|
||||
+ "}]",
|
||||
ToStringBuilder.reflectionToString(simple));
|
||||
this.validateEmptyReflectionRegistry();
|
||||
}
|
||||
|
||||
void validateEmptyReflectionRegistry() {
|
||||
assertTrue(ToStringBuilder.getReflectionRegistry().isEmpty());
|
||||
}
|
||||
// End: Reflection cycle tests
|
||||
|
||||
public void testAppendSuper() {
|
||||
assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString());
|
||||
assertEquals(baseStr + "[<null>]", new ToStringBuilder(base).appendSuper("Integer@8888[<null>]").toString());
|
||||
|
Loading…
x
Reference in New Issue
Block a user