From 5c3ec55e15922c58bb2f39145de9fe641840bb50 Mon Sep 17 00:00:00 2001 From: Matthew Jason Benson Date: Thu, 4 Feb 2010 21:46:22 +0000 Subject: [PATCH] [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 --- .../commons/lang3/builder/ToStringStyle.java | 192 ++++++++++-------- 1 file changed, 103 insertions(+), 89 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java b/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java index 9f2096bb7..95b580bbd 100644 --- a/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java +++ b/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java @@ -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 @@ *

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.

- * + * *

If you want to format the output of certain objects, such as dates, you * must create a subclass and override a method. *

@@ -73,17 +74,17 @@ public abstract class ToStringStyle implements Serializable {
     /**
      * The default toString style. Using the Using the Person
      * example from {@link ToStringBuilder}, the output would look like this:
-     * 
+     *
      * 
      * Person@182f0db[name=John Doe,age=33,smoker=false]
      * 
*/ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle(); - + /** * The multi line toString style. Using the Using the Person * example from {@link ToStringBuilder}, the output would look like this: - * + * *
      * Person@182f0db[
      *   name=John Doe
@@ -93,26 +94,26 @@ public abstract class ToStringStyle implements Serializable {
      * 
*/ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle(); - + /** * The no field names toString style. Using the Using the * Person example from {@link ToStringBuilder}, the output * would look like this: - * + * *
      * Person@182f0db[John Doe,33,false]
      * 
*/ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle(); - + /** * The short prefix toString style. Using the Person example * from {@link ToStringBuilder}, the output would look like this: - * + * *
      * Person[name=John Doe,age=33,smoker=false]
      * 
- * + * * @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 Person * example from {@link ToStringBuilder}, the output would look like this: - * + * *
      * John Doe,33,false
      * 
*/ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle(); - + /** *

* A registry of objects used by reflectionToString methods * to detect cyclical object references and avoid infinite loops. *

*/ - private static final ThreadLocal> registry = new ThreadLocal>() { - @Override - protected Set initialValue() { - // The HashSet implementation is not synchronized, - // which is just what we need here. - return new HashSet(); - } - }; + private static final ThreadLocal> REGISTRY = new ThreadLocal>(); /** *

* Returns the registry of objects being traversed by the reflectionToString * methods in the current thread. *

- * + * * @return Set the registry of objects being traversed */ static Set getRegistry() { - return registry.get(); + WeakHashMap m = REGISTRY.get(); + return m == null ? Collections. emptySet() : m.keySet(); } /** @@ -159,7 +154,7 @@ static Set getRegistry() { * Returns true 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 true if the registry contains the given @@ -174,13 +169,21 @@ static boolean isRegistered(Object 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) { if (value != null) { - getRegistry().add(value); + WeakHashMap m; + synchronized (ToStringStyle.class) { + m = REGISTRY.get(); + if (m == null) { + m = new WeakHashMap(); + REGISTRY.set(m); + } + } + m.put(value, null); } } @@ -188,33 +191,44 @@ static void register(Object value) { *

* 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); + if (value != null) { + WeakHashMap 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 true. */ private boolean useFieldNames = true; - + /** * Whether to use the class name, the default is true. */ private boolean useClassName = true; - + /** * Whether to use short class names, the default is false. */ private boolean useShortClassName = false; - + /** * Whether to use the identity hash code, the default is true. */ @@ -224,78 +238,78 @@ static void unregister(Object value) { * The content start '['. */ private String contentStart = "["; - + /** * The content end ']'. */ private String contentEnd = "]"; - + /** * The field name value separator '='. */ 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 ','. */ private String fieldSeparator = ","; - + /** * The array start '{'. */ private String arrayStart = "{"; - + /** * The array separator ','. */ private String arraySeparator = ","; - + /** * The detail for array content. */ private boolean arrayContentDetail = true; - + /** * The array end '}'. */ private String arrayEnd = "}"; - + /** * The value to use when fullDetail is null, * the default value is true. */ private boolean defaultFullDetail = true; - + /** * The null text '<null>'. */ private String nullText = ""; - + /** * The summary size text start '. */ private String sizeStartText = "'>'. */ private String sizeEndText = ">"; - + /** * The summary object text start '<'. */ private String summaryObjectStartText = "<"; - + /** * The summary object text start '>'. */ @@ -315,9 +329,9 @@ protected ToStringStyle() { /** *

Append to the toString the superclass toString.

*

NOTE: It assumes that the toString has been created from the same ToStringStyle.

- * + * *

A null superToString is ignored.

- * + * * @param buffer the StringBuffer to populate * @param superToString the super.toString() * @since 2.0 @@ -329,9 +343,9 @@ public void appendSuper(StringBuffer buffer, String superToString) { /** *

Append to the toString another toString.

*

NOTE: It assumes that the toString has been created from the same ToStringStyle.

- * + * *

A null toString is ignored.

- * + * * @param buffer the StringBuffer to populate * @param toString the additional toString * @since 2.0 @@ -353,7 +367,7 @@ public void appendToString(StringBuffer buffer, String toString) { /** *

Append to the toString the start of data indicator.

- * + * * @param buffer the StringBuffer to populate * @param object the Object to build a toString for */ @@ -370,7 +384,7 @@ public void appendStart(StringBuffer buffer, Object object) { /** *

Append to the toString the end of data indicator.

- * + * * @param buffer the StringBuffer to populate * @param object the Object to build a * toString for. @@ -385,7 +399,7 @@ public void appendEnd(StringBuffer buffer, Object object) { /** *

Remove the last field separator from the buffer.

- * + * * @param buffer the StringBuffer to populate * @since 2.0 */ @@ -456,7 +470,7 @@ protected void appendInternal(StringBuffer buffer, String fieldName, Object valu && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) { appendCyclicObject(buffer, fieldName, value); return; - } + } register(value); @@ -467,77 +481,77 @@ protected void appendInternal(StringBuffer buffer, String fieldName, Object valu } 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 @@ protected void appendInternal(StringBuffer buffer, String fieldName, Object valu unregister(value); } } - + /** *

Append to the toString an Object * value that has been detected to participate in a cycle. This * implementation will print the standard string value of the value.

- * + * * @param buffer the StringBuffer to populate * @param fieldName the field name, typically not used as already appended * @param value the value to add to the toString, * not null - * + * * @since 2.2 */ protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) { @@ -1428,7 +1442,7 @@ protected void appendSummary(StringBuffer buffer, String fieldName, boolean[] ar /** *

Append to the toString the class name.

- * + * * @param buffer the StringBuffer to populate * @param object the Object whose name to output */ @@ -1445,7 +1459,7 @@ protected void appendClassName(StringBuffer buffer, Object object) { /** *

Append the {@link System#identityHashCode(java.lang.Object)}.

- * + * * @param buffer the StringBuffer to populate * @param object the Object whose id to output */ @@ -1459,7 +1473,7 @@ protected void appendIdentityHashCode(StringBuffer buffer, Object object) { /** *

Append to the toString the content start.

- * + * * @param buffer the StringBuffer to populate */ protected void appendContentStart(StringBuffer buffer) { @@ -1468,7 +1482,7 @@ protected void appendContentStart(StringBuffer buffer) { /** *

Append to the toString the content end.

- * + * * @param buffer the StringBuffer to populate */ protected void appendContentEnd(StringBuffer buffer) { @@ -1479,7 +1493,7 @@ protected void appendContentEnd(StringBuffer buffer) { *

Append to the toString an indicator for null.

* *

The default indicator is '<null>'.

- * + * * @param buffer the StringBuffer to populate * @param fieldName the field name, typically not used as already appended */ @@ -1489,7 +1503,7 @@ protected void appendNullText(StringBuffer buffer, String fieldName) { /** *

Append to the toString the field separator.

- * + * * @param buffer the StringBuffer to populate */ protected void appendFieldSeparator(StringBuffer buffer) { @@ -1498,7 +1512,7 @@ protected void appendFieldSeparator(StringBuffer buffer) { /** *

Append to the toString the field start.

- * + * * @param buffer the StringBuffer to populate * @param fieldName the field name */ @@ -1511,7 +1525,7 @@ protected void appendFieldStart(StringBuffer buffer, String fieldName) { /** *

Append to the toString the field end.

- * + * * @param buffer the StringBuffer to populate * @param fieldName the field name, typically not used as already appended */ @@ -1550,7 +1564,7 @@ protected void appendSummarySize(StringBuffer buffer, String fieldName, int size * null indicating that it doesn't care about * the detail level. In this case the default detail level is * used.

- * + * * @param fullDetailRequest the detail level requested * @return whether full detail is to be shown */ @@ -1886,9 +1900,9 @@ protected void setFieldSeparator(String fieldSeparator) { //--------------------------------------------------------------------- /** - *

Gets whether the field separator should be added at the start + *

Gets whether the field separator should be added at the start * of each buffer.

- * + * * @return the fieldSeparatorAtStart flag * @since 2.0 */ @@ -1897,9 +1911,9 @@ protected boolean isFieldSeparatorAtStart() { } /** - *

Sets whether the field separator should be added at the start + *

Sets whether the field separator should be added at the start * of each buffer.

- * + * * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag * @since 2.0 */ @@ -1910,9 +1924,9 @@ protected void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) { //--------------------------------------------------------------------- /** - *

Gets whether the field separator should be added at the end + *

Gets whether the field separator should be added at the end * of each buffer.

- * + * * @return fieldSeparatorAtEnd flag * @since 2.0 */ @@ -1921,9 +1935,9 @@ protected boolean isFieldSeparatorAtEnd() { } /** - *

Sets whether the field separator should be added at the end + *

Sets whether the field separator should be added at the end * of each buffer.

- * + * * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag * @since 2.0 */ @@ -2097,7 +2111,7 @@ private static final class DefaultToStringStyle extends ToStringStyle { /** * Required for serialization support. - * + * * @see java.io.Serializable */ private static final long serialVersionUID = 1L; @@ -2157,7 +2171,7 @@ private Object readResolve() { } //---------------------------------------------------------------------------- - + /** *

ToStringStyle that prints out the short * class name and no identity hashcode.