Add ReflectionDiffBuilder.Builder
- Add ReflectionDiffBuilder.builder()
This commit is contained in:
parent
aa4eef85cf
commit
4949adec48
|
@ -50,6 +50,8 @@ The <action> type attribute can be add,update,fix,remove.
|
|||
<action issue="LANG-1724" type="add" dev="ggregory" due-to="Gary Gregory, Dennis Baerten">Customize text pattern in DiffResult#toString().</action>
|
||||
<action issue="LANG-1724" type="add" dev="ggregory" due-to="Gary Gregory">Add DiffBuilder.Builder</action>
|
||||
<action issue="LANG-1724" type="add" dev="ggregory" due-to="Gary Gregory">Add DiffBuilder.builder()</action>
|
||||
<action issue="LANG-1724" type="add" dev="ggregory" due-to="Gary Gregory">Add ReflectionDiffBuilder.Builder</action>
|
||||
<action issue="LANG-1724" type="add" dev="ggregory" due-to="Gary Gregory">Add ReflectionDiffBuilder.builder()</action>
|
||||
<!-- FIX -->
|
||||
<action type="fix" dev="ggregory" due-to="Miklós Karakó, Gary Gregory">Improve Javadoc in ExceptionUtils #1136.</action>
|
||||
<action type="fix" dev="ggregory" due-to="Saiharshith Karuneegar Ramesh, Gary Gregory">Fixed two non-deterministic tests in EnumUtilsTest.java #1131.</action>
|
||||
|
@ -62,7 +64,10 @@ The <action> type attribute can be add,update,fix,remove.
|
|||
<action type="update" dev="sebb" due-to="Dependabot">Bump commons-parent from 64 to 65.</action>
|
||||
<!-- REMOVE -->
|
||||
<action type="remove" dev="ggregory" due-to="Paranoïd User">Drop obsolete JDK 13 Maven profile #1142.</action>
|
||||
<action type="remove" dev="ggregory" due-to="Gary Gregory">Deprecate org.apache.commons.lang3.builder.Diff.getType().</action>
|
||||
<action type="remove" dev="ggregory" due-to="Gary Gregory">Deprecate Diff.getType().</action>
|
||||
<action type="remove" dev="ggregory" due-to="Gary Gregory">Deprecate DiffBuilder.DiffBuilder(T, T, ToStringStyle).</action>
|
||||
<action type="remove" dev="ggregory" due-to="Gary Gregory">Deprecate DiffBuilder.DiffBuilder(T, T, ToStringStyle, boolean).</action>
|
||||
<action type="remove" dev="ggregory" due-to="Gary Gregory">Deprecate ReflectionDiffBuilder.ReflectionDiffBuilder(T, T, ToStringStyle).</action>
|
||||
</release>
|
||||
<release version="3.14.0" date="2023-11-18" description="New features and bug fixes (Java 8 or above).">
|
||||
<!-- FIX -->
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||
* To use this class, write code as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* <pre>{@code
|
||||
* public class Person implements Diffable<Person> {
|
||||
* String name;
|
||||
* int age;
|
||||
|
@ -42,14 +42,18 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||
*
|
||||
* 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)
|
||||
* return new DiffBuilder.<Person>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();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* The {@link ToStringStyle} passed to the constructor is embedded in the returned {@link DiffResult} and influences the style of the
|
||||
|
@ -574,4 +578,22 @@ public class DiffBuilder<T> implements Builder<DiffResult<T>> {
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,16 +29,14 @@ import org.apache.commons.lang3.reflect.FieldUtils;
|
|||
* Assists in implementing {@link Diffable#diff(Object)} methods.
|
||||
*
|
||||
* <p>
|
||||
* All non-static, non-transient fields (including inherited fields)
|
||||
* of the objects to diff are discovered using reflection and compared
|
||||
* for differences.
|
||||
* All non-static, non-transient fields (including inherited fields) of the objects to diff are discovered using reflection and compared for differences.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To use this class, write code as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* <pre>{@code
|
||||
* public class Person implements Diffable<Person> {
|
||||
* String name;
|
||||
* int age;
|
||||
|
@ -47,23 +45,27 @@ import org.apache.commons.lang3.reflect.FieldUtils;
|
|||
*
|
||||
* public DiffResult diff(Person obj) {
|
||||
* // No need for null check, as NullPointerException correct if obj is null
|
||||
* return new ReflectionDiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
* return new ReflectionDiffBuilder.<Person>builder()
|
||||
* .setDiffBuilder(DiffBuilder.<Person>builder()
|
||||
* .setLeft(this)
|
||||
* .setRight(obj)
|
||||
* .setStyle(ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
* .build())
|
||||
* .setExcludeFieldNames("userName", "password")
|
||||
* .build();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* 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)}.
|
||||
* 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)}.
|
||||
* </p>
|
||||
* <p>
|
||||
* See {@link DiffBuilder} for a non-reflection based version of this class.
|
||||
* </p>
|
||||
* @param <T>
|
||||
* type of the left and right object to diff.
|
||||
*
|
||||
* @param <T> type of the left and right object to diff.
|
||||
* @see Diffable
|
||||
* @see Diff
|
||||
* @see DiffResult
|
||||
|
@ -73,39 +75,98 @@ import org.apache.commons.lang3.reflect.FieldUtils;
|
|||
*/
|
||||
public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
|
||||
|
||||
private final T left;
|
||||
private final T right;
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
*
|
||||
* @param <T> type of the left and right object.
|
||||
* @since 3.15.0
|
||||
*/
|
||||
public static final class Builder<T> {
|
||||
|
||||
private String[] excludeFieldNames = ArrayUtils.EMPTY_STRING_ARRAY;
|
||||
private DiffBuilder<T> diffBuilder;
|
||||
|
||||
/**
|
||||
* Builds a new configured {@link ReflectionDiffBuilder}.
|
||||
*
|
||||
* @return a new configured {@link ReflectionDiffBuilder}.
|
||||
*/
|
||||
public ReflectionDiffBuilder<T> build() {
|
||||
return new ReflectionDiffBuilder<>(diffBuilder, excludeFieldNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DiffBuilder.
|
||||
*
|
||||
* @param diffBuilder the DiffBuilder.
|
||||
* @return this.
|
||||
*/
|
||||
public Builder<T> setDiffBuilder(final DiffBuilder<T> diffBuilder) {
|
||||
this.diffBuilder = diffBuilder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets field names to exclude from output. Intended for fields like {@code "password"} or {@code "lastModificationDate"}.
|
||||
*
|
||||
* @param excludeFieldNames field names to exclude.
|
||||
* @return this.
|
||||
*/
|
||||
public Builder<T> setExcludeFieldNames(final String... excludeFieldNames) {
|
||||
this.excludeFieldNames = toExcludeFieldNames(excludeFieldNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder}.
|
||||
*
|
||||
* @param <T> type of the left and right object.
|
||||
* @return a new {@link Builder}.
|
||||
* @since 3.15.0
|
||||
*/
|
||||
public static <T> Builder<T> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
private static String[] toExcludeFieldNames(final String[] excludeFieldNames) {
|
||||
if (excludeFieldNames == null) {
|
||||
return ArrayUtils.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
// clone and remove nulls
|
||||
return ArraySorter.sort(ReflectionToStringBuilder.toNoNullStringArray(excludeFieldNames));
|
||||
}
|
||||
|
||||
private final DiffBuilder<T> diffBuilder;
|
||||
|
||||
/**
|
||||
* Field names to exclude from output. Intended for fields like {@code "password"} or {@code "lastModificationDate"}.
|
||||
*
|
||||
* @since 3.13.0
|
||||
*/
|
||||
private String[] excludeFieldNames;
|
||||
|
||||
private ReflectionDiffBuilder(final DiffBuilder<T> diffBuilder, final String[] excludeFieldNames) {
|
||||
this.diffBuilder = diffBuilder;
|
||||
this.excludeFieldNames = excludeFieldNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a builder for the specified objects with the specified style.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* If {@code left == right} or {@code left.equals(right)} then the builder will not evaluate any calls to {@code append(...)} and will return an empty
|
||||
* {@link DiffResult} when {@link #build()} is executed.
|
||||
* </p>
|
||||
* @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 IllegalArgumentException
|
||||
* if {@code lhs} or {@code rhs} is {@code null}
|
||||
*
|
||||
* @param left {@code this} object.
|
||||
* @param right the object to diff against.
|
||||
* @param style the style will use when outputting the objects, {@code null} uses the default
|
||||
* @throws IllegalArgumentException if {@code left} or {@code right} is {@code null}.
|
||||
* @deprecated Use {@link Builder}.
|
||||
*/
|
||||
public ReflectionDiffBuilder(final T lhs, final T rhs, final ToStringStyle style) {
|
||||
this.left = lhs;
|
||||
this.right = rhs;
|
||||
this.diffBuilder = DiffBuilder.<T>builder().setLeft(lhs).setRight(rhs).setStyle(style).build();
|
||||
@Deprecated
|
||||
public ReflectionDiffBuilder(final T left, final T right, final ToStringStyle style) {
|
||||
this(DiffBuilder.<T>builder().setLeft(left).setRight(right).setStyle(style).build(), null);
|
||||
}
|
||||
|
||||
private boolean accept(final Field field) {
|
||||
|
@ -118,8 +179,7 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
|
|||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
if (this.excludeFieldNames != null
|
||||
&& Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
|
||||
if (this.excludeFieldNames != null && Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
|
||||
// Reject fields from the getExcludeFieldNames list.
|
||||
return false;
|
||||
}
|
||||
|
@ -130,7 +190,7 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
|
|||
for (final Field field : FieldUtils.getAllFields(clazz)) {
|
||||
if (accept(field)) {
|
||||
try {
|
||||
diffBuilder.append(field.getName(), FieldUtils.readField(field, left, true), FieldUtils.readField(field, right, true));
|
||||
diffBuilder.append(field.getName(), readField(field, getLeft()), readField(field, getRight()));
|
||||
} catch (final IllegalAccessException e) {
|
||||
// this can't happen. Would get a Security exception instead
|
||||
// throw a runtime exception in case the impossible happens.
|
||||
|
@ -142,11 +202,11 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
|
|||
|
||||
@Override
|
||||
public DiffResult<T> build() {
|
||||
if (left.equals(right)) {
|
||||
if (getLeft().equals(getRight())) {
|
||||
return diffBuilder.build();
|
||||
}
|
||||
|
||||
appendFields(left.getClass());
|
||||
appendFields(getLeft().getClass());
|
||||
return diffBuilder.build();
|
||||
}
|
||||
|
||||
|
@ -160,21 +220,29 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
|
|||
return this.excludeFieldNames.clone();
|
||||
}
|
||||
|
||||
private T getLeft() {
|
||||
return diffBuilder.getLeft();
|
||||
}
|
||||
|
||||
private T getRight() {
|
||||
return diffBuilder.getRight();
|
||||
}
|
||||
|
||||
private Object readField(final Field field, final Object target) throws IllegalAccessException {
|
||||
return FieldUtils.readField(field, target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field names to exclude.
|
||||
*
|
||||
* @param excludeFieldNamesParam
|
||||
* The field names to exclude from the diff or {@code null}.
|
||||
* @param excludeFieldNames The field names to exclude from the diff or {@code null}.
|
||||
* @return {@code this}
|
||||
* @since 3.13.0
|
||||
* @deprecated Use {@link Builder#setExcludeFieldNames(String[])}.
|
||||
*/
|
||||
public ReflectionDiffBuilder<T> setExcludeFieldNames(final String... excludeFieldNamesParam) {
|
||||
if (excludeFieldNamesParam == null) {
|
||||
this.excludeFieldNames = ArrayUtils.EMPTY_STRING_ARRAY;
|
||||
} else {
|
||||
// clone and remove nulls
|
||||
this.excludeFieldNames = ArraySorter.sort(ReflectionToStringBuilder.toNoNullStringArray(excludeFieldNamesParam));
|
||||
}
|
||||
@Deprecated
|
||||
public ReflectionDiffBuilder<T> setExcludeFieldNames(final String... excludeFieldNames) {
|
||||
this.excludeFieldNames = toExcludeFieldNames(excludeFieldNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,23 +34,23 @@ public class ReflectionDiffBuilderTest extends AbstractLangTest {
|
|||
private static int staticField;
|
||||
private final ToStringStyle style = SHORT_STYLE;
|
||||
private final boolean booleanField = true;
|
||||
private final boolean[] booleanArrayField = {true};
|
||||
private final boolean[] booleanArrayField = { true };
|
||||
private final byte byteField = (byte) 0xFF;
|
||||
private final byte[] byteArrayField = {(byte) 0xFF};
|
||||
private final byte[] byteArrayField = { (byte) 0xFF };
|
||||
private char charField = 'a';
|
||||
private char[] charArrayField = {'a'};
|
||||
private char[] charArrayField = { 'a' };
|
||||
private final double doubleField = 1.0;
|
||||
private final double[] doubleArrayField = {1.0};
|
||||
private final double[] doubleArrayField = { 1.0 };
|
||||
private final float floatField = 1.0f;
|
||||
private final float[] floatArrayField = {1.0f};
|
||||
private final float[] floatArrayField = { 1.0f };
|
||||
int intField = 1;
|
||||
private final int[] intArrayField = {1};
|
||||
private final int[] intArrayField = { 1 };
|
||||
private final long longField = 1L;
|
||||
private final long[] longArrayField = {1L};
|
||||
private final long[] longArrayField = { 1L };
|
||||
private final short shortField = 1;
|
||||
private final short[] shortArrayField = {1};
|
||||
private final short[] shortArrayField = { 1 };
|
||||
private final Object objectField = null;
|
||||
private final Object[] objectArrayField = {null};
|
||||
private final Object[] objectArrayField = { null };
|
||||
private transient String transientField;
|
||||
@DiffExclude
|
||||
private String annotatedField = "a";
|
||||
|
@ -166,7 +166,26 @@ public class ReflectionDiffBuilderTest extends AbstractLangTest {
|
|||
|
||||
@Test
|
||||
public void testGetExcludeFieldNamesWithNullExcludedFieldNames() {
|
||||
final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
|
||||
// @formatter:off
|
||||
final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
|
||||
.setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
|
||||
.setLeft(new TypeTestClass())
|
||||
.setRight(new TypeTestChildClass())
|
||||
.setStyle(SHORT_STYLE)
|
||||
.build())
|
||||
.build();
|
||||
// @formatter:on
|
||||
final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
|
||||
assertNotNull(excludeFieldNames);
|
||||
assertEquals(0, excludeFieldNames.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetExcludeFieldNamesWithNullExcludedFieldNamesCtor() {
|
||||
// @formatter:off
|
||||
final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
|
||||
new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
|
||||
// @formatter:on
|
||||
reflectionDiffBuilder.setExcludeFieldNames(null);
|
||||
final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
|
||||
assertNotNull(excludeFieldNames);
|
||||
|
@ -175,7 +194,28 @@ public class ReflectionDiffBuilderTest extends AbstractLangTest {
|
|||
|
||||
@Test
|
||||
public void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNames() {
|
||||
final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
|
||||
// @formatter:off
|
||||
final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
|
||||
.setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
|
||||
.setLeft(new TypeTestClass())
|
||||
.setRight(new TypeTestChildClass())
|
||||
.setStyle(SHORT_STYLE)
|
||||
.build())
|
||||
.setExcludeFieldNames("charField", null)
|
||||
.build();
|
||||
// @formatter:on
|
||||
final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
|
||||
assertNotNull(excludeFieldNames);
|
||||
assertEquals(1, excludeFieldNames.length);
|
||||
assertEquals("charField", excludeFieldNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNamesCtor() {
|
||||
// @formatter:off
|
||||
final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
|
||||
new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
|
||||
// @formatter:on
|
||||
reflectionDiffBuilder.setExcludeFieldNames("charField", null);
|
||||
final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
|
||||
assertNotNull(excludeFieldNames);
|
||||
|
|
Loading…
Reference in New Issue