Skip to content

Commit

Permalink
Merge pull request #160 from scompo/master
Browse files Browse the repository at this point in the history
Fixes #157
  • Loading branch information
SQiShER committed Mar 7, 2016
2 parents 10dac19 + a020094 commit b3d5c2e
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package de.danielbechler.diff.categories

import de.danielbechler.diff.ObjectDifferBuilder
import de.danielbechler.diff.introspection.ObjectDiffProperty
import de.danielbechler.diff.node.DiffNode
import de.danielbechler.diff.node.Visit
import de.danielbechler.diff.path.NodePath
import spock.lang.Specification

class CategoriesTestIT extends Specification{

def "should return all categories"(){
setup:
def obj1 = new MyObject("aaa","aaa")
def obj2 = new MyObject("bbb","bbb")
def differ = ObjectDifferBuilder.startBuilding()
.categories()
.ofNode(NodePath.with("firstString")).toBe("cat1")
.ofNode(NodePath.with("secondString")).toBe("cat1")
.and()
.build()
def node = differ.compare(obj1,obj2)

expect :
node.getChild("firstString").getCategories() == ["cat1"] as Set
node.getChild("secondString").getCategories() == ["cat1", "catAnnotation"] as Set
}

class MyObject{

def firstString
def secondString

MyObject(firstString,secondString) {

this.firstString = firstString
this.secondString = secondString
}

@ObjectDiffProperty(categories = ["catAnnotation"])
def getSecondString() {
return secondString
}
}
}
3 changes: 2 additions & 1 deletion src/main/java/de/danielbechler/diff/ObjectDifferBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ public ObjectDiffer build()
circularReferenceService,
inclusionService,
returnableNodeService,
introspectionService);
introspectionService,
categoryService);
differProvider.push(new BeanDiffer(
differDispatcher,
introspectionService,
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/de/danielbechler/diff/differ/DifferDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import de.danielbechler.diff.access.Accessor;
import de.danielbechler.diff.access.Instances;
import de.danielbechler.diff.access.PropertyAwareAccessor;
import de.danielbechler.diff.category.CategoryResolver;
import de.danielbechler.diff.introspection.PropertyReadException;
import de.danielbechler.diff.circular.CircularReferenceDetector;
import de.danielbechler.diff.circular.CircularReferenceDetectorFactory;
Expand Down Expand Up @@ -46,6 +47,7 @@ public class DifferDispatcher
private final CircularReferenceDetectorFactory circularReferenceDetectorFactory;
private final CircularReferenceExceptionHandler circularReferenceExceptionHandler;
private final IsIgnoredResolver isIgnoredResolver;
private final CategoryResolver categoryResolver;
private final IsReturnableResolver isReturnableResolver;
private final PropertyAccessExceptionHandlerResolver propertyAccessExceptionHandlerResolver;
private static final ThreadLocal<CircularReferenceDetector> workingThreadLocal = new ThreadLocal<CircularReferenceDetector>();
Expand All @@ -56,14 +58,18 @@ public DifferDispatcher(final DifferProvider differProvider,
final CircularReferenceExceptionHandler circularReferenceExceptionHandler,
final IsIgnoredResolver ignoredResolver,
final IsReturnableResolver returnableResolver,
final PropertyAccessExceptionHandlerResolver propertyAccessExceptionHandlerResolver)
final PropertyAccessExceptionHandlerResolver propertyAccessExceptionHandlerResolver,
final CategoryResolver categoryResolver)
{
Assert.notNull(differProvider, "differFactory");
this.differProvider = differProvider;

Assert.notNull(ignoredResolver, "ignoredResolver");
this.isIgnoredResolver = ignoredResolver;

Assert.notNull(categoryResolver, "categoryResolver");
this.categoryResolver = categoryResolver;

this.circularReferenceDetectorFactory = circularReferenceDetectorFactory;
this.circularReferenceExceptionHandler = circularReferenceExceptionHandler;
this.isReturnableResolver = returnableResolver;
Expand Down Expand Up @@ -101,6 +107,10 @@ public DiffNode dispatch(final DiffNode parentNode,
{
parentNode.addChild(node);
}
if(node != null)
{
node.addCategories(categoryResolver.resolveCategories(node));
}
return node;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,5 @@ public ObjectDifferBuilder and()
{
return rootConfiguration;
}

}
18 changes: 16 additions & 2 deletions src/main/java/de/danielbechler/diff/node/DiffNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;

import static java.util.Collections.unmodifiableSet;
Expand Down Expand Up @@ -67,6 +68,7 @@ public class DiffNode
private Class<?> valueType;
private TypeInfo valueTypeInfo;
private IdentityStrategy childIdentityStrategy;
private final Set<String> additionalCategories = new TreeSet<String>();

public void setChildIdentityStrategy(final IdentityStrategy identityStrategy)
{
Expand Down Expand Up @@ -573,7 +575,11 @@ public boolean isExcluded()
return false;
}

// TODO These categories should also contain the ones configured via CategoryService
/**
* Returns an unmodifiable {@link java.util.Set} of {@link java.lang.String} with the categories of this node.
*
* @return an unmodifiable {@link java.util.Set} of {@link java.lang.String} with the categories of this node
*/
public final Set<String> getCategories()
{
final Set<String> categories = new TreeSet<String>();
Expand All @@ -589,7 +595,9 @@ public final Set<String> getCategories()
categories.addAll(categoriesFromAccessor);
}
}
return categories;
categories.addAll(additionalCategories);

return unmodifiableSet(categories);
}

