From a9b9c9f2ef3e322db044f332af26726085c2c526 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Fri, 14 Feb 2014 18:40:38 +0100 Subject: [PATCH 001/215] Some minor POM cleanups --- pom.xml | 891 +++++++++++++++++++++++++++----------------------------- 1 file changed, 433 insertions(+), 458 deletions(-) diff --git a/pom.xml b/pom.xml index 26b24e2f..dde93f77 100644 --- a/pom.xml +++ b/pom.xml @@ -9,475 +9,450 @@ under the License. --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - - org.sonatype.oss - oss-parent - 7 - + + org.sonatype.oss + oss-parent + 7 + - 4.0.0 - de.danielbechler - java-object-diff - Java Object Diff - 0.90.0-SNAPSHOT + 4.0.0 + de.danielbechler + java-object-diff + Java Object Diff + 0.90.0-SNAPSHOT - - 2.2.1 - + + 2.2.1 + - - - SQiShER - Daniel Bechler - https://github.com/SQiShER - - + + + SQiShER + Daniel Bechler + https://github.com/SQiShER + + - https://github.com/SQiShER/java-object-diff - Framework to detect and handle differences between Java objects + https://github.com/SQiShER/java-object-diff + Framework to detect and handle differences between Java objects - - - Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + + - - https://github.com/SQiShER/java-object-diff - scm:git:ssh://git@github.com/SQiShER/java-object-diff.git - scm:git:ssh://git@github.com/SQiShER/java-object-diff.git - + + https://github.com/SQiShER/java-object-diff + scm:git:ssh://git@github.com/SQiShER/java-object-diff.git + scm:git:ssh://git@github.com/SQiShER/java-object-diff.git + - - GitHub - https://github.com/SQiShER/java-object-diff/issues - + + GitHub + https://github.com/SQiShER/java-object-diff/issues + - - UTF-8 - + + travis-ci + https://travis-ci.org/SQiShER/java-object-diff + - - - - maven-compiler-plugin - - 1.5 - 1.5 - UTF-8 - groovy-eclipse-compiler - - - - org.codehaus.groovy - groovy-eclipse-compiler - 2.8.0-01 - - - org.codehaus.groovy - groovy-eclipse-batch - 2.1.5-03 - - - - - maven-javadoc-plugin - - - site - - javadoc - - - - - UTF-8 - - - - maven-release-plugin - - - - - - maven-deploy-plugin - - - org.apache.maven.wagon - wagon-ssh - 2.0 - - - - - maven-surefire-plugin - - - org.apache.maven.surefire - surefire-junit4 - 2.16 - - - org.apache.maven.surefire - surefire-testng - 2.16 - - - - true - true - - **/*Test.java - - **/*Should.java - **/*Spec.java - **/*Specification.java - - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.16 - - UTF-8 - true - - **/*IT.java - **/*ITCase.java - **/*ITSpec.java - - - - - - integration-test - verify - - - none:none - - - - integration-test-testng - - integration-test - verify - - - none:none - - - - - - org.codehaus.mojo - license-maven-plugin - - - add-third-party - - add-third-party - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-dependencies - compile - - copy-dependencies - - - - - - org.apache.felix - maven-bundle-plugin - true - - - true - - - Daniel Bechler - ${project.url} - {local-packages} - - - - - bundle-manifest - process-classes - - manifest - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-integration-test-source - generate-test-sources - - add-test-source - - - - src/integration-test/java - - - - - - - org.jvnet - animal-sniffer - 1.2 - - - animal-sniffer - compile - - check - - - - org.jvnet.animal-sniffer - java1.5 - 1.0 - - - - - - - org.jvnet.animal-sniffer - java1.5 - 1.0 - sig - - - - - org.codehaus.mojo - cobertura-maven-plugin - 2.6 - - - - - clean - - - - - - - - - - - - - - - - - - - - maven-compiler-plugin - 3.1 - - - maven-javadoc-plugin - 2.9.1 - - - maven-deploy-plugin - 2.8.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.16 - - - maven-resources-plugin - 2.6 - - - maven-release-plugin - 2.4.2 - - - maven-jar-plugin - 2.4 - - - org.apache.felix - maven-bundle-plugin - 2.4.0 - - - org.codehaus.mojo - license-maven-plugin - 1.6 - - - org.codehaus.mojo - build-helper-maven-plugin - 1.8 - - - - + + UTF-8 + 1.5 + - - - org.slf4j - slf4j-api - 1.7.6 - - - org.testng - testng - 6.8.7 - test - - - junit - junit - - - org.hamcrest - hamcrest-core - - - org.beanshell - bsh - - - org.yaml - snakeyaml - - - - - org.hamcrest - hamcrest-all - 1.3 - test - - - org.mockito - mockito-all - 1.9.5 - test - - - ch.qos.logback - logback-core - 1.1.1 - test - - - ch.qos.logback - logback-classic - 1.1.1 - test - - - org.easytesting - fest-assert-core - 2.0M10 - test - - - org.codehaus.groovy - groovy-all - 2.2.1 - test - - - org.spockframework - spock-core - 0.7-groovy-2.0 - test - - - cglib - cglib-nodep - 3.1 - test - - + + + + maven-compiler-plugin + + ${project.jdk} + ${project.jdk} + UTF-8 + groovy-eclipse-compiler + + + + org.codehaus.groovy + groovy-eclipse-compiler + 2.8.0-01 + + + org.codehaus.groovy + groovy-eclipse-batch + 2.1.5-03 + + + + + maven-javadoc-plugin + + + site + + javadoc + + + + + UTF-8 + + + + maven-release-plugin + + + + + + maven-deploy-plugin + + + org.apache.maven.wagon + wagon-ssh + 2.0 + + + + + maven-surefire-plugin + + + org.apache.maven.surefire + surefire-junit4 + 2.16 + + + org.apache.maven.surefire + surefire-testng + 2.16 + + + + true + true + + **/*Test.java + + **/*Should.java + **/*Spec.java + **/*Specification.java + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.16 + + UTF-8 + true + + **/*IT.java + **/*ITCase.java + **/*ITSpec.java + + + + + + integration-test + verify + + + none:none + + + + integration-test-testng + + integration-test + verify + + + none:none + + + + + + org.codehaus.mojo + license-maven-plugin + + + add-third-party + + add-third-party + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + compile + + copy-dependencies + + + + + + org.apache.felix + maven-bundle-plugin + true + + + true + + + Daniel Bechler + ${project.url} + {local-packages} + + + + + bundle-manifest + process-classes + + manifest + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-integration-test-source + generate-test-sources + + add-test-source + + + + src/integration-test/java + + + + + + + org.jvnet + animal-sniffer + 1.2 + + + animal-sniffer + compile + + check + + + + org.jvnet.animal-sniffer + java1.5 + 1.0 + + + + + + + org.jvnet.animal-sniffer + java1.5 + 1.0 + sig + + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.6 + + + + clean + + + + + + + + + maven-compiler-plugin + 3.1 + + + maven-javadoc-plugin + 2.9.1 + + + maven-deploy-plugin + 2.8.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.16 + + + maven-resources-plugin + 2.6 + + + maven-release-plugin + 2.4.2 + + + maven-jar-plugin + 2.4 + + + org.apache.felix + maven-bundle-plugin + 2.4.0 + + + org.codehaus.mojo + license-maven-plugin + 1.6 + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + + - - - - org.apache.maven.plugins - maven-pmd-plugin - 3.0.1 - - true - utf-8 - 100 - 1.5 - - **/*Bean.java - **/generated/*.java - - - target/generated-sources/stubs - - - - - org.apache.maven.plugins - maven-pmd-plugin - 3.0.1 - - true - utf-8 - 100 - 1.7 - - - - - - - - - + + + org.slf4j + slf4j-api + 1.7.6 + + + org.testng + testng + 6.8.7 + test + + + junit + junit + + + org.hamcrest + hamcrest-core + + + org.beanshell + bsh + + + org.yaml + snakeyaml + + + + + org.hamcrest + hamcrest-all + 1.3 + test + + + org.mockito + mockito-all + 1.9.5 + test + + + ch.qos.logback + logback-core + 1.1.1 + test + + + ch.qos.logback + logback-classic + 1.1.1 + test + + + org.easytesting + fest-assert-core + 2.0M10 + test + + + org.codehaus.groovy + groovy-all + 2.2.1 + test + + + org.spockframework + spock-core + 0.7-groovy-2.0 + test + + + cglib + cglib-nodep + 3.1 + test + + - - org.codehaus.mojo - cobertura-maven-plugin - 2.6 - - - maven-javadoc-plugin - - UTF-8 - - - - + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.0.1 + + true + utf-8 + 100 + ${project.jdk} + + **/*Bean.java + **/generated/*.java + + + target/generated-sources/stubs + + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.6 + + + maven-javadoc-plugin + + UTF-8 + + + + From 2f5ccf3e194f4ed416d273611c9de142e6e02be9 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 1 Mar 2014 21:52:05 +0100 Subject: [PATCH 002/215] Removed Accessor interface from DiffNode --- src/main/java/de/danielbechler/diff/DiffNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/danielbechler/diff/DiffNode.java b/src/main/java/de/danielbechler/diff/DiffNode.java index 5706fdf9..c8baf3d8 100644 --- a/src/main/java/de/danielbechler/diff/DiffNode.java +++ b/src/main/java/de/danielbechler/diff/DiffNode.java @@ -36,7 +36,7 @@ * * @author Daniel Bechler */ -public class DiffNode implements Accessor +public class DiffNode { public static final DiffNode ROOT = null; private final Accessor accessor; From 3c04983fc9d44534e58f206f84fe9a2d99990b65 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 1 Mar 2014 21:55:23 +0100 Subject: [PATCH 003/215] Cleanup --- src/main/java/de/danielbechler/diff/DiffNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/danielbechler/diff/DiffNode.java b/src/main/java/de/danielbechler/diff/DiffNode.java index c8baf3d8..a7102793 100644 --- a/src/main/java/de/danielbechler/diff/DiffNode.java +++ b/src/main/java/de/danielbechler/diff/DiffNode.java @@ -353,7 +353,7 @@ public final void visitChildren(final Visitor visitor) { child.visit(visitor); } - catch (StopVisitationException e) + catch (final StopVisitationException e) { return; } From ecffea22039fdcb40e45e6e5dc5154294e6c551c Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 1 Mar 2014 22:15:10 +0100 Subject: [PATCH 004/215] Renamed NamedPropertyElement to BeanPropertyElement and deleted CanonicalAccessor --- .../danielbechler/diff/CanonicalAccessor.java | 22 --- .../java/de/danielbechler/diff/DiffNode.java | 2 +- .../danielbechler/diff/InclusionService.java | 4 +- .../java/de/danielbechler/diff/NodePath.java | 12 +- .../BeanPropertyAccessExceptionHandler.java | 5 +- .../diff/bean/BeanPropertyAccessor.java | 2 +- ...yElement.java => BeanPropertyElement.java} | 10 +- .../diff/CategoryServiceTest.groovy | 157 +++++++++--------- .../de/danielbechler/diff/DiffNodeTest.java | 16 +- ...rShould.java => DifferProviderShould.java} | 9 +- .../diff/InclusionServiceSpec.groovy | 8 +- .../diff/NodePathBuilderTest.java | 46 ++--- .../diff/bean/BeanDifferShould.java | 43 +++-- .../diff/bean/BeanPropertyAccessorShould.java | 44 ++--- .../bean/StandardBeanIntrospectorTest.java | 14 +- .../diff/helper/NodeAssertionsTest.java | 11 +- .../diff/primitive/PrimitiveDifferTest.java | 52 +++--- 17 files changed, 231 insertions(+), 226 deletions(-) delete mode 100644 src/main/java/de/danielbechler/diff/CanonicalAccessor.java rename src/main/java/de/danielbechler/diff/bean/{NamedPropertyElement.java => BeanPropertyElement.java} (86%) rename src/test/java/de/danielbechler/diff/{DefaultDifferProviderShould.java => DifferProviderShould.java} (94%) diff --git a/src/main/java/de/danielbechler/diff/CanonicalAccessor.java b/src/main/java/de/danielbechler/diff/CanonicalAccessor.java deleted file mode 100644 index 7c066256..00000000 --- a/src/main/java/de/danielbechler/diff/CanonicalAccessor.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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; - -/** @author Daniel Bechler */ -public interface CanonicalAccessor extends Accessor { - -} diff --git a/src/main/java/de/danielbechler/diff/DiffNode.java b/src/main/java/de/danielbechler/diff/DiffNode.java index a7102793..a6418d7f 100644 --- a/src/main/java/de/danielbechler/diff/DiffNode.java +++ b/src/main/java/de/danielbechler/diff/DiffNode.java @@ -232,7 +232,7 @@ public Collection getChildren() */ public DiffNode getChild(final String propertyName) { - return children.get(new NamedPropertyElement(propertyName)); + return getChild(new BeanPropertyElement(propertyName)); } /** diff --git a/src/main/java/de/danielbechler/diff/InclusionService.java b/src/main/java/de/danielbechler/diff/InclusionService.java index 05feec88..c839f25c 100644 --- a/src/main/java/de/danielbechler/diff/InclusionService.java +++ b/src/main/java/de/danielbechler/diff/InclusionService.java @@ -28,9 +28,9 @@ public InclusionService(final CategoryResolver categoryResolver) private static String getNodePropertyName(final DiffNode node) { final Element pathElement = node.getPathElement(); - if (pathElement instanceof NamedPropertyElement) + if (pathElement instanceof BeanPropertyElement) { - return ((NamedPropertyElement) pathElement).getPropertyName(); + return ((BeanPropertyElement) pathElement).getPropertyName(); } return null; } diff --git a/src/main/java/de/danielbechler/diff/NodePath.java b/src/main/java/de/danielbechler/diff/NodePath.java index c6c16857..b231f59d 100644 --- a/src/main/java/de/danielbechler/diff/NodePath.java +++ b/src/main/java/de/danielbechler/diff/NodePath.java @@ -24,7 +24,9 @@ import java.util.*; import java.util.Collections; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public final class NodePath implements Comparable { private final List elements; @@ -154,7 +156,9 @@ else if (this.getElements().size() > that.getElements().size()) } } - /** @author Daniel Bechler */ + /** + * @author Daniel Bechler + */ @SuppressWarnings({"UnusedDeclaration"}) private static final class InitialBuilderImpl implements InitialBuilder { @@ -195,10 +199,10 @@ public AppendableBuilder withElement(final Element element) public AppendableBuilder withPropertyName(final String name, final String... names) { - elements.add(new NamedPropertyElement(name)); + elements.add(new BeanPropertyElement(name)); for (final String s : names) { - elements.add(new NamedPropertyElement(s)); + elements.add(new BeanPropertyElement(s)); } return this; } diff --git a/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessExceptionHandler.java b/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessExceptionHandler.java index 87beb226..c589974f 100644 --- a/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessExceptionHandler.java +++ b/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessExceptionHandler.java @@ -2,7 +2,10 @@ import de.danielbechler.diff.*; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ +@SuppressWarnings("UnusedDeclaration") public interface BeanPropertyAccessExceptionHandler { DiffNode onPropertyWriteException(BeanPropertyWriteException ex, DiffNode propertyNode); diff --git a/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java b/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java index aa804ab2..2240be15 100644 --- a/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java +++ b/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java @@ -221,7 +221,7 @@ public String getPropertyName() public Element getPathElement() { - return new NamedPropertyElement(this.propertyName); + return new BeanPropertyElement(this.propertyName); } /** diff --git a/src/main/java/de/danielbechler/diff/bean/NamedPropertyElement.java b/src/main/java/de/danielbechler/diff/bean/BeanPropertyElement.java similarity index 86% rename from src/main/java/de/danielbechler/diff/bean/NamedPropertyElement.java rename to src/main/java/de/danielbechler/diff/bean/BeanPropertyElement.java index 12816a3a..39d4ecef 100644 --- a/src/main/java/de/danielbechler/diff/bean/NamedPropertyElement.java +++ b/src/main/java/de/danielbechler/diff/bean/BeanPropertyElement.java @@ -19,12 +19,14 @@ import de.danielbechler.diff.*; import de.danielbechler.util.*; -/** @author Daniel Bechler */ -public final class NamedPropertyElement extends Element +/** + * @author Daniel Bechler + */ +public final class BeanPropertyElement extends Element { private final String propertyName; - public NamedPropertyElement(final String propertyName) + public BeanPropertyElement(final String propertyName) { Assert.hasText(propertyName, "propertyName"); this.propertyName = propertyName; @@ -53,7 +55,7 @@ public boolean equals(final Object o) return false; } - final NamedPropertyElement that = (NamedPropertyElement) o; + final BeanPropertyElement that = (BeanPropertyElement) o; if (!propertyName.equals(that.propertyName)) { diff --git a/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy b/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy index 69a626d8..9f2d8824 100644 --- a/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy +++ b/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy @@ -16,7 +16,7 @@ package de.danielbechler.diff -import de.danielbechler.diff.bean.NamedPropertyElement +import de.danielbechler.diff.bean.BeanPropertyElement import spock.lang.Specification import static DiffNode.ROOT @@ -24,86 +24,77 @@ import static DiffNode.ROOT /** * @author Daniel Bechler */ -class CategoryServiceTest extends Specification -{ - def categoryService = new CategoryService() - def accessor = Mock(PropertyAwareAccessor) - def nodePath = NodePath.buildWith("foo") - def nodeType = Alliance - def DiffNode node - def DiffNode rootNode - - def "setup"() - { - accessor.pathElement >> new NamedPropertyElement("foo") - rootNode = new DiffNode(ROOT, RootAccessor.instance, null) - node = new DiffNode(rootNode, accessor, nodeType) - } - - def "resolveCategories: should return categories configured via path"() - { - given: - categoryService.ofNode(nodePath).toBe("Stark", "Lannister") - - expect: - categoryService.resolveCategories(node) == ["Stark", "Lannister"] as Set - } - - def "resolveCategories: should return categories configured via type"() - { - given: - categoryService.ofType(nodeType).toBe("Stark", "Lannister") - - expect: - categoryService.resolveCategories(node) == ["Stark", "Lannister"] as Set - } - - def "resolveCategories: should return categories configured via node"() - { - given: - accessor.categories >> ["Stark", "Lannister"] - - expect: - categoryService.resolveCategories(node) == ["Stark", "Lannister"] as Set - } - - def "resolveCategories: should return combined categories configured via all possible options"() - { - given: - categoryService.ofNode(nodePath).toBe("A") - - and: - categoryService.ofType(nodeType).toBe("B") - - and: - accessor.categories >> ["C"] - - expect: - categoryService.resolveCategories(node) == ["A", "B", "C"] as Set - } - - def "resolveCategories: should not return categories of parent nodes"() - { - given: - accessor.categories >> ["B"] - - and: - categoryService.ofNode(NodePath.buildRootPath()).toBe("A") - - expect: - categoryService.resolveCategories(node) == ["B"] as Set - } - - def "resolveCategories: should return empty Set if no category is defined"() - { - given: - node = new DiffNode() - - expect: - categoryService.resolveCategories(node) == [] as Set - } - - class Alliance - { - } +class CategoryServiceTest extends Specification { + def categoryService = new CategoryService() + def accessor = Mock(PropertyAwareAccessor) + def nodePath = NodePath.buildWith("foo") + def nodeType = Alliance + def DiffNode node + def DiffNode rootNode + + def "setup"() { + accessor.pathElement >> new BeanPropertyElement("foo") + rootNode = new DiffNode(ROOT, RootAccessor.instance, null) + node = new DiffNode(rootNode, accessor, nodeType) + } + + def "resolveCategories: should return categories configured via path"() { + given: + categoryService.ofNode(nodePath).toBe("Stark", "Lannister") + + expect: + categoryService.resolveCategories(node) == ["Stark", "Lannister"] as Set + } + + def "resolveCategories: should return categories configured via type"() { + given: + categoryService.ofType(nodeType).toBe("Stark", "Lannister") + + expect: + categoryService.resolveCategories(node) == ["Stark", "Lannister"] as Set + } + + def "resolveCategories: should return categories configured via node"() { + given: + accessor.categories >> ["Stark", "Lannister"] + + expect: + categoryService.resolveCategories(node) == ["Stark", "Lannister"] as Set + } + + def "resolveCategories: should return combined categories configured via all possible options"() { + given: + categoryService.ofNode(nodePath).toBe("A") + + and: + categoryService.ofType(nodeType).toBe("B") + + and: + accessor.categories >> ["C"] + + expect: + categoryService.resolveCategories(node) == ["A", "B", "C"] as Set + } + + def "resolveCategories: should not return categories of parent nodes"() { + given: + accessor.categories >> ["B"] + + and: + categoryService.ofNode(NodePath.buildRootPath()).toBe("A") + + expect: + categoryService.resolveCategories(node) == ["B"] as Set + } + + def "resolveCategories: should return empty Set if no category is defined"() { + given: + node = new DiffNode() + + expect: + categoryService.resolveCategories(node) == [] as Set + } + + class Alliance { + } } diff --git a/src/test/java/de/danielbechler/diff/DiffNodeTest.java b/src/test/java/de/danielbechler/diff/DiffNodeTest.java index d65e6d95..cb2048ee 100644 --- a/src/test/java/de/danielbechler/diff/DiffNodeTest.java +++ b/src/test/java/de/danielbechler/diff/DiffNodeTest.java @@ -29,10 +29,16 @@ import static org.fest.assertions.api.Assertions.assertThat; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.*; - -/** @author Daniel Bechler */ +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.Mock; +import static org.mockito.MockitoAnnotations.initMocks; + +/** + * @author Daniel Bechler + */ public class DiffNodeTest { @Mock @@ -106,7 +112,7 @@ public void testHasChanges_returns_true_when_child_has_changed() public void testGetPropertyPath_with_parent_node_should_return_canonical_path() { final DiffNode parentNode = new DiffNode(RootAccessor.getInstance(), String.class); - when(accessor.getPathElement()).thenReturn(new NamedPropertyElement("foo")); + when(accessor.getPathElement()).thenReturn(new BeanPropertyElement("foo")); final DiffNode root = new DiffNode(parentNode, accessor, Object.class); diff --git a/src/test/java/de/danielbechler/diff/DefaultDifferProviderShould.java b/src/test/java/de/danielbechler/diff/DifferProviderShould.java similarity index 94% rename from src/test/java/de/danielbechler/diff/DefaultDifferProviderShould.java rename to src/test/java/de/danielbechler/diff/DifferProviderShould.java index b830af90..7b060a46 100644 --- a/src/test/java/de/danielbechler/diff/DefaultDifferProviderShould.java +++ b/src/test/java/de/danielbechler/diff/DifferProviderShould.java @@ -22,10 +22,13 @@ import java.util.*; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -/** @author Daniel Bechler */ -public class DefaultDifferProviderShould +/** + * @author Daniel Bechler + */ +public class DifferProviderShould { private DifferProvider differProvider; diff --git a/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy b/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy index 80a4dc45..f5b5eb8a 100644 --- a/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy +++ b/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy @@ -16,7 +16,7 @@ package de.danielbechler.diff -import de.danielbechler.diff.bean.NamedPropertyElement +import de.danielbechler.diff.bean.BeanPropertyElement import spock.lang.Specification /** @@ -32,7 +32,7 @@ class InclusionServiceSpec extends Specification { def DiffNode node def "setup"() { - accessor.pathElement >> new NamedPropertyElement("foo") + accessor.pathElement >> new BeanPropertyElement("foo") rootNode = new DiffNode(RootAccessor.instance, null) node = new DiffNode(rootNode, accessor, null) categoryResolver.resolveCategories(_ as DiffNode) >> [] @@ -152,7 +152,7 @@ class InclusionServiceSpec extends Specification { given: def accessor = Mock(PropertyAwareAccessor) def node = new DiffNode(accessor, null) - accessor.pathElement >> new NamedPropertyElement("foo") + accessor.pathElement >> new BeanPropertyElement("foo") and: inclusionService.toExclude().propertyNames("foo") @@ -165,7 +165,7 @@ class InclusionServiceSpec extends Specification { given: def accessor = Mock(PropertyAwareAccessor) def node = new DiffNode(accessor, null) - accessor.pathElement >> new NamedPropertyElement("foo") + accessor.pathElement >> new BeanPropertyElement("foo") and: inclusionService.toInclude().propertyNames("foo") diff --git a/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java b/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java index 47da7fe7..88e87f0c 100644 --- a/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java +++ b/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java @@ -21,17 +21,19 @@ import de.danielbechler.diff.map.*; import org.testng.annotations.*; -import static org.fest.assertions.api.Assertions.*; +import static org.fest.assertions.api.Assertions.assertThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class NodePathBuilderTest { @Test public void testWithRoot() { final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .build(); + .withRoot() + .build(); assertThat(nodePath.getElements()).containsOnly(RootElement.getInstance()); } @@ -40,9 +42,9 @@ public void testWithElement() { final CollectionElement element = new CollectionElement("foo"); final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withElement(element) - .build(); + .withRoot() + .withElement(element) + .build(); assertThat(nodePath.getElements()).containsSequence( RootElement.getInstance(), element); @@ -52,13 +54,13 @@ public void testWithElement() public void testWithPropertyName() { final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withPropertyName("foo", "bar") - .build(); + .withRoot() + .withPropertyName("foo", "bar") + .build(); assertThat(nodePath.getElements()).containsSequence( RootElement.getInstance(), - new NamedPropertyElement("foo"), - new NamedPropertyElement("bar") + new BeanPropertyElement("foo"), + new BeanPropertyElement("bar") ); } @@ -66,9 +68,9 @@ public void testWithPropertyName() public void testWithMapKey() { final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withMapKey("foo") - .build(); + .withRoot() + .withMapKey("foo") + .build(); assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new MapElement("foo")); } @@ -82,9 +84,9 @@ public void testWithMapKey_throws_exception_when_key_is_null() public void testWithCollectionItem() { final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withCollectionItem("foo") - .build(); + .withRoot() + .withCollectionItem("foo") + .build(); assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new CollectionElement("foo")); } @@ -92,10 +94,10 @@ public void testWithCollectionItem() public void testWithPropertyPath() { final NodePath nodePath = NodePath.createBuilder() - .withPropertyPath(NodePath - .buildWith("foo")) - .build(); - assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new NamedPropertyElement("foo")); + .withPropertyPath(NodePath + .buildWith("foo")) + .build(); + assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new BeanPropertyElement("foo")); } @Test(expectedExceptions = IllegalArgumentException.class) diff --git a/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java b/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java index 8fb04fad..b1e7a300 100644 --- a/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java +++ b/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java @@ -18,22 +18,29 @@ import de.danielbechler.diff.*; import org.fest.assertions.api.*; -import org.mockito.Mock; +import org.mockito.*; import org.mockito.invocation.*; import org.mockito.stubbing.*; import org.testng.annotations.*; import java.util.*; -import static de.danielbechler.diff.NodePath.*; +import static de.danielbechler.diff.NodePath.buildRootPath; import static de.danielbechler.diff.helper.NodeAssertions.assertThat; -import static de.danielbechler.diff.helper.NodeMatchers.*; +import static de.danielbechler.diff.helper.NodeMatchers.node; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.*; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.*; - -/** @author Daniel Bechler */ +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.same; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +/** + * @author Daniel Bechler + */ public class BeanDifferShould { private BeanDiffer beanDiffer; @@ -77,21 +84,21 @@ public Object answer(final InvocationOnMock invocation) throws Throwable @DataProvider public static Object[][] acceptableTypes() { - return new Object[][] { - new Class[] {String.class}, - new Class[] {Object.class}, - new Class[] {Number.class}, - new Class[] {Date.class}, + return new Object[][]{ + new Class[]{String.class}, + new Class[]{Object.class}, + new Class[]{Number.class}, + new Class[]{Date.class}, }; } @DataProvider public static Object[][] rejectableTypes() { - return new Object[][] { - new Class[] {int.class}, - new Class[] {int[].class}, - new Class[] {boolean.class} + return new Object[][]{ + new Class[]{int.class}, + new Class[]{int[].class}, + new Class[]{boolean.class} }; } @@ -248,7 +255,7 @@ private void given_root_node_is_introspectable() private PropertyAwareAccessor given_introspector_returns_PropertyAccessor(final String propertyName) { - final NamedPropertyElement propertyElement = new NamedPropertyElement(propertyName); + final BeanPropertyElement propertyElement = new BeanPropertyElement(propertyName); final PropertyAwareAccessor propertyAccessor = mock(PropertyAwareAccessor.class); when(propertyAccessor.getPathElement()).thenReturn(propertyElement); final Set propertyAccessors = Collections.singleton(propertyAccessor); diff --git a/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java b/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java index 283fc9aa..dd24b08b 100644 --- a/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java +++ b/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java @@ -23,10 +23,12 @@ import java.lang.annotation.*; import java.lang.reflect.*; -import static java.util.Arrays.*; -import static org.fest.assertions.api.Assertions.*; +import static java.util.Arrays.asList; +import static org.fest.assertions.api.Assertions.assertThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class BeanPropertyAccessorShould { private BeanPropertyAccessor accessor; @@ -37,9 +39,9 @@ public class BeanPropertyAccessorShould public void setUp() throws Exception { accessor = BeanPropertyAccessorBuilder.forPropertyOf(ObjectWithHashCodeAndEquals.class) - .property("value", String.class) - .readOnly(false) - .build(); + .property("value", String.class) + .readOnly(false) + .build(); // readMethod = ObjectWithHashCodeAndEquals.class.getMethod("getValue"); // final Method writeMethod = ObjectWithHashCodeAndEquals.class.getMethod("setValue", String.class); // accessor = new PropertyAccessor("value", readMethod, writeMethod); @@ -70,9 +72,9 @@ public void fail_on_set_if_no_setter_exists() throws NoSuchMethodException public void assign_nothing_if_no_write_method_is_available() throws Exception { accessor = BeanPropertyAccessorBuilder.forPropertyOf(ObjectWithHashCodeAndEquals.class) - .property("value", String.class) - .readOnly(true) - .build(); + .property("value", String.class) + .readOnly(true) + .build(); accessor.set(item, "bar"); @@ -133,7 +135,7 @@ public void return_property_value_type() throws Exception @Test public void return_proper_path_element() throws Exception { - assertThat(accessor.getPathElement()).isEqualTo(new NamedPropertyElement("value")); + assertThat(accessor.getPathElement()).isEqualTo(new BeanPropertyElement("value")); } @Test @@ -152,11 +154,11 @@ public void includes_accessor_type_in_string_representation() public void returns_annotations_of_property_getter() throws Exception { accessor = BeanPropertyAccessorBuilder.forPropertyOf(ObjectWithAnnotatedProperty.class) - .property("value", String.class) - .readOnly(false) - .build(); + .property("value", String.class) + .readOnly(false) + .build(); final Annotation[] expectedAnnotations = ObjectWithAnnotatedProperty.class.getMethod("getValue") - .getAnnotations(); + .getAnnotations(); assertThat(expectedAnnotations).hasSize(2); assertThat(accessor.getReadMethodAnnotations()).containsAll(asList(expectedAnnotations)); } @@ -165,11 +167,11 @@ public void returns_annotations_of_property_getter() throws Exception public void returns_specific_annotation_of_property_getter() throws Exception { accessor = BeanPropertyAccessorBuilder.forPropertyOf(ObjectWithAnnotatedProperty.class) - .property("value", String.class) - .readOnly(false) - .build(); + .property("value", String.class) + .readOnly(false) + .build(); final ObjectDiffProperty expectedAnnotation = ObjectWithAnnotatedProperty.class.getMethod("getValue") - .getAnnotation(ObjectDiffProperty.class); + .getAnnotation(ObjectDiffProperty.class); assertThat(expectedAnnotation).isNotNull(); final ObjectDiffProperty annotation = accessor.getReadMethodAnnotation(ObjectDiffProperty.class); assertThat(annotation).isEqualTo(expectedAnnotation); @@ -179,9 +181,9 @@ public void returns_specific_annotation_of_property_getter() throws Exception public void returns_null_if_specific_annotation_of_property_getter_does_not_exist() throws Exception { accessor = BeanPropertyAccessorBuilder.forPropertyOf(ObjectWithAnnotatedProperty.class) - .property("value", String.class) - .readOnly(false) - .build(); + .property("value", String.class) + .readOnly(false) + .build(); assertThat(accessor.getReadMethodAnnotation(Override.class)).isNull(); } } diff --git a/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java b/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java index ed066e7c..f680f658 100644 --- a/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java +++ b/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java @@ -80,30 +80,30 @@ public void testIntrospectWithPropertyAnnotations() final Iterable accessors = introspector.introspect(ObjectWithPropertyAnnotations.class); for (final PropertyAwareAccessor accessor : accessors) { - if (accessor.getPathElement().equals(new NamedPropertyElement("ignored"))) + if (accessor.getPathElement().equals(new BeanPropertyElement("ignored"))) { assertThat(accessor.isExcluded(), is(true)); } - else if (accessor.getPathElement().equals(new NamedPropertyElement("equalsOnly"))) + else if (accessor.getPathElement().equals(new BeanPropertyElement("equalsOnly"))) { assertThat(accessor.getComparisonStrategy(), instanceOf(EqualsOnlyComparisonStrategy.class)); } - else if (accessor.getPathElement().equals(new NamedPropertyElement("categorized"))) + else if (accessor.getPathElement().equals(new BeanPropertyElement("categorized"))) { assertThat(accessor.getCategories().size(), is(1)); assertThat(accessor.getCategories(), IsEqual.equalTo(Collections.setOf("foo"))); } - else if (accessor.getPathElement().equals(new NamedPropertyElement("item"))) + else if (accessor.getPathElement().equals(new BeanPropertyElement("item"))) { assertThat(accessor.getComparisonStrategy(), IsNull.nullValue()); assertThat(accessor.isExcluded(), is(false)); assertThat(accessor.getCategories().isEmpty(), is(true)); } - else if (accessor.getPathElement().equals(new NamedPropertyElement("key"))) + else if (accessor.getPathElement().equals(new BeanPropertyElement("key"))) { // no op } - else if (accessor.getPathElement().equals(new NamedPropertyElement("value"))) + else if (accessor.getPathElement().equals(new BeanPropertyElement("value"))) { // no op } @@ -119,7 +119,7 @@ public void testIntrospectWithInheritedPropertyAnnotations() { final Iterable accessors = introspector.introspect(ObjectWithInheritedPropertyAnnotation.class); final PropertyAwareAccessor accessor = accessors.iterator().next(); - assertThat((NamedPropertyElement) accessor.getPathElement(), IsEqual.equalTo(new NamedPropertyElement("value"))); + assertThat((BeanPropertyElement) accessor.getPathElement(), IsEqual.equalTo(new BeanPropertyElement("value"))); assertThat(accessor.isExcluded(), is(true)); } diff --git a/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java b/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java index 30c934fe..1c4031e1 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java @@ -21,10 +21,13 @@ import de.danielbechler.diff.mock.*; import org.testng.annotations.*; -import static de.danielbechler.diff.helper.NodeAssertions.*; -import static org.mockito.Mockito.*; +import static de.danielbechler.diff.helper.NodeAssertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class NodeAssertionsTest { @Test @@ -73,7 +76,7 @@ public void testAssertThat_node_has_children_fails_when_no_children_are_present( public void testAssertThat_child_at_property_names_does_exist_succeeds_when_child_exists() { final Accessor accessor = mock(Accessor.class); - when(accessor.getPathElement()).thenReturn(new NamedPropertyElement("value")); + when(accessor.getPathElement()).thenReturn(new BeanPropertyElement("value")); final DiffNode node = new DiffNode(ObjectWithString.class); final DiffNode child = new DiffNode(node, accessor, String.class); node.addChild(child); diff --git a/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java b/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java index 9b6b249a..ee529893 100644 --- a/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java +++ b/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java @@ -18,23 +18,27 @@ import de.danielbechler.diff.*; import de.danielbechler.diff.bean.*; -import org.mockito.Mock; +import org.mockito.*; import org.mockito.invocation.*; import org.mockito.stubbing.*; import org.testng.annotations.*; -import java.util.ArrayList; import java.util.*; -import static de.danielbechler.diff.PrimitiveDefaultValueMode.*; -import static de.danielbechler.diff.helper.MockitoAnswers.*; -import static de.danielbechler.diff.helper.MockitoExtensions.*; -import static de.danielbechler.diff.helper.NodeAssertions.*; -import static java.util.Arrays.*; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.*; - -/** @author Daniel Bechler */ +import static de.danielbechler.diff.PrimitiveDefaultValueMode.ASSIGNED; +import static de.danielbechler.diff.PrimitiveDefaultValueMode.UNASSIGNED; +import static de.danielbechler.diff.helper.MockitoAnswers.withType; +import static de.danielbechler.diff.helper.MockitoExtensions.returnClass; +import static de.danielbechler.diff.helper.NodeAssertions.assertThat; +import static java.util.Arrays.asList; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +/** + * @author Daniel Bechler + */ public class PrimitiveDifferTest { private PrimitiveDiffer primitiveDiffer; @@ -142,7 +146,7 @@ private Instances given_instances(final Class type, final Object fresh) { when(accessor.getType()).then(returnClass(type)); - when(accessor.getPathElement()).thenReturn(new NamedPropertyElement("ignored")); + when(accessor.getPathElement()).thenReturn(new BeanPropertyElement("ignored")); instances = mock(Instances.class); when(instances.getWorking()).thenReturn(working); when(instances.getBase()).thenReturn(base); @@ -154,13 +158,13 @@ private Instances given_instances(final Class type, private static Object[] instances(final Class type, final T base, final T working, final T fresh) { - return new Object[] {type, base, working, fresh}; + return new Object[]{type, base, working, fresh}; } @DataProvider public Object[][] additions() { - return new Object[][] { + return new Object[][]{ instances(int.class, 0, 1, 0), instances(long.class, 0L, 1L, 0L), instances(float.class, 0F, 1F, 0F), @@ -172,7 +176,7 @@ public Object[][] additions() @DataProvider public Object[][] removals() { - return new Object[][] { + return new Object[][]{ instances(int.class, 1, 0, 0), instances(long.class, 1L, 0L, 0L), instances(float.class, 1F, 0F, 0F), @@ -184,7 +188,7 @@ public Object[][] removals() @DataProvider public Object[][] changes() { - return new Object[][] { + return new Object[][]{ instances(int.class, 1, 2, 0), instances(long.class, 1L, 2L, 0L), instances(float.class, 1F, 2F, 0F), @@ -195,14 +199,14 @@ public Object[][] changes() @DataProvider public Object[][] wrapperTypes() { - return new Object[][] { - new Object[] {Integer.class}, - new Object[] {Long.class}, - new Object[] {Float.class}, - new Object[] {Double.class}, - new Object[] {Short.class}, - new Object[] {Byte.class}, - new Object[] {Boolean.class}, + return new Object[][]{ + new Object[]{Integer.class}, + new Object[]{Long.class}, + new Object[]{Float.class}, + new Object[]{Double.class}, + new Object[]{Short.class}, + new Object[]{Byte.class}, + new Object[]{Boolean.class}, }; } From 85a398f3c20cbe58c49abf46e758ed397dbcd024 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 1 Mar 2014 23:20:05 +0100 Subject: [PATCH 005/215] Renamed CollectionElement to CollectionItemElement and MapElement to MapKeyElement --- .../diff/ObjectDifferITCase.java | 87 ++++++++----- .../example/MapEntryValueAccessExample.java | 16 ++- .../example/phonebook/PhoneBookITCase.java | 23 ++-- .../diff/issues/issue66/MainApp.java | 6 +- .../diff/CircularReferenceMatchingMode.java | 8 +- .../java/de/danielbechler/diff/Inclusion.java | 4 +- .../java/de/danielbechler/diff/NodePath.java | 18 +-- .../diff/NodePathValueHolder.java | 22 ++-- .../diff/ObjectDifferBuilder.java | 114 ++++++++++-------- .../diff/ReturnableNodeConfiguration.java | 13 +- .../diff/ReturnableNodeService.java | 16 ++- .../StopVisitationException.java | 8 +- .../diff/{visitor => }/Visit.java | 6 +- .../collection/CollectionItemAccessor.java | 12 +- ...lement.java => CollectionItemElement.java} | 18 +-- .../diff/map/MapEntryAccessor.java | 2 +- .../{MapElement.java => MapKeyElement.java} | 18 +-- .../visitor/AbstractFilteringVisitor.java | 12 +- .../visitor/CategoryFilteringVisitor.java | 10 +- .../diff/visitor/NodeHierarchyVisitor.java | 29 ++--- .../diff/visitor/PrintingVisitor.java | 11 +- .../diff/visitor/PropertyVisitor.java | 10 +- .../diff/visitor/StateFilteringVisitor.java | 9 +- .../diff/visitor/ToMapPrintingVisitor.java | 6 +- .../de/danielbechler/merge/ObjectMerger.java | 6 +- .../de/danielbechler/diff/ListDiffSpec.groovy | 51 ++++---- .../diff/NodePathBuilderTest.java | 14 +-- .../collection/CollectionElementTest.groovy | 71 ----------- .../CollectionItemAccessorShould.java | 21 ++-- .../CollectionItemElementTest.groovy | 63 ++++++++++ .../diff/helper/NodeAssertions.java | 20 +-- .../diff/helper/NodeMatchers.java | 21 ++-- .../diff/map/MapEntryAccessorTest.groovy | 2 +- ...ntTest.groovy => MapKeyElementTest.groovy} | 24 ++-- 34 files changed, 447 insertions(+), 324 deletions(-) rename src/main/java/de/danielbechler/diff/{visitor => }/StopVisitationException.java (84%) rename src/main/java/de/danielbechler/diff/{visitor => }/Visit.java (93%) rename src/main/java/de/danielbechler/diff/collection/{CollectionElement.java => CollectionItemElement.java} (76%) rename src/main/java/de/danielbechler/diff/map/{MapElement.java => MapKeyElement.java} (78%) delete mode 100644 src/test/java/de/danielbechler/diff/collection/CollectionElementTest.groovy create mode 100644 src/test/java/de/danielbechler/diff/collection/CollectionItemElementTest.groovy rename src/test/java/de/danielbechler/diff/map/{MapElementTest.groovy => MapKeyElementTest.groovy} (77%) diff --git a/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java b/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java index 27e58431..44b1c2fc 100644 --- a/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java @@ -16,22 +16,43 @@ package de.danielbechler.diff; -import de.danielbechler.diff.collection.*; -import de.danielbechler.diff.helper.*; -import de.danielbechler.diff.map.*; -import de.danielbechler.diff.mock.*; -import de.danielbechler.diff.visitor.*; -import org.fest.assertions.api.*; -import org.testng.annotations.*; +import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.helper.NodeAssertions; +import de.danielbechler.diff.helper.TestGroups; +import de.danielbechler.diff.map.MapKeyElement; +import de.danielbechler.diff.mock.ObjectWithAccessTrackingIgnoredProperty; +import de.danielbechler.diff.mock.ObjectWithCollection; +import de.danielbechler.diff.mock.ObjectWithHashCodeAndEquals; +import de.danielbechler.diff.mock.ObjectWithIdentityAndValue; +import de.danielbechler.diff.mock.ObjectWithIgnoredMap; +import de.danielbechler.diff.mock.ObjectWithMethodEqualsOnlyValueProviderMethodOnGetCollection; +import de.danielbechler.diff.mock.ObjectWithMethodEqualsOnlyValueProviderMethodOnGetMap; +import de.danielbechler.diff.mock.ObjectWithMethodEqualsOnlyValueProviderMethodOnGetNestedObject; +import de.danielbechler.diff.mock.ObjectWithNestedObject; +import de.danielbechler.diff.visitor.NodeHierarchyVisitor; +import org.fest.assertions.api.Assertions; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; import java.util.ArrayList; -import java.util.*; - -import static de.danielbechler.diff.helper.NodeAssertions.*; -import static de.danielbechler.diff.helper.TestGroups.*; -import static java.util.Arrays.*; - -/** @author Daniel Bechler */ +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.TreeSet; + +import static de.danielbechler.diff.helper.NodeAssertions.assertThat; +import static de.danielbechler.diff.helper.TestGroups.INTEGRATION; +import static java.util.Arrays.asList; + +/** + * @author Daniel Bechler + */ @Test(groups = TestGroups.INTEGRATION) public class ObjectDifferITCase { @@ -60,8 +81,8 @@ public void testCompareCollectionWithIgnoredCollectionProperty() working.getCollection().add("foo"); final ObjectDifferBuilder objectDifferBuilder = ObjectDifferBuilder.startBuilding(); objectDifferBuilder.configure().inclusion() - .toExclude() - .nodes(NodePath.buildWith("collection")); + .toExclude() + .nodes(NodePath.buildWith("collection")); objectDiffer = objectDifferBuilder.build(); final DiffNode node = objectDiffer.compare(working, new ObjectWithCollection()); @@ -78,7 +99,7 @@ public void testCompareCollectionWithAddedItem() throws Exception final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); - NodeAssertions.assertThat(node).child(new CollectionElement("foo")).hasState(DiffNode.State.ADDED); + NodeAssertions.assertThat(node).child(new CollectionItemElement("foo")).hasState(DiffNode.State.ADDED); } public void testCompareCollectionWithRemovedItem() throws Exception @@ -89,7 +110,7 @@ public void testCompareCollectionWithRemovedItem() throws Exception final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); - NodeAssertions.assertThat(node).child(new CollectionElement("foo")).hasState(DiffNode.State.REMOVED); + NodeAssertions.assertThat(node).child(new CollectionItemElement("foo")).hasState(DiffNode.State.REMOVED); } @Test(groups = TestGroups.INTEGRATION) @@ -140,7 +161,7 @@ public void testCompareCollectionWithChangedItem() throws Exception final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); assertThat(node).self().hasChanges(); - assertThat(node).child(new CollectionElement(new ObjectWithIdentityAndValue("foo"))) + assertThat(node).child(new CollectionItemElement(new ObjectWithIdentityAndValue("foo"))) .hasState(DiffNode.State.CHANGED); } @@ -154,7 +175,7 @@ public void testWithNewMapInWorkingAndNoneInBase() final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.ADDED); - NodeAssertions.assertThat(node).child(new MapElement("foo")).hasState(DiffNode.State.ADDED); + NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.ADDED); } @Test(groups = TestGroups.INTEGRATION) @@ -167,7 +188,7 @@ public void testWithAdditionOfSimpleTypeToWorkingMap() final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); - NodeAssertions.assertThat(node).child(new MapElement("foo")).hasState(DiffNode.State.ADDED); + NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.ADDED); } @Test(groups = TestGroups.INTEGRATION) @@ -194,7 +215,7 @@ public void testWithSingleEntryAddedToWorkingMap() final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.REMOVED); - NodeAssertions.assertThat(node).child(new MapElement("foo")).hasState(DiffNode.State.REMOVED); + NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.REMOVED); } /** @@ -227,11 +248,11 @@ public void testWithSameEntries() NodeAssertions.assertThat(node).root().hasChildren(1); NodeAssertions.assertThat(node) - .child(NodePath.createBuilder().withRoot().withMapKey("foo")) - .doesNotExist(); + .child(NodePath.createBuilder().withRoot().withMapKey("foo")) + .doesNotExist(); NodeAssertions.assertThat(node).child(NodePath.createBuilder().withRoot().withMapKey("ping")) - .hasState(DiffNode.State.ADDED) - .hasNoChildren(); + .hasState(DiffNode.State.ADDED) + .hasNoChildren(); } @Test(groups = TestGroups.INTEGRATION) @@ -245,7 +266,7 @@ public void testWithChangedEntry() final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); NodeAssertions.assertThat(node).self().hasChildren(1); - NodeAssertions.assertThat(node).child(new MapElement("foo")).hasState(DiffNode.State.CHANGED); + NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.CHANGED); } @Test(groups = TestGroups.INTEGRATION) @@ -310,9 +331,9 @@ public void testCompareWithListContainingObjectTwiceDetectsIfOneGetsRemoved() th node.visit(new NodeHierarchyVisitor()); assertThat(node) .child(NodePath.createBuilder() - .withRoot() - .withCollectionItem(new ObjectWithHashCodeAndEquals("foo")) - .build()) + .withRoot() + .withCollectionItem(new ObjectWithHashCodeAndEquals("foo")) + .build()) .hasState(DiffNode.State.REMOVED); } @@ -324,9 +345,9 @@ public void testCompareWithListContainingObjectOnceDetectsIfAnotherInstanceOfItG final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); node.visit(new NodeHierarchyVisitor()); assertThat(node).child(NodePath.createBuilder() - .withRoot() - .withCollectionItem(new ObjectWithHashCodeAndEquals("foo")) - .build()).hasState(DiffNode.State.ADDED); + .withRoot() + .withCollectionItem(new ObjectWithHashCodeAndEquals("foo")) + .build()).hasState(DiffNode.State.ADDED); } public void testCompareBeanWithEqualsOnlyValueProviderMethodOnGetCollectionPropertyNoChangeInMethodResult() diff --git a/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java b/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java index 2f725d34..497c698a 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java @@ -16,13 +16,17 @@ package de.danielbechler.diff.example; -import de.danielbechler.diff.*; -import de.danielbechler.diff.map.*; -import de.danielbechler.diff.visitor.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.Visit; +import de.danielbechler.diff.map.MapKeyElement; -import java.util.*; +import java.util.Map; +import java.util.TreeMap; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ class MapEntryValueAccessExample { private MapEntryValueAccessExample() @@ -45,7 +49,7 @@ public static void main(final String[] args) { public void accept(final DiffNode node, final Visit visit) { - final Object key = ((MapElement) node.getPathElement()).getKey(); + final Object key = ((MapKeyElement) node.getPathElement()).getKey(); // ^^^ I do not encourage this, but currently it's the only way final Object value = node.canonicalGet(working); System.out.println(key + " => " + value); diff --git a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java index afb24bab..6381a585 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java @@ -16,15 +16,20 @@ package de.danielbechler.diff.example.phonebook; -import de.danielbechler.diff.*; -import de.danielbechler.diff.collection.*; -import org.hamcrest.core.*; -import org.testng.annotations.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.collection.CollectionItemElement; +import org.hamcrest.core.IsEqual; +import org.hamcrest.core.IsNull; +import org.testng.annotations.Test; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.core.Is.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class PhoneBookITCase { @Test @@ -56,7 +61,7 @@ public void testContactMiddleNameChanges() assertThat(contactsNode, IsNull.notNullValue()); assertThat(contactsNode.hasChanges(), is(true)); - final DiffNode pinkmanNode = contactsNode.getChild(new CollectionElement(jessePinkman)); + final DiffNode pinkmanNode = contactsNode.getChild(new CollectionItemElement(jessePinkman)); assertThat(pinkmanNode.hasChanges(), is(true)); final DiffNode middleNameNode = pinkmanNode.getChild("middleName"); @@ -64,7 +69,7 @@ public void testContactMiddleNameChanges() assertThat(middleNameNode.canonicalGet(phoneBook), IsNull.nullValue()); assertThat((String) middleNameNode.canonicalGet(modifiedPhoneBook), IsEqual.equalTo("Bruce")); - final DiffNode whiteNode = contactsNode.getChild(new CollectionElement(walterWhite)); + final DiffNode whiteNode = contactsNode.getChild(new CollectionItemElement(walterWhite)); assertThat(whiteNode.hasChanges(), is(true)); final DiffNode whiteMiddleNameNode = whiteNode.getChild("middleName"); diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java b/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java index 97591cfb..b11dbcb5 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java @@ -16,8 +16,10 @@ package de.danielbechler.diff.issues.issue66; -import de.danielbechler.diff.*; -import de.danielbechler.diff.visitor.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.Visit; public class MainApp { diff --git a/src/main/java/de/danielbechler/diff/CircularReferenceMatchingMode.java b/src/main/java/de/danielbechler/diff/CircularReferenceMatchingMode.java index e111c515..0fa3e2d2 100644 --- a/src/main/java/de/danielbechler/diff/CircularReferenceMatchingMode.java +++ b/src/main/java/de/danielbechler/diff/CircularReferenceMatchingMode.java @@ -27,9 +27,13 @@ */ public enum CircularReferenceMatchingMode { - /** Compares objects using the == operator. */ + /** + * Compares objects using the == operator. + */ EQUALITY_OPERATOR, - /** Compares objects using {@linkplain Object#equals(Object)}. */ + /** + * Compares objects using {@linkplain Object#equals(Object)}. + */ EQUALS_METHOD } diff --git a/src/main/java/de/danielbechler/diff/Inclusion.java b/src/main/java/de/danielbechler/diff/Inclusion.java index 8ecf9a13..61a64c02 100644 --- a/src/main/java/de/danielbechler/diff/Inclusion.java +++ b/src/main/java/de/danielbechler/diff/Inclusion.java @@ -16,7 +16,9 @@ package de.danielbechler.diff; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public enum Inclusion { INCLUDED, diff --git a/src/main/java/de/danielbechler/diff/NodePath.java b/src/main/java/de/danielbechler/diff/NodePath.java index b231f59d..962db971 100644 --- a/src/main/java/de/danielbechler/diff/NodePath.java +++ b/src/main/java/de/danielbechler/diff/NodePath.java @@ -16,13 +16,15 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.*; -import de.danielbechler.diff.collection.*; -import de.danielbechler.diff.map.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.map.MapKeyElement; +import de.danielbechler.util.Assert; -import java.util.*; +import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; +import java.util.List; /** * @author Daniel Bechler @@ -75,7 +77,7 @@ public String toString() { sb.append("/"); } - else if (element instanceof CollectionElement || element instanceof MapElement) + else if (element instanceof CollectionItemElement || element instanceof MapKeyElement) { sb.append(element); } @@ -209,14 +211,14 @@ public AppendableBuilder withPropertyName(final String name, final String... nam public AppendableBuilder withCollectionItem(final T item) { - elements.add(new CollectionElement(item)); + elements.add(new CollectionItemElement(item)); return this; } public AppendableBuilder withMapKey(final K key) { Assert.notNull(key, "key"); - elements.add(new MapElement(key)); + elements.add(new MapKeyElement(key)); return this; } diff --git a/src/main/java/de/danielbechler/diff/NodePathValueHolder.java b/src/main/java/de/danielbechler/diff/NodePathValueHolder.java index 5c4f24b5..0da8d5af 100644 --- a/src/main/java/de/danielbechler/diff/NodePathValueHolder.java +++ b/src/main/java/de/danielbechler/diff/NodePathValueHolder.java @@ -1,22 +1,28 @@ package de.danielbechler.diff; -import de.danielbechler.util.*; +import de.danielbechler.util.Assert; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; -/** @author Daniel Bechler */ -public class NodePathValueHolder +/** + * @author Daniel Bechler + */ +class NodePathValueHolder { + private final Map> elementValueHolders = new HashMap>(); + private T value; + public static NodePathValueHolder of(final Class type) { Assert.notNull(type, "type"); return new NodePathValueHolder(); } - private final Map> elementValueHolders = new HashMap>(); - - private T value; - public NodePathValueHolder put(final NodePath nodePath, final T value) { put(nodePath.getElements(), value); diff --git a/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java b/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java index 4f5a85f9..1382201f 100644 --- a/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java +++ b/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java @@ -24,66 +24,84 @@ /** * @author Daniel Bechler */ -public final class ObjectDifferBuilder { - private final CategoryService categoryService = new CategoryService(); - private final ComparisonService comparisonService = new ComparisonService(); - private final ReturnableNodeService returnableNodeService = new ReturnableNodeService(); - private final IntrospectionService introspectionService = new IntrospectionService(); - private final InclusionService inclusionService = new InclusionService(categoryService); - private final CircularReferenceService circularReferenceService = new CircularReferenceService(); - private final Configurable configurable = new Configurable(); +public final class ObjectDifferBuilder +{ + private final CategoryService categoryService = new CategoryService(); + private final InclusionService inclusionService = new InclusionService(categoryService); + private final ComparisonService comparisonService = new ComparisonService(); + private final ReturnableNodeService returnableNodeService = new ReturnableNodeService(); + private final IntrospectionService introspectionService = new IntrospectionService(); + private final CircularReferenceService circularReferenceService = new CircularReferenceService(); + private final Configurable configurable = new Configurable(); - private ObjectDifferBuilder() { - } + private ObjectDifferBuilder() + { + } - public static ObjectDiffer buildDefaultObjectDiffer() { - return startBuilding().build(); - } + public static ObjectDiffer buildDefaultObjectDiffer() + { + return startBuilding().build(); + } - public static ObjectDifferBuilder startBuilding() { - return new ObjectDifferBuilder(); - } + public static ObjectDifferBuilder startBuilding() + { + return new ObjectDifferBuilder(); + } - public ObjectDiffer build() { - final DifferProvider differProvider = new DifferProvider(); - final DifferDispatcher differDispatcher = new DifferDispatcher(differProvider, circularReferenceService, circularReferenceService, inclusionService, returnableNodeService); - differProvider.push(new BeanDiffer(differDispatcher, introspectionService, returnableNodeService, comparisonService, introspectionService)); - differProvider.push(new CollectionDiffer(differDispatcher, comparisonService)); + public ObjectDiffer build() + { + final DifferProvider differProvider = new DifferProvider(); + final DifferDispatcher differDispatcher = new DifferDispatcher(differProvider, circularReferenceService, circularReferenceService, inclusionService, returnableNodeService); + differProvider.push(new BeanDiffer(differDispatcher, introspectionService, returnableNodeService, comparisonService, introspectionService)); + differProvider.push(new CollectionDiffer(differDispatcher, comparisonService)); differProvider.push(new MapDiffer(differDispatcher, comparisonService)); differProvider.push(new PrimitiveDiffer(comparisonService)); - return new ObjectDiffer(differDispatcher); - } + return new ObjectDiffer(differDispatcher); + } - public final Configurable configure() { - return configurable; - } + public final Configurable configure() + { + return configurable; + } - public class Configurable { - private Configurable() { - } + public class Configurable + { + private Configurable() + { + } - public ReturnableNodeConfiguration filtering() { - return returnableNodeService; - } + /** + * Allows to exclude nodes from being added to the object graph based on criteria that are only known after + * the diff for the affected node and all its children has been determined. + */ + public ReturnableNodeConfiguration filtering() + { + return returnableNodeService; + } - public IntrospectionConfiguration introspection() { - return introspectionService; - } + public IntrospectionConfiguration introspection() + { + return introspectionService; + } - public CircularReferenceConfiguration circularReferenceHandling() { - return circularReferenceService; - } + public CircularReferenceConfiguration circularReferenceHandling() + { + return circularReferenceService; + } - public InclusionConfiguration inclusion() { - return inclusionService; - } + public InclusionConfiguration inclusion() + { + return inclusionService; + } - public ComparisonConfiguration comparison() { - return comparisonService; - } + public ComparisonConfiguration comparison() + { + return comparisonService; + } - public CategoryConfiguration categories() { - return categoryService; - } - } + public CategoryConfiguration categories() + { + return categoryService; + } + } } diff --git a/src/main/java/de/danielbechler/diff/ReturnableNodeConfiguration.java b/src/main/java/de/danielbechler/diff/ReturnableNodeConfiguration.java index 4a493a18..1da66dae 100644 --- a/src/main/java/de/danielbechler/diff/ReturnableNodeConfiguration.java +++ b/src/main/java/de/danielbechler/diff/ReturnableNodeConfiguration.java @@ -1,6 +1,17 @@ package de.danielbechler.diff; -/** @author Daniel Bechler */ +/** + * Allows to exclude nodes from being added to the object graph based on criteria that are only known after the diff + * for + * the affected node and all its children has been determined. + *

