diff --git a/src/main/java/de/danielbechler/diff/BeanDiffer.java b/src/main/java/de/danielbechler/diff/BeanDiffer.java index 1851d6f2..430e7289 100644 --- a/src/main/java/de/danielbechler/diff/BeanDiffer.java +++ b/src/main/java/de/danielbechler/diff/BeanDiffer.java @@ -72,14 +72,18 @@ else if (instances.hasBeenRemoved()) private void compareUsingAppropriateMethod(final Node beanNode, final Instances instances) { - if (nodeInspector.isIntrospectible(beanNode)) + if (nodeInspector.isCompareToOnly(beanNode)) { - compareUsingIntrospection(beanNode, instances); + compareUsingCompareTo(beanNode, instances); } else if (nodeInspector.isEqualsOnly(beanNode)) { compareUsingEquals(beanNode, instances); } + else if (nodeInspector.isIntrospectible(beanNode)) + { + compareUsingIntrospection(beanNode, instances); + } } private void compareUsingIntrospection(final Node beanNode, final Instances beanInstances) @@ -96,6 +100,19 @@ private void compareUsingIntrospection(final Node beanNode, final Instances bean } } + @SuppressWarnings({"MethodMayBeStatic"}) + private void compareUsingCompareTo(final Node beanNode, final Instances instances) + { + if (instances.areEqualByComparison()) + { + beanNode.setState(Node.State.UNTOUCHED); + } + else + { + beanNode.setState(Node.State.CHANGED); + } + } + @SuppressWarnings({"MethodMayBeStatic"}) private void compareUsingEquals(final Node beanNode, final Instances instances) { diff --git a/src/main/java/de/danielbechler/diff/Configuration.java b/src/main/java/de/danielbechler/diff/Configuration.java index 5b9a4e4a..5396a2ea 100644 --- a/src/main/java/de/danielbechler/diff/Configuration.java +++ b/src/main/java/de/danielbechler/diff/Configuration.java @@ -77,6 +77,7 @@ public enum PrimitiveDefaultValueMode private final Collection includedProperties = new HashSet(10); private final Collection excludedProperties = new HashSet(10); private final Collection equalsOnlyProperties = new LinkedHashSet(10); + private final Collection> compareToOnlyTypes = new LinkedHashSet>(10); private final Collection> equalsOnlyTypes = new LinkedHashSet>(10); private boolean returnUnchangedNodes = false; private boolean returnIgnoredNodes = false; @@ -124,6 +125,12 @@ public Configuration withoutProperty(final PropertyPath propertyPath) return this; } + public Configuration withCompareToOnlyType(final Class type) + { + this.compareToOnlyTypes.add(type); + return this; + } + public Configuration withEqualsOnlyType(final Class type) { this.equalsOnlyTypes.add(type); @@ -257,6 +264,23 @@ public boolean isExcluded(final Node node) return false; } + public boolean isCompareToOnly(final Node node) + { + final Class propertyType = node.getType(); + if (propertyType != null) + { + if (compareToOnlyTypes.contains(propertyType) && Comparable.class.isAssignableFrom(propertyType)) + { + return true; + } + if (Classes.isComparableType(propertyType)) + { + return true; + } + } + return false; + } + public boolean isEqualsOnly(final Node node) { final Class propertyType = node.getType(); @@ -311,13 +335,9 @@ else if (node.hasChildren()) return true; } - public boolean isIntrospectible(final Node node) + public boolean isIntrospectible(final Node node) { - if (isEqualsOnly(node)) - { - return false; - } - else if (node.isAdded()) + if (node.isAdded()) { return returnChildrenOfAddedNodes; } diff --git a/src/main/java/de/danielbechler/diff/Instances.java b/src/main/java/de/danielbechler/diff/Instances.java index f790f904..0c02765f 100644 --- a/src/main/java/de/danielbechler/diff/Instances.java +++ b/src/main/java/de/danielbechler/diff/Instances.java @@ -25,6 +25,7 @@ import java.util.*; import static de.danielbechler.util.Objects.*; +import static de.danielbechler.util.Comparables.*; /** @author Daniel Bechler */ @SuppressWarnings({"UnusedDeclaration"}) @@ -172,7 +173,12 @@ public boolean areEqual() return isEqual(base, working); } - public boolean areSame() + public boolean areEqualByComparison() + { + return isEqualByComparison((Comparable) base, (Comparable) working); + } + + public boolean areSame() { return working == base; } diff --git a/src/main/java/de/danielbechler/diff/NodeInspector.java b/src/main/java/de/danielbechler/diff/NodeInspector.java index ea11ac1d..9744eb4f 100644 --- a/src/main/java/de/danielbechler/diff/NodeInspector.java +++ b/src/main/java/de/danielbechler/diff/NodeInspector.java @@ -27,6 +27,8 @@ interface NodeInspector boolean isExcluded(Node node); + boolean isCompareToOnly(Node node); + boolean isEqualsOnly(Node node); boolean isReturnable(Node node); diff --git a/src/main/java/de/danielbechler/util/Classes.java b/src/main/java/de/danielbechler/util/Classes.java index 72c36a2c..870af819 100644 --- a/src/main/java/de/danielbechler/util/Classes.java +++ b/src/main/java/de/danielbechler/util/Classes.java @@ -19,6 +19,7 @@ import org.slf4j.*; import java.lang.reflect.*; +import java.math.BigDecimal; import java.net.*; import java.util.*; @@ -98,6 +99,11 @@ public static boolean isSimpleType(final Class clazz) Class.class.equals(clazz); } + public static boolean isComparableType(final Class clazz) + { + return BigDecimal.class.equals(clazz); + } + public static T freshInstanceOf(final Class clazz) { if (clazz == null) diff --git a/src/main/java/de/danielbechler/util/Comparables.java b/src/main/java/de/danielbechler/util/Comparables.java new file mode 100644 index 00000000..2cc11976 --- /dev/null +++ b/src/main/java/de/danielbechler/util/Comparables.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Daniel Bechler + * + * Licensed 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 de.danielbechler.util; + +/** @author Daniel Bechler */ +public class Comparables +{ + private Comparables() + { + } + + public static > boolean isEqualByComparison(final T a, final T b) + { + if (a != null) + { + return a.compareTo(b) == 0; + } + else if (b != null) + { + return b.compareTo(a) == 0; + } + return true; + } +} diff --git a/src/test/java/de/danielbechler/diff/BeanDifferShould.java b/src/test/java/de/danielbechler/diff/BeanDifferShould.java index e9a1b93d..9aaeacdf 100644 --- a/src/test/java/de/danielbechler/diff/BeanDifferShould.java +++ b/src/test/java/de/danielbechler/diff/BeanDifferShould.java @@ -97,6 +97,18 @@ public void ignore_ignored_properties() assertThat(node).self().hasState(Node.State.IGNORED); } + @Test + public void compare_bean_via_compare_to() + { + final ObjectWithCompareTo working = new ObjectWithCompareTo("foo", "ignore"); + final ObjectWithCompareTo base = new ObjectWithCompareTo("foo", "ignore this too"); + configuration.withCompareToOnlyType(ObjectWithCompareTo.class); + + final Node node = differ.compare(Node.ROOT, Instances.of(working, base)); + + assertThat(node).self().isUntouched(); + } + @Test public void compare_bean_via_equals() { diff --git a/src/test/java/de/danielbechler/diff/ConfigurationTest.java b/src/test/java/de/danielbechler/diff/ConfigurationTest.java index 2a6e924e..ae88fa42 100644 --- a/src/test/java/de/danielbechler/diff/ConfigurationTest.java +++ b/src/test/java/de/danielbechler/diff/ConfigurationTest.java @@ -24,6 +24,8 @@ import org.mockito.stubbing.*; import org.testng.annotations.*; +import java.math.BigDecimal; + import static org.fest.assertions.api.Assertions.assertThat; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.*; @@ -98,6 +100,35 @@ public void testIsIgnoredWithCategory() throws Exception assertThat(configuration.isIgnored(node), is(true)); } + @SuppressWarnings({"unchecked"}) + @Test + public void testIsCompareToOnlyWithConfiguredPropertyType() throws Exception + { + final Class aClass = ObjectWithStringAndCompareTo.class; + when(node.getType()).thenReturn(aClass); + configuration.withCompareToOnlyType(aClass); + assertThat(configuration.isCompareToOnly(node), is(true)); + } + + @SuppressWarnings({"unchecked"}) + @Test + public void testIsCompareToOnlyWithConfiguredPropertyTypeNotComparable() throws Exception + { + final Class aClass = ObjectWithString.class; + when(node.getType()).thenReturn(aClass); + configuration.withCompareToOnlyType(aClass); + assertThat(configuration.isCompareToOnly(node), is(false)); + } + + @SuppressWarnings({"unchecked"}) + @Test + public void testIsCompareToOnlyWithComparableType() throws Exception + { + final Class aClass = BigDecimal.class; + when(node.getType()).thenReturn(aClass); + assertThat(configuration.isCompareToOnly(node), is(true)); + } + @Test public void testIsEqualsOnlyWithConfiguredPropertyPath() throws Exception { @@ -142,13 +173,6 @@ public void testIsEqualsOnlyWithTypeThatShouldNotBeComparedUsingEquals() throws assertThat(configuration.isEqualsOnly(node), is(false)); } - @Test - public void testIsIntrospectibleWithEqualsOnlyNodeReturnsFalse() - { - when(node.getType()).then(returnClass(String.class)); - assertThat(configuration.isIntrospectible(node)).isFalse(); - } - @Test public void testIsIntrospectibleWithUntouchedNonEqualsOnlyNodeReturnsFalse() { @@ -189,7 +213,7 @@ public void testIsIntrospectibleReturnsFalseForRemovedNodeIfChildrenOfRemovedNod assertThat(configuration.isIntrospectible(node)).isFalse(); } - @SuppressWarnings({"TypeMayBeWeakened"}) + @SuppressWarnings({"TypeMayBeWeakened"}) private static Answer> returnClass(final Class aClass) { return new Answer>() diff --git a/src/test/java/de/danielbechler/diff/mock/ObjectWithCompareTo.java b/src/test/java/de/danielbechler/diff/mock/ObjectWithCompareTo.java new file mode 100644 index 00000000..2e5baf92 --- /dev/null +++ b/src/test/java/de/danielbechler/diff/mock/ObjectWithCompareTo.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012 Daniel Bechler + * + * Licensed 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 de.danielbechler.diff.mock; + +import de.danielbechler.util.Assert; + +/** @author Daniel Bechler */ +public class ObjectWithCompareTo implements Comparable +{ + private final String key; + + private String value; + private ObjectWithCompareTo item; + + public ObjectWithCompareTo(final String key) + { + Assert.hasText(key, "key"); + this.key = key; + } + + public ObjectWithCompareTo(final String key, final String value) + { + this(key); + this.value = value; + } + + public String getKey() + { + return key; + } + + public String getValue() + { + return value; + } + + public void setValue(final String value) + { + this.value = value; + } + + public ObjectWithCompareTo getItem() + { + return item; + } + + public ObjectWithCompareTo setItem(final ObjectWithCompareTo item) + { + this.item = item; + return this; + } + + public int compareTo(ObjectWithCompareTo objectWithCompareTo) { + return this.key.compareTo(objectWithCompareTo.key); + } +} diff --git a/src/test/java/de/danielbechler/diff/mock/ObjectWithStringAndCompareTo.java b/src/test/java/de/danielbechler/diff/mock/ObjectWithStringAndCompareTo.java new file mode 100644 index 00000000..f4e90c0d --- /dev/null +++ b/src/test/java/de/danielbechler/diff/mock/ObjectWithStringAndCompareTo.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012 Daniel Bechler + * + * Licensed 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 de.danielbechler.diff.mock; + +/** @author Daniel Bechler */ +@SuppressWarnings({ + "UnusedDeclaration" +}) +public class ObjectWithStringAndCompareTo implements Comparable +{ + private String value; + + public ObjectWithStringAndCompareTo() + { + } + + public ObjectWithStringAndCompareTo(final String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + + public void setValue(final String value) + { + this.value = value; + } + + public int compareTo(ObjectWithStringAndCompareTo objectWithStringAndCompareTo) { + return this.value.compareTo(objectWithStringAndCompareTo.value); + } +}