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 java.util.function.Supplier;

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: *

* *
{@code
 * 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.builder()
 *         .setLeft(this)
 *         .setRight(obj)
 *         .setStyle(ToStringStyle.SHORT_PREFIX_STYLE))
 *         .build()
 *       .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> { /** * Constructs a new instance. * * @param type of the left and right object. * @since 3.15.0 */ public static final class Builder { private T left; private T right; private ToStringStyle style; private boolean testObjectsEquals = true; private String toStringFormat = TO_STRING_FORMAT; /** * Builds a new configured {@link DiffBuilder}. * * @return a new configured {@link DiffBuilder}. */ public DiffBuilder build() { return new DiffBuilder<>(left, right, style, testObjectsEquals, toStringFormat); } /** * Sets the left object. * * @param left the left object. * @return {@code this} instance. */ public Builder setLeft(final T left) { this.left = left; return this; } /** * Sets the right object. * * @param right the left object. * @return {@code this} instance. */ public Builder setRight(final T right) { this.right = right; return this; } /** * Sets the style will to use when outputting the objects, {@code null} uses the default. * * @param style the style to use when outputting the objects, {@code null} uses the default. * @return {@code this} instance. */ public Builder setStyle(final ToStringStyle style) { this.style = style != null ? style : ToStringStyle.DEFAULT_STYLE; return this; } /** * Sets whether to test if left and right are the same or equal. All of the append(fieldName, left, right) 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}. * * @param testObjectsEquals If true, this will test if lhs and rhs are the same or equal. All of the append(fieldName, left, right) 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}. * @return {@code this} instance. */ public Builder setTestObjectsEquals(final boolean testObjectsEquals) { this.testObjectsEquals = testObjectsEquals; return this; } /** * Sets the two-argument format string for {@link String#format(String, Object...)}, for example {@code "%s differs from %s"}. * * @param toStringFormat {@code null} uses the default. * @return {@code this} instance. */ public Builder setToStringFormat(final String toStringFormat) { this.toStringFormat = toStringFormat != null ? toStringFormat : TO_STRING_FORMAT; return this; } } private static final class SDiff extends Diff { private static final long serialVersionUID = 1L; private final transient Supplier leftSupplier; private final transient Supplier rightSupplier; private SDiff(final String fieldName, final Supplier leftSupplier, final Supplier rightSupplier, final Class type) { super(fieldName, type); this.leftSupplier = Objects.requireNonNull(leftSupplier); this.rightSupplier = Objects.requireNonNull(rightSupplier); } @Override public T getLeft() { return leftSupplier.get(); } @Override public T getRight() { return rightSupplier.get(); } } static final String TO_STRING_FORMAT = "%s differs from %s"; /** * Constructs a new {@link Builder}. * * @param type of the left and right object. * @return a new {@link Builder}. * @since 3.15.0 */ public static Builder builder() { return new Builder<>(); } private final List> diffs; private final boolean equals; private final T left; private final T right; private final ToStringStyle style; private final String toStringFormat; /** * 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 left {@code this} object * @param right the object to diff against * @param style the style to use when outputting the objects, {@code null} uses the default * @throws NullPointerException if {@code lhs} or {@code rhs} is {@code null} * @deprecated Use {@link Builder}. */ @Deprecated public DiffBuilder(final T left, final T right, final ToStringStyle style) { this(left, right, 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 left {@code this} object * @param right the object to diff against * @param style the style to use when outputting the objects, {@code null} uses the default * @param testObjectsEquals 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 * @deprecated Use {@link Builder}. */ @Deprecated public DiffBuilder(final T left, final T right, final ToStringStyle style, final boolean testObjectsEquals) { this(left, right, style, testObjectsEquals, TO_STRING_FORMAT); } private DiffBuilder(final T left, final T right, final ToStringStyle style, final boolean testObjectsEquals, final String toStringFormat) { this.left = Objects.requireNonNull(left, "left"); this.right = Objects.requireNonNull(right, "right"); this.diffs = new ArrayList<>(); this.toStringFormat = toStringFormat; this.style = style != null ? style : ToStringStyle.DEFAULT_STYLE; // Don't compare any fields if objects equal this.equals = testObjectsEquals && Objects.equals(left, right); } private DiffBuilder add(final String fieldName, final Supplier left, final Supplier right, final Class type) { diffs.add(new SDiff<>(fieldName, left, right, type)); return this; } /** * Tests if two {@code boolean}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code boolean} * @param rhs the right-hand side {@code boolean} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final boolean lhs, final boolean rhs) { return equals || lhs == rhs ? this : add(fieldName, () -> Boolean.valueOf(lhs), () -> Boolean.valueOf(rhs), Boolean.class); } /** * Tests if two {@code boolean[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code boolean[]} * @param rhs the right-hand side {@code boolean[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final boolean[] lhs, final boolean[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Boolean[].class); } /** * Tests if two {@code byte}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code byte} * @param rhs the right-hand side {@code byte} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final byte lhs, final byte rhs) { return equals || lhs == rhs ? this : add(fieldName, () -> Byte.valueOf(lhs), () -> Byte.valueOf(rhs), Byte.class); } /** * Tests if two {@code byte[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code byte[]} * @param rhs the right-hand side {@code byte[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final byte[] lhs, final byte[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Byte[].class); } /** * Tests if two {@code char}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code char} * @param rhs the right-hand side {@code char} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final char lhs, final char rhs) { return equals || lhs == rhs ? this : add(fieldName, () -> Character.valueOf(lhs), () -> Character.valueOf(rhs), Character.class); } /** * Tests if two {@code char[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code char[]} * @param rhs the right-hand side {@code char[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final char[] lhs, final char[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Character[].class); } /** * Appends diffs from another {@link DiffResult}. * *

* Useful this method 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 {@code this} instance. * @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) { Objects.requireNonNull(diffResult, "diffResult"); if (equals) { return this; } diffResult.getDiffs().forEach(diff -> append(fieldName + "." + diff.getFieldName(), diff.getLeft(), diff.getRight())); return this; } /** * Tests if two {@code double}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code double} * @param rhs the right-hand side {@code double} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final double lhs, final double rhs) { return equals || Double.doubleToLongBits(lhs) == Double.doubleToLongBits(rhs) ? this : add(fieldName, () -> Double.valueOf(lhs), () -> Double.valueOf(rhs), Double.class); } /** * Tests if two {@code double[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code double[]} * @param rhs the right-hand side {@code double[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final double[] lhs, final double[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Double[].class); } /** * Test if two {@code float}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code float} * @param rhs the right-hand side {@code float} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final float lhs, final float rhs) { return equals || Float.floatToIntBits(lhs) == Float.floatToIntBits(rhs) ? this : add(fieldName, () -> Float.valueOf(lhs), () -> Float.valueOf(rhs), Float.class); } /** * Tests if two {@code float[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code float[]} * @param rhs the right-hand side {@code float[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final float[] lhs, final float[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Float[].class); } /** * Tests if two {@code int}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code int} * @param rhs the right-hand side {@code int} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final int lhs, final int rhs) { return equals || lhs == rhs ? this : add(fieldName, () -> Integer.valueOf(lhs), () -> Integer.valueOf(rhs), Integer.class); } /** * Tests if two {@code int[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code int[]} * @param rhs the right-hand side {@code int[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final int[] lhs, final int[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Integer[].class); } /** * Tests if two {@code long}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code long} * @param rhs the right-hand side {@code long} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final long lhs, final long rhs) { return equals || lhs == rhs ? this : add(fieldName, () -> Long.valueOf(lhs), () -> Long.valueOf(rhs), Long.class); } /** * Tests if two {@code long[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code long[]} * @param rhs the right-hand side {@code long[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final long[] lhs, final long[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Long[].class); } /** * Tests if two {@link Objects}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@link Object} * @param rhs the right-hand side {@link Object} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final Object lhs, final Object rhs) { if (equals || lhs == rhs) { return this; } // rhs cannot be null, as lhs != rhs final Object test = lhs != null ? lhs : rhs; if (ObjectUtils.isArray(test)) { if (test instanceof boolean[]) { return append(fieldName, (boolean[]) lhs, (boolean[]) rhs); } if (test instanceof byte[]) { return append(fieldName, (byte[]) lhs, (byte[]) rhs); } if (test instanceof char[]) { return append(fieldName, (char[]) lhs, (char[]) rhs); } if (test instanceof double[]) { return append(fieldName, (double[]) lhs, (double[]) rhs); } if (test instanceof float[]) { return append(fieldName, (float[]) lhs, (float[]) rhs); } if (test instanceof int[]) { return append(fieldName, (int[]) lhs, (int[]) rhs); } if (test instanceof long[]) { return append(fieldName, (long[]) lhs, (long[]) rhs); } if (test instanceof short[]) { return append(fieldName, (short[]) lhs, (short[]) rhs); } return append(fieldName, (Object[]) lhs, (Object[]) rhs); } // Not array type return Objects.equals(lhs, rhs) ? this : add(fieldName, () -> lhs, () -> rhs, Object.class); } /** * Tests if two {@code Object[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code Object[]} * @param rhs the right-hand side {@code Object[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final Object[] lhs, final Object[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> lhs, () -> rhs, Object[].class); } /** * Tests if two {@code short}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code short} * @param rhs the right-hand side {@code short} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final short lhs, final short rhs) { return equals || lhs == rhs ? this : add(fieldName, () -> Short.valueOf(lhs), () -> Short.valueOf(rhs), Short.class); } /** * Tests if two {@code short[]}s are equal. * * @param fieldName the field name * @param lhs the left-hand side {@code short[]} * @param rhs the right-hand side {@code short[]} * @return {@code this} instance. * @throws NullPointerException if field name is {@code null} */ public DiffBuilder append(final String fieldName, final short[] lhs, final short[] rhs) { return equals || Arrays.equals(lhs, rhs) ? this : add(fieldName, () -> ArrayUtils.toObject(lhs), () -> ArrayUtils.toObject(rhs), Short[].class); } /** * 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, toStringFormat); } /** * Gets the left object. * * @return the left object. */ T getLeft() { return left; } /** * Gets the right object. * * @return the right object. */ T getRight() { return right; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy