/* * 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: *
* 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.
*
* 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 isnull
*/
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.
*
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.
*
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.
*
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.
*
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 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(Collectionnull.
*
* @param array
* The array to check
* @return The given array or a new array without null.
*/
static String[] toNoNullStringArray(Object[] array) {
List"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.
*
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.
*
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.
*
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 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 Field.
* true.
* true.
* 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.
*
* 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).
*
* 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.
*
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 ornull.
* @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(); } }