/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.utility.iterators; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.Set; import org.eclipse.persistence.tools.workbench.utility.ClassTools; /** * A <code>GraphIterator</code> is similar to a <code>TreeIterator</code> * except that it cannot be assumed that all nodes assume a strict tree * structure. For instance, in a tree, a node cannot be a descendent of * itself, but a graph may have a cyclical structure. * * A <code>GraphIterator</code> simplifies the traversal of a * graph of objects, where the objects' protocol(s) provides * a method for getting the next collection of nodes in the graph, * (or *neighbors*), but does not provide a method for getting *all* * of the nodes in the graph. * (e.g. a neighbor can return his neighbors, and those neighbors * can return their neighbors, which might also include the original * neighbor, but you only want to visit the original neighbor once.) * <p> * If a neighbor has already been visited (determined by using * <code>equals(Object)</code>), that neighbor is not visited again, * nor are the neighbors of that object. * <p> * It is up to the user of this class to ensure a *complete* graph. * <p> * To use, supply:<ul> * <li> either the initial node of the graph or an Iterator over an * initial collection of graph nodes * <li> a <code>MisterRogers</code> that tells who the neighbors are * of each node * (alternatively, subclass <code>GraphIterator</code> * and override the <code>neighbors(Object)</code> method) * </ul> * <p> * <code>remove()</code> is not supported. This method, if * desired, must be implemented by the user of this class. */ public class GraphIterator implements Iterator { private Collection iterators; private Set visitedNeighbors; private MisterRogers misterRogers; private Iterator currentIterator; private static final Iterator END_ITERATOR = NullIterator.instance(); private Object nextNeighbor; private static final Object END_NEIGHBOR = new Object(); /** * Construct an iterator with the specified collection of roots * and a misterRogers that simply returns an empty iterator * for each of the roots. * Use this constructor if you want to override the * <code>children(Object)</code> method instead of building * a <code>MisterRogers</code>. */ public GraphIterator(Iterator roots) { this(roots, MisterRogers.NULL_INSTANCE); } /** * Construct an iterator with the specified root * and a misterRogers that simply returns an empty iterator * for the root. * Use this constructor if you want to override the * <code>children(Object)</code> method instead of building * a <code>MisterRogers</code>. */ public GraphIterator(Object root) { this(root, MisterRogers.NULL_INSTANCE); } /** * Construct an iterator with the specified root * and misterRogers. */ public GraphIterator(Object root, MisterRogers misterRogers) { this(new SingleElementIterator(root), misterRogers); } /** * Construct an iterator with the specified roots * and misterRogers. */ public GraphIterator(Iterator roots, MisterRogers misterRogers) { super(); this.currentIterator = roots; // use a LinkedList since we will be pulling off the front and adding to the end this.iterators = new LinkedList(); this.misterRogers = misterRogers; this.visitedNeighbors = new HashSet(); this.loadNextNeighbor(); } /** * Load nextNeighbor with the next entry from the current iterator. * If the current iterator has none, load the next iterator. * If there are no more, nextNeighbor is set to <code>END_NEIGHBOR</code>. */ private void loadNextNeighbor() { if (this.currentIterator == END_ITERATOR) { this.nextNeighbor = END_NEIGHBOR; } else if (this.currentIterator.hasNext()) { Object nextPossibleNeighbor = this.currentIterator.next(); if (this.visitedNeighbors.contains(nextPossibleNeighbor)) { this.loadNextNeighbor(); } else { this.nextNeighbor = nextPossibleNeighbor; this.visitedNeighbors.add(nextPossibleNeighbor); this.iterators.add(this.neighbors(nextPossibleNeighbor)); } } else { for (Iterator stream = this.iterators.iterator(); ! this.currentIterator.hasNext() && stream.hasNext(); ) { this.currentIterator = (Iterator) stream.next(); stream.remove(); } if (! this.currentIterator.hasNext()) { this.currentIterator = END_ITERATOR; } this.loadNextNeighbor(); } } /** * @see java.util.Iterator#hasNext() */ public boolean hasNext() { return this.nextNeighbor != END_NEIGHBOR; } /** * @see java.util.Iterator#next() */ public Object next() { if (this.nextNeighbor == END_NEIGHBOR) { throw new NoSuchElementException(); } Object next = this.nextNeighbor; this.loadNextNeighbor(); return next; } /** * @see java.util.Iterator#remove() */ public void remove() { throw new UnsupportedOperationException("remove()"); } /** * Return the immediate children of the specified object. */ protected Iterator neighbors(Object next) { return this.misterRogers.neighbors(next); } /** * @see java.lang.Object#toString() */ public String toString() { return ClassTools.shortClassNameForObject(this) + '(' + this.currentIterator + ')'; } //********** inner classes ********** /** * Used by <code>GraphIterator</code> to retrieve * the immediate neighbors of a node in the graph. * "These are the people in your neighborhood..." */ public interface MisterRogers { /** * Return the immediate neighbors of the specified object. */ Iterator neighbors(Object next); MisterRogers NULL_INSTANCE = new MisterRogers() { // return no neighbors public Iterator neighbors(Object next) { return NullIterator.instance(); } public String toString() { return super.toString() + "(Hello, neighbor.)"; } }; } }