[LANG-586] clear ThreadLocal recursion registry (compatibly with existing tests, first pass)
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@906673 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c98d0840bc
commit
5c3ec55e15
|
@ -5,9 +5,9 @@
|
|||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -19,9 +19,10 @@ package org.apache.commons.lang3.builder;
|
|||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
@ -46,7 +47,7 @@ import org.apache.commons.lang3.SystemUtils;
|
|||
* <p>For example, the detail version of the array based methods will
|
||||
* output the whole array, whereas the summary method will just output
|
||||
* the array length.</p>
|
||||
*
|
||||
*
|
||||
* <p>If you want to format the output of certain objects, such as dates, you
|
||||
* must create a subclass and override a method.
|
||||
* <pre>
|
||||
|
@ -73,17 +74,17 @@ public abstract class ToStringStyle implements Serializable {
|
|||
/**
|
||||
* The default toString style. Using the Using the <code>Person</code>
|
||||
* example from {@link ToStringBuilder}, the output would look like this:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* Person@182f0db[name=John Doe,age=33,smoker=false]
|
||||
* </pre>
|
||||
*/
|
||||
public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
|
||||
|
||||
|
||||
/**
|
||||
* The multi line toString style. Using the Using the <code>Person</code>
|
||||
* example from {@link ToStringBuilder}, the output would look like this:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* Person@182f0db[
|
||||
* name=John Doe
|
||||
|
@ -93,26 +94,26 @@ public abstract class ToStringStyle implements Serializable {
|
|||
* </pre>
|
||||
*/
|
||||
public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
|
||||
|
||||
|
||||
/**
|
||||
* The no field names toString style. Using the Using the
|
||||
* <code>Person</code> example from {@link ToStringBuilder}, the output
|
||||
* would look like this:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* Person@182f0db[John Doe,33,false]
|
||||
* </pre>
|
||||
*/
|
||||
public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
|
||||
|
||||
|
||||
/**
|
||||
* The short prefix toString style. Using the <code>Person</code> example
|
||||
* from {@link ToStringBuilder}, the output would look like this:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* Person[name=John Doe,age=33,smoker=false]
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
|
||||
|
@ -120,38 +121,32 @@ public abstract class ToStringStyle implements Serializable {
|
|||
/**
|
||||
* The simple toString style. Using the Using the <code>Person</code>
|
||||
* example from {@link ToStringBuilder}, the output would look like this:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* John Doe,33,false
|
||||
* </pre>
|
||||
*/
|
||||
public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A registry of objects used by <code>reflectionToString</code> methods
|
||||
* to detect cyclical object references and avoid infinite loops.
|
||||
* </p>
|
||||
*/
|
||||
private static final ThreadLocal<Set<Object>> registry = new ThreadLocal<Set<Object>>() {
|
||||
@Override
|
||||
protected Set<Object> initialValue() {
|
||||
// The HashSet implementation is not synchronized,
|
||||
// which is just what we need here.
|
||||
return new HashSet<Object>();
|
||||
}
|
||||
};
|
||||
private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY = new ThreadLocal<WeakHashMap<Object,Object>>();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the registry of objects being traversed by the <code>reflectionToString</code>
|
||||
* methods in the current thread.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return Set the registry of objects being traversed
|
||||
*/
|
||||
static Set<Object> getRegistry() {
|
||||
return registry.get();
|
||||
WeakHashMap<Object, Object> m = REGISTRY.get();
|
||||
return m == null ? Collections.<Object> emptySet() : m.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +154,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
* Returns <code>true</code> if the registry contains the given object.
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param value
|
||||
* The object to lookup in the registry.
|
||||
* @return boolean <code>true</code> if the registry contains the given
|
||||
|
@ -174,13 +169,21 @@ public abstract class ToStringStyle implements Serializable {
|
|||
* Registers the given object. Used by the reflection methods to avoid
|
||||
* infinite loops.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param value
|
||||
* The object to register.
|
||||
*/
|
||||
static void register(Object value) {
|
||||
if (value != null) {
|
||||
getRegistry().add(value);
|
||||
WeakHashMap<Object, Object> m;
|
||||
synchronized (ToStringStyle.class) {
|
||||
m = REGISTRY.get();
|
||||
if (m == null) {
|
||||
m = new WeakHashMap<Object, Object>();
|
||||
REGISTRY.set(m);
|
||||
}
|
||||
}
|
||||
m.put(value, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,33 +191,44 @@ public abstract class ToStringStyle implements Serializable {
|
|||
* <p>
|
||||
* Unregisters the given object.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param value
|
||||
* The object to unregister.
|
||||
*/
|
||||
static void unregister(Object value) {
|
||||
getRegistry().remove(value);
|
||||
if (value != null) {
|
||||
WeakHashMap<Object, Object> m;
|
||||
synchronized (ToStringStyle.class) {
|
||||
m = REGISTRY.get();
|
||||
if (m != null) {
|
||||
m.remove(value);
|
||||
if (m.isEmpty()) {
|
||||
REGISTRY.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use the field names, the default is <code>true</code>.
|
||||
*/
|
||||
private boolean useFieldNames = true;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use the class name, the default is <code>true</code>.
|
||||
*/
|
||||
private boolean useClassName = true;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use short class names, the default is <code>false</code>.
|
||||
*/
|
||||
private boolean useShortClassName = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use the identity hash code, the default is <code>true</code>.
|
||||
*/
|
||||
|
@ -224,78 +238,78 @@ public abstract class ToStringStyle implements Serializable {
|
|||
* The content start <code>'['</code>.
|
||||
*/
|
||||
private String contentStart = "[";
|
||||
|
||||
|
||||
/**
|
||||
* The content end <code>']'</code>.
|
||||
*/
|
||||
private String contentEnd = "]";
|
||||
|
||||
|
||||
/**
|
||||
* The field name value separator <code>'='</code>.
|
||||
*/
|
||||
private String fieldNameValueSeparator = "=";
|
||||
|
||||
|
||||
/**
|
||||
* Whether the field separator should be added before any other fields.
|
||||
*/
|
||||
private boolean fieldSeparatorAtStart = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the field separator should be added after any other fields.
|
||||
*/
|
||||
private boolean fieldSeparatorAtEnd = false;
|
||||
|
||||
|
||||
/**
|
||||
* The field separator <code>','</code>.
|
||||
*/
|
||||
private String fieldSeparator = ",";
|
||||
|
||||
|
||||
/**
|
||||
* The array start <code>'{'</code>.
|
||||
*/
|
||||
private String arrayStart = "{";
|
||||
|
||||
|
||||
/**
|
||||
* The array separator <code>','</code>.
|
||||
*/
|
||||
private String arraySeparator = ",";
|
||||
|
||||
|
||||
/**
|
||||
* The detail for array content.
|
||||
*/
|
||||
private boolean arrayContentDetail = true;
|
||||
|
||||
|
||||
/**
|
||||
* The array end <code>'}'</code>.
|
||||
*/
|
||||
private String arrayEnd = "}";
|
||||
|
||||
|
||||
/**
|
||||
* The value to use when fullDetail is <code>null</code>,
|
||||
* the default value is <code>true</code>.
|
||||
*/
|
||||
private boolean defaultFullDetail = true;
|
||||
|
||||
|
||||
/**
|
||||
* The <code>null</code> text <code>'<null>'</code>.
|
||||
*/
|
||||
private String nullText = "<null>";
|
||||
|
||||
|
||||
/**
|
||||
* The summary size text start <code>'<size'</code>.
|
||||
*/
|
||||
private String sizeStartText = "<size=";
|
||||
|
||||
|
||||
/**
|
||||
* The summary size text start <code>'>'</code>.
|
||||
*/
|
||||
private String sizeEndText = ">";
|
||||
|
||||
|
||||
/**
|
||||
* The summary object text start <code>'<'</code>.
|
||||
*/
|
||||
private String summaryObjectStartText = "<";
|
||||
|
||||
|
||||
/**
|
||||
* The summary object text start <code>'>'</code>.
|
||||
*/
|
||||
|
@ -315,9 +329,9 @@ public abstract class ToStringStyle implements Serializable {
|
|||
/**
|
||||
* <p>Append to the <code>toString</code> the superclass toString.</p>
|
||||
* <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
|
||||
*
|
||||
*
|
||||
* <p>A <code>null</code> <code>superToString</code> is ignored.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param superToString the <code>super.toString()</code>
|
||||
* @since 2.0
|
||||
|
@ -329,9 +343,9 @@ public abstract class ToStringStyle implements Serializable {
|
|||
/**
|
||||
* <p>Append to the <code>toString</code> another toString.</p>
|
||||
* <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
|
||||
*
|
||||
*
|
||||
* <p>A <code>null</code> <code>toString</code> is ignored.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param toString the additional <code>toString</code>
|
||||
* @since 2.0
|
||||
|
@ -353,7 +367,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the start of data indicator.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param object the <code>Object</code> to build a <code>toString</code> for
|
||||
*/
|
||||
|
@ -370,7 +384,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the end of data indicator.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param object the <code>Object</code> to build a
|
||||
* <code>toString</code> for.
|
||||
|
@ -385,7 +399,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Remove the last field separator from the buffer.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @since 2.0
|
||||
*/
|
||||
|
@ -456,7 +470,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
&& !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
|
||||
appendCyclicObject(buffer, fieldName, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
register(value);
|
||||
|
||||
|
@ -467,77 +481,77 @@ public abstract class ToStringStyle implements Serializable {
|
|||
} else {
|
||||
appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof Map<?, ?>) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (Map<?, ?>) value);
|
||||
} else {
|
||||
appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof long[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (long[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (long[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof int[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (int[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (int[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof short[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (short[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (short[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof byte[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (byte[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (byte[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof char[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (char[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (char[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof double[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (double[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (double[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof float[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (float[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (float[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value instanceof boolean[]) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (boolean[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (boolean[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else if (value.getClass().isArray()) {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, (Object[]) value);
|
||||
} else {
|
||||
appendSummary(buffer, fieldName, (Object[]) value);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
if (detail) {
|
||||
appendDetail(buffer, fieldName, value);
|
||||
|
@ -549,17 +563,17 @@ public abstract class ToStringStyle implements Serializable {
|
|||
unregister(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> an <code>Object</code>
|
||||
* value that has been detected to participate in a cycle. This
|
||||
* implementation will print the standard string value of the value.</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>,
|
||||
* not <code>null</code>
|
||||
*
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) {
|
||||
|
@ -1428,7 +1442,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the class name.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param object the <code>Object</code> whose name to output
|
||||
*/
|
||||
|
@ -1445,7 +1459,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append the {@link System#identityHashCode(java.lang.Object)}.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param object the <code>Object</code> whose id to output
|
||||
*/
|
||||
|
@ -1459,7 +1473,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the content start.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
*/
|
||||
protected void appendContentStart(StringBuffer buffer) {
|
||||
|
@ -1468,7 +1482,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the content end.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
*/
|
||||
protected void appendContentEnd(StringBuffer buffer) {
|
||||
|
@ -1479,7 +1493,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
* <p>Append to the <code>toString</code> an indicator for <code>null</code>.</p>
|
||||
*
|
||||
* <p>The default indicator is <code>'<null>'</code>.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param fieldName the field name, typically not used as already appended
|
||||
*/
|
||||
|
@ -1489,7 +1503,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the field separator.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
*/
|
||||
protected void appendFieldSeparator(StringBuffer buffer) {
|
||||
|
@ -1498,7 +1512,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString</code> the field start.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param fieldName the field name
|
||||
*/
|
||||
|
@ -1511,7 +1525,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* <p>Append to the <code>toString<code> the field end.</p>
|
||||
*
|
||||
*
|
||||
* @param buffer the <code>StringBuffer</code> to populate
|
||||
* @param fieldName the field name, typically not used as already appended
|
||||
*/
|
||||
|
@ -1550,7 +1564,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
* <code>null</code> indicating that it doesn't care about
|
||||
* the detail level. In this case the default detail level is
|
||||
* used.</p>
|
||||
*
|
||||
*
|
||||
* @param fullDetailRequest the detail level requested
|
||||
* @return whether full detail is to be shown
|
||||
*/
|
||||
|
@ -1886,9 +1900,9 @@ public abstract class ToStringStyle implements Serializable {
|
|||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether the field separator should be added at the start
|
||||
* <p>Gets whether the field separator should be added at the start
|
||||
* of each buffer.</p>
|
||||
*
|
||||
*
|
||||
* @return the fieldSeparatorAtStart flag
|
||||
* @since 2.0
|
||||
*/
|
||||
|
@ -1897,9 +1911,9 @@ public abstract class ToStringStyle implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether the field separator should be added at the start
|
||||
* <p>Sets whether the field separator should be added at the start
|
||||
* of each buffer.</p>
|
||||
*
|
||||
*
|
||||
* @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
|
||||
* @since 2.0
|
||||
*/
|
||||
|
@ -1910,9 +1924,9 @@ public abstract class ToStringStyle implements Serializable {
|
|||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether the field separator should be added at the end
|
||||
* <p>Gets whether the field separator should be added at the end
|
||||
* of each buffer.</p>
|
||||
*
|
||||
*
|
||||
* @return fieldSeparatorAtEnd flag
|
||||
* @since 2.0
|
||||
*/
|
||||
|
@ -1921,9 +1935,9 @@ public abstract class ToStringStyle implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether the field separator should be added at the end
|
||||
* <p>Sets whether the field separator should be added at the end
|
||||
* of each buffer.</p>
|
||||
*
|
||||
*
|
||||
* @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
|
||||
* @since 2.0
|
||||
*/
|
||||
|
@ -2097,7 +2111,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
|
||||
/**
|
||||
* Required for serialization support.
|
||||
*
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -2157,7 +2171,7 @@ public abstract class ToStringStyle implements Serializable {
|
|||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* <p><code>ToStringStyle</code> that prints out the short
|
||||
* class name and no identity hashcode.</p>
|
||||
|
|
Loading…
Reference in New Issue