package fr.openwide.core.jpa.more.business.difference.util; import java.io.Serializable; import java.util.Iterator; import java.util.List; import javax.annotation.Nonnull; import org.bindgen.Binding; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Lists; import de.danielbechler.diff.ObjectDiffer; import de.danielbechler.diff.ObjectDifferBuilder; import de.danielbechler.diff.node.DiffNode; import de.danielbechler.diff.path.NodePath; import de.danielbechler.diff.path.NodePath.AppendableBuilder; import de.danielbechler.diff.selector.CollectionItemElementSelector; import de.danielbechler.diff.selector.ElementSelector; import de.danielbechler.diff.selector.MapKeyElementSelector; import de.danielbechler.diff.selector.RootElementSelector; import fr.openwide.core.commons.util.fieldpath.FieldPath; import fr.openwide.core.commons.util.fieldpath.FieldPathComponent; import fr.openwide.core.commons.util.fieldpath.FieldPathPropertyComponent; import fr.openwide.core.jpa.more.business.difference.model.Difference; import fr.openwide.core.jpa.more.business.difference.selector.IKeyAwareSelector; public final class DiffUtils { private DiffUtils() { } public static ObjectDifferBuilder builder() { return ObjectDifferBuilder.startBuilding(); } public static <T> Difference<T> diff(T before, T after, ObjectDiffer differ) { return new Difference<T>( before, after, differ.compare(after, before) ); } public static Predicate<DiffNode> hasPropertyName(String propertyName) { return new PropertyNamePredicate(propertyName); } private static class PropertyNamePredicate implements Predicate<DiffNode>, Serializable { private static final long serialVersionUID = -3331762514876209810L; private final String propertyName; public PropertyNamePredicate(String propertyName) { super(); this.propertyName = propertyName; } @Override public boolean apply(DiffNode input) { return input != null && propertyName.equals(input.getPropertyName()); } } public static Predicate<DiffNode> hasPath(FieldPath path) { return new FieldPathPredicate(path); } private static class FieldPathPredicate implements Predicate<DiffNode>, Serializable { private static final long serialVersionUID = -3331762514876209810L; private final FieldPath fieldPath; public FieldPathPredicate(FieldPath fieldPath) { super(); this.fieldPath = fieldPath; } @Override public boolean apply(DiffNode input) { return input != null && fieldPath.equals(DiffUtils.toFieldPath(input.getPath())); } } public static Predicate<DiffNode> isField(Binding<?> binding) { return Predicates.and( new RootTypePredicate(binding.getRootBinding().getType()), new FieldPathPredicate(FieldPath.fromBinding(binding)) ); } private static class RootTypePredicate implements Predicate<DiffNode>, Serializable { private static final long serialVersionUID = -3331762514876209810L; private final Class<?> expectedType; public RootTypePredicate(Class<?> expectedType) { super(); this.expectedType = expectedType; } @Override public boolean apply(DiffNode input) { if (input == null) { return false; } DiffNode root = getRootNode(input); return expectedType.isAssignableFrom(root.getValueType()); } } public static boolean isItem(DiffNode node) { return FieldPathComponent.ITEM.equals(toFieldPathComponent(node.getElementSelector())); } public static DiffNode getRootNode(DiffNode node) { DiffNode parent = node; DiffNode currentNode; do { currentNode = parent; parent = currentNode.getParentNode(); } while (parent != null); return currentNode; } public static FieldPathComponent toFieldPathComponent(ElementSelector elementSelector) { if (elementSelector instanceof RootElementSelector) { return null; } else if (elementSelector instanceof CollectionItemElementSelector) { return FieldPathComponent.ITEM; } else if (elementSelector instanceof MapKeyElementSelector) { return FieldPathComponent.ITEM; } else if (elementSelector instanceof IKeyAwareSelector<?>) { return FieldPathComponent.ITEM; } else { return new FieldPathPropertyComponent(elementSelector.toString()); } } public static FieldPath toFieldPath(NodePath nodePath) { final Iterator<ElementSelector> iterator = nodePath.getElementSelectors().iterator(); List<FieldPathComponent> components = Lists.newArrayList(); while (iterator.hasNext()) { final ElementSelector elementSelector = iterator.next(); final FieldPathComponent component = toFieldPathComponent(elementSelector); if (component != null) { components.add(component); } } return FieldPath.of(components); } public static NodePath toNodePath(Binding<?> binding) { return toNodePath(FieldPath.fromBinding(binding)); } public static NodePath toNodePath(FieldPath fieldPath) { final Iterator<FieldPathComponent> iterator = fieldPath.iterator(); AppendableBuilder builder = NodePath.startBuilding(); while (iterator.hasNext()) { final FieldPathComponent pathComponent = iterator.next(); if (FieldPathComponent.ITEM.equals(pathComponent)) { throw new IllegalArgumentException("Transforming FieldPaths containing item path components to NodePath is not supported."); } builder = builder.propertyName(pathComponent.getName()); } return builder.build(); } public static Function<FieldPath, NodePath> toNodePathFunction() { return ToNodePath.INSTANCE; } private static enum ToNodePath implements Function<FieldPath, NodePath> { INSTANCE; @Override public NodePath apply(@Nonnull FieldPath input) { return DiffUtils.toNodePath(input); } @Override public String toString() { return "ToNodePath.INSTANCE"; } }; }