diff options
Diffstat (limited to 'src/org/apache/commons/lang3/builder')
9 files changed, 7694 insertions, 0 deletions
diff --git a/src/org/apache/commons/lang3/builder/Builder.java b/src/org/apache/commons/lang3/builder/Builder.java new file mode 100644 index 0000000..ce696e8 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/Builder.java @@ -0,0 +1,89 @@ +/*
+ * 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;
+
+/**
+ * <p>
+ * The Builder interface is designed to designate a class as a <em>builder</em>
+ * object in the Builder design pattern. Builders are capable of creating and
+ * configuring objects or results that normally take multiple steps to construct
+ * or are very complex to derive.
+ * </p>
+ *
+ * <p>
+ * The builder interface defines a single method, {@link #build()}, that
+ * classes must implement. The result of this method should be the final
+ * configured object or result after all building operations are performed.
+ * </p>
+ *
+ * <p>
+ * It is a recommended practice that the methods supplied to configure the
+ * object or result being built return a reference to {@code this} so that
+ * method calls can be chained together.
+ * </p>
+ *
+ * <p>
+ * Example Builder:
+ * <code><pre>
+ * class FontBuilder implements Builder<Font> {
+ * private Font font;
+ *
+ * public FontBuilder(String fontName) {
+ * this.font = new Font(fontName, Font.PLAIN, 12);
+ * }
+ *
+ * public FontBuilder bold() {
+ * this.font = this.font.deriveFont(Font.BOLD);
+ * return this; // Reference returned so calls can be chained
+ * }
+ *
+ * public FontBuilder size(float pointSize) {
+ * this.font = this.font.deriveFont(pointSize);
+ * return this; // Reference returned so calls can be chained
+ * }
+ *
+ * // Other Font construction methods
+ *
+ * public Font build() {
+ * return this.font;
+ * }
+ * }
+ * </pre></code>
+ *
+ * Example Builder Usage:
+ * <code><pre>
+ * Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
+ * .size(14.0f)
+ * .build();
+ * </pre></code>
+ * </p>
+ *
+ * @param <T> the type of object that the builder will construct or compute.
+ *
+ * @since 3.0
+ * @version $Id: Builder.java 1088899 2011-04-05 05:31:27Z bayard $
+ */
+public interface Builder<T> {
+
+ /**
+ * Returns a reference to the object being constructed or result being
+ * calculated by the builder.
+ *
+ * @return the object constructed or result calculated by the builder.
+ */
+ public T build();
+}
diff --git a/src/org/apache/commons/lang3/builder/CompareToBuilder.java b/src/org/apache/commons/lang3/builder/CompareToBuilder.java new file mode 100644 index 0000000..6f51a2f --- /dev/null +++ b/src/org/apache/commons/lang3/builder/CompareToBuilder.java @@ -0,0 +1,1019 @@ +/*
+ * 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.Collection;
+import java.util.Comparator;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Assists in implementing {@link java.lang.Comparable#compareTo(Object)} methods.
+ *
+ * It is consistent with <code>equals(Object)</code> and
+ * <code>hashcode()</code> built with {@link EqualsBuilder} and
+ * {@link HashCodeBuilder}.</p>
+ *
+ * <p>Two Objects that compare equal using <code>equals(Object)</code> should normally
+ * also compare equal using <code>compareTo(Object)</code>.</p>
+ *
+ * <p>All relevant fields should be included in the calculation of the
+ * comparison. Derived fields may be ignored. The same fields, in the same
+ * order, should be used in both <code>compareTo(Object)</code> and
+ * <code>equals(Object)</code>.</p>
+ *
+ * <p>To use this class write code as follows:</p>
+ *
+ * <pre>
+ * public class MyClass {
+ * String field1;
+ * int field2;
+ * boolean field3;
+ *
+ * ...
+ *
+ * public int compareTo(Object o) {
+ * MyClass myClass = (MyClass) o;
+ * return new CompareToBuilder()
+ * .appendSuper(super.compareTo(o)
+ * .append(this.field1, myClass.field1)
+ * .append(this.field2, myClass.field2)
+ * .append(this.field3, myClass.field3)
+ * .toComparison();
+ * }
+ * }
+ * </pre>
+ *
+ * <p>Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use
+ * reflection to determine the fields to append. Because fields can be private,
+ * <code>reflectionCompare</code> uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to
+ * bypass normal access control checks. This will fail under a security manager,
+ * unless the appropriate permissions are set up correctly. It is also
+ * slower than appending explicitly.</p>
+ *
+ * <p>A typical implementation of <code>compareTo(Object)</code> using
+ * <code>reflectionCompare</code> looks like:</p>
+
+ * <pre>
+ * public int compareTo(Object o) {
+ * return CompareToBuilder.reflectionCompare(this, o);
+ * }
+ * </pre>
+ *
+ * @see java.lang.Comparable
+ * @see java.lang.Object#equals(Object)
+ * @see java.lang.Object#hashCode()
+ * @see EqualsBuilder
+ * @see HashCodeBuilder
+ * @since 1.0
+ * @version $Id: CompareToBuilder.java 1090813 2011-04-10 15:03:23Z mbenson $
+ */
+public class CompareToBuilder implements Builder<Integer> {
+
+ /**
+ * Current state of the comparison as appended fields are checked.
+ */
+ private int comparison;
+
+ /**
+ * <p>Constructor for CompareToBuilder.</p>
+ *
+ * <p>Starts off assuming that the objects are equal. Multiple calls are
+ * then made to the various append methods, followed by a call to
+ * {@link #toComparison} to get the result.</p>
+ */
+ public CompareToBuilder() {
+ super();
+ comparison = 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Compares two <code>Object</code>s via reflection.</p>
+ *
+ * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
+ * is used to bypass normal access control checks. This will fail under a
+ * security manager unless the appropriate permissions are set.</p>
+ *
+ * <ul>
+ * <li>Static fields will not be compared</li>
+ * <li>Transient members will be not be compared, as they are likely derived
+ * fields</li>
+ * <li>Superclass fields will be compared</li>
+ * </ul>
+ *
+ * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
+ * they are considered equal.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @return a negative integer, zero, or a positive integer as <code>lhs</code>
+ * is less than, equal to, or greater than <code>rhs</code>
+ * @throws NullPointerException if either (but not both) parameters are
+ * <code>null</code>
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ */
+ public static int reflectionCompare(Object lhs, Object rhs) {
+ return reflectionCompare(lhs, rhs, false, null);
+ }
+
+ /**
+ * <p>Compares two <code>Object</code>s via reflection.</p>
+ *
+ * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
+ * is used to bypass normal access control checks. This will fail under a
+ * security manager unless the appropriate permissions are set.</p>
+ *
+ * <ul>
+ * <li>Static fields will not be compared</li>
+ * <li>If <code>compareTransients</code> is <code>true</code>,
+ * compares transient members. Otherwise ignores them, as they
+ * are likely derived fields.</li>
+ * <li>Superclass fields will be compared</li>
+ * </ul>
+ *
+ * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
+ * they are considered equal.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @param compareTransients whether to compare transient fields
+ * @return a negative integer, zero, or a positive integer as <code>lhs</code>
+ * is less than, equal to, or greater than <code>rhs</code>
+ * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
+ * (but not both) is <code>null</code>
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ */
+ public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients) {
+ return reflectionCompare(lhs, rhs, compareTransients, null);
+ }
+
+ /**
+ * <p>Compares two <code>Object</code>s via reflection.</p>
+ *
+ * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
+ * is used to bypass normal access control checks. This will fail under a
+ * security manager unless the appropriate permissions are set.</p>
+ *
+ * <ul>
+ * <li>Static fields will not be compared</li>
+ * <li>If <code>compareTransients</code> is <code>true</code>,
+ * compares transient members. Otherwise ignores them, as they
+ * are likely derived fields.</li>
+ * <li>Superclass fields will be compared</li>
+ * </ul>
+ *
+ * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
+ * they are considered equal.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @param excludeFields Collection of String fields to exclude
+ * @return a negative integer, zero, or a positive integer as <code>lhs</code>
+ * is less than, equal to, or greater than <code>rhs</code>
+ * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
+ * (but not both) is <code>null</code>
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ * @since 2.2
+ */
+ public static int reflectionCompare(Object lhs, Object rhs, Collection<String> excludeFields) {
+ return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
+ }
+
+ /**
+ * <p>Compares two <code>Object</code>s via reflection.</p>
+ *
+ * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
+ * is used to bypass normal access control checks. This will fail under a
+ * security manager unless the appropriate permissions are set.</p>
+ *
+ * <ul>
+ * <li>Static fields will not be compared</li>
+ * <li>If <code>compareTransients</code> is <code>true</code>,
+ * compares transient members. Otherwise ignores them, as they
+ * are likely derived fields.</li>
+ * <li>Superclass fields will be compared</li>
+ * </ul>
+ *
+ * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
+ * they are considered equal.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @param excludeFields array of fields to exclude
+ * @return a negative integer, zero, or a positive integer as <code>lhs</code>
+ * is less than, equal to, or greater than <code>rhs</code>
+ * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
+ * (but not both) is <code>null</code>
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ * @since 2.2
+ */
+ public static int reflectionCompare(Object lhs, Object rhs, String... excludeFields) {
+ return reflectionCompare(lhs, rhs, false, null, excludeFields);
+ }
+
+ /**
+ * <p>Compares two <code>Object</code>s via reflection.</p>
+ *
+ * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
+ * is used to bypass normal access control checks. This will fail under a
+ * security manager unless the appropriate permissions are set.</p>
+ *
+ * <ul>
+ * <li>Static fields will not be compared</li>
+ * <li>If the <code>compareTransients</code> is <code>true</code>,
+ * compares transient members. Otherwise ignores them, as they
+ * are likely derived fields.</li>
+ * <li>Compares superclass fields up to and including <code>reflectUpToClass</code>.
+ * If <code>reflectUpToClass</code> is <code>null</code>, compares all superclass fields.</li>
+ * </ul>
+ *
+ * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
+ * they are considered equal.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @param compareTransients whether to compare transient fields
+ * @param reflectUpToClass last superclass for which fields are compared
+ * @param excludeFields fields to exclude
+ * @return a negative integer, zero, or a positive integer as <code>lhs</code>
+ * is less than, equal to, or greater than <code>rhs</code>
+ * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
+ * (but not both) is <code>null</code>
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ * @since 2.2 (2.0 as <code>reflectionCompare(Object, Object, boolean, Class)</code>)
+ */
+ public static int reflectionCompare(
+ Object lhs,
+ Object rhs,
+ boolean compareTransients,
+ Class<?> reflectUpToClass,
+ String... excludeFields) {
+
+ if (lhs == rhs) {
+ return 0;
+ }
+ if (lhs == null || rhs == null) {
+ throw new NullPointerException();
+ }
+ Class<?> lhsClazz = lhs.getClass();
+ if (!lhsClazz.isInstance(rhs)) {
+ throw new ClassCastException();
+ }
+ CompareToBuilder compareToBuilder = new CompareToBuilder();
+ reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
+ while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
+ lhsClazz = lhsClazz.getSuperclass();
+ reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
+ }
+ return compareToBuilder.toComparison();
+ }
+
+ /**
+ * <p>Appends to <code>builder</code> the comparison of <code>lhs</code>
+ * to <code>rhs</code> using the fields defined in <code>clazz</code>.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @param clazz <code>Class</code> that defines fields to be compared
+ * @param builder <code>CompareToBuilder</code> to append to
+ * @param useTransients whether to compare transient fields
+ * @param excludeFields fields to exclude
+ */
+ private static void reflectionAppend(
+ Object lhs,
+ Object rhs,
+ Class<?> clazz,
+ CompareToBuilder builder,
+ boolean useTransients,
+ String[] excludeFields) {
+
+ Field[] fields = clazz.getDeclaredFields();
+ AccessibleObject.setAccessible(fields, true);
+ for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
+ Field f = fields[i];
+ if (!ArrayUtils.contains(excludeFields, f.getName())
+ && (f.getName().indexOf('$') == -1)
+ && (useTransients || !Modifier.isTransient(f.getModifiers()))
+ && (!Modifier.isStatic(f.getModifiers()))) {
+ try {
+ builder.append(f.get(lhs), f.get(rhs));
+ } catch (IllegalAccessException e) {
+ // This can't happen. Would get a Security exception instead.
+ // Throw a runtime exception in case the impossible happens.
+ throw new InternalError("Unexpected IllegalAccessException");
+ }
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Appends to the <code>builder</code> the <code>compareTo(Object)</code>
+ * result of the superclass.</p>
+ *
+ * @param superCompareTo result of calling <code>super.compareTo(Object)</code>
+ * @return this - used to chain append calls
+ * @since 2.0
+ */
+ public CompareToBuilder appendSuper(int superCompareTo) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = superCompareTo;
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Appends to the <code>builder</code> the comparison of
+ * two <code>Object</code>s.</p>
+ *
+ * <ol>
+ * <li>Check if <code>lhs == rhs</code></li>
+ * <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>,
+ * a <code>null</code> object is less than a non-<code>null</code> object</li>
+ * <li>Check the object contents</li>
+ * </ol>
+ *
+ * <p><code>lhs</code> must either be an array or implement {@link Comparable}.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @return this - used to chain append calls
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ */
+ public CompareToBuilder append(Object lhs, Object rhs) {
+ return append(lhs, rhs, null);
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the comparison of
+ * two <code>Object</code>s.</p>
+ *
+ * <ol>
+ * <li>Check if <code>lhs == rhs</code></li>
+ * <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>,
+ * a <code>null</code> object is less than a non-<code>null</code> object</li>
+ * <li>Check the object contents</li>
+ * </ol>
+ *
+ * <p>If <code>lhs</code> is an array, array comparison methods will be used.
+ * Otherwise <code>comparator</code> will be used to compare the objects.
+ * If <code>comparator</code> is <code>null</code>, <code>lhs</code> must
+ * implement {@link Comparable} instead.</p>
+ *
+ * @param lhs left-hand object
+ * @param rhs right-hand object
+ * @param comparator <code>Comparator</code> used to compare the objects,
+ * <code>null</code> means treat lhs as <code>Comparable</code>
+ * @return this - used to chain append calls
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ * @since 2.0
+ */
+ public CompareToBuilder append(Object lhs, Object rhs, Comparator<?> comparator) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.getClass().isArray()) {
+ // switch on type of array, to dispatch to the correct handler
+ // handles multi dimensional arrays
+ // throws a ClassCastException if rhs is not the correct array type
+ if (lhs instanceof long[]) {
+ append((long[]) lhs, (long[]) rhs);
+ } else if (lhs instanceof int[]) {
+ append((int[]) lhs, (int[]) rhs);
+ } else if (lhs instanceof short[]) {
+ append((short[]) lhs, (short[]) rhs);
+ } else if (lhs instanceof char[]) {
+ append((char[]) lhs, (char[]) rhs);
+ } else if (lhs instanceof byte[]) {
+ append((byte[]) lhs, (byte[]) rhs);
+ } else if (lhs instanceof double[]) {
+ append((double[]) lhs, (double[]) rhs);
+ } else if (lhs instanceof float[]) {
+ append((float[]) lhs, (float[]) rhs);
+ } else if (lhs instanceof boolean[]) {
+ append((boolean[]) lhs, (boolean[]) rhs);
+ } else {
+ // not an array of primitives
+ // throws a ClassCastException if rhs is not an array
+ append((Object[]) lhs, (Object[]) rhs, comparator);
+ }
+ } else {
+ // the simple case, not an array, just test the element
+ if (comparator == null) {
+ @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
+ final Comparable<Object> comparable = (Comparable<Object>) lhs;
+ comparison = comparable.compareTo(rhs);
+ } else {
+ @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
+ final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
+ comparison = comparator2.compare(lhs, rhs);
+ }
+ }
+ return this;
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Appends to the <code>builder</code> the comparison of
+ * two <code>long</code>s.
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(long lhs, long rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
+ return this;
+ }
+
+ /**
+ * Appends to the <code>builder</code> the comparison of
+ * two <code>int</code>s.
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(int lhs, int rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
+ return this;
+ }
+
+ /**
+ * Appends to the <code>builder</code> the comparison of
+ * two <code>short</code>s.
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(short lhs, short rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
+ return this;
+ }
+
+ /**
+ * Appends to the <code>builder</code> the comparison of
+ * two <code>char</code>s.
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(char lhs, char rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
+ return this;
+ }
+
+ /**
+ * Appends to the <code>builder</code> the comparison of
+ * two <code>byte</code>s.
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(byte lhs, byte rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the comparison of
+ * two <code>double</code>s.</p>
+ *
+ * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
+ *
+ * <p>It is compatible with the hash code generated by
+ * <code>HashCodeBuilder</code>.</p>
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(double lhs, double rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = Double.compare(lhs, rhs);
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the comparison of
+ * two <code>float</code>s.</p>
+ *
+ * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
+ *
+ * <p>It is compatible with the hash code generated by
+ * <code>HashCodeBuilder</code>.</p>
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(float lhs, float rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ comparison = Float.compare(lhs, rhs);
+ return this;
+ }
+
+ /**
+ * Appends to the <code>builder</code> the comparison of
+ * two <code>booleans</code>s.
+ *
+ * @param lhs left-hand value
+ * @param rhs right-hand value
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(boolean lhs, boolean rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == false) {
+ comparison = -1;
+ } else {
+ comparison = +1;
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>Object</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a short length array is less than a long length array</li>
+ * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
+ * </ol>
+ *
+ * <p>This method will also will be called for the top level of multi-dimensional,
+ * ragged, and multi-typed arrays.</p>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ */
+ public CompareToBuilder append(Object[] lhs, Object[] rhs) {
+ return append(lhs, rhs, null);
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>Object</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a short length array is less than a long length array</li>
+ * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
+ * </ol>
+ *
+ * <p>This method will also will be called for the top level of multi-dimensional,
+ * ragged, and multi-typed arrays.</p>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @param comparator <code>Comparator</code> to use to compare the array elements,
+ * <code>null</code> means to treat <code>lhs</code> elements as <code>Comparable</code>.
+ * @return this - used to chain append calls
+ * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
+ * with <code>lhs</code>
+ * @since 2.0
+ */
+ public CompareToBuilder append(Object[] lhs, Object[] rhs, Comparator<?> comparator) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i], comparator);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>long</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(long, long)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(long[] lhs, long[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>int</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(int, int)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(int[] lhs, int[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>short</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(short, short)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(short[] lhs, short[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>char</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(char, char)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(char[] lhs, char[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>byte</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(byte, byte)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(byte[] lhs, byte[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>double</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(double, double)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(double[] lhs, double[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>float</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(float, float)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(float[] lhs, float[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Appends to the <code>builder</code> the deep comparison of
+ * two <code>boolean</code> arrays.</p>
+ *
+ * <ol>
+ * <li>Check if arrays are the same using <code>==</code></li>
+ * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
+ * <li>Check array length, a shorter length array is less than a longer length array</li>
+ * <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
+ * </ol>
+ *
+ * @param lhs left-hand array
+ * @param rhs right-hand array
+ * @return this - used to chain append calls
+ */
+ public CompareToBuilder append(boolean[] lhs, boolean[] rhs) {
+ if (comparison != 0) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null) {
+ comparison = -1;
+ return this;
+ }
+ if (rhs == null) {
+ comparison = +1;
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ comparison = (lhs.length < rhs.length) ? -1 : +1;
+ return this;
+ }
+ for (int i = 0; i < lhs.length && comparison == 0; i++) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a negative integer, a positive integer, or zero as
+ * the <code>builder</code> has judged the "left-hand" side
+ * as less than, greater than, or equal to the "right-hand"
+ * side.
+ *
+ * @return final comparison result
+ */
+ public int toComparison() {
+ return comparison;
+ }
+
+ /**
+ * Returns a negative integer, a positive integer, or zero as
+ * the <code>builder</code> has judged the "left-hand" side
+ * as less than, greater than, or equal to the "right-hand"
+ * side.
+ *
+ * @return final comparison result
+ *
+ * @since 3.0
+ */
+ public Integer build() {
+ return Integer.valueOf(toComparison());
+ }
+}
+
diff --git a/src/org/apache/commons/lang3/builder/EqualsBuilder.java b/src/org/apache/commons/lang3/builder/EqualsBuilder.java new file mode 100644 index 0000000..d619495 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/EqualsBuilder.java @@ -0,0 +1,944 @@ +/*
+ * 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.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * <p>Assists in implementing {@link Object#equals(Object)} methods.</p>
+ *
+ * <p> This class provides methods to build a good equals method for any
+ * class. It follows rules laid out in
+ * <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
+ * , by Joshua Bloch. In particular the rule for comparing <code>doubles</code>,
+ * <code>floats</code>, and arrays can be tricky. Also, making sure that
+ * <code>equals()</code> and <code>hashCode()</code> are consistent can be
+ * difficult.</p>
+ *
+ * <p>Two Objects that compare as equals must generate the same hash code,
+ * but two Objects with the same hash code do not have to be equal.</p>
+ *
+ * <p>All relevant fields should be included in the calculation of equals.
+ * Derived fields may be ignored. In particular, any field used in
+ * generating a hash code must be used in the equals method, and vice
+ * versa.</p>
+ *
+ * <p>Typical use for the code is as follows:</p>
+ * <pre>
+ * public boolean equals(Object obj) {
+ * if (obj == null) { return false; }
+ * if (obj == this) { return true; }
+ * if (obj.getClass() != getClass()) {
+ * return false;
+ * }
+ * MyClass rhs = (MyClass) obj;
+ * return new EqualsBuilder()
+ * .appendSuper(super.equals(obj))
+ * .append(field1, rhs.field1)
+ * .append(field2, rhs.field2)
+ * .append(field3, rhs.field3)
+ * .isEquals();
+ * }
+ * </pre>
+ *
+ * <p> Alternatively, there is a method that uses reflection to determine
+ * the fields to test. Because these fields are usually private, the method,
+ * <code>reflectionEquals</code>, uses <code>AccessibleObject.setAccessible</code> to
+ * change the visibility of the fields. This will fail under a security
+ * manager, unless the appropriate permissions are set up correctly. It is
+ * also slower than testing explicitly.</p>
+ *
+ * <p> A typical invocation for this method would look like:</p>
+ * <pre>
+ * public boolean equals(Object obj) {
+ * return EqualsBuilder.reflectionEquals(this, obj);
+ * }
+ * </pre>
+ *
+ * @since 1.0
+ * @version $Id: EqualsBuilder.java 1091531 2011-04-12 18:29:49Z ggregory $
+ */
+public class EqualsBuilder implements Builder<Boolean> {
+
+ /**
+ * <p>
+ * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
+ * </p>
+ *
+ * @since 3.0
+ */
+ private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = new ThreadLocal<Set<Pair<IDKey, IDKey>>>();
+
+ /*
+ * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
+ * we are in the process of calculating.
+ *
+ * So we generate a one-to-one mapping from the original object to a new object.
+ *
+ * Now HashSet uses equals() to determine if two elements with the same hashcode really
+ * are equal, so we also need to ensure that the replacement objects are only equal
+ * if the original objects are identical.
+ *
+ * The original implementation (2.4 and before) used the System.indentityHashCode()
+ * method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
+ *
+ * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
+ * to disambiguate the duplicate ids.
+ */
+
+ /**
+ * <p>
+ * Returns the registry of object pairs being traversed by the reflection
+ * methods in the current thread.
+ * </p>
+ *
+ * @return Set the registry of objects being traversed
+ * @since 3.0
+ */
+ static Set<Pair<IDKey, IDKey>> getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ * <p>
+ * Converters value pair into a register pair.
+ * </p>
+ *
+ * @param lhs <code>this</code> object
+ * @param rhs the other object
+ *
+ * @return the pair
+ */
+ static Pair<IDKey, IDKey> getRegisterPair(Object lhs, Object rhs) {
+ IDKey left = new IDKey(lhs);
+ IDKey right = new IDKey(rhs);
+ return Pair.of(left, right);
+ }
+
+ /**
+ * <p>
+ * Returns <code>true</code> if the registry contains the given object pair.
+ * Used by the reflection methods to avoid infinite loops.
+ * Objects might be swapped therefore a check is needed if the object pair
+ * is registered in given or swapped order.
+ * </p>
+ *
+ * @param lhs <code>this</code> object to lookup in registry
+ * @param rhs the other object to lookup on registry
+ * @return boolean <code>true</code> if the registry contains the given object.
+ * @since 3.0
+ */
+ static boolean isRegistered(Object lhs, Object rhs) {
+ Set<Pair<IDKey, IDKey>> registry = getRegistry();
+ Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
+ Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getLeft(), pair.getRight());
+
+ return registry != null
+ && (registry.contains(pair) || registry.contains(swappedPair));
+ }
+
+ /**
+ * <p>
+ * Registers the given object pair.
+ * Used by the reflection methods to avoid infinite loops.
+ * </p>
+ *
+ * @param lhs <code>this</code> object to register
+ * @param rhs the other object to register
+ */
+ static void register(Object lhs, Object rhs) {
+ synchronized (EqualsBuilder.class) {
+ if (getRegistry() == null) {
+ REGISTRY.set(new HashSet<Pair<IDKey, IDKey>>());
+ }
+ }
+
+ Set<Pair<IDKey, IDKey>> registry = getRegistry();
+ Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
+ registry.add(pair);
+ }
+
+ /**
+ * <p>
+ * Unregisters the given object pair.
+ * </p>
+ *
+ * <p>
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ * @param lhs <code>this</code> object to unregister
+ * @param rhs the other object to unregister
+ * @since 3.0
+ */
+ static void unregister(Object lhs, Object rhs) {
+ Set<Pair<IDKey, IDKey>> registry = getRegistry();
+ if (registry != null) {
+ Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
+ registry.remove(pair);
+ synchronized (EqualsBuilder.class) {
+ //read again
+ registry = getRegistry();
+ if (registry != null && registry.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * If the fields tested are equals.
+ * The default value is <code>true</code>.
+ */
+ private boolean isEquals = true;
+
+ /**
+ * <p>Constructor for EqualsBuilder.</p>
+ *
+ * <p>Starts off assuming that equals is <code>true</code>.</p>
+ * @see Object#equals(Object)
+ */
+ public EqualsBuilder() {
+ // do nothing for now.
+ }
+
+ //-------------------------------------------------------------------------
+
+ /**
+ * <p>This method uses reflection to determine if the two <code>Object</code>s
+ * are equal.</p>
+ *
+ * <p>It uses <code>AccessibleObject.setAccessible</code> 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 tested, as they are likely derived
+ * fields, and not part of the value of the Object.</p>
+ *
+ * <p>Static fields will not be tested. Superclass fields will be included.</p>
+ *
+ * @param lhs <code>this</code> object
+ * @param rhs the other object
+ * @param excludeFields Collection of String field names to exclude from testing
+ * @return <code>true</code> if the two Objects have tested equals.
+ */
+ public static boolean reflectionEquals(Object lhs, Object rhs, Collection<String> excludeFields) {
+ return reflectionEquals(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
+ }
+
+ /**
+ * <p>This method uses reflection to determine if the two <code>Object</code>s
+ * are equal.</p>
+ *
+ * <p>It uses <code>AccessibleObject.setAccessible</code> 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 tested, as they are likely derived
+ * fields, and not part of the value of the Object.</p>
+ *
+ * <p>Static fields will not be tested. Superclass fields will be included.</p>
+ *
+ * @param lhs <code>this</code> object
+ * @param rhs the other object
+ * @param excludeFields array of field names to exclude from testing
+ * @return <code>true</code> if the two Objects have tested equals.
+ */
+ public static boolean reflectionEquals(Object lhs, Object rhs, String... excludeFields) {
+ return reflectionEquals(lhs, rhs, false, null, excludeFields);
+ }
+
+ /**
+ * <p>This method uses reflection to determine if the two <code>Object</code>s
+ * are equal.</p>
+ *
+ * <p>It uses <code>AccessibleObject.setAccessible</code> 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 TestTransients parameter is set to <code>true</code>, transient
+ * members will be tested, otherwise they are ignored, as they are likely
+ * derived fields, and not part of the value of the <code>Object</code>.</p>
+ *
+ * <p>Static fields will not be tested. Superclass fields will be included.</p>
+ *
+ * @param lhs <code>this</code> object
+ * @param rhs the other object
+ * @param testTransients whether to include transient fields
+ * @return <code>true</code> if the two Objects have tested equals.
+ */
+ public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) {
+ return reflectionEquals(lhs, rhs, testTransients, null);
+ }
+
+ /**
+ * <p>This method uses reflection to determine if the two <code>Object</code>s
+ * are equal.</p>
+ *
+ * <p>It uses <code>AccessibleObject.setAccessible</code> 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 testTransients parameter is set to <code>true</code>, transient
+ * members will be tested, otherwise they are ignored, as they are likely
+ * derived fields, and not part of the value of the <code>Object</code>.</p>
+ *
+ * <p>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.</p>
+ *
+ * @param lhs <code>this</code> object
+ * @param rhs the other object
+ * @param testTransients whether to include transient fields
+ * @param reflectUpToClass the superclass to reflect up to (inclusive),
+ * may be <code>null</code>
+ * @param excludeFields array of field names to exclude from testing
+ * @return <code>true</code> if the two Objects have tested equals.
+ * @since 2.0
+ */
+ public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class<?> reflectUpToClass,
+ String... excludeFields) {
+ if (lhs == rhs) {
+ return true;
+ }
+ if (lhs == null || rhs == null) {
+ return false;
+ }
+ // Find the leaf class since there may be transients in the leaf
+ // class or in classes between the leaf and root.
+ // If we are not testing transients or a subclass has no ivars,
+ // then a subclass can test equals to a superclass.
+ Class<?> lhsClass = lhs.getClass();
+ Class<?> rhsClass = rhs.getClass();
+ Class<?> testClass;
+ if (lhsClass.isInstance(rhs)) {
+ testClass = lhsClass;
+ if (!rhsClass.isInstance(lhs)) {
+ // rhsClass is a subclass of lhsClass
+ testClass = rhsClass;
+ }
+ } else if (rhsClass.isInstance(lhs)) {
+ testClass = rhsClass;
+ if (!lhsClass.isInstance(rhs)) {
+ // lhsClass is a subclass of rhsClass
+ testClass = lhsClass;
+ }
+ } else {
+ // The two classes are not related.
+ return false;
+ }
+ EqualsBuilder equalsBuilder = new EqualsBuilder();
+ try {
+ reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
+ while (testClass.getSuperclass() != null && testClass != reflectUpToClass) {
+ testClass = testClass.getSuperclass();
+ reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
+ }
+ } catch (IllegalArgumentException e) {
+ // In this case, we tried to test a subclass vs. a superclass and
+ // the subclass has ivars or the ivars are transient and
+ // we are testing transients.
+ // If a subclass has ivars that we are trying to test them, we get an
+ // exception and we know that the objects are not equal.
+ return false;
+ }
+ return equalsBuilder.isEquals();
+ }
+
+ /**
+ * <p>Appends the fields and values defined by the given object of the
+ * given Class.</p>
+ *
+ * @param lhs the left hand object
+ * @param rhs the right hand object
+ * @param clazz the class to append details of
+ * @param builder the builder to append to
+ * @param useTransients whether to test transient fields
+ * @param excludeFields array of field names to exclude from testing
+ */
+ private static void reflectionAppend(
+ Object lhs,
+ Object rhs,
+ Class<?> clazz,
+ EqualsBuilder builder,
+ boolean useTransients,
+ String[] excludeFields) {
+
+ if (isRegistered(lhs, rhs)) {
+ return;
+ }
+
+ try {
+ register(lhs, rhs);
+ Field[] fields = clazz.getDeclaredFields();
+ AccessibleObject.setAccessible(fields, true);
+ for (int i = 0; i < fields.length && builder.isEquals; i++) {
+ Field f = fields[i];
+ if (!ArrayUtils.contains(excludeFields, f.getName())
+ && (f.getName().indexOf('$') == -1)
+ && (useTransients || !Modifier.isTransient(f.getModifiers()))
+ && (!Modifier.isStatic(f.getModifiers()))) {
+ try {
+ builder.append(f.get(lhs), f.get(rhs));
+ } catch (IllegalAccessException e) {
+ //this can't happen. Would get a Security exception instead
+ //throw a runtime exception in case the impossible happens.
+ throw new InternalError("Unexpected IllegalAccessException");
+ }
+ }
+ }
+ } finally {
+ unregister(lhs, rhs);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+
+ /**
+ * <p>Adds the result of <code>super.equals()</code> to this builder.</p>
+ *
+ * @param superEquals the result of calling <code>super.equals()</code>
+ * @return EqualsBuilder - used to chain calls.
+ * @since 2.0
+ */
+ public EqualsBuilder appendSuper(boolean superEquals) {
+ if (isEquals == false) {
+ return this;
+ }
+ isEquals = superEquals;
+ return this;
+ }
+
+ //-------------------------------------------------------------------------
+
+ /**
+ * <p>Test if two <code>Object</code>s are equal using their
+ * <code>equals</code> method.</p>
+ *
+ * @param lhs the left hand object
+ * @param rhs the right hand object
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(Object lhs, Object rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ Class<?> lhsClass = lhs.getClass();
+ if (!lhsClass.isArray()) {
+ // The simple case, not an array, just test the element
+ isEquals = lhs.equals(rhs);
+ } else if (lhs.getClass() != rhs.getClass()) {
+ // Here when we compare different dimensions, for example: a boolean[][] to a boolean[]
+ this.setEquals(false);
+ }
+ // 'Switch' on type of array, to dispatch to the correct handler
+ // This handles multi dimensional arrays of the same depth
+ else if (lhs instanceof long[]) {
+ append((long[]) lhs, (long[]) rhs);
+ } else if (lhs instanceof int[]) {
+ append((int[]) lhs, (int[]) rhs);
+ } else if (lhs instanceof short[]) {
+ append((short[]) lhs, (short[]) rhs);
+ } else if (lhs instanceof char[]) {
+ append((char[]) lhs, (char[]) rhs);
+ } else if (lhs instanceof byte[]) {
+ append((byte[]) lhs, (byte[]) rhs);
+ } else if (lhs instanceof double[]) {
+ append((double[]) lhs, (double[]) rhs);
+ } else if (lhs instanceof float[]) {
+ append((float[]) lhs, (float[]) rhs);
+ } else if (lhs instanceof boolean[]) {
+ append((boolean[]) lhs, (boolean[]) rhs);
+ } else {
+ // Not an array of primitives
+ append((Object[]) lhs, (Object[]) rhs);
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Test if two <code>long</code> s are equal.
+ * </p>
+ *
+ * @param lhs
+ * the left hand <code>long</code>
+ * @param rhs
+ * the right hand <code>long</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(long lhs, long rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ isEquals = (lhs == rhs);
+ return this;
+ }
+
+ /**
+ * <p>Test if two <code>int</code>s are equal.</p>
+ *
+ * @param lhs the left hand <code>int</code>
+ * @param rhs the right hand <code>int</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(int lhs, int rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ isEquals = (lhs == rhs);
+ return this;
+ }
+
+ /**
+ * <p>Test if two <code>short</code>s are equal.</p>
+ *
+ * @param lhs the left hand <code>short</code>
+ * @param rhs the right hand <code>short</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(short lhs, short rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ isEquals = (lhs == rhs);
+ return this;
+ }
+
+ /**
+ * <p>Test if two <code>char</code>s are equal.</p>
+ *
+ * @param lhs the left hand <code>char</code>
+ * @param rhs the right hand <code>char</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(char lhs, char rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ isEquals = (lhs == rhs);
+ return this;
+ }
+
+ /**
+ * <p>Test if two <code>byte</code>s are equal.</p>
+ *
+ * @param lhs the left hand <code>byte</code>
+ * @param rhs the right hand <code>byte</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(byte lhs, byte rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ isEquals = (lhs == rhs);
+ return this;
+ }
+
+ /**
+ * <p>Test if two <code>double</code>s are equal by testing that the
+ * pattern of bits returned by <code>doubleToLong</code> are equal.</p>
+ *
+ * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
+ *
+ * <p>It is compatible with the hash code generated by
+ * <code>HashCodeBuilder</code>.</p>
+ *
+ * @param lhs the left hand <code>double</code>
+ * @param rhs the right hand <code>double</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(double lhs, double rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
+ }
+
+ /**
+ * <p>Test if two <code>float</code>s are equal byt testing that the
+ * pattern of bits returned by doubleToLong are equal.</p>
+ *
+ * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
+ *
+ * <p>It is compatible with the hash code generated by
+ * <code>HashCodeBuilder</code>.</p>
+ *
+ * @param lhs the left hand <code>float</code>
+ * @param rhs the right hand <code>float</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(float lhs, float rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
+ }
+
+ /**
+ * <p>Test if two <code>booleans</code>s are equal.</p>
+ *
+ * @param lhs the left hand <code>boolean</code>
+ * @param rhs the right hand <code>boolean</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(boolean lhs, boolean rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ isEquals = (lhs == rhs);
+ return this;
+ }
+
+ /**
+ * <p>Performs a deep comparison of two <code>Object</code> arrays.</p>
+ *
+ * <p>This also will be called for the top level of
+ * multi-dimensional, ragged, and multi-typed arrays.</p>
+ *
+ * @param lhs the left hand <code>Object[]</code>
+ * @param rhs the right hand <code>Object[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(Object[] lhs, Object[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>long</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(long, long)} is used.</p>
+ *
+ * @param lhs the left hand <code>long[]</code>
+ * @param rhs the right hand <code>long[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(long[] lhs, long[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>int</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(int, int)} is used.</p>
+ *
+ * @param lhs the left hand <code>int[]</code>
+ * @param rhs the right hand <code>int[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(int[] lhs, int[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>short</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(short, short)} is used.</p>
+ *
+ * @param lhs the left hand <code>short[]</code>
+ * @param rhs the right hand <code>short[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(short[] lhs, short[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>char</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(char, char)} is used.</p>
+ *
+ * @param lhs the left hand <code>char[]</code>
+ * @param rhs the right hand <code>char[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(char[] lhs, char[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>byte</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(byte, byte)} is used.</p>
+ *
+ * @param lhs the left hand <code>byte[]</code>
+ * @param rhs the right hand <code>byte[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(byte[] lhs, byte[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>double</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(double, double)} is used.</p>
+ *
+ * @param lhs the left hand <code>double[]</code>
+ * @param rhs the right hand <code>double[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(double[] lhs, double[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>float</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(float, float)} is used.</p>
+ *
+ * @param lhs the left hand <code>float[]</code>
+ * @param rhs the right hand <code>float[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(float[] lhs, float[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Deep comparison of array of <code>boolean</code>. Length and all
+ * values are compared.</p>
+ *
+ * <p>The method {@link #append(boolean, boolean)} is used.</p>
+ *
+ * @param lhs the left hand <code>boolean[]</code>
+ * @param rhs the right hand <code>boolean[]</code>
+ * @return EqualsBuilder - used to chain calls.
+ */
+ public EqualsBuilder append(boolean[] lhs, boolean[] rhs) {
+ if (isEquals == false) {
+ return this;
+ }
+ if (lhs == rhs) {
+ return this;
+ }
+ if (lhs == null || rhs == null) {
+ this.setEquals(false);
+ return this;
+ }
+ if (lhs.length != rhs.length) {
+ this.setEquals(false);
+ return this;
+ }
+ for (int i = 0; i < lhs.length && isEquals; ++i) {
+ append(lhs[i], rhs[i]);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Returns <code>true</code> if the fields that have been checked
+ * are all equal.</p>
+ *
+ * @return boolean
+ */
+ public boolean isEquals() {
+ return this.isEquals;
+ }
+
+ /**
+ * <p>Returns <code>true</code> if the fields that have been checked
+ * are all equal.</p>
+ *
+ * @return <code>true</code> if all of the fields that have been checked
+ * are equal, <code>false</code> otherwise.
+ *
+ * @since 3.0
+ */
+ public Boolean build() {
+ return Boolean.valueOf(isEquals());
+ }
+
+ /**
+ * Sets the <code>isEquals</code> value.
+ *
+ * @param isEquals The value to set.
+ * @since 2.1
+ */
+ protected void setEquals(boolean isEquals) {
+ this.isEquals = isEquals;
+ }
+
+ /**
+ * Reset the EqualsBuilder so you can use the same object again
+ * @since 2.5
+ */
+ public void reset() {
+ this.isEquals = true;
+ }
+}
diff --git a/src/org/apache/commons/lang3/builder/HashCodeBuilder.java b/src/org/apache/commons/lang3/builder/HashCodeBuilder.java new file mode 100644 index 0000000..9ae2bd5 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/HashCodeBuilder.java @@ -0,0 +1,961 @@ +/*
+ * 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.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * <p>
+ * Assists in implementing {@link Object#hashCode()} methods.
+ * </p>
+ *
+ * <p>
+ * This class enables a good <code>hashCode</code> method to be built for any class. It follows the rules laid out in
+ * the book <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a> by Joshua Bloch. Writing a
+ * good <code>hashCode</code> method is actually quite difficult. This class aims to simplify the process.
+ * </p>
+ *
+ * <p>
+ * The following is the approach taken. When appending a data field, the current total is multiplied by the
+ * multiplier then a relevant value
+ * for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then
+ * appending the integer 45 will create a hashcode of 674, namely 17 * 37 + 45.
+ * </p>
+ *
+ * <p>
+ * All relevant fields from the object should be included in the <code>hashCode</code> method. Derived fields may be
+ * excluded. In general, any field used in the <code>equals</code> method must be used in the <code>hashCode</code>
+ * method.
+ * </p>
+ *
+ * <p>
+ * To use this class write code as follows:
+ * </p>
+ *
+ * <pre>
+ * public class Person {
+ * String name;
+ * int age;
+ * boolean smoker;
+ * ...
+ *
+ * public int hashCode() {
+ * // you pick a hard-coded, randomly chosen, non-zero, odd number
+ * // ideally different for each class
+ * return new HashCodeBuilder(17, 37).
+ * append(name).
+ * append(age).
+ * append(smoker).
+ * toHashCode();
+ * }
+ * }
+ * </pre>
+ *
+ * <p>
+ * If required, the superclass <code>hashCode()</code> can be added using {@link #appendSuper}.
+ * </p>
+ *
+ * <p>
+ * Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are
+ * usually private, the method, <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code>
+ * to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions
+ * are set up correctly. It is also slower than testing explicitly.
+ * </p>
+ *
+ * <p>
+ * A typical invocation for this method would look like:
+ * </p>
+ *
+ * <pre>
+ * public int hashCode() {
+ * return HashCodeBuilder.reflectionHashCode(this);
+ * }
+ * </pre>
+ *
+ * @since 1.0
+ * @version $Id: HashCodeBuilder.java 1144929 2011-07-10 18:26:16Z ggregory $
+ */
+public class HashCodeBuilder implements Builder<Integer> {
+ /**
+ * <p>
+ * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
+ * </p>
+ *
+ * @since 2.3
+ */
+ private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<Set<IDKey>>();
+
+ /*
+ * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
+ * we are in the process of calculating.
+ *
+ * So we generate a one-to-one mapping from the original object to a new object.
+ *
+ * Now HashSet uses equals() to determine if two elements with the same hashcode really
+ * are equal, so we also need to ensure that the replacement objects are only equal
+ * if the original objects are identical.
+ *
+ * The original implementation (2.4 and before) used the System.indentityHashCode()
+ * method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
+ *
+ * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
+ * to disambiguate the duplicate ids.
+ */
+
+ /**
+ * <p>
+ * Returns the registry of objects being traversed by the reflection methods in the current thread.
+ * </p>
+ *
+ * @return Set the registry of objects being traversed
+ * @since 2.3
+ */
+ static Set<IDKey> getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ * <p>
+ * Returns <code>true</code> if the registry contains the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ * </p>
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean <code>true</code> if the registry contains the given object.
+ * @since 2.3
+ */
+ static boolean isRegistered(Object value) {
+ Set<IDKey> registry = getRegistry();
+ return registry != null && registry.contains(new IDKey(value));
+ }
+
+ /**
+ * <p>
+ * Appends the fields and values defined by the given object of the given <code>Class</code>.
+ * </p>
+ *
+ * @param object
+ * the object to append details of
+ * @param clazz
+ * the class to append details of
+ * @param builder
+ * the builder to append to
+ * @param useTransients
+ * whether to use transient fields
+ * @param excludeFields
+ * Collection of String field names to exclude from use in calculation of hash code
+ */
+ private static void reflectionAppend(Object object, Class<?> clazz, HashCodeBuilder builder, boolean useTransients,
+ String[] excludeFields) {
+ if (isRegistered(object)) {
+ return;
+ }
+ try {
+ register(object);
+ Field[] fields = clazz.getDeclaredFields();
+ AccessibleObject.setAccessible(fields, true);
+ for (Field field : fields) {
+ if (!ArrayUtils.contains(excludeFields, field.getName())
+ && (field.getName().indexOf('$') == -1)
+ && (useTransients || !Modifier.isTransient(field.getModifiers()))
+ && (!Modifier.isStatic(field.getModifiers()))) {
+ try {
+ Object fieldValue = field.get(object);
+ builder.append(fieldValue);
+ } catch (IllegalAccessException e) {
+ // this can't happen. Would get a Security exception instead
+ // throw a runtime exception in case the impossible happens.
+ throw new InternalError("Unexpected IllegalAccessException");
+ }
+ }
+ }
+ } finally {
+ unregister(object);
+ }
+ }
+
+ /**
+ * <p>
+ * This method uses reflection to build a valid hash code.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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 used, as they are likely derived fields, and not part of the value of the
+ * <code>Object</code>.
+ * </p>
+ *
+ * <p>
+ * Static fields will not be tested. Superclass fields will be included.
+ * </p>
+ *
+ * <p>
+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+ * however this is not vital. Prime numbers are preferred, especially for the multiplier.
+ * </p>
+ *
+ * @param initialNonZeroOddNumber
+ * a non-zero, odd number used as the initial value
+ * @param multiplierNonZeroOddNumber
+ * a non-zero, odd number used as the multiplier
+ * @param object
+ * the Object to create a <code>hashCode</code> for
+ * @return int hash code
+ * @throws IllegalArgumentException
+ * if the Object is <code>null</code>
+ * @throws IllegalArgumentException
+ * if the number is zero or even
+ */
+ public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) {
+ return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null);
+ }
+
+ /**
+ * <p>
+ * This method uses reflection to build a valid hash code.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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 TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
+ * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
+ * </p>
+ *
+ * <p>
+ * Static fields will not be tested. Superclass fields will be included.
+ * </p>
+ *
+ * <p>
+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+ * however this is not vital. Prime numbers are preferred, especially for the multiplier.
+ * </p>
+ *
+ * @param initialNonZeroOddNumber
+ * a non-zero, odd number used as the initial value
+ * @param multiplierNonZeroOddNumber
+ * a non-zero, odd number used as the multiplier
+ * @param object
+ * the Object to create a <code>hashCode</code> for
+ * @param testTransients
+ * whether to include transient fields
+ * @return int hash code
+ * @throws IllegalArgumentException
+ * if the Object is <code>null</code>
+ * @throws IllegalArgumentException
+ * if the number is zero or even
+ */
+ public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object,
+ boolean testTransients) {
+ return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null);
+ }
+
+ /**
+ * <p>
+ * This method uses reflection to build a valid hash code.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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 TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
+ * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
+ * </p>
+ *
+ * <p>
+ * Static fields will not be included. Superclass fields will be included up to and including the specified
+ * superclass. A null superclass is treated as java.lang.Object.
+ * </p>
+ *
+ * <p>
+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+ * however this is not vital. Prime numbers are preferred, especially for the multiplier.
+ * </p>
+ *
+ * @param <T>
+ * the type of the object involved
+ * @param initialNonZeroOddNumber
+ * a non-zero, odd number used as the initial value
+ * @param multiplierNonZeroOddNumber
+ * a non-zero, odd number used as the multiplier
+ * @param object
+ * the Object to create a <code>hashCode</code> for
+ * @param testTransients
+ * whether to include transient fields
+ * @param reflectUpToClass
+ * the superclass to reflect up to (inclusive), may be <code>null</code>
+ * @param excludeFields
+ * array of field names to exclude from use in calculation of hash code
+ * @return int hash code
+ * @throws IllegalArgumentException
+ * if the Object is <code>null</code>
+ * @throws IllegalArgumentException
+ * if the number is zero or even
+ * @since 2.0
+ */
+ public static <T> int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, T object,
+ boolean testTransients, Class<? super T> reflectUpToClass, String... excludeFields) {
+
+ if (object == null) {
+ throw new IllegalArgumentException("The object to build a hash code for must not be null");
+ }
+ HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
+ Class<?> clazz = object.getClass();
+ reflectionAppend(object, clazz, builder, testTransients, excludeFields);
+ while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
+ clazz = clazz.getSuperclass();
+ reflectionAppend(object, clazz, builder, testTransients, excludeFields);
+ }
+ return builder.toHashCode();
+ }
+
+ /**
+ * <p>
+ * This method uses reflection to build a valid hash code.
+ * </p>
+ *
+ * <p>
+ * This constructor uses two hard coded choices for the constants needed to build a hash code.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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 TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
+ * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
+ * </p>
+ *
+ * <p>
+ * Static fields will not be tested. Superclass fields will be included.
+ * </p>
+ *
+ * @param object
+ * the Object to create a <code>hashCode</code> for
+ * @param testTransients
+ * whether to include transient fields
+ * @return int hash code
+ * @throws IllegalArgumentException
+ * if the object is <code>null</code>
+ */
+ public static int reflectionHashCode(Object object, boolean testTransients) {
+ return reflectionHashCode(17, 37, object, testTransients, null);
+ }
+
+ /**
+ * <p>
+ * This method uses reflection to build a valid hash code.
+ * </p>
+ *
+ * <p>
+ * This constructor uses two hard coded choices for the constants needed to build a hash code.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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 used, as they are likely derived fields, and not part of the value of the
+ * <code>Object</code>.
+ * </p>
+ *
+ * <p>
+ * Static fields will not be tested. Superclass fields will be included.
+ * </p>
+ *
+ * @param object
+ * the Object to create a <code>hashCode</code> for
+ * @param excludeFields
+ * Collection of String field names to exclude from use in calculation of hash code
+ * @return int hash code
+ * @throws IllegalArgumentException
+ * if the object is <code>null</code>
+ */
+ public static int reflectionHashCode(Object object, Collection<String> excludeFields) {
+ return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
+ }
+
+ // -------------------------------------------------------------------------
+
+ /**
+ * <p>
+ * This method uses reflection to build a valid hash code.
+ * </p>
+ *
+ * <p>
+ * This constructor uses two hard coded choices for the constants needed to build a hash code.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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 used, as they are likely derived fields, and not part of the value of the
+ * <code>Object</code>.
+ * </p>
+ *
+ * <p>
+ * Static fields will not be tested. Superclass fields will be included.
+ * </p>
+ *
+ * @param object
+ * the Object to create a <code>hashCode</code> for
+ * @param excludeFields
+ * array of field names to exclude from use in calculation of hash code
+ * @return int hash code
+ * @throws IllegalArgumentException
+ * if the object is <code>null</code>
+ */
+ public static int reflectionHashCode(Object object, String... excludeFields) {
+ return reflectionHashCode(17, 37, object, false, null, excludeFields);
+ }
+
+ /**
+ * <p>
+ * Registers the given object. Used by the reflection methods to avoid infinite loops.
+ * </p>
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(Object value) {
+ synchronized (HashCodeBuilder.class) {
+ if (getRegistry() == null) {
+ REGISTRY.set(new HashSet<IDKey>());
+ }
+ }
+ getRegistry().add(new IDKey(value));
+ }
+
+ /**
+ * <p>
+ * Unregisters the given object.
+ * </p>
+ *
+ * <p>
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ * @param value
+ * The object to unregister.
+ * @since 2.3
+ */
+ static void unregister(Object value) {
+ Set<IDKey> registry = getRegistry();
+ if (registry != null) {
+ registry.remove(new IDKey(value));
+ synchronized (HashCodeBuilder.class) {
+ //read again
+ registry = getRegistry();
+ if (registry != null && registry.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Constant to use in building the hashCode.
+ */
+ private final int iConstant;
+
+ /**
+ * Running total of the hashCode.
+ */
+ private int iTotal = 0;
+
+ /**
+ * <p>
+ * Uses two hard coded choices for the constants needed to build a <code>hashCode</code>.
+ * </p>
+ */
+ public HashCodeBuilder() {
+ iConstant = 37;
+ iTotal = 17;
+ }
+
+ /**
+ * <p>
+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
+ * however this is not vital.
+ * </p>
+ *
+ * <p>
+ * Prime numbers are preferred, especially for the multiplier.
+ * </p>
+ *
+ * @param initialNonZeroOddNumber
+ * a non-zero, odd number used as the initial value
+ * @param multiplierNonZeroOddNumber
+ * a non-zero, odd number used as the multiplier
+ * @throws IllegalArgumentException
+ * if the number is zero or even
+ */
+ public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) {
+ if (initialNonZeroOddNumber == 0) {
+ throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
+ }
+ if (initialNonZeroOddNumber % 2 == 0) {
+ throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
+ }
+ if (multiplierNonZeroOddNumber == 0) {
+ throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
+ }
+ if (multiplierNonZeroOddNumber % 2 == 0) {
+ throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
+ }
+ iConstant = multiplierNonZeroOddNumber;
+ iTotal = initialNonZeroOddNumber;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>boolean</code>.
+ * </p>
+ * <p>
+ * This adds <code>1</code> when true, and <code>0</code> when false to the <code>hashCode</code>.
+ * </p>
+ * <p>
+ * This is in contrast to the standard <code>java.lang.Boolean.hashCode</code> handling, which computes
+ * a <code>hashCode</code> value of <code>1231</code> for <code>java.lang.Boolean</code> instances
+ * that represent <code>true</code> or <code>1237</code> for <code>java.lang.Boolean</code> instances
+ * that represent <code>false</code>.
+ * </p>
+ * <p>
+ * This is in accordance with the <quote>Effective Java</quote> design.
+ * </p>
+ *
+ * @param value
+ * the boolean to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(boolean value) {
+ iTotal = iTotal * iConstant + (value ? 0 : 1);
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>boolean</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(boolean[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (boolean element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ // -------------------------------------------------------------------------
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>byte</code>.
+ * </p>
+ *
+ * @param value
+ * the byte to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(byte value) {
+ iTotal = iTotal * iConstant + value;
+ return this;
+ }
+
+ // -------------------------------------------------------------------------
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>byte</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(byte[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (byte element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>char</code>.
+ * </p>
+ *
+ * @param value
+ * the char to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(char value) {
+ iTotal = iTotal * iConstant + value;
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>char</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(char[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (char element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>double</code>.
+ * </p>
+ *
+ * @param value
+ * the double to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(double value) {
+ return append(Double.doubleToLongBits(value));
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>double</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(double[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (double element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>float</code>.
+ * </p>
+ *
+ * @param value
+ * the float to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(float value) {
+ iTotal = iTotal * iConstant + Float.floatToIntBits(value);
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>float</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(float[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (float element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for an <code>int</code>.
+ * </p>
+ *
+ * @param value
+ * the int to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(int value) {
+ iTotal = iTotal * iConstant + value;
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for an <code>int</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(int[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (int element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>long</code>.
+ * </p>
+ *
+ * @param value
+ * the long to add to the <code>hashCode</code>
+ * @return this
+ */
+ // NOTE: This method uses >> and not >>> as Effective Java and
+ // Long.hashCode do. Ideally we should switch to >>> at
+ // some stage. There are backwards compat issues, so
+ // that will have to wait for the time being. cf LANG-342.
+ public HashCodeBuilder append(long value) {
+ iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>long</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(long[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (long element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for an <code>Object</code>.
+ * </p>
+ *
+ * @param object
+ * the Object to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(Object object) {
+ if (object == null) {
+ iTotal = iTotal * iConstant;
+
+ } else {
+ if(object.getClass().isArray()) {
+ // 'Switch' on type of array, to dispatch to the correct handler
+ // This handles multi dimensional arrays
+ if (object instanceof long[]) {
+ append((long[]) object);
+ } else if (object instanceof int[]) {
+ append((int[]) object);
+ } else if (object instanceof short[]) {
+ append((short[]) object);
+ } else if (object instanceof char[]) {
+ append((char[]) object);
+ } else if (object instanceof byte[]) {
+ append((byte[]) object);
+ } else if (object instanceof double[]) {
+ append((double[]) object);
+ } else if (object instanceof float[]) {
+ append((float[]) object);
+ } else if (object instanceof boolean[]) {
+ append((boolean[]) object);
+ } else {
+ // Not an array of primitives
+ append((Object[]) object);
+ }
+ } else {
+ iTotal = iTotal * iConstant + object.hashCode();
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for an <code>Object</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(Object[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (Object element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>short</code>.
+ * </p>
+ *
+ * @param value
+ * the short to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(short value) {
+ iTotal = iTotal * iConstant + value;
+ return this;
+ }
+
+ /**
+ * <p>
+ * Append a <code>hashCode</code> for a <code>short</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public HashCodeBuilder append(short[] array) {
+ if (array == null) {
+ iTotal = iTotal * iConstant;
+ } else {
+ for (short element : array) {
+ append(element);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * <p>
+ * Adds the result of super.hashCode() to this builder.
+ * </p>
+ *
+ * @param superHashCode
+ * the result of calling <code>super.hashCode()</code>
+ * @return this HashCodeBuilder, used to chain calls.
+ * @since 2.0
+ */
+ public HashCodeBuilder appendSuper(int superHashCode) {
+ iTotal = iTotal * iConstant + superHashCode;
+ return this;
+ }
+
+ /**
+ * <p>
+ * Return the computed <code>hashCode</code>.
+ * </p>
+ *
+ * @return <code>hashCode</code> based on the fields appended
+ */
+ public int toHashCode() {
+ return iTotal;
+ }
+
+ /**
+ * Returns the computed <code>hashCode</code>.
+ *
+ * @return <code>hashCode</code> based on the fields appended
+ *
+ * @since 3.0
+ */
+ public Integer build() {
+ return Integer.valueOf(toHashCode());
+ }
+
+ /**
+ * <p>
+ * The computed <code>hashCode</code> from toHashCode() is returned due to the likelihood
+ * of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for
+ * HashCodeBuilder itself is.</p>
+ *
+ * @return <code>hashCode</code> based on the fields appended
+ * @since 2.5
+ */
+ @Override
+ public int hashCode() {
+ return toHashCode();
+ }
+
+}
diff --git a/src/org/apache/commons/lang3/builder/IDKey.java b/src/org/apache/commons/lang3/builder/IDKey.java new file mode 100644 index 0000000..68414c0 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/IDKey.java @@ -0,0 +1,74 @@ +/*
+ * 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;
+
+// adapted from org.apache.axis.utils.IDKey
+
+/**
+ * Wrap an identity key (System.identityHashCode())
+ * so that an object can only be equal() to itself.
+ *
+ * This is necessary to disambiguate the occasional duplicate
+ * identityHashCodes that can occur.
+ *
+ */
+final class IDKey {
+ private final Object value;
+ private final int id;
+
+ /**
+ * Constructor for IDKey
+ * @param _value The value
+ */
+ public IDKey(Object _value) {
+ // This is the Object hashcode
+ id = System.identityHashCode(_value);
+ // There have been some cases (LANG-459) that return the
+ // same identity hash code for different objects. So
+ // the value is also added to disambiguate these cases.
+ value = _value;
+ }
+
+ /**
+ * returns hashcode - i.e. the system identity hashcode.
+ * @return the hashcode
+ */
+ @Override
+ public int hashCode() {
+ return id;
+ }
+
+ /**
+ * checks if instances are equal
+ * @param other The other object to compare to
+ * @return if the instances are for the same object
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof IDKey)) {
+ return false;
+ }
+ IDKey idKey = (IDKey) other;
+ if (id != idKey.id) {
+ return false;
+ }
+ // Note that identity equals is used.
+ return value == idKey.value;
+ }
+}
diff --git a/src/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java b/src/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java new file mode 100644 index 0000000..df85657 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java @@ -0,0 +1,697 @@ +/*
+ * 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;
+
+/**
+ * <p>
+ * Assists in implementing {@link Object#toString()} methods using reflection.
+ * </p>
+ *
+ * <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>
+ * 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:
+ * <ul>
+ * <li>{@link #accept(java.lang.reflect.Field)}</li>
+ * <li>{@link #getValue(java.lang.reflect.Field)}</li>
+ * </ul>
+ * </p>
+ * <p>
+ * For example, this method does <i>not</i> include the <code>password</code> field in the returned
+ * <code>String</code>:
+ * </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>
+ * The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the
+ * constructor.
+ * </p>
+ *
+ * @since 2.0
+ * @version $Id: ReflectionToStringBuilder.java 1090821 2011-04-10 15:59:07Z mbenson $
+ */
+public class ReflectionToStringBuilder extends ToStringBuilder {
+
+ /**
+ * <p>
+ * Builds a <code>toString</code> value using the default <code>ToStringStyle</code> through reflection.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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</code>
+ */
+ public static String toString(Object object) {
+ return toString(object, null, false, false, null);
+ }
+
+ /**
+ * <p>
+ * Builds a <code>toString</code> value through reflection.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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</code>, the default <code>ToStringStyle</code> is used.
+ * </p>
+ *
+ * @param object
+ * the Object to be output
+ * @param style
+ * the style of the <code>toString</code> to create, may be <code>null</code>
+ * @return the String result
+ * @throws IllegalArgumentException
+ * if the Object or <code>ToStringStyle</code> is <code>null</code>
+ */
+ public static String toString(Object object, ToStringStyle style) {
+ return toString(object, style, false, false, null);
+ }
+
+ /**
+ * <p>
+ * Builds a <code>toString</code> value through reflection.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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</code> is <code>true</code>, 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</code>, the default <code>ToStringStyle</code> is used.
+ * </p>
+ *
+ * @param object
+ * the Object to be output
+ * @param style
+ * the style of the <code>toString</code> to create, may be <code>null</code>
+ * @param outputTransients
+ * whether to include transient fields
+ * @return the String result
+ * @throws IllegalArgumentException
+ * if the Object is <code>null</code>
+ */
+ public static String toString(Object object, ToStringStyle style, boolean outputTransients) {
+ return toString(object, style, outputTransients, false, null);
+ }
+
+ /**
+ * <p>
+ * Builds a <code>toString</code> value through reflection.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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</code> is <code>true</code>, 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</code> is <code>true</code>, 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</code>, the default <code>ToStringStyle</code> is used.
+ * </p>
+ *
+ * @param object
+ * the Object to be output
+ * @param style
+ * the style of the <code>toString</code> to create, may be <code>null</code>
+ * @param outputTransients
+ * whether to include transient fields
+ * @param outputStatics
+ * whether to include transient fields
+ * @return the String result
+ * @throws IllegalArgumentException
+ * if the Object is <code>null</code>
+ * @since 2.1
+ */
+ public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) {
+ return toString(object, style, outputTransients, outputStatics, null);
+ }
+
+ /**
+ * <p>
+ * Builds a <code>toString</code> value through reflection.
+ * </p>
+ *
+ * <p>
+ * It uses <code>AccessibleObject.setAccessible</code> 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</code> is <code>true</code>, 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</code> is <code>true</code>, 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</code>.
+ * </p>
+ *
+ * <p>
+ * If the style is <code>null</code>, the default <code>ToStringStyle</code> 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</code> to create, may be <code>null</code>
+ * @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</code>
+ * @return the String result
+ * @throws IllegalArgumentException
+ * if the Object is <code>null</code>
+ * @since 2.1
+ */
+ public static <T> String toString(
+ T object, ToStringStyle style, boolean outputTransients,
+ boolean outputStatics, 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(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 <code>null</code>
+ * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element
+ * is <code>null</code>.
+ *
+ * @param collection
+ * The collection to convert
+ * @return A new array of Strings.
+ */
+ static String[] toNoNullStringArray(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</code>.
+ *
+ * @param array
+ * The array to check
+ * @return The given array or a new array without null.
+ */
+ static String[] toNoNullStringArray(Object[] array) {
+ List<String> list = new ArrayList<String>(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 <code>"password"</code>.
+ *
+ * @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;
+
+ /**
+ * <p>
+ * Constructor.
+ * </p>
+ *
+ * <p>
+ * This constructor outputs using the default style set with <code>setDefaultStyle</code>.
+ * </p>
+ *
+ * @param object
+ * the Object to build a <code>toString</code> for, must not be <code>null</code>
+ * @throws IllegalArgumentException
+ * if the Object passed in is <code>null</code>
+ */
+ public ReflectionToStringBuilder(Object object) {
+ super(object);
+ }
+
+ /**
+ * <p>
+ * Constructor.
+ * </p>
+ *
+ * <p>
+ * If the style is <code>null</code>, the default style is used.
+ * </p>
+ *
+ * @param object
+ * the Object to build a <code>toString</code> for, must not be <code>null</code>
+ * @param style
+ * the style of the <code>toString</code> to create, may be <code>null</code>
+ * @throws IllegalArgumentException
+ * if the Object passed in is <code>null</code>
+ */
+ public ReflectionToStringBuilder(Object object, ToStringStyle style) {
+ super(object, style);
+ }
+
+ /**
+ * <p>
+ * Constructor.
+ * </p>
+ *
+ * <p>
+ * If the style is <code>null</code>, the default style is used.
+ * </p>
+ *
+ * <p>
+ * If the buffer is <code>null</code>, a new one is created.
+ * </p>
+ *
+ * @param object
+ * the Object to build a <code>toString</code> for
+ * @param style
+ * the style of the <code>toString</code> to create, may be <code>null</code>
+ * @param buffer
+ * the <code>StringBuffer</code> to populate, may be <code>null</code>
+ * @throws IllegalArgumentException
+ * if the Object passed in is <code>null</code>
+ */
+ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
+ super(object, style, buffer);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param <T>
+ * the type of the object
+ * @param object
+ * the Object to build a <code>toString</code> for
+ * @param style
+ * the style of the <code>toString</code> to create, may be <code>null</code>
+ * @param buffer
+ * the <code>StringBuffer</code> to populate, may be <code>null</code>
+ * @param reflectUpToClass
+ * the superclass to reflect up to (inclusive), may be <code>null</code>
+ * @param outputTransients
+ * whether to include transient fields
+ * @param outputStatics
+ * whether to include static fields
+ * @since 2.1
+ */
+ public <T> ReflectionToStringBuilder(
+ T object, ToStringStyle style, StringBuffer buffer,
+ Class<? super T> 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 <code>Field</code>.
+ * <ul>
+ * <li>Transient fields are appended only if {@link #isAppendTransients()} returns <code>true</code>.
+ * <li>Static fields are appended only if {@link #isAppendStatics()} returns <code>true</code>.
+ * <li>Inner class fields are not appened.</li>
+ * </ul>
+ *
+ * @param field
+ * The Field to test.
+ * @return Whether or not to append the given <code>Field</code>.
+ */
+ 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;
+ }
+
+ /**
+ * <p>
+ * Appends the fields and values defined by the given object of the given Class.
+ * </p>
+ *
+ * <p>
+ * If a cycle is detected as an object is "toString()'ed", such an object is rendered as if
+ * <code>Object.toString()</code> had been called and not implemented by the object.
+ * </p>
+ *
+ * @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();
+ }
+
+ /**
+ * <p>
+ * Gets the last super class to stop appending fields for.
+ * </p>
+ *
+ * @return The last super class to stop appending fields for.
+ */
+ public Class<?> getUpToClass() {
+ return this.upToClass;
+ }
+
+ /**
+ * <p>
+ * Calls <code>java.lang.reflect.Field.get(Object)</code>.
+ * </p>
+ *
+ * @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());
+ }
+
+ /**
+ * <p>
+ * Gets whether or not to append static fields.
+ * </p>
+ *
+ * @return Whether or not to append static fields.
+ * @since 2.1
+ */
+ public boolean isAppendStatics() {
+ return this.appendStatics;
+ }
+
+ /**
+ * <p>
+ * Gets whether or not to append transient fields.
+ * </p>
+ *
+ * @return Whether or not to append transient fields.
+ */
+ public boolean isAppendTransients() {
+ return this.appendTransients;
+ }
+
+ /**
+ * <p>
+ * Append to the <code>toString</code> an <code>Object</code> array.
+ * </p>
+ *
+ * @param array
+ * the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ReflectionToStringBuilder reflectionAppendArray(Object array) {
+ this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
+ return this;
+ }
+
+ /**
+ * <p>
+ * Sets whether or not to append static fields.
+ * </p>
+ *
+ * @param appendStatics
+ * Whether or not to append static fields.
+ * @since 2.1
+ */
+ public void setAppendStatics(boolean appendStatics) {
+ this.appendStatics = appendStatics;
+ }
+
+ /**
+ * <p>
+ * Sets whether or not to append transient fields.
+ * </p>
+ *
+ * @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 <code>null</code>.
+ * @return <code>this</code>
+ */
+ 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;
+ }
+
+ /**
+ * <p>
+ * Sets the last super class to stop appending fields for.
+ * </p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>
+ * Gets the String built by this builder.
+ * </p>
+ *
+ * @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();
+ }
+
+}
diff --git a/src/org/apache/commons/lang3/builder/StandardToStringStyle.java b/src/org/apache/commons/lang3/builder/StandardToStringStyle.java new file mode 100644 index 0000000..b58b154 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/StandardToStringStyle.java @@ -0,0 +1,560 @@ +/*
+ * 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;
+
+/**
+ * <p>Works with {@link ToStringBuilder} to create a <code>toString</code>.</p>
+ *
+ * <p>This class is intended to be used as a singleton.
+ * There is no need to instantiate a new style each time.
+ * Simply instantiate the class once, customize the values as required, and
+ * store the result in a public static final variable for the rest of the
+ * program to access.</p>
+ *
+ * @since 1.0
+ * @version $Id: StandardToStringStyle.java 1089740 2011-04-07 05:01:54Z bayard $
+ */
+public class StandardToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * <p>Constructor.</p>
+ */
+ public StandardToStringStyle() {
+ super();
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use the class name.</p>
+ *
+ * @return the current useClassName flag
+ */
+ @Override
+ public boolean isUseClassName() { // NOPMD as this is implementing the abstract class
+ return super.isUseClassName();
+ }
+
+ /**
+ * <p>Sets whether to use the class name.</p>
+ *
+ * @param useClassName the new useClassName flag
+ */
+ @Override
+ public void setUseClassName(boolean useClassName) { // NOPMD as this is implementing the abstract class
+ super.setUseClassName(useClassName);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to output short or long class names.</p>
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ @Override
+ public boolean isUseShortClassName() { // NOPMD as this is implementing the abstract class
+ return super.isUseShortClassName();
+ }
+
+ /**
+ * <p>Sets whether to output short or long class names.</p>
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ @Override
+ public void setUseShortClassName(boolean useShortClassName) { // NOPMD as this is implementing the abstract class
+ super.setUseShortClassName(useShortClassName);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use the identity hash code.</p>
+ * @return the current useIdentityHashCode flag
+ */
+ @Override
+ public boolean isUseIdentityHashCode() { // NOPMD as this is implementing the abstract class
+ return super.isUseIdentityHashCode();
+ }
+
+ /**
+ * <p>Sets whether to use the identity hash code.</p>
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ @Override
+ public void setUseIdentityHashCode(boolean useIdentityHashCode) { // NOPMD as this is implementing the abstract class
+ super.setUseIdentityHashCode(useIdentityHashCode);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use the field names passed in.</p>
+ *
+ * @return the current useFieldNames flag
+ */
+ @Override
+ public boolean isUseFieldNames() { // NOPMD as this is implementing the abstract class
+ return super.isUseFieldNames();
+ }
+
+ /**
+ * <p>Sets whether to use the field names passed in.</p>
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ @Override
+ public void setUseFieldNames(boolean useFieldNames) { // NOPMD as this is implementing the abstract class
+ super.setUseFieldNames(useFieldNames);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use full detail when the caller doesn't
+ * specify.</p>
+ *
+ * @return the current defaultFullDetail flag
+ */
+ @Override
+ public boolean isDefaultFullDetail() { // NOPMD as this is implementing the abstract class
+ return super.isDefaultFullDetail();
+ }
+
+ /**
+ * <p>Sets whether to use full detail when the caller doesn't
+ * specify.</p>
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ @Override
+ public void setDefaultFullDetail(boolean defaultFullDetail) { // NOPMD as this is implementing the abstract class
+ super.setDefaultFullDetail(defaultFullDetail);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to output array content detail.</p>
+ *
+ * @return the current array content detail setting
+ */
+ @Override
+ public boolean isArrayContentDetail() { // NOPMD as this is implementing the abstract class
+ return super.isArrayContentDetail();
+ }
+
+ /**
+ * <p>Sets whether to output array content detail.</p>
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ @Override
+ public void setArrayContentDetail(boolean arrayContentDetail) { // NOPMD as this is implementing the abstract class
+ super.setArrayContentDetail(arrayContentDetail);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the array start text.</p>
+ *
+ * @return the current array start text
+ */
+ @Override
+ public String getArrayStart() { // NOPMD as this is implementing the abstract class
+ return super.getArrayStart();
+ }
+
+ /**
+ * <p>Sets the array start text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param arrayStart the new array start text
+ */
+ @Override
+ public void setArrayStart(String arrayStart) { // NOPMD as this is implementing the abstract class
+ super.setArrayStart(arrayStart);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the array end text.</p>
+ *
+ * @return the current array end text
+ */
+ @Override
+ public String getArrayEnd() { // NOPMD as this is implementing the abstract class
+ return super.getArrayEnd();
+ }
+
+ /**
+ * <p>Sets the array end text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param arrayEnd the new array end text
+ */
+ @Override
+ public void setArrayEnd(String arrayEnd) { // NOPMD as this is implementing the abstract class
+ super.setArrayEnd(arrayEnd);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the array separator text.</p>
+ *
+ * @return the current array separator text
+ */
+ @Override
+ public String getArraySeparator() { // NOPMD as this is implementing the abstract class
+ return super.getArraySeparator();
+ }
+
+ /**
+ * <p>Sets the array separator text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param arraySeparator the new array separator text
+ */
+ @Override
+ public void setArraySeparator(String arraySeparator) { // NOPMD as this is implementing the abstract class
+ super.setArraySeparator(arraySeparator);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the content start text.</p>
+ *
+ * @return the current content start text
+ */
+ @Override
+ public String getContentStart() { // NOPMD as this is implementing the abstract class
+ return super.getContentStart();
+ }
+
+ /**
+ * <p>Sets the content start text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param contentStart the new content start text
+ */
+ @Override
+ public void setContentStart(String contentStart) { // NOPMD as this is implementing the abstract class
+ super.setContentStart(contentStart);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the content end text.</p>
+ *
+ * @return the current content end text
+ */
+ @Override
+ public String getContentEnd() { // NOPMD as this is implementing the abstract class
+ return super.getContentEnd();
+ }
+
+ /**
+ * <p>Sets the content end text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param contentEnd the new content end text
+ */
+ @Override
+ public void setContentEnd(String contentEnd) { // NOPMD as this is implementing the abstract class
+ super.setContentEnd(contentEnd);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the field name value separator text.</p>
+ *
+ * @return the current field name value separator text
+ */
+ @Override
+ public String getFieldNameValueSeparator() { // NOPMD as this is implementing the abstract class
+ return super.getFieldNameValueSeparator();
+ }
+
+ /**
+ * <p>Sets the field name value separator text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ @Override
+ public void setFieldNameValueSeparator(String fieldNameValueSeparator) { // NOPMD as this is implementing the abstract class
+ super.setFieldNameValueSeparator(fieldNameValueSeparator);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the field separator text.</p>
+ *
+ * @return the current field separator text
+ */
+ @Override
+ public String getFieldSeparator() { // NOPMD as this is implementing the abstract class
+ return super.getFieldSeparator();
+ }
+
+ /**
+ * <p>Sets the field separator text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ @Override
+ public void setFieldSeparator(String fieldSeparator) { // NOPMD as this is implementing the abstract class
+ super.setFieldSeparator(fieldSeparator);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether the field separator should be added at the start
+ * of each buffer.</p>
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ @Override
+ public boolean isFieldSeparatorAtStart() { // NOPMD as this is implementing the abstract class
+ return super.isFieldSeparatorAtStart();
+ }
+
+ /**
+ * <p>Sets whether the field separator should be added at the start
+ * of each buffer.</p>
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ @Override
+ public void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) { // NOPMD as this is implementing the abstract class
+ super.setFieldSeparatorAtStart(fieldSeparatorAtStart);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether the field separator should be added at the end
+ * of each buffer.</p>
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ @Override
+ public boolean isFieldSeparatorAtEnd() { // NOPMD as this is implementing the abstract class
+ return super.isFieldSeparatorAtEnd();
+ }
+
+ /**
+ * <p>Sets whether the field separator should be added at the end
+ * of each buffer.</p>
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ @Override
+ public void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) { // NOPMD as this is implementing the abstract class
+ super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the text to output when <code>null</code> found.</p>
+ *
+ * @return the current text to output when <code>null</code> found
+ */
+ @Override
+ public String getNullText() { // NOPMD as this is implementing the abstract class
+ return super.getNullText();
+ }
+
+ /**
+ * <p>Sets the text to output when <code>null</code> found.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param nullText the new text to output when <code>null</code> found
+ */
+ @Override
+ public void setNullText(String nullText) { // NOPMD as this is implementing the abstract class
+ super.setNullText(nullText);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the text to output when a <code>Collection</code>,
+ * <code>Map</code> or <code>Array</code> size is output.</p>
+ *
+ * <p>This is output before the size value.</p>
+ *
+ * @return the current start of size text
+ */
+ @Override
+ public String getSizeStartText() { // NOPMD as this is implementing the abstract class
+ return super.getSizeStartText();
+ }
+
+ /**
+ * <p>Sets the start text to output when a <code>Collection</code>,
+ * <code>Map</code> or <code>Array</code> size is output.</p>
+ *
+ * <p>This is output before the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param sizeStartText the new start of size text
+ */
+ @Override
+ public void setSizeStartText(String sizeStartText) { // NOPMD as this is implementing the abstract class
+ super.setSizeStartText(sizeStartText);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a <code>Collection</code>,
+ * <code>Map</code> or <code>Array</code> size is output.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * @return the current end of size text
+ */
+ @Override
+ public String getSizeEndText() { // NOPMD as this is implementing the abstract class
+ return super.getSizeEndText();
+ }
+
+ /**
+ * <p>Sets the end text to output when a <code>Collection</code>,
+ * <code>Map</code> or <code>Array</code> size is output.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted
+ * to an empty String.</p>
+ *
+ * @param sizeEndText the new end of size text
+ */
+ @Override
+ public void setSizeEndText(String sizeEndText) { // NOPMD as this is implementing the abstract class
+ super.setSizeEndText(sizeEndText);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the start text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <P>This is output before the size value.</p>
+ *
+ * @return the current start of summary text
+ */
+ @Override
+ public String getSummaryObjectStartText() { // NOPMD as this is implementing the abstract class
+ return super.getSummaryObjectStartText();
+ }
+
+ /**
+ * <p>Sets the start text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <p>This is output before the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ @Override
+ public void setSummaryObjectStartText(String summaryObjectStartText) { // NOPMD as this is implementing the abstract class
+ super.setSummaryObjectStartText(summaryObjectStartText);
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the end text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * @return the current end of summary text
+ */
+ @Override
+ public String getSummaryObjectEndText() { // NOPMD as this is implementing the abstract class
+ return super.getSummaryObjectEndText();
+ }
+
+ /**
+ * <p>Sets the end text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ @Override
+ public void setSummaryObjectEndText(String summaryObjectEndText) { // NOPMD as this is implementing the abstract class
+ super.setSummaryObjectEndText(summaryObjectEndText);
+ }
+
+ //---------------------------------------------------------------------
+
+}
diff --git a/src/org/apache/commons/lang3/builder/ToStringBuilder.java b/src/org/apache/commons/lang3/builder/ToStringBuilder.java new file mode 100644 index 0000000..63c6268 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/ToStringBuilder.java @@ -0,0 +1,1079 @@ +/*
+ * 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 org.apache.commons.lang3.ObjectUtils;
+
+/**
+ * <p>Assists in implementing {@link Object#toString()} methods.</p>
+ *
+ * <p>This class enables a good and consistent <code>toString()</code> to be built for any
+ * class or object. This class aims to simplify the process by:</p>
+ * <ul>
+ * <li>allowing field names</li>
+ * <li>handling all types consistently</li>
+ * <li>handling nulls consistently</li>
+ * <li>outputting arrays and multi-dimensional arrays</li>
+ * <li>enabling the detail level to be controlled for Objects and Collections</li>
+ * <li>handling class hierarchies</li>
+ * </ul>
+ *
+ * <p>To use this class write code as follows:</p>
+ *
+ * <pre>
+ * public class Person {
+ * String name;
+ * int age;
+ * boolean smoker;
+ *
+ * ...
+ *
+ * public String toString() {
+ * return new ToStringBuilder(this).
+ * append("name", name).
+ * append("age", age).
+ * append("smoker", smoker).
+ * toString();
+ * }
+ * }
+ * </pre>
+ *
+ * <p>This will produce a toString of the format:
+ * <code>Person@7f54[name=Stephen,age=29,smoker=false]</code></p>
+ *
+ * <p>To add the superclass <code>toString</code>, use {@link #appendSuper}.
+ * To append the <code>toString</code> from an object that is delegated
+ * to (or any other object), use {@link #appendToString}.</p>
+ *
+ * <p>Alternatively, there is a method that uses reflection to determine
+ * the fields to test. Because these fields are usually private, the method,
+ * <code>reflectionToString</code>, uses <code>AccessibleObject.setAccessible</code> to
+ * change the visibility of the fields. This will fail under a security manager,
+ * unless the appropriate permissions are set up correctly. It is also
+ * slower than testing explicitly.</p>
+ *
+ * <p>A typical invocation for this method would look like:</p>
+ *
+ * <pre>
+ * public String toString() {
+ * return ToStringBuilder.reflectionToString(this);
+ * }
+ * </pre>
+ *
+ * <p>You can also use the builder to debug 3rd party objects:</p>
+ *
+ * <pre>
+ * System.out.println("An object: " + ToStringBuilder.reflectionToString(anObject));
+ * </pre>
+ *
+ * <p>The exact format of the <code>toString</code> is determined by
+ * the {@link ToStringStyle} passed into the constructor.</p>
+ *
+ * @since 1.0
+ * @version $Id: ToStringBuilder.java 1088899 2011-04-05 05:31:27Z bayard $
+ */
+public class ToStringBuilder implements Builder<String> {
+
+ /**
+ * The default style of output to use, not null.
+ */
+ private static volatile ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE;
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Gets the default <code>ToStringStyle</code> to use.</p>
+ *
+ * <p>This method gets a singleton default value, typically for the whole JVM.
+ * Changing this default should generally only be done during application startup.
+ * It is recommended to pass a <code>ToStringStyle</code> to the constructor instead
+ * of using this global default.</p>
+ *
+ * <p>This method can be used from multiple threads.
+ * Internally, a <code>volatile</code> variable is used to provide the guarantee
+ * that the latest value set using {@link #setDefaultStyle} is the value returned.
+ * It is strongly recommended that the default style is only changed during application startup.</p>
+ *
+ * <p>One reason for changing the default could be to have a verbose style during
+ * development and a compact style in production.</p>
+ *
+ * @return the default <code>ToStringStyle</code>, never null
+ */
+ public static ToStringStyle getDefaultStyle() {
+ return defaultStyle;
+ }
+
+ /**
+ * <p>Sets the default <code>ToStringStyle</code> to use.</p>
+ *
+ * <p>This method sets a singleton default value, typically for the whole JVM.
+ * Changing this default should generally only be done during application startup.
+ * It is recommended to pass a <code>ToStringStyle</code> to the constructor instead
+ * of changing this global default.</p>
+ *
+ * <p>This method is not intended for use from multiple threads.
+ * Internally, a <code>volatile</code> variable is used to provide the guarantee
+ * that the latest value set is the value returned from {@link #getDefaultStyle}.</p>
+ *
+ * @param style the default <code>ToStringStyle</code>
+ * @throws IllegalArgumentException if the style is <code>null</code>
+ */
+ public static void setDefaultStyle(ToStringStyle style) {
+ if (style == null) {
+ throw new IllegalArgumentException("The style must not be null");
+ }
+ defaultStyle = style;
+ }
+
+ //----------------------------------------------------------------------------
+ /**
+ * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
+ * <code>toString</code> for the specified object.</p>
+ *
+ * @param object the Object to be output
+ * @return the String result
+ * @see ReflectionToStringBuilder#toString(Object)
+ */
+ public static String reflectionToString(Object object) {
+ return ReflectionToStringBuilder.toString(object);
+ }
+
+ /**
+ * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
+ * <code>toString</code> for the specified object.</p>
+ *
+ * @param object the Object to be output
+ * @param style the style of the <code>toString</code> to create, may be <code>null</code>
+ * @return the String result
+ * @see ReflectionToStringBuilder#toString(Object,ToStringStyle)
+ */
+ public static String reflectionToString(Object object, ToStringStyle style) {
+ return ReflectionToStringBuilder.toString(object, style);
+ }
+
+ /**
+ * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
+ * <code>toString</code> for the specified object.</p>
+ *
+ * @param object the Object to be output
+ * @param style the style of the <code>toString</code> to create, may be <code>null</code>
+ * @param outputTransients whether to include transient fields
+ * @return the String result
+ * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean)
+ */
+ public static String reflectionToString(Object object, ToStringStyle style, boolean outputTransients) {
+ return ReflectionToStringBuilder.toString(object, style, outputTransients, false, null);
+ }
+
+ /**
+ * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
+ * <code>toString</code> for the specified object.</p>
+ *
+ * @param <T> the type of the object
+ * @param object the Object to be output
+ * @param style the style of the <code>toString</code> to create, may be <code>null</code>
+ * @param outputTransients whether to include transient fields
+ * @param reflectUpToClass the superclass to reflect up to (inclusive), may be <code>null</code>
+ * @return the String result
+ * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,boolean,Class)
+ * @since 2.0
+ */
+ public static <T> String reflectionToString(
+ T object,
+ ToStringStyle style,
+ boolean outputTransients,
+ Class<? super T> reflectUpToClass) {
+ return ReflectionToStringBuilder.toString(object, style, outputTransients, false, reflectUpToClass);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Current toString buffer, not null.
+ */
+ private final StringBuffer buffer;
+ /**
+ * The object being output, may be null.
+ */
+ private final Object object;
+ /**
+ * The style of output to use, not null.
+ */
+ private final ToStringStyle style;
+
+ /**
+ * <p>Constructs a builder for the specified object using the default output style.</p>
+ *
+ * <p>This default style is obtained from {@link #getDefaultStyle()}.</p>
+ *
+ * @param object the Object to build a <code>toString</code> for, not recommended to be null
+ */
+ public ToStringBuilder(Object object) {
+ this(object, null, null);
+ }
+
+ /**
+ * <p>Constructs a builder for the specified object using the a defined output style.</p>
+ *
+ * <p>If the style is <code>null</code>, the default style is used.</p>
+ *
+ * @param object the Object to build a <code>toString</code> for, not recommended to be null
+ * @param style the style of the <code>toString</code> to create, null uses the default style
+ */
+ public ToStringBuilder(Object object, ToStringStyle style) {
+ this(object, style, null);
+ }
+
+ /**
+ * <p>Constructs a builder for the specified object.</p>
+ *
+ * <p>If the style is <code>null</code>, the default style is used.</p>
+ *
+ * <p>If the buffer is <code>null</code>, a new one is created.</p>
+ *
+ * @param object the Object to build a <code>toString</code> for, not recommended to be null
+ * @param style the style of the <code>toString</code> to create, null uses the default style
+ * @param buffer the <code>StringBuffer</code> to populate, may be null
+ */
+ public ToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
+ if (style == null) {
+ style = getDefaultStyle();
+ }
+ if (buffer == null) {
+ buffer = new StringBuffer(512);
+ }
+ this.buffer = buffer;
+ this.style = style;
+ this.object = object;
+
+ style.appendStart(buffer, object);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(boolean value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(boolean[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>byte</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(byte value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>byte</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(byte[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(char value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(char[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(double value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(double[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>float</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(float value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>float</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(float[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(int value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(int[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(long value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(long[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * value.</p>
+ *
+ * @param obj the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(Object obj) {
+ style.append(buffer, null, obj, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(Object[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>short</code>
+ * value.</p>
+ *
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(short value) {
+ style.append(buffer, null, value);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>short</code>
+ * array.</p>
+ *
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(short[] array) {
+ style.append(buffer, null, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, boolean value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>hashCode</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, boolean[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, boolean[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>byte</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, byte value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>byte</code> array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, byte[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>byte</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, byte[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, char value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, char[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, char[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, double value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, double[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, double[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>float</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, float value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>float</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, float[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>float</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, float[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, int value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, int[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, int[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, long value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, long[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, long[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param obj the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, Object obj) {
+ style.append(buffer, fieldName, obj, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param obj the value to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail,
+ * <code>false</code> for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, Object obj, boolean fullDetail) {
+ style.append(buffer, fieldName, obj, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, Object[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, Object[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>short</code>
+ * value.</p>
+ *
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, short value) {
+ style.append(buffer, fieldName, value);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>short</code>
+ * array.</p>
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, short[] array) {
+ style.append(buffer, fieldName, array, null);
+ return this;
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>short</code>
+ * array.</p>
+ *
+ * <p>A boolean parameter controls the level of detail to show.
+ * Setting <code>true</code> will output the array in full. Setting
+ * <code>false</code> will output a summary, typically the size of
+ * the array.
+ *
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info
+ * @return this
+ */
+ public ToStringBuilder append(String fieldName, short[] array, boolean fullDetail) {
+ style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
+ return this;
+ }
+
+ /**
+ * <p>Appends with the same format as the default <code>Object toString()
+ * </code> method. Appends the class name followed by
+ * {@link System#identityHashCode(java.lang.Object)}.</p>
+ *
+ * @param object the <code>Object</code> whose class name and id to output
+ * @return this
+ * @since 2.0
+ */
+ public ToStringBuilder appendAsObjectToString(Object object) {
+ ObjectUtils.identityToString(this.getStringBuffer(), object);
+ return this;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append the <code>toString</code> from the superclass.</p>
+ *
+ * <p>This method assumes that the superclass uses the same <code>ToStringStyle</code>
+ * as this one.</p>
+ *
+ * <p>If <code>superToString</code> is <code>null</code>, no change is made.</p>
+ *
+ * @param superToString the result of <code>super.toString()</code>
+ * @return this
+ * @since 2.0
+ */
+ public ToStringBuilder appendSuper(String superToString) {
+ if (superToString != null) {
+ style.appendSuper(buffer, superToString);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Append the <code>toString</code> from another object.</p>
+ *
+ * <p>This method is useful where a class delegates most of the implementation of
+ * its properties to another class. You can then call <code>toString()</code> on
+ * the other class and pass the result into this method.</p>
+ *
+ * <pre>
+ * private AnotherObject delegate;
+ * private String fieldInThisClass;
+ *
+ * public String toString() {
+ * return new ToStringBuilder(this).
+ * appendToString(delegate.toString()).
+ * append(fieldInThisClass).
+ * toString();
+ * }</pre>
+ *
+ * <p>This method assumes that the other object uses the same <code>ToStringStyle</code>
+ * as this one.</p>
+ *
+ * <p>If the <code>toString</code> is <code>null</code>, no change is made.</p>
+ *
+ * @param toString the result of <code>toString()</code> on another object
+ * @return this
+ * @since 2.0
+ */
+ public ToStringBuilder appendToString(String toString) {
+ if (toString != null) {
+ style.appendToString(buffer, toString);
+ }
+ return this;
+ }
+
+ /**
+ * <p>Returns the <code>Object</code> being output.</p>
+ *
+ * @return The object being output.
+ * @since 2.0
+ */
+ public Object getObject() {
+ return object;
+ }
+
+ /**
+ * <p>Gets the <code>StringBuffer</code> being populated.</p>
+ *
+ * @return the <code>StringBuffer</code> being populated
+ */
+ public StringBuffer getStringBuffer() {
+ return buffer;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Gets the <code>ToStringStyle</code> being used.</p>
+ *
+ * @return the <code>ToStringStyle</code> being used
+ * @since 2.0
+ */
+ public ToStringStyle getStyle() {
+ return style;
+ }
+
+ /**
+ * <p>Returns the built <code>toString</code>.</p>
+ *
+ * <p>This method appends the end of data indicator, and can only be called once.
+ * Use {@link #getStringBuffer} to get the current string state.</p>
+ *
+ * <p>If the object is <code>null</code>, return the style's <code>nullText</code></p>
+ *
+ * @return the String <code>toString</code>
+ */
+ @Override
+ public String toString() {
+ if (this.getObject() == null) {
+ this.getStringBuffer().append(this.getStyle().getNullText());
+ } else {
+ style.appendEnd(this.getStringBuffer(), this.getObject());
+ }
+ return this.getStringBuffer().toString();
+ }
+
+ /**
+ * Returns the String that was build as an object representation. The
+ * default implementation utilizes the {@link #toString()} implementation.
+ *
+ * @return the String <code>toString</code>
+ *
+ * @see #toString()
+ *
+ * @since 3.0
+ */
+ public String build() {
+ return toString();
+ }
+}
diff --git a/src/org/apache/commons/lang3/builder/ToStringStyle.java b/src/org/apache/commons/lang3/builder/ToStringStyle.java new file mode 100644 index 0000000..d9ee587 --- /dev/null +++ b/src/org/apache/commons/lang3/builder/ToStringStyle.java @@ -0,0 +1,2271 @@ +/*
+ * 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.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * <p>Controls <code>String</code> formatting for {@link ToStringBuilder}.
+ * The main public interface is always via <code>ToStringBuilder</code>.</p>
+ *
+ * <p>These classes are intended to be used as <code>Singletons</code>.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.</p>
+ *
+ * <p>If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from <code>boolean</code>
+ * to <code>long</code> to <code>Object</code> to <code>int[]</code>) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ * <p>For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.</p>
+ *
+ * <p>If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ * <pre>
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ * </pre>
+ * </p>
+ *
+ * @since 1.0
+ * @version $Id: ToStringStyle.java 1091066 2011-04-11 13:30:11Z mbenson $
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Using the <code>Person</code>
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ * <pre>
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ * </pre>
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Using the <code>Person</code>
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ * <pre>
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ * </pre>
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the Using the
+ * <code>Person</code> example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ * <pre>
+ * Person@182f0db[John Doe,33,false]
+ * </pre>
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the <code>Person</code> example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ * <pre>
+ * Person[name=John Doe,age=33,smoker=false]
+ * </pre>
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Using the <code>Person</code>
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ * <pre>
+ * John Doe,33,false
+ * </pre>
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * <p>
+ * A registry of objects used by <code>reflectionToString</code> methods
+ * to detect cyclical object references and avoid infinite loops.
+ * </p>
+ */
+ private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY =
+ new ThreadLocal<WeakHashMap<Object,Object>>();
+
+ /**
+ * <p>
+ * Returns the registry of objects being traversed by the <code>reflectionToString</code>
+ * methods in the current thread.
+ * </p>
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map<Object, Object> getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ * <p>
+ * Returns <code>true</code> if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ * </p>
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean <code>true</code> if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(Object value) {
+ Map<Object, Object> m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ * <p>
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ * </p>
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(Object value) {
+ if (value != null) {
+ Map<Object, Object> m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap<Object, Object>());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ * <p>
+ * Unregisters the given object.
+ * </p>
+ *
+ * <p>
+ * Used by the reflection methods to avoid infinite loops.
+ * </p>
+ *
+ * @param value
+ * The object to unregister.
+ */
+ static void unregister(Object value) {
+ if (value != null) {
+ Map<Object, Object> m = getRegistry();
+ if (m != null) {
+ m.remove(value);
+ if (m.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether to use the field names, the default is <code>true</code>.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is <code>true</code>.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is <code>false</code>.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is <code>true</code>.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start <code>'['</code>.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end <code>']'</code>.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator <code>'='</code>.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator <code>','</code>.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start <code>'{'</code>.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator <code>','</code>.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end <code>'}'</code>.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is <code>null</code>,
+ * the default value is <code>true</code>.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The <code>null</code> text <code>'<null>'</code>.
+ */
+ private String nullText = "<null>";
+
+ /**
+ * The summary size text start <code>'<size'</code>.
+ */
+ private String sizeStartText = "<size=";
+
+ /**
+ * The summary size text start <code>'>'</code>.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start <code>'<'</code>.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start <code>'>'</code>.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Constructor.</p>
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> the superclass toString.</p>
+ * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
+ *
+ * <p>A <code>null</code> <code>superToString</code> is ignored.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param superToString the <code>super.toString()</code>
+ * @since 2.0
+ */
+ public void appendSuper(StringBuffer buffer, String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> another toString.</p>
+ * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
+ *
+ * <p>A <code>null</code> <code>toString</code> is ignored.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param toString the additional <code>toString</code>
+ * @since 2.0
+ */
+ public void appendToString(StringBuffer buffer, String toString) {
+ if (toString != null) {
+ int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the start of data indicator.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param object the <code>Object</code> to build a <code>toString</code> for
+ */
+ public void appendStart(StringBuffer buffer, Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the end of data indicator.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param object the <code>Object</code> to build a
+ * <code>toString</code> for.
+ */
+ public void appendEnd(StringBuffer buffer, Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * <p>Remove the last field separator from the buffer.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(StringBuffer buffer) {
+ int len = buffer.length();
+ int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * value, printing the full <code>toString</code> of the
+ * <code>Object</code> passed in.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>,
+ * correctly interpreting its type.</p>
+ *
+ * <p>This method performs the main lookup by Class type to correctly
+ * route arrays, <code>Collections</code>, <code>Maps</code> and
+ * <code>Objects</code> to the appropriate method.</p>
+ *
+ * <p>Either detail or summary views can be specified.</p>
+ *
+ * <p>If a cycle is detected, an object will be appended with the
+ * <code>Object.toString()</code> format.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>,
+ * not <code>null</code>
+ * @param detail output detail or not
+ */
+ protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection<?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection<?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
+ }
+
+ } else if (value instanceof Map<?, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map<?, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>,
+ * not <code>null</code>
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * value, printing the full detail of the <code>Object</code>.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>Collection</code>.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the <code>Collection</code> to add to the
+ * <code>toString</code>, not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, Collection<?> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>Map<code>.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the <code>Map</code> to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * value, printing a summary of the <code>Object</code>.</P>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>short</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>short</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>byte</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>byte</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>float</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>float</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param value the value to add to the <code>toString</code>
+ */
+ public void append(StringBuffer buffer, String fieldName, boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * value.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the <code>toString</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>Object</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, Object[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of an
+ * <code>Object</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of an array type.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(StringBuffer buffer, String fieldName, Object array) {
+ buffer.append(arrayStart);
+ int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of an
+ * <code>Object</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>long</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, long[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of a
+ * <code>long</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of a
+ * <code>long</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> an <code>int</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, int[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of an
+ * <code>int</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of an
+ * <code>int</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>short</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, short[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of a
+ * <code>short</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of a
+ * <code>short</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>byte</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, byte[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of a
+ * <code>byte</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of a
+ * <code>byte</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>char</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the <code>toString</code>
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, char[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of a
+ * <code>char</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of a
+ * <code>char</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>double</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, double[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of a
+ * <code>double</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of a
+ * <code>double</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>float</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, float[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of a
+ * <code>float</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of a
+ * <code>float</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> a <code>boolean</code>
+ * array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail <code>true</code> for detail, <code>false</code>
+ * for summary info, <code>null</code> for style decides
+ */
+ public void append(StringBuffer buffer, String fieldName, boolean[] array, Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the detail of a
+ * <code>boolean</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendDetail(StringBuffer buffer, String fieldName, boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a summary of a
+ * <code>boolean</code> array.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the <code>toString</code>,
+ * not <code>null</code>
+ */
+ protected void appendSummary(StringBuffer buffer, String fieldName, boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Append to the <code>toString</code> the class name.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param object the <code>Object</code> whose name to output
+ */
+ protected void appendClassName(StringBuffer buffer, Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * <p>Append the {@link System#identityHashCode(java.lang.Object)}.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param object the <code>Object</code> whose id to output
+ */
+ protected void appendIdentityHashCode(StringBuffer buffer, Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the content start.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ */
+ protected void appendContentStart(StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the content end.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ */
+ protected void appendContentEnd(StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> an indicator for <code>null</code>.</p>
+ *
+ * <p>The default indicator is <code>'<null>'</code>.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(StringBuffer buffer, String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the field separator.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ */
+ protected void appendFieldSeparator(StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> the field start.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * <p>Append to the <code>toString<code> the field end.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(StringBuffer buffer, String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * <p>Append to the <code>toString</code> a size summary.</p>
+ *
+ * <p>The size summary is used to summarize the contents of
+ * <code>Collections</code>, <code>Maps</code> and arrays.</p>
+ *
+ * <p>The output consists of a prefix, the passed in size
+ * and a suffix.</p>
+ *
+ * <p>The default format is <code>'<size=n>'<code>.</p>
+ *
+ * @param buffer the <code>StringBuffer</code> to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(StringBuffer buffer, String fieldName, int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * <p>Is this field to be output in full detail.</p>
+ *
+ * <p>This method converts a detail request into a detail level.
+ * The calling code may request full detail (<code>true</code>),
+ * but a subclass might ignore that and always return
+ * <code>false</code>. The calling code may pass in
+ * <code>null</code> indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.</p>
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * <p>Gets the short class name for a class.</p>
+ *
+ * <p>The short class name is the classname excluding
+ * the package name.</p>
+ *
+ * @param cls the <code>Class</code> to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(Class<?> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use the class name.</p>
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * <p>Sets whether to use the class name.</p>
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to output short or long class names.</p>
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * <p>Sets whether to output short or long class names.</p>
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use the identity hash code.</p>
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * <p>Sets whether to use the identity hash code.</p>
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use the field names passed in.</p>
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * <p>Sets whether to use the field names passed in.</p>
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to use full detail when the caller doesn't
+ * specify.</p>
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * <p>Sets whether to use full detail when the caller doesn't
+ * specify.</p>
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether to output array content detail.</p>
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * <p>Sets whether to output array content detail.</p>
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the array start text.</p>
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * <p>Sets the array start text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = "";
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the array end text.</p>
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * <p>Sets the array end text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = "";
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the array separator text.</p>
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * <p>Sets the array separator text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = "";
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the content start text.</p>
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * <p>Sets the content start text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = "";
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the content end text.</p>
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * <p>Sets the content end text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = "";
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the field name value separator text.</p>
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * <p>Sets the field name value separator text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = "";
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the field separator text.</p>
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * <p>Sets the field separator text.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = "";
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether the field separator should be added at the start
+ * of each buffer.</p>
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * <p>Sets whether the field separator should be added at the start
+ * of each buffer.</p>
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets whether the field separator should be added at the end
+ * of each buffer.</p>
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * <p>Sets whether the field separator should be added at the end
+ * of each buffer.</p>
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the text to output when <code>null</code> found.</p>
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * <p>Sets the text to output when <code>null</code> found.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = "";
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the start text to output when a <code>Collection</code>,
+ * <code>Map</code> or array size is output.</p>
+ *
+ * <p>This is output before the size value.</p>
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * <p>Sets the start text to output when a <code>Collection</code>,
+ * <code>Map</code> or array size is output.</p>
+ *
+ * <p>This is output before the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = "";
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the end text to output when a <code>Collection</code>,
+ * <code>Map</code> or array size is output.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * <p>Sets the end text to output when a <code>Collection</code>,
+ * <code>Map</code> or array size is output.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = "";
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the start text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <p>This is output before the size value.</p>
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * <p>Sets the start text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <p>This is output before the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = "";
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * <p>Gets the end text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * <p>Sets the end text to output when an <code>Object</code> is
+ * output in summary mode.</p>
+ *
+ * <p>This is output after the size value.</p>
+ *
+ * <p><code>null</code> is accepted, but will be converted to
+ * an empty String.</p>
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = "";
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p>Default <code>ToStringStyle</code>.</p>
+ *
+ * <p>This is an inner class rather than using
+ * <code>StandardToStringStyle</code> to ensure its immutability.</p>
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * <p>Constructor.</p>
+ *
+ * <p>Use the static constant rather than instantiating.</p>
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * <p>Ensure <code>Singleton</code> after serialization.</p>
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p><code>ToStringStyle</code> that does not print out
+ * the field names.</p>
+ *
+ * <p>This is an inner class rather than using
+ * <code>StandardToStringStyle</code> to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * <p>Constructor.</p>
+ *
+ * <p>Use the static constant rather than instantiating.</p>
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * <p>Ensure <code>Singleton</code> after serialization.</p>
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p><code>ToStringStyle</code> that prints out the short
+ * class name and no identity hashcode.</p>
+ *
+ * <p>This is an inner class rather than using
+ * <code>StandardToStringStyle</code> to ensure its immutability.</p>
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * <p>Constructor.</p>
+ *
+ * <p>Use the static constant rather than instantiating.</p>
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * <p>Ensure <code>Singleton</ode> after serialization.</p>
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ /**
+ * <p><code>ToStringStyle</code> that does not print out the
+ * classname, identity hashcode, content start or field name.</p>
+ *
+ * <p>This is an inner class rather than using
+ * <code>StandardToStringStyle</code> to ensure its immutability.</p>
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * <p>Constructor.</p>
+ *
+ * <p>Use the static constant rather than instantiating.</p>
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart("");
+ this.setContentEnd("");
+ }
+
+ /**
+ * <p>Ensure <code>Singleton</ode> after serialization.</p>
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * <p><code>ToStringStyle</code> that outputs on multiple lines.</p>
+ *
+ * <p>This is an inner class rather than using
+ * <code>StandardToStringStyle</code> to ensure its immutability.</p>
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * <p>Constructor.</p>
+ *
+ * <p>Use the static constant rather than instantiating.</p>
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * <p>Ensure <code>Singleton</code> after serialization.</p>
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+}
|
