Skip to content

Commit 3ca579f

Browse files
committed
Implemented feature to ignore properties regardless of type or place in the hierarchy
1 parent bc24cdf commit 3ca579f

4 files changed

Lines changed: 300 additions & 193 deletions

File tree

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

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import java.util.Collections;
2626
import java.util.concurrent.atomic.*;
2727

28-
import static java.util.Collections.*;
28+
import static java.util.Collections.unmodifiableSet;
2929

3030
/**
3131
* Represents a part of an object. It could be the object itself, one of its properties, an item in a
@@ -71,12 +71,23 @@ public DiffNode()
7171
this(ROOT, RootAccessor.getInstance(), null);
7272
}
7373

74-
/** @return The state of this node. */
74+
/**
75+
* @return The state of this node.
76+
*/
7577
public State getState()
7678
{
7779
return this.state;
7880
}
7981

82+
/**
83+
* @param state The state of this node.
84+
*/
85+
public void setState(final State state)
86+
{
87+
Assert.notNull(state, "state");
88+
this.state = state;
89+
}
90+
8091
public boolean matches(final NodePath path)
8192
{
8293
return path.matches(getPath());
@@ -103,45 +114,57 @@ public void accept(final DiffNode node, final Visit visit)
103114
return result.get();
104115
}
105116

106-
/** Convenience method for <code>{@link #getState()} == {@link DiffNode.State#ADDED}</code> */
117+
/**
118+
* Convenience method for <code>{@link #getState()} == {@link DiffNode.State#ADDED}</code>
119+
*/
107120
public final boolean isAdded()
108121
{
109122
return state == State.ADDED;
110123
}
111124

112-
/** Convenience method for <code>{@link #getState()} == {@link DiffNode.State#CHANGED}</code> */
125+
/**
126+
* Convenience method for <code>{@link #getState()} == {@link DiffNode.State#CHANGED}</code>
127+
*/
113128
public final boolean isChanged()
114129
{
115130
return state == State.CHANGED;
116131
}
117132

118-
/** Convenience method for <code>{@link #getState()} == {@link DiffNode.State#REMOVED}</code> */
133+
/**
134+
* Convenience method for <code>{@link #getState()} == {@link DiffNode.State#REMOVED}</code>
135+
*/
119136
public final boolean isRemoved()
120137
{
121138
return state == State.REMOVED;
122139
}
123140

124-
/** Convenience method for <code>{@link #getState()} == {@link DiffNode.State#UNTOUCHED}</code> */
141+
/**
142+
* Convenience method for <code>{@link #getState()} == {@link DiffNode.State#UNTOUCHED}</code>
143+
*/
125144
public final boolean isUntouched()
126145
{
127146
return state == State.UNTOUCHED;
128147
}
129148

130-
/** Convenience method for <code>{@link #getState()} == {@link DiffNode.State#CIRCULAR}</code> */
149+
/**
150+
* Convenience method for <code>{@link #getState()} == {@link DiffNode.State#CIRCULAR}</code>
151+
*/
131152
public boolean isCircular()
132153
{
133154
return state == State.CIRCULAR;
134155
}
135156

