diff --git a/src/java/org/apache/commons/lang/builder/ReflectionToStringBuilder.java b/src/java/org/apache/commons/lang/builder/ReflectionToStringBuilder.java index 52e907303..07ba23428 100644 --- a/src/java/org/apache/commons/lang/builder/ReflectionToStringBuilder.java +++ b/src/java/org/apache/commons/lang/builder/ReflectionToStringBuilder.java @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.commons.lang.builder; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -29,11 +31,10 @@ *

* *

- * This class uses reflection to determine the fields to append. Because these - * fields are usually private, the class uses - * {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} - * to change the visibility of the fields. This will fail under a security - * manager, unless the appropriate permissions are set up correctly. + * This class uses reflection to determine the fields to append. Because these fields are usually private, the class + * uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to + * change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are + * set up correctly. *

* *

@@ -64,8 +65,8 @@ * *

*

- * For example, this method does not include the password - * field in the returned String: + * For example, this method does not include the password field in the returned + * String: *

* *
@@ -80,22 +81,21 @@
  * 
  * 
  * 

- * The exact format of the toString is determined by the - * {@link ToStringStyle} passed into the constructor. + * The exact format of the toString is determined by the {@link ToStringStyle} passed into the + * constructor. *

* * @author Gary Gregory * @author Stephen Colebourne * @author Pete Gieser * @since 2.0 - * @version $Id: ReflectionToStringBuilder.java,v 1.15 2003/12/02 19:11:58 - * ggregory Exp $ + * @version $Id$ */ public class ReflectionToStringBuilder extends ToStringBuilder { /** *

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

*/ private static ThreadLocal registry = new ThreadLocal() { @@ -108,8 +108,8 @@ protected synchronized Object initialValue() { /** *

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

* * @return Set the registry of objects being traversed @@ -120,14 +120,13 @@ static Set getRegistry() { /** *

- * Returns true if the registry contains the given object. - * Used by the reflection methods to avoid infinite loops. + * 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 - * object. + * The object to lookup in the registry. + * @return boolean true if the registry contains the given object. */ static boolean isRegistered(Object value) { return getRegistry().contains(value); @@ -135,12 +134,11 @@ static boolean isRegistered(Object value) { /** *

- * Registers the given object. Used by the reflection methods to avoid - * infinite loops. + * Registers the given object. Used by the reflection methods to avoid infinite loops. *

* * @param value - * The object to register. + * The object to register. */ static void register(Object value) { getRegistry().add(value); @@ -152,22 +150,21 @@ static void register(Object value) { *

* *

- * It uses AccessibleObject.setAccessible to gain access to - * private fields. This means that it will throw a security exception if - * run under a security manager, if the permissions are not set up - * correctly. It is also not as efficient as testing explicitly. + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. *

* *

- * Transient members will be not be included, as they are likely derived. - * Static fields will not be included. Superclass fields will be appended. + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. *

* * @param object - * the Object to be output + * the Object to be output * @return the String result * @throws IllegalArgumentException - * if the Object is null + * if the Object is null */ public static String toString(Object object) { return toString(object, null, false, false, null); @@ -179,30 +176,27 @@ public static String toString(Object object) { *

* *

- * It uses AccessibleObject.setAccessible to gain access to - * private fields. This means that it will throw a security exception if - * run under a security manager, if the permissions are not set up - * correctly. It is also not as efficient as testing explicitly. + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. *

* *

- * Transient members will be not be included, as they are likely derived. - * Static fields will not be included. Superclass fields will be appended. + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. *

* *

- * If the style is null, the default ToStringStyle - * is used. + * If the style is null, the default ToStringStyle is used. *

* * @param object - * the Object to be output + * the Object to be output * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @return the String result * @throws IllegalArgumentException - * if the Object or ToStringStyle is null + * if the Object or ToStringStyle is null */ public static String toString(Object object, ToStringStyle style) { return toString(object, style, false, false, null); @@ -214,16 +208,14 @@ public static String toString(Object object, ToStringStyle style) { *

* *

- * It uses AccessibleObject.setAccessible to gain access to - * private fields. This means that it will throw a security exception if - * run under a security manager, if the permissions are not set up - * correctly. It is also not as efficient as testing explicitly. + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. *

* *

- * If the outputTransients is true, - * transient members will be output, otherwise they are ignored, as they - * are likely derived fields, and not part of the value of the Object. + * If the outputTransients is true, transient members will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. *

* *

@@ -231,20 +223,18 @@ public static String toString(Object object, ToStringStyle style) { *

* *

- * If the style is null, the default ToStringStyle - * is used. + * If the style is null, the default ToStringStyle is used. *

* * @param object - * the Object to be output + * the Object to be output * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @param outputTransients - * whether to include transient fields + * whether to include transient fields * @return the String result * @throws IllegalArgumentException - * if the Object is null + * if the Object is null */ public static String toString(Object object, ToStringStyle style, boolean outputTransients) { return toString(object, style, outputTransients, false, null); @@ -256,21 +246,19 @@ public static String toString(Object object, ToStringStyle style, boolean output *

* *

- * It uses AccessibleObject.setAccessible to gain access to - * private fields. This means that it will throw a security exception if - * run under a security manager, if the permissions are not set up - * correctly. It is also not as efficient as testing explicitly. + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. *

* *

- * If the outputTransients is true, - * transient fields will be output, otherwise they are ignored, as they are - * likely derived fields, and not part of the value of the Object. + * If the outputTransients is true, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. *

* *

- * If the outputStatics is true, static - * fields will be output, otherwise they are ignored. + * If the outputStatics is true, static fields will be output, otherwise they are + * ignored. *

* *

@@ -278,22 +266,20 @@ public static String toString(Object object, ToStringStyle style, boolean output *

* *

- * If the style is null, the default ToStringStyle - * is used. + * If the style is null, the default ToStringStyle is used. *

* * @param object - * the Object to be output + * the Object to be output * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @param outputTransients - * whether to include transient fields + * whether to include transient fields * @param outputStatics - * whether to include transient fields + * whether to include transient fields * @return the String result * @throws IllegalArgumentException - * if the Object is null + * if the Object is null * @since 2.1 */ public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) { @@ -306,47 +292,43 @@ public static String toString(Object object, ToStringStyle style, boolean output *

* *

- * It uses AccessibleObject.setAccessible to gain access to - * private fields. This means that it will throw a security exception if - * run under a security manager, if the permissions are not set up - * correctly. It is also not as efficient as testing explicitly. + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. *

* *

- * If the outputTransients is true, - * transient fields will be output, otherwise they are ignored, as they are - * likely derived fields, and not part of the value of the Object. + * If the outputTransients is true, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. *

* *

- * If the outputStatics is true, static - * fields will be output, otherwise they are ignored. + * If the outputStatics is true, static fields will be output, otherwise they are + * ignored. *

* *

- * Superclass fields will be appended up to and including the specified - * superclass. A null superclass is treated as java.lang.Object. + * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as + * java.lang.Object. *

* *

- * If the style is null, the default ToStringStyle - * is used. + * If the style is null, the default ToStringStyle is used. *

* * @param object - * the Object to be output + * the Object to be output * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @param outputTransients - * whether to include transient fields + * whether to include transient fields * @param outputStatics - * whether to include static fields + * whether to include static fields * @param reflectUpToClass - * the superclass to reflect up to (inclusive), may be null + * the superclass to reflect up to (inclusive), may be null * @return the String result * @throws IllegalArgumentException - * if the Object is null + * if the Object is null * @since 2.1 */ public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics, @@ -361,51 +343,70 @@ public static String toString(Object object, ToStringStyle style, boolean output *

* *

- * It uses AccessibleObject.setAccessible to gain access to - * private fields. This means that it will throw a security exception if - * run under a security manager, if the permissions are not set up - * correctly. It is also not as efficient as testing explicitly. + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. *

* *

- * If the outputTransients is true, - * transient members will be output, otherwise they are ignored, as they - * are likely derived fields, and not part of the value of the Object. + * If the outputTransients is true, transient members will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. *

* *

- * 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. + * 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. *

* *

- * If the style is null, the default ToStringStyle - * is used. + * If the style is null, the default ToStringStyle is used. *

* - * @deprecated Use - * {@link #toString(Object,ToStringStyle,boolean,boolean,Class)} + * @deprecated Use {@link #toString(Object,ToStringStyle,boolean,boolean,Class)} * * @param object - * the Object to be output + * the Object to be output * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @param outputTransients - * whether to include transient fields + * whether to include transient fields * @param reflectUpToClass - * the superclass to reflect up to (inclusive), may be null + * the superclass to reflect up to (inclusive), may be null * @return the String result * @throws IllegalArgumentException - * if the Object is null + * if the Object is null * @since 2.0 */ - public static String toString(Object object, ToStringStyle style, boolean outputTransients, - Class reflectUpToClass) { + public static String toString(Object object, ToStringStyle style, boolean outputTransients, Class reflectUpToClass) { return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients).toString(); } + /** + * Builds a String for a toString method excluding the given field name. + * + * @param object + * The object to "toString". + * @param excludeFieldName + * The field name to exclude + * @return The toString value. + */ + public static String toStringExclude(Object object, final String excludeFieldName) { + return toStringExclude(object, new String[]{excludeFieldName}); + } + + /** + * Builds a String for a toString method excluding the given field name. + * + * @param object + * The object to "toString". + * @param excludeFieldNames + * The field names to exclude + * @return The toString value. + */ + public static String toStringExclude(Object object, String[] excludeFieldNames) { + return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString(); + } + /** *

* Unregisters the given object. @@ -416,7 +417,7 @@ public static String toString(Object object, ToStringStyle style, boolean output *

* * @param value - * The object to unregister. + * The object to unregister. */ static void unregister(Object value) { getRegistry().remove(value); @@ -432,6 +433,11 @@ static void unregister(Object value) { */ private boolean appendTransients = false; + /** + * Which field names to exclude from output. Intended for fields like "password". + */ + private String[] excludeFieldNames; + /** * The last super class to stop appending fields for. */ @@ -447,10 +453,9 @@ static void unregister(Object value) { *

* * @param object - * the Object to build a toString for, must not - * be null + * the Object to build a toString for, must not be null * @throws IllegalArgumentException - * if the Object passed in is null + * if the Object passed in is null */ public ReflectionToStringBuilder(Object object) { super(object); @@ -466,13 +471,11 @@ public ReflectionToStringBuilder(Object object) { *

* * @param object - * the Object to build a toString for, must not - * be null + * the Object to build a toString for, must not be null * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @throws IllegalArgumentException - * if the Object passed in is null + * if the Object passed in is null */ public ReflectionToStringBuilder(Object object, ToStringStyle style) { super(object, style); @@ -492,14 +495,13 @@ public ReflectionToStringBuilder(Object object, ToStringStyle style) { *

* * @param object - * the Object to build a toString for + * the Object to build a toString for * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @param buffer - * the StringBuffer to populate, may be null + * the StringBuffer to populate, may be null * @throws IllegalArgumentException - * if the Object passed in is null + * if the Object passed in is null */ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { super(object, style, buffer); @@ -508,20 +510,18 @@ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffe /** * Constructor. * - * @deprecated Use - * {@link #ReflectionToStringBuilder(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)}. + * @deprecated Use {@link #ReflectionToStringBuilder(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)}. * * @param object - * the Object to build a toString for + * the Object to build a toString for * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @param buffer - * the StringBuffer to populate, may be null + * the StringBuffer to populate, may be null * @param reflectUpToClass - * the superclass to reflect up to (inclusive), may be null + * the superclass to reflect up to (inclusive), may be null * @param outputTransients - * whether to include transient fields + * whether to include transient fields */ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, boolean outputTransients) { @@ -534,18 +534,17 @@ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffe * Constructor. * * @param object - * the Object to build a toString for + * the Object to build a toString for * @param style - * the style of the toString to create, may be - * null + * the style of the toString to create, may be null * @param buffer - * the StringBuffer to populate, may be null + * the StringBuffer to populate, may be null * @param reflectUpToClass - * the superclass to reflect up to (inclusive), may be null + * the superclass to reflect up to (inclusive), may be null * @param outputTransients - * whether to include transient fields + * whether to include transient fields * @param outputStatics - * whether to include static fields + * whether to include static fields * @since 2.1 */ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, @@ -559,15 +558,13 @@ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffe /** * Returns whether or not to append the given Field. * * * @param field - * The Field to test. + * The Field to test. * @return Whether or not to append the given Field. */ protected boolean accept(Field field) { @@ -576,11 +573,16 @@ protected boolean accept(Field field) { return false; } if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) { - // transients. + // Reject transient fields. return false; } if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) { - // transients. + // Rject static fields. + return false; + } + if (this.getExcludeFieldNames() != null + && Arrays.binarySearch(this.getExcludeFieldNames(), field.getName()) >= 0) { + // Reject fields from the getExcludeFieldNames list. return false; } return true; @@ -588,18 +590,16 @@ protected boolean accept(Field field) { /** *

- * Appends the fields and values defined by the given object of the given - * Class. + * Appends the fields and values defined by the given object of the given Class. *

* *

- * If a cycle is detected as an object is "toString()'ed", such - * an object is rendered as if Object.toString() had been - * called and not implemented by the object. + * If a cycle is detected as an object is "toString()'ed", such an object is rendered as if + * Object.toString() had been called and not implemented by the object. *

* * @param clazz - * The class of object parameter + * The class of object parameter */ protected void appendFieldsIn(Class clazz) { if (isRegistered(this.getObject())) { @@ -634,7 +634,7 @@ protected void appendFieldsIn(Class clazz) { this.appendAsObjectToString(fieldValue); this.getStyle().appendFieldEnd(this.getStringBuffer(), fieldName); // The recursion out of - // builder.append(fieldName, fieldValue); + // builder.append(fieldName, fieldValue); // below will append the field // end marker. } else { @@ -646,9 +646,9 @@ protected void appendFieldsIn(Class clazz) { } } } catch (IllegalAccessException ex) { - //this can't happen. Would get a Security exception + // this can't happen. Would get a Security exception // instead - //throw a runtime exception in case the impossible + // throw a runtime exception in case the impossible // happens. throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage()); } @@ -659,6 +659,13 @@ protected void appendFieldsIn(Class clazz) { } } + /** + * @return Returns the excludeFieldNames. + */ + public String[] getExcludeFieldNames() { + return this.excludeFieldNames; + } + /** *

* Gets the last super class to stop appending fields for. @@ -676,13 +683,13 @@ public Class getUpToClass() { *

* * @param field - * The Field to query. + * The Field to query. * @return The Object from the given Field. * * @throws IllegalArgumentException - * see {@link java.lang.reflect.Field#get(Object)} + * see {@link java.lang.reflect.Field#get(Object)} * @throws IllegalAccessException - * see {@link java.lang.reflect.Field#get(Object)} + * see {@link java.lang.reflect.Field#get(Object)} * * @see java.lang.reflect.Field#get(Object) */ @@ -719,7 +726,7 @@ public boolean isAppendTransients() { *

* * @param array - * the array to add to the toString + * the array to add to the toString * @return this */ public ToStringBuilder reflectionAppendArray(Object array) { @@ -729,8 +736,7 @@ public ToStringBuilder reflectionAppendArray(Object array) { /** *

- * Registers this builder's source object to avoid infinite loops when - * processing circular object references. + * Registers this builder's source object to avoid infinite loops when processing circular object references. *

*/ void registerObject() { @@ -743,7 +749,7 @@ void registerObject() { *

* * @param appendStatics - * Whether or not to append static fields. + * Whether or not to append static fields. * @since 2.1 */ public void setAppendStatics(boolean appendStatics) { @@ -756,19 +762,35 @@ public void setAppendStatics(boolean appendStatics) { *

* * @param appendTransients - * Whether or not to append transient fields. + * Whether or not to append transient fields. */ public void setAppendTransients(boolean appendTransients) { this.appendTransients = appendTransients; } + /** + * Sets the field names to exclude. + * + * @param excludeFieldNamesParam + * The excludeFieldNames to set. + * @return this + */ + public ReflectionToStringBuilder setExcludeFieldNames(String[] excludeFieldNamesParam) { + if (excludeFieldNamesParam == null) { + this.excludeFieldNames = null; + } + this.excludeFieldNames = (String[]) excludeFieldNamesParam.clone(); + Arrays.sort(this.excludeFieldNames); + return this; + } + /** *

* Sets the last super class to stop appending fields for. *

* * @param clazz - * The last super class to stop appending fields for. + * The last super class to stop appending fields for. */ public void setUpToClass(Class clazz) { this.upToClass = clazz; @@ -796,8 +818,7 @@ public String toString() { /** *

- * Unregisters this builder's source object to avoid infinite loops when - * processing circular object references. + * Unregisters this builder's source object to avoid infinite loops when processing circular object references. *

*/ void unregisterObject() { diff --git a/src/test/org/apache/commons/lang/builder/BuilderTestSuite.java b/src/test/org/apache/commons/lang/builder/BuilderTestSuite.java index 89a1cf5fa..6443f3902 100644 --- a/src/test/org/apache/commons/lang/builder/BuilderTestSuite.java +++ b/src/test/org/apache/commons/lang/builder/BuilderTestSuite.java @@ -47,17 +47,18 @@ public static void main(String[] args) { public static Test suite() { TestSuite suite = new TestSuite(); suite.setName("Commons-Lang-Builder Tests"); - suite.addTest(CompareToBuilderTest.suite()); - suite.addTest(EqualsBuilderTest.suite()); - suite.addTest(HashCodeBuilderTest.suite()); - suite.addTest(HashCodeBuilderAndEqualsBuilderTest.suite()); - suite.addTest(ToStringBuilderTest.suite()); - suite.addTest(DefaultToStringStyleTest.suite()); - suite.addTest(NoFieldNamesToStringStyleTest.suite()); - suite.addTest(MultiLineToStringStyleTest.suite()); - suite.addTest(SimpleToStringStyleTest.suite()); - suite.addTest(StandardToStringStyleTest.suite()); - suite.addTest(ToStringStyleTest.suite()); + suite.addTestSuite(CompareToBuilderTest.class); + suite.addTestSuite(EqualsBuilderTest.class); + suite.addTestSuite(HashCodeBuilderTest.class); + suite.addTestSuite(HashCodeBuilderAndEqualsBuilderTest.class); + suite.addTestSuite(ToStringBuilderTest.class); + suite.addTestSuite(DefaultToStringStyleTest.class); + suite.addTestSuite(NoFieldNamesToStringStyleTest.class); + suite.addTestSuite(MultiLineToStringStyleTest.class); + suite.addTestSuite(ReflectionToStringBuilderExcludeTest.class); + suite.addTestSuite(SimpleToStringStyleTest.class); + suite.addTestSuite(StandardToStringStyleTest.class); + suite.addTestSuite(ToStringStyleTest.class); return suite; } }