package fr.openwide.core.jpa.more.business.difference.differ;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import de.danielbechler.diff.access.Accessor;
import de.danielbechler.diff.access.Instances;
import de.danielbechler.diff.comparison.ComparisonStrategy;
import de.danielbechler.diff.comparison.ComparisonStrategyResolver;
import de.danielbechler.diff.differ.Differ;
import de.danielbechler.diff.differ.DifferDispatcher;
import de.danielbechler.diff.filtering.IsReturnableResolver;
import de.danielbechler.diff.node.DiffNode;
import fr.openwide.core.jpa.more.business.difference.differ.strategy.AbstractContainerDifferStrategy;
public abstract class AbstractContainerDiffer implements Differ {
private final DifferDispatcher differDispatcher;
private final ComparisonStrategyResolver comparisonStrategyResolver;
private final IsReturnableResolver isReturnableResolver;
private final Map<Predicate<? super DiffNode>, AbstractContainerDifferStrategy<?, ?>> strategies = Maps.newLinkedHashMap();
private final AbstractContainerDifferStrategy<?, ?> defaultStrategy;
public AbstractContainerDiffer(DifferDispatcher differDispatcher,
ComparisonStrategyResolver comparisonStrategyResolver, IsReturnableResolver isReturnableResolver,
AbstractContainerDifferStrategy<?, ?> defaultStrategy) {
this.differDispatcher = differDispatcher;
this.comparisonStrategyResolver = comparisonStrategyResolver;
this.isReturnableResolver = isReturnableResolver;
this.defaultStrategy = defaultStrategy;
}
protected void addStrategy(Predicate<? super DiffNode> predicate, AbstractContainerDifferStrategy<?, ?> strategy) {
strategies.put(predicate, strategy);
}
private static DiffNode newNode(final DiffNode parentNode, final Instances instances) {
final Accessor accessor = instances.getSourceAccessor();
final Class<?> type = instances.getType();
return new DiffNode(parentNode, accessor, type);
}
@Override
public DiffNode compare(DiffNode parentNode, Instances instances) {
final DiffNode containerNode = newNode(parentNode, instances);
for (Map.Entry<Predicate<? super DiffNode>, AbstractContainerDifferStrategy<?, ?>> entry : strategies.entrySet()) {
if (entry.getKey().apply(containerNode)) {
return compare(instances, entry.getValue(), containerNode);
}
}
return compare(instances, defaultStrategy, containerNode);
}
protected <TContainer, K> DiffNode compare(Instances instances, AbstractContainerDifferStrategy<TContainer, K> strategy, DiffNode collectionNode) {
TContainer working = strategy.toContainer(instances.getWorking());
TContainer base = strategy.toContainer(instances.getBase());
if (instances.hasBeenAdded()) {
final TContainer addedItems = working;
strategy.compareItems(differDispatcher, isReturnableResolver, collectionNode, instances, addedItems);
if (collectionNode.hasChildren()) {
collectionNode.setState(DiffNode.State.CHANGED);
} else {
// No child element, be it before or after the change: we'll say the collection did not change.
collectionNode.setState(DiffNode.State.UNTOUCHED);
}
} else if (instances.hasBeenRemoved()) {
final TContainer removedItems = base;
strategy.compareItems(differDispatcher, isReturnableResolver, collectionNode, instances, removedItems);
if (collectionNode.hasChildren()) {
collectionNode.setState(DiffNode.State.CHANGED);
} else {
// No child element, be it before or after the change: we'll say the collection did not change.
collectionNode.setState(DiffNode.State.UNTOUCHED);
}
} else if (instances.areSame()) {
collectionNode.setState(DiffNode.State.UNTOUCHED);
} else {
final ComparisonStrategy comparisonStrategy = comparisonStrategyResolver.resolveComparisonStrategy(collectionNode);
if (comparisonStrategy == null) {
// Compare internally
final Iterable<K> addedKeys = strategy.difference(working, base);
final Iterable<K> removedKeys = strategy.difference(base, working);
final Iterable<K> commonKeys = strategy.intersection(working, base);
strategy.compareKeys(differDispatcher, isReturnableResolver, collectionNode, instances, addedKeys);
strategy.compareKeys(differDispatcher, isReturnableResolver, collectionNode, instances, removedKeys);
strategy.compareKeys(differDispatcher, isReturnableResolver, collectionNode, instances, commonKeys);
} else {
comparisonStrategy.compare(collectionNode, instances.getType(), working, base);
}
}
return collectionNode;
}
}