Author: mbenson
Date: Thu Feb 4 21:46:22 2010
New Revision: 906673
URL: http://svn.apache.org/viewvc?rev=906673&view=rev
Log:
[LANG-586] clear ThreadLocal recursion registry (compatibly with
existing tests, first pass)
Modified:
commons/proper/lang/trunk/src/main/java/org/apache/commons/
lang3/builder/ToStringStyle.java
Modified: commons/proper/lang/trunk/src/main/java/org/apache/
commons/lang3/builder/ToStringStyle.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/
main/java/org/apache/commons/lang3/builder/ToStringStyle.java?
rev=906673&r1=906672&r2=906673&view=diff
====================================================================
==========
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/
lang3/builder/ToStringStyle.java (original)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/
lang3/builder/ToStringStyle.java Thu Feb 4 21:46:22 2010
@@ -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 @@
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 @@
* <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 @@
/**
* The default toString style. Using the Using the
<code>Person</code>
* example from {...@link ToStringBuilder}, the output would
look like this:
- *
+ *
* <pre>
* per...@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>
* per...@182f0db[
* name=John Doe
@@ -93,26 +94,26 @@
* </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>
* per...@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 @@
/**
* 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 @@
* 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 @@
* 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 @@
* <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 @@
* 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 @@
/**
* <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 @@
/**
* <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 @@
/**
* <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 @@
/**
* <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 @@
/**
* <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 @@
&& !(value instanceof Number || value instanceof
Boolean || value instanceof Character)) {
appendCyclicObject(buffer, fieldName, value);
return;
- }
+ }
register(value);
@@ -467,77 +481,77 @@
} 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 @@
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 @@
/**
* <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 @@
/**
* <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 @@
/**
* <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 @@
/**
* <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 @@
* <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 @@
/**
* <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 @@
/**
* <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 @@
/**
* <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 @@
* <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 @@
//--------------------------------------------------------------
-------
/**
- * <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 @@
}
/**
- * <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 @@
//--------------------------------------------------------------
-------
/**
- * <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 @@
}
/**
- * <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 @@
/**
* Required for serialization support.
- *
+ *
* @see java.io.Serializable
*/
private static final long serialVersionUID = 1L;
@@ -2157,7 +2171,7 @@
}
//--------------------------------------------------------------
--------------
-
+
/**
* <p><code>ToStringStyle</code> that prints out the short
* class name and no identity hashcode.</p>