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

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

Go to download

Apache Commons Lang, a package of Java utility classes for the classes that are in java.lang's hierarchy, or are considered to be so standard as to justify existence in java.lang. The code is tested using the latest revision of the JDK for supported LTS releases: 8, 11, 17 and 21 currently. See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml Please ensure your build environment is up-to-date and kindly report any build issues.

There is a newer version: 3.17.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.lang3.builder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;

/**
 * Assists in implementing {@link Diffable#diff(Object)} methods.
 *
 * 

* To use this class, write code as follows: *

* *
 * public class Person implements Diffable<Person> {
 *   String name;
 *   int age;
 *   boolean smoker;
 *
 *   ...
 *
 *   public DiffResult diff(Person obj) {
 *     // No need for null check, as NullPointerException correct if obj is null
 *     return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
 *       .append("name", this.name, obj.name)
 *       .append("age", this.age, obj.age)
 *       .append("smoker", this.smoker, obj.smoker)
 *       .build();
 *   }
 * }
 * 
* *

* The {@link ToStringStyle} passed to the constructor is embedded in the * returned {@link DiffResult} and influences the style of the * {@code DiffResult.toString()} method. This style choice can be overridden by * calling {@link DiffResult#toString(ToStringStyle)}. *

*

* See {@link ReflectionDiffBuilder} for a reflection based version of this class. *

* * @param type of the left and right object. * @see Diffable * @see Diff * @see DiffResult * @see ToStringStyle * @see ReflectionDiffBuilder * @since 3.3 */ public class DiffBuilder implements Builder> { private final List> diffs; private final boolean objectsTriviallyEqual; private final T left; private final T right; private final ToStringStyle style; /** * Constructs a builder for the specified objects with the specified style. * *

* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will * not evaluate any calls to {@code append(...)} and will return an empty * {@link DiffResult} when {@link #build()} is executed. *

* *

* This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)} * with the testTriviallyEqual flag enabled. *

* * @param lhs * {@code this} object * @param rhs * the object to diff against * @param style * the style will use when outputting the objects, {@code null} * uses the default * @throws NullPointerException * if {@code lhs} or {@code rhs} is {@code null} */ public DiffBuilder(final T lhs, final T rhs, final ToStringStyle style) { this(lhs, rhs, style, true); } /** * Constructs a builder for the specified objects with the specified style. * *

* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will * not evaluate any calls to {@code append(...)} and will return an empty * {@link DiffResult} when {@link #build()} is executed. *

* * @param lhs * {@code this} object * @param rhs * the object to diff against * @param style * the style will use when outputting the objects, {@code null} * uses the default * @param testTriviallyEqual * If true, this will test if lhs and rhs are the same or equal. * All of the append(fieldName, lhs, rhs) methods will abort * without creating a field {@link Diff} if the trivially equal * test is enabled and returns true. The result of this test * is never changed throughout the life of this {@link DiffBuilder}. * @throws NullPointerException * if {@code lhs} or {@code rhs} is {@code null} * @since 3.4 */ public DiffBuilder(final T lhs, final T rhs, final ToStringStyle style, final boolean testTriviallyEqual) { Objects.requireNonNull(lhs, "lhs"); Objects.requireNonNull(rhs, "rhs"); this.diffs = new ArrayList<>(); this.left = lhs; this.right = rhs; this.style = style; // Don't compare any fields if objects equal this.objectsTriviallyEqual = testTriviallyEqual && Objects.equals(lhs, rhs); } /** * Test if two {@code boolean}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code boolean} * @param rhs * the right-hand {@code boolean} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final boolean lhs, final boolean rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (lhs != rhs) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Boolean getLeft() { return Boolean.valueOf(lhs); } @Override public Boolean getRight() { return Boolean.valueOf(rhs); } }); } return this; } /** * Test if two {@code boolean[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code boolean[]} * @param rhs * the right-hand {@code boolean[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final boolean[] lhs, final boolean[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Boolean[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Boolean[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Test if two {@code byte}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code byte} * @param rhs * the right-hand {@code byte} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final byte lhs, final byte rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (lhs != rhs) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Byte getLeft() { return Byte.valueOf(lhs); } @Override public Byte getRight() { return Byte.valueOf(rhs); } }); } return this; } /** * Test if two {@code byte[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code byte[]} * @param rhs * the right-hand {@code byte[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final byte[] lhs, final byte[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Byte[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Byte[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Test if two {@code char}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code char} * @param rhs * the right-hand {@code char} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final char lhs, final char rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (lhs != rhs) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Character getLeft() { return Character.valueOf(lhs); } @Override public Character getRight() { return Character.valueOf(rhs); } }); } return this; } /** * Test if two {@code char[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code char[]} * @param rhs * the right-hand {@code char[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final char[] lhs, final char[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Character[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Character[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Append diffs from another {@link DiffResult}. * *

* This method is useful if you want to compare properties which are * themselves Diffable and would like to know which specific part of * it is different. *

* *
     * public class Person implements Diffable<Person> {
     *   String name;
     *   Address address; // implements Diffable<Address>
     *
     *   ...
     *
     *   public DiffResult diff(Person obj) {
     *     return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
     *       .append("name", this.name, obj.name)
     *       .append("address", this.address.diff(obj.address))
     *       .build();
     *   }
     * }
     * 
* * @param fieldName * the field name * @param diffResult * the {@link DiffResult} to append * @return this * @throws NullPointerException if field name is {@code null} or diffResult is {@code null} * @since 3.5 */ public DiffBuilder append(final String fieldName, final DiffResult diffResult) { validateFieldNameNotNull(fieldName); Objects.requireNonNull(diffResult, "diffResult"); if (objectsTriviallyEqual) { return this; } diffResult.getDiffs().forEach(diff -> append(fieldName + "." + diff.getFieldName(), diff.getLeft(), diff.getRight())); return this; } /** * Test if two {@code double}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code double} * @param rhs * the right-hand {@code double} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final double lhs, final double rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (Double.doubleToLongBits(lhs) != Double.doubleToLongBits(rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Double getLeft() { return Double.valueOf(lhs); } @Override public Double getRight() { return Double.valueOf(rhs); } }); } return this; } /** * Test if two {@code double[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code double[]} * @param rhs * the right-hand {@code double[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final double[] lhs, final double[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Double[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Double[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Test if two {@code float}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code float} * @param rhs * the right-hand {@code float} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final float lhs, final float rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (Float.floatToIntBits(lhs) != Float.floatToIntBits(rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Float getLeft() { return Float.valueOf(lhs); } @Override public Float getRight() { return Float.valueOf(rhs); } }); } return this; } /** * Test if two {@code float[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code float[]} * @param rhs * the right-hand {@code float[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final float[] lhs, final float[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Float[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Float[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Test if two {@code int}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code int} * @param rhs * the right-hand {@code int} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final int lhs, final int rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (lhs != rhs) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Integer getLeft() { return Integer.valueOf(lhs); } @Override public Integer getRight() { return Integer.valueOf(rhs); } }); } return this; } /** * Test if two {@code int[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code int[]} * @param rhs * the right-hand {@code int[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final int[] lhs, final int[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Integer[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Integer[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Test if two {@code long}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code long} * @param rhs * the right-hand {@code long} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final long lhs, final long rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (lhs != rhs) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Long getLeft() { return Long.valueOf(lhs); } @Override public Long getRight() { return Long.valueOf(rhs); } }); } return this; } /** * Test if two {@code long[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code long[]} * @param rhs * the right-hand {@code long[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final long[] lhs, final long[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Long[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Long[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Test if two {@link Objects}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@link Object} * @param rhs * the right-hand {@link Object} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final Object lhs, final Object rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (lhs == rhs) { return this; } final Object objectToTest; if (lhs != null) { objectToTest = lhs; } else { // rhs cannot be null, as lhs != rhs objectToTest = rhs; } if (ObjectUtils.isArray(objectToTest)) { if (objectToTest instanceof boolean[]) { return append(fieldName, (boolean[]) lhs, (boolean[]) rhs); } if (objectToTest instanceof byte[]) { return append(fieldName, (byte[]) lhs, (byte[]) rhs); } if (objectToTest instanceof char[]) { return append(fieldName, (char[]) lhs, (char[]) rhs); } if (objectToTest instanceof double[]) { return append(fieldName, (double[]) lhs, (double[]) rhs); } if (objectToTest instanceof float[]) { return append(fieldName, (float[]) lhs, (float[]) rhs); } if (objectToTest instanceof int[]) { return append(fieldName, (int[]) lhs, (int[]) rhs); } if (objectToTest instanceof long[]) { return append(fieldName, (long[]) lhs, (long[]) rhs); } if (objectToTest instanceof short[]) { return append(fieldName, (short[]) lhs, (short[]) rhs); } return append(fieldName, (Object[]) lhs, (Object[]) rhs); } // Not array type if (Objects.equals(lhs, rhs)) { return this; } diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Object getLeft() { return lhs; } @Override public Object getRight() { return rhs; } }); return this; } /** * Test if two {@code Object[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code Object[]} * @param rhs * the right-hand {@code Object[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final Object[] lhs, final Object[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Object[] getLeft() { return lhs; } @Override public Object[] getRight() { return rhs; } }); } return this; } /** * Test if two {@code short}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code short} * @param rhs * the right-hand {@code short} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final short lhs, final short rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (lhs != rhs) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Short getLeft() { return Short.valueOf(lhs); } @Override public Short getRight() { return Short.valueOf(rhs); } }); } return this; } /** * Test if two {@code short[]}s are equal. * * @param fieldName * the field name * @param lhs * the left-hand {@code short[]} * @param rhs * the right-hand {@code short[]} * @return this * @throws NullPointerException * if field name is {@code null} */ public DiffBuilder append(final String fieldName, final short[] lhs, final short[] rhs) { validateFieldNameNotNull(fieldName); if (objectsTriviallyEqual) { return this; } if (!Arrays.equals(lhs, rhs)) { diffs.add(new Diff(fieldName) { private static final long serialVersionUID = 1L; @Override public Short[] getLeft() { return ArrayUtils.toObject(lhs); } @Override public Short[] getRight() { return ArrayUtils.toObject(rhs); } }); } return this; } /** * Builds a {@link DiffResult} based on the differences appended to this * builder. * * @return a {@link DiffResult} containing the differences between the two * objects. */ @Override public DiffResult build() { return new DiffResult<>(left, right, diffs, style); } private void validateFieldNameNotNull(final String fieldName) { Objects.requireNonNull(fieldName, "fieldName"); } }