136-
/** @return The absolute property path from the object root up to this node. */
157+
/**
158+
* @return The absolute property path from the object root up to this node.
159+
*/
137160
public final NodePath getPath()
138161
{
139162
if (parentNode != null)
140163
{
141164
return NodePath.createBuilder()
142-
.withPropertyPath(parentNode.getPath())
143-
.withElement(accessor.getPathElement())
144-
.build();
165+
.withPropertyPath(parentNode.getPath())
166+
.withElement(accessor.getPathElement())
167+
.build();
145168
}
146169
else if (accessor instanceof RootAccessor)
147170
{
@@ -158,7 +181,9 @@ public Element getPathElement()
158181
return accessor.getPathElement();
159182
}
160183

161-
/** @return Returns the type of the property represented by this node, or null if unavailable. */
184+
/**
185+
* @return Returns the type of the property represented by this node, or null if unavailable.
186+
*/
162187
public Class<?> getValueType()
163188
{
164189
if (valueType != null)
@@ -183,13 +208,17 @@ public void setType(final Class<?> aClass)
183208
this.valueType = aClass;
184209
}
185210

186-
/** @return <code>true</code> if this node has children. */
211+
/**
212+
* @return <code>true</code> if this node has children.
213+
*/
187214
public boolean hasChildren()
188215
{
189216
return !children.isEmpty();
190217
}
191218

192-
/** @return The child nodes of this node. */
219+
/**
220+
* @return The child nodes of this node.
221+
*/
193222
public Collection<DiffNode> getChildren()
194223
{
195224
return children.values();
@@ -199,7 +228,6 @@ public Collection<DiffNode> getChildren()
199228
* Retrieve a child with the given property name relative to this node.
200229
*
201230
* @param propertyName The name of the property represented by the child node.
202-
*
203231
* @return The requested child node or <code>null</code>.
204232
*/
205233
public DiffNode getChild(final String propertyName)
@@ -211,7 +239,6 @@ public DiffNode getChild(final String propertyName)
211239
* Retrieve a child that matches the given absolute path, starting from the current node.
212240
*
213241
* @param path The path from the object root to the requested child node.
214-
*
215242
* @return The requested child node or <code>null</code>.
216243
*/
217244
public DiffNode getChild(final NodePath path)
@@ -225,7 +252,6 @@ public DiffNode getChild(final NodePath path)
225252
* Retrieve a child that matches the given path element relative to this node.
226253
*
227254
* @param pathElement The path element of the child node to get.
228-
*
229255
* @return The requested child node or <code>null</code>.
230256
*/
231257
public DiffNode getChild(final Element pathElement)
@@ -362,7 +388,9 @@ public final boolean isRootNode()
362388
return accessor instanceof RootAccessor;
363389
}
364390

365-
/** Convenience method for <code>{@link #getState()} == {@link DiffNode.State#IGNORED}</code> */
391+
/**
392+
* Convenience method for <code>{@link #getState()} == {@link DiffNode.State#IGNORED}</code>
393+
*/
366394
public final boolean isIgnored()
367395
{
368396
return state == State.IGNORED;
@@ -405,14 +433,9 @@ public final Set<String> getCategories()
405433
return categories;
406434
}
407435

408-
/** @param state The state of this node. */
409-
public void setState(final State state)
410-
{
411-
Assert.notNull(state, "state");
412-
this.state = state;
413-
}
414-
415-
/** @return The parent node, if any. */
436+
/**
437+
* @return The parent node, if any.
438+
*/
416439
public DiffNode getParentNode()
417440
{
418441
return parentNode;
@@ -537,7 +560,7 @@ public int hashCode()
537560

538561
/**
539562
* @return Returns the path to the first node in the hierarchy that represents the same object instance as
540-
* this one. (Only if {@link #isCircular()} returns <code>true</code>.
563+
* this one. (Only if {@link #isCircular()} returns <code>true</code>.
541564
*/
542565
public NodePath getCircleStartPath()
543566
{
@@ -559,29 +582,45 @@ public void setCircleStartNode(final DiffNode circleStartNode)
559582
this.circleStartNode = circleStartNode;
560583
}
561584

562-
/** The state of a {@link DiffNode} representing the difference between two objects. */
585+
/**
586+
* The state of a {@link DiffNode} representing the difference between two objects.
587+
*/
563588
public enum State
564589
{
565-
/** The value has been added to the working object. */
590+
/**
591+
* The value has been added to the working object.
592+
*/
566593
ADDED,
567594

568-
/** The value has been changed compared to the base object. */
595+
/**
596+
* The value has been changed compared to the base object.
597+
*/
569598
CHANGED,
570599

571-
/** The value has been removed from the working object. */
600+
/**
601+
* The value has been removed from the working object.
602+
*/
572603
REMOVED,
573604

574-
/** The value is identical between working and base */
605+
/**
606+
* The value is identical between working and base
607+
*/
575608
UNTOUCHED,
576609

577-
/** Special state to mark circular references */
610+
/**
611+
* Special state to mark circular references
612+
*/
578613
CIRCULAR,
579614

580-
/** The value has not been looked at and has been ignored. */
615+
/**
616+
* The value has not been looked at and has been ignored.
617+
*/
581618
IGNORED
582619
}
583620

584-
/** Visitor to traverse a node graph. */
621+
/**
622+
* Visitor to traverse a node graph.
623+
*/
585624
public static interface Visitor
586625
{
587626
void accept(DiffNode node, Visit visit);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package de.danielbechler.diff;
22

3-
/** @author Daniel Bechler */
3+
/**
4+
* @author Daniel Bechler
5+
*/
46
public interface InclusionConfiguration
57
{
68
To toInclude();
@@ -14,5 +16,7 @@ public interface To
1416
To types(Class<?>... types);
1517

1618
To nodes(NodePath... nodePath);
19+
20+
To propertyNames(String... propertyNames);
1721
}
1822
}

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

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package de.danielbechler.diff;
22

3+
import de.danielbechler.diff.bean.*;
34
import de.danielbechler.util.*;
45

56
import java.util.*;
67

7-
import static de.danielbechler.diff.Inclusion.*;
8+
import static de.danielbechler.diff.Inclusion.EXCLUDED;
9+
import static de.danielbechler.diff.Inclusion.INCLUDED;
810

911
/**
1012
*
@@ -15,13 +17,24 @@ class InclusionService implements InclusionConfiguration, IsIgnoredResolver
1517
private final NodePathValueHolder<Inclusion> nodeInclusions = NodePathValueHolder.of(Inclusion.class);
1618
private final Map<Class<?>, Inclusion> typeInclusions = new HashMap<Class<?>, Inclusion>();
1719
private final Map<String, Inclusion> categoryInclusions = new TreeMap<String, Inclusion>();
20+
private final Map<String, Inclusion> propertyNameInclusions = new TreeMap<String, Inclusion>();
1821

1922
public InclusionService(final CategoryResolver categoryResolver)
2023
{
2124
Assert.notNull(categoryResolver, "categoryResolver");
2225
this.categoryResolver = categoryResolver;
2326
}
2427

28+
private static String getNodePropertyName(final DiffNode node)
29+
{
30+
final Element pathElement = node.getPathElement();
31+
if (pathElement instanceof NamedPropertyElement)
32+
{
33+
return ((NamedPropertyElement) pathElement).getPropertyName();
34+
}
35+
return null;
36+
}
37+
2538
public boolean isIgnored(final DiffNode node)
2639
{
2740
return node.isExcluded() || !isIncluded(node) || isExcluded(node);
@@ -47,11 +60,25 @@ else if (isIncludedByType(node))
4760
{
4861
return true;
4962
}
63+
else if (isIncludedByPropertyName(node))
64+
{
65+
return true;
66+
}
5067
return false;
5168
}
5269
return true;
5370
}
5471

72+
private boolean isIncludedByPropertyName(final DiffNode node)
73+
{
74+
final String propertyName = getNodePropertyName(node);
75+
if (propertyName != null)
76+
{
77+
return propertyNameInclusions.get(propertyName) == INCLUDED;
78+
}
79+
return false;
80+
}
81+
5582
private boolean isIncludedByCategory(final DiffNode node)
5683
{
5784
return hasCategoryWithInclusion(node, INCLUDED);
@@ -78,6 +105,21 @@ else if (isExcludedByType(node))
78105
{
79106
return true;
80107
}
108+
else if (isExcludedByPropertyName(node))
109+
{
110+
return true;
111+
}
112+
}
113+
return false;
114+
}
115+
116+
@SuppressWarnings("TypeMayBeWeakened") // we don't want to weaken the type for consistency reasons
117+
private boolean isExcludedByPropertyName(final DiffNode node)
118+
{
119+
final String propertyName = getNodePropertyName(node);
120+
if (propertyName != null)
121+
{
122+
return propertyNameInclusions.get(propertyName) == EXCLUDED;
81123
}
82124
return false;
83125
}
@@ -106,6 +148,10 @@ private boolean hasInclusions(final Inclusion inclusion)
106148
{
107149
return true;
108150
}
151+
if (propertyNameInclusions.containsValue(inclusion))
152+
{
153+
return true;
154+
}
109155
return false;
110156
}
111157

@@ -197,5 +243,14 @@ public To nodes(final NodePath... nodePaths)
197243
}
198244
return this;
199245
}
246+
247+
public To propertyNames(final String... propertyNames)
248+
{
249+
for (final String propertyName : propertyNames)
250+
{
251+
propertyNameInclusions.put(propertyName, inclusion);
252+
}
253+
return this;
254+
}
200255
}
201256
}

0 commit comments

Comments
 (0)