diff options
Diffstat (limited to 'src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java')
-rw-r--r-- | src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java new file mode 100644 index 000000000..8bb0d0e10 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java @@ -0,0 +1,877 @@ +/* + * 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.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Objects; + +import org.apache.commons.lang3.ArraySorter; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.stream.Streams; + +/** + * Assists in implementing {@link Object#toString()} methods using reflection. + * + * <p> + * 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. + * </p> + * <p> + * Using reflection to access (private) fields circumvents any synchronization protection guarding access to these + * fields. If a toString method cannot safely read a field, you should exclude it from the toString method, or use + * synchronization consistent with the class' lock management around the invocation of the method. Take special care to + * exclude non-thread-safe collection classes, because these classes may throw ConcurrentModificationException if + * modified while the toString method is executing. + * </p> + * <p> + * A typical invocation for this method would look like: + * </p> + * <pre> + * public String toString() { + * return ReflectionToStringBuilder.toString(this); + * } + * </pre> + * <p> + * You can also use the builder to debug 3rd party objects: + * </p> + * <pre> + * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject)); + * </pre> + * <p> + * A subclass can control field output by overriding the methods: + * </p> + * <ul> + * <li>{@link #accept(java.lang.reflect.Field)}</li> + * <li>{@link #getValue(java.lang.reflect.Field)}</li> + * </ul> + * <p> + * For example, this method does <i>not</i> include the {@code password} field in the returned {@link String}: + * </p> + * <pre> + * public String toString() { + * return (new ReflectionToStringBuilder(this) { + * protected boolean accept(Field f) { + * return super.accept(f) && !f.getName().equals("password"); + * } + * }).toString(); + * } + * </pre> + * <p> + * Alternatively the {@link ToStringExclude} annotation can be used to exclude fields from being incorporated in the + * result. + * </p> + * <p> + * It is also possible to use the {@link ToStringSummary} annotation to output the summary information instead of the + * detailed information of a field. + * </p> + * <p> + * The exact format of the {@code toString} is determined by the {@link ToStringStyle} passed into the constructor. + * </p> + * + * <p> + * <b>Note:</b> the default {@link ToStringStyle} will only do a "shallow" formatting, i.e. composed objects are not + * further traversed. To get "deep" formatting, use an instance of {@link RecursiveToStringStyle}. + * </p> + * + * @since 2.0 + */ +public class ReflectionToStringBuilder extends ToStringBuilder { + + /** + * Converts the given Collection into an array of Strings. The returned array does not contain {@code null} + * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element + * is {@code null}. + * + * @param collection + * The collection to convert + * @return A new array of Strings. + */ + static String[] toNoNullStringArray(final Collection<String> 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 {@code null}. + * + * @param array + * The array to check + * @return The given array or a new array without null. + */ + static String[] toNoNullStringArray(final Object[] array) { + return Streams.nonNull(array).map(Objects::toString).toArray(String[]::new); + } + + /** + * Builds a {@code toString} value using the default {@link ToStringStyle} through reflection. + * + * <p> + * It uses {@code 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. + * </p> + * + * <p> + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + * </p> + * + * @param object + * the Object to be output + * @return the String result + * @throws IllegalArgumentException + * if the Object is {@code null} + * + * @see ToStringExclude + * @see ToStringSummary + */ + public static String toString(final Object object) { + return toString(object, null, false, false, null); + } + + /** + * Builds a {@code toString} value through reflection. + * + * <p> + * It uses {@code 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. + * </p> + * + * <p> + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + * </p> + * + * <p> + * If the style is {@code null}, the default {@link ToStringStyle} is used. + * </p> + * + * @param object + * the Object to be output + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @return the String result + * @throws IllegalArgumentException + * if the Object or {@link ToStringStyle} is {@code null} + * + * @see ToStringExclude + * @see ToStringSummary + */ + public static String toString(final Object object, final ToStringStyle style) { + return toString(object, style, false, false, null); + } + + /** + * Builds a {@code toString} value through reflection. + * + * <p> + * It uses {@code 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. + * </p> + * + * <p> + * If the {@code outputTransients} is {@code 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. + * </p> + * + * <p> + * Static fields will not be included. Superclass fields will be appended. + * </p> + * + * <p> + * If the style is {@code null}, the default {@link ToStringStyle} is used. + * </p> + * + * @param object + * the Object to be output + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @param outputTransients + * whether to include transient fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is {@code null} + * + * @see ToStringExclude + * @see ToStringSummary + */ + public static String toString(final Object object, final ToStringStyle style, final boolean outputTransients) { + return toString(object, style, outputTransients, false, null); + } + + /** + * Builds a {@code toString} value through reflection. + * + * <p> + * It uses {@code 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. + * </p> + * + * <p> + * If the {@code outputTransients} is {@code 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. + * </p> + * + * <p> + * If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are + * ignored. + * </p> + * + * <p> + * Static fields will not be included. Superclass fields will be appended. + * </p> + * + * <p> + * If the style is {@code null}, the default {@link ToStringStyle} is used. + * </p> + * + * @param object + * the Object to be output + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is {@code null} + * + * @see ToStringExclude + * @see ToStringSummary + * @since 2.1 + */ + public static String toString(final Object object, final ToStringStyle style, final boolean outputTransients, final boolean outputStatics) { + return toString(object, style, outputTransients, outputStatics, null); + } + + /** + * Builds a {@code toString} value through reflection. + * + * <p> + * It uses {@code 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. + * </p> + * + * <p> + * If the {@code outputTransients} is {@code 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. + * </p> + * + * <p> + * If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are + * ignored. + * </p> + * + * <p> + * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as + * {@code java.lang.Object}. + * </p> + * + * <p> + * If the style is {@code null}, the default {@link ToStringStyle} is used. + * </p> + * + * @param <T> + * the type of the object + * @param object + * the Object to be output + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @param excludeNullValues + * whether to exclude fields whose values are null + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be {@code null} + * @return the String result + * @throws IllegalArgumentException + * if the Object is {@code null} + * + * @see ToStringExclude + * @see ToStringSummary + * @since 3.6 + */ + public static <T> String toString( + final T object, final ToStringStyle style, final boolean outputTransients, + final boolean outputStatics, final boolean excludeNullValues, final Class<? super T> reflectUpToClass) { + return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics, excludeNullValues) + .toString(); + } + + /** + * Builds a {@code toString} value through reflection. + * + * <p> + * It uses {@code 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. + * </p> + * + * <p> + * If the {@code outputTransients} is {@code 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. + * </p> + * + * <p> + * If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are + * ignored. + * </p> + * + * <p> + * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as + * {@code java.lang.Object}. + * </p> + * + * <p> + * If the style is {@code null}, the default {@link ToStringStyle} is used. + * </p> + * + * @param <T> + * the type of the object + * @param object + * the Object to be output + * @param style + * the style of the {@code toString} to create, may be {@code 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 {@code null} + * @return the String result + * @throws IllegalArgumentException + * if the Object is {@code null} + * + * @see ToStringExclude + * @see ToStringSummary + * @since 2.1 + */ + public static <T> String toString( + final T object, final ToStringStyle style, final boolean outputTransients, + final boolean outputStatics, final Class<? super T> 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(final Object object, final Collection<String> excludeFieldNames) { + return toStringExclude(object, toNoNullStringArray(excludeFieldNames)); + } + + /** + * 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(final Object object, final String... excludeFieldNames) { + return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString(); + } + + + /** + * Builds a String for a toString method including the given field names. + * + * @param object + * The object to "toString". + * @param includeFieldNames + * {@code null} or empty means all fields are included. All fields are included by default. This method will override the default behavior. + * @return The toString value. + * @since 3.13.0 + */ + public static String toStringInclude(final Object object, final Collection<String> includeFieldNames) { + return toStringInclude(object, toNoNullStringArray(includeFieldNames)); + } + + /** + * Builds a String for a toString method including the given field names. + * + * @param object + * The object to "toString". + * @param includeFieldNames + * The field names to include. {@code null} or empty means all fields are included. All fields are included by default. This method will override the default + * behavior. + * @return The toString value. + * @since 3.13.0 + */ + public static String toStringInclude(final Object object, final String... includeFieldNames) { + return new ReflectionToStringBuilder(object).setIncludeFieldNames(includeFieldNames).toString(); + } + + /** + * Whether or not to append static fields. + */ + private boolean appendStatics; + + /** + * Whether or not to append transient fields. + */ + private boolean appendTransients; + + /** + * Whether or not to append fields that are null. + */ + private boolean excludeNullValues; + + /** + * Which field names to exclude from output. Intended for fields like {@code "password"}. + * + * @since 3.0 this is protected instead of private + */ + protected String[] excludeFieldNames; + + /** + * Field names that will be included in the output. All fields are included by default. + * + * @since 3.13.0 + */ + protected String[] includeFieldNames; + + /** + * The last super class to stop appending fields for. + */ + private Class<?> upToClass; + + /** + * Constructs a new instance. + * + * <p> + * This constructor outputs using the default style set with {@code setDefaultStyle}. + * </p> + * + * @param object + * the Object to build a {@code toString} for, must not be {@code null} + * @throws IllegalArgumentException + * if the Object passed in is {@code null} + */ + public ReflectionToStringBuilder(final Object object) { + super(Objects.requireNonNull(object, "obj")); + } + + /** + * Constructs a new instance. + * + * <p> + * If the style is {@code null}, the default style is used. + * </p> + * + * @param object + * the Object to build a {@code toString} for, must not be {@code null} + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @throws IllegalArgumentException + * if the Object passed in is {@code null} + */ + public ReflectionToStringBuilder(final Object object, final ToStringStyle style) { + super(Objects.requireNonNull(object, "obj"), style); + } + + /** + * Constructs a new instance. + * + * <p> + * If the style is {@code null}, the default style is used. + * </p> + * + * <p> + * If the buffer is {@code null}, a new one is created. + * </p> + * + * @param object + * the Object to build a {@code toString} for + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @param buffer + * the {@link StringBuffer} to populate, may be {@code null} + * @throws IllegalArgumentException + * if the Object passed in is {@code null} + */ + public ReflectionToStringBuilder(final Object object, final ToStringStyle style, final StringBuffer buffer) { + super(Objects.requireNonNull(object, "obj"), style, buffer); + } + + /** + * Constructs a new instance. + * + * @param <T> + * the type of the object + * @param object + * the Object to build a {@code toString} for + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @param buffer + * the {@link StringBuffer} to populate, may be {@code null} + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be {@code null} + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @since 2.1 + */ + public <T> ReflectionToStringBuilder( + final T object, final ToStringStyle style, final StringBuffer buffer, + final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics) { + super(Objects.requireNonNull(object, "obj"), style, buffer); + this.setUpToClass(reflectUpToClass); + this.setAppendTransients(outputTransients); + this.setAppendStatics(outputStatics); + } + + /** + * Constructs a new instance. + * + * @param <T> + * the type of the object + * @param object + * the Object to build a {@code toString} for + * @param style + * the style of the {@code toString} to create, may be {@code null} + * @param buffer + * the {@link StringBuffer} to populate, may be {@code null} + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be {@code null} + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @param excludeNullValues + * whether to exclude fields who value is null + * @since 3.6 + */ + public <T> ReflectionToStringBuilder( + final T object, final ToStringStyle style, final StringBuffer buffer, + final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics, + final boolean excludeNullValues) { + super(Objects.requireNonNull(object, "obj"), style, buffer); + this.setUpToClass(reflectUpToClass); + this.setAppendTransients(outputTransients); + this.setAppendStatics(outputStatics); + this.setExcludeNullValues(excludeNullValues); + } + + /** + * Returns whether or not to append the given {@link Field}. + * <ul> + * <li>Transient fields are appended only if {@link #isAppendTransients()} returns {@code true}. + * <li>Static fields are appended only if {@link #isAppendStatics()} returns {@code true}. + * <li>Inner class fields are not appended.</li> + * </ul> + * + * @param field + * The Field to test. + * @return Whether or not to append the given {@link Field}. + */ + protected boolean accept(final 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; + } + + if (ArrayUtils.isNotEmpty(includeFieldNames)) { + // Accept fields from the getIncludeFieldNames list. {@code null} or empty means all fields are included. All fields are included by default. + return Arrays.binarySearch(this.includeFieldNames, field.getName()) >= 0; + } + + return !field.isAnnotationPresent(ToStringExclude.class); + } + + /** + * Appends the fields and values defined by the given object of the given Class. + * + * <p> + * If a cycle is detected as an object is "toString()'ed", such an object is rendered as if + * {@code Object.toString()} had been called and not implemented by the object. + * </p> + * + * @param clazz + * The class of object parameter + */ + protected void appendFieldsIn(final Class<?> clazz) { + if (clazz.isArray()) { + this.reflectionAppendArray(this.getObject()); + return; + } + // The elements in the returned array are not sorted and are not in any particular order. + final Field[] fields = ArraySorter.sort(clazz.getDeclaredFields(), Comparator.comparing(Field::getName)); + AccessibleObject.setAccessible(fields, true); + for (final Field field : fields) { + final String fieldName = field.getName(); + if (this.accept(field)) { + try { + // Warning: Field.get(Object) creates wrappers objects for primitive types. + final Object fieldValue = this.getValue(field); + if (!excludeNullValues || fieldValue != null) { + this.append(fieldName, fieldValue, !field.isAnnotationPresent(ToStringSummary.class)); + } + } catch (final 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()); + } + } + } + } + + /** + * Gets the excludeFieldNames. + * + * @return the excludeFieldNames. + */ + public String[] getExcludeFieldNames() { + return this.excludeFieldNames.clone(); + } + + /** + * Gets the includeFieldNames + * + * @return the includeFieldNames. + * @since 3.13.0 + */ + public String[] getIncludeFieldNames() { + return this.includeFieldNames.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 {@code 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(final Field field) throws 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; + } + + /** + * Gets whether or not to append fields whose values are null. + * + * @return Whether or not to append fields whose values are null. + * @since 3.6 + */ + public boolean isExcludeNullValues() { + return this.excludeNullValues; + } + + /** + * Appends to the {@code toString} an {@link Object} array. + * + * @param array + * the array to add to the {@code toString} + * @return this + */ + public ReflectionToStringBuilder reflectionAppendArray(final 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(final 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(final boolean appendTransients) { + this.appendTransients = appendTransients; + } + + /** + * Sets the field names to exclude. + * + * @param excludeFieldNamesParam + * The excludeFieldNames to excluding from toString or {@code null}. + * @return {@code this} + */ + public ReflectionToStringBuilder setExcludeFieldNames(final String... excludeFieldNamesParam) { + if (excludeFieldNamesParam == null) { + this.excludeFieldNames = null; + } else { + // clone and remove nulls + this.excludeFieldNames = ArraySorter.sort(toNoNullStringArray(excludeFieldNamesParam)); + } + return this; + } + + /** + * Sets whether or not to append fields whose values are null. + * + * @param excludeNullValues + * Whether or not to append fields whose values are null. + * @since 3.6 + */ + public void setExcludeNullValues(final boolean excludeNullValues) { + this.excludeNullValues = excludeNullValues; + } + + /** + * Sets the field names to include. {@code null} or empty means all fields are included. All fields are included by default. This method will override the default behavior. + * + * @param includeFieldNamesParam + * The includeFieldNames that must be on toString or {@code null}. + * @return {@code this} + * @since 3.13.0 + */ + public ReflectionToStringBuilder setIncludeFieldNames(final String... includeFieldNamesParam) { + if (includeFieldNamesParam == null) { + this.includeFieldNames = null; + } else { + // clone and remove nulls + this.includeFieldNames = ArraySorter.sort(toNoNullStringArray(includeFieldNamesParam)); + } + 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(final Class<?> clazz) { + if (clazz != null) { + final Object object = getObject(); + if (object != null && !clazz.isInstance(object)) { + 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(); + } + + validate(); + + Class<?> clazz = this.getObject().getClass(); + this.appendFieldsIn(clazz); + while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) { + clazz = clazz.getSuperclass(); + this.appendFieldsIn(clazz); + } + return super.toString(); + } + + /** + * Validates that include and exclude names do not intersect. + */ + private void validate() { + if (ArrayUtils.containsAny(this.excludeFieldNames, (Object[]) this.includeFieldNames)) { + ToStringStyle.unregister(this.getObject()); + throw new IllegalStateException("includeFieldNames and excludeFieldNames must not intersect"); + } + } + +} |