/* Copyright (c) 2012-2013 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.api.plumbing.diff; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Iterator; import javax.annotation.Nullable; import org.locationtech.geogig.api.Bounded; import org.locationtech.geogig.api.Bucket; import org.locationtech.geogig.api.Node; import org.locationtech.geogig.api.NodeRef; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.RevObject.TYPE; import org.locationtech.geogig.api.RevTree; import org.locationtech.geogig.storage.ObjectDatabase; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.AbstractIterator; import com.google.common.collect.Iterators; /** * An iterator over a {@link RevTree} that can return different results depending on the * {@link #Strategy} given; */ public class DepthTreeIterator extends AbstractIterator<NodeRef> { public enum Strategy { /** * Default strategy, list the all direct child entries of a tree, no recursion */ CHILDREN, /** * List only the direct child entries of a tree that are of type FEATURE */ FEATURES_ONLY, /** * List only the direct child entries of a tree that are of type TREE */ TREES_ONLY, /** * Recursively list the contents of a tree in depth-first order, including both TREE and * FEATURE entries */ RECURSIVE, /** * Recursively list the contents of a tree in depth-first order, but do not report TREE * entries, only FEATURE ones */ RECURSIVE_FEATURES_ONLY, /** * Recursively list the contents of a tree in depth-first order, but do not report TREE * entries, only FEATURE ones */ RECURSIVE_TREES_ONLY } private Iterator<NodeRef> iterator; private ObjectDatabase source; private Strategy strategy; private Predicate<Bounded> boundsFilter; private NodeToRef functor; private RevTree tree; private String treePath; private ObjectId metadataId; private static class NodeToRef implements Function<Node, NodeRef> { private final String treePath; private final ObjectId metadataId; public NodeToRef(String treePath, ObjectId metadataId) { this.treePath = treePath; this.metadataId = metadataId; } @Override public NodeRef apply(Node node) { return new NodeRef(node, treePath, node.getMetadataId().or(metadataId)); } }; public DepthTreeIterator(final String treePath, final ObjectId metadataId, RevTree tree, ObjectDatabase source, Strategy strategy) { checkNotNull(treePath); checkNotNull(metadataId); checkNotNull(tree); checkNotNull(source); checkNotNull(strategy); this.tree = tree; this.treePath = treePath; this.metadataId = metadataId; this.source = source; this.strategy = strategy; this.functor = new NodeToRef(treePath, metadataId); this.boundsFilter = Predicates.alwaysTrue(); } public void setBoundsFilter(@Nullable Predicate<Bounded> boundsFilter) { Predicate<Bounded> alwaysTrue = Predicates.alwaysTrue(); this.boundsFilter = boundsFilter == null ? alwaysTrue : boundsFilter; } @Override protected NodeRef computeNext() { if (iterator == null) { switch (strategy) { case CHILDREN: iterator = Iterators.transform(new Children(tree), functor); break; case FEATURES_ONLY: iterator = Iterators.transform(new Features(tree), functor); break; case TREES_ONLY: iterator = Iterators.transform(new Trees(tree), functor); break; case RECURSIVE: iterator = new Recursive(treePath, metadataId, tree, true, true); break; case RECURSIVE_FEATURES_ONLY: iterator = new Recursive(treePath, metadataId, tree, true, false); break; case RECURSIVE_TREES_ONLY: iterator = new Recursive(treePath, metadataId, tree, false, true); break; default: throw new IllegalArgumentException("Unrecognized strategy: " + strategy); } } if (iterator.hasNext()) { return iterator.next(); } return endOfData(); } private class Recursive extends AbstractIterator<NodeRef> { private boolean features; private boolean trees; private Iterator<Node> myEntries; private Iterator<NodeRef> currEntryIterator; private NodeToRef functor; public Recursive(String treePath, ObjectId metadataId, RevTree tree, boolean features, boolean trees) { Preconditions.checkArgument(features || trees); this.functor = new NodeToRef(treePath, metadataId); this.features = features; this.trees = trees; if (!features) { this.myEntries = new Trees(tree); } else { this.myEntries = new Children(tree); } currEntryIterator = Iterators.emptyIterator(); } @Override protected NodeRef computeNext() { while (!currEntryIterator.hasNext()) { if (myEntries.hasNext()) { currEntryIterator = resolveEntryIterator(myEntries.next()); } else { return endOfData(); } } return currEntryIterator.next(); } private Iterator<NodeRef> resolveEntryIterator(Node next) { if (TYPE.FEATURE.equals(next.getType())) { if (features) { return Iterators.singletonIterator(functor.apply(next)); } return Iterators.emptyIterator(); } Preconditions.checkArgument(TYPE.TREE.equals(next.getType())); ObjectId treeId = next.getObjectId(); RevTree childTree = source.getTree(treeId); String childTreePath = NodeRef.appendChild(this.functor.treePath, next.getName()); Iterator<NodeRef> children = new Recursive(childTreePath, next.getMetadataId().or( functor.metadataId), childTree, features, trees); if (trees) { children = Iterators.concat(Iterators.singletonIterator(functor.apply(next)), children); } return children; } } private class Children extends AbstractIterator<Node> { private Iterator<Node> children; public Children(RevTree tree) { if (tree.buckets().isPresent()) { this.children = new Buckets(tree); } else { this.children = Iterators.filter(tree.children(), boundsFilter); } } @Override protected Node computeNext() { if (children.hasNext()) { return children.next(); } return endOfData(); } } private class Features extends AbstractIterator<Node> { private Iterator<Node> features; public Features(RevTree tree) { if (tree.features().isPresent()) { this.features = Iterators.filter(tree.features().get().iterator(), boundsFilter); } else if (tree.buckets().isPresent()) { this.features = new FeatureBuckets(tree); } else { this.features = Iterators.emptyIterator(); } } @Override protected Node computeNext() { if (features.hasNext()) { return features.next(); } return endOfData(); } } private class Trees extends AbstractIterator<Node> { private Iterator<Node> trees; public Trees(RevTree tree) { if (tree.numTrees() == 0) { this.trees = Iterators.emptyIterator(); } else if (tree.trees().isPresent()) { this.trees = Iterators.filter(tree.trees().get().iterator(), boundsFilter); } else if (tree.buckets().isPresent()) { this.trees = new TreeBuckets(tree); } else { this.trees = Iterators.emptyIterator(); } } @Override protected Node computeNext() { if (trees.hasNext()) { return trees.next(); } return endOfData(); } } /** * Returns all direct children of a buckets tree */ private class Buckets extends AbstractIterator<Node> { private Iterator<Bucket> buckets; private Iterator<Node> bucketEntries; public Buckets(RevTree tree) { Preconditions.checkArgument(tree.buckets().isPresent()); buckets = Iterators.filter(tree.buckets().get().values().iterator(), boundsFilter); bucketEntries = Iterators.emptyIterator(); } @Override protected Node computeNext() { while (!bucketEntries.hasNext()) { if (buckets.hasNext()) { Bucket nextBucket = buckets.next(); bucketEntries = resolveBucketEntries(nextBucket.id()); } else { return endOfData(); } } return bucketEntries.next(); } /** * @param bucketId * @return */ protected Iterator<Node> resolveBucketEntries(ObjectId bucketId) { RevTree bucketTree = source.getTree(bucketId); if (bucketTree.buckets().isPresent()) { return new Buckets(bucketTree); } return new Children(bucketTree); } } /** * Returns all direct children of a buckets tree of type TREE */ private class TreeBuckets extends Buckets { public TreeBuckets(RevTree tree) { super(tree); } @Override protected Iterator<Node> resolveBucketEntries(ObjectId bucketId) { RevTree bucketTree = source.getTree(bucketId); if (bucketTree.numTrees() == 0) { return Iterators.emptyIterator(); } if (bucketTree.trees().isPresent()) { return new Trees(bucketTree); } if (bucketTree.buckets().isPresent()) { return new TreeBuckets(bucketTree); } return Iterators.emptyIterator(); } } /** * Returns all direct children of a buckets tree of type FEATURE */ private class FeatureBuckets extends Buckets { public FeatureBuckets(RevTree tree) { super(tree); } @Override protected Iterator<Node> resolveBucketEntries(ObjectId bucketId) { RevTree bucketTree = source.getTree(bucketId); if (bucketTree.buckets().isPresent()) { return new FeatureBuckets(bucketTree); } if (bucketTree.features().isPresent()) { return new Features(bucketTree); } return Iterators.emptyIterator(); } } }