package fr.openwide.core.jpa.more.business.difference.factory; import java.util.Collection; import java.util.List; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import de.danielbechler.diff.node.DiffNode; import de.danielbechler.diff.node.DiffNode.Visitor; import de.danielbechler.diff.node.Visit; import fr.openwide.core.commons.util.fieldpath.FieldPath; import fr.openwide.core.commons.util.fieldpath.FieldPathComponent; import fr.openwide.core.jpa.more.business.difference.model.Difference; import fr.openwide.core.jpa.more.business.difference.util.DiffUtils; import fr.openwide.core.jpa.more.business.history.model.AbstractHistoryDifference; /** * A HistoryDifferenceFactory which creates a HistoryDifference for each DiffNode: * <ul> * <li>which respects the predicate <code>branchFilter</code> * <li>AND all the parents of which respect the predicate <code>branchFilter</code> * <li>AND which respects the predicate <code>nodeFilter</code> * </ul> * * <p>By default, the <code>branchFilter</code> only includes the modified nodes (CHANGED, ADDED, REMOVED). * <p>By default, the <code>nodeFilter</code> is right for each node: * <ul> * <li>which is an element of a collection or a map * <li>OR which does not have any children * </ul> */ @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public final class DefaultHistoryDifferenceFactory<T> extends AbstractHistoryDifferenceFactory<T> { private Predicate<? super DiffNode> branchFilter = new Predicate<DiffNode>() { @Override public boolean apply(DiffNode input) { return input.hasChanges(); } }; private Predicate<? super DiffNode> nodeFilter = new Predicate<DiffNode>() { @Override public boolean apply(DiffNode input) { return DiffUtils.isItem(input) || !input.hasChildren(); } }; public void setFilter(Predicate<? super DiffNode> filter) { this.branchFilter = filter; } public void setNodeFilter(Predicate<? super DiffNode> nodeFilter) { this.nodeFilter = nodeFilter; } @Override public <HD extends AbstractHistoryDifference<HD, ?>> List<HD> create(Supplier<HD> historyDifferenceSupplier, Difference<T> rootDifference, Collection<DiffNode> nodes) { final List<HD> result = Lists.newLinkedList(); for (DiffNode node : nodes) { FieldPath path = DiffUtils.toFieldPath(node.getPath()); result.add(create(historyDifferenceSupplier, rootDifference, rootDifference.getDiffNode(), node, path)); } return result; } public <HD extends AbstractHistoryDifference<HD, ?>> HD create(Supplier<HD> historyDifferenceSupplier, Difference<T> rootDifference, DiffNode parentNode, DiffNode currentNode, FieldPath currentNodeRelativePath) { HD difference = newHistoryDifference(historyDifferenceSupplier, rootDifference, parentNode, currentNode, currentNodeRelativePath, toHistoryDifferenceAction(currentNode)); List<HD> subDifferences = createChildren(historyDifferenceSupplier, rootDifference, currentNode, FieldPath.ROOT); add(difference, subDifferences); return difference; } public <HD extends AbstractHistoryDifference<HD, ?>> List<HD> createChildren(final Supplier<HD> historyDifferenceSupplier, final Difference<T> rootDifference, final DiffNode parentNode, final FieldPath parentRelativePath) { final List<HD> result = Lists.newLinkedList(); parentNode.visitChildren(new Visitor() { @Override public void node(DiffNode currentNode, Visit visit) { visit.dontGoDeeper(); if (branchFilter.apply(currentNode)) { FieldPathComponent component = DiffUtils.toFieldPathComponent(currentNode.getPath().getLastElementSelector()); FieldPath currentNodeRelativePath = parentRelativePath.append(component); if (nodeFilter.apply(currentNode)) { result.add(create(historyDifferenceSupplier, rootDifference, parentNode, currentNode, currentNodeRelativePath)); } else { result.addAll(createChildren(historyDifferenceSupplier, rootDifference, currentNode, currentNodeRelativePath)); } } } }); return result; } }