Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 101528c

Browse files
author
Gabor Nagy
committed
IdentityStrategy / EqualsIdentityStrategy
1 parent 02b7b68 commit 101528c

File tree

14 files changed

+1003
-180
lines changed

14 files changed

+1003
-180
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/*
2+
* Copyright 2015 Daniel Bechler
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package de.danielbechler.diff.identity
18+
19+
import de.danielbechler.diff.ObjectDifferBuilder
20+
import de.danielbechler.diff.comparison.IdentityStrategy
21+
import de.danielbechler.diff.node.DiffNode
22+
import de.danielbechler.diff.node.Visit
23+
import de.danielbechler.diff.path.NodePath
24+
import de.danielbechler.diff.selector.CollectionItemElementSelector
25+
import de.danielbechler.diff.selector.MapKeyElementSelector
26+
import groovy.transform.EqualsAndHashCode
27+
import groovy.transform.ToString
28+
import spock.lang.Specification
29+
import spock.lang.Unroll
30+
31+
class IdentityStrategyConfigIT extends Specification {
32+
33+
def working = new Container(
34+
productMap: [
35+
"PROD1": new Product(
36+
id: "PROD1",
37+
code: "Code1",
38+
productVersions: [
39+
new ProductVersion(id: "ID1", code: "PVC1"),
40+
new ProductVersion(id: "ID2", code: "PVC2")
41+
]),
42+
"PROD2": new Product(
43+
id: "PROD2",
44+
code: "Code2",
45+
productVersions: [
46+
new ProductVersion(id: "ID1", code: "PVC1"),
47+
new ProductVersion(id: "ID2", code: "PVC2")
48+
])
49+
],
50+
otherMap: [
51+
"PROD1": new Product(
52+
id: "PROD1",
53+
code: "Code1",
54+
productVersions: [
55+
new ProductVersion(id: "ID1", code: "PVC1"),
56+
new ProductVersion(id: "ID2", code: "PVC2")
57+
]),
58+
"PROD2": new Product(
59+
id: "PROD2",
60+
code: "Code2",
61+
productVersions: [
62+
new ProductVersion(id: "ID1", code: "PVC1"),
63+
new ProductVersion(id: "ID2", code: "PVC2")
64+
])
65+
]
66+
)
67+
68+
def base = new Container(
69+
productMap: [
70+
"PROD1": new Product(
71+
id: "PROD1",
72+
code: "Code1",
73+
productVersions: [
74+
new ProductVersion(id: "ID3", code: "PVC1"),
75+
new ProductVersion(id: "ID4", code: "PVC2")
76+
]),
77+
"PROD2": new Product(
78+
id: "PROD2",
79+
code: "Code2",
80+
productVersions: [
81+
new ProductVersion(id: "ID3", code: "PVC1"),
82+
new ProductVersion(id: "ID4", code: "PVC2")
83+
])
84+
],
85+
otherMap: [
86+
"PROD1": new Product(
87+
id: "PROD1",
88+
code: "Code1",
89+
productVersions: [
90+
new ProductVersion(id: "ID1", code: "PVC1"),
91+
new ProductVersion(id: "ID2", code: "PVC2")
92+
]),
93+
"PROD2": new Product(
94+
id: "PROD2",
95+
code: "Code2",
96+
productVersions: [
97+
new ProductVersion(id: "ID1", code: "PVC1"),
98+
new ProductVersion(id: "ID2", code: "PVC2")
99+
])
100+
]
101+
)
102+
103+
def 'Without IdentityStrategy'() {
104+
when:
105+
def node = ObjectDifferBuilder
106+
.startBuilding()
107+
.filtering().returnNodesWithState(DiffNode.State.UNTOUCHED).and()
108+
.build().compare(working, base);
109+
then: "High level nodes all changed"
110+
// print(node, working, base)
111+
node.getChild("otherMap").untouched
112+
node.getChild("productMap").changed
113+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).changed
114+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions").changed
115+
and: "ID1 and ID2 are ADDED"
116+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
117+
.getChild(PV1Selector).added
118+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
119+
.getChild(PV2Selector).added
120+
and: "ID3 and ID4 are REMOVED"
121+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
122+
.getChild(PV3Selector).removed
123+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
124+
.getChild(PV4Selector).removed
125+
}
126+
127+
def 'PropertyOfType configuration WITH IdentityStrategy'() {
128+
when:
129+
def node = ObjectDifferBuilder
130+
.startBuilding()
131+
.comparison().ofCollectionItems(Product, "productVersions").toUse(codeIdentity).and()
132+
.filtering().returnNodesWithState(DiffNode.State.UNTOUCHED).and()
133+
.build().compare(working, base);
134+
then: "High level nodes"
135+
// print(node, working, base)
136+
node.getChild("otherMap").untouched
137+
node.getChild("productMap").changed
138+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).changed
139+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions").changed
140+
and: "ID1 and ID2 are CHANGED"
141+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
142+
.getChild(PV1CodeSelector).changed
143+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
144+
.getChild(PV1CodeSelector).changed
145+
and: "id changed, code untouched"
146+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
147+
.getChild(PV1CodeSelector).getChild("id").changed
148+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
149+
.getChild(PV1CodeSelector).getChild("code").untouched
150+
}
151+
152+
def 'OfNode configuration WITH IdentityStrategy'() {
153+
when:
154+
def node = ObjectDifferBuilder
155+
.startBuilding()
156+
.comparison().ofCollectionItems(
157+
// this is not very useful without wildcards on maps and collections...
158+
NodePath.startBuilding().propertyName("productMap").mapKey("PROD1")
159+
.propertyName("productVersions").build()
160+
).toUse(codeIdentity).and()
161+
.filtering().returnNodesWithState(DiffNode.State.UNTOUCHED).and()
162+
.build().compare(working, base);
163+
then: "High level nodes"
164+
// print(node, working, base)
165+
node.getChild("otherMap").untouched
166+
node.getChild("productMap").changed
167+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).changed
168+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions").changed
169+
and: "ID1 and ID2 are CHANGED"
170+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
171+
.getChild(PV1CodeSelector).changed
172+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
173+
.getChild(PV1CodeSelector).changed
174+
and: "id changed, code untouched"
175+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
176+
.getChild(PV1CodeSelector).getChild("id").changed
177+
node.getChild("productMap").getChild(new MapKeyElementSelector("PROD1")).getChild("productVersions")
178+
.getChild(PV1CodeSelector).getChild("code").untouched
179+
}
180+
181+
182+
private void print(final DiffNode diffNode, final Object working,
183+
final Object base) {
184+
diffNode.visit(new DiffNode.Visitor() {
185+
@Override
186+
void node(final DiffNode node, final Visit visit) {
187+
System.out.println("" + node.getPath() + " " + node.getState()
188+
// + " " + node.canonicalGet(base) + " => " + node.canonicalGet(working)
189+
)
190+
}
191+
})
192+
}
193+
194+
195+
public static class Container {
196+
Map<String, Product> productMap;
197+
Map<String, Product> otherMap;
198+
}
199+
200+
public static interface CodeId {
201+
String getCode();
202+
}
203+
204+
@EqualsAndHashCode(includes = ["id"])
205+
@ToString(includePackage = false)
206+
public static class Product implements CodeId {
207+
String id;
208+
String code;
209+
List<ProductVersion> productVersions;
210+
List<ProductVersion> others;
211+
}
212+
213+
@EqualsAndHashCode(includes = ["id"])
214+
@ToString(includePackage = false)
215+
public static class ProductVersion implements CodeId {
216+
String id;
217+
String code;
218+
}
219+
220+
@EqualsAndHashCode(includes = ["id"])
221+
@ToString(includePackage = false)
222+
public static class OtherClass implements CodeId {
223+
String id;
224+
String code;
225+
List<ProductVersion> productVersions;
226+
}
227+
228+
def codeIdentity = new IdentityStrategy() {
229+
@Override
230+
boolean equals(final Object working, final Object base) {
231+
return Objects.equals(((CodeId) working).getCode(), ((CodeId) base).getCode());
232+
}
233+
}
234+
235+
def PV1Selector = new CollectionItemElementSelector(new ProductVersion(id: "ID1", code: "PVC1"));
236+
def PV2Selector = new CollectionItemElementSelector(new ProductVersion(id: "ID2", code: "PVC2"));
237+
def PV3Selector = new CollectionItemElementSelector(new ProductVersion(id: "ID3"));
238+
def PV4Selector = new CollectionItemElementSelector(new ProductVersion(id: "ID4"));
239+
240+
// need to fill code as well because that's used for the codeIdentity cases
241+
def PV1CodeSelector = new CollectionItemElementSelector(new ProductVersion(code: "PVC1"), codeIdentity);
242+
def PV2CodeSelector = new CollectionItemElementSelector(new ProductVersion(code: "PVC2"), codeIdentity);
243+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright 2015 Daniel Bechler
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package de.danielbechler.diff.identity
18+
19+
import de.danielbechler.diff.ObjectDifferBuilder
20+
import de.danielbechler.diff.comparison.IdentityStrategy
21+
import de.danielbechler.diff.node.DiffNode
22+
import de.danielbechler.diff.node.Visit
23+
import de.danielbechler.diff.path.NodePath
24+
import de.danielbechler.diff.selector.CollectionItemElementSelector
25+
import spock.lang.Specification
26+
27+
class IdentityStrategyIT extends Specification {
28+
29+
List<A> list1 = [
30+
new A(id: "Id1", code: "Code1"),
31+
new A(id: "Id2", code: "Code2"),
32+
new A(id: "Id3", code: "Code3")
33+
]
34+
List<A> list2 = [
35+
new A(id: "Id1", code: "Code1"),
36+
new A(id: "Id2", code: "Code2"),
37+
new A(id: "Id3", code: "Code3")
38+
]
39+
List<A> list2b = [
40+
new A(id: "Id2", code: "Code2"),
41+
new A(id: "Id3", code: "Code3"),
42+
new A(id: "Id1", code: "Code1")
43+
]
44+
List<A> list3 = [
45+
new A(id: "Id1", code: "Code1"),
46+
new A(id: "Id2", code: "newCode"),
47+
new A(id: "newId", code: "Code2")
48+
]
49+
50+
// def 'Test default equals SAME'() {
51+
// when:
52+
// def diffNode = ObjectDifferBuilder.startBuilding()
53+
// .build().compare(list2, list1)
54+
// then:
55+
// diffNode.untouched
56+
// }
57+
//
58+
// def 'Test default equals SAME B'() {
59+
// when:
60+
// def diffNode = ObjectDifferBuilder.startBuilding()
61+
// .build().compare(list2b, list1)
62+
// then:
63+
// diffNode.untouched
64+
// }
65+
//
66+
// def 'Test default equals CHANGED'() {
67+
// when:
68+
// def diffNode = ObjectDifferBuilder.startBuilding()
69+
// .build().compare(list3, list1)
70+
// then:
71+
// diffNode.changed
72+
// diffNode.getChild(new CollectionItemElementSelector(new A(id: "Id1"))) == null
73+
// diffNode.getChild(new CollectionItemElementSelector(new A(id: "Id2"))).changed
74+
// diffNode.getChild(new CollectionItemElementSelector(new A(id: "newId"))).added
75+
// diffNode.getChild(new CollectionItemElementSelector(new A(id: "Id3"))).removed
76+
// }
77+
//
78+
// def 'Test field CODE equals SAME'() {
79+
// when:
80+
// def diffNode = ObjectDifferBuilder.startBuilding()
81+
// .comparison().ofType(A).toUseEqualsMethodOfValueProvidedByMethod("getCode").and()
82+
// .build().compare(list2, list1)
83+
// then:
84+
// diffNode.state == DiffNode.State.UNTOUCHED
85+
// }
86+
//
87+
// def 'Test field CODE equals SAME B'() {
88+
// when:
89+
// def diffNode = ObjectDifferBuilder.startBuilding()
90+
// .identity().ofType(A).toUse(new CodeIdentity()).and()
91+
// .build().compare(list2b, list1)
92+
// then:
93+
// diffNode.state == DiffNode.State.UNTOUCHED
94+
// }
95+
96+
def 'Test field CODE equals equals CHANGED'() {
97+
when:
98+
def codeStrategy = new CodeIdentity();
99+
def diffNode = ObjectDifferBuilder.startBuilding()
100+
.comparison().ofCollectionItems(NodePath.withRoot()) // TODO configuration shouldn't be like this!
101+
.toUse(codeStrategy).and()
102+
.build().compare(list3, list1)
103+
then:
104+
diffNode.state == DiffNode.State.CHANGED
105+
diffNode.getChild(new CollectionItemElementSelector(new A(code: "Code1"), codeStrategy)) == null
106+
diffNode.getChild(new CollectionItemElementSelector(new A(code: "newCode"), codeStrategy)).added
107+
diffNode.getChild(new CollectionItemElementSelector(new A(code: "Code2"), codeStrategy)).changed
108+
diffNode.getChild(new CollectionItemElementSelector(new A(code: "Code3"), codeStrategy)).removed
109+
}
110+
111+
private void print(final DiffNode diffNode, final Object working,
112+
final Object base) {
113+
diffNode.visit(new DiffNode.Visitor() {
114+
@Override
115+
void node(final DiffNode node, final Visit visit) {
116+
System.out.println("" + node.getPath() + " " + node.getState() + " "
117+
+ node.canonicalGet(base) + " => " + node.canonicalGet(working))
118+
}
119+
})
120+
}
121+
122+
public static class A {
123+
String id;
124+
String code;
125+
126+
String getCode() {
127+
return code
128+
}
129+
130+
@Override
131+
boolean equals(final o) {
132+
if (this.is(o)) return true
133+
if (!(o instanceof A)) return false
134+
135+
A a = (A) o
136+
137+
if (!Objects.equals(id, a.id)) return false
138+
139+
return true
140+
}
141+
142+
@Override
143+
int hashCode() {
144+
return (id != null ? id.hashCode() : 0)
145+
}
146+
}
147+
148+
public static class CodeIdentity implements IdentityStrategy {
149+
@Override
150+
boolean equals(final Object working, final Object base) {
151+
return Objects.equals(((A) working).getCode(), ((A) base).getCode());
152+
}
153+
}
154+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /