Java tutorial
/******************************************************************************* * Copyright (c) 2013, 2015 Obeo and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Obeo - initial API and implementation * Alexandra Buzila - bug 478620 *******************************************************************************/ package org.eclipse.emf.compare.internal.utils; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterators.concat; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.emf.common.util.AbstractTreeIterator; import org.eclipse.emf.compare.graph.IGraph; import org.eclipse.emf.compare.graph.PruningIterator; /** * This structure will be used to maintain a undirected graph of elements. * <p> * This boils down to maintaining a list of children and parents of each node, updating them in sync with each * other. * </p> * <p> * Take note that the elements of this graph are not necessarily all connected together. This can be used to * represent a set of trees, a set of undirected graphs, a set of roots with no children... * </p> * <p> * This class is not intended to be sub-classed. * </p> * * @param <E> * Kind of elements used as this graph's nodes. * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ public class Graph<E> implements IGraph<E> { /** Keeps track of this graph's individual nodes. */ private final Map<E, Node<E>> nodes; /** * keeps track of this graph's roots. This will reference all nodes with no parents themselves, whether * they are part of the same subgraph or totally unrelated to each other. */ private final Set<Node<E>> roots; /** * All operations affecting the graph will be synchronized so that changes to {@link #nodes} and * {@link #roots} are consistent with each other. */ private final ReentrantLock lock; /** * This will be incremented each time this graph is structurally modified by an operation, ensuring * fail-fast iterations from our returned iterators. */ private transient volatile int modcount; /** Constructs an empty graph. */ public Graph() { this.nodes = new LinkedHashMap<E, Node<E>>(); this.roots = new LinkedHashSet<Node<E>>(); this.lock = new ReentrantLock(); } /** * Checks whether this graph already contains the given element. * * @param element * Element we need to check. * @return <code>true</code> if this graph already contains the given elment, <code>false</code> * otherwise. */ public boolean contains(E element) { lock.lock(); try { return nodes.containsKey(element); } finally { lock.unlock(); } } /** Clears this graph and goes back to a pristine state. */ public void clear() { lock.lock(); try { nodes.clear(); roots.clear(); modcount++; } finally { lock.unlock(); } } /** * Adds a new element to this graph, if it does not exists yet. Elements will initially have no connection * to other elements, and can thus be considered "roots" of the graph. * * @param element * The element to add as a new root to this graph. * @return <code>true</code> if this element did not previously exist in the graph. */ public boolean add(E element) { lock.lock(); try { Node<E> node = nodes.get(element); if (node == null) { node = new Node<E>(element); nodes.put(element, node); roots.add(node); modcount++; return true; } return false; } finally { lock.unlock(); } } /** * Removes the given element's node from this graph. This will effectively break all connections to that * node. * * @param element * The element which is to be removed from this graph. */ public void remove(E element) { lock.lock(); try { final Node<E> node = nodes.remove(element); if (node != null) { node.breakConnections(); roots.remove(node); modcount++; } } finally { lock.unlock(); } } /** * Removes the given elements' nodes from this graph. This will effectively break all connections to these * nodes. * * @param elements * The elements which are to be removed from this graph. */ public void removeAll(Collection<E> elements) { lock.lock(); try { for (E e : elements) { remove(e); } } finally { lock.unlock(); } } /** * Connects the given set of elements to a given parent. Note that nodes will be created for all new * elements if they do not exist yet. * * @param element * The element that is to be connected with new children. * @param newChildren * The set of elements to connect to the given parent. */ public void addChildren(E element, Set<E> newChildren) { lock.lock(); try { Node<E> node = nodes.get(element); if (node == null) { node = new Node<E>(element); nodes.put(element, node); roots.add(node); modcount++; } for (E newChild : newChildren) { Node<E> childNode = nodes.get(newChild); if (childNode == null) { childNode = new Node<E>(newChild); nodes.put(newChild, childNode); } else { roots.remove(childNode); } modcount++; node.connectChild(childNode); } } finally { lock.unlock(); } } /** * Checks if the given element is a parent of the given potential child, directly or not. * * @param parent * Element that could be a parent of <code>potentialChild</code>. * @param potentialChild * The potential child of <code>parent</code>. * @return <code>true</code> if <code>parent</code> is an ancestor of <code>potentialChild</code>. */ public boolean hasChild(E parent, E potentialChild) { lock.lock(); try { final Node<E> node = nodes.get(potentialChild); if (node != null) { return Iterables.any(node.getAllParents(), is(parent)); } return false; } finally { lock.unlock(); } } /** * This predicate will be used to check if a Node's element is the given one. * * @param element * the element we expect a node for. * @return The constructed predicate. */ private Predicate<? super Node<E>> is(final E element) { return new Predicate<Node<E>>() { public boolean apply(Node<E> input) { return input != null && input.getElement().equals(element); } }; } /** * Returns the <u>direct</u> parents of the given <code>element</code>. * <p> * <b>Note</b> that the returned set is a view over a sub-graph of this graph, and that changes to it will * not be reflected within the graph itself. * </p> * * @param element * The element which parents we seek. * @return The set of <u>direct</u> parents for the given <code>element</code>. */ public Set<E> getDirectParents(E element) { final Set<E> parents = new LinkedHashSet<E>(); lock.lock(); try { final Node<E> node = nodes.get(element); if (node != null) { for (Node<E> parent : node.getParents()) { parents.add(parent.getElement()); } } } finally { lock.unlock(); } return parents; } /** * Returns the tree starting from the given root element if it is contained in the graph. * <p> * Contrarily to {@link #getSubgraphContaining(Object)}, this will only iterate over the children (and * recursively) of the given node, without ever "going up" to parents of these children. * </p> * <p> * <b>Note</b> that the returned set is a view over a sub-graph of this graph, and that changes to it will * not be reflected within the graph itself. * </p> * * @param root * The element we are to consider as the root of a tree. * @return The tree starting from the given root element if it is contained in this graph, and empty set * otherwise. */ public Set<E> getTreeFrom(E root) { return getTreeFrom(root, Collections.<E>emptySet()); } /** * Returns the tree starting from the given root element and ending at the given boundaries.. * <p> * Contrarily to {@link #getSubgraphContaining(Object, Set)}, this will only iterate over the children * (and recursively) of the given node, without ever "going up" to parents of these children. * </p> * <p> * <b>Note</b> that the returned set is a view over a sub-graph of this graph, and that changes to it will * not be reflected within the graph itself. * </p> * * @param root * The element we are to consider as the root of a tree. * @param endPoints * Boundaries of the tree. * @return The tree starting from the given root element if it is contained in this graph, and empty set * otherwise. */ public Set<E> getTreeFrom(E root, Set<E> endPoints) { lock.lock(); try { final Node<E> node = nodes.get(root); if (node != null) { Set<E> boundaries = endPoints; if (boundaries == null) { boundaries = Collections.emptySet(); } return new SubgraphBuilder<E>(node, boundaries).buildTree(); } } finally { lock.unlock(); } return Collections.emptySet(); } /** * Returns the set of all elements of the subgraph containing the given element. * <p> * <b>Note</b> that the returned set is a view over a sub-graph of this graph, and that changes to it will * not be reflected within the graph itself. * </p> * * @param element * Element we need the subgraph of. * @return The set of all elements of the subgraph containing the given element, an empty set if that * element is not present in this graph. */ public Set<E> getSubgraphContaining(E element) { return getSubgraphContaining(element, Collections.<E>emptySet()); } /** * Returns the set of all elements of the subgraph containing the given element and ending at the given * boundaries. * <p> * <b>Note</b> that the returned set is a view over a sub-graph of this graph, and that changes to it will * not be reflected within the graph itself. * </p> * * @param element * Element we need the subgraph of. * @param endPoints * Boundaries of the needed subgraph. * @return An iterable over all elements of the subgraph containing the given element, an empty set if * that element is not present in this graph. */ public Set<E> getSubgraphContaining(E element, Set<E> endPoints) { lock.lock(); try { final Node<E> node = nodes.get(element); if (node != null) { Set<E> boundaries = endPoints; if (boundaries == null) { boundaries = Collections.emptySet(); } return new SubgraphBuilder<E>(node, boundaries).buildSubgraph(); } return Collections.emptySet(); } finally { lock.unlock(); } } /** * Returns a breadth-first iterator over this whole graph. This will begin iteration on this graph's roots * (whether they are linked together (directly or indirectly) or not), then carry on over each depth * level. This will never visit the same element twice, nor will it ever visit an element which parents * haven't all been iterated over yet. * <p> * The returned iterator does not support removal, and will fail or return undefined results if this graph * is modified after the iterator's creation. * </p> * * @return A breadth-first iterator over this whole graph. */ public PruningIterator<E> breadthFirstIterator() { return new BreadthFirstIterator(); } /** * Returns a depth first iterator created with the given element as root. If the graph contains cycles, * the same node won't be returned twice. * <p> * The root will be returned first, then the left-most child of that root, then the left-most child of * that child if any, or the closest sibling to the right of the current element. For example, with the * following tree: * * <pre> * A * / \ * B C * / / \ * D E F * </pre> * * The iteration order will be : A, B, D, C, E, F. * </p> * <p> * The returned iterator does not support removal, and will fail or return undefined results if this graph * is modified after the iterator's creation. * </p> * * @param root * The root of the tree over which we need to iterate. * @return An iterator over the tree starting from the given root. */ public Iterator<E> depthFirstIterator(E root) { return new ChildrenIterator(root); } /** * Get the parent data of the given element. * <p> * The parent data, is the URI of the parent resource's object in case of a containment relationship * between the given element and its parent. * <p> * * @param element * Element we need the parent data of. * @return A parent data of type <code>E</code> if this element has a parent data, <code>null</code> * otherwise. */ public E getParentData(E element) { lock.lock(); try { Node<E> node = nodes.get(element); if (node != null) { return node.getParentData(); } } finally { lock.unlock(); } return null; } /** * Set the parent data for the given element. * <p> * The parent data, is the URI of the parent resource's object in case of a containment relationship * between the given element and its parent. * </p> * If the given element has several parents, then the addition of a new parent data results in delete the * parent data. If the given element has no parents, then the addition of a new parent data results in * delete the parent data. * * @param element * Element for which we need to set the parent data. * @param parentData * The parent data to set. */ public void addParentData(E element, E parentData) { lock.lock(); try { Node<E> node = nodes.get(element); if (node != null) { E tmp = this.getParentData(element); if (tmp == null) { node.setParentData(parentData); } else { node.setParentData(null); } } } finally { lock.unlock(); } } /** * This will be used to represent individual elements in our graph. * * @param <K> * Type of the Node's underlying data. * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private static class Node<K> { /** Underlying data of this Node. */ private final K element; /** The object that contains underlying data of this Node. */ private K parentData; /** Nodes that are connected with this one as a parent. */ private final Set<Node<K>> children; /** Nodes that are connected with this one as a child. */ private final Set<Node<K>> parents; /** * Construct a new Node for the given element. * * @param element * The element for which we need a graph Node. This element must not be null. */ public Node(K element) { Preconditions.checkNotNull(element); this.element = element; this.parentData = null; this.parents = new LinkedHashSet<Node<K>>(); this.children = new LinkedHashSet<Node<K>>(); } /** * Returns all children nodes of this one. * * @return All children nodes of this one. */ public Set<Node<K>> getChildren() { return Collections.unmodifiableSet(children); } /** * Returns all parent nodes of this one. * * @return All parent nodes of this one. */ public Set<Node<K>> getParents() { return Collections.unmodifiableSet(parents); } /** * Returns all direct and indirect parents of this node. * * @return All direct and indirect parents of this node. */ public Iterable<Node<K>> getAllParents() { return new ParentsIterable<K>(this); } /** * Registers the given element as a child of this one. * * @param child * The Node that is to be connected as a child of this one. */ public void connectChild(Node<K> child) { this.children.add(child); child.parents.add(this); } /** * Breaks all connections of this node. */ public void breakConnections() { for (Node<K> parent : this.parents) { parent.children.remove(this); } for (Node<K> child : this.children) { child.parents.remove(this); } this.parents.clear(); this.children.clear(); this.parentData = null; } /** * Returns the underlying data of this Node. * * @return The underlying data of this Node. */ public K getElement() { return element; } /** * Returns the object that contains underlying data of this Node. * * @return The object that contains underlying data of this Node. */ public K getParentData() { return parentData; } /** * Sets the object that contains underlying data of this Node. * * @param parentData * the object that contains underlying data of this Node. */ public void setParentData(K parentData) { this.parentData = parentData; } } /** * This can be used to create a set over an entire subgraph given a starting point within said subgraph. * * @param <L> * Kind of elements the created set is meant to contain. * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private static class SubgraphBuilder<L> { /** Keeps track of the elements we've already iterated over. */ protected final Set<L> set; /** Nodes that will be considered as boundaries for this subgraph. */ protected final Set<L> endPoints; /** The Node that will be used as a starting point of the iteration. */ private final Node<L> start; /** * Constructs a new iterable given the starting point the target subgraph and the elements that will * be considered as (excluded) boundaries of that subgraph. * * @param start * Starting point of the iteration. * @param endPoints * Excluded boundaries of the target subgraph. Iteration will be pruned on these, along * with their own subgraphs. */ public SubgraphBuilder(Node<L> start, Set<L> endPoints) { this.start = start; this.set = new LinkedHashSet<L>(); this.set.add(start.getElement()); this.endPoints = checkNotNull(endPoints); } /** * Constructs a set over the entire subgraph containing the specified starting point. * * @return The set of all nodes composing the required subgraph. */ public Set<L> buildSubgraph() { return ImmutableSet.copyOf(new NodeIterator(start)); } /** * Constructs a set over the subtree starting from the specified starting point. This will only * (recursively) iterate over the children of the nodes. * * @return The tree containined the specified starting point. */ public Set<L> buildTree() { final Iterator<L> iterator = new NodeIterator(start) { @Override protected Iterator<Node<L>> createConnectedNodesIterator(Node<L> node) { return node.getChildren().iterator(); } }; return ImmutableSet.copyOf(iterator); } /** * A node iterator will allow us to iterate over the data of a Node and the data of all directly * connected Nodes; all the while avoiding data we've already iterated over. * <p> * This kind of iterator does not support {@link #remove() removal}. * </p> * <p> * Note : this is not a static class as we're building the external SubgraphBuilder's set through this * (and checking for already iterated over elements). * </p> * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private class NodeIterator implements Iterator<L> { /** Iterator over the all nodes directly connected with the underlying Node. */ private final Iterator<Node<L>> nodesIterator; /** * Keeps track of the next Node's iterator. This will allow recursion over the connected Nodes' * own connections. */ private Iterator<L> nextNodeIterator; /** Element that should be returned by the next call to {@link #next()}. */ private L next; /** * Construct an iterator over the given Node's connected data. * * @param node * The node for which we need an iterator. */ public NodeIterator(Node<L> node) { this.next = node.getElement(); this.nodesIterator = createConnectedNodesIterator(node); prepareNextIterator(); } /** * Creates the iterator over the nodes connected to the current. By default, this will iterate * over all parents, then all children. * * @param node * the starting node for this iteration. * @return Iterator over the connected nodes we are to iterate over. */ protected Iterator<Node<L>> createConnectedNodesIterator(Node<L> node) { return concat(node.getParents().iterator(), node.getChildren().iterator()); } /** * {@inheritDoc} * * @see java.util.Iterator#hasNext() */ public boolean hasNext() { return next != null || nextNodeIterator.hasNext(); } /** * {@inheritDoc} * * @see java.util.Iterator#next() */ public L next() { if (next == null) { throw new NoSuchElementException(); } L result = next; prepareNext(); return result; } /** * {@inheritDoc} * * @see java.util.Iterator#remove() */ public void remove() { throw new UnsupportedOperationException(); } /** * This will be called to prepare for a subsequent call to {@link #next()}. */ private void prepareNext() { if (!nextNodeIterator.hasNext()) { prepareNextIterator(); } if (nextNodeIterator.hasNext()) { next = nextNodeIterator.next(); } else { next = null; } } /** * This will be called whenever we've consumed all nodes connected to the current to "jump over" * to the next one. */ private void prepareNextIterator() { if (nodesIterator.hasNext()) { Node<L> nextNode = nodesIterator.next(); while (SubgraphBuilder.this.set.contains(nextNode.getElement()) && !SubgraphBuilder.this.endPoints.contains(nextNode.getElement()) && nodesIterator.hasNext()) { nextNode = nodesIterator.next(); } if (!SubgraphBuilder.this.endPoints.contains(nextNode.getElement()) && SubgraphBuilder.this.set.add(nextNode.getElement())) { nextNodeIterator = new NodeIterator(nextNode); } else { nextNodeIterator = Iterators.emptyIterator(); } } else { nextNodeIterator = Iterators.emptyIterator(); } } } } /** * A custom Iterable that will iterate over the Node->parent Node tree of a given Node. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private static class ParentsIterable<O> implements Iterable<Node<O>> { /** The leaf of the Node->parent tree for which this iterable has been constructed. */ private final Node<O> start; /** * Constructs an iterable given the leaf of its tree. * * @param start * Leaf node of the tree we'll iterate over. */ public ParentsIterable(Node<O> start) { this.start = start; } /** * {@inheritDoc} * * @see java.lang.Iterable#iterator() */ public Iterator<Node<O>> iterator() { return new ParentsIterator<O>(start); } } /** * A custom TreeIterator that will iterate over the Node->parent Node tree. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private static class ParentsIterator<P> extends AbstractTreeIterator<Node<P>> { /** Generated SUID. */ private static final long serialVersionUID = -4476850344598138970L; /** * Construct an iterator given the root (well, "leaf") of its tree. * * @param start * Start node of the tree we'll iterate over. */ public ParentsIterator(Node<P> start) { super(start, false); } /** * {@inheritDoc} * * @see org.eclipse.emf.common.util.AbstractTreeIterator#getChildren(java.lang.Object) */ @SuppressWarnings("unchecked") @Override protected Iterator<? extends Node<P>> getChildren(Object obj) { return ((Node<P>) obj).getParents().iterator(); } } /** * A custom iterator implementing a tree iteration algorithm over the underlying graph, using a given node * as root. * <p> * <b>Note</b> that this iterator doesn't allow clients to structurally change the graph after its * creation. Any attempt at doing so will make the next call to {@link #next()} fail in * ConcurrentModificationException. * </p> * <p> * This iterator does not support removal of values from its underlying graph. * </p> * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private class ChildrenIterator implements Iterator<E>, Predicate<Node<E>> { /** * The expected {@link Graph#modcount} for this iterator. Any attempt at calling {@link #next()} after * the underlying graph has been modified will fail in {@link ConcurrentModificationException}. */ private final int expectedModCount; /** The current stack of iterators. */ private final LinkedList<Iterator<Node<E>>> iteratorStack; /** * The set of all nodes from the underlying graph we've already iterated over. This will prevent us * from cycling over nodes if there are cycles in our graph, and will also prevent us from iterating * twice over the same node (when a node has multiple parents). */ private final Set<Node<E>> consumedNodes; /** * Creates an iterator over the tree starting from the given root element. * * @param root * Root of the tree we are to iterate over. */ public ChildrenIterator(E root) { this.expectedModCount = Graph.this.modcount; this.iteratorStack = new LinkedList<Iterator<Node<E>>>(); iteratorStack.add(Iterators.singletonIterator(Graph.this.nodes.get(root))); consumedNodes = new HashSet<Node<E>>(); } /** {@inheritDoc} */ public boolean hasNext() { return !iteratorStack.isEmpty() && iteratorStack.getLast().hasNext(); } /** {@inheritDoc} */ public E next() { if (Graph.this.modcount != expectedModCount) { throw new ConcurrentModificationException(); } if (iteratorStack.isEmpty()) { throw new NoSuchElementException(); } Node<E> resultNode = iteratorStack.getLast().next(); if (consumedNodes.add(resultNode)) { // no check for emptiness, would be redundant with the following loop iteratorStack.add(Iterators.filter(resultNode.getChildren().iterator(), this)); } // remove the iterators we've consumed entirely from the stack. ListIterator<Iterator<Node<E>>> reverseStackIterator = iteratorStack.listIterator(iteratorStack.size()); while (reverseStackIterator.hasPrevious() && !reverseStackIterator.previous().hasNext()) { reverseStackIterator.remove(); } return resultNode.getElement(); } /** * Unsupported. * * @throws UnsupportedOperationException */ public void remove() { throw new UnsupportedOperationException(); } /** * Predicate implementation, only nodes that are not consumed will be accepted. * * @param input * The node to filter * @return true If and only if the given node is not in comsumedNodes. */ public boolean apply(Node<E> input) { return !consumedNodes.contains(input); } } /** * A custom iterator implementing a breadth-first iteration algorithm over the underlying graph. This will * start from the graph's {@link Graph#roots roots} and carry the iteration downward to each individual * node, never iterating twice on the same node, and never iterating over a node which parents have yet to * be iterated over. * <p> * <b>Note</b> that this iterator doesn't allow clients to structurally change the graph after its * creation. Any attempt at doing so will make the next call to {@link #next()} fail in * ConcurrentModificationException. * </p> * <p> * This iterator does not support removal of values from its underlying graph. * </p> * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private class BreadthFirstIterator implements PruningIterator<E> { /** * The current depth-level iterator (first, an iterator over all roots, then, an iterator over all of * these roots' direct children, and so on). See also {@link #nextIterable}. */ private Iterator<Node<E>> currentIterator; /** * The set of all children found for the current depth level. This will be used in conjunction with * {@link #consumedNodes} in order to determine the iterator we'll use when {@link #currentIterator} * is exhausted. */ private Set<Node<E>> nextIterable; /** * The set of all nodes from the underlying graph we've already iterated over. This will prevent us * from cycling over nodes if there are cycles in our graph, and will also prevent us from iterating * twice over the same node (when a node has multiple parents). */ private Set<Node<E>> consumedNodes; /** * The node we've last returned from a call to {@link #next()}. This will be <code>null</code>ed out * by a call to {@link #prune()}. */ private Node<E> lastReturned; /** * The node we'll next return from a call to {@link #next()}. <code>null</code> when we've reached the * end of our iteration. */ private Node<E> next; /** * The expected {@link Graph#modcount} for this iterator. Any attempt at calling {@link #next()} after * the underlying graph has been modified will fail in {@link ConcurrentModificationException}. */ private final int expectedModCount; /** Default constructor. */ public BreadthFirstIterator() { this.currentIterator = Graph.this.roots.iterator(); this.nextIterable = new LinkedHashSet<Node<E>>(); this.consumedNodes = new LinkedHashSet<Node<E>>(); this.expectedModCount = Graph.this.modcount; prepareNext(); } /** {@inheritDoc} */ public boolean hasNext() { return next != null || currentIterator.hasNext() || !consumedNodes.containsAll(nextIterable); } /** {@inheritDoc} */ public E next() { if (Graph.this.modcount != expectedModCount) { throw new ConcurrentModificationException(); } if (next == null) { throw new NoSuchElementException(); } final E result = next.getElement(); consumedNodes.add(next); lastReturned = next; prepareNext(); return result; } /** {@inheritDoc} */ public void prune() { // do not accept successive pruning calls, and don't fail when trying to prune before even // starting the iteration. if (lastReturned == null) { return; } // the current has already been added to the "consumed" set. // All direct and indirect children of this path must be pruned as well. // TODO any effective way of achieving this without iterating? Set<Node<E>> children = lastReturned.getChildren(); lastReturned = null; while (!children.isEmpty()) { final Set<Node<E>> copy = children; children = new LinkedHashSet<Node<E>>(); for (Node<E> child : copy) { if (consumedNodes.add(child)) { children.addAll(child.getChildren()); } } } } /** * Unsupported. * * @throws UnsupportedOperationException */ public void remove() { throw new UnsupportedOperationException(); } /** This will prepare this iterator for a subsequent call to {@link #next()}. */ private void prepareNext() { if (!currentIterator.hasNext()) { prepareNextIterator(); } if (currentIterator.hasNext()) { next = currentIterator.next(); nextIterable.addAll(next.getChildren()); } else { next = null; } } /** * Whenever we've consumed all of the current depth's nodes, this will create a new iterator for the * next depth level out of {@link #nextIterable} and {@link #consumedNodes}. */ private void prepareNextIterator() { final Set<Node<E>> difference = new LinkedHashSet<Node<E>>(); for (Node<E> node : nextIterable) { if (!consumedNodes.contains(node)) { difference.add(node); } } currentIterator = Iterators.filter(difference.iterator(), new Predicate<Node<E>>() { public boolean apply(Node<E> input) { return consumedNodes.containsAll(input.getParents()); } }); nextIterable.clear(); } } }