+ * Currently it is only possible to configure returnability based on the state (_added_, _changed_, _untouched_, etc.) + * of the `DiffNode`. But this is just the beginning. Nothing speaks against adding more powerful options. It would be + * nice for example to be able to pass some kind of matcher to determine returnability based on dynamic criteria at + * runtime. + * + * @author Daniel Bechler + */ public interface ReturnableNodeConfiguration { ReturnableNodeConfiguration returnNodesWithState(DiffNode.State state, boolean enabled); diff --git a/src/main/java/de/danielbechler/diff/ReturnableNodeService.java b/src/main/java/de/danielbechler/diff/ReturnableNodeService.java index 6047ef33..4127b2de 100644 --- a/src/main/java/de/danielbechler/diff/ReturnableNodeService.java +++ b/src/main/java/de/danielbechler/diff/ReturnableNodeService.java @@ -16,13 +16,19 @@ package de.danielbechler.diff; -import java.util.*; +import java.util.Collection; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static de.danielbechler.util.Collections.*; -import static java.util.Arrays.*; +import static de.danielbechler.util.Collections.filteredCopyOf; +import static java.util.Arrays.asList; -/** @author Daniel Bechler */ -public class ReturnableNodeService implements ReturnableNodeConfiguration, IsReturnableResolver +/** + * @author Daniel Bechler + */ +class ReturnableNodeService implements ReturnableNodeConfiguration, IsReturnableResolver { private final Map stateFilterSettings; diff --git a/src/main/java/de/danielbechler/diff/visitor/StopVisitationException.java b/src/main/java/de/danielbechler/diff/StopVisitationException.java similarity index 84% rename from src/main/java/de/danielbechler/diff/visitor/StopVisitationException.java rename to src/main/java/de/danielbechler/diff/StopVisitationException.java index e16120b5..490bdce6 100644 --- a/src/main/java/de/danielbechler/diff/visitor/StopVisitationException.java +++ b/src/main/java/de/danielbechler/diff/StopVisitationException.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package de.danielbechler.diff.visitor; +package de.danielbechler.diff; -/** @author Daniel Bechler */ -public final class StopVisitationException extends RuntimeException +/** + * @author Daniel Bechler + */ +final class StopVisitationException extends RuntimeException { private static final long serialVersionUID = 1L; diff --git a/src/main/java/de/danielbechler/diff/visitor/Visit.java b/src/main/java/de/danielbechler/diff/Visit.java similarity index 93% rename from src/main/java/de/danielbechler/diff/visitor/Visit.java rename to src/main/java/de/danielbechler/diff/Visit.java index 136e6225..f8660d42 100644 --- a/src/main/java/de/danielbechler/diff/visitor/Visit.java +++ b/src/main/java/de/danielbechler/diff/Visit.java @@ -14,9 +14,11 @@ * limitations under the License. */ -package de.danielbechler.diff.visitor; +package de.danielbechler.diff; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public final class Visit { private enum State diff --git a/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java b/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java index b803f005..f1726ed8 100644 --- a/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java +++ b/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java @@ -16,11 +16,15 @@ package de.danielbechler.diff.collection; -import de.danielbechler.diff.*; +import de.danielbechler.diff.Accessor; +import de.danielbechler.diff.Element; +import de.danielbechler.diff.TypeAwareAccessor; -import java.util.*; +import java.util.Collection; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class CollectionItemAccessor implements TypeAwareAccessor, Accessor { private final Object referenceItem; @@ -32,7 +36,7 @@ public CollectionItemAccessor(final Object referenceItem) public Element getPathElement() { - return new CollectionElement(referenceItem); + return new CollectionItemElement(referenceItem); } public void set(final Object target, final Object value) diff --git a/src/main/java/de/danielbechler/diff/collection/CollectionElement.java b/src/main/java/de/danielbechler/diff/collection/CollectionItemElement.java similarity index 76% rename from src/main/java/de/danielbechler/diff/collection/CollectionElement.java rename to src/main/java/de/danielbechler/diff/collection/CollectionItemElement.java index adea3813..9a5c94e4 100644 --- a/src/main/java/de/danielbechler/diff/collection/CollectionElement.java +++ b/src/main/java/de/danielbechler/diff/collection/CollectionItemElement.java @@ -16,20 +16,24 @@ package de.danielbechler.diff.collection; -import de.danielbechler.diff.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.Element; +import de.danielbechler.util.Strings; -/** @author Daniel Bechler */ -public final class CollectionElement extends Element +/** + * @author Daniel Bechler + */ +public final class CollectionItemElement extends Element { private final Object item; - public CollectionElement(final Object item) + public CollectionItemElement(final Object item) { this.item = item; } - /** @deprecated Low-level API. Don't use in production code. May be removed in future versions. */ + /** + * @deprecated Low-level API. Don't use in production code. May be removed in future versions. + */ @SuppressWarnings({"UnusedDeclaration"}) @Deprecated public Object getItem() @@ -55,7 +59,7 @@ public boolean equals(final Object o) return false; } - final CollectionElement that = (CollectionElement) o; + final CollectionItemElement that = (CollectionItemElement) o; if (item != null ? !item.equals(that.item) : that.item != null) { diff --git a/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java b/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java index 329b2105..f9b2c0e1 100644 --- a/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java +++ b/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java @@ -81,7 +81,7 @@ public String toString() public Element getPathElement() { - return new MapElement(referenceKey); + return new MapKeyElement(referenceKey); } public Object get(final Object target) diff --git a/src/main/java/de/danielbechler/diff/map/MapElement.java b/src/main/java/de/danielbechler/diff/map/MapKeyElement.java similarity index 78% rename from src/main/java/de/danielbechler/diff/map/MapElement.java rename to src/main/java/de/danielbechler/diff/map/MapKeyElement.java index d24e8205..2e49f715 100644 --- a/src/main/java/de/danielbechler/diff/map/MapElement.java +++ b/src/main/java/de/danielbechler/diff/map/MapKeyElement.java @@ -16,20 +16,24 @@ package de.danielbechler.diff.map; -import de.danielbechler.diff.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.Element; +import de.danielbechler.util.Strings; -/** @author Daniel Bechler */ -public final class MapElement extends Element +/** + * @author Daniel Bechler + */ +public final class MapKeyElement extends Element { private final Object key; - public MapElement(final Object key) + public MapKeyElement(final Object key) { this.key = key; } - /** @deprecated Low-level API. Don't use in production code. May be removed in future versions. */ + /** + * @deprecated Low-level API. Don't use in production code. May be removed in future versions. + */ @SuppressWarnings({"UnusedDeclaration"}) @Deprecated public Object getKey() @@ -55,7 +59,7 @@ public boolean equals(final Object o) return false; } - final MapElement that = (MapElement) o; + final MapKeyElement that = (MapKeyElement) o; if (key != null ? !key.equals(that.key) : that.key != null) { diff --git a/src/main/java/de/danielbechler/diff/visitor/AbstractFilteringVisitor.java b/src/main/java/de/danielbechler/diff/visitor/AbstractFilteringVisitor.java index ab152b4a..7326bf03 100644 --- a/src/main/java/de/danielbechler/diff/visitor/AbstractFilteringVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/AbstractFilteringVisitor.java @@ -16,14 +16,18 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.Visit; -import java.util.*; +import java.util.Collection; +import java.util.LinkedList; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public abstract class AbstractFilteringVisitor implements DiffNode.Visitor { - private final Collection matches = new ArrayList(30); + private final Collection matches = new LinkedList(); protected abstract boolean accept(final DiffNode node); diff --git a/src/main/java/de/danielbechler/diff/visitor/CategoryFilteringVisitor.java b/src/main/java/de/danielbechler/diff/visitor/CategoryFilteringVisitor.java index c3529fac..ddb760d2 100644 --- a/src/main/java/de/danielbechler/diff/visitor/CategoryFilteringVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/CategoryFilteringVisitor.java @@ -16,12 +16,16 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.Visit; import de.danielbechler.util.Collections; -import java.util.*; +import java.util.Collection; +import java.util.TreeSet; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ @SuppressWarnings("UnusedDeclaration") public abstract class CategoryFilteringVisitor extends AbstractFilteringVisitor { diff --git a/src/main/java/de/danielbechler/diff/visitor/NodeHierarchyVisitor.java b/src/main/java/de/danielbechler/diff/visitor/NodeHierarchyVisitor.java index 28caa6d2..935e2dd9 100644 --- a/src/main/java/de/danielbechler/diff/visitor/NodeHierarchyVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/NodeHierarchyVisitor.java @@ -16,8 +16,9 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.Visit; +import de.danielbechler.util.Strings; /** * Prints the hierarchy of the object graph in a human-readable form. @@ -41,6 +42,18 @@ public NodeHierarchyVisitor(final int maxDepth) this.maxDepth = maxDepth; } + private static int calculateDepth(final DiffNode node) + { + int count = 0; + DiffNode parentNode = node.getParentNode(); + while (parentNode != null) + { + count++; + parentNode = parentNode.getParentNode(); + } + return count; + } + public void accept(final DiffNode node, final Visit visit) { if (maxDepth == 0) @@ -77,16 +90,4 @@ protected void print(final String text) { System.out.println(text); } - - private static int calculateDepth(final DiffNode node) - { - int count = 0; - DiffNode parentNode = node.getParentNode(); - while (parentNode != null) - { - count++; - parentNode = parentNode.getParentNode(); - } - return count; - } } diff --git a/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java b/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java index 69b07b6f..9a3e0be1 100644 --- a/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java @@ -16,11 +16,14 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.Visit; +import de.danielbechler.util.Strings; -/** @author Daniel Bechler */ -@SuppressWarnings({"MethodMayBeStatic"}) +/** + * @author Daniel Bechler + */ public class PrintingVisitor implements DiffNode.Visitor { private final Object working; diff --git a/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java b/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java index 502ee654..76ff1e93 100644 --- a/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java @@ -16,10 +16,14 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.Visit; +import de.danielbechler.util.Assert; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class PropertyVisitor implements DiffNode.Visitor { private final NodePath nodePath; diff --git a/src/main/java/de/danielbechler/diff/visitor/StateFilteringVisitor.java b/src/main/java/de/danielbechler/diff/visitor/StateFilteringVisitor.java index 02b2b0b3..6c7f8644 100644 --- a/src/main/java/de/danielbechler/diff/visitor/StateFilteringVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/StateFilteringVisitor.java @@ -16,10 +16,13 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.Visit; +import de.danielbechler.util.Assert; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class StateFilteringVisitor extends AbstractFilteringVisitor { private final DiffNode.State state; diff --git a/src/main/java/de/danielbechler/diff/visitor/ToMapPrintingVisitor.java b/src/main/java/de/danielbechler/diff/visitor/ToMapPrintingVisitor.java index 08ff6d72..dbe75161 100644 --- a/src/main/java/de/danielbechler/diff/visitor/ToMapPrintingVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/ToMapPrintingVisitor.java @@ -16,9 +16,11 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; -import java.util.*; +import java.util.LinkedHashMap; +import java.util.Map; /** * This visitor generates the same output as the {@link PrintingVisitor}, but instead of printing it to diff --git a/src/main/java/de/danielbechler/merge/ObjectMerger.java b/src/main/java/de/danielbechler/merge/ObjectMerger.java index 317efb8b..976c37f3 100644 --- a/src/main/java/de/danielbechler/merge/ObjectMerger.java +++ b/src/main/java/de/danielbechler/merge/ObjectMerger.java @@ -16,8 +16,10 @@ package de.danielbechler.merge; -import de.danielbechler.diff.*; -import de.danielbechler.diff.visitor.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.Visit; /** * Careful: This class has not yet been tested very thoroughly and serves more as an example for your own diff --git a/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy b/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy index 7b768ca2..09ec48b8 100644 --- a/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy +++ b/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy @@ -16,7 +16,7 @@ package de.danielbechler.diff -import de.danielbechler.diff.collection.CollectionElement +import de.danielbechler.diff.collection.CollectionItemElement import de.danielbechler.diff.visitor.PrintingVisitor import spock.lang.Ignore import spock.lang.Specification @@ -24,30 +24,27 @@ import spock.lang.Specification /** * @author Daniel Bechler */ -class ListDiffSpec extends Specification -{ - def objectDiffer; - - def setup() - { - objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); - } - - @Ignore - def "detects position switch"() - { - DiffNode node; - - when: - node = objectDiffer.compare(working, base); - - then: - node.visit(new PrintingVisitor(working, base)); - node.getChild(new CollectionElement("a")).getState() == DiffNode.State.CHANGED; - node.getChild(new CollectionElement("b")).getState() == DiffNode.State.CHANGED; - - where: - base | working - ["a", "b"] | ["b", "a"] - } +class ListDiffSpec extends Specification { + def objectDiffer; + + def setup() { + objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + } + + @Ignore + def "detects position switch"() { + DiffNode node; + + when: + node = objectDiffer.compare(working, base); + + then: + node.visit(new PrintingVisitor(working, base)); + node.getChild(new CollectionItemElement("a")).getState() == DiffNode.State.CHANGED; + node.getChild(new CollectionItemElement("b")).getState() == DiffNode.State.CHANGED; + + where: + base | working + ["a", "b"] | ["b", "a"] + } } diff --git a/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java b/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java index 88e87f0c..cb4674f3 100644 --- a/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java +++ b/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java @@ -16,10 +16,10 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.*; -import de.danielbechler.diff.collection.*; -import de.danielbechler.diff.map.*; -import org.testng.annotations.*; +import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.map.MapKeyElement; +import org.testng.annotations.Test; import static org.fest.assertions.api.Assertions.assertThat; @@ -40,7 +40,7 @@ public void testWithRoot() @Test public void testWithElement() { - final CollectionElement element = new CollectionElement("foo"); + final CollectionItemElement element = new CollectionItemElement("foo"); final NodePath nodePath = NodePath.createBuilder() .withRoot() .withElement(element) @@ -71,7 +71,7 @@ public void testWithMapKey() .withRoot() .withMapKey("foo") .build(); - assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new MapElement("foo")); + assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new MapKeyElement("foo")); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -87,7 +87,7 @@ public void testWithCollectionItem() .withRoot() .withCollectionItem("foo") .build(); - assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new CollectionElement("foo")); + assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new CollectionItemElement("foo")); } @Test diff --git a/src/test/java/de/danielbechler/diff/collection/CollectionElementTest.groovy b/src/test/java/de/danielbechler/diff/collection/CollectionElementTest.groovy deleted file mode 100644 index 507e9a04..00000000 --- a/src/test/java/de/danielbechler/diff/collection/CollectionElementTest.groovy +++ /dev/null @@ -1,71 +0,0 @@ -package de.danielbechler.diff.collection - -import de.danielbechler.diff.map.MapElement -import spock.lang.Specification - -/** - * @author Daniel Bechler - */ -class CollectionElementTest extends Specification -{ - def 'can be created with "null" item'() - { - expect: - new CollectionElement(null) != null - } - - def 'should equal other CollectionElement if items are equal'() - { - setup: - def element = new CollectionElement("foo") - def equalElement = new CollectionElement("foo") - - expect: - element.equals(equalElement) - } - - def 'should not equal elements of different class'() - { - setup: - def element = new CollectionElement("foo") - def mapElement = new MapElement("foo") - - expect: - !element.equals(mapElement) - } - - def 'should not equal null'() - { - setup: - def element = new CollectionElement("foo") - - expect: - !element.equals(null) - } - - def 'should have same hashCode as item'() - { - setup: - def item = "foo" - def element = new CollectionElement(item) - - expect: - element.hashCode() == item.hashCode() - } - - def 'should provide accessor for item'() - { - setup: - def item = "foo" - def element = new CollectionElement(item) - - expect: - element.getItem() == item - } - - def 'should have proper toString() representation'() - { - expect: "string representation of the item should be converted to single line" - new CollectionElement("foo\nbar").toString() == '[foo \\ bar]' - } -} diff --git a/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java b/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java index 9592b83d..f3f27b4c 100644 --- a/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java +++ b/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java @@ -16,16 +16,21 @@ package de.danielbechler.diff.collection; -import de.danielbechler.diff.*; -import de.danielbechler.diff.mock.*; -import org.fest.assertions.core.*; -import org.testng.annotations.*; +import de.danielbechler.diff.Element; +import de.danielbechler.diff.mock.ObjectWithIdentityAndValue; +import org.fest.assertions.core.Condition; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; -import static org.fest.assertions.api.Assertions.*; +import static org.fest.assertions.api.Assertions.assertThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class CollectionItemAccessorShould { private CollectionItemAccessor accessor; @@ -103,7 +108,7 @@ public void fail_remove_if_target_object_is_not_a_collection() throws Exception @Test public void return_proper_path_element() { - final CollectionElement expectedPathElement = new CollectionElement(referenceItem); + final CollectionItemElement expectedPathElement = new CollectionItemElement(referenceItem); final Element actualPathElement = accessor.getPathElement(); diff --git a/src/test/java/de/danielbechler/diff/collection/CollectionItemElementTest.groovy b/src/test/java/de/danielbechler/diff/collection/CollectionItemElementTest.groovy new file mode 100644 index 00000000..2d79b4e0 --- /dev/null +++ b/src/test/java/de/danielbechler/diff/collection/CollectionItemElementTest.groovy @@ -0,0 +1,63 @@ +package de.danielbechler.diff.collection + +import de.danielbechler.diff.map.MapKeyElement +import spock.lang.Specification + +/** + * @author Daniel Bechler + */ +class CollectionItemElementTest extends Specification { + def 'can be created with "null" item'() { + expect: + new CollectionItemElement(null) != null + } + + def 'should equal other CollectionElement if items are equal'() { + setup: + def element = new CollectionItemElement("foo") + def equalElement = new CollectionItemElement("foo") + + expect: + element.equals(equalElement) + } + + def 'should not equal elements of different class'() { + setup: + def element = new CollectionItemElement("foo") + def mapElement = new MapKeyElement("foo") + + expect: + !element.equals(mapElement) + } + + def 'should not equal null'() { + setup: + def element = new CollectionItemElement("foo") + + expect: + !element.equals(null) + } + + def 'should have same hashCode as item'() { + setup: + def item = "foo" + def element = new CollectionItemElement(item) + + expect: + element.hashCode() == item.hashCode() + } + + def 'should provide accessor for item'() { + setup: + def item = "foo" + def element = new CollectionItemElement(item) + + expect: + element.getItem() == item + } + + def 'should have proper toString() representation'() { + expect: "string representation of the item should be converted to single line" + new CollectionItemElement("foo\nbar").toString() == '[foo \\ bar]' + } +} diff --git a/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java b/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java index 5b085393..ce1b0ab5 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java @@ -16,13 +16,17 @@ package de.danielbechler.diff.helper; -import de.danielbechler.diff.*; -import de.danielbechler.diff.collection.*; -import de.danielbechler.diff.visitor.*; -import org.fest.assertions.api.*; -import org.fest.assertions.core.*; - -/** @author Daniel Bechler */ +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.Element; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.visitor.PropertyVisitor; +import org.fest.assertions.api.Assertions; +import org.fest.assertions.core.Condition; + +/** + * @author Daniel Bechler + */ public final class NodeAssertions { public static Syntax.SelectNode assertThat(final DiffNode node) @@ -83,7 +87,7 @@ public Syntax.AssertNode child(final Element pathElement) public Syntax.AssertNode collectionChild(final Object referenceItem) { - return child(new CollectionElement(referenceItem)); + return child(new CollectionItemElement(referenceItem)); } public Syntax.AssertNode doesExist() diff --git a/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java b/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java index 3c4d4bf5..40adb05d 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java @@ -16,14 +16,19 @@ package de.danielbechler.diff.helper; -import de.danielbechler.diff.*; -import de.danielbechler.diff.collection.*; -import org.fest.assertions.core.*; -import org.mockito.*; +import de.danielbechler.diff.Accessor; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.collection.CollectionItemAccessor; +import de.danielbechler.diff.collection.CollectionItemElement; +import org.fest.assertions.core.Condition; +import org.mockito.ArgumentMatcher; -import static org.mockito.Matchers.*; +import static org.mockito.Matchers.argThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class NodeMatchers { private NodeMatchers() @@ -41,8 +46,8 @@ public boolean matches(final Object argument) { @SuppressWarnings("TypeMayBeWeakened") final CollectionItemAccessor accessor = (CollectionItemAccessor) argument; - final CollectionElement collectionElement = new CollectionElement(item); - return accessor.getPathElement().equals(collectionElement); + final CollectionItemElement collectionItemElement = new CollectionItemElement(item); + return accessor.getPathElement().equals(collectionItemElement); } return false; } diff --git a/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy b/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy index 4162d98f..d940c265 100644 --- a/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy +++ b/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy @@ -41,7 +41,7 @@ class MapEntryAccessorTest extends Specification { def "provide access to its path element"() { expect: - accessor.getPathElement() instanceof MapElement + accessor.getPathElement() instanceof MapKeyElement } def "provide write access to referenced value in any map"() { diff --git a/src/test/java/de/danielbechler/diff/map/MapElementTest.groovy b/src/test/java/de/danielbechler/diff/map/MapKeyElementTest.groovy similarity index 77% rename from src/test/java/de/danielbechler/diff/map/MapElementTest.groovy rename to src/test/java/de/danielbechler/diff/map/MapKeyElementTest.groovy index 1fb37103..e65e7163 100644 --- a/src/test/java/de/danielbechler/diff/map/MapElementTest.groovy +++ b/src/test/java/de/danielbechler/diff/map/MapKeyElementTest.groovy @@ -19,11 +19,11 @@ package de.danielbechler.diff.map import spock.lang.Specification import spock.lang.Unroll -class MapElementTest extends Specification { +class MapKeyElementTest extends Specification { def "should be constructable with 'null'"() { when: - new MapElement(null) + new MapKeyElement(null) then: notThrown(Throwable) @@ -31,7 +31,7 @@ class MapElementTest extends Specification { def "should be equal to self"() { setup: - def element = new MapElement('foo') + def element = new MapKeyElement('foo') expect: element.equals(element) @@ -40,8 +40,8 @@ class MapElementTest extends Specification { @Unroll def "equals should be #expected when keys are #key and #anotherKey"() { given: - def element = new MapElement(key) - def anotherElement = new MapElement(anotherKey) + def element = new MapKeyElement(key) + def anotherElement = new MapKeyElement(anotherKey) expect: element.equals(anotherElement) == expected @@ -57,12 +57,12 @@ class MapElementTest extends Specification { def "should not be equal to 'null'"() { expect: - !new MapElement('foo').equals(null) + !new MapKeyElement('foo').equals(null) } def "should not be equal to different types"() { expect: - !new MapElement('foo').equals(2) + !new MapKeyElement('foo').equals(2) } def "hashCode should equal the hashCode of the key"() { @@ -70,17 +70,17 @@ class MapElementTest extends Specification { def key = new Object() expect: - new MapElement(key).hashCode() == key.hashCode() + new MapKeyElement(key).hashCode() == key.hashCode() } def "hashCode should be null when key is null"() { expect: - new MapElement(null).hashCode() == 0 + new MapKeyElement(null).hashCode() == 0 } def "toString should return the same as toHumanReadableString()"() { setup: - def element = new MapElement('foo') + def element = new MapKeyElement('foo') expect: element.toString() == element.toHumanReadableString() @@ -91,11 +91,11 @@ class MapElementTest extends Specification { def key = new Object() expect: - new MapElement(key).key.is(key) + new MapKeyElement(key).key.is(key) } def "toHumanReadableString should return the string representation of the key (without line breaks)"() { expect: - new MapElement("foo,\nbar").toHumanReadableString() == "{foo, \\ bar}" + new MapKeyElement("foo,\nbar").toHumanReadableString() == "{foo, \\ bar}" } } From d67f7b24c87949af983b772de0de83d3e002220d Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 1 Mar 2014 23:30:25 +0100 Subject: [PATCH 006/215] Added documentation to the config classes --- .../diff/ObjectDifferITCase.java | 38 ++-- .../example/CanonicalAccessorExample.java | 18 +- .../diff/example/Example2ITSpec.groovy | 169 +++++++------- .../example/MapEntryValueAccessExample.java | 2 +- .../example/RuntimeTypeResolutionExample.java | 9 +- .../diff/example/SimpleNodeExample.java | 12 +- .../gettingstarted/GettingStarted.java | 18 +- .../example/phonebook/PhoneBookITCase.java | 2 +- .../AdditionIntegrationITCase.java | 20 +- ...DeepDiffingCollectionItemChangeITCase.java | 35 +-- .../CircularReferenceIntegrationTest.java | 27 ++- .../diff/issues/issue15/GraphITCase.java | 212 +++++++++--------- .../diff/issues/issue38/Issue38.java | 14 +- .../diff/issues/issue66/MainApp.java | 2 +- .../diff/issues/issue77/Issue77ITCase.groovy | 38 ++-- .../diff/CategoryConfiguration.java | 8 +- .../diff/CircularReferenceConfiguration.java | 15 +- .../diff/ComparisonConfiguration.java | 9 +- .../diff/InclusionConfiguration.java | 8 + .../diff/IntrospectionConfiguration.java | 11 +- .../diff/ObjectDifferBuilder.java | 25 ++- .../de/danielbechler/merge/ObjectMerger.java | 2 +- .../de/danielbechler/diff/ListDiffSpec.groovy | 2 +- .../diff/helper/NodeAssertionsTest.java | 15 +- .../diff/visitor/PrintingVisitorTest.java | 15 +- 25 files changed, 410 insertions(+), 316 deletions(-) diff --git a/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java b/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java index 44b1c2fc..90680cba 100644 --- a/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java @@ -61,7 +61,7 @@ public class ObjectDifferITCase @BeforeMethod public void setUp() throws Exception { - objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + objectDiffer = ObjectDifferBuilder.buildDefault(); } public void testCompareBeanWithIgnoredMapProperty() @@ -96,7 +96,7 @@ public void testCompareCollectionWithAddedItem() throws Exception final Collection working = new LinkedList(asList("foo")); final Collection base = new LinkedList(); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(node).child(new CollectionItemElement("foo")).hasState(DiffNode.State.ADDED); @@ -107,7 +107,7 @@ public void testCompareCollectionWithRemovedItem() throws Exception final Collection working = new LinkedList(); final Collection base = new LinkedList(asList("foo")); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(node).child(new CollectionItemElement("foo")).hasState(DiffNode.State.REMOVED); @@ -119,7 +119,7 @@ public void testThatIgnoredPropertiesAreNeverAccessed() final ObjectWithAccessTrackingIgnoredProperty working = new ObjectWithAccessTrackingIgnoredProperty(); final ObjectWithAccessTrackingIgnoredProperty base = new ObjectWithAccessTrackingIgnoredProperty(); - ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + ObjectDifferBuilder.buildDefault().compare(working, base); Assertions.assertThat(working.accessed).isFalse(); Assertions.assertThat(base.accessed).isFalse(); @@ -147,7 +147,7 @@ public void testCompareCollectionWithDifferentCollectionImplementationsSucceeds( final Collection base = new LinkedHashSet(asList("one", "two")); final Collection working = new TreeSet(asList("one", "three")); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(base, working); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(base, working); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); } @@ -158,7 +158,7 @@ public void testCompareCollectionWithChangedItem() throws Exception final List working = asList(new ObjectWithIdentityAndValue("foo", "1")); final List base = asList(new ObjectWithIdentityAndValue("foo", "2")); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); assertThat(node).self().hasChanges(); assertThat(node).child(new CollectionItemElement(new ObjectWithIdentityAndValue("foo"))) @@ -172,7 +172,7 @@ public void testWithNewMapInWorkingAndNoneInBase() final Map working = new TreeMap(); working.put("foo", "bar"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.ADDED); NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.ADDED); @@ -185,7 +185,7 @@ public void testWithAdditionOfSimpleTypeToWorkingMap() final Map working = new TreeMap(); working.put("foo", "bar"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.ADDED); @@ -199,7 +199,7 @@ public void testWithSameEntryInBaseAndWorking() final Map working = new TreeMap(); working.put("foo", "bar"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.UNTOUCHED); NodeAssertions.assertThat(node).self().hasNoChildren(); @@ -212,7 +212,7 @@ public void testWithSingleEntryAddedToWorkingMap() base.put("foo", "bar"); final Map working = null; - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.REMOVED); NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.REMOVED); @@ -231,7 +231,7 @@ public void testWithAllNullMapItem() final Map base = new HashMap(1); base.put("foo", null); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.UNTOUCHED); } @@ -244,7 +244,7 @@ public void testWithSameEntries() final Map base = new LinkedHashMap(modified); modified.put("ping", "pong"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(modified, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(modified, base); NodeAssertions.assertThat(node).root().hasChildren(1); NodeAssertions.assertThat(node) @@ -264,7 +264,7 @@ public void testWithChangedEntry() final Map base = new LinkedHashMap(1); base.put("foo", "woot"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasChildren(1); NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.CHANGED); } @@ -275,7 +275,7 @@ public void testCompareWithDifferentMapImplementationsSucceeds() throws Exceptio final Map base = new LinkedHashMap(Collections.singletonMap("test", "foo")); final Map working = new TreeMap(Collections.singletonMap("test", "bar")); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); } @@ -283,7 +283,7 @@ public void testCompareWithDifferentMapImplementationsSucceeds() throws Exceptio @Test(groups = INTEGRATION) public void testCompareWithDifferentStrings() throws Exception { - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare("foo", "bar"); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare("foo", "bar"); assertThat(node).self().hasState(DiffNode.State.CHANGED); } @@ -291,7 +291,7 @@ public void testCompareWithDifferentStrings() throws Exception @Test(expectedExceptions = IllegalArgumentException.class, groups = INTEGRATION) public void testCompareWithDifferentTypes() { - ObjectDifferBuilder.buildDefaultObjectDiffer().compare("foo", 1337); + ObjectDifferBuilder.buildDefault().compare("foo", 1337); } @Test(groups = INTEGRATION) @@ -315,7 +315,7 @@ public void testCompareWithComplexType() // when(configuration.isReturnable(any(Node.class))).thenReturn(true); // when(node.hasChanges()).thenReturn(true); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare( + final DiffNode node = ObjectDifferBuilder.buildDefault().compare( new ObjectWithIdentityAndValue("a", "1"), new ObjectWithIdentityAndValue("a", "2")); @@ -327,7 +327,7 @@ public void testCompareWithListContainingObjectTwiceDetectsIfOneGetsRemoved() th { final List base = asList(new ObjectWithHashCodeAndEquals("foo"), new ObjectWithHashCodeAndEquals("foo")); final List working = asList(new ObjectWithHashCodeAndEquals("foo")); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); node.visit(new NodeHierarchyVisitor()); assertThat(node) .child(NodePath.createBuilder() @@ -342,7 +342,7 @@ public void testCompareWithListContainingObjectOnceDetectsIfAnotherInstanceOfItG { final List base = asList(new ObjectWithHashCodeAndEquals("foo")); final List working = asList(new ObjectWithHashCodeAndEquals("foo"), new ObjectWithHashCodeAndEquals("foo")); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); node.visit(new NodeHierarchyVisitor()); assertThat(node).child(NodePath.createBuilder() .withRoot() diff --git a/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java b/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java index 62ee2b03..3879bda8 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java @@ -16,14 +16,18 @@ package de.danielbechler.diff.example; -import de.danielbechler.diff.*; -import de.danielbechler.diff.visitor.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.visitor.NodeHierarchyVisitor; -import java.util.*; +import java.util.LinkedList; +import java.util.List; -import static java.util.Arrays.*; +import static java.util.Arrays.asList; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ class CanonicalAccessorExample { private CanonicalAccessorExample() @@ -41,8 +45,8 @@ public static void main(final String[] args) final Contact baseContact = new Contact("Walter White"); baseAddressBook.setContacts(asList(baseContact)); - final DiffNode rootNode = ObjectDifferBuilder.buildDefaultObjectDiffer() - .compare(workingAddressBook, baseAddressBook); + final DiffNode rootNode = ObjectDifferBuilder.buildDefault() + .compare(workingAddressBook, baseAddressBook); final DiffNode contactsNode = getFirstChildOf(rootNode); final DiffNode contactNode = getFirstChildOf(contactsNode); final DiffNode nicknameNode = getFirstChildOf(contactNode); diff --git a/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy b/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy index c1a8567a..713ec34d 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy +++ b/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy @@ -24,104 +24,91 @@ import spock.lang.Specification import static de.danielbechler.diff.DiffNode.State.UNTOUCHED -class Example2ITSpec extends Specification -{ - def "Comparing an object via equalsOnlyValueProvider (via property annotation)"() - { - given: - def base = new Company() - base.manager = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") - - and: - def working = new Company() - working.manager = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") - - expect: - ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base).state == UNTOUCHED - } - - def "Comparing an object via equalsOnlyValueProvider (via type annotation)"() - { - given: - def base = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") - def working = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") - - expect: - ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base).state == UNTOUCHED - } - - def "Comparing an object via equalsOnlyValueProvider (via configuration using property path)"() - { - given: - def builder = ObjectDifferBuilder.startBuilding() - builder.configure().comparison().ofNode(NodePath.buildRootPath()).toUseEqualsMethodOfValueProvidedByMethod("presentationForDiffer") - - def base = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") - def working = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") - - expect: - builder.build().compare(working, base).state == UNTOUCHED - } - - def "Comparing an object via equalsOnlyValueProvider (via configuration using type)"() - { - given: - def builder = ObjectDifferBuilder.startBuilding() - builder.configure().comparison().ofType(Person).toUseEqualsMethodOfValueProvidedByMethod("presentationForDiffer") - def base = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") - def working = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") - - expect: - builder.build().compare(working, base).state == UNTOUCHED - } - - - public static class Company - { - private Person manager; - - @ObjectDiffProperty(equalsOnly = true, equalsOnlyValueProviderMethod = "presentationForDiffer") - public Person getManager() - { - return manager +class Example2ITSpec extends Specification { + def "Comparing an object via equalsOnlyValueProvider (via property annotation)"() { + given: + def base = new Company() + base.manager = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") + + and: + def working = new Company() + working.manager = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") + + expect: + ObjectDifferBuilder.buildDefault().compare(working, base).state == UNTOUCHED } - void setManager(final Person manager) - { - this.manager = manager + def "Comparing an object via equalsOnlyValueProvider (via type annotation)"() { + given: + def base = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") + def working = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") + + expect: + ObjectDifferBuilder.buildDefault().compare(working, base).state == UNTOUCHED } - } - - public static class Person - { - private String firstname - private String lastname - private String nickname - - public Person(String firstname, String nickname, String lastname) - { - this.firstname = firstname - this.nickname = nickname - this.lastname = lastname + + def "Comparing an object via equalsOnlyValueProvider (via configuration using property path)"() { + given: + def builder = ObjectDifferBuilder.startBuilding() + builder.configure().comparison().ofNode(NodePath.buildRootPath()).toUseEqualsMethodOfValueProvidedByMethod("presentationForDiffer") + + def base = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") + def working = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") + + expect: + builder.build().compare(working, base).state == UNTOUCHED + } + + def "Comparing an object via equalsOnlyValueProvider (via configuration using type)"() { + given: + def builder = ObjectDifferBuilder.startBuilding() + builder.configure().comparison().ofType(Person).toUseEqualsMethodOfValueProvidedByMethod("presentationForDiffer") + def base = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") + def working = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") + + expect: + builder.build().compare(working, base).state == UNTOUCHED } - public String presentationForDiffer() - { - return firstname + " " + lastname + + public static class Company { + private Person manager; + + @ObjectDiffProperty(equalsOnly = true, equalsOnlyValueProviderMethod = "presentationForDiffer") + public Person getManager() { + return manager + } + + void setManager(final Person manager) { + this.manager = manager + } } - boolean equals(final o) - { - return false + public static class Person { + private String firstname + private String lastname + private String nickname + + public Person(String firstname, String nickname, String lastname) { + this.firstname = firstname + this.nickname = nickname + this.lastname = lastname + } + + public String presentationForDiffer() { + return firstname + " " + lastname + } + + boolean equals(final o) { + return false + } } - } - - @ObjectDiffEqualsOnlyType(valueProviderMethod = "presentationForDiffer") - public static class PersonWithEqualsOnlyTypeAnnotation extends Person - { - public PersonWithEqualsOnlyTypeAnnotation(final String firstname, final String nickname, final String lastname) - { - super(firstname, nickname, lastname) + + @ObjectDiffEqualsOnlyType(valueProviderMethod = "presentationForDiffer") + public static class PersonWithEqualsOnlyTypeAnnotation extends Person { + public PersonWithEqualsOnlyTypeAnnotation( + final String firstname, final String nickname, final String lastname) { + super(firstname, nickname, lastname) + } } - } } diff --git a/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java b/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java index 497c698a..69bac2e2 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java @@ -44,7 +44,7 @@ public static void main(final String[] args) working.put(23, "Shephard"); working.put(42, "Kwon"); - final DiffNode mapNode = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode mapNode = ObjectDifferBuilder.buildDefault().compare(working, base); mapNode.visitChildren(new DiffNode.Visitor() { public void accept(final DiffNode node, final Visit visit) diff --git a/src/integration-test/java/de/danielbechler/diff/example/RuntimeTypeResolutionExample.java b/src/integration-test/java/de/danielbechler/diff/example/RuntimeTypeResolutionExample.java index 922ba04d..ae8ea919 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/RuntimeTypeResolutionExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/RuntimeTypeResolutionExample.java @@ -16,9 +16,12 @@ package de.danielbechler.diff.example; -import de.danielbechler.diff.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDifferBuilder; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class RuntimeTypeResolutionExample { private RuntimeTypeResolutionExample() @@ -82,7 +85,7 @@ public static void main(final String[] args) { final Point base = new Point(new Coordinate3D(1, 2, 3)); final Point working = new Point(new Coordinate3D(1, 2, 30)); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); assert node.getChild("coordinate").getChild("z").isChanged() : "The changed 'z' coordinate should have been detected because the property type should be resolved to Coordinate3D at runtime."; diff --git a/src/integration-test/java/de/danielbechler/diff/example/SimpleNodeExample.java b/src/integration-test/java/de/danielbechler/diff/example/SimpleNodeExample.java index 6ac12f8f..f2017ee7 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/SimpleNodeExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/SimpleNodeExample.java @@ -16,10 +16,14 @@ package de.danielbechler.diff.example; -import de.danielbechler.diff.*; -import de.danielbechler.diff.visitor.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.visitor.NodeHierarchyVisitor; +import de.danielbechler.diff.visitor.PrintingVisitor; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ class SimpleNodeExample { private SimpleNodeExample() @@ -77,7 +81,7 @@ public static void main(final String[] args) { final Person bruceWayne = new Person("Bruce", "Wayne"); final Person batman = new Person("Batman", null); - final DiffNode rootNode = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(batman, bruceWayne); + final DiffNode rootNode = ObjectDifferBuilder.buildDefault().compare(batman, bruceWayne); rootNode.visit(new NodeHierarchyVisitor(10)); rootNode.visit(new PrintingVisitor(batman, bruceWayne) { diff --git a/src/integration-test/java/de/danielbechler/diff/example/gettingstarted/GettingStarted.java b/src/integration-test/java/de/danielbechler/diff/example/gettingstarted/GettingStarted.java index 0cc2d58a..3c332bca 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/gettingstarted/GettingStarted.java +++ b/src/integration-test/java/de/danielbechler/diff/example/gettingstarted/GettingStarted.java @@ -16,11 +16,17 @@ package de.danielbechler.diff.example.gettingstarted; -import de.danielbechler.diff.*; -import de.danielbechler.diff.example.phonebook.*; -import de.danielbechler.diff.visitor.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.example.phonebook.Contact; +import de.danielbechler.diff.example.phonebook.PhoneBook; +import de.danielbechler.diff.example.phonebook.PhoneNumber; +import de.danielbechler.diff.visitor.PrintingVisitor; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class GettingStarted { private GettingStarted() @@ -35,7 +41,7 @@ public static void main(final String[] args) private static void helloWorldExample() { - final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefault(); final String working = "Hello"; final String base = "World"; @@ -61,7 +67,7 @@ private static void phoneBookExample() modifiedPhoneBook.getContact("Jesse", "Pinkman").setMiddleName("Bruce"); modifiedPhoneBook.getContact("Walter", "White").setMiddleName("Hartwell"); - final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefault(); final DiffNode root = objectDiffer.compare(modifiedPhoneBook, phoneBook); final DiffNode.Visitor visitor = new PrintingVisitor(modifiedPhoneBook, phoneBook); root.visit(visitor); diff --git a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java index 6381a585..8fab4cdb 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java @@ -50,7 +50,7 @@ public void testContactMiddleNameChanges() modifiedPhoneBook.getContact("Jesse", "Pinkman").setMiddleName("Bruce"); modifiedPhoneBook.getContact("Walter", "White").setMiddleName("Hartwell"); - final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefault(); final DiffNode node = objectDiffer.compare(modifiedPhoneBook, phoneBook); assertThat(node.hasChanges(), is(true)); diff --git a/src/integration-test/java/de/danielbechler/diff/integration/AdditionIntegrationITCase.java b/src/integration-test/java/de/danielbechler/diff/integration/AdditionIntegrationITCase.java index 073a66c6..15355a3b 100644 --- a/src/integration-test/java/de/danielbechler/diff/integration/AdditionIntegrationITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/integration/AdditionIntegrationITCase.java @@ -16,15 +16,19 @@ package de.danielbechler.diff.integration; -import de.danielbechler.diff.*; -import de.danielbechler.diff.example.phonebook.*; -import de.danielbechler.diff.mock.*; -import org.testng.annotations.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.example.phonebook.Contact; +import de.danielbechler.diff.example.phonebook.PhoneBook; +import de.danielbechler.diff.mock.ObjectWithString; +import org.testng.annotations.Test; import static de.danielbechler.diff.helper.NodeAssertions.assertThat; import static org.testng.AssertJUnit.assertEquals; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class AdditionIntegrationITCase { @Test @@ -33,7 +37,7 @@ public void detects_change_from_null_to_object_referenct_as_addition() throws Ex final ObjectWithString base = new ObjectWithString(); final ObjectWithString working = new ObjectWithString("foo"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); assertThat(node).child("value").hasState(DiffNode.State.ADDED); } @@ -47,11 +51,11 @@ public void testDetectsChangeForDuplicatesInList() throws Exception final PhoneBook phoneBookMobile = new PhoneBook("devs"); phoneBookMobile.addContact(joe); - assertEquals(DiffNode.State.UNTOUCHED, ObjectDifferBuilder.buildDefaultObjectDiffer().compare(phoneBookMobile, phoneBookServer).getState()); + assertEquals(DiffNode.State.UNTOUCHED, ObjectDifferBuilder.buildDefault().compare(phoneBookMobile, phoneBookServer).getState()); phoneBookMobile.addContact(joe); assertEquals(2, phoneBookMobile.getContacts().size()); //Should be ADDED! //assertEquals(DiffNode.State.ADDED, ObjectDifferFactory.getInstance().compare(phoneBookMobile, phoneBookServer).getState()); - assertEquals(DiffNode.State.UNTOUCHED, ObjectDifferBuilder.buildDefaultObjectDiffer().compare(phoneBookMobile, phoneBookServer).getState()); + assertEquals(DiffNode.State.UNTOUCHED, ObjectDifferBuilder.buildDefault().compare(phoneBookMobile, phoneBookServer).getState()); } } diff --git a/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java b/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java index b09a1337..ab29e372 100644 --- a/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java @@ -16,16 +16,23 @@ package de.danielbechler.diff.integration; -import de.danielbechler.diff.*; -import de.danielbechler.diff.mock.*; -import de.danielbechler.diff.visitor.*; -import org.testng.annotations.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.mock.ObjectWithString; +import de.danielbechler.diff.visitor.NodeHierarchyVisitor; +import org.testng.annotations.Test; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; -import static de.danielbechler.diff.helper.NodeAssertions.*; +import static de.danielbechler.diff.helper.NodeAssertions.assertThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class DeepDiffingCollectionItemChangeITCase { @Test @@ -42,19 +49,19 @@ public void test_returns_full_property_graph_of_added_collection_items() node.visit(new NodeHierarchyVisitor()); assertThat(node).child(NodePath.createBuilder() - .withRoot() - .withMapKey("foo")).hasState(DiffNode.State.ADDED); + .withRoot() + .withMapKey("foo")).hasState(DiffNode.State.ADDED); assertThat(node).child(NodePath.createBuilder() - .withRoot() - .withMapKey("foo") - .withPropertyName("value")).hasState(DiffNode.State.ADDED); + .withRoot() + .withMapKey("foo") + .withPropertyName("value")).hasState(DiffNode.State.ADDED); } @Test public void test_collection_with_null_item() { - ObjectDifferBuilder.buildDefaultObjectDiffer() - .compare(Arrays.asList((String) null), Arrays.asList("foobar")); + ObjectDifferBuilder.buildDefault() + .compare(Arrays.asList((String) null), Arrays.asList("foobar")); } } diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java b/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java index e72ab6ca..5d54ea95 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java @@ -16,15 +16,20 @@ package de.danielbechler.diff.issues.issue15; -import de.danielbechler.diff.*; -import de.danielbechler.diff.mock.*; -import de.danielbechler.diff.visitor.*; -import org.fest.assertions.api.*; -import org.testng.annotations.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.mock.ObjectWithCircularReference; +import de.danielbechler.diff.visitor.PrintingVisitor; +import org.fest.assertions.api.Assertions; +import org.testng.annotations.Test; -import static de.danielbechler.diff.helper.NodeAssertions.*; +import static de.danielbechler.diff.helper.NodeAssertions.assertThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class CircularReferenceIntegrationTest { @Test @@ -40,15 +45,15 @@ public void testCircularReference() baseA.setReference(baseB); baseB.setReference(baseA); - final DiffNode root = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(workingA, baseA); + final DiffNode root = ObjectDifferBuilder.buildDefault().compare(workingA, baseA); assertThat(root).child("reference", "reference").isCircular(); assertThat(root).child("reference", "reference") .hasCircularStartPathEqualTo(NodePath.buildRootPath()); Assertions.assertThat(root.canonicalGet(workingA)) - .isSameAs(root.getChild("reference").getChild("reference").canonicalGet(workingA)); + .isSameAs(root.getChild("reference").getChild("reference").canonicalGet(workingA)); Assertions.assertThat(root.getChild("reference").getChild("reference").getCircleStartNode()) - .isSameAs(root); + .isSameAs(root); } @Test @@ -68,7 +73,7 @@ public void testCircularReferenceShouldBeAddedWhenEnabledInConfiguration() baseB.setReference(baseC); baseC.setReference(baseA); - final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefault(); // objectDiffer.getConfiguration().withoutCircularNodes(); final DiffNode root = objectDiffer.compare(workingA, baseA); root.visit(new PrintingVisitor(workingA, baseA)); diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java b/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java index ec3b9d3b..813d7f44 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java @@ -16,12 +16,16 @@ package de.danielbechler.diff.issues.issue15; -import de.danielbechler.diff.*; -import de.danielbechler.diff.helper.*; -import de.danielbechler.diff.visitor.*; -import org.testng.annotations.*; - -import static de.danielbechler.diff.NodePath.*; +import de.danielbechler.diff.CircularReferenceDetector; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.helper.NodeAssertions; +import de.danielbechler.diff.visitor.NodeHierarchyVisitor; +import de.danielbechler.diff.visitor.PrintingVisitor; +import org.testng.annotations.Test; + +import static de.danielbechler.diff.NodePath.createBuilder; import static de.danielbechler.diff.helper.NodeAssertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat; @@ -212,7 +216,9 @@ public void basicNodeWithDirectReferences2() assertThat(root).child("value").hasState(DiffNode.State.CHANGED).hasNoChildren(); } - /** Does not detect any changes since no primary key defined for each node */ + /** + * Does not detect any changes since no primary key defined for each node + */ @Test public void basicBidirectionalNodeWithChildNodes() { @@ -267,15 +273,15 @@ public void basicBidirectionalNodeWithChildNodesWithIds() .withRoot() .withPropertyName("children") .withCollectionItem(a)) - .hasState(DiffNode.State.CHANGED) - .hasChildren(1); + .hasState(DiffNode.State.CHANGED) + .hasChildren(1); NodeAssertions.assertThat(root).child(createBuilder() .withRoot() .withPropertyName("children") .withCollectionItem(b)) - .hasState(DiffNode.State.CHANGED) - .hasChildren(1); + .hasState(DiffNode.State.CHANGED) + .hasChildren(1); } @Test @@ -295,7 +301,7 @@ public void simpleGraphWithChanges() .withPropertyName("children") .withCollectionItem(modifiedA) .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .hasState(DiffNode.State.CHANGED); } @Test @@ -380,108 +386,108 @@ public void bidirectionalGraphStackOverflow() final DiffNode root = compareAndPrint(modified, base); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa)) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(a) + .withPropertyName("children") + .withCollectionItem(aa) + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("children") + .withCollectionItem(baa)) + .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(a) + .withPropertyName("children") + .withCollectionItem(aa) + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("value")) + .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa) - .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(a) + .withPropertyName("directReference") + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("children") + .withCollectionItem(baa) + .withPropertyName("value")) + .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(a) + .withPropertyName("directReference") + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("value")) + .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa) - .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(b) + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("children") + .withCollectionItem(baa) + .withPropertyName("value")) + .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(b) + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("value")) + .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa) - .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(b) + .withPropertyName("directReference") + .withPropertyName("children") + .withCollectionItem(aa) + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("children") + .withCollectionItem(baa) + .withPropertyName("value")) + .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) - .hasState(DiffNode.State.CHANGED); + .child(createBuilder() + .withRoot() + .withPropertyName("children") + .withCollectionItem(b) + .withPropertyName("directReference") + .withPropertyName("children") + .withCollectionItem(aa) + .withPropertyName("children") + .withCollectionItem(ba) + .withPropertyName("value")) + .hasState(DiffNode.State.CHANGED); } private static DiffNode compareAndPrint(final GraphNode modified, final GraphNode base) { - final DiffNode root = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(modified, base); + final DiffNode root = ObjectDifferBuilder.buildDefault().compare(modified, base); if (PRINT_ENABLED) { root.visit(new PrintingVisitor(modified, base)); @@ -532,13 +538,13 @@ public void testWithMapBasedBiDirectionalConnection() node.visit(new NodeHierarchyVisitor()); NodeAssertions.assertThat(node) - .child(createBuilder() - .withRoot() - .withPropertyName("map") - .withMapKey("foo") - .withPropertyName("map") - .withMapKey("bar")) - .isCircular(); + .child(createBuilder() + .withRoot() + .withPropertyName("map") + .withMapKey("foo") + .withPropertyName("map") + .withMapKey("bar")) + .isCircular(); } private static void establishParentChildRelationship(final GraphNode parent, final GraphNode child) diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue38/Issue38.java b/src/integration-test/java/de/danielbechler/diff/issues/issue38/Issue38.java index 2a785bc4..8fdb6346 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue38/Issue38.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue38/Issue38.java @@ -16,8 +16,10 @@ package de.danielbechler.diff.issues.issue38; -import de.danielbechler.diff.*; -import de.danielbechler.diff.visitor.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.visitor.PrintingVisitor; public class Issue38 { @@ -80,7 +82,7 @@ public static void testIntegerFailsPublic() TestIntBeanPublic base = new TestIntBeanPublic(); base.setValue(1); - ObjectDiffer differ = ObjectDifferBuilder.buildDefaultObjectDiffer(); + ObjectDiffer differ = ObjectDifferBuilder.buildDefault(); final DiffNode root = differ.compare(working, base); root.visit(new PrintingVisitor(working, base)); @@ -95,7 +97,7 @@ public static void testIntegerWorksPrivate() TestIntBeanPrivate base = new TestIntBeanPrivate(); base.setValue(1); - ObjectDiffer differ = ObjectDifferBuilder.buildDefaultObjectDiffer(); + ObjectDiffer differ = ObjectDifferBuilder.buildDefault(); final DiffNode root = differ.compare(working, base); root.visit(new PrintingVisitor(working, base)); @@ -110,7 +112,7 @@ public static void testIntegerWorksPublic() TestIntBeanPublic base = new TestIntBeanPublic(); base.setValue(1); - ObjectDiffer differ = ObjectDifferBuilder.buildDefaultObjectDiffer(); + ObjectDiffer differ = ObjectDifferBuilder.buildDefault(); final DiffNode root = differ.compare(working, base); root.visit(new PrintingVisitor(working, base)); @@ -124,7 +126,7 @@ public static void main(final String[] args) System.out.println("The following comparison does not work properly (detects removal)"); Issue38.testIntegerFailsPublic(); System.out - .println("The following comparison works properly bewcause the class has private access (detects change)"); + .println("The following comparison works properly bewcause the class has private access (detects change)"); Issue38.testIntegerWorksPrivate(); } } diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java b/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java index b11dbcb5..e42f8aee 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue66/MainApp.java @@ -29,7 +29,7 @@ private MainApp() public static void main(final String[] args) { - final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + final ObjectDiffer objectDiffer = ObjectDifferBuilder.buildDefault(); final TopHat hat1 = new TopHat(1, 10); final TopHat hat2 = new TopHat(2, 20); diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue77/Issue77ITCase.groovy b/src/integration-test/java/de/danielbechler/diff/issues/issue77/Issue77ITCase.groovy index 3e354cf3..6d06f2c0 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue77/Issue77ITCase.groovy +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue77/Issue77ITCase.groovy @@ -22,31 +22,25 @@ import spock.lang.Specification /** * @author Daniel Bechler */ -class Issue77ITCase extends Specification -{ - def "NullPointerException on null unsafe Comparable implementations"() - { - expect: - ObjectDifferBuilder.buildDefaultObjectDiffer().compare(new SomeObject(BigDecimal.ONE), new SomeObject(null)); - } +class Issue77ITCase extends Specification { + def "NullPointerException on null unsafe Comparable implementations"() { + expect: + ObjectDifferBuilder.buildDefault().compare(new SomeObject(BigDecimal.ONE), new SomeObject(null)); + } - class SomeObject - { - BigDecimal value; + class SomeObject { + BigDecimal value; - SomeObject(BigDecimal value) - { - this.value = value; - } + SomeObject(BigDecimal value) { + this.value = value; + } - BigDecimal getValue() - { - return value - } + BigDecimal getValue() { + return value + } - void setValue(final BigDecimal value) - { - this.value = value + void setValue(final BigDecimal value) { + this.value = value + } } - } } diff --git a/src/main/java/de/danielbechler/diff/CategoryConfiguration.java b/src/main/java/de/danielbechler/diff/CategoryConfiguration.java index 33553f18..e7b76f84 100644 --- a/src/main/java/de/danielbechler/diff/CategoryConfiguration.java +++ b/src/main/java/de/danielbechler/diff/CategoryConfiguration.java @@ -1,6 +1,12 @@ package de.danielbechler.diff; -/** @author Daniel Bechler */ +/** + * Allows to assign custom categories (or tags) to entire types or selected elements and properties. These categories + * come in very handy, when combined with the `InclusionConfiguration`. They make it very easy to limit the comparison + * to a specific subset of the object graph. + * + * @author Daniel Bechler + */ public interface CategoryConfiguration { Of ofNode(NodePath nodePath); diff --git a/src/main/java/de/danielbechler/diff/CircularReferenceConfiguration.java b/src/main/java/de/danielbechler/diff/CircularReferenceConfiguration.java index 017cd769..68c48df2 100644 --- a/src/main/java/de/danielbechler/diff/CircularReferenceConfiguration.java +++ b/src/main/java/de/danielbechler/diff/CircularReferenceConfiguration.java @@ -1,6 +1,19 @@ package de.danielbechler.diff; -/** @author Daniel Bechler */ +/** + * Allows to define how the circular reference detector compares object instances. By default it uses the equality + * operator (`==`) which should be fine in mose cases. + *

+ * When dealing with object models that return copies of its properties on every access, it's possible to end up in + * infinite loops, because even though the objects may look the same, they would be different instances. In those cases + * it is possible to switch the instance detection mode to use the equals method instead of the equality operator. This + * way objects will be considered to be "the same" whenever `equals` returns `true`. + *

+ * This configuration interface also allows to register a custom handler for exception thrown, whenever a circular + * reference is detected. The default handler simply logs a warning. + * + * @author Daniel Bechler + */ public interface CircularReferenceConfiguration { CircularReferenceConfiguration matchCircularReferencesUsing(CircularReferenceMatchingMode matchingMode); diff --git a/src/main/java/de/danielbechler/diff/ComparisonConfiguration.java b/src/main/java/de/danielbechler/diff/ComparisonConfiguration.java index 6105b345..35c8167c 100644 --- a/src/main/java/de/danielbechler/diff/ComparisonConfiguration.java +++ b/src/main/java/de/danielbechler/diff/ComparisonConfiguration.java @@ -1,6 +1,13 @@ package de.danielbechler.diff; -/** @author Daniel Bechler */ +/** + * Allows to configure the way objects are compared. Sometimes introspection is just not the way to go. Let it be for + * performance reasons or simply because the object doesn't expose any useful properties. In those cases it's possible + * to define alternative comparison strategies, like using the equals method, a comparator or even a custom strategy. + * These settings can be made for specific nodes or entire types. + * + * @author Daniel Bechler + */ public interface ComparisonConfiguration { Of ofNode(NodePath nodePath); diff --git a/src/main/java/de/danielbechler/diff/InclusionConfiguration.java b/src/main/java/de/danielbechler/diff/InclusionConfiguration.java index 6e285ec9..a0172faf 100644 --- a/src/main/java/de/danielbechler/diff/InclusionConfiguration.java +++ b/src/main/java/de/danielbechler/diff/InclusionConfiguration.java @@ -1,6 +1,14 @@ package de.danielbechler.diff; /** + * Allows to in- or exclude nodes based on property name, object type, category or location in the object graph. + * Excluded nodes will not be compared, to make sure their accessors won't get called. This is useful in cases where + * getters could throw exceptions under certain conditions or when certain accessors are expensive to call or simply + * not relevant for the use-case. + *

+ * In combination with categories this allows to define sub-sets of properties, in order to compare only relevant parts + * of an object (e.g. exclude all properties marked as _metadata_.) + * * @author Daniel Bechler */ public interface InclusionConfiguration diff --git a/src/main/java/de/danielbechler/diff/IntrospectionConfiguration.java b/src/main/java/de/danielbechler/diff/IntrospectionConfiguration.java index c8e223cf..bdf92fd3 100644 --- a/src/main/java/de/danielbechler/diff/IntrospectionConfiguration.java +++ b/src/main/java/de/danielbechler/diff/IntrospectionConfiguration.java @@ -1,6 +1,15 @@ package de.danielbechler.diff; -/** @author Daniel Bechler */ +/** + * Allows to replace the default bean introspector with a custom implementation. The default introspector internally + * uses the `java.beans.Introspector` which has some limitations. The most important one being that it only operates on + * getters and setters. In case field introspection is needed a custom introspector must be used. An introspector can + * be + * set as global default or on a per-property basis. It is also possible to turn off introspection for specific + * properties in which case they will simply be compared via `equals` method. + * + * @author Daniel Bechler + */ public interface IntrospectionConfiguration { IntrospectionConfiguration setDefaultIntrospector(Introspector introspector); diff --git a/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java b/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java index 1382201f..9e320e06 100644 --- a/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java +++ b/src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java @@ -22,6 +22,10 @@ import de.danielbechler.diff.primitive.PrimitiveDiffer; /** + * This is the entry point of every diffing operation. It acts as a factory to get hold of an actual {@link + * de.danielbechler.diff.ObjectDiffer} instance and exposes a configuration API to customize its behavior to + * suit your needs. + * * @author Daniel Bechler */ public final class ObjectDifferBuilder @@ -38,7 +42,7 @@ private ObjectDifferBuilder() { } - public static ObjectDiffer buildDefaultObjectDiffer() + public static ObjectDiffer buildDefault() { return startBuilding().build(); } @@ -59,6 +63,9 @@ public ObjectDiffer build() return new ObjectDiffer(differDispatcher); } + /** + * Configure the way the ObjectDiffer should behave. + */ public final Configurable configure() { return configurable; @@ -79,26 +86,42 @@ public ReturnableNodeConfiguration filtering() return returnableNodeService; } + /** + * Allows to replace the default bean introspector with a custom implementation. + */ public IntrospectionConfiguration introspection() { return introspectionService; } + /** + * Allows to define how the circular reference detector compares object instances. + */ public CircularReferenceConfiguration circularReferenceHandling() { return circularReferenceService; } + /** + * Allows to in- or exclude nodes based on property name, object type, category or location in the object + * graph. + */ public InclusionConfiguration inclusion() { return inclusionService; } + /** + * Allows to configure the way objects are compared. + */ public ComparisonConfiguration comparison() { return comparisonService; } + /** + * Allows to assign custom categories (or tags) to entire types or selected elements and properties. + */ public CategoryConfiguration categories() { return categoryService; diff --git a/src/main/java/de/danielbechler/merge/ObjectMerger.java b/src/main/java/de/danielbechler/merge/ObjectMerger.java index 976c37f3..c6ba3e88 100644 --- a/src/main/java/de/danielbechler/merge/ObjectMerger.java +++ b/src/main/java/de/danielbechler/merge/ObjectMerger.java @@ -34,7 +34,7 @@ public final class ObjectMerger public ObjectMerger() { - this.objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + this.objectDiffer = ObjectDifferBuilder.buildDefault(); } public ObjectMerger(final ObjectDiffer objectDiffer) diff --git a/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy b/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy index 09ec48b8..2c92e96b 100644 --- a/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy +++ b/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy @@ -28,7 +28,7 @@ class ListDiffSpec extends Specification { def objectDiffer; def setup() { - objectDiffer = ObjectDifferBuilder.buildDefaultObjectDiffer(); + objectDiffer = ObjectDifferBuilder.buildDefault(); } @Ignore diff --git a/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java b/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java index 1c4031e1..f7706e82 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java @@ -16,10 +16,13 @@ package de.danielbechler.diff.helper; -import de.danielbechler.diff.*; -import de.danielbechler.diff.bean.*; -import de.danielbechler.diff.mock.*; -import org.testng.annotations.*; +import de.danielbechler.diff.Accessor; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.mock.ObjectWithString; +import org.testng.annotations.Test; import static de.danielbechler.diff.helper.NodeAssertions.assertThat; import static org.mockito.Mockito.mock; @@ -94,7 +97,7 @@ public void testAssertThat_child_at_property_path_does_exist_succeeds_when_child { final ObjectWithString working = new ObjectWithString("foo"); final ObjectWithString base = new ObjectWithString("bar"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); assertThat(node).child(NodePath.buildWith("value")).doesExist(); } @@ -109,7 +112,7 @@ public void testAssertThat_child_at_property_path_builder_does_exist_succeeds_wh { final ObjectWithString working = new ObjectWithString("foo"); final ObjectWithString base = new ObjectWithString("bar"); - final DiffNode node = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(working, base); + final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); assertThat(node).child(NodePath.createBuilder().withRoot().withPropertyName("value")).doesExist(); } diff --git a/src/test/java/de/danielbechler/diff/visitor/PrintingVisitorTest.java b/src/test/java/de/danielbechler/diff/visitor/PrintingVisitorTest.java index 5e6f10e0..fba6b229 100644 --- a/src/test/java/de/danielbechler/diff/visitor/PrintingVisitorTest.java +++ b/src/test/java/de/danielbechler/diff/visitor/PrintingVisitorTest.java @@ -16,13 +16,16 @@ package de.danielbechler.diff.visitor; -import de.danielbechler.diff.*; -import de.danielbechler.diff.mock.*; -import org.testng.annotations.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.mock.ObjectWithCircularReference; +import org.testng.annotations.Test; -import static org.fest.assertions.api.Assertions.*; +import static org.fest.assertions.api.Assertions.assertThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class PrintingVisitorTest { @Test @@ -50,7 +53,7 @@ public void omits_intermediate_nodes_with_changed_child_nodes() a2.setReference(b2); b2.setReference(d2); - final DiffNode rootNode = ObjectDifferBuilder.buildDefaultObjectDiffer().compare(a1, a2); + final DiffNode rootNode = ObjectDifferBuilder.buildDefault().compare(a1, a2); final TestablePrintingVisitor visitor = new TestablePrintingVisitor(a1, a2); rootNode.visit(visitor); final String output = visitor.getOutput(); From 02890cdf7fb6cc6cb6f8cc1393c05d28381d9ab1 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 1 Mar 2014 23:33:54 +0100 Subject: [PATCH 007/215] Gnah, need to make CircularReferenceDetector.java public again for some old regression test --- .../de/danielbechler/diff/CircularReferenceDetector.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/danielbechler/diff/CircularReferenceDetector.java b/src/main/java/de/danielbechler/diff/CircularReferenceDetector.java index a2878336..631d2a1e 100644 --- a/src/main/java/de/danielbechler/diff/CircularReferenceDetector.java +++ b/src/main/java/de/danielbechler/diff/CircularReferenceDetector.java @@ -16,14 +16,14 @@ package de.danielbechler.diff; -import de.danielbechler.util.*; +import de.danielbechler.util.Assert; -import java.util.*; +import java.util.LinkedList; -import static de.danielbechler.util.Objects.*; +import static de.danielbechler.util.Objects.isEqual; /** @author Daniel Bechler */ -class CircularReferenceDetector +public class CircularReferenceDetector { /** * It would be better to use java.util.Deque instead of the linked list, but that was first From ec215b2ede62d6eaa11e92ad907c85c465a4151b Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Fri, 7 Mar 2014 00:27:07 +0100 Subject: [PATCH 008/215] Created specification for exclusion configuration --- .idea/inspectionProfiles/Project_Default.xml | 1 + .../danielbechler/diff/ExclusionITCase.groovy | 169 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 src/integration-test/java/de/danielbechler/diff/ExclusionITCase.groovy diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 63fded89..1a530b1f 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -15,6 +15,7 @@ + diff --git a/src/integration-test/java/de/danielbechler/diff/ExclusionITCase.groovy b/src/integration-test/java/de/danielbechler/diff/ExclusionITCase.groovy new file mode 100644 index 00000000..c476178e --- /dev/null +++ b/src/integration-test/java/de/danielbechler/diff/ExclusionITCase.groovy @@ -0,0 +1,169 @@ +/* + * Copyright 2014 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 + +import de.danielbechler.diff.annotation.ObjectDiffProperty +import de.danielbechler.diff.collection.CollectionItemElement +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import spock.lang.Specification +import spock.lang.Stepwise + +/** + * Created by dbechler. + */ +@Stepwise +class ExclusionITCase extends Specification { + + def builder = ObjectDifferBuilder.startBuilding() + def configurable = builder.configure() + + def base = new PhoneBook(name: "Jerry's Phone Book", revision: 1, contacts: [ + new Contact(id: "elaine", name: "Elaine", number: "917-555-0186"), + new Contact(id: "george", name: "George", number: "917-555-0102"), + new Contact(id: "kramer", name: "Kramer", number: "917-555-3456") + ]) + + // Jerry decided to give his phone book a more formal title + def working = new PhoneBook(name: "Jerry Seinfeld's Phone Book", revision: 2, contacts: [ + new Contact(id: "elaine", name: "Elaine", number: "917-555-0186"), + + // George came up with a ridiculous new nickname + new Contact(id: "george", name: "T-Bone", number: "917-555-0102"), + + // Jerry always ended up at the Moviefone hotline until he realized Kramers new number actually ends with 5, not 6 + new Contact(id: "kramer", name: "Kramer", number: "917-555-3455") + ]) + + def "Sanity check"() { + when: + def node = ObjectDifferBuilder.buildDefault().compare(working, base) + + then: + node.getChild('name').changed + + and: "only two contacts should have changed" + node.getChild('contacts').changed + node.getChild('contacts').children.size() == 2 + + and: "only Georges name should have changed" + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))).children.size() == 1 + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))).getChild('name').changed + + and: "only Kramers number should have changed" + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))).children.size() == 1 + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))).getChild('number').changed + } + + def "Property with specific name excluded via configuration"() { + given: + configurable.inclusion().toExclude().propertyNames("name") + + when: + def node = builder.build().compare(working, base) + + then: "Georges name change should be ignored" + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))) == null + + and: "The name change of the phone book should be ignored too" + node.getChild('name') == null + } + + def "Category excluded via configuration"() { + given: + configurable.inclusion().toExclude().categories('private') + + when: + def node = builder.build().compare(working, base) + + then: "Kramers changed number should be ignored" + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))) == null + } + + def "Type excluded via configuration"() { + given: + configurable.inclusion().toExclude().types(Contact) + + when: + def node = builder.build().compare(working, base) + + then: "all contact changes should be ignored" + node.getChild('contacts') == null + } + + def "Node at specific NodePath excluded via configuration"() { + given: + configurable.inclusion().toExclude().nodes(NodePath.createBuilder() + .withRoot() + .withPropertyName('contacts') + .withCollectionItem(new Contact(id: 'george')) + .build()) + + when: + def node = builder.build().compare(working, base) + + then: + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))) == null + } + + def "Property excluded via @ObjectDiffProperty annotation"() { + given: + configurable.inclusion().toExclude().nodes(NodePath.buildWith('name')) + + when: + def node = builder.build().compare(working, base) + + then: "the name change of the phone book should be ignored" + node.getChild('revision') == null + } + + @EqualsAndHashCode + @ToString + class PhoneBook { + def name + def contacts = [] + def revision + + @SuppressWarnings("GroovyUnusedDeclaration") + @ObjectDiffProperty(excluded = true) + def getRevision() { + return revision + } + + void setRevision(revision) { + this.revision = revision + } + } + + @EqualsAndHashCode(includes = ["id"]) + @ToString + class Contact { + def id + def name + def number + + @SuppressWarnings("GroovyUnusedDeclaration") + @ObjectDiffProperty(categories = ['private']) + def getNumber() { + return number + } + + void setNumber(number) { + this.number = number + } + } +} From c7573a3e8e29647196f63d5367a8bf4f64c72c29 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sun, 9 Mar 2014 01:00:26 +0100 Subject: [PATCH 009/215] Fixed inclusion for intermediate nodes like collection items or map entries --- ...ovy => InclusionAndExclusionITCase.groovy} | 18 ++- .../java/de/danielbechler/diff/DiffNode.java | 50 ++++++-- .../danielbechler/diff/InclusionService.java | 22 ++-- .../diff/PropertyAwareAccessor.java | 10 +- .../de/danielbechler/diff/DiffNodeTest.java | 115 +++++++++++++----- .../diff/InclusionServiceSpec.groovy | 23 ++-- 6 files changed, 174 insertions(+), 64 deletions(-) rename src/integration-test/java/de/danielbechler/diff/{ExclusionITCase.groovy => InclusionAndExclusionITCase.groovy} (89%) diff --git a/src/integration-test/java/de/danielbechler/diff/ExclusionITCase.groovy b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy similarity index 89% rename from src/integration-test/java/de/danielbechler/diff/ExclusionITCase.groovy rename to src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy index c476178e..4a2ee45f 100644 --- a/src/integration-test/java/de/danielbechler/diff/ExclusionITCase.groovy +++ b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy @@ -18,6 +18,7 @@ package de.danielbechler.diff import de.danielbechler.diff.annotation.ObjectDiffProperty import de.danielbechler.diff.collection.CollectionItemElement +import de.danielbechler.diff.visitor.NodeHierarchyVisitor import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import spock.lang.Specification @@ -27,7 +28,7 @@ import spock.lang.Stepwise * Created by dbechler. */ @Stepwise -class ExclusionITCase extends Specification { +class InclusionAndExclusionITCase extends Specification { def builder = ObjectDifferBuilder.startBuilding() def configurable = builder.configure() @@ -42,8 +43,8 @@ class ExclusionITCase extends Specification { def working = new PhoneBook(name: "Jerry Seinfeld's Phone Book", revision: 2, contacts: [ new Contact(id: "elaine", name: "Elaine", number: "917-555-0186"), - // George came up with a ridiculous new nickname - new Contact(id: "george", name: "T-Bone", number: "917-555-0102"), + // George has a new nickname + new Contact(id: "george", name: "Koko", number: "917-555-0102"), // Jerry always ended up at the Moviefone hotline until he realized Kramers new number actually ends with 5, not 6 new Contact(id: "kramer", name: "Kramer", number: "917-555-3455") @@ -131,6 +132,17 @@ class ExclusionITCase extends Specification { node.getChild('revision') == null } + def "Inclusion via property name"() { + given: + configurable.inclusion().toInclude().propertyNames('name', 'contacts') + when: + def node = builder.build().compare(working, base) + then: + node.visit(new NodeHierarchyVisitor()) + node.getChild("name").changed + node.getChild("contacts").getChild(new CollectionItemElement(new Contact(id: "george"))).getChild("name").changed + } + @EqualsAndHashCode @ToString class PhoneBook { diff --git a/src/main/java/de/danielbechler/diff/DiffNode.java b/src/main/java/de/danielbechler/diff/DiffNode.java index a6418d7f..91d2ce4f 100644 --- a/src/main/java/de/danielbechler/diff/DiffNode.java +++ b/src/main/java/de/danielbechler/diff/DiffNode.java @@ -16,14 +16,18 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.*; -import de.danielbechler.diff.visitor.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.visitor.PropertyVisitor; +import de.danielbechler.util.Assert; -import java.lang.annotation.*; -import java.util.*; +import java.lang.annotation.Annotation; +import java.util.Collection; import java.util.Collections; -import java.util.concurrent.atomic.*; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicBoolean; import static java.util.Collections.unmodifiableSet; @@ -39,6 +43,7 @@ public class DiffNode { public static final DiffNode ROOT = null; + private final Accessor accessor; private final Map children = new LinkedHashMap(10); @@ -383,6 +388,37 @@ public T getPropertyAnnotation(final Class annotationC return null; } + /** + * If this node represents a bean property, this method will simply return its name. Otherwise it will return the + * property name of its closest bean property representing ancestor. This way intermediate nodes like those + * representing collection, map or array items will be semantically tied to their container objects. + *

+ * That is especially useful for inclusion and exclusion rules. For example, when a List is explicitly included by + * property name, it would be weird if the inclusion didn't also apply to its items. + */ + public String getPropertyName() + { + if (isPropertyAware()) + { + return ((PropertyAwareAccessor) accessor).getPropertyName(); + } + else if (parentNode != null) + { + return parentNode.getPropertyName(); + } + return null; + } + + /** + * Returns true when this node represents a bean property and can therefore be queried for property + * specific information like annotations or property types. But there will also be nodes that represent collection + * items, map entries, etc. In those cases this method will return false. + */ + public final boolean isPropertyAware() + { + return accessor instanceof PropertyAwareAccessor; + } + public final boolean isRootNode() { return accessor instanceof RootAccessor; @@ -446,7 +482,7 @@ public DiffNode getParentNode() * * @param parentNode The parent of this node. May be null, if this is a root node. */ - public final void setParentNode(final DiffNode parentNode) + protected final void setParentNode(final DiffNode parentNode) { if (this.parentNode != null && this.parentNode != parentNode) { diff --git a/src/main/java/de/danielbechler/diff/InclusionService.java b/src/main/java/de/danielbechler/diff/InclusionService.java index c839f25c..accdb995 100644 --- a/src/main/java/de/danielbechler/diff/InclusionService.java +++ b/src/main/java/de/danielbechler/diff/InclusionService.java @@ -1,9 +1,11 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.*; -import de.danielbechler.util.*; +import de.danielbechler.util.Assert; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; import static de.danielbechler.diff.Inclusion.EXCLUDED; import static de.danielbechler.diff.Inclusion.INCLUDED; @@ -25,16 +27,6 @@ public InclusionService(final CategoryResolver categoryResolver) this.categoryResolver = categoryResolver; } - private static String getNodePropertyName(final DiffNode node) - { - final Element pathElement = node.getPathElement(); - if (pathElement instanceof BeanPropertyElement) - { - return ((BeanPropertyElement) pathElement).getPropertyName(); - } - return null; - } - public boolean isIgnored(final DiffNode node) { return node.isExcluded() || !isIncluded(node) || isExcluded(node); @@ -71,7 +63,7 @@ else if (isIncludedByPropertyName(node)) private boolean isIncludedByPropertyName(final DiffNode node) { - final String propertyName = getNodePropertyName(node); + final String propertyName = node.getPropertyName(); if (propertyName != null) { return propertyNameInclusions.get(propertyName) == INCLUDED; @@ -116,7 +108,7 @@ else if (isExcludedByPropertyName(node)) @SuppressWarnings("TypeMayBeWeakened") // we don't want to weaken the type for consistency reasons private boolean isExcludedByPropertyName(final DiffNode node) { - final String propertyName = getNodePropertyName(node); + final String propertyName = node.getPropertyName(); if (propertyName != null) { return propertyNameInclusions.get(propertyName) == EXCLUDED; diff --git a/src/main/java/de/danielbechler/diff/PropertyAwareAccessor.java b/src/main/java/de/danielbechler/diff/PropertyAwareAccessor.java index cacf2b59..dd2921c1 100644 --- a/src/main/java/de/danielbechler/diff/PropertyAwareAccessor.java +++ b/src/main/java/de/danielbechler/diff/PropertyAwareAccessor.java @@ -16,12 +16,16 @@ package de.danielbechler.diff; -import java.lang.annotation.*; -import java.util.*; +import java.lang.annotation.Annotation; +import java.util.Set; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public interface PropertyAwareAccessor extends TypeAwareAccessor, CategoryAwareAccessor, ComparisonStrategyAwareAccessor, ExclusionAwareAccessor { + String getPropertyName(); + Set getReadMethodAnnotations(); T getReadMethodAnnotation(Class annotationClass); diff --git a/src/test/java/de/danielbechler/diff/DiffNodeTest.java b/src/test/java/de/danielbechler/diff/DiffNodeTest.java index cb2048ee..2500c2c2 100644 --- a/src/test/java/de/danielbechler/diff/DiffNodeTest.java +++ b/src/test/java/de/danielbechler/diff/DiffNodeTest.java @@ -16,16 +16,21 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.*; -import de.danielbechler.diff.collection.*; -import de.danielbechler.diff.helper.*; -import de.danielbechler.diff.mock.*; -import org.fest.assertions.api.*; -import org.hamcrest.core.*; -import org.testng.annotations.*; - -import java.lang.annotation.*; -import java.util.*; +import de.danielbechler.diff.bean.BeanPropertyAccessor; +import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.collection.CollectionItemAccessor; +import de.danielbechler.diff.helper.NodeAssertions; +import de.danielbechler.diff.mock.ObjectDiffTest; +import org.fest.assertions.api.Assertions; +import org.hamcrest.core.Is; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import static org.fest.assertions.api.Assertions.assertThat; import static org.hamcrest.MatcherAssert.assertThat; @@ -51,7 +56,7 @@ public void setUp() } @Test - public void testHasChanges_returns_false_when_untouched() + public void hasChanges_returns_false_when_untouched() { final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.UNTOUCHED); @@ -59,7 +64,7 @@ public void testHasChanges_returns_false_when_untouched() } @Test - public void testHasChanges_returns_false_when_ignored() + public void hasChanges_returns_false_when_ignored() { final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.IGNORED); @@ -67,7 +72,7 @@ public void testHasChanges_returns_false_when_ignored() } @Test - public void testHasChanges_returns_false_when_circular() + public void hasChanges_returns_false_when_circular() { final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.CIRCULAR); @@ -75,7 +80,7 @@ public void testHasChanges_returns_false_when_circular() } @Test - public void testHasChanges_returns_true_when_changed() + public void hasChanges_returns_true_when_changed() { final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.CHANGED); @@ -83,7 +88,7 @@ public void testHasChanges_returns_true_when_changed() } @Test - public void testHasChanges_returns_true_when_removed() + public void hasChanges_returns_true_when_removed() { final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.REMOVED); @@ -91,7 +96,7 @@ public void testHasChanges_returns_true_when_removed() } @Test - public void testHasChanges_returns_true_when_added() + public void hasChanges_returns_true_when_added() { final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.ADDED); @@ -99,7 +104,7 @@ public void testHasChanges_returns_true_when_added() } @Test - public void testHasChanges_returns_true_when_child_has_changed() + public void hasChanges_returns_true_when_child_has_changed() { final DiffNode root = new DiffNode(List.class); final DiffNode child = new DiffNode(root, new CollectionItemAccessor("foo"), String.class); @@ -109,7 +114,7 @@ public void testHasChanges_returns_true_when_child_has_changed() } @Test - public void testGetPropertyPath_with_parent_node_should_return_canonical_path() + public void getPropertyPath_with_parent_node_should_return_canonical_path() { final DiffNode parentNode = new DiffNode(RootAccessor.getInstance(), String.class); when(accessor.getPathElement()).thenReturn(new BeanPropertyElement("foo")); @@ -120,7 +125,7 @@ public void testGetPropertyPath_with_parent_node_should_return_canonical_path() } @Test - public void testGetPropertyPath_without_parent_node_should_return_root_path() + public void getPropertyPath_without_parent_node_should_return_root_path() { final DiffNode root = new DiffNode(Object.class); assertThat(root.getPath()).isEqualTo(NodePath @@ -128,14 +133,14 @@ public void testGetPropertyPath_without_parent_node_should_return_root_path() } @Test(expectedExceptions = IllegalArgumentException.class) - public void testAddChild_throws_exception_when_root_node_is_passed() throws Exception + public void addChild_throws_exception_when_root_node_is_passed() throws Exception { final DiffNode root = new DiffNode(Object.class); root.addChild(new DiffNode(Object.class)); } @Test(expectedExceptions = IllegalArgumentException.class) - public void testAddChild_throws_exception_when_passed_node_is_already_child_of_another_node() throws Exception + public void addChild_throws_exception_when_passed_node_is_already_child_of_another_node() throws Exception { final DiffNode node1 = new DiffNode(Object.class); final DiffNode node2 = new DiffNode(Object.class); @@ -145,14 +150,14 @@ public void testAddChild_throws_exception_when_passed_node_is_already_child_of_a } @Test(expectedExceptions = IllegalArgumentException.class) - public void testAddChild_throws_exception_when_node_is_added_to_itself() throws Exception + public void addChild_throws_exception_when_node_is_added_to_itself() throws Exception { final DiffNode node = new DiffNode(null, accessor, Object.class); node.addChild(node); } @Test - public void testAddChild_establishes_parent_child_relationship() throws Exception + public void addChild_establishes_parent_child_relationship() throws Exception { final DiffNode node1 = new DiffNode(List.class); final DiffNode node2 = new DiffNode(new CollectionItemAccessor("foo"), String.class); @@ -162,7 +167,7 @@ public void testAddChild_establishes_parent_child_relationship() throws Exceptio } @Test - public void testAddChild_changes_node_state_to_changed_if_changed_child_node_gets_added() + public void addChild_changes_node_state_to_changed_if_changed_child_node_gets_added() { final DiffNode node = new DiffNode(Object.class); final DiffNode childNode = new DiffNode(node, accessor, String.class); @@ -174,7 +179,7 @@ public void testAddChild_changes_node_state_to_changed_if_changed_child_node_get } @Test - public void testShould_return_property_annotations_of_property_accessor() throws Exception + public void should_return_property_annotations_of_property_accessor() throws Exception { final BeanPropertyAccessor propertyAccessor = mock(BeanPropertyAccessor.class); final Annotation annotation = mock(Annotation.class); @@ -187,7 +192,7 @@ public void testShould_return_property_annotations_of_property_accessor() throws } @Test - public void testShould_return_empty_set_of_property_annotations_if_accessor_is_not_property_accessor() throws Exception + public void should_return_empty_set_of_property_annotations_if_accessor_is_not_property_accessor() throws Exception { final BeanPropertyAccessor propertyAccessor = mock(BeanPropertyAccessor.class); final Annotation annotation = mock(Annotation.class); @@ -200,7 +205,7 @@ public void testShould_return_empty_set_of_property_annotations_if_accessor_is_n } @Test - public void test_get_property_annotation_should_delegate_call_to_property_accessor() + public void getPropertyAnnotation_should_delegate_call_to_property_accessor() { final BeanPropertyAccessor propertyAccessor = mock(BeanPropertyAccessor.class); when(propertyAccessor.getReadMethodAnnotation(ObjectDiffTest.class)).thenReturn(null); @@ -211,7 +216,7 @@ public void test_get_property_annotation_should_delegate_call_to_property_access } @Test - public void test_get_property_annotation_should_return_null_if_accessor_is_not_property_accessor() + public void getPropertyAnnotation_should_return_null_if_accessor_is_not_property_accessor() { final Accessor propertyAccessor = mock(Accessor.class); @@ -219,4 +224,58 @@ public void test_get_property_annotation_should_return_null_if_accessor_is_not_p assertThat(annotation).isNull(); } + + @Test + public void getPropertyName_returns_name_from_PropertyAwareAccessor() + { + final String expectedPropertyName = "foo"; + final PropertyAwareAccessor accessor = mock(PropertyAwareAccessor.class); + when(accessor.getPropertyName()).thenReturn(expectedPropertyName); + + final DiffNode diffNode = new DiffNode(accessor, Object.class); + final String actualPropertyName = diffNode.getPropertyName(); + + assertThat(actualPropertyName).isEqualTo(expectedPropertyName); + } + + @Test + public void getPropertyName_returns_name_from_parentNode() + { + final String expectedPropertyName = "foo"; + + final PropertyAwareAccessor propertyAwareAccessor = mock(PropertyAwareAccessor.class); + when(propertyAwareAccessor.getPropertyName()).thenReturn(expectedPropertyName); + + final DiffNode parentNodeWithPropertyAwareAccessor = new DiffNode(propertyAwareAccessor, Object.class); + final DiffNode node = new DiffNode(parentNodeWithPropertyAwareAccessor, mock(Accessor.class), Object.class); + + assertThat(node.getPropertyName()).isEqualTo(expectedPropertyName); + } + + @Test + public void getPropertyName_returns_null_when_property_name_can_not_be_resolved() + { + final DiffNode node = new DiffNode(mock(Accessor.class), Object.class); + + assertThat(node.getPropertyName()).isNull(); + } + + @Test + public void isPropertyAware_returns_true() + { + final PropertyAwareAccessor propertyAwareAccessor = mock(PropertyAwareAccessor.class); + final DiffNode node = new DiffNode(propertyAwareAccessor, Object.class); + + assertThat(node.isPropertyAware()).isTrue(); + } + + @Test + public void isPropertyAware_returns_false() + { + final Accessor notAPropertyAwareAccessor = mock(Accessor.class); + + final DiffNode node = new DiffNode(notAPropertyAwareAccessor, Object.class); + + assertThat(node.isPropertyAware()).isFalse(); + } } diff --git a/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy b/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy index f5b5eb8a..8523faad 100644 --- a/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy +++ b/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy @@ -150,12 +150,12 @@ class InclusionServiceSpec extends Specification { def "isIgnored: should return 'true' when node is excluded via property name"() { given: - def accessor = Mock(PropertyAwareAccessor) - def node = new DiffNode(accessor, null) - accessor.pathElement >> new BeanPropertyElement("foo") + def propertyName = "foo" + def propertyAwareAccessor = mockPropertyAwareAccessor(propertyName) + def node = new DiffNode(propertyAwareAccessor, null) and: - inclusionService.toExclude().propertyNames("foo") + inclusionService.toExclude().propertyNames(propertyName) expect: inclusionService.isIgnored(node) == true @@ -163,17 +163,24 @@ class InclusionServiceSpec extends Specification { def "isIgnored: should return 'false' when node is included via property name"() { given: - def accessor = Mock(PropertyAwareAccessor) - def node = new DiffNode(accessor, null) - accessor.pathElement >> new BeanPropertyElement("foo") + def propertyName = "foo" + def propertyAwareAccessor = mockPropertyAwareAccessor(propertyName) + def node = new DiffNode(propertyAwareAccessor, null) and: - inclusionService.toInclude().propertyNames("foo") + inclusionService.toInclude().propertyNames(propertyName) expect: inclusionService.isIgnored(node) == false } + def mockPropertyAwareAccessor(String name) { + def propertyAwareAccessor = Mock(PropertyAwareAccessor) + propertyAwareAccessor.pathElement >> new BeanPropertyElement(name) + propertyAwareAccessor.propertyName >> name + propertyAwareAccessor + } + def "isIgnored: should return 'false' for children of included nodes"() { given: inclusionService.toInclude().nodes(NodePath.buildRootPath()) From 1173de37080996827c76a93c99c95842ffcd1157 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sun, 9 Mar 2014 01:31:05 +0100 Subject: [PATCH 010/215] Removed getChildren from DiffNode because it is not really needed --- .../diff/InclusionAndExclusionITCase.groovy | 17 +++++----- .../example/CanonicalAccessorExample.java | 12 +++---- .../example/phonebook/PhoneBookITCase.java | 2 +- .../diff/issues/issue3/IgnoreTest.groovy | 4 +-- .../java/de/danielbechler/diff/DiffNode.java | 18 ++++------- .../diff/visitor/PrintingVisitor.java | 2 +- .../de/danielbechler/diff/DiffNodeTest.java | 4 ++- .../diff/bean/BeanDifferShould.java | 32 +++++++++++++------ .../diff/helper/NodeAssertions.java | 4 +-- 9 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy index 4a2ee45f..6ac00191 100644 --- a/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy +++ b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy @@ -18,7 +18,6 @@ package de.danielbechler.diff import de.danielbechler.diff.annotation.ObjectDiffProperty import de.danielbechler.diff.collection.CollectionItemElement -import de.danielbechler.diff.visitor.NodeHierarchyVisitor import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import spock.lang.Specification @@ -59,14 +58,14 @@ class InclusionAndExclusionITCase extends Specification { and: "only two contacts should have changed" node.getChild('contacts').changed - node.getChild('contacts').children.size() == 2 + node.getChild('contacts').childCount() == 2 and: "only Georges name should have changed" - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))).children.size() == 1 + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))).childCount() == 1 node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))).getChild('name').changed and: "only Kramers number should have changed" - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))).children.size() == 1 + node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))).childCount() == 1 node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))).getChild('number').changed } @@ -138,13 +137,15 @@ class InclusionAndExclusionITCase extends Specification { when: def node = builder.build().compare(working, base) then: - node.visit(new NodeHierarchyVisitor()) node.getChild("name").changed - node.getChild("contacts").getChild(new CollectionItemElement(new Contact(id: "george"))).getChild("name").changed + and: + def element = new CollectionItemElement(new Contact(id: "george")) + node.getChild("contacts").childCount() == 1 + node.getChild("contacts").getChild(element).getChild("name").changed } @EqualsAndHashCode - @ToString + @ToString(includePackage = false) class PhoneBook { def name def contacts = [] @@ -162,7 +163,7 @@ class InclusionAndExclusionITCase extends Specification { } @EqualsAndHashCode(includes = ["id"]) - @ToString + @ToString(includePackage = false) class Contact { def id def name diff --git a/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java b/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java index 3879bda8..049efce5 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java @@ -18,6 +18,7 @@ import de.danielbechler.diff.DiffNode; import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.collection.CollectionItemElement; import de.danielbechler.diff.visitor.NodeHierarchyVisitor; import java.util.LinkedList; @@ -47,9 +48,9 @@ public static void main(final String[] args) final DiffNode rootNode = ObjectDifferBuilder.buildDefault() .compare(workingAddressBook, baseAddressBook); - final DiffNode contactsNode = getFirstChildOf(rootNode); - final DiffNode contactNode = getFirstChildOf(contactsNode); - final DiffNode nicknameNode = getFirstChildOf(contactNode); + final DiffNode contactsNode = rootNode.getChild("contacts"); + final DiffNode contactNode = contactsNode.getChild(new CollectionItemElement(baseContact)); + final DiffNode nicknameNode = contactNode.getChild("nickname"); rootNode.visit(new NodeHierarchyVisitor()); @@ -74,11 +75,6 @@ public static void main(final String[] args) assert nicknameNode.get(contact) == nicknameNode.canonicalGet(workingAddressBook); } - private static DiffNode getFirstChildOf(final DiffNode rootNode) - { - return rootNode.getChildren().iterator().next(); - } - public static class AddressBook { private List contacts = new LinkedList(); diff --git a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java index 8fab4cdb..8ea62f8f 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java @@ -55,7 +55,7 @@ public void testContactMiddleNameChanges() assertThat(node.hasChanges(), is(true)); assertThat(node.hasChildren(), is(true)); - assertThat(node.getChildren().size(), is(1)); + assertThat(node.childCount(), is(1)); final DiffNode contactsNode = node.getChild("contacts"); assertThat(contactsNode, IsNull.notNullValue()); diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue3/IgnoreTest.groovy b/src/integration-test/java/de/danielbechler/diff/issues/issue3/IgnoreTest.groovy index ed2fd5dc..779876b4 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue3/IgnoreTest.groovy +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue3/IgnoreTest.groovy @@ -40,9 +40,9 @@ class IgnoreTest extends Specification { def result = objectDiffer.compare(working, base) then: "excluded properties should not have been diffed" - result.children.size() == 1 + result.childCount() == 1 result.getChild("barContainer") != null - result.getChild("barContainer").children.size() == 1 + result.getChild("barContainer").childCount() == 1 result.getChild("barContainer").getChild("test") != null } diff --git a/src/main/java/de/danielbechler/diff/DiffNode.java b/src/main/java/de/danielbechler/diff/DiffNode.java index 91d2ce4f..e019476f 100644 --- a/src/main/java/de/danielbechler/diff/DiffNode.java +++ b/src/main/java/de/danielbechler/diff/DiffNode.java @@ -21,7 +21,6 @@ import de.danielbechler.util.Assert; import java.lang.annotation.Annotation; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -221,12 +220,9 @@ public boolean hasChildren() return !children.isEmpty(); } - /** - * @return The child nodes of this node. - */ - public Collection getChildren() + public int childCount() { - return children.values(); + return children.size(); } /** @@ -352,7 +348,7 @@ protected final void visit(final Visitor visitor, final Visit visit) */ public final void visitChildren(final Visitor visitor) { - for (final DiffNode child : getChildren()) + for (final DiffNode child : children.values()) { try { @@ -545,13 +541,13 @@ public String toString() { sb.append(", type=").append(getValueType().getCanonicalName()); } - if (getChildren().size() == 1) + if (childCount() == 1) { - sb.append(", ").append(getChildren().size()).append(" child"); + sb.append(", ").append(childCount()).append(" child"); } - else if (getChildren().size() > 1) + else if (childCount() > 1) { - sb.append(", ").append(getChildren().size()).append(" children"); + sb.append(", ").append(childCount()).append(" children"); } else { diff --git a/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java b/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java index 9a3e0be1..e2574746 100644 --- a/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/PrintingVisitor.java @@ -47,7 +47,7 @@ public void accept(final DiffNode node, final Visit visit) protected boolean filter(final DiffNode node) { return (node.isRootNode() && !node.hasChanges()) - || (node.hasChanges() && node.getChildren().isEmpty()); + || (node.hasChanges() && !node.hasChildren()); } protected void print(final String text) diff --git a/src/test/java/de/danielbechler/diff/DiffNodeTest.java b/src/test/java/de/danielbechler/diff/DiffNodeTest.java index 2500c2c2..c0c3d112 100644 --- a/src/test/java/de/danielbechler/diff/DiffNodeTest.java +++ b/src/test/java/de/danielbechler/diff/DiffNodeTest.java @@ -19,6 +19,7 @@ import de.danielbechler.diff.bean.BeanPropertyAccessor; import de.danielbechler.diff.bean.BeanPropertyElement; import de.danielbechler.diff.collection.CollectionItemAccessor; +import de.danielbechler.diff.collection.CollectionItemElement; import de.danielbechler.diff.helper.NodeAssertions; import de.danielbechler.diff.mock.ObjectDiffTest; import org.fest.assertions.api.Assertions; @@ -162,7 +163,8 @@ public void addChild_establishes_parent_child_relationship() throws Exception final DiffNode node1 = new DiffNode(List.class); final DiffNode node2 = new DiffNode(new CollectionItemAccessor("foo"), String.class); node1.addChild(node2); - assertThat(node1.getChildren()).containsOnly(node2); + assertThat(node1.childCount()).isEqualTo(1); + assertThat(node1.getChild(new CollectionItemElement("foo"))).isSameAs(node2); assertThat(node2.getParentNode()).isSameAs(node1); } diff --git a/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java b/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java index b1e7a300..4ad3367f 100644 --- a/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java +++ b/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java @@ -16,14 +16,28 @@ package de.danielbechler.diff.bean; -import de.danielbechler.diff.*; -import org.fest.assertions.api.*; -import org.mockito.*; -import org.mockito.invocation.*; -import org.mockito.stubbing.*; -import org.testng.annotations.*; - -import java.util.*; +import de.danielbechler.diff.ComparisonStrategy; +import de.danielbechler.diff.ComparisonStrategyResolver; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.DifferDispatcher; +import de.danielbechler.diff.Instances; +import de.danielbechler.diff.Introspector; +import de.danielbechler.diff.IntrospectorResolver; +import de.danielbechler.diff.IsIntrospectableResolver; +import de.danielbechler.diff.IsReturnableResolver; +import de.danielbechler.diff.PropertyAwareAccessor; +import de.danielbechler.diff.RootAccessor; +import org.fest.assertions.api.Assertions; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.Collections; +import java.util.Date; +import java.util.Set; import static de.danielbechler.diff.NodePath.buildRootPath; import static de.danielbechler.diff.helper.NodeAssertions.assertThat; @@ -197,7 +211,7 @@ public void do_not_add_property_nodes_to_bean_node_if_they_are_not_returnable() final DiffNode node = beanDiffer.compare(DiffNode.ROOT, instances); - Assertions.assertThat(node.getChildren()).isEmpty(); + Assertions.assertThat(node.hasChildren()).isFalse(); } @Test(expectedExceptions = IllegalArgumentException.class) diff --git a/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java b/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java index ce1b0ab5..e3c03b4d 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java @@ -209,11 +209,11 @@ public boolean matches(final DiffNode value) { if (count == 0) { - return value == null || value.getChildren().isEmpty(); + return value == null || !value.hasChildren(); } else { - return value != null && value.getChildren().size() == count; + return value != null && value.childCount() == count; } } }; From 7542e111109e76ee5c18e638266d3f7bd90efa0d Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sun, 9 Mar 2014 20:01:03 +0100 Subject: [PATCH 011/215] Update README.md --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e4d653ca..720d29f2 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,8 @@ This library has been battle-tested in a rather big project of mine, where I use * Write some tests which show that the bug was fixed or that the feature works as expected. * Send a pull request and bug the maintainer until it gets merged and published. :) -## Buy Me a Beer +## Donate -I put a lot of effort into this project and I really enjoy working on it. However, I do all of the work in my spare time, of which I don’t have very much. Unfortunately. If you’d like to support this project, how about you buy me a beer by clicking the Pledgie button down below? Alternatively you could send me a nice tweet, write a blog post, tell your friends or star the project. I'm basically happy about everything that keeps the morale up and shows me the effort is appreciated and my time well spent. +If you’d like to show me some love with a small donation you can do so via [Gittip](https://www.gittip.com/SQiShER/) or [Bitcoin](https://blockchain.info/address/19kRmHJ4qMnYCY6rnY6kCf96Prj6WGxisk). -Click here to lend your support to: java-object-diff and make a donation at pledgie.com ! - -Bitcoin: `19kRmHJ4qMnYCY6rnY6kCf96Prj6WGxisk` +Alternatively you could send me a [nice tweet](https://twitter.com/SQiShER), start contributing, write a blog post about this project, tell your friends about it or simply [**star**](https://github.com/SQiShER/java-object-diff) the main repository. I'm pretty much happy about everything that shows me that someone out there is actually using this library and appreciates all the hard work that goes into its development. From a735d72324f699c44f39b151cc010364771e4ab9 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sun, 9 Mar 2014 20:06:07 +0100 Subject: [PATCH 012/215] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 720d29f2..7903eb94 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -[![Build Status](https://travis-ci.org/SQiShER/java-object-diff.png?branch=master)](https://travis-ci.org/SQiShER/java-object-diff) - ## Introduction `java-object-diff` is a simple, yet powerful library to find differences between Java objects. It takes two objects and generates a tree structure that represents any differences between the objects and their children. This tree can then be traversed to extract more information or apply changes to the underlying data structures. +[![Build Status](https://travis-ci.org/SQiShER/java-object-diff.png?branch=master)](https://travis-ci.org/SQiShER/java-object-diff) + ## Features * Generates an easily traversable tree structure to analyze and modify with surgical precision @@ -46,8 +46,9 @@ This library has been battle-tested in a rather big project of mine, where I use * Automatically resolve conflicts on conflicting database updates * Detect and persist only properties that were actually changed -## Contributing +## Contribute +* Make youself familiar with the [anatomy of java-object-diff](https://github.com/SQiShER/java-object-diff/wiki/The-Anatomy-of-Java-Object-Diff). * [Check for open issues](https://github.com/SQiShER/java-object-diff/issues) or open a fresh issue to start a discussion around a feature idea or a bug. There is a *Contributor Friendly* tag for issues that should be ideal for people who are not very familiar with the codebase yet. * Fork the java-object-diff repository on Github to start making your changes. * Write some tests which show that the bug was fixed or that the feature works as expected. From 9e633c79f6cd382310334c186597d0d5b6acce8e Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sun, 9 Mar 2014 20:09:10 +0100 Subject: [PATCH 013/215] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7903eb94..204c2457 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ This library has been battle-tested in a rather big project of mine, where I use * Generate Facebook-like activity streams * Visualize the differences between object versions -* Automatically resolve conflicts on conflicting database updates +* Automatically resolve conflicts on concurrent database updates * Detect and persist only properties that were actually changed ## Contribute From 7da2360676ee563294a9d69f4ee705de003a3177 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 15 Mar 2014 17:19:10 +0100 Subject: [PATCH 014/215] Added more use-case integration tests --- .../diff/InclusionAndExclusionITCase.groovy | 101 ++++++++++++++++-- .../diff/InclusionConfiguration.java | 4 + .../danielbechler/diff/InclusionService.java | 32 ++++++ 3 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy index 6ac00191..f203da0a 100644 --- a/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy +++ b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy @@ -21,12 +21,9 @@ import de.danielbechler.diff.collection.CollectionItemElement import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import spock.lang.Specification -import spock.lang.Stepwise - /** * Created by dbechler. */ -@Stepwise class InclusionAndExclusionITCase extends Specification { def builder = ObjectDifferBuilder.startBuilding() @@ -131,17 +128,103 @@ class InclusionAndExclusionITCase extends Specification { node.getChild('revision') == null } - def "Inclusion via property name"() { + def "including an element via property name"() { given: - configurable.inclusion().toInclude().propertyNames('name', 'contacts') + configurable.inclusion().toInclude().propertyNames('name') + when: def node = builder.build().compare(working, base) + then: node.getChild("name").changed - and: - def element = new CollectionItemElement(new Contact(id: "george")) - node.getChild("contacts").childCount() == 1 - node.getChild("contacts").getChild(element).getChild("name").changed + node.getChild("contacts") == null + } + + def "including an element via property name includes all its children"() { + given: + configurable.inclusion().toInclude().propertyNames('contacts') + + when: + def node = builder.build().compare(working, base) + + then: + node.getChild('contacts').changed + node.getChild("contacts").childCount() == 2 + } + + def "including an element via path includes all its children"() { + + } + + def "including an element via category"() { + def includedCategory = "representation" + + given: "the name property of the phonebook is part of our included category" + configurable.categories().ofNode(NodePath.buildWith("name")).toBe(includedCategory) + + and: "the category is included" + configurable.inclusion().toInclude().categories(includedCategory) + + when: + def node = builder.build().compare(working, base) + + then: + node.getChild('name').changed + } + + def "including an element implicitly includes its children"() { + given: + configurable.inclusion().toInclude().nodes(NodePath.buildWith('contacts')) + + when: + def node = builder.build().compare(working, base) + + then: + node.getChild('contacts').changed + node.getChild('contacts').childCount() == 2 + } + + def "including an element only works if its parent element is also included"() { +// configurable.inclusion().toInclude().propertyNames() +// configurable.inclusion().node(NodePath.buildRootPath()).toInclude().propertyNames('foo', 'bar') +//// NOTE NodePath Element => ElementSelector? +// configurable.inclusion().type(Contact).toInclude().propertyNames('foo', 'bar') +// configurable.inclusion().type(Contact).toInclude().propertyNames('foo', 'bar') + } + + def "children of included elements can be excluded"() { + + } + + def "elements can be excluded via wildcard"() { + + } + + def "elements can be excluded via exclude-all-but(x, y, ...) rule"() { + + } + + def "including an element via category only includes properties if any their parent elements is also somehow included"() { + def includedCategory = "representation" + def nodePathToKramer = NodePath.createBuilder() + .withRoot() + .withPropertyName("contacts") + .withCollectionItem(new Contact(id: "kramer")) + .build() + + given: + configurable.categories().ofNode(NodePath.buildWith("name")).toBe(includedCategory) +// configurable.categories().ofNode(NodePath.buildWith("contacts")).toBe(includedCategory) + configurable.categories().ofNode(nodePathToKramer).toBe(includedCategory) + + and: "the category is included" + configurable.inclusion().toInclude().categories(includedCategory) + + when: + def node = builder.build().compare(working, base) + + then: + node.getChild('name').changed } @EqualsAndHashCode diff --git a/src/main/java/de/danielbechler/diff/InclusionConfiguration.java b/src/main/java/de/danielbechler/diff/InclusionConfiguration.java index a0172faf..1365d175 100644 --- a/src/main/java/de/danielbechler/diff/InclusionConfiguration.java +++ b/src/main/java/de/danielbechler/diff/InclusionConfiguration.java @@ -13,6 +13,10 @@ */ public interface InclusionConfiguration { + /** + * Includes elements (and implicitly all their children) based on certain criteria, unless their parent element + * doesn't match any inclusion rules. + */ To toInclude(); To toExclude(); diff --git a/src/main/java/de/danielbechler/diff/InclusionService.java b/src/main/java/de/danielbechler/diff/InclusionService.java index accdb995..13b6b931 100644 --- a/src/main/java/de/danielbechler/diff/InclusionService.java +++ b/src/main/java/de/danielbechler/diff/InclusionService.java @@ -1,5 +1,6 @@ package de.danielbechler.diff; +import de.danielbechler.diff.bean.BeanPropertyElement; import de.danielbechler.util.Assert; import java.util.HashMap; @@ -62,6 +63,19 @@ else if (isIncludedByPropertyName(node)) } private boolean isIncludedByPropertyName(final DiffNode node) + { + if (isIncludedByOwnPropertyName(node)) + { + return true; + } + else if (isIncludedByParentPropertyName(node)) + { + return true; + } + return false; + } + + private boolean isIncludedByOwnPropertyName(final DiffNode node) { final String propertyName = node.getPropertyName(); if (propertyName != null) @@ -71,6 +85,24 @@ private boolean isIncludedByPropertyName(final DiffNode node) return false; } + private boolean isIncludedByParentPropertyName(final DiffNode node) + { + final List pathElements = node.getPath().getElements(); + for (final Element element : pathElements) + { + if (element instanceof BeanPropertyElement) + { + final BeanPropertyElement beanPropertyElement = (BeanPropertyElement) element; + final String propertyName = beanPropertyElement.getPropertyName(); + if (propertyName != null && propertyNameInclusions.get(propertyName) == INCLUDED) + { + return true; + } + } + } + return false; + } + private boolean isIncludedByCategory(final DiffNode node) { return hasCategoryWithInclusion(node, INCLUDED); From 43ff0933b44c89490ffaa64e986a59f820dbfbb6 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Fri, 28 Mar 2014 00:12:36 +0100 Subject: [PATCH 015/215] Implemented a better way to configure node inclusions --- .idea/inspectionProfiles/Project_Default.xml | 2 - ...IncludingAnElementViaNodePathITCase.groovy | 50 +++ .../diff/InclusionAndExclusionITCase.groovy | 336 ++++++++---------- .../diff/ObjectDifferITCase.java | 42 ++- .../danielbechler/diff/PhoneBookSetup.groovy | 90 +++++ .../example/CanonicalAccessorExample.java | 4 +- .../EqualsOnlyValueProviderMethodExample.java | 2 +- .../diff/example/Example2ITSpec.groovy | 2 +- .../diff/example/IgnoreExample.java | 16 +- .../example/MapEntryValueAccessExample.java | 4 +- .../example/phonebook/PhoneBookITCase.java | 6 +- ...DeepDiffingCollectionItemChangeITCase.java | 12 +- .../diff/integration/IgnoreITCase.java | 21 +- .../CircularReferenceIntegrationTest.java | 2 +- .../diff/issues/issue15/GraphITCase.java | 331 ++++++++--------- .../diff/issues/issue43/Issue43ITCase.java | 31 +- .../diff/issues/issue70/PersonDiffITCase.java | 15 +- .../java/de/danielbechler/diff/Accessor.java | 2 +- .../diff/AnyElementSelector.java | 45 +++ .../de/danielbechler/diff/ConfigNode.java | 237 ++++++++++++ .../java/de/danielbechler/diff/DiffNode.java | 31 +- .../{Element.java => ElementSelector.java} | 4 +- .../diff/InclusionConfiguration.java | 2 +- .../danielbechler/diff/InclusionService.java | 40 +-- .../java/de/danielbechler/diff/Instances.java | 58 +-- .../java/de/danielbechler/diff/NodePath.java | 209 ++++++----- .../diff/NodePathValueHolder.java | 96 ++++- .../de/danielbechler/diff/RootAccessor.java | 4 +- ...tElement.java => RootElementSelector.java} | 8 +- .../diff/bean/BeanPropertyAccessor.java | 4 +- ....java => BeanPropertyElementSelector.java} | 6 +- .../collection/CollectionItemAccessor.java | 36 +- ...ava => CollectionItemElementSelector.java} | 8 +- .../diff/map/MapEntryAccessor.java | 8 +- ...lement.java => MapKeyElementSelector.java} | 8 +- .../diff/visitor/PropertyVisitor.java | 2 +- .../diff/CategoryServiceTest.groovy | 8 +- .../diff/CircularReferenceDetectorTest.java | 19 +- .../danielbechler/diff/ConfigNodeTest.groovy | 189 ++++++++++ .../de/danielbechler/diff/DiffNodeTest.java | 13 +- .../diff/DifferDispatcherShould.java | 6 +- .../diff/InclusionServiceSpec.groovy | 16 +- .../diff/IntrospectionServiceTest.groovy | 2 +- .../de/danielbechler/diff/ListDiffSpec.groovy | 6 +- .../diff/NodePathBuilderTest.java | 65 ++-- .../de/danielbechler/diff/NodePathTest.groovy | 73 ++++ .../diff/NodePathValueHolderTest.groovy | 16 +- .../danielbechler/diff/RootAccessorTest.java | 2 +- ....groovy => RootElementSelectorTest.groovy} | 14 +- .../diff/bean/BeanDifferShould.java | 14 +- .../diff/bean/BeanPropertyAccessorShould.java | 2 +- .../bean/StandardBeanIntrospectorTest.java | 16 +- .../collection/CollectionDifferShould.java | 12 +- .../CollectionItemAccessorShould.java | 8 +- ... CollectionItemElementSelectorTest.groovy} | 22 +- .../diff/helper/NodeAssertions.java | 16 +- .../diff/helper/NodeAssertionsTest.java | 12 +- .../diff/helper/NodeMatchers.java | 6 +- .../diff/map/MapEntryAccessorTest.groovy | 2 +- ...roovy => MapKeyElementSelectorTest.groovy} | 24 +- .../diff/primitive/PrimitiveDifferTest.java | 2 +- .../visitor/ToMapPrintingVisitorTest.java | 14 +- 62 files changed, 1503 insertions(+), 850 deletions(-) create mode 100644 src/integration-test/java/de/danielbechler/diff/IncludingAnElementViaNodePathITCase.groovy create mode 100644 src/integration-test/java/de/danielbechler/diff/PhoneBookSetup.groovy create mode 100644 src/main/java/de/danielbechler/diff/AnyElementSelector.java create mode 100644 src/main/java/de/danielbechler/diff/ConfigNode.java rename src/main/java/de/danielbechler/diff/{Element.java => ElementSelector.java} (96%) rename src/main/java/de/danielbechler/diff/{RootElement.java => RootElementSelector.java} (82%) rename src/main/java/de/danielbechler/diff/bean/{BeanPropertyElement.java => BeanPropertyElementSelector.java} (86%) rename src/main/java/de/danielbechler/diff/collection/{CollectionItemElement.java => CollectionItemElementSelector.java} (85%) rename src/main/java/de/danielbechler/diff/map/{MapKeyElement.java => MapKeyElementSelector.java} (86%) create mode 100644 src/test/java/de/danielbechler/diff/ConfigNodeTest.groovy create mode 100644 src/test/java/de/danielbechler/diff/NodePathTest.groovy rename src/test/java/de/danielbechler/diff/{RootElementTest.groovy => RootElementSelectorTest.groovy} (72%) rename src/test/java/de/danielbechler/diff/collection/{CollectionItemElementTest.groovy => CollectionItemElementSelectorTest.groovy} (59%) rename src/test/java/de/danielbechler/diff/map/{MapKeyElementTest.groovy => MapKeyElementSelectorTest.groovy} (75%) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 1a530b1f..c3d8c7ae 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -128,8 +128,6 @@ diff --git a/src/integration-test/java/de/danielbechler/diff/IncludingAnElementViaNodePathITCase.groovy b/src/integration-test/java/de/danielbechler/diff/IncludingAnElementViaNodePathITCase.groovy new file mode 100644 index 00000000..62033555 --- /dev/null +++ b/src/integration-test/java/de/danielbechler/diff/IncludingAnElementViaNodePathITCase.groovy @@ -0,0 +1,50 @@ +package de.danielbechler.diff + +import spock.lang.Specification + +import static de.danielbechler.diff.PhoneBookSetup.Contact + +/** + * Created by Daniel Bechler. + */ +class IncludingAnElementViaNodePathITCase extends Specification { + + def builder = ObjectDifferBuilder.startBuilding() + def configurable = builder.configure() + def working = PhoneBookSetup.getWorking() + def base = PhoneBookSetup.getBase() + def pathToContacts = NodePath.startBuilding().propertyName('contacts').build() + def pathToContactKramer = NodePath.startBuildingFrom(pathToContacts).collectionItem(new Contact(id: 'kramer')).build() + def pathToContactGeorge = NodePath.startBuildingFrom(pathToContacts).collectionItem(new Contact(id: 'george')).build() + + def 'should include its children as well'() { + def includedNode = NodePath.with('contacts') + + given: + configurable.inclusion().toInclude().node(includedNode) + + when: + def node = builder.build().compare(working, base) + + then: + node.getChild(includedNode).childCount() == 2 + } + + def 'should include its parents but not their children'() { + given: + configurable.inclusion().toInclude().node(pathToContactKramer) + + when: + def node = builder.build().compare(working, base) + + then: "kramer should be included, because he has been explicitly included" + node.getChild(pathToContactKramer).changed + + and: "george should be excluded, since he has not been explicitly included" + node.getChild(pathToContactGeorge) == null + } + + def 'ideas'() { + configurable.inclusion().toInclude().node(pathToContactKramer) + } +} diff --git a/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy index f203da0a..054c633f 100644 --- a/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy +++ b/src/integration-test/java/de/danielbechler/diff/InclusionAndExclusionITCase.groovy @@ -16,250 +16,206 @@ package de.danielbechler.diff -import de.danielbechler.diff.annotation.ObjectDiffProperty -import de.danielbechler.diff.collection.CollectionItemElement -import groovy.transform.EqualsAndHashCode -import groovy.transform.ToString +import de.danielbechler.diff.collection.CollectionItemElementSelector +import de.danielbechler.diff.visitor.PrintingVisitor import spock.lang.Specification + +import static de.danielbechler.diff.PhoneBookSetup.Contact + /** * Created by dbechler. */ class InclusionAndExclusionITCase extends Specification { - def builder = ObjectDifferBuilder.startBuilding() - def configurable = builder.configure() - - def base = new PhoneBook(name: "Jerry's Phone Book", revision: 1, contacts: [ - new Contact(id: "elaine", name: "Elaine", number: "917-555-0186"), - new Contact(id: "george", name: "George", number: "917-555-0102"), - new Contact(id: "kramer", name: "Kramer", number: "917-555-3456") - ]) - - // Jerry decided to give his phone book a more formal title - def working = new PhoneBook(name: "Jerry Seinfeld's Phone Book", revision: 2, contacts: [ - new Contact(id: "elaine", name: "Elaine", number: "917-555-0186"), - - // George has a new nickname - new Contact(id: "george", name: "Koko", number: "917-555-0102"), - - // Jerry always ended up at the Moviefone hotline until he realized Kramers new number actually ends with 5, not 6 - new Contact(id: "kramer", name: "Kramer", number: "917-555-3455") - ]) - - def "Sanity check"() { - when: - def node = ObjectDifferBuilder.buildDefault().compare(working, base) - - then: - node.getChild('name').changed - - and: "only two contacts should have changed" - node.getChild('contacts').changed - node.getChild('contacts').childCount() == 2 - - and: "only Georges name should have changed" - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))).childCount() == 1 - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))).getChild('name').changed + def builder = ObjectDifferBuilder.startBuilding() + def configurable = builder.configure() + def working = PhoneBookSetup.getWorking() + def base = PhoneBookSetup.getBase() - and: "only Kramers number should have changed" - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))).childCount() == 1 - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))).getChild('number').changed - } + def "Sanity check"() { + when: + def node = ObjectDifferBuilder.buildDefault().compare(working, base) - def "Property with specific name excluded via configuration"() { - given: - configurable.inclusion().toExclude().propertyNames("name") + then: + node.getChild('name').changed - when: - def node = builder.build().compare(working, base) + and: "only two contacts should have changed" + node.getChild('contacts').changed + node.getChild('contacts').childCount() == 2 - then: "Georges name change should be ignored" - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))) == null + and: "only Georges name should have changed" + node.getChild('contacts').getChild(new CollectionItemElementSelector(new Contact(id: 'george'))).childCount() == 1 + node.getChild('contacts').getChild(new CollectionItemElementSelector(new Contact(id: 'george'))).getChild('name').changed - and: "The name change of the phone book should be ignored too" - node.getChild('name') == null - } + and: "only Kramers number should have changed" + node.getChild('contacts').getChild(new CollectionItemElementSelector(new Contact(id: 'kramer'))).childCount() == 1 + node.getChild('contacts').getChild(new CollectionItemElementSelector(new Contact(id: 'kramer'))).getChild('number').changed + } - def "Category excluded via configuration"() { - given: - configurable.inclusion().toExclude().categories('private') + def "Property with specific name excluded via configuration"() { + given: + configurable.inclusion().toExclude().propertyNames("name") - when: - def node = builder.build().compare(working, base) + when: + def node = builder.build().compare(working, base) - then: "Kramers changed number should be ignored" - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'kramer'))) == null - } + then: "Georges name change should be ignored" + node.getChild('contacts').getChild(new CollectionItemElementSelector(new Contact(id: 'george'))) == null - def "Type excluded via configuration"() { - given: - configurable.inclusion().toExclude().types(Contact) + and: "The name change of the phone book should be ignored too" + node.getChild('name') == null + } - when: - def node = builder.build().compare(working, base) + def "Category excluded via configuration"() { + given: + configurable.inclusion().toExclude().categories('private') - then: "all contact changes should be ignored" - node.getChild('contacts') == null - } + when: + def node = builder.build().compare(working, base) - def "Node at specific NodePath excluded via configuration"() { - given: - configurable.inclusion().toExclude().nodes(NodePath.createBuilder() - .withRoot() - .withPropertyName('contacts') - .withCollectionItem(new Contact(id: 'george')) - .build()) + then: "Kramers changed number should be ignored" + node.getChild('contacts').getChild(new CollectionItemElementSelector(new Contact(id: 'kramer'))) == null + } - when: - def node = builder.build().compare(working, base) + def "Type excluded via configuration"() { + given: + configurable.inclusion().toExclude().types(Contact) - then: - node.getChild('contacts').getChild(new CollectionItemElement(new Contact(id: 'george'))) == null - } + when: + def node = builder.build().compare(working, base) - def "Property excluded via @ObjectDiffProperty annotation"() { - given: - configurable.inclusion().toExclude().nodes(NodePath.buildWith('name')) + then: "all contact changes should be ignored" + node.getChild('contacts') == null + } - when: - def node = builder.build().compare(working, base) + def "Node at specific NodePath excluded via configuration"() { + given: + configurable.inclusion().toExclude().node(NodePath + .startBuilding() + .propertyName('contacts') + .collectionItem(new Contact(id: 'george')) + .build()) - then: "the name change of the phone book should be ignored" - node.getChild('revision') == null - } + when: + def node = builder.build().compare(working, base) - def "including an element via property name"() { - given: - configurable.inclusion().toInclude().propertyNames('name') + then: + node.getChild('contacts').getChild(new CollectionItemElementSelector(new Contact(id: 'george'))) == null + } - when: - def node = builder.build().compare(working, base) + def "Property excluded via @ObjectDiffProperty annotation"() { + given: + configurable.inclusion().toExclude().node(NodePath.with('name')) - then: - node.getChild("name").changed - node.getChild("contacts") == null - } + when: + def node = builder.build().compare(working, base) - def "including an element via property name includes all its children"() { - given: - configurable.inclusion().toInclude().propertyNames('contacts') + then: "the name change of the phone book should be ignored" + node.getChild('revision') == null + } - when: - def node = builder.build().compare(working, base) + def "including an element via property name"() { + given: + configurable.inclusion().toInclude().propertyNames('name') - then: - node.getChild('contacts').changed - node.getChild("contacts").childCount() == 2 - } + when: + def node = builder.build().compare(working, base) - def "including an element via path includes all its children"() { + then: + node.getChild("name").changed + node.getChild("contacts") == null + } - } + def "including an element via property name includes all its children"() { + given: + configurable.inclusion().toInclude().propertyNames('contacts') - def "including an element via category"() { - def includedCategory = "representation" + when: + def node = builder.build().compare(working, base) - given: "the name property of the phonebook is part of our included category" - configurable.categories().ofNode(NodePath.buildWith("name")).toBe(includedCategory) + then: + node.getChild('contacts').changed + node.getChild("contacts").childCount() == 2 + } - and: "the category is included" - configurable.inclusion().toInclude().categories(includedCategory) + def "including an element via path includes all its children"() { - when: - def node = builder.build().compare(working, base) + } - then: - node.getChild('name').changed - } + def "including an element via category"() { + def includedCategory = "representation" - def "including an element implicitly includes its children"() { - given: - configurable.inclusion().toInclude().nodes(NodePath.buildWith('contacts')) + given: "the name property of the phonebook is part of our included category" + configurable.categories().ofNode(NodePath.with("name")).toBe(includedCategory) - when: - def node = builder.build().compare(working, base) + and: "the category is included" + configurable.inclusion().toInclude().categories(includedCategory) - then: - node.getChild('contacts').changed - node.getChild('contacts').childCount() == 2 - } + when: + def node = builder.build().compare(working, base) - def "including an element only works if its parent element is also included"() { -// configurable.inclusion().toInclude().propertyNames() -// configurable.inclusion().node(NodePath.buildRootPath()).toInclude().propertyNames('foo', 'bar') -//// NOTE NodePath Element => ElementSelector? -// configurable.inclusion().type(Contact).toInclude().propertyNames('foo', 'bar') -// configurable.inclusion().type(Contact).toInclude().propertyNames('foo', 'bar') - } + then: + node.getChild('name').changed + } - def "children of included elements can be excluded"() { + def "including an element implicitly includes its children"() { + given: + configurable.inclusion().toInclude().node(NodePath.with('contacts')) - } + when: + def node = builder.build().compare(working, base) - def "elements can be excluded via wildcard"() { + then: + node.getChild('contacts').changed + node.getChild('contacts').childCount() == 2 + } - } + def "include all but some specific elements"() { + given: + configurable.inclusion().toInclude().node(NodePath.startBuilding().propertyName('contacts').any().build()) + configurable.inclusion().toExclude().node(NodePath.startBuilding().propertyName('contacts').collectionItem(new Contact(id: 'kramer')).build()) - def "elements can be excluded via exclude-all-but(x, y, ...) rule"() { + when: + def node = builder.build().compare(working, base) - } + and: + node.visit(new PrintingVisitor(working, base)) - def "including an element via category only includes properties if any their parent elements is also somehow included"() { - def includedCategory = "representation" - def nodePathToKramer = NodePath.createBuilder() - .withRoot() - .withPropertyName("contacts") - .withCollectionItem(new Contact(id: "kramer")) - .build() + then: + node.getChild('contacts').changed + node.getChild('contacts').childCount() == 1 + } - given: - configurable.categories().ofNode(NodePath.buildWith("name")).toBe(includedCategory) -// configurable.categories().ofNode(NodePath.buildWith("contacts")).toBe(includedCategory) - configurable.categories().ofNode(nodePathToKramer).toBe(includedCategory) + def "including an element via category only includes properties if any of their parent elements is also somehow included"() { + def includedCategory = "representation" + def nodePathToKramer = NodePath.startBuilding() + .propertyName("contacts") + .collectionItem(new Contact(id: "kramer")) + .build() - and: "the category is included" - configurable.inclusion().toInclude().categories(includedCategory) + given: + configurable.categories().ofNode(NodePath.with("name")).toBe(includedCategory) + configurable.categories().ofNode(nodePathToKramer).toBe(includedCategory) - when: - def node = builder.build().compare(working, base) + and: "the category is included" + configurable.inclusion().toInclude().categories(includedCategory) - then: - node.getChild('name').changed - } + when: + def node = builder.build().compare(working, base) - @EqualsAndHashCode - @ToString(includePackage = false) - class PhoneBook { - def name - def contacts = [] - def revision + then: + node.getChild('name').changed + } - @SuppressWarnings("GroovyUnusedDeclaration") - @ObjectDiffProperty(excluded = true) - def getRevision() { - return revision - } + def "including an element only works if its parent element is also included"() { + } - void setRevision(revision) { - this.revision = revision - } - } + def "children of included elements can be excluded"() { + } - @EqualsAndHashCode(includes = ["id"]) - @ToString(includePackage = false) - class Contact { - def id - def name - def number + def "elements can be excluded via wildcard"() { + } - @SuppressWarnings("GroovyUnusedDeclaration") - @ObjectDiffProperty(categories = ['private']) - def getNumber() { - return number - } - - void setNumber(number) { - this.number = number - } - } + def "elements can be excluded via exclude-all-but(x, y, ...) rule"() { + } } diff --git a/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java b/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java index 90680cba..3d45253e 100644 --- a/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/ObjectDifferITCase.java @@ -16,10 +16,10 @@ package de.danielbechler.diff; -import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.collection.CollectionItemElementSelector; import de.danielbechler.diff.helper.NodeAssertions; import de.danielbechler.diff.helper.TestGroups; -import de.danielbechler.diff.map.MapKeyElement; +import de.danielbechler.diff.map.MapKeyElementSelector; import de.danielbechler.diff.mock.ObjectWithAccessTrackingIgnoredProperty; import de.danielbechler.diff.mock.ObjectWithCollection; import de.danielbechler.diff.mock.ObjectWithHashCodeAndEquals; @@ -82,7 +82,7 @@ public void testCompareCollectionWithIgnoredCollectionProperty() final ObjectDifferBuilder objectDifferBuilder = ObjectDifferBuilder.startBuilding(); objectDifferBuilder.configure().inclusion() .toExclude() - .nodes(NodePath.buildWith("collection")); + .node(NodePath.with("collection")); objectDiffer = objectDifferBuilder.build(); final DiffNode node = objectDiffer.compare(working, new ObjectWithCollection()); @@ -99,7 +99,7 @@ public void testCompareCollectionWithAddedItem() throws Exception final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); - NodeAssertions.assertThat(node).child(new CollectionItemElement("foo")).hasState(DiffNode.State.ADDED); + NodeAssertions.assertThat(node).child(new CollectionItemElementSelector("foo")).hasState(DiffNode.State.ADDED); } public void testCompareCollectionWithRemovedItem() throws Exception @@ -110,7 +110,7 @@ public void testCompareCollectionWithRemovedItem() throws Exception final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); - NodeAssertions.assertThat(node).child(new CollectionItemElement("foo")).hasState(DiffNode.State.REMOVED); + NodeAssertions.assertThat(node).child(new CollectionItemElementSelector("foo")).hasState(DiffNode.State.REMOVED); } @Test(groups = TestGroups.INTEGRATION) @@ -161,7 +161,7 @@ public void testCompareCollectionWithChangedItem() throws Exception final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); assertThat(node).self().hasChanges(); - assertThat(node).child(new CollectionItemElement(new ObjectWithIdentityAndValue("foo"))) + assertThat(node).child(new CollectionItemElementSelector(new ObjectWithIdentityAndValue("foo"))) .hasState(DiffNode.State.CHANGED); } @@ -175,7 +175,7 @@ public void testWithNewMapInWorkingAndNoneInBase() final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.ADDED); - NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.ADDED); + NodeAssertions.assertThat(node).child(new MapKeyElementSelector("foo")).hasState(DiffNode.State.ADDED); } @Test(groups = TestGroups.INTEGRATION) @@ -188,7 +188,7 @@ public void testWithAdditionOfSimpleTypeToWorkingMap() final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); - NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.ADDED); + NodeAssertions.assertThat(node).child(new MapKeyElementSelector("foo")).hasState(DiffNode.State.ADDED); } @Test(groups = TestGroups.INTEGRATION) @@ -215,7 +215,7 @@ public void testWithSingleEntryAddedToWorkingMap() final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasState(DiffNode.State.REMOVED); - NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.REMOVED); + NodeAssertions.assertThat(node).child(new MapKeyElementSelector("foo")).hasState(DiffNode.State.REMOVED); } /** @@ -248,9 +248,9 @@ public void testWithSameEntries() NodeAssertions.assertThat(node).root().hasChildren(1); NodeAssertions.assertThat(node) - .child(NodePath.createBuilder().withRoot().withMapKey("foo")) + .child(NodePath.startBuilding().mapKey("foo")) .doesNotExist(); - NodeAssertions.assertThat(node).child(NodePath.createBuilder().withRoot().withMapKey("ping")) + NodeAssertions.assertThat(node).child(NodePath.startBuilding().mapKey("ping")) .hasState(DiffNode.State.ADDED) .hasNoChildren(); } @@ -266,7 +266,7 @@ public void testWithChangedEntry() final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); NodeAssertions.assertThat(node).self().hasChildren(1); - NodeAssertions.assertThat(node).child(new MapKeyElement("foo")).hasState(DiffNode.State.CHANGED); + NodeAssertions.assertThat(node).child(new MapKeyElementSelector("foo")).hasState(DiffNode.State.CHANGED); } @Test(groups = TestGroups.INTEGRATION) @@ -298,7 +298,7 @@ public void testCompareWithDifferentTypes() public void testCompareWithIgnoredProperty() { final ObjectDifferBuilder objectDifferBuilder = ObjectDifferBuilder.startBuilding(); - objectDifferBuilder.configure().inclusion().toExclude().nodes(NodePath.buildRootPath()); + objectDifferBuilder.configure().inclusion().toExclude().node(NodePath.withRoot()); objectDiffer = objectDifferBuilder.build(); final DiffNode node = objectDiffer.compare("foo", "bar"); @@ -330,9 +330,8 @@ public void testCompareWithListContainingObjectTwiceDetectsIfOneGetsRemoved() th final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); node.visit(new NodeHierarchyVisitor()); assertThat(node) - .child(NodePath.createBuilder() - .withRoot() - .withCollectionItem(new ObjectWithHashCodeAndEquals("foo")) + .child(NodePath.startBuilding() + .collectionItem(new ObjectWithHashCodeAndEquals("foo")) .build()) .hasState(DiffNode.State.REMOVED); } @@ -344,9 +343,8 @@ public void testCompareWithListContainingObjectOnceDetectsIfAnotherInstanceOfItG final List working = asList(new ObjectWithHashCodeAndEquals("foo"), new ObjectWithHashCodeAndEquals("foo")); final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); node.visit(new NodeHierarchyVisitor()); - assertThat(node).child(NodePath.createBuilder() - .withRoot() - .withCollectionItem(new ObjectWithHashCodeAndEquals("foo")) + assertThat(node).child(NodePath.startBuilding() + .collectionItem(new ObjectWithHashCodeAndEquals("foo")) .build()).hasState(DiffNode.State.ADDED); } @@ -379,7 +377,7 @@ public void testCompareBeanWithEqualOnlyValueProviderMethodOnGetCollectionProper NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); assertThat(node) - .child(NodePath.buildWith("collection")) + .child(NodePath.with("collection")) .hasState(DiffNode.State.CHANGED); } @@ -411,7 +409,7 @@ public void testCompareBeanWithEqualsOnlyValueProviderMethodOnGetObjectPropertyW NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); assertThat(node) - .child(NodePath.buildWith("object")) + .child(NodePath.with("object")) .hasState(DiffNode.State.CHANGED); } @@ -444,7 +442,7 @@ public void testCompareBeanWithEqualsOnlyValueProviderMethodOnGetMapPropertyWith NodeAssertions.assertThat(node).self().hasState(DiffNode.State.CHANGED); assertThat(node) - .child(NodePath.buildWith("map")) + .child(NodePath.with("map")) .hasState(DiffNode.State.CHANGED); } } diff --git a/src/integration-test/java/de/danielbechler/diff/PhoneBookSetup.groovy b/src/integration-test/java/de/danielbechler/diff/PhoneBookSetup.groovy new file mode 100644 index 00000000..2956739e --- /dev/null +++ b/src/integration-test/java/de/danielbechler/diff/PhoneBookSetup.groovy @@ -0,0 +1,90 @@ +/* + * Copyright 2014 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 + +import de.danielbechler.diff.annotation.ObjectDiffProperty +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +/** + * Created by Daniel Bechler. + */ +public class PhoneBookSetup { + + static def getBase() { + return new PhoneBook( + name: "Jerry's Phone Book", + revision: 1, + contacts: [ + new Contact(id: "elaine", name: "Elaine", number: "917-555-0186"), + new Contact(id: "george", name: "George", number: "917-555-0102"), + new Contact(id: "kramer", name: "Kramer", number: "917-555-3456") + ]) + } + + static def getWorking() { + return new PhoneBook( + // Jerry decided to give his phone book a more formal title + name: "Jerry Seinfeld's Phone Book", + revision: 2, + contacts: [ + new Contact(id: "elaine", name: "Elaine", number: "917-555-0186"), + + // George has a new nickname + new Contact(id: "george", name: "Koko", number: "917-555-0102"), + + // Jerry always ended up at the Moviefone hotline until he realized Kramers new number actually ends with 5, not 6 + new Contact(id: "kramer", name: "Kramer", number: "917-555-3455") + ]) + } + + @EqualsAndHashCode + @ToString(includePackage = false) + public static class PhoneBook { + def name + def contacts = [] + def revision + + @SuppressWarnings("GroovyUnusedDeclaration") + @ObjectDiffProperty(excluded = true) + def getRevision() { + return revision + } + + void setRevision(revision) { + this.revision = revision + } + } + + @EqualsAndHashCode(includes = ["id"]) + @ToString(includePackage = false) + public static class Contact { + def id + def name + def number + + @SuppressWarnings("GroovyUnusedDeclaration") + @ObjectDiffProperty(categories = ['private']) + def getNumber() { + return number + } + + void setNumber(number) { + this.number = number + } + } +} diff --git a/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java b/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java index 049efce5..8508a035 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/CanonicalAccessorExample.java @@ -18,7 +18,7 @@ import de.danielbechler.diff.DiffNode; import de.danielbechler.diff.ObjectDifferBuilder; -import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.collection.CollectionItemElementSelector; import de.danielbechler.diff.visitor.NodeHierarchyVisitor; import java.util.LinkedList; @@ -49,7 +49,7 @@ public static void main(final String[] args) final DiffNode rootNode = ObjectDifferBuilder.buildDefault() .compare(workingAddressBook, baseAddressBook); final DiffNode contactsNode = rootNode.getChild("contacts"); - final DiffNode contactNode = contactsNode.getChild(new CollectionItemElement(baseContact)); + final DiffNode contactNode = contactsNode.getChild(new CollectionItemElementSelector(baseContact)); final DiffNode nicknameNode = contactNode.getChild("nickname"); rootNode.visit(new NodeHierarchyVisitor()); diff --git a/src/integration-test/java/de/danielbechler/diff/example/EqualsOnlyValueProviderMethodExample.java b/src/integration-test/java/de/danielbechler/diff/example/EqualsOnlyValueProviderMethodExample.java index c8cff95a..08bf9980 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/EqualsOnlyValueProviderMethodExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/EqualsOnlyValueProviderMethodExample.java @@ -20,7 +20,7 @@ public static void main(final String[] args) // (Option 1) Causes the ObjectDiffer to compare using the method "getProp1" on the 'prop' property of the root object builder.configure().comparison() - .ofNode(NodePath.buildWith("prop")) + .ofNode(NodePath.with("prop")) .toUseEqualsMethodOfValueProvidedByMethod("getProp1"); final DiffNode node = builder.build().compare(working, base); diff --git a/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy b/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy index 713ec34d..82898d11 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy +++ b/src/integration-test/java/de/danielbechler/diff/example/Example2ITSpec.groovy @@ -50,7 +50,7 @@ class Example2ITSpec extends Specification { def "Comparing an object via equalsOnlyValueProvider (via configuration using property path)"() { given: def builder = ObjectDifferBuilder.startBuilding() - builder.configure().comparison().ofNode(NodePath.buildRootPath()).toUseEqualsMethodOfValueProvidedByMethod("presentationForDiffer") + builder.configure().comparison().ofNode(NodePath.withRoot()).toUseEqualsMethodOfValueProvidedByMethod("presentationForDiffer") def base = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Walt", "White") def working = new PersonWithEqualsOnlyTypeAnnotation("Walter", "Heisenberg", "White") diff --git a/src/integration-test/java/de/danielbechler/diff/example/IgnoreExample.java b/src/integration-test/java/de/danielbechler/diff/example/IgnoreExample.java index 85db2c3d..cde8ce10 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/IgnoreExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/IgnoreExample.java @@ -1,10 +1,14 @@ package de.danielbechler.diff.example; -import de.danielbechler.diff.*; -import de.danielbechler.diff.annotation.*; -import de.danielbechler.diff.visitor.*; - -/** @author Daniel Bechler */ +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.annotation.ObjectDiffProperty; +import de.danielbechler.diff.visitor.PrintingVisitor; + +/** + * @author Daniel Bechler + */ @SuppressWarnings("UnusedDeclaration") class IgnoreExample { @@ -20,7 +24,7 @@ public static void main(final String[] args) final ObjectDifferBuilder builder = ObjectDifferBuilder.startBuilding(); // (Option 1) Causes the ObjectDiffer to ignore the 'password' property of the root object - builder.configure().inclusion().toExclude().nodes(NodePath.buildWith("password")); + builder.configure().inclusion().toExclude().node(NodePath.with("password")); final DiffNode node = builder.build().compare(working, base); diff --git a/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java b/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java index 69bac2e2..66a3415e 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java +++ b/src/integration-test/java/de/danielbechler/diff/example/MapEntryValueAccessExample.java @@ -19,7 +19,7 @@ import de.danielbechler.diff.DiffNode; import de.danielbechler.diff.ObjectDifferBuilder; import de.danielbechler.diff.Visit; -import de.danielbechler.diff.map.MapKeyElement; +import de.danielbechler.diff.map.MapKeyElementSelector; import java.util.Map; import java.util.TreeMap; @@ -49,7 +49,7 @@ public static void main(final String[] args) { public void accept(final DiffNode node, final Visit visit) { - final Object key = ((MapKeyElement) node.getPathElement()).getKey(); + final Object key = ((MapKeyElementSelector) node.getElementSelector()).getKey(); // ^^^ I do not encourage this, but currently it's the only way final Object value = node.canonicalGet(working); System.out.println(key + " => " + value); diff --git a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java index 8ea62f8f..b5be74aa 100644 --- a/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/example/phonebook/PhoneBookITCase.java @@ -19,7 +19,7 @@ import de.danielbechler.diff.DiffNode; import de.danielbechler.diff.ObjectDiffer; import de.danielbechler.diff.ObjectDifferBuilder; -import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.collection.CollectionItemElementSelector; import org.hamcrest.core.IsEqual; import org.hamcrest.core.IsNull; import org.testng.annotations.Test; @@ -61,7 +61,7 @@ public void testContactMiddleNameChanges() assertThat(contactsNode, IsNull.notNullValue()); assertThat(contactsNode.hasChanges(), is(true)); - final DiffNode pinkmanNode = contactsNode.getChild(new CollectionItemElement(jessePinkman)); + final DiffNode pinkmanNode = contactsNode.getChild(new CollectionItemElementSelector(jessePinkman)); assertThat(pinkmanNode.hasChanges(), is(true)); final DiffNode middleNameNode = pinkmanNode.getChild("middleName"); @@ -69,7 +69,7 @@ public void testContactMiddleNameChanges() assertThat(middleNameNode.canonicalGet(phoneBook), IsNull.nullValue()); assertThat((String) middleNameNode.canonicalGet(modifiedPhoneBook), IsEqual.equalTo("Bruce")); - final DiffNode whiteNode = contactsNode.getChild(new CollectionItemElement(walterWhite)); + final DiffNode whiteNode = contactsNode.getChild(new CollectionItemElementSelector(walterWhite)); assertThat(whiteNode.hasChanges(), is(true)); final DiffNode whiteMiddleNameNode = whiteNode.getChild("middleName"); diff --git a/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java b/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java index ab29e372..14a6fe9f 100644 --- a/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/integration/DeepDiffingCollectionItemChangeITCase.java @@ -48,14 +48,12 @@ public void test_returns_full_property_graph_of_added_collection_items() node.visit(new NodeHierarchyVisitor()); - assertThat(node).child(NodePath.createBuilder() - .withRoot() - .withMapKey("foo")).hasState(DiffNode.State.ADDED); + assertThat(node).child(NodePath.startBuilding() + .mapKey("foo")).hasState(DiffNode.State.ADDED); - assertThat(node).child(NodePath.createBuilder() - .withRoot() - .withMapKey("foo") - .withPropertyName("value")).hasState(DiffNode.State.ADDED); + assertThat(node).child(NodePath.startBuilding() + .mapKey("foo") + .propertyName("value")).hasState(DiffNode.State.ADDED); } @Test diff --git a/src/integration-test/java/de/danielbechler/diff/integration/IgnoreITCase.java b/src/integration-test/java/de/danielbechler/diff/integration/IgnoreITCase.java index 36832d4b..6f92b8ec 100644 --- a/src/integration-test/java/de/danielbechler/diff/integration/IgnoreITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/integration/IgnoreITCase.java @@ -16,14 +16,19 @@ package de.danielbechler.diff.integration; -import de.danielbechler.diff.*; -import de.danielbechler.diff.mock.*; -import de.danielbechler.diff.visitor.*; -import org.testng.annotations.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.mock.ObjectWithCircularReference; +import de.danielbechler.diff.visitor.PrintingVisitor; +import org.testng.annotations.Test; -import static de.danielbechler.diff.helper.NodeAssertions.*; +import static de.danielbechler.diff.helper.NodeAssertions.assertThat; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ public class IgnoreITCase { @Test @@ -41,7 +46,7 @@ public void verify_that_ignore_rules_with_complex_paths_works_properly() throws modifiedObj1.setReference(modifiedObj2); modifiedObj2.setReference(modifiedObj3); - final NodePath nodePath = NodePath.buildWith("reference", "reference"); + final NodePath nodePath = NodePath.with("reference", "reference"); // verify that the node can be found when it's not excluded ObjectDiffer objectDiffer = ObjectDifferBuilder.startBuilding().build(); @@ -51,7 +56,7 @@ public void verify_that_ignore_rules_with_complex_paths_works_properly() throws // verify that the node can't be found, when it's excluded final ObjectDifferBuilder objectDifferBuilder = ObjectDifferBuilder.startBuilding(); - objectDifferBuilder.configure().inclusion().toExclude().nodes(nodePath); + objectDifferBuilder.configure().inclusion().toExclude().node(nodePath); objectDiffer = objectDifferBuilder.build(); final DiffNode node = objectDiffer.compare(obj1, modifiedObj1); node.visit(new PrintingVisitor(obj1, modifiedObj1)); diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java b/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java index 5d54ea95..7c9e0d14 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue15/CircularReferenceIntegrationTest.java @@ -48,7 +48,7 @@ public void testCircularReference() final DiffNode root = ObjectDifferBuilder.buildDefault().compare(workingA, baseA); assertThat(root).child("reference", "reference").isCircular(); assertThat(root).child("reference", "reference") - .hasCircularStartPathEqualTo(NodePath.buildRootPath()); + .hasCircularStartPathEqualTo(NodePath.withRoot()); Assertions.assertThat(root.canonicalGet(workingA)) .isSameAs(root.getChild("reference").getChild("reference").canonicalGet(workingA)); diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java b/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java index 813d7f44..c0686f0e 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue15/GraphITCase.java @@ -25,7 +25,7 @@ import de.danielbechler.diff.visitor.PrintingVisitor; import org.testng.annotations.Test; -import static de.danielbechler.diff.NodePath.createBuilder; +import static de.danielbechler.diff.NodePath.startBuilding; import static de.danielbechler.diff.helper.NodeAssertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat; @@ -41,6 +41,34 @@ public class GraphITCase { private static final boolean PRINT_ENABLED = true; + private static DiffNode compareAndPrint(final GraphNode modified, final GraphNode base) + { + final DiffNode root = ObjectDifferBuilder.buildDefault().compare(modified, base); + if (PRINT_ENABLED) + { + root.visit(new PrintingVisitor(modified, base)); + } + return root; + } + + private static void establishParentChildRelationship(final GraphNode parent, final GraphNode child) + { + child.setParent(parent); + parent.addChild(child); + } + + private static void establishCircularChildRelationship(final GraphNode a, final GraphNode b) + { + a.addChild(b); + b.addChild(a); + } + + private static void establishCircularDirectReference(final GraphNode a, final GraphNode b) + { + a.setDirectReference(b); + b.setDirectReference(a); + } + @Test public void basicNode() { @@ -94,15 +122,13 @@ public void basicBidirectionalWithChildren() assertThat(root).root().hasChildren(1); assertThat(root).child("children").hasChildren(2); - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a)) + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(a)) .hasState(DiffNode.State.CHANGED); - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b)) + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b)) .hasState(DiffNode.State.CHANGED); } @@ -136,50 +162,44 @@ public void basicBidirectionalWithChildrenAndMaps() * is a parent of it, we cannot reliably test, whether the node has been changed. (added or removed * is easier, but since changed is not yet possible, that would be inconsistent) */ - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("directReference") - .withPropertyName("map") - .withMapKey("node-b-again")) + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("directReference") + .propertyName("map") + .mapKey("node-b-again")) .hasState(DiffNode.State.CIRCULAR); // TODO Change circular reference detection, to allow returning the expected "REMOVED" state - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("directReference") - .withPropertyName("value")).hasState(DiffNode.State.CHANGED); - - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("map") - .withMapKey("node-a") - .withPropertyName("map") - .withMapKey("node-b-again")) + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("directReference") + .propertyName("value")).hasState(DiffNode.State.CHANGED); + + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("map") + .mapKey("node-a") + .propertyName("map") + .mapKey("node-b-again")) .hasState(DiffNode.State.CIRCULAR); // TODO Change circular reference detection, to allow returning the expected "REMOVED" state - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("map") - .withMapKey("node-a") - .withPropertyName("value")).hasState(DiffNode.State.CHANGED); - - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("value")).hasState(DiffNode.State.CHANGED); - - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("map") - .withMapKey("a")).hasState(DiffNode.State.CHANGED); + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("map") + .mapKey("node-a") + .propertyName("value")).hasState(DiffNode.State.CHANGED); + + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("value")).hasState(DiffNode.State.CHANGED); + + assertThat(root).child(startBuilding() + .propertyName("map") + .mapKey("a")).hasState(DiffNode.State.CHANGED); } @Test @@ -269,17 +289,15 @@ public void basicBidirectionalNodeWithChildNodesWithIds() final DiffNode root = compareAndPrint(modified, base); - NodeAssertions.assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a)) + NodeAssertions.assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(a)) .hasState(DiffNode.State.CHANGED) .hasChildren(1); - NodeAssertions.assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b)) + NodeAssertions.assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b)) .hasState(DiffNode.State.CHANGED) .hasChildren(1); } @@ -296,11 +314,10 @@ public void simpleGraphWithChanges() final DiffNode root = compareAndPrint(modified, base); - NodeAssertions.assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(modifiedA) - .withPropertyName("value")) + NodeAssertions.assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(modifiedA) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); } @@ -336,11 +353,10 @@ public void simpleGraphExtended() final DiffNode root = compareAndPrint(modified, base); - assertThat(root).child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("value")) + assertThat(root).child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); } @@ -386,115 +402,97 @@ public void bidirectionalGraphStackOverflow() final DiffNode root = compareAndPrint(modified, base); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa)) + .child(startBuilding() + .propertyName("children") + .collectionItem(a) + .propertyName("children") + .collectionItem(aa) + .propertyName("children") + .collectionItem(ba) + .propertyName("children") + .collectionItem(baa)) .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) + .child(startBuilding() + .propertyName("children") + .collectionItem(a) + .propertyName("children") + .collectionItem(aa) + .propertyName("children") + .collectionItem(ba) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa) - .withPropertyName("value")) + .child(startBuilding() + .propertyName("children") + .collectionItem(a) + .propertyName("directReference") + .propertyName("children") + .collectionItem(ba) + .propertyName("children") + .collectionItem(baa) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(a) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) + .child(startBuilding() + .propertyName("children") + .collectionItem(a) + .propertyName("directReference") + .propertyName("children") + .collectionItem(ba) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa) - .withPropertyName("value")) + .child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("children") + .collectionItem(ba) + .propertyName("children") + .collectionItem(baa) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) + .child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("children") + .collectionItem(ba) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("children") - .withCollectionItem(baa) - .withPropertyName("value")) + .child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("directReference") + .propertyName("children") + .collectionItem(aa) + .propertyName("children") + .collectionItem(ba) + .propertyName("children") + .collectionItem(baa) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); NodeAssertions.assertThat(root) - .child(createBuilder() - .withRoot() - .withPropertyName("children") - .withCollectionItem(b) - .withPropertyName("directReference") - .withPropertyName("children") - .withCollectionItem(aa) - .withPropertyName("children") - .withCollectionItem(ba) - .withPropertyName("value")) + .child(startBuilding() + .propertyName("children") + .collectionItem(b) + .propertyName("directReference") + .propertyName("children") + .collectionItem(aa) + .propertyName("children") + .collectionItem(ba) + .propertyName("value")) .hasState(DiffNode.State.CHANGED); } - private static DiffNode compareAndPrint(final GraphNode modified, final GraphNode base) - { - final DiffNode root = ObjectDifferBuilder.buildDefault().compare(modified, base); - if (PRINT_ENABLED) - { - root.visit(new PrintingVisitor(modified, base)); - } - return root; - } - @Test public void testWithSimpleBiDirectionalConnection() { @@ -538,30 +536,11 @@ public void testWithMapBasedBiDirectionalConnection() node.visit(new NodeHierarchyVisitor()); NodeAssertions.assertThat(node) - .child(createBuilder() - .withRoot() - .withPropertyName("map") - .withMapKey("foo") - .withPropertyName("map") - .withMapKey("bar")) + .child(startBuilding() + .propertyName("map") + .mapKey("foo") + .propertyName("map") + .mapKey("bar")) .isCircular(); } - - private static void establishParentChildRelationship(final GraphNode parent, final GraphNode child) - { - child.setParent(parent); - parent.addChild(child); - } - - private static void establishCircularChildRelationship(final GraphNode a, final GraphNode b) - { - a.addChild(b); - b.addChild(a); - } - - private static void establishCircularDirectReference(final GraphNode a, final GraphNode b) - { - a.setDirectReference(b); - b.setDirectReference(a); - } } diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue43/Issue43ITCase.java b/src/integration-test/java/de/danielbechler/diff/issues/issue43/Issue43ITCase.java index 21ce8d3a..995b65ce 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue43/Issue43ITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue43/Issue43ITCase.java @@ -1,16 +1,21 @@ package de.danielbechler.diff.issues.issue43; -import de.danielbechler.diff.*; -import org.testng.annotations.*; - -import java.util.*; - -import static java.util.Arrays.*; -import static java.util.Collections.*; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.*; - -/** https://github.com/SQiShER/java-object-diff/issues/43 */ +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.ObjectDifferBuilder; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Set; + +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * https://github.com/SQiShER/java-object-diff/issues/43 + */ @SuppressWarnings("ALL") public class Issue43ITCase { @@ -145,9 +150,9 @@ public void shouldDiffThings() ObjectDifferBuilder builder = ObjectDifferBuilder.startBuilding(); for (final String name : propertyNames) { - final NodePath nodePath = NodePath.buildWith(name); + final NodePath nodePath = NodePath.with(name); builder.configure().comparison().ofNode(nodePath).toUseEqualsMethod(); - builder.configure().inclusion().toInclude().nodes(nodePath); + builder.configure().inclusion().toInclude().node(nodePath); } final Thing thingOne = new Thing("a", "b"); diff --git a/src/integration-test/java/de/danielbechler/diff/issues/issue70/PersonDiffITCase.java b/src/integration-test/java/de/danielbechler/diff/issues/issue70/PersonDiffITCase.java index 8d0984dc..3b35eb0b 100644 --- a/src/integration-test/java/de/danielbechler/diff/issues/issue70/PersonDiffITCase.java +++ b/src/integration-test/java/de/danielbechler/diff/issues/issue70/PersonDiffITCase.java @@ -1,11 +1,14 @@ package de.danielbechler.diff.issues.issue70; -import de.danielbechler.diff.*; -import de.danielbechler.diff.helper.*; -import de.danielbechler.diff.visitor.*; -import org.testng.annotations.*; +import de.danielbechler.diff.DiffNode; +import de.danielbechler.diff.NodePath; +import de.danielbechler.diff.ObjectDiffer; +import de.danielbechler.diff.ObjectDifferBuilder; +import de.danielbechler.diff.helper.NodeAssertions; +import de.danielbechler.diff.visitor.PrintingVisitor; +import org.testng.annotations.Test; -import java.util.*; +import java.util.Arrays; public class PersonDiffITCase { @@ -16,7 +19,7 @@ public void testIncludeCollectionAttribute() final Person b = new Person("Gulen Chongthamm", Arrays.asList("Hola Espanyol", "Vicky Boss", "Roger Harper")); final ObjectDifferBuilder builder = ObjectDifferBuilder.startBuilding(); - builder.configure().inclusion().toInclude().nodes(NodePath.buildWith("aliases")); + builder.configure().inclusion().toInclude().node(NodePath.with("aliases")); final ObjectDiffer differ = builder.build(); final DiffNode root = differ.compare(b, a); diff --git a/src/main/java/de/danielbechler/diff/Accessor.java b/src/main/java/de/danielbechler/diff/Accessor.java index 9fa3e54b..7f6c83d6 100644 --- a/src/main/java/de/danielbechler/diff/Accessor.java +++ b/src/main/java/de/danielbechler/diff/Accessor.java @@ -19,7 +19,7 @@ /** @author Daniel Bechler */ public interface Accessor { - Element getPathElement(); + ElementSelector getElementSelector(); Object get(Object target); diff --git a/src/main/java/de/danielbechler/diff/AnyElementSelector.java b/src/main/java/de/danielbechler/diff/AnyElementSelector.java new file mode 100644 index 00000000..9417c56e --- /dev/null +++ b/src/main/java/de/danielbechler/diff/AnyElementSelector.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014 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; + +/** + * Created by Daniel Bechler. + */ +public final class AnyElementSelector extends ElementSelector +{ + @Override + public String toHumanReadableString() + { + return "*"; + } + + @Override + public boolean equals(final Object obj) + { + if (obj != null && obj instanceof AnyElementSelector) + { + return true; + } + return false; + } + + @Override + public int hashCode() + { + return 0; + } +} diff --git a/src/main/java/de/danielbechler/diff/ConfigNode.java b/src/main/java/de/danielbechler/diff/ConfigNode.java new file mode 100644 index 00000000..90f5bc0c --- /dev/null +++ b/src/main/java/de/danielbechler/diff/ConfigNode.java @@ -0,0 +1,237 @@ +/* + * Copyright 2014 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; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static de.danielbechler.diff.Inclusion.EXCLUDED; +import static de.danielbechler.diff.Inclusion.INCLUDED; + +/** + * Created by Daniel Bechler. + */ +public class ConfigNode +{ + private final Map children = new HashMap(); + private final ElementSelector elementSelector; + private final ConfigNode parent; + private ConfigNode prototype; + private Inclusion inclusion; + + public ConfigNode() + { + this(RootElementSelector.getInstance(), null); + } + + private ConfigNode(final ElementSelector elementSelector, final ConfigNode parent) + { + this.elementSelector = elementSelector; + this.parent = parent; + } + + public Inclusion getInclusion() + { + return inclusion; + } + + public void setInclusion(final Inclusion inclusion) + { + this.inclusion = inclusion; + } + + public ElementSelector getElementSelector() + { + return elementSelector; + } + + public ConfigNode getParent() + { + return parent; + } + + public ConfigNode getNodeForPath(final NodePath nodePath) + { + if (parent == null) + { + final List elementSelectors = nodePath.getElementSelectors(); + if (elementSelectors.size() != 1) + { + return getChild(elementSelectors.subList(1, elementSelectors.size())); + } + else + { + return this; + } + } + else + { + return parent.getNodeForPath(nodePath); + } + } + + public ConfigNode getChild(final ElementSelector childSelector) + { + if (childSelector == RootElementSelector.getInstance()) + { + throw new IllegalArgumentException("A child node can never be the root"); + } + if (childSelector instanceof AnyElementSelector) + { + if (prototype == null) + { + prototype = new ConfigNode(childSelector, parent); + } + return prototype; + } + if (children.containsKey(childSelector)) + { + return children.get(childSelector); + } + else + { + final ConfigNode childNode = new ConfigNode(childSelector, this); + children.put(childSelector, childNode); + return childNode; + } + } + + private ConfigNode getChild(final List childSelectors) + { + assert !childSelectors.isEmpty(); + if (childSelectors.contains(RootElementSelector.getInstance())) + { + throw new IllegalArgumentException("Child nodes can never match the RootElementSelector"); + } + else if (childSelectors.size() == 1) + { + return getChild(childSelectors.get(0)); + } + final ConfigNode child = getChild(childSelectors.get(0)); + return child.getChild(childSelectors.subList(1, childSelectors.size())); + } + + public boolean hasChild(final ElementSelector childSelector) + { + return children.get(childSelector) != null; + } + + public boolean isIncluded() + { + if (inclusion == EXCLUDED) + { + return false; + } + final ConfigNode parentWithInclusion = getClosestParentWithInclusion(); + if (parentWithInclusion == null) + { + if (!hasInclusion() && hasPrototype() && prototype.hasInclusion()) + { + return prototype.inclusion == INCLUDED; + } + else + { + return inclusion == INCLUDED || hasIncludedChildren(); + } + } + else + { + return parentWithInclusion.getInclusion() != EXCLUDED; + } + } + + public boolean isExcluded() + { + if (inclusion == EXCLUDED) + { + return true; + } + if (parent != null && parent.isExcluded()) + { + return true; + } + return false; + } + + public ConfigNode getClosestParentWithInclusion() + { + if (parent != null) + { + if (parent.hasInclusion()) + { + return parent; + } + else + { + return parent.getClosestParentWithInclusion(); + } + } + return null; + } + + private boolean hasIncludedChildren() + { + for (final ConfigNode child : children.values()) + { + if (child.isIncluded()) + { + return true; + } + } + return false; + } + + public boolean hasInclusion() + { + return inclusion != null; + } + + public boolean containsInclusion(final Inclusion inclusion) + { + if (this.inclusion == inclusion) + { + return true; + } + else + { + for (final ConfigNode child : children.values()) + { + if (child.containsInclusion(inclusion)) + { + return true; + } + } + } + return false; + } + + public ConfigNode getPrototype() + { + return prototype; + } + + public void setPrototype(final ConfigNode prototype) + { + this.prototype = prototype; + } + + public boolean hasPrototype() + { + return prototype != null; + } +} diff --git a/src/main/java/de/danielbechler/diff/DiffNode.java b/src/main/java/de/danielbechler/diff/DiffNode.java index e019476f..79d450c2 100644 --- a/src/main/java/de/danielbechler/diff/DiffNode.java +++ b/src/main/java/de/danielbechler/diff/DiffNode.java @@ -16,7 +16,7 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.bean.BeanPropertyElementSelector; import de.danielbechler.diff.visitor.PropertyVisitor; import de.danielbechler.util.Assert; @@ -44,7 +44,7 @@ public class DiffNode public static final DiffNode ROOT = null; private final Accessor accessor; - private final Map children = new LinkedHashMap(10); + private final Map children = new LinkedHashMap(10); private State state = State.UNTOUCHED; private DiffNode parentNode; @@ -165,24 +165,23 @@ public final NodePath getPath() { if (parentNode != null) { - return NodePath.createBuilder() - .withPropertyPath(parentNode.getPath()) - .withElement(accessor.getPathElement()) + return NodePath.startBuildingFrom(parentNode.getPath()) + .element(accessor.getElementSelector()) .build(); } else if (accessor instanceof RootAccessor) { - return NodePath.createBuilder().withRoot().build(); + return NodePath.withRoot(); } else { - return NodePath.createBuilder().withRoot().withElement(accessor.getPathElement()).build(); + return NodePath.startBuilding().element(accessor.getElementSelector()).build(); } } - public Element getPathElement() + public ElementSelector getElementSelector() { - return accessor.getPathElement(); + return accessor.getElementSelector(); } /** @@ -233,7 +232,7 @@ public int childCount() */ public DiffNode getChild(final String propertyName) { - return getChild(new BeanPropertyElement(propertyName)); + return getChild(new BeanPropertyElementSelector(propertyName)); } /** @@ -252,12 +251,12 @@ public DiffNode getChild(final NodePath path) /** * Retrieve a child that matches the given path element relative to this node. * - * @param pathElement The path element of the child node to get. + * @param pathElementSelector The path element of the child node to get. * @return The requested child node or null. */ - public DiffNode getChild(final Element pathElement) + public DiffNode getChild(final ElementSelector pathElementSelector) { - return children.get(pathElement); + return children.get(pathElementSelector); } /** @@ -283,15 +282,15 @@ else if (node.getParentNode() != null && node.getParentNode() != this) "child of another node. Adding nodes multiple times is not allowed, since it could " + "cause infinite loops."); } - final Element pathElement = node.getPathElement(); + final ElementSelector pathElementSelector = node.getElementSelector(); if (node.getParentNode() == null) { node.setParentNode(this); - children.put(pathElement, node); + children.put(pathElementSelector, node); } else if (node.getParentNode() == this) { - children.put(pathElement, node); + children.put(pathElementSelector, node); } else { diff --git a/src/main/java/de/danielbechler/diff/Element.java b/src/main/java/de/danielbechler/diff/ElementSelector.java similarity index 96% rename from src/main/java/de/danielbechler/diff/Element.java rename to src/main/java/de/danielbechler/diff/ElementSelector.java index 7ae58c9a..46dfb6b6 100644 --- a/src/main/java/de/danielbechler/diff/Element.java +++ b/src/main/java/de/danielbechler/diff/ElementSelector.java @@ -18,11 +18,11 @@ /** * Serves mainly as marker class and enforces the proper implementation of hashCode(), equals() and toString() - * for all property path elements. + * for all element selectors. * * @author Daniel Bechler */ -public abstract class Element +public abstract class ElementSelector { public abstract String toHumanReadableString(); diff --git a/src/main/java/de/danielbechler/diff/InclusionConfiguration.java b/src/main/java/de/danielbechler/diff/InclusionConfiguration.java index 1365d175..3ac118c6 100644 --- a/src/main/java/de/danielbechler/diff/InclusionConfiguration.java +++ b/src/main/java/de/danielbechler/diff/InclusionConfiguration.java @@ -27,7 +27,7 @@ public interface To To types(Class... types); - To nodes(NodePath... nodePath); + To node(NodePath nodePath); To propertyNames(String... propertyNames); } diff --git a/src/main/java/de/danielbechler/diff/InclusionService.java b/src/main/java/de/danielbechler/diff/InclusionService.java index 13b6b931..28a612e7 100644 --- a/src/main/java/de/danielbechler/diff/InclusionService.java +++ b/src/main/java/de/danielbechler/diff/InclusionService.java @@ -1,6 +1,6 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.bean.BeanPropertyElementSelector; import de.danielbechler.util.Assert; import java.util.HashMap; @@ -17,9 +17,11 @@ class InclusionService implements InclusionConfiguration, IsIgnoredResolver { private final CategoryResolver categoryResolver; - private final NodePathValueHolder nodeInclusions = NodePathValueHolder.of(Inclusion.class); + private final ConfigNode nodeInclusions = new ConfigNode(); private final Map, Inclusion> typeInclusions = new HashMap, Inclusion>(); private final Map categoryInclusions = new TreeMap(); + + @Deprecated private final Map propertyNameInclusions = new TreeMap(); public InclusionService(final CategoryResolver categoryResolver) @@ -87,12 +89,12 @@ private boolean isIncludedByOwnPropertyName(final DiffNode node) private boolean isIncludedByParentPropertyName(final DiffNode node) { - final List pathElements = node.getPath().getElements(); - for (final Element element : pathElements) + final List pathElementSelectors = node.getPath().getElementSelectors(); + for (final ElementSelector elementSelector : pathElementSelectors) { - if (element instanceof BeanPropertyElement) + if (elementSelector instanceof BeanPropertyElementSelector) { - final BeanPropertyElement beanPropertyElement = (BeanPropertyElement) element; + final BeanPropertyElementSelector beanPropertyElement = (BeanPropertyElementSelector) elementSelector; final String propertyName = beanPropertyElement.getPropertyName(); if (propertyName != null && propertyNameInclusions.get(propertyName) == INCLUDED) { @@ -110,7 +112,7 @@ private boolean isIncludedByCategory(final DiffNode node) private boolean isIncludedByPath(final DiffNode node) { - return getInclusionByPath(node.getPath(), INCLUDED) == INCLUDED; + return nodeInclusions.getNodeForPath(node.getPath()).isIncluded(); } private boolean isExcluded(final DiffNode node) @@ -150,7 +152,7 @@ private boolean isExcludedByPropertyName(final DiffNode node) private boolean isExcludedByPath(final DiffNode node) { - return getInclusionByPath(node.getPath(), EXCLUDED) == EXCLUDED; + return nodeInclusions.getNodeForPath(node.getPath()).isExcluded(); } private boolean isExcludedByCategory(final DiffNode node) @@ -160,7 +162,7 @@ private boolean isExcludedByCategory(final DiffNode node) private boolean hasInclusions(final Inclusion inclusion) { - if (nodeInclusions.containsValue(inclusion)) + if (nodeInclusions.containsInclusion(inclusion)) { return true; } @@ -179,19 +181,6 @@ private boolean hasInclusions(final Inclusion inclusion) return false; } - private Inclusion getInclusionByPath(final NodePath nodePath, final Inclusion inclusion) - { - final List inclusions = nodeInclusions.accumulatedValuesForNodePath(nodePath); - if (inclusions.contains(inclusion)) - { - return inclusion; - } - else - { - return nodeInclusions.valueForNodePath(nodePath); - } - } - private boolean hasCategoryWithInclusion(final DiffNode node, final Inclusion inclusion) { for (final String category : categoryResolver.resolveCategories(node)) @@ -259,12 +248,9 @@ public To types(final Class... types) return this; } - public To nodes(final NodePath... nodePaths) + public To node(final NodePath nodePath) { - for (final NodePath nodePath : nodePaths) - { - nodeInclusions.put(nodePath, inclusion); - } + nodeInclusions.getNodeForPath(nodePath).setInclusion(inclusion); return this; } diff --git a/src/main/java/de/danielbechler/diff/Instances.java b/src/main/java/de/danielbechler/diff/Instances.java index 6ebc2713..c6cf9424 100644 --- a/src/main/java/de/danielbechler/diff/Instances.java +++ b/src/main/java/de/danielbechler/diff/Instances.java @@ -16,16 +16,22 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.*; -import de.danielbechler.util.*; +import de.danielbechler.diff.bean.BeanPropertyAccessor; +import de.danielbechler.util.Assert; +import de.danielbechler.util.Classes; import de.danielbechler.util.Collections; +import de.danielbechler.util.Exceptions; -import java.lang.reflect.*; -import java.util.*; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; +import java.util.Set; -import static de.danielbechler.util.Objects.*; +import static de.danielbechler.util.Objects.isEqual; -/** @author Daniel Bechler */ +/** + * @author Daniel Bechler + */ @SuppressWarnings({"UnusedDeclaration"}) public class Instances { @@ -34,6 +40,18 @@ public class Instances private final Object base; private final Object fresh; + Instances(final Accessor sourceAccessor, + final Object working, + final Object base, + final Object fresh) + { + Assert.notNull(sourceAccessor, "sourceAccessor"); + this.sourceAccessor = sourceAccessor; + this.working = working; + this.base = base; + this.fresh = fresh; + } + static Instances of(final Accessor sourceAccessor, final T working, final T base, @@ -54,19 +72,9 @@ static Instances of(final T working, final T base) return new Instances(RootAccessor.getInstance(), working, base, fresh); } - Instances(final Accessor sourceAccessor, - final Object working, - final Object base, - final Object fresh) - { - Assert.notNull(sourceAccessor, "sourceAccessor"); - this.sourceAccessor = sourceAccessor; - this.working = working; - this.base = base; - this.fresh = fresh; - } - - /** @return The {@link Accessor} that has been used to get to these instances. */ + /** + * @return The {@link Accessor} that has been used to get to these instances. + */ public Accessor getSourceAccessor() { return sourceAccessor; @@ -285,14 +293,10 @@ public NodePath getPropertyPath(final DiffNode parentNode) { if (parentNode != null) { - return NodePath.createBuilder() - .withPropertyPath(parentNode.getPath()) - .withElement(sourceAccessor.getPathElement()) - .build(); - } - else - { - return NodePath.createBuilder().withRoot().build(); + final NodePath parentPath = parentNode.getPath(); + final ElementSelector elementSelector = sourceAccessor.getElementSelector(); + return NodePath.startBuildingFrom(parentPath).element(elementSelector).build(); } + return NodePath.withRoot(); } } diff --git a/src/main/java/de/danielbechler/diff/NodePath.java b/src/main/java/de/danielbechler/diff/NodePath.java index 962db971..95ea5c57 100644 --- a/src/main/java/de/danielbechler/diff/NodePath.java +++ b/src/main/java/de/danielbechler/diff/NodePath.java @@ -16,14 +16,15 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.BeanPropertyElement; -import de.danielbechler.diff.collection.CollectionItemElement; -import de.danielbechler.diff.map.MapKeyElement; +import de.danielbechler.diff.bean.BeanPropertyElementSelector; +import de.danielbechler.diff.collection.CollectionItemElementSelector; +import de.danielbechler.diff.map.MapKeyElementSelector; import de.danielbechler.util.Assert; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; /** @@ -31,16 +32,39 @@ */ public final class NodePath implements Comparable { - private final List elements; + private final List elementSelectors; - private NodePath(final List elements) + private NodePath(final List elementSelectors) { - this.elements = Collections.unmodifiableList(elements); + this.elementSelectors = Collections.unmodifiableList(elementSelectors); } - public List getElements() + public static AppendableBuilder startBuilding() { - return elements; + final List elementSelectors1 = new LinkedList(); + elementSelectors1.add(RootElementSelector.getInstance()); + return new AppendableBuilderImpl(elementSelectors1); + } + + public static AppendableBuilder startBuildingFrom(final NodePath nodePath) + { + Assert.notNull(nodePath, "propertyPath"); + return new AppendableBuilderImpl(new ArrayList(nodePath.getElementSelectors())); + } + + public static NodePath with(final String propertyName, final String... additionalPropertyNames) + { + return startBuilding().propertyName(propertyName, additionalPropertyNames).build(); + } + + public static NodePath withRoot() + { + return startBuilding().build(); + } + + public List getElementSelectors() + { + return elementSelectors; } public boolean matches(final NodePath nodePath) @@ -50,47 +74,56 @@ public boolean matches(final NodePath nodePath) public boolean isParentOf(final NodePath nodePath) { - final Iterator iterator1 = elements.iterator(); - final Iterator iterator2 = nodePath.getElements().iterator(); - while (iterator1.hasNext() && iterator2.hasNext()) + final List otherElementSelectors = nodePath.getElementSelectors(); + if (elementSelectors.size() < otherElementSelectors.size()) { - final Element next1 = iterator1.next(); - final Element next2 = iterator2.next(); - if (!next1.equals(next2)) - { - return false; - } + return otherElementSelectors.subList(0, elementSelectors.size()).equals(elementSelectors); + } + return false; + } + + public boolean isChildOf(final NodePath nodePath) + { + final List otherElementSelectors = nodePath.getElementSelectors(); + if (elementSelectors.size() > otherElementSelectors.size()) + { + return elementSelectors.subList(0, otherElementSelectors.size()).equals(otherElementSelectors); } - return !iterator1.hasNext(); + return false; + } + + public ElementSelector getLastElementSelector() + { + return elementSelectors.get(elementSelectors.size() - 1); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); - final Iterator iterator = elements.iterator(); - Element previousElement = null; + final Iterator iterator = elementSelectors.iterator(); + ElementSelector previousElementSelector = null; while (iterator.hasNext()) { - final Element element = iterator.next(); - if (element instanceof RootElement) + final ElementSelector elementSelector = iterator.next(); + if (elementSelector instanceof RootElementSelector) { sb.append("/"); } - else if (element instanceof CollectionItemElement || element instanceof MapKeyElement) + else if (elementSelector instanceof CollectionItemElementSelector || elementSelector instanceof MapKeyElementSelector) { - sb.append(element); + sb.append(elementSelector); } - else if (previousElement instanceof RootElement) + else if (previousElementSelector instanceof RootElementSelector) { - sb.append(element); + sb.append(elementSelector); } else { sb.append('/'); - sb.append(element); + sb.append(elementSelector); } - previousElement = element; + previousElementSelector = elementSelector; } return sb.toString(); } @@ -109,7 +142,7 @@ public boolean equals(final Object o) final NodePath that = (NodePath) o; - if (!elements.equals(that.elements)) + if (!elementSelectors.equals(that.elementSelectors)) { return false; } @@ -120,27 +153,12 @@ public boolean equals(final Object o) @Override public int hashCode() { - return elements.hashCode(); - } - - public static InitialBuilder createBuilder() - { - return new InitialBuilderImpl(); - } - - public static NodePath buildWith(final String propertyName, final String... additionalPropertyNames) - { - return createBuilder().withRoot().withPropertyName(propertyName, additionalPropertyNames).build(); - } - - public static NodePath buildRootPath() - { - return createBuilder().withRoot().build(); + return elementSelectors.hashCode(); } public int compareTo(final NodePath that) { - if (this.getElements().size() <= that.getElements().size()) + if (this.getElementSelectors().size() <= that.getElementSelectors().size()) { return -1; } @@ -148,7 +166,7 @@ else if (this.matches(that)) { return 0; } - else if (this.getElements().size() > that.getElements().size()) + else if (this.getElementSelectors().size() > that.getElementSelectors().size()) { return 1; } @@ -158,94 +176,91 @@ else if (this.getElements().size() > that.getElements().size()) } } - /** - * @author Daniel Bechler - */ - @SuppressWarnings({"UnusedDeclaration"}) - private static final class InitialBuilderImpl implements InitialBuilder + public static interface AppendableBuilder { - private InitialBuilderImpl() - { - } + AppendableBuilder any(); - public AppendableBuilder withRoot() - { - final List elements = new ArrayList(1); - elements.add(RootElement.getInstance()); - return new AppendableBuilderImpl(elements); - } + AppendableBuilder element(ElementSelector elementSelector); - public AppendableBuilder withPropertyPath(final NodePath nodePath) - { - Assert.notNull(nodePath, "propertyPath"); - return new AppendableBuilderImpl(new ArrayList(nodePath.getElements())); - } + AppendableBuilder propertyName(String name, String... names); + + AppendableBuilder collectionItem(T item); + + AppendableBuilder mapKey(K key); + + NodePath build(); } private static final class AppendableBuilderImpl implements AppendableBuilder { - private final List elements; + private final List elementSelectors; - public AppendableBuilderImpl(final List elements) + public AppendableBuilderImpl(final List elementSelectors) { - Assert.notEmpty(elements, "elements"); - this.elements = new ArrayList(elements); + Assert.notEmpty(elementSelectors, "elementSelectors"); + this.elementSelectors = new LinkedList(elementSelectors); } - public AppendableBuilder withElement(final Element element) + public AppendableBuilder any() { - Assert.notNull(element, "element"); - elements.add(element); + elementSelectors.add(new AnyElementSelector()); return this; } - public AppendableBuilder withPropertyName(final String name, final String... names) + public AppendableBuilder element(final ElementSelector elementSelector) { - elements.add(new BeanPropertyElement(name)); + Assert.notNull(elementSelector, "elementSelector"); + elementSelectors.add(elementSelector); + return this; + } + + public AppendableBuilder propertyName(final String name, final String... names) + { + elementSelectors.add(new BeanPropertyElementSelector(name)); for (final String s : names) { - elements.add(new BeanPropertyElement(s)); + elementSelectors.add(new BeanPropertyElementSelector(s)); } return this; } - public AppendableBuilder withCollectionItem(final T item) + public AppendableBuilder collectionItem(final T item) { - elements.add(new CollectionItemElement(item)); + elementSelectors.add(new CollectionItemElementSelector(item)); return this; } - public AppendableBuilder withMapKey(final K key) + public AppendableBuilder mapKey(final K key) { Assert.notNull(key, "key"); - elements.add(new MapKeyElement(key)); + elementSelectors.add(new MapKeyElementSelector(key)); return this; } public NodePath build() { - if (elements.isEmpty()) + if (elementSelectors.isEmpty()) { throw new IllegalStateException("A property path cannot be empty"); } - else if (!(elements.get(0) instanceof RootElement)) + else if (!(elementSelectors.get(0) instanceof RootElementSelector)) { throw new IllegalStateException("A property path must start with a root element"); } - else if (elementCount(RootElement.class) > 1) + else if (elementCount(RootElementSelector.class) > 1) { throw new IllegalStateException("A property path cannot contain multiple root elements"); } - return new NodePath(elements); + return new NodePath(elementSelectors); } - private int elementCount(final Class type) + private int elementCount(final Class type) { assert type != null : "Type must not be null"; int count = 0; - for (final Element element : elements) + for (final ElementSelector elementSelector : elementSelectors) { - if (type.isAssignableFrom(element.getClass())) + if (type.isAssignableFrom(elementSelector.getClass())) { count++; } @@ -253,24 +268,4 @@ private int elementCount(final Class type) return count; } } - - public static interface InitialBuilder - { - AppendableBuilder withPropertyPath(NodePath nodePath); - - AppendableBuilder withRoot(); - } - - public static interface AppendableBuilder - { - AppendableBuilder withElement(Element element); - - AppendableBuilder withPropertyName(String name, String... names); - - AppendableBuilder withCollectionItem(T item); - - AppendableBuilder withMapKey(K key); - - NodePath build(); - } } diff --git a/src/main/java/de/danielbechler/diff/NodePathValueHolder.java b/src/main/java/de/danielbechler/diff/NodePathValueHolder.java index 0da8d5af..236bfc31 100644 --- a/src/main/java/de/danielbechler/diff/NodePathValueHolder.java +++ b/src/main/java/de/danielbechler/diff/NodePathValueHolder.java @@ -14,7 +14,7 @@ */ class NodePathValueHolder { - private final Map> elementValueHolders = new HashMap>(); + private final Map> elementValueHolders = new HashMap>(); private T value; public static NodePathValueHolder of(final Class type) @@ -25,36 +25,36 @@ public static NodePathValueHolder of(final Class type) public NodePathValueHolder put(final NodePath nodePath, final T value) { - put(nodePath.getElements(), value); + put(nodePath.getElementSelectors(), value); return this; } - private void put(final List nodePathElements, final T value) + private void put(final List elementSelectors, final T value) { - if (nodePathElements.isEmpty()) + if (elementSelectors.isEmpty()) { return; } - final Element element = nodePathElements.get(0); - NodePathValueHolder nodePathValueHolder = elementValueHolders.get(element); + final ElementSelector elementSelector = elementSelectors.get(0); + NodePathValueHolder nodePathValueHolder = valueHolderForElementSelector(elementSelector); if (nodePathValueHolder == null) { nodePathValueHolder = new NodePathValueHolder(); - elementValueHolders.put(element, nodePathValueHolder); + elementValueHolders.put(elementSelector, nodePathValueHolder); } - if (nodePathElements.size() == 1) + if (elementSelectors.size() == 1) { nodePathValueHolder.value = value; } else { - final List nodePathElementsTail = new ArrayList(nodePathElements.size() - 1); - nodePathElementsTail.addAll(nodePathElements.subList(1, nodePathElements.size())); + final List nodePathElementsTail = new ArrayList(elementSelectors.size() - 1); + nodePathElementsTail.addAll(elementSelectors.subList(1, elementSelectors.size())); nodePathValueHolder.put(nodePathElementsTail, value); } } - private T visit(final List accumulator, final Iterator elementIterator) + private T visit(final List accumulator, final Iterator elementIterator) { if (value != null) { @@ -62,29 +62,31 @@ private T visit(final List accumulator, final Iterator elementIterat } if (elementIterator.hasNext()) { - final Element element = elementIterator.next(); - final NodePathValueHolder valueHolder = elementValueHolders.get(element); + final ElementSelector selector = elementIterator.next(); + final NodePathValueHolder valueHolder = valueHolderForElementSelector(selector); if (valueHolder != null) { return valueHolder.visit(accumulator, elementIterator); } return null; } - else - { - return value; - } + return value; + } + + private NodePathValueHolder valueHolderForElementSelector(final ElementSelector elementSelector) + { + return elementValueHolders.get(elementSelector); } public T valueForNodePath(final NodePath nodePath) { - return visit(new LinkedList(), nodePath.getElements().iterator()); + return visit(new LinkedList(), nodePath.getElementSelectors().iterator()); } public List accumulatedValuesForNodePath(final NodePath nodePath) { final List accumulator = new LinkedList(); - visit(accumulator, nodePath.getElements().iterator()); + visit(accumulator, nodePath.getElementSelectors().iterator()); return accumulator; } @@ -110,4 +112,60 @@ else if (value != null && value.equals(this.value)) return false; } } + + public void collect(final Collector collector) + { + collect(null, collector); + } + + private void collect(final NodePath nodePath, final Collector collector) + { + if (nodePath != null && value != null) + { + collector.it(nodePath, value); + } + for (final Map.Entry> entry : elementValueHolders.entrySet()) + { + final NodePath childNodePath; + final ElementSelector elementSelector = entry.getKey(); + final NodePathValueHolder valueHolder = entry.getValue(); + if (elementSelector == RootElementSelector.getInstance()) + { + childNodePath = NodePath.withRoot(); + } + else + { + childNodePath = NodePath.startBuildingFrom(nodePath).element(elementSelector).build(); + } + + if (valueHolder != null) + { + valueHolder.collect(childNodePath, collector); + } + } + } + + @Override + public String toString() + { + final StringBuilder stringBuilder = new StringBuilder(); + collect(new Collector() + { + public void it(final NodePath path, final T value) + { + stringBuilder.append(path.toString()).append(" => ").append(value).append('\n'); + } + }); + return stringBuilder.toString(); + } + + public void hasChildMatchingValue(NodePath nodePath, T inclusion) + { + + } + + public static interface Collector + { + void it(NodePath path, T value); + } } diff --git a/src/main/java/de/danielbechler/diff/RootAccessor.java b/src/main/java/de/danielbechler/diff/RootAccessor.java index 323884fb..1e89d11b 100644 --- a/src/main/java/de/danielbechler/diff/RootAccessor.java +++ b/src/main/java/de/danielbechler/diff/RootAccessor.java @@ -45,9 +45,9 @@ public void unset(final Object target) throw new UnsupportedOperationException(); } - public Element getPathElement() + public ElementSelector getElementSelector() { - return RootElement.getInstance(); + return RootElementSelector.getInstance(); } @Override diff --git a/src/main/java/de/danielbechler/diff/RootElement.java b/src/main/java/de/danielbechler/diff/RootElementSelector.java similarity index 82% rename from src/main/java/de/danielbechler/diff/RootElement.java rename to src/main/java/de/danielbechler/diff/RootElementSelector.java index f6e9539a..ffcedde3 100644 --- a/src/main/java/de/danielbechler/diff/RootElement.java +++ b/src/main/java/de/danielbechler/diff/RootElementSelector.java @@ -17,15 +17,15 @@ package de.danielbechler.diff; /** @author Daniel Bechler */ -public final class RootElement extends Element +public final class RootElementSelector extends ElementSelector { - private static final RootElement instance = new RootElement(); + private static final RootElementSelector instance = new RootElementSelector(); - RootElement() + RootElementSelector() { } - public static RootElement getInstance() + public static RootElementSelector getInstance() { return instance; } diff --git a/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java b/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java index 2240be15..eab688bf 100644 --- a/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java +++ b/src/main/java/de/danielbechler/diff/bean/BeanPropertyAccessor.java @@ -219,9 +219,9 @@ public String getPropertyName() return this.propertyName; } - public Element getPathElement() + public ElementSelector getElementSelector() { - return new BeanPropertyElement(this.propertyName); + return new BeanPropertyElementSelector(this.propertyName); } /** diff --git a/src/main/java/de/danielbechler/diff/bean/BeanPropertyElement.java b/src/main/java/de/danielbechler/diff/bean/BeanPropertyElementSelector.java similarity index 86% rename from src/main/java/de/danielbechler/diff/bean/BeanPropertyElement.java rename to src/main/java/de/danielbechler/diff/bean/BeanPropertyElementSelector.java index 39d4ecef..c2ec7987 100644 --- a/src/main/java/de/danielbechler/diff/bean/BeanPropertyElement.java +++ b/src/main/java/de/danielbechler/diff/bean/BeanPropertyElementSelector.java @@ -22,11 +22,11 @@ /** * @author Daniel Bechler */ -public final class BeanPropertyElement extends Element +public final class BeanPropertyElementSelector extends ElementSelector { private final String propertyName; - public BeanPropertyElement(final String propertyName) + public BeanPropertyElementSelector(final String propertyName) { Assert.hasText(propertyName, "propertyName"); this.propertyName = propertyName; @@ -55,7 +55,7 @@ public boolean equals(final Object o) return false; } - final BeanPropertyElement that = (BeanPropertyElement) o; + final BeanPropertyElementSelector that = (BeanPropertyElementSelector) o; if (!propertyName.equals(that.propertyName)) { diff --git a/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java b/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java index f1726ed8..b26bf6ed 100644 --- a/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java +++ b/src/main/java/de/danielbechler/diff/collection/CollectionItemAccessor.java @@ -17,7 +17,7 @@ package de.danielbechler.diff.collection; import de.danielbechler.diff.Accessor; -import de.danielbechler.diff.Element; +import de.danielbechler.diff.ElementSelector; import de.danielbechler.diff.TypeAwareAccessor; import java.util.Collection; @@ -34,9 +34,23 @@ public CollectionItemAccessor(final Object referenceItem) this.referenceItem = referenceItem; } - public Element getPathElement() + @SuppressWarnings("unchecked") + private static Collection objectAsCollection(final Object object) + { + if (object == null) + { + return null; + } + else if (object instanceof Collection) + { + return (Collection) object; + } + throw new IllegalArgumentException(object.getClass().toString()); + } + + public ElementSelector getElementSelector() { - return new CollectionItemElement(referenceItem); + return new CollectionItemElementSelector(referenceItem); } public void set(final Object target, final Object value) @@ -76,20 +90,6 @@ public Class getType() return referenceItem != null ? referenceItem.getClass() : null; } - private static Collection objectAsCollection(final Object object) - { - if (object == null) - { - return null; - } - else if (object instanceof Collection) - { - //noinspection unchecked - return (Collection) object; - } - throw new IllegalArgumentException(object.getClass().toString()); - } - public void unset(final Object target) { final Collection targetCollection = objectAsCollection(target); @@ -102,6 +102,6 @@ public void unset(final Object target) @Override public String toString() { - return "collection item " + getPathElement(); + return "collection item " + getElementSelector(); } } diff --git a/src/main/java/de/danielbechler/diff/collection/CollectionItemElement.java b/src/main/java/de/danielbechler/diff/collection/CollectionItemElementSelector.java similarity index 85% rename from src/main/java/de/danielbechler/diff/collection/CollectionItemElement.java rename to src/main/java/de/danielbechler/diff/collection/CollectionItemElementSelector.java index 9a5c94e4..4c1043be 100644 --- a/src/main/java/de/danielbechler/diff/collection/CollectionItemElement.java +++ b/src/main/java/de/danielbechler/diff/collection/CollectionItemElementSelector.java @@ -16,17 +16,17 @@ package de.danielbechler.diff.collection; -import de.danielbechler.diff.Element; +import de.danielbechler.diff.ElementSelector; import de.danielbechler.util.Strings; /** * @author Daniel Bechler */ -public final class CollectionItemElement extends Element +public final class CollectionItemElementSelector extends ElementSelector { private final Object item; - public CollectionItemElement(final Object item) + public CollectionItemElementSelector(final Object item) { this.item = item; } @@ -59,7 +59,7 @@ public boolean equals(final Object o) return false; } - final CollectionItemElement that = (CollectionItemElement) o; + final CollectionItemElementSelector that = (CollectionItemElementSelector) o; if (item != null ? !item.equals(that.item) : that.item != null) { diff --git a/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java b/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java index f9b2c0e1..0336379e 100644 --- a/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java +++ b/src/main/java/de/danielbechler/diff/map/MapEntryAccessor.java @@ -17,7 +17,7 @@ package de.danielbechler.diff.map; import de.danielbechler.diff.Accessor; -import de.danielbechler.diff.Element; +import de.danielbechler.diff.ElementSelector; import de.danielbechler.util.Assert; import java.util.Map; @@ -76,12 +76,12 @@ public boolean equals(final Object o) @Override public String toString() { - return "map key " + getPathElement(); + return "map key " + getElementSelector(); } - public Element getPathElement() + public ElementSelector getElementSelector() { - return new MapKeyElement(referenceKey); + return new MapKeyElementSelector(referenceKey); } public Object get(final Object target) diff --git a/src/main/java/de/danielbechler/diff/map/MapKeyElement.java b/src/main/java/de/danielbechler/diff/map/MapKeyElementSelector.java similarity index 86% rename from src/main/java/de/danielbechler/diff/map/MapKeyElement.java rename to src/main/java/de/danielbechler/diff/map/MapKeyElementSelector.java index 2e49f715..cce98000 100644 --- a/src/main/java/de/danielbechler/diff/map/MapKeyElement.java +++ b/src/main/java/de/danielbechler/diff/map/MapKeyElementSelector.java @@ -16,17 +16,17 @@ package de.danielbechler.diff.map; -import de.danielbechler.diff.Element; +import de.danielbechler.diff.ElementSelector; import de.danielbechler.util.Strings; /** * @author Daniel Bechler */ -public final class MapKeyElement extends Element +public final class MapKeyElementSelector extends ElementSelector { private final Object key; - public MapKeyElement(final Object key) + public MapKeyElementSelector(final Object key) { this.key = key; } @@ -59,7 +59,7 @@ public boolean equals(final Object o) return false; } - final MapKeyElement that = (MapKeyElement) o; + final MapKeyElementSelector that = (MapKeyElementSelector) o; if (key != null ? !key.equals(that.key) : that.key != null) { diff --git a/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java b/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java index 76ff1e93..d25102ea 100644 --- a/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java +++ b/src/main/java/de/danielbechler/diff/visitor/PropertyVisitor.java @@ -39,7 +39,7 @@ public PropertyVisitor(final NodePath nodePath) public void accept(final DiffNode node, final Visit visit) { final NodePath differencePath = node.getPath(); - if (differencePath.isParentOf(nodePath)) + if (differencePath.matches(nodePath) || differencePath.isParentOf(nodePath)) { if (differencePath.matches(nodePath)) { diff --git a/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy b/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy index 9f2d8824..f3e29f2f 100644 --- a/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy +++ b/src/test/java/de/danielbechler/diff/CategoryServiceTest.groovy @@ -16,7 +16,7 @@ package de.danielbechler.diff -import de.danielbechler.diff.bean.BeanPropertyElement +import de.danielbechler.diff.bean.BeanPropertyElementSelector import spock.lang.Specification import static DiffNode.ROOT @@ -27,13 +27,13 @@ import static DiffNode.ROOT class CategoryServiceTest extends Specification { def categoryService = new CategoryService() def accessor = Mock(PropertyAwareAccessor) - def nodePath = NodePath.buildWith("foo") + def nodePath = NodePath.with("foo") def nodeType = Alliance def DiffNode node def DiffNode rootNode def "setup"() { - accessor.pathElement >> new BeanPropertyElement("foo") + accessor.elementSelector >> new BeanPropertyElementSelector("foo") rootNode = new DiffNode(ROOT, RootAccessor.instance, null) node = new DiffNode(rootNode, accessor, nodeType) } @@ -81,7 +81,7 @@ class CategoryServiceTest extends Specification { accessor.categories >> ["B"] and: - categoryService.ofNode(NodePath.buildRootPath()).toBe("A") + categoryService.ofNode(NodePath.withRoot()).toBe("A") expect: categoryService.resolveCategories(node) == ["B"] as Set diff --git a/src/test/java/de/danielbechler/diff/CircularReferenceDetectorTest.java b/src/test/java/de/danielbechler/diff/CircularReferenceDetectorTest.java index b5fd41c6..ec198f1a 100644 --- a/src/test/java/de/danielbechler/diff/CircularReferenceDetectorTest.java +++ b/src/test/java/de/danielbechler/diff/CircularReferenceDetectorTest.java @@ -46,26 +46,25 @@ public void testPush_does_nothing_with_null_object() throws Exception @Test public void testPush_adds_unknown_object_to_stack() throws Exception { - circularReferenceDetector.push("foo", NodePath.buildRootPath()); + circularReferenceDetector.push("foo", NodePath.withRoot()); assertThat(circularReferenceDetector.size(), is(1)); } @Test public void testPush_throws_CircularReferenceException_on_known_object() throws Exception { - circularReferenceDetector.push("foo", NodePath.buildRootPath()); + circularReferenceDetector.push("foo", NodePath.withRoot()); try { - circularReferenceDetector.push("foo", NodePath.createBuilder() - .withRoot() - .withPropertyName("test") + circularReferenceDetector.push("foo", NodePath.startBuilding() + .propertyName("test") .build()); Assertions.fail("Expected CircularReferenceException wasn't thrown."); } catch (CircularReferenceDetector.CircularReferenceException e) { final NodePath nodePath = e.getNodePath(); - assertThat(nodePath).isEqualTo(NodePath.buildRootPath()); + assertThat(nodePath).isEqualTo(NodePath.withRoot()); } } @@ -118,7 +117,7 @@ public void testKnows_returns_false_if_instance_has_been_pushed_but_later_remove public void knowsEqualObject_whenReferenceMatchingModeIsSetToEqualsMethod() { circularReferenceDetector = new CircularReferenceDetector(CircularReferenceDetector.ReferenceMatchingMode.EQUALS_METHOD); - circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.buildRootPath()); + circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.withRoot()); assertThat(circularReferenceDetector.knows(new ObjectWithString("foo"))).isTrue(); } @@ -126,7 +125,7 @@ public void knowsEqualObject_whenReferenceMatchingModeIsSetToEqualsMethod() public void removesEqualObject_whenReferenceMatchingModeIsSetToEqualsMethod() { circularReferenceDetector = new CircularReferenceDetector(CircularReferenceDetector.ReferenceMatchingMode.EQUALS_METHOD); - circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.buildRootPath()); + circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.withRoot()); circularReferenceDetector.remove(new ObjectWithString("foo")); assertThat(circularReferenceDetector.size()).isEqualTo(0); } @@ -135,7 +134,7 @@ public void removesEqualObject_whenReferenceMatchingModeIsSetToEqualsMethod() public void throwsException_onAttemptToPushEqualObject_whenReferenceMatchingModeIsSetToEqualsMethod() { circularReferenceDetector = new CircularReferenceDetector(CircularReferenceDetector.ReferenceMatchingMode.EQUALS_METHOD); - circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.buildRootPath()); - circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.buildRootPath()); + circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.withRoot()); + circularReferenceDetector.push(new ObjectWithString("foo"), NodePath.withRoot()); } } diff --git a/src/test/java/de/danielbechler/diff/ConfigNodeTest.groovy b/src/test/java/de/danielbechler/diff/ConfigNodeTest.groovy new file mode 100644 index 00000000..5084a68e --- /dev/null +++ b/src/test/java/de/danielbechler/diff/ConfigNodeTest.groovy @@ -0,0 +1,189 @@ +/* + * Copyright 2014 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 + +import de.danielbechler.diff.bean.BeanPropertyElementSelector +import spock.lang.FailsWith +import spock.lang.Specification +import spock.lang.Unroll + +import static de.danielbechler.diff.Inclusion.EXCLUDED +import static de.danielbechler.diff.Inclusion.INCLUDED + +/** + * Created by Daniel Bechler. + */ +class ConfigNodeTest extends Specification { + + def node = new ConfigNode() + + def 'getChild creates a child for an unknown selector'() { + given: + def childElementSelector = new BeanPropertyElementSelector('foo') + + when: "called with unknown selector" + def childNode = node.getChild(childElementSelector) + + then: "returns new child node" + childNode.elementSelector == childElementSelector + + and: "the next call returns the same child node" + node.getChild(childElementSelector) == childNode + } + + def 'getChild returns the same child on consecutive calls with the same selector'() { + setup: + def childElementSelector = new BeanPropertyElementSelector('foo') + + expect: "called with unknown selector" + node.getChild(childElementSelector) is node.getChild(childElementSelector) + + and: + node.getChild(childElementSelector).elementSelector == childElementSelector + } + + def 'getChild sets reference to parent when creating new child nodes'() { + when: + def childNode = node.getChild(new BeanPropertyElementSelector('foo')) + + then: + node == childNode.parent + } + + def 'getChild creates child node hierarchy for list of ElementSelectors'() { + when: + node.getNodeForPath(NodePath.with('foo', 'bar')) + then: + node.hasChild(new BeanPropertyElementSelector('foo')) + and: + node.getChild(new BeanPropertyElementSelector('foo')).hasChild(new BeanPropertyElementSelector('bar')) + } + + @FailsWith(value = IllegalArgumentException) + def 'getChild throws an exception when called with a RootElementSelector'() { + expect: + node.getChild(RootElementSelector.instance) + } + + def 'isIncluded returns false when neither the node itself nor any of its children are INCLUDED'() { + expect: + !node.isIncluded() + } + + def 'isIncluded returns true when the node itself is INCLUDED'() { + given: + node.inclusion = INCLUDED + + expect: + node.isIncluded() + } + + def 'isIncluded returns true when the node has at least one INCLUDED children'() { + given: + node.getNodeForPath(NodePath.with('foo', 'bar')).inclusion = INCLUDED + + expect: + node.isIncluded() + } + + def 'isIncluded returns false when closest parent with inclusion is EXCLUDED'() { + given: + node.getNodeForPath(NodePath.with('a')).inclusion = EXCLUDED + node.getNodeForPath(NodePath.with('a', 'b', 'c')).inclusion = INCLUDED + + expect: + !node.getNodeForPath(NodePath.with('a', 'b', 'c')).isIncluded() + } + + def 'isIncluded returns false when inclusion is EXCLUDED'() { + given: + node.getNodeForPath(NodePath.with('a')).inclusion = INCLUDED + node.getNodeForPath(NodePath.with('a', 'b', 'c')).inclusion = EXCLUDED + + expect: + !node.getNodeForPath(NodePath.with('a', 'b', 'c')).isIncluded() + } + + def 'isIncluded returns true when closest parent with explicit inclusion is INCLUDED'() { + given: + node.getNodeForPath(NodePath.with('a')).inclusion = INCLUDED + + expect: + node.getNodeForPath(NodePath.with('a', 'b', 'c')).isIncluded() + } + + def 'isIncluded returns false when node has no included children'() { + given: + node.getNodeForPath(NodePath.with('a', 'b')).inclusion = EXCLUDED + + expect: + !node.getNodeForPath(NodePath.with('a')).isIncluded() + } + + def 'isExcluded returns true when the node is EXCLUDED'() { + given: + node.inclusion = EXCLUDED + expect: + node.isExcluded() + } + + def 'isExcluded returns true when any of the parents is EXCLUDED'() { + given: + node.getNodeForPath(NodePath.with('a')).inclusion = EXCLUDED + node.getNodeForPath(NodePath.with('a', 'b', 'c')).inclusion = INCLUDED + expect: + node.getNodeForPath(NodePath.with('a', 'b', 'c')).isExcluded() + } + + def 'isExcluded returns false when neither a parent nor the node itself is excluded'() { + expect: + !node.getNodeForPath(NodePath.with('a', 'b', 'c')).isExcluded() + } + + @Unroll + def 'hasInclusion returns #expected when inclusion is set to #inclusion'() { + when: + node.setInclusion(inclusion) + + then: + node.hasInclusion() == expected + + where: + inclusion || expected + INCLUDED || true + EXCLUDED || true + null || false + } + + def 'getNodeForPath returns root node for root path'() { + expect: + node.getNodeForPath(NodePath.withRoot()) is node + } + + def 'getNodeForPath returns child node for child path'() { + expect: + node.getNodeForPath(NodePath.with('foo')).elementSelector == new BeanPropertyElementSelector('foo') + } + + def 'getNodeForPath always starts at root node'() { + given: + def childNode = node.getNodeForPath(NodePath.with('foo', 'bar')) + + expect: + childNode.getNodeForPath(NodePath.with('foo', 'bar')) is childNode + } +} diff --git a/src/test/java/de/danielbechler/diff/DiffNodeTest.java b/src/test/java/de/danielbechler/diff/DiffNodeTest.java index c0c3d112..8db39a63 100644 --- a/src/test/java/de/danielbechler/diff/DiffNodeTest.java +++ b/src/test/java/de/danielbechler/diff/DiffNodeTest.java @@ -17,9 +17,9 @@ package de.danielbechler.diff; import de.danielbechler.diff.bean.BeanPropertyAccessor; -import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.bean.BeanPropertyElementSelector; import de.danielbechler.diff.collection.CollectionItemAccessor; -import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.collection.CollectionItemElementSelector; import de.danielbechler.diff.helper.NodeAssertions; import de.danielbechler.diff.mock.ObjectDiffTest; import org.fest.assertions.api.Assertions; @@ -118,19 +118,18 @@ public void hasChanges_returns_true_when_child_has_changed() public void getPropertyPath_with_parent_node_should_return_canonical_path() { final DiffNode parentNode = new DiffNode(RootAccessor.getInstance(), String.class); - when(accessor.getPathElement()).thenReturn(new BeanPropertyElement("foo")); + when(accessor.getElementSelector()).thenReturn(new BeanPropertyElementSelector("foo")); final DiffNode root = new DiffNode(parentNode, accessor, Object.class); - assertThat(root.getPath()).isEqualTo(NodePath.buildWith("foo")); + assertThat(root.getPath()).isEqualTo(NodePath.with("foo")); } @Test public void getPropertyPath_without_parent_node_should_return_root_path() { final DiffNode root = new DiffNode(Object.class); - assertThat(root.getPath()).isEqualTo(NodePath - .createBuilder().withRoot().build()); + assertThat(root.getPath()).isEqualTo(NodePath.withRoot()); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -164,7 +163,7 @@ public void addChild_establishes_parent_child_relationship() throws Exception final DiffNode node2 = new DiffNode(new CollectionItemAccessor("foo"), String.class); node1.addChild(node2); assertThat(node1.childCount()).isEqualTo(1); - assertThat(node1.getChild(new CollectionItemElement("foo"))).isSameAs(node2); + assertThat(node1.getChild(new CollectionItemElementSelector("foo"))).isSameAs(node2); assertThat(node2.getParentNode()).isSameAs(node1); } diff --git a/src/test/java/de/danielbechler/diff/DifferDispatcherShould.java b/src/test/java/de/danielbechler/diff/DifferDispatcherShould.java index fd1d35d0..c7ddff2e 100644 --- a/src/test/java/de/danielbechler/diff/DifferDispatcherShould.java +++ b/src/test/java/de/danielbechler/diff/DifferDispatcherShould.java @@ -79,7 +79,7 @@ private void given_the_delegated_node_is_circular(final NodePath circularStartPa @Test public void assign_the_circular_start_path_if_the_delegated_node_is_circular() throws Exception { - final NodePath circularStartPath = NodePath.buildRootPath(); + final NodePath circularStartPath = NodePath.withRoot(); given_the_delegated_node_is_circular(circularStartPath); final DiffNode node = differDispatcher.dispatch(DiffNode.ROOT, instances, accessor); @@ -90,7 +90,7 @@ public void assign_the_circular_start_path_if_the_delegated_node_is_circular() t @Test public void mark_node_as_circular_if_the_delegated_node_is_circular() throws Exception { - given_the_delegated_node_is_circular(NodePath.buildRootPath()); + given_the_delegated_node_is_circular(NodePath.withRoot()); final DiffNode node = differDispatcher.dispatch(DiffNode.ROOT, instances, accessor); @@ -100,7 +100,7 @@ public void mark_node_as_circular_if_the_delegated_node_is_circular() throws Exc @Test public void pass_node_to_onCircularReferenceException_method_of_the_exceptionListener_if_the_delegated_node_is_circular() throws Exception { - given_the_delegated_node_is_circular(NodePath.buildRootPath()); + given_the_delegated_node_is_circular(NodePath.withRoot()); final DiffNode node = differDispatcher.dispatch(DiffNode.ROOT, instances, accessor); diff --git a/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy b/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy index 8523faad..47cc32d8 100644 --- a/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy +++ b/src/test/java/de/danielbechler/diff/InclusionServiceSpec.groovy @@ -16,7 +16,7 @@ package de.danielbechler.diff -import de.danielbechler.diff.bean.BeanPropertyElement +import de.danielbechler.diff.bean.BeanPropertyElementSelector import spock.lang.Specification /** @@ -27,12 +27,12 @@ class InclusionServiceSpec extends Specification { def categoryResolver = Mock(CategoryResolver) def accessor = Mock(PropertyAwareAccessor) def inclusionService = new InclusionService(categoryResolver) - def NodePath nodePath = NodePath.buildWith("foo") + def NodePath nodePath = NodePath.with("foo") def DiffNode rootNode def DiffNode node def "setup"() { - accessor.pathElement >> new BeanPropertyElement("foo") + accessor.elementSelector >> new BeanPropertyElementSelector("foo") rootNode = new DiffNode(RootAccessor.instance, null) node = new DiffNode(rootNode, accessor, null) categoryResolver.resolveCategories(_ as DiffNode) >> [] @@ -80,7 +80,7 @@ class InclusionServiceSpec extends Specification { def "isIgnored: should return 'false' if node is included via path"() { setup: - inclusionService.toInclude().nodes(nodePath) + inclusionService.toInclude().node(nodePath) expect: inclusionService.isIgnored(node) == false @@ -119,7 +119,7 @@ class InclusionServiceSpec extends Specification { def "isIgnored: should return 'true' if node is excluded via path"() { setup: - inclusionService.toExclude().nodes(nodePath) + inclusionService.toExclude().node(nodePath) expect: inclusionService.isIgnored(node) == true @@ -176,14 +176,14 @@ class InclusionServiceSpec extends Specification { def mockPropertyAwareAccessor(String name) { def propertyAwareAccessor = Mock(PropertyAwareAccessor) - propertyAwareAccessor.pathElement >> new BeanPropertyElement(name) + propertyAwareAccessor.elementSelector >> new BeanPropertyElementSelector(name) propertyAwareAccessor.propertyName >> name propertyAwareAccessor } def "isIgnored: should return 'false' for children of included nodes"() { given: - inclusionService.toInclude().nodes(NodePath.buildRootPath()) + inclusionService.toInclude().node(NodePath.withRoot()) expect: inclusionService.isIgnored(node) == false @@ -191,7 +191,7 @@ class InclusionServiceSpec extends Specification { def "isIgnored: should return 'true' for children of excluded nodes"() { given: - inclusionService.toExclude().nodes(NodePath.buildRootPath()) + inclusionService.toExclude().node(NodePath.withRoot()) expect: inclusionService.isIgnored(node) == true diff --git a/src/test/java/de/danielbechler/diff/IntrospectionServiceTest.groovy b/src/test/java/de/danielbechler/diff/IntrospectionServiceTest.groovy index 4c5c5ce0..01f987cf 100644 --- a/src/test/java/de/danielbechler/diff/IntrospectionServiceTest.groovy +++ b/src/test/java/de/danielbechler/diff/IntrospectionServiceTest.groovy @@ -179,7 +179,7 @@ class IntrospectionServiceTest extends Specification { given: def nodeIntrospector = Mock(Introspector) - introspectionService.ofNode(NodePath.buildRootPath()).toUse(nodeIntrospector) + introspectionService.ofNode(NodePath.withRoot()).toUse(nodeIntrospector) expect: introspectionService.introspectorForNode(rootNode) == nodeIntrospector diff --git a/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy b/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy index 2c92e96b..fcf0ab17 100644 --- a/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy +++ b/src/test/java/de/danielbechler/diff/ListDiffSpec.groovy @@ -16,7 +16,7 @@ package de.danielbechler.diff -import de.danielbechler.diff.collection.CollectionItemElement +import de.danielbechler.diff.collection.CollectionItemElementSelector import de.danielbechler.diff.visitor.PrintingVisitor import spock.lang.Ignore import spock.lang.Specification @@ -40,8 +40,8 @@ class ListDiffSpec extends Specification { then: node.visit(new PrintingVisitor(working, base)); - node.getChild(new CollectionItemElement("a")).getState() == DiffNode.State.CHANGED; - node.getChild(new CollectionItemElement("b")).getState() == DiffNode.State.CHANGED; + node.getChild(new CollectionItemElementSelector("a")).getState() == DiffNode.State.CHANGED; + node.getChild(new CollectionItemElementSelector("b")).getState() == DiffNode.State.CHANGED; where: base | working diff --git a/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java b/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java index cb4674f3..66a5c215 100644 --- a/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java +++ b/src/test/java/de/danielbechler/diff/NodePathBuilderTest.java @@ -16,9 +16,9 @@ package de.danielbechler.diff; -import de.danielbechler.diff.bean.BeanPropertyElement; -import de.danielbechler.diff.collection.CollectionItemElement; -import de.danielbechler.diff.map.MapKeyElement; +import de.danielbechler.diff.bean.BeanPropertyElementSelector; +import de.danielbechler.diff.collection.CollectionItemElementSelector; +import de.danielbechler.diff.map.MapKeyElementSelector; import org.testng.annotations.Test; import static org.fest.assertions.api.Assertions.assertThat; @@ -31,85 +31,68 @@ public class NodePathBuilderTest @Test public void testWithRoot() { - final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .build(); - assertThat(nodePath.getElements()).containsOnly(RootElement.getInstance()); + final NodePath nodePath = NodePath.startBuilding().build(); + assertThat(nodePath.getElementSelectors()).containsOnly(RootElementSelector.getInstance()); } @Test public void testWithElement() { - final CollectionItemElement element = new CollectionItemElement("foo"); - final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withElement(element) - .build(); - assertThat(nodePath.getElements()).containsSequence( - RootElement.getInstance(), + final CollectionItemElementSelector element = new CollectionItemElementSelector("foo"); + final NodePath nodePath = NodePath.startBuilding().element(element).build(); + assertThat(nodePath.getElementSelectors()).containsSequence( + RootElementSelector.getInstance(), element); } @Test public void testWithPropertyName() { - final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withPropertyName("foo", "bar") - .build(); - assertThat(nodePath.getElements()).containsSequence( - RootElement.getInstance(), - new BeanPropertyElement("foo"), - new BeanPropertyElement("bar") + final NodePath nodePath = NodePath.startBuilding().propertyName("foo", "bar").build(); + assertThat(nodePath.getElementSelectors()).containsSequence( + RootElementSelector.getInstance(), + new BeanPropertyElementSelector("foo"), + new BeanPropertyElementSelector("bar") ); } @Test public void testWithMapKey() { - final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withMapKey("foo") - .build(); - assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new MapKeyElement("foo")); + final NodePath nodePath = NodePath.startBuilding().mapKey("foo").build(); + assertThat(nodePath.getElementSelectors()).containsSequence(RootElementSelector.getInstance(), new MapKeyElementSelector("foo")); } @Test(expectedExceptions = IllegalArgumentException.class) public void testWithMapKey_throws_exception_when_key_is_null() { - NodePath.createBuilder().withRoot().withMapKey(null).build(); + NodePath.startBuilding().mapKey(null).build(); } @Test public void testWithCollectionItem() { - final NodePath nodePath = NodePath.createBuilder() - .withRoot() - .withCollectionItem("foo") - .build(); - assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new CollectionItemElement("foo")); + final NodePath nodePath = NodePath.startBuilding().collectionItem("foo").build(); + assertThat(nodePath.getElementSelectors()).containsSequence(RootElementSelector.getInstance(), new CollectionItemElementSelector("foo")); } @Test public void testWithPropertyPath() { - final NodePath nodePath = NodePath.createBuilder() - .withPropertyPath(NodePath - .buildWith("foo")) - .build(); - assertThat(nodePath.getElements()).containsSequence(RootElement.getInstance(), new BeanPropertyElement("foo")); + final NodePath nodePath = NodePath.startBuildingFrom(NodePath.with("foo")).build(); + assertThat(nodePath.getElementSelectors()).containsSequence(RootElementSelector.getInstance(), new BeanPropertyElementSelector("foo")); } @Test(expectedExceptions = IllegalArgumentException.class) public void testWithPropertyPath_throws_exception_when_property_path_is_null() { - NodePath.createBuilder().withPropertyPath(null).build(); + NodePath.startBuildingFrom(null).build(); } @Test public void testBuild_with_one_root_element_should_succeed() throws Exception { - final NodePath nodePath = NodePath.createBuilder().withRoot().build(); - assertThat(nodePath.getElements()).containsOnly(RootElement.getInstance()); + final NodePath nodePath = NodePath.withRoot(); + assertThat(nodePath.getElementSelectors()).containsOnly(RootElementSelector.getInstance()); } } diff --git a/src/test/java/de/danielbechler/diff/NodePathTest.groovy b/src/test/java/de/danielbechler/diff/NodePathTest.groovy new file mode 100644 index 00000000..fd1547f5 --- /dev/null +++ b/src/test/java/de/danielbechler/diff/NodePathTest.groovy @@ -0,0 +1,73 @@ +/* + * Copyright 2014 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 + +import de.danielbechler.diff.bean.BeanPropertyElementSelector +import spock.lang.Specification +import spock.lang.Unroll + +/** + * Created by Daniel Bechler. + */ +class NodePathTest extends Specification { + + def 'matches'() { + expect: + path.matches(givenPath) == expected + + where: + path | givenPath || expected + NodePath.with('a') | NodePath.with('a') || true + NodePath.with('a') | NodePath.with('z') || false + } + + @Unroll + def 'isParentOf returns #expected #message'() { + expect: + path.isParentOf(givenPath) == expected + + where: + path | givenPath || expected || message + NodePath.with('a') | NodePath.with('a', 'b') || true || "when given path is child" + NodePath.with('a', 'b') | NodePath.with('a') || false || "when given path is parent" + NodePath.with('a') | NodePath.with('a') || false || "when given path is equal" + NodePath.with('a', 'b') | NodePath.with('z', 'b', 'c') || false || 'when given path doesn\'t share the same ancestry' + } + + @Unroll + def 'isChildOf returns #expected #message'() { + expect: + path.isChildOf(otherPath) == expected + + where: + path | otherPath || expected || message + NodePath.with('a', 'b') | NodePath.with('a') || true || 'when given path is parent' + NodePath.with('a') | NodePath.with('a', 'b') || false || 'when given path is child' + NodePath.with('a') | NodePath.with('a') || false || 'when given path is equal' + NodePath.with('a', 'b') | NodePath.with('z', 'b') || false || 'when given path doesn\'t share the same ancestry' + } + + def 'getLastElementSelector returns last element from path'() { + expect: + path.getLastElementSelector() == expected + + where: + path || expected + NodePath.withRoot() || RootElementSelector.instance + NodePath.with('a', 'b', 'c') || new BeanPropertyElementSelector('c') + } +} diff --git a/src/test/java/de/danielbechler/diff/NodePathValueHolderTest.groovy b/src/test/java/de/danielbechler/diff/NodePathValueHolderTest.groovy index 95cd1208..08d77ced 100644 --- a/src/test/java/de/danielbechler/diff/NodePathValueHolderTest.groovy +++ b/src/test/java/de/danielbechler/diff/NodePathValueHolderTest.groovy @@ -26,9 +26,9 @@ class NodePathValueHolderTest extends Specification where: nodePath | value - NodePath.buildRootPath() | "foo1" - NodePath.buildWith("a") | "foo2" - NodePath.buildWith("a", "b") | "foo3" + NodePath.withRoot() | "foo1" + NodePath.with("a") | "foo2" + NodePath.with("a", "b") | "foo3" } def "should return null for unknown paths"() @@ -37,18 +37,18 @@ class NodePathValueHolderTest extends Specification def valueHolder = NodePathValueHolder.of(String) expect: - valueHolder.valueForNodePath(NodePath.buildWith("a")) == null + valueHolder.valueForNodePath(NodePath.with("a")) == null } def "should return accumulated values along path"() { given: def valueHolder = NodePathValueHolder.of(String) - valueHolder.put(NodePath.buildRootPath(), "foo1") - valueHolder.put(NodePath.buildWith("a"), "foo2") - valueHolder.put(NodePath.buildWith("a", "b"), "foo3") + valueHolder.put(NodePath.withRoot(), "foo1") + valueHolder.put(NodePath.with("a"), "foo2") + valueHolder.put(NodePath.with("a", "b"), "foo3") expect: - valueHolder.accumulatedValuesForNodePath(NodePath.buildWith("a", "b")) == ["foo1", "foo2", "foo3"] + valueHolder.accumulatedValuesForNodePath(NodePath.with("a", "b")) == ["foo1", "foo2", "foo3"] } } diff --git a/src/test/java/de/danielbechler/diff/RootAccessorTest.java b/src/test/java/de/danielbechler/diff/RootAccessorTest.java index c9c69c81..c742fcd9 100644 --- a/src/test/java/de/danielbechler/diff/RootAccessorTest.java +++ b/src/test/java/de/danielbechler/diff/RootAccessorTest.java @@ -56,7 +56,7 @@ public void unset_should_not_be_supported() @Test public void getPathElement_should_return_RootElement() { - assertThat(RootAccessor.getInstance().getPathElement(), is(instanceOf(RootElement.class))); + assertThat(RootAccessor.getInstance().getElementSelector(), is(instanceOf(RootElementSelector.class))); } @Test diff --git a/src/test/java/de/danielbechler/diff/RootElementTest.groovy b/src/test/java/de/danielbechler/diff/RootElementSelectorTest.groovy similarity index 72% rename from src/test/java/de/danielbechler/diff/RootElementTest.groovy rename to src/test/java/de/danielbechler/diff/RootElementSelectorTest.groovy index 3595efa2..8b098137 100644 --- a/src/test/java/de/danielbechler/diff/RootElementTest.groovy +++ b/src/test/java/de/danielbechler/diff/RootElementSelectorTest.groovy @@ -21,38 +21,38 @@ import spock.lang.Specification /** * @author Daniel Bechler */ -class RootElementTest extends Specification +class RootElementSelectorTest extends Specification { def 'equals should be true'() { expect: - RootElement.instance.equals(instance) + RootElementSelector.instance.equals(instance) where: - instance << [RootElement.instance, new RootElement()] + instance << [RootElementSelector.instance, new RootElementSelector()] } def 'equals should be false'() { expect: - !RootElement.instance.equals(null) + !RootElementSelector.instance.equals(null) } def 'toHumanReadableString should always return empty string'() { expect: - RootElement.instance.toHumanReadableString() == '' + RootElementSelector.instance.toHumanReadableString() == '' } def 'getInstance should always return the same instance'() { expect: - RootElement.instance == RootElement.instance + RootElementSelector.instance == RootElementSelector.instance } def 'hashCode should always be 0'() { expect: - RootElement.instance.hashCode() == 0 + RootElementSelector.instance.hashCode() == 0 } } diff --git a/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java b/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java index 4ad3367f..5a3ba695 100644 --- a/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java +++ b/src/test/java/de/danielbechler/diff/bean/BeanDifferShould.java @@ -39,7 +39,7 @@ import java.util.Date; import java.util.Set; -import static de.danielbechler.diff.NodePath.buildRootPath; +import static de.danielbechler.diff.NodePath.withRoot; import static de.danielbechler.diff.helper.NodeAssertions.assertThat; import static de.danielbechler.diff.helper.NodeMatchers.node; import static org.hamcrest.MatcherAssert.assertThat; @@ -171,17 +171,17 @@ public void return_untouched_node_if_working_and_base_are_the_same_instance() @Test public void compare_via_comparisonStrategy_if_present() { - when(comparisonStrategyResolver.resolveComparisonStrategy(node(buildRootPath()))).thenReturn(comparisonStrategy); + when(comparisonStrategyResolver.resolveComparisonStrategy(node(withRoot()))).thenReturn(comparisonStrategy); beanDiffer.compare(DiffNode.ROOT, instances); - verify(comparisonStrategy).compare(node(buildRootPath()), same(instances)); + verify(comparisonStrategy).compare(node(withRoot()), same(instances)); } @Test public void compare_via_introspector_if_introspectable() { - when(introspectableResolver.isIntrospectable(node(buildRootPath()))).thenReturn(true); + when(introspectableResolver.isIntrospectable(node(withRoot()))).thenReturn(true); beanDiffer.compare(DiffNode.ROOT, instances); @@ -264,14 +264,14 @@ private DiffNode given_DifferDispatcher_returns_Node_for_PropertyAccessor(final private void given_root_node_is_introspectable() { - doReturn(true).when(introspectableResolver).isIntrospectable(node(buildRootPath())); + doReturn(true).when(introspectableResolver).isIntrospectable(node(withRoot())); } private PropertyAwareAccessor given_introspector_returns_PropertyAccessor(final String propertyName) { - final BeanPropertyElement propertyElement = new BeanPropertyElement(propertyName); + final BeanPropertyElementSelector propertyElement = new BeanPropertyElementSelector(propertyName); final PropertyAwareAccessor propertyAccessor = mock(PropertyAwareAccessor.class); - when(propertyAccessor.getPathElement()).thenReturn(propertyElement); + when(propertyAccessor.getElementSelector()).thenReturn(propertyElement); final Set propertyAccessors = Collections.singleton(propertyAccessor); when(introspector.introspect(any(Class.class))).thenReturn(propertyAccessors); return propertyAccessor; diff --git a/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java b/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java index dd24b08b..5ea30a0b 100644 --- a/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java +++ b/src/test/java/de/danielbechler/diff/bean/BeanPropertyAccessorShould.java @@ -135,7 +135,7 @@ public void return_property_value_type() throws Exception @Test public void return_proper_path_element() throws Exception { - assertThat(accessor.getPathElement()).isEqualTo(new BeanPropertyElement("value")); + assertThat(accessor.getElementSelector()).isEqualTo(new BeanPropertyElementSelector("value")); } @Test diff --git a/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java b/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java index f680f658..fa6343bb 100644 --- a/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java +++ b/src/test/java/de/danielbechler/diff/bean/StandardBeanIntrospectorTest.java @@ -80,36 +80,36 @@ public void testIntrospectWithPropertyAnnotations() final Iterable accessors = introspector.introspect(ObjectWithPropertyAnnotations.class); for (final PropertyAwareAccessor accessor : accessors) { - if (accessor.getPathElement().equals(new BeanPropertyElement("ignored"))) + if (accessor.getElementSelector().equals(new BeanPropertyElementSelector("ignored"))) { assertThat(accessor.isExcluded(), is(true)); } - else if (accessor.getPathElement().equals(new BeanPropertyElement("equalsOnly"))) + else if (accessor.getElementSelector().equals(new BeanPropertyElementSelector("equalsOnly"))) { assertThat(accessor.getComparisonStrategy(), instanceOf(EqualsOnlyComparisonStrategy.class)); } - else if (accessor.getPathElement().equals(new BeanPropertyElement("categorized"))) + else if (accessor.getElementSelector().equals(new BeanPropertyElementSelector("categorized"))) { assertThat(accessor.getCategories().size(), is(1)); assertThat(accessor.getCategories(), IsEqual.equalTo(Collections.setOf("foo"))); } - else if (accessor.getPathElement().equals(new BeanPropertyElement("item"))) + else if (accessor.getElementSelector().equals(new BeanPropertyElementSelector("item"))) { assertThat(accessor.getComparisonStrategy(), IsNull.nullValue()); assertThat(accessor.isExcluded(), is(false)); assertThat(accessor.getCategories().isEmpty(), is(true)); } - else if (accessor.getPathElement().equals(new BeanPropertyElement("key"))) + else if (accessor.getElementSelector().equals(new BeanPropertyElementSelector("key"))) { // no op } - else if (accessor.getPathElement().equals(new BeanPropertyElement("value"))) + else if (accessor.getElementSelector().equals(new BeanPropertyElementSelector("value"))) { // no op } else { - org.testng.Assert.fail("Unexpected accessor: " + accessor.getPathElement()); + org.testng.Assert.fail("Unexpected accessor: " + accessor.getElementSelector()); } } } @@ -119,7 +119,7 @@ public void testIntrospectWithInheritedPropertyAnnotations() { final Iterable accessors = introspector.introspect(ObjectWithInheritedPropertyAnnotation.class); final PropertyAwareAccessor accessor = accessors.iterator().next(); - assertThat((BeanPropertyElement) accessor.getPathElement(), IsEqual.equalTo(new BeanPropertyElement("value"))); + assertThat((BeanPropertyElementSelector) accessor.getElementSelector(), IsEqual.equalTo(new BeanPropertyElementSelector("value"))); assertThat(accessor.isExcluded(), is(true)); } diff --git a/src/test/java/de/danielbechler/diff/collection/CollectionDifferShould.java b/src/test/java/de/danielbechler/diff/collection/CollectionDifferShould.java index aa2e504b..8e718d37 100644 --- a/src/test/java/de/danielbechler/diff/collection/CollectionDifferShould.java +++ b/src/test/java/de/danielbechler/diff/collection/CollectionDifferShould.java @@ -113,7 +113,7 @@ public void delegate_added_items_to_dispatcher_when_instance_has_been_added() node = collectionDiffer.compare(DiffNode.ROOT, instances); - verify(differDispatcher).dispatch(node(NodePath.buildRootPath()), same(instances), collectionItemAccessor(addedItem)); + verify(differDispatcher).dispatch(node(NodePath.withRoot()), same(instances), collectionItemAccessor(addedItem)); verifyNoMoreInteractions(differDispatcher); } @@ -136,7 +136,7 @@ public void delegate_removed_items_to_dispatcher_when_instance_has_been_removed( node = collectionDiffer.compare(DiffNode.ROOT, instances); - verify(differDispatcher).dispatch(node(NodePath.buildRootPath()), same(instances), collectionItemAccessor(removedItem)); + verify(differDispatcher).dispatch(node(NodePath.withRoot()), same(instances), collectionItemAccessor(removedItem)); verifyNoMoreInteractions(differDispatcher); } @@ -147,7 +147,7 @@ public void compare_using_comparison_strategy_if_available() node = collectionDiffer.compare(DiffNode.ROOT, instances); - verify(comparisonStrategy, atLeastOnce()).compare(node(NodePath.buildRootPath()), same(instances)); + verify(comparisonStrategy, atLeastOnce()).compare(node(NodePath.withRoot()), same(instances)); } @Test @@ -158,7 +158,7 @@ public void delegate_added_items_to_dispatcher_when_performaing_deep_comparison( node = collectionDiffer.compare(DiffNode.ROOT, instances); - verify(differDispatcher).dispatch(node(NodePath.buildRootPath()), same(instances), collectionItemAccessor(addedItem)); + verify(differDispatcher).dispatch(node(NodePath.withRoot()), same(instances), collectionItemAccessor(addedItem)); verifyNoMoreInteractions(differDispatcher); } @@ -170,7 +170,7 @@ public void delegate_removed_items_to_dispatcher_when_performaing_deep_compariso node = collectionDiffer.compare(DiffNode.ROOT, instances); - verify(differDispatcher).dispatch(node(NodePath.buildRootPath()), same(instances), collectionItemAccessor(removedItem)); + verify(differDispatcher).dispatch(node(NodePath.withRoot()), same(instances), collectionItemAccessor(removedItem)); verifyNoMoreInteractions(differDispatcher); } @@ -182,7 +182,7 @@ public void delegate_known_items_to_dispatcher_when_performaing_deep_comparison( node = collectionDiffer.compare(DiffNode.ROOT, instances); - verify(differDispatcher).dispatch(node(NodePath.buildRootPath()), same(instances), collectionItemAccessor(knownItem)); + verify(differDispatcher).dispatch(node(NodePath.withRoot()), same(instances), collectionItemAccessor(knownItem)); verifyNoMoreInteractions(differDispatcher); } diff --git a/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java b/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java index f3f27b4c..3e994b4d 100644 --- a/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java +++ b/src/test/java/de/danielbechler/diff/collection/CollectionItemAccessorShould.java @@ -16,7 +16,7 @@ package de.danielbechler.diff.collection; -import de.danielbechler.diff.Element; +import de.danielbechler.diff.ElementSelector; import de.danielbechler.diff.mock.ObjectWithIdentityAndValue; import org.fest.assertions.core.Condition; import org.testng.annotations.BeforeMethod; @@ -108,11 +108,11 @@ public void fail_remove_if_target_object_is_not_a_collection() throws Exception @Test public void return_proper_path_element() { - final CollectionItemElement expectedPathElement = new CollectionItemElement(referenceItem); + final CollectionItemElementSelector expectedPathElement = new CollectionItemElementSelector(referenceItem); - final Element actualPathElement = accessor.getPathElement(); + final ElementSelector actualPathElementSelector = accessor.getElementSelector(); - assertThat(actualPathElement).isEqualTo(expectedPathElement); + assertThat(actualPathElementSelector).isEqualTo(expectedPathElement); } @Test diff --git a/src/test/java/de/danielbechler/diff/collection/CollectionItemElementTest.groovy b/src/test/java/de/danielbechler/diff/collection/CollectionItemElementSelectorTest.groovy similarity index 59% rename from src/test/java/de/danielbechler/diff/collection/CollectionItemElementTest.groovy rename to src/test/java/de/danielbechler/diff/collection/CollectionItemElementSelectorTest.groovy index 2d79b4e0..33642e53 100644 --- a/src/test/java/de/danielbechler/diff/collection/CollectionItemElementTest.groovy +++ b/src/test/java/de/danielbechler/diff/collection/CollectionItemElementSelectorTest.groovy @@ -1,21 +1,21 @@ package de.danielbechler.diff.collection -import de.danielbechler.diff.map.MapKeyElement +import de.danielbechler.diff.map.MapKeyElementSelector import spock.lang.Specification /** * @author Daniel Bechler */ -class CollectionItemElementTest extends Specification { +class CollectionItemElementSelectorTest extends Specification { def 'can be created with "null" item'() { expect: - new CollectionItemElement(null) != null + new CollectionItemElementSelector(null) != null } def 'should equal other CollectionElement if items are equal'() { setup: - def element = new CollectionItemElement("foo") - def equalElement = new CollectionItemElement("foo") + def element = new CollectionItemElementSelector("foo") + def equalElement = new CollectionItemElementSelector("foo") expect: element.equals(equalElement) @@ -23,8 +23,8 @@ class CollectionItemElementTest extends Specification { def 'should not equal elements of different class'() { setup: - def element = new CollectionItemElement("foo") - def mapElement = new MapKeyElement("foo") + def element = new CollectionItemElementSelector("foo") + def mapElement = new MapKeyElementSelector("foo") expect: !element.equals(mapElement) @@ -32,7 +32,7 @@ class CollectionItemElementTest extends Specification { def 'should not equal null'() { setup: - def element = new CollectionItemElement("foo") + def element = new CollectionItemElementSelector("foo") expect: !element.equals(null) @@ -41,7 +41,7 @@ class CollectionItemElementTest extends Specification { def 'should have same hashCode as item'() { setup: def item = "foo" - def element = new CollectionItemElement(item) + def element = new CollectionItemElementSelector(item) expect: element.hashCode() == item.hashCode() @@ -50,7 +50,7 @@ class CollectionItemElementTest extends Specification { def 'should provide accessor for item'() { setup: def item = "foo" - def element = new CollectionItemElement(item) + def element = new CollectionItemElementSelector(item) expect: element.getItem() == item @@ -58,6 +58,6 @@ class CollectionItemElementTest extends Specification { def 'should have proper toString() representation'() { expect: "string representation of the item should be converted to single line" - new CollectionItemElement("foo\nbar").toString() == '[foo \\ bar]' + new CollectionItemElementSelector("foo\nbar").toString() == '[foo \\ bar]' } } diff --git a/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java b/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java index e3c03b4d..523e43a5 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeAssertions.java @@ -17,9 +17,9 @@ package de.danielbechler.diff.helper; import de.danielbechler.diff.DiffNode; -import de.danielbechler.diff.Element; +import de.danielbechler.diff.ElementSelector; import de.danielbechler.diff.NodePath; -import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.collection.CollectionItemElementSelector; import de.danielbechler.diff.visitor.PropertyVisitor; import org.fest.assertions.api.Assertions; import org.fest.assertions.core.Condition; @@ -49,7 +49,7 @@ private NodeAssertionLanguage(final DiffNode rootNode) public Syntax.AssertNode root() { this.selectedNode = rootNode; - this.nodePath = NodePath.createBuilder().withRoot().build(); + this.nodePath = NodePath.withRoot(); return this; } @@ -77,17 +77,17 @@ public Syntax.AssertNode child(final NodePath.AppendableBuilder propertyPathBuil public Syntax.AssertNode child(final String propertyName, final String... propertyNames) { - return child(NodePath.buildWith(propertyName, propertyNames)); + return child(NodePath.with(propertyName, propertyNames)); } - public Syntax.AssertNode child(final Element pathElement) + public Syntax.AssertNode child(final ElementSelector pathElementSelector) { - return child(NodePath.createBuilder().withRoot().withElement(pathElement)); + return child(NodePath.startBuilding().element(pathElementSelector)); } public Syntax.AssertNode collectionChild(final Object referenceItem) { - return child(new CollectionItemElement(referenceItem)); + return child(new CollectionItemElementSelector(referenceItem)); } public Syntax.AssertNode doesExist() @@ -271,7 +271,7 @@ public interface SelectNode AssertNode child(String propertyName, String... propertyPathElements); - AssertNode child(Element pathElement); + AssertNode child(ElementSelector pathElementSelector); AssertNode collectionChild(Object referenceItem); } diff --git a/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java b/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java index f7706e82..b12ec9a5 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeAssertionsTest.java @@ -20,7 +20,7 @@ import de.danielbechler.diff.DiffNode; import de.danielbechler.diff.NodePath; import de.danielbechler.diff.ObjectDifferBuilder; -import de.danielbechler.diff.bean.BeanPropertyElement; +import de.danielbechler.diff.bean.BeanPropertyElementSelector; import de.danielbechler.diff.mock.ObjectWithString; import org.testng.annotations.Test; @@ -79,7 +79,7 @@ public void testAssertThat_node_has_children_fails_when_no_children_are_present( public void testAssertThat_child_at_property_names_does_exist_succeeds_when_child_exists() { final Accessor accessor = mock(Accessor.class); - when(accessor.getPathElement()).thenReturn(new BeanPropertyElement("value")); + when(accessor.getElementSelector()).thenReturn(new BeanPropertyElementSelector("value")); final DiffNode node = new DiffNode(ObjectWithString.class); final DiffNode child = new DiffNode(node, accessor, String.class); node.addChild(child); @@ -98,13 +98,13 @@ public void testAssertThat_child_at_property_path_does_exist_succeeds_when_child final ObjectWithString working = new ObjectWithString("foo"); final ObjectWithString base = new ObjectWithString("bar"); final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); - assertThat(node).child(NodePath.buildWith("value")).doesExist(); + assertThat(node).child(NodePath.with("value")).doesExist(); } @Test(expectedExceptions = AssertionError.class) public void testAssertThat_child_at_property_path_does_exist_fails_when_child_doesnt_exist() { - assertThat(null).child(NodePath.buildWith("value")).doesExist(); + assertThat(null).child(NodePath.with("value")).doesExist(); } @Test @@ -113,13 +113,13 @@ public void testAssertThat_child_at_property_path_builder_does_exist_succeeds_wh final ObjectWithString working = new ObjectWithString("foo"); final ObjectWithString base = new ObjectWithString("bar"); final DiffNode node = ObjectDifferBuilder.buildDefault().compare(working, base); - assertThat(node).child(NodePath.createBuilder().withRoot().withPropertyName("value")).doesExist(); + assertThat(node).child(NodePath.startBuilding().propertyName("value")).doesExist(); } @Test(expectedExceptions = AssertionError.class) public void testAssertThat_child_at_property_path_builder_does_exist_fails_when_child_doesnt_exist() { - assertThat(null).child(NodePath.createBuilder().withRoot().withPropertyName("value")).doesExist(); + assertThat(null).child(NodePath.startBuilding().propertyName("value")).doesExist(); } @Test diff --git a/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java b/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java index 40adb05d..be3b6848 100644 --- a/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java +++ b/src/test/java/de/danielbechler/diff/helper/NodeMatchers.java @@ -20,7 +20,7 @@ import de.danielbechler.diff.DiffNode; import de.danielbechler.diff.NodePath; import de.danielbechler.diff.collection.CollectionItemAccessor; -import de.danielbechler.diff.collection.CollectionItemElement; +import de.danielbechler.diff.collection.CollectionItemElementSelector; import org.fest.assertions.core.Condition; import org.mockito.ArgumentMatcher; @@ -46,8 +46,8 @@ public boolean matches(final Object argument) { @SuppressWarnings("TypeMayBeWeakened") final CollectionItemAccessor accessor = (CollectionItemAccessor) argument; - final CollectionItemElement collectionItemElement = new CollectionItemElement(item); - return accessor.getPathElement().equals(collectionItemElement); + final CollectionItemElementSelector collectionItemElement = new CollectionItemElementSelector(item); + return accessor.getElementSelector().equals(collectionItemElement); } return false; } diff --git a/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy b/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy index d940c265..73b23083 100644 --- a/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy +++ b/src/test/java/de/danielbechler/diff/map/MapEntryAccessorTest.groovy @@ -41,7 +41,7 @@ class MapEntryAccessorTest extends Specification { def "provide access to its path element"() { expect: - accessor.getPathElement() instanceof MapKeyElement + accessor.getElementSelector() instanceof MapKeyElementSelector } def "provide write access to referenced value in any map"() { diff --git a/src/test/java/de/danielbechler/diff/map/MapKeyElementTest.groovy b/src/test/java/de/danielbechler/diff/map/MapKeyElementSelectorTest.groovy similarity index 75% rename from src/test/java/de/danielbechler/diff/map/MapKeyElementTest.groovy rename to src/test/java/de/danielbechler/diff/map/MapKeyElementSelectorTest.groovy index e65e7163..0f176f95 100644 --- a/src/test/java/de/danielbechler/diff/map/MapKeyElementTest.groovy +++ b/src/test/java/de/danielbechler/diff/map/MapKeyElementSelectorTest.groovy @@ -19,11 +19,11 @@ package de.danielbechler.diff.map import spock.lang.Specification import spock.lang.Unroll -class MapKeyElementTest extends Specification { +class MapKeyElementSelectorTest extends Specification { def "should be constructable with 'null'"() { when: - new MapKeyElement(null) + new MapKeyElementSelector(null) then: notThrown(Throwable) @@ -31,7 +31,7 @@ class MapKeyElementTest extends Specification { def "should be equal to self"() { setup: - def element = new MapKeyElement('foo') + def element = new MapKeyElementSelector('foo') expect: element.equals(element) @@ -40,8 +40,8 @@ class MapKeyElementTest extends Specification { @Unroll def "equals should be #expected when keys are #key and #anotherKey"() { given: - def element = new MapKeyElement(key) - def anotherElement = new MapKeyElement(anotherKey) + def element = new MapKeyElementSelector(key) + def anotherElement = new MapKeyElementSelector(anotherKey) expect: element.equals(anotherElement) == expected @@ -57,12 +57,12 @@ class MapKeyElementTest extends Specification { def "should not be equal to 'null'"() { expect: - !new MapKeyElement('foo').equals(null) + !new MapKeyElementSelector('foo').equals(null) } def "should not be equal to different types"() { expect: - !new MapKeyElement('foo').equals(2) + !new MapKeyElementSelector('foo').equals(2) } def "hashCode should equal the hashCode of the key"() { @@ -70,17 +70,17 @@ class MapKeyElementTest extends Specification { def key = new Object() expect: - new MapKeyElement(key).hashCode() == key.hashCode() + new MapKeyElementSelector(key).hashCode() == key.hashCode() } def "hashCode should be null when key is null"() { expect: - new MapKeyElement(null).hashCode() == 0 + new MapKeyElementSelector(null).hashCode() == 0 } def "toString should return the same as toHumanReadableString()"() { setup: - def element = new MapKeyElement('foo') + def element = new MapKeyElementSelector('foo') expect: element.toString() == element.toHumanReadableString() @@ -91,11 +91,11 @@ class MapKeyElementTest extends Specification { def key = new Object() expect: - new MapKeyElement(key).key.is(key) + new MapKeyElementSelector(key).key.is(key) } def "toHumanReadableString should return the string representation of the key (without line breaks)"() { expect: - new MapKeyElement("foo,\nbar").toHumanReadableString() == "{foo, \\ bar}" + new MapKeyElementSelector("foo,\nbar").toHumanReadableString() == "{foo, \\ bar}" } } diff --git a/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java b/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java index ee529893..08525320 100644 --- a/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java +++ b/src/test/java/de/danielbechler/diff/primitive/PrimitiveDifferTest.java @@ -146,7 +146,7 @@ private Instances given_instances(final Class type, final Object fresh) { when(accessor.getType()).then(returnClass(type)); - when(accessor.getPathElement()).thenReturn(new BeanPropertyElement("ignored")); + when(accessor.getElementSelector()).thenReturn(new BeanPropertyElementSelector("ignored")); instances = mock(Instances.class); when(instances.getWorking()).thenReturn(working); when(instances.getBase()).thenReturn(base); diff --git a/src/test/java/de/danielbechler/diff/visitor/ToMapPrintingVisitorTest.java b/src/test/java/de/danielbechler/diff/visitor/ToMapPrintingVisitorTest.java index e660196b..44499ec1 100644 --- a/src/test/java/de/danielbechler/diff/visitor/ToMapPrintingVisitorTest.java +++ b/src/test/java/de/danielbechler/diff/visitor/ToMapPrintingVisitorTest.java @@ -44,7 +44,7 @@ public void testGetMessages_returns_map_with_accepted_messages() throws Exceptio final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.CHANGED); node.visit(visitor); - assertThat(visitor.getMessages()).hasSize(1).containsKey(NodePath.buildRootPath()); + assertThat(visitor.getMessages()).hasSize(1).containsKey(NodePath.withRoot()); } @Test @@ -60,7 +60,7 @@ public void testGetMessages_returns_modifiable_map_when_messages_exist() throws node.setState(DiffNode.State.CHANGED); node.visit(visitor); assertThat(visitor.getMessages()).hasSize(1); - visitor.getMessages().put(NodePath.buildWith("foo"), "bar"); + visitor.getMessages().put(NodePath.with("foo"), "bar"); assertThat(visitor.getMessages()).hasSize(2); } @@ -68,7 +68,7 @@ public void testGetMessages_returns_modifiable_map_when_messages_exist() throws public void testGetMessages_returns_modifiable_map_when_no_messages_exist() throws Exception { assertThat(visitor.getMessages()).hasSize(0); - visitor.getMessages().put(NodePath.buildWith("foo"), "bar"); + visitor.getMessages().put(NodePath.with("foo"), "bar"); assertThat(visitor.getMessages()).hasSize(1); } @@ -78,7 +78,7 @@ public void testGetMessage_returns_message_when_message_present() throws Excepti final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.CHANGED); node.visit(visitor); - final NodePath path = NodePath.buildRootPath(); + final NodePath path = NodePath.withRoot(); assertThat(visitor.getMessage(path)).isNotEmpty(); } @@ -88,7 +88,7 @@ public void testGetMessage_returns_null_when_message_absend() throws Exception final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.CHANGED); node.visit(visitor); - final NodePath path = NodePath.buildWith("doesn't-exist"); + final NodePath path = NodePath.with("doesn't-exist"); assertThat(visitor.getMessage(path)).isNull(); } @@ -113,7 +113,7 @@ public void testGetMessagesAsString_returns_line_break_separated_list_of_message final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.CHANGED); node.visit(visitor); - assertThat(visitor.getMessagesAsString()).isEqualTo(visitor.getMessage(NodePath.buildRootPath()) + "\n"); + assertThat(visitor.getMessagesAsString()).isEqualTo(visitor.getMessage(NodePath.withRoot()) + "\n"); } @Test @@ -128,7 +128,7 @@ public void testToString_is_analogous_to_getMessagesAsString() throws Exception final DiffNode node = new DiffNode(String.class); node.setState(DiffNode.State.CHANGED); node.visit(visitor); - assertThat(visitor.toString()).isEqualTo(visitor.getMessage(NodePath.buildRootPath()) + "\n"); + assertThat(visitor.toString()).isEqualTo(visitor.getMessage(NodePath.withRoot()) + "\n"); } @Test From 06faec0ef03974ff0b50a2ede6dd31f480cd4668 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sun, 30 Mar 2014 16:04:58 +0200 Subject: [PATCH 016/215] Upgraded Travic CI button to SVG version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 204c2457..735a1130 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ `java-object-diff` is a simple, yet powerful library to find differences between Java objects. It takes two objects and generates a tree structure that represents any differences between the objects and their children. This tree can then be traversed to extract more information or apply changes to the underlying data structures. -[![Build Status](https://travis-ci.org/SQiShER/java-object-diff.png?branch=master)](https://travis-ci.org/SQiShER/java-object-diff) +[![Build Status](https://travis-ci.org/SQiShER/java-object-diff.svg?branch=master)](https://travis-ci.org/SQiShER/java-object-diff) ## Features From cf6a782dd1ab0a925e1f6b04fe871e1c95cb292b Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 5 Apr 2014 16:05:19 +0200 Subject: [PATCH 017/215] Java Syntax Highlighting --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 735a1130..96dfd85c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,9 @@ While all this isn't exactly rocket science, it is complex enough to add quite a This library will hide all the complexities of deep object comparison behind one line of code: - Node root = ObjectDifferFactory.getInstance().compare(workingObject, baseObject); +```java +Node root = ObjectDifferFactory.getInstance().compare(workingObject, baseObject); +``` This generates a tree structure of the given object type and lets you traverse its nodes via visitors. Each node represents one property (or collection item) of the underlying object and tells you exactly if and how the value differs from the base version. It also provides accessors to read, write and remove the value from or to any given instance. This way, all you need to worry about is **how to treat** changes and **not how to find** them. From d6167c58b6b41d3a465365b4d5a9d82a777a63b1 Mon Sep 17 00:00:00 2001 From: Daniel Bechler Date: Sat, 5 Apr 2014 23:32:04 +0200 Subject: [PATCH 018/215] Inclusion stuff works now. Tests still unfinished. --- .idea/compiler.xml | 2 +- .idea/inspectionProfiles/Project_Default.xml | 5 + .idea/misc.xml | 2 +- .../diff/InclusionAndExclusionITCase.groovy | 81 ++++- .../diff/AnyElementSelector.java | 3 + .../danielbechler/diff/CategoryService.java | 31 +- .../de/danielbechler/diff/ConfigNode.java | 26 +- .../danielbechler/diff/InclusionService.java | 21 +- .../diff/NodePathValueHolder.java | 3 + .../diff/CategoryServiceTest.groovy | 146 ++++---- .../diff/InclusionServiceSpec.groovy | 337 +++++++++--------- .../de/danielbechler/util/ClassesTest.java | 123 ++++--- 12 files changed, 437 insertions(+), 343 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 777ea293..50f4e6e5 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -31,7 +31,7 @@ - + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index c3d8c7ae..cac9ca25 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -129,6 +129,10 @@