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

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

Go to download

Apache 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. The code is tested using the latest revision of the JDK for supported LTS releases: 8, 11, 17 and 21 currently. See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml Please ensure your build environment is up-to-date and kindly report any build issues.

There is a newer version: 3.17.0
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.lang3.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.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.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. *

* * @since 2.0 * @version $Id: ReflectionToStringBuilder.java 1090821 2011-04-10 15:59:07Z mbenson $ */ public class ReflectionToStringBuilder extends ToStringBuilder { /** *

* 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 * the type of the object * @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( T object, ToStringStyle style, boolean outputTransients, boolean outputStatics, Class reflectUpToClass) { return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics) .toString(); } /** * 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 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) { List list = new ArrayList(array.length); for (Object e : array) { if (e != null) { list.add(e.toString()); } } return 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(); } /** * 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". * * @since 3.0 this is protected instead of private */ protected 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. * * @param * the type of the object * @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( T 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()) { // Reject static fields. return false; } if (this.excludeFieldNames != null && Arrays.binarySearch(this.excludeFieldNames, 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 (clazz.isArray()) { this.reflectionAppendArray(this.getObject()); return; } Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (Field field : fields) { String fieldName = field.getName(); if (this.accept(field)) { try { // Warning: Field.get(Object) creates wrappers objects // for primitive types. Object fieldValue = this.getValue(field); this.append(fieldName, fieldValue); } 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()); } } } } /** * @return Returns the excludeFieldNames. */ public String[] getExcludeFieldNames() { return this.excludeFieldNames.clone(); } /** *

* 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 ReflectionToStringBuilder reflectionAppendArray(Object array) { this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array); return this; } /** *

* 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 { //clone and remove nulls 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) { if (clazz != null) { Object object = getObject(); if (object != null && clazz.isInstance(object) == false) { throw new IllegalArgumentException("Specified class is not a superclass of the object"); } } this.upToClass = clazz; } /** *

* Gets the String built by this builder. *

* * @return the built string */ @Override 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(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy