All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.lang.builder.ReflectionToStringBuilder Maven / Gradle / Ivy

Go to download

Commons.Lang, a package of Java utility classes for the classes that are in java.lang's hierarchy, or are considered to be so standard as to justify existence in java.lang.

There is a newer version: 20030203.000129
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * 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.
 * 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.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ClassUtils;

/**
 * 

* Assists in implementing {@link Object#toString()} methods using reflection. *

* *

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

* *

* A typical invocation for this method would look like: *

* *
 * public String toString() {
 *   return ReflectionToStringBuilder.toString(this);
 * }
* * * *

* You can also use the builder to debug 3rd party objects: *

* *
 * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));
* * * *

* A subclass can control field output by overriding the methods: *

    *
  • {@link #accept(java.lang.reflect.Field)}
  • *
  • {@link #getValue(java.lang.reflect.Field)}
  • *
*

*

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

* *
 * public String toString() {
 *     return (new ReflectionToStringBuilder(this) {
 *         protected boolean accept(Field f) {
 *             return super.accept(f) && !f.getName().equals("password");
 *         }
 *     }).toString();
 * }
* * * *

* 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 437554 2006-08-28 06:21:41Z bayard $ */ public class ReflectionToStringBuilder extends ToStringBuilder { /** *

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

*/ private static ThreadLocal registry = new ThreadLocal() { protected synchronized Object initialValue() { // The HashSet implementation is not synchronized, // which is just what we need here. return new HashSet(); } }; /** *

* 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 (Set) registry.get(); } /** *

* 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. */ static boolean isRegistered(Object value) { return getRegistry().contains(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) { getRegistry().add(value); } /** *

* Builds a toString value using the default ToStringStyle through reflection. *

* *

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

* * @param object * the Object to be output * @return the String result * @throws IllegalArgumentException * if the Object is null */ public static String toString(Object object) { return toString(object, null, false, false, null); } /** *

* Builds a toString value through reflection. *

* *

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

* *

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

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

* Builds a toString value through reflection. *

* *

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

* *

* Static fields will not be included. Superclass fields will be appended. *

* *

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

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

* Builds a toString value through reflection. *

* *

* 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 outputStatics is true, static fields will be output, otherwise they are * ignored. *

* *

* Static fields will not be included. Superclass fields will be appended. *

* *

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

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

* Builds a toString value through reflection. *

* *

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

* *

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

* * @param object * the Object to be output * @param style * the style of the toString to create, may be null * @param outputTransients * whether to include transient fields * @param outputStatics * whether to include static fields * @param reflectUpToClass * the superclass to reflect up to (inclusive), may be null * @return the String result * @throws IllegalArgumentException * if the Object is null * @since 2.1 */ public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics, Class reflectUpToClass) { return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics) .toString(); } /** *

* Builds a toString value through reflection. *

* *

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

* *

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

* * @deprecated Use {@link #toString(Object,ToStringStyle,boolean,boolean,Class)} * * @param object * the Object to be output * @param style * the style of the toString to create, may be null * @param outputTransients * whether to include transient fields * @param reflectUpToClass * the superclass to reflect up to (inclusive), may be null * @return the String result * @throws IllegalArgumentException * if the Object is null * @since 2.0 */ 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 names. * * @param object * The object to "toString". * @param excludeFieldNames * The field names to exclude. Null excludes nothing. * @return The toString value. */ public static String toStringExclude(Object object, Collection /*String*/ excludeFieldNames) { return toStringExclude(object, toNoNullStringArray(excludeFieldNames)); } /** * Converts the given Collection into an array of Strings. The returned array does not contain null * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element * is null. * * @param collection * The collection to convert * @return A new array of Strings. */ static String[] toNoNullStringArray(Collection collection) { if (collection == null) { return ArrayUtils.EMPTY_STRING_ARRAY; } return toNoNullStringArray(collection.toArray()); } /** * Returns a new array of Strings without null elements. Internal method used to normalize exclude lists * (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} * if an array element is null. * * @param array * The array to check * @return The given array or a new array without null. */ static String[] toNoNullStringArray(Object[] array) { ArrayList list = new ArrayList(array.length); for (int i = 0; i < array.length; i++) { Object e = array[i]; if (e != null) { list.add(e.toString()); } } return (String[]) list.toArray(ArrayUtils.EMPTY_STRING_ARRAY); } /** * Builds a String for a toString method excluding the given field names. * * @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. *

* *

* Used by the reflection methods to avoid infinite loops. *

* * @param value * The object to unregister. */ static void unregister(Object value) { getRegistry().remove(value); } /** * Whether or not to append static fields. */ private boolean appendStatics = false; /** * Whether or not to append transient fields. */ 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. */ private Class upToClass = null; /** *

* Constructor. *

* *

* This constructor outputs using the default style set with setDefaultStyle. *

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

* Constructor. *

* *

* If the style is null, the default style is used. *

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

* Constructor. *

* *

* If the style is null, the default style is used. *

* *

* If the buffer is null, a new one is created. *

* * @param object * the Object to build a toString for * @param style * the style of the toString to create, may be null * @param buffer * the StringBuffer to populate, may be null * @throws IllegalArgumentException * if the Object passed in is null */ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { super(object, style, buffer); } /** * Constructor. * * @deprecated Use {@link #ReflectionToStringBuilder(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)}. * * @param object * the Object to build a toString for * @param style * the style of the toString to create, may be null * @param buffer * the StringBuffer to populate, may be null * @param reflectUpToClass * the superclass to reflect up to (inclusive), may be null * @param outputTransients * whether to include transient fields */ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, boolean outputTransients) { super(object, style, buffer); this.setUpToClass(reflectUpToClass); this.setAppendTransients(outputTransients); } /** * Constructor. * * @param object * the Object to build a toString for * @param style * the style of the toString to create, may be null * @param buffer * the StringBuffer to populate, may be null * @param reflectUpToClass * the superclass to reflect up to (inclusive), may be null * @param outputTransients * whether to include transient fields * @param outputStatics * whether to include static fields * @since 2.1 */ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, boolean outputTransients, boolean outputStatics) { super(object, style, buffer); this.setUpToClass(reflectUpToClass); this.setAppendTransients(outputTransients); this.setAppendStatics(outputStatics); } /** * Returns whether or not to append the given Field. *
    *
  • Transient fields are appended only if {@link #isAppendTransients()} returns true. *
  • Static fields are appended only if {@link #isAppendStatics()} returns true. *
  • Inner class fields are not appened.
  • *
* * @param field * The Field to test. * @return Whether or not to append the given Field. */ protected boolean accept(Field field) { if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) { // Reject field from inner class. return false; } if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) { // Reject transient fields. return false; } if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) { // 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; } /** *

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

* * @param clazz * The class of object parameter */ protected void appendFieldsIn(Class clazz) { if (isRegistered(this.getObject())) { // 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. this.appendAsObjectToString(this.getObject()); return; } try { this.registerObject(); if (clazz.isArray()) { this.reflectionAppendArray(this.getObject()); return; } Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; String fieldName = field.getName(); if (this.accept(field)) { try { // Warning: Field.get(Object) creates wrappers objects // for primitive types. Object fieldValue = this.getValue(field); if (isRegistered(fieldValue) && !field.getType().isPrimitive()) { // A known field value has already been appended, // therefore we have an object cycle, // append a simple Object.toString style string. this.getStyle().appendFieldStart(this.getStringBuffer(), fieldName); this.appendAsObjectToString(fieldValue); this.getStyle().appendFieldEnd(this.getStringBuffer(), fieldName); // The recursion out of // builder.append(fieldName, fieldValue); // below will append the field // end marker. } else { try { this.registerObject(); this.append(fieldName, fieldValue); } finally { this.unregisterObject(); } } } 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()); } } } } finally { this.unregisterObject(); } } /** * @return Returns the excludeFieldNames. */ public String[] getExcludeFieldNames() { return this.excludeFieldNames; } /** *

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

* * @return The last super class to stop appending fields for. */ public Class getUpToClass() { return this.upToClass; } /** *

* Calls java.lang.reflect.Field.get(Object). *

* * @param field * The Field to query. * @return The Object from the given Field. * * @throws IllegalArgumentException * see {@link java.lang.reflect.Field#get(Object)} * @throws IllegalAccessException * see {@link java.lang.reflect.Field#get(Object)} * * @see java.lang.reflect.Field#get(Object) */ protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException { return field.get(this.getObject()); } /** *

* Gets whether or not to append static fields. *

* * @return Whether or not to append static fields. * @since 2.1 */ public boolean isAppendStatics() { return this.appendStatics; } /** *

* Gets whether or not to append transient fields. *

* * @return Whether or not to append transient fields. */ public boolean isAppendTransients() { return this.appendTransients; } /** *

* Append to the toString an Object array. *

* * @param array * the array to add to the toString * @return this */ public ToStringBuilder reflectionAppendArray(Object array) { this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array); return this; } /** *

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

*/ void registerObject() { register(this.getObject()); } /** *

* Sets whether or not to append static fields. *

* * @param appendStatics * Whether or not to append static fields. * @since 2.1 */ public void setAppendStatics(boolean appendStatics) { this.appendStatics = appendStatics; } /** *

* Sets whether or not to append transient fields. *

* * @param appendTransients * 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 excluding from toString or null. * @return this */ public ReflectionToStringBuilder setExcludeFieldNames(String[] excludeFieldNamesParam) { if (excludeFieldNamesParam == null) { this.excludeFieldNames = null; } else { this.excludeFieldNames = toNoNullStringArray(excludeFieldNamesParam); 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. */ public void setUpToClass(Class clazz) { this.upToClass = clazz; } /** *

* Gets the String built by this builder. *

* * @return the built string */ public String toString() { if (this.getObject() == null) { return this.getStyle().getNullText(); } Class clazz = this.getObject().getClass(); this.appendFieldsIn(clazz); while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) { clazz = clazz.getSuperclass(); this.appendFieldsIn(clazz); } return super.toString(); } /** *

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

*/ void unregisterObject() { unregister(this.getObject()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy