Skip to content

Commit 97bcd3c

Browse files
committed
Testing, Cleanup, README
1 parent 52f5ecb commit 97bcd3c

37 files changed

+596
-261
lines changed

README.md

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,32 @@
1-
java-object-diff
2-
================
1+
# Java Object Diff
32

4-
Framework to detect and handle differences between Java objects
3+
An easy-to-use Framework to detect and handle differences between different versions of a given Java Object.
4+
5+
## Use Cases
6+
7+
This Framework is currently used to…
8+
9+
* Generate Facebook-like activity streams
10+
* Visualize the differences between two object versions
11+
* Automatically resolve conflicts on concurrent database writes
12+
* Detect and persist only properties that actually changed
13+
14+
None one of these solutions comes out-of-the-box, though. But this Framework was the solid foundation, that made them very easy to realize.
15+
16+
## Feature List
17+
18+
* Generates a graph of your object, allowing you to build powerful visitors to suit your needs.
19+
* Works with every kind of object, so no need to change a thing.
20+
* Can be configured entirely from the outside. No annotations needed. (Although they make it much more convenient.)
21+
* No dependencies except for SLF4J.
22+
23+
## Disclaimer
24+
25+
Although I use this Framework in a rather complex project of mine, I'm sure there are still lots of corner cases, that I didn't discover myself. Fortunately, the simple API makes it very easy for you, to quickly test it out on your own set of objects.
26+
27+
## Known Issues and Ways to Improve
28+
29+
* Performance optimization was not the biggest concern so far, so there is still some room for improvement.
30+
* Object comparison is very strict. Objects with different types cannot be compared, even when they share the same interface.
31+
* Arrays are treated poorly
32+
* The annotations should be applied to fields instead of methods. (Would make it easier to handle Arrays.)

src/main/java/de/danielbechler/diff/BeanDiffer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ private void compareProperties(final Node parentNode, final Instances instances)
9797
parentNode.setState(Node.State.CHANGED);
9898
parentNode.addChild(child);
9999
}
100+
else if (getConfiguration().isReturnUnchangedNodes())
101+
{
102+
parentNode.addChild(child);
103+
}
100104
}
101105
}
102106

src/main/java/de/danielbechler/diff/CollectionDiffer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ private void handleItems(final CollectionNode collectionNode, final Instances in
6060
collectionNode.addChild(child);
6161
collectionNode.setState(Node.State.CHANGED);
6262
}
63+
else if (getConfiguration().isReturnUnchangedNodes())
64+
{
65+
collectionNode.addChild(child);
66+
}
6367
}
6468
}
6569

src/main/java/de/danielbechler/diff/Configuration.java

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,27 @@
88
/** @author Daniel Bechler */
99
public class Configuration
1010
{
11+
private Collection<String> ignoreCategories = new TreeSet<String>();
1112
private Collection<PropertyPath> ignoreProperties = new LinkedHashSet<PropertyPath>(10);
1213
private Collection<PropertyPath> equalsOnlyProperties = new LinkedHashSet<PropertyPath>(10);
1314
private Collection<Class<?>> equalsOnlyTypes = new LinkedHashSet<Class<?>>(10);
15+
private boolean returnUnchangedNodes = false;
16+
private boolean returnIgnoredNodes = false;
17+
18+
public Collection<String> getIgnoreCategories()
19+
{
20+
return Collections.unmodifiableCollection(ignoreCategories);
21+
}
22+
23+
public void setIgnoreCategories(final Collection<String> ignoreCategories)
24+
{
25+
this.ignoreCategories = ignoreCategories;
26+
}
27+
28+
public void addIgnoreCategories(final String... category)
29+
{
30+
this.ignoreCategories.addAll(Arrays.asList(category));
31+
}
1432

1533
public Collection<PropertyPath> getIgnoreProperties()
1634
{
@@ -57,39 +75,41 @@ public void addEqualsOnlyType(final Class<?> type)
5775
this.equalsOnlyTypes.add(type);
5876
}
5977

60-
public boolean isIgnored(final PropertyPath propertyPath)
78+
public boolean isEqualsOnlyPath(final PropertyPath selectorPath)
6179
{
62-
return ignoreProperties.contains(propertyPath);
80+
return equalsOnlyProperties.contains(selectorPath);
6381
}
6482

65-
public boolean isEqualsOnly(final PropertyPath propertyPath, final Class<?> propertyType)
83+
public boolean isEqualsOnlyType(final Class<?> type)
6684
{
67-
if (isEqualsOnlyPath(propertyPath))
85+
if (type.getAnnotation(ObjectDiffEqualsOnlyType.class) != null)
6886
{
6987
return true;
7088
}
71-
else if (isEqualsOnlyType(propertyType))
89+
else if (equalsOnlyTypes.contains(type))
7290
{
7391
return true;
7492
}
7593
return false;
7694
}
7795

78-
public boolean isEqualsOnlyPath(final PropertyPath selectorPath)
96+
public boolean isReturnIgnoredNodes()
7997
{
80-
return equalsOnlyProperties.contains(selectorPath);
98+
return returnIgnoredNodes;
8199
}
82100

83-
public boolean isEqualsOnlyType(final Class<?> propertyType)
101+
public void setReturnIgnoredNodes(final boolean returnIgnoredNodes)
84102
{
85-
if (propertyType.getAnnotation(ObjectDiffEqualsOnlyType.class) != null)
86-
{
87-
return true;
88-
}
89-
else if (equalsOnlyTypes.contains(propertyType))
90-
{
91-
return true;
92-
}
93-
return false;
103+
this.returnIgnoredNodes = returnIgnoredNodes;
104+
}
105+
106+
public boolean isReturnUnchangedNodes()
107+
{
108+
return returnUnchangedNodes;
109+
}
110+
111+
public void setReturnUnchangedNodes(final boolean returnUnchangedNodes)
112+
{
113+
this.returnUnchangedNodes = returnUnchangedNodes;
94114
}
95115
}

src/main/java/de/danielbechler/diff/DelegatingObjectDiffer.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import de.danielbechler.diff.node.*;
44
import de.danielbechler.util.*;
5+
import de.danielbechler.util.Collections;
56

67
import java.util.*;
78

@@ -58,7 +59,11 @@ public boolean isIgnored(final Node parentNode, final Instances instances)
5859
{
5960
return true;
6061
}
61-
if (configuration.isIgnored(instances.getPropertyPath(parentNode)))
62+
if (Collections.containsAny(instances.getSourceAccessor().getCategories(), configuration.getIgnoreCategories()))
63+
{
64+
return true;
65+
}
66+
if (configuration.getIgnoreProperties().contains(instances.getPropertyPath(parentNode)))
6267
{
6368
return true;
6469
}

src/main/java/de/danielbechler/diff/Instances.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public Class<?> getType()
126126
{
127127
if (sourceAccessor instanceof TypeAwareAccessor)
128128
{
129-
return ((TypeAwareAccessor) sourceAccessor).getType();
129+
return ((TypeAwareAccessor) sourceAccessor).getPropertyType();
130130
}
131131
final Set<Class<?>> types = Classes.typesOf(working, base, fresh);
132132
if (types.isEmpty())

src/main/java/de/danielbechler/diff/MapDiffer.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,18 @@ private void handleEntries(final Instances instances, final MapNode parent, fina
6666
}
6767
}
6868

69-
private boolean handleEntries(final Object key, final Instances instances, final MapNode parent)
69+
private void handleEntries(final Object key, final Instances instances, final MapNode parent)
7070
{
7171
final Node node = compareEntry(key, instances, parent);
7272
if (node.hasChanges())
7373
{
7474
parent.setState(Node.State.CHANGED);
7575
parent.addChild(node);
76-
return true;
7776
}
78-
return false;
77+
else if (getConfiguration().isReturnUnchangedNodes())
78+
{
79+
parent.addChild(node);
80+
}
7981
}
8082

8183
private Node compareEntry(final Object key, Instances instances, final MapNode parent)

src/main/java/de/danielbechler/diff/accessor/CanonicalAccessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ public interface CanonicalAccessor extends Accessor
77

88
void canonicalSet(Object target, Object value);
99

10-
void canonicalUnset(Object target, Object value);
10+
void canonicalUnset(Object target);
1111
}

src/main/java/de/danielbechler/diff/accessor/CollectionItemAccessor.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@ public CollectionItemAccessor(final Object referenceItem)
1414
this.referenceItem = referenceItem;
1515
}
1616

17-
public String getPropertyName()
18-
{
19-
return "[" + referenceItem + "]";
20-
}
21-
2217
public PropertyPath.Element getPathElement()
2318
{
2419
return new CollectionElement(referenceItem);

src/main/java/de/danielbechler/diff/accessor/MapEntryAccessor.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ public MapEntryAccessor(final List<?> referenceKeys, final int index)
2222
this.index = index;
2323
}
2424

25-
public String getPropertyName()
26-
{
27-
return "[" + Integer.toString(index) + "]";
28-
}
29-
3025
public PropertyPath.Element getPathElement()
3126
{
3227
return new MapElement(getReferenceKey());

0 commit comments

Comments
 (0)