/**
Expand Down Expand Up @@ -732,6 +740,12 @@ else if (childCount() > 1)
return sb.toString();
}

public void addCategories(final Collection<String> additionalCategories)
{
Assert.notNull(additionalCategories, "additionalCategories");
this.additionalCategories.addAll(additionalCategories);
}

/**
* @return Returns the path to the first node in the hierarchy that represents the same object instance as
* this one. (Only if {@link #isCircular()} returns <code>true</code>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import de.danielbechler.diff.access.Accessor;
import de.danielbechler.diff.access.Instances;
import de.danielbechler.diff.access.RootAccessor;
import de.danielbechler.diff.category.CategoryResolver;
import de.danielbechler.diff.circular.CircularReferenceDetector;
import de.danielbechler.diff.circular.CircularReferenceDetectorFactory;
import de.danielbechler.diff.circular.CircularReferenceExceptionHandler;
Expand Down Expand Up @@ -62,6 +63,8 @@ public class DifferDispatcherShould
@Mock
private IsIgnoredResolver ignoredResolver;
@Mock
private CategoryResolver categoryResolver;
@Mock
private IsReturnableResolver returnableResolver;
@Mock
private Instances instances;
Expand All @@ -79,7 +82,7 @@ public void setUp() throws Exception
when(instances.access(any(Accessor.class))).thenReturn(accessedInstances);
when(accessedInstances.getSourceAccessor()).thenReturn(accessor);

differDispatcher = new DifferDispatcher(differProvider, circularReferenceDetectorFactory, circularReferenceExceptionHandler, ignoredResolver, returnableResolver, propertyAccessExceptionHandlerResolver);
differDispatcher = new DifferDispatcher(differProvider, circularReferenceDetectorFactory, circularReferenceExceptionHandler, ignoredResolver, returnableResolver, propertyAccessExceptionHandlerResolver, categoryResolver);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package de.danielbechler.diff.differ

import de.danielbechler.diff.access.*
import de.danielbechler.diff.category.CategoryResolver
import de.danielbechler.diff.circular.CircularReferenceDetector
import de.danielbechler.diff.circular.CircularReferenceDetectorFactory
import de.danielbechler.diff.circular.CircularReferenceExceptionHandler
Expand Down Expand Up @@ -53,14 +54,18 @@ class DifferDispatcherTest extends Specification {
def isReturnableResolver = Stub IsReturnableResolver, {
isReturnable(_ as DiffNode) >> true
}
def categoryResolver = Stub CategoryResolver, {
resolveCategories(_ as DiffNode) >> Collections.emptySet()
}
def propertyAccessExceptionHandlerResolver = Mock PropertyAccessExceptionHandlerResolver
def differDispatcher = new DifferDispatcher(
differProvider,
circularReferenceDetectorFactory,
circularReferenceExceptionHandler,
isIgnoredResolver,
isReturnableResolver,
propertyAccessExceptionHandlerResolver)
propertyAccessExceptionHandlerResolver,
categoryResolver)

@Ignore
def "when a circular reference is detected"() {
Expand Down
28 changes: 28 additions & 0 deletions src/test/java/de/danielbechler/diff/node/DiffNodeTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,32 @@ class DiffNodeTest extends Specification {

doesOrDoesNotImplement = expectedResult ? 'implements' : 'does not implement'
}

def "should return added categories"(){
given:
def node = new DiffNode(null, Mock(Accessor), Object)
node.addCategories(["addedCategory"] as List)
expect :
node.getCategories() == ["addedCategory"] as Set
}

def "categories should not be modifiable by a client directly"(){

when:
def node = new DiffNode(null, Mock(Accessor), Object)
def cats = node.getCategories()
cats.removeAll()
then :
thrown UnsupportedOperationException
}

def "should throw exception when added a null collection"(){

when:
def node = new DiffNode(null, Mock(Accessor), Object)
node.addCategories(null)
then :
def ex = thrown(IllegalArgumentException)
ex.message == "'additionalCategories' must not be null"
}
}

0 comments on commit b3d5c2e

Please sign in to comment.