/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.jboss.com.sun.corba.se.impl.orbutil.graph;
import java.util.Collection;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
public class GraphImpl extends AbstractSet<Node> implements Graph
{
private Map<Node, NodeData> nodeToData;
public GraphImpl()
{
nodeToData = new HashMap<Node, NodeData>();
}
public GraphImpl(Collection<Node> coll)
{
this();
addAll(coll);
}
/***********************************************************************************/
/************ AbstractSet implementation *******************************************/
/***********************************************************************************/
// Required for AbstractSet
public boolean add(Node obj) // obj must be a Node
{
Node node = obj;
boolean found = nodeToData.keySet().contains(obj);
if (!found)
{
NodeData nd = new NodeData();
nodeToData.put(node, nd);
}
return !found;
}
// Required for AbstractSet
public Iterator<Node> iterator()
{
return nodeToData.keySet().iterator();
}
// Required for AbstractSet
public int size()
{
return nodeToData.keySet().size();
}
/***********************************************************************************/
public NodeData getNodeData(Node node)
{
return nodeToData.get(node);
}
private void clearNodeData()
{
// Clear every node
Iterator<Map.Entry<Node, NodeData>> iter = nodeToData.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry<Node, NodeData> entry = iter.next();
NodeData nd = entry.getValue();
nd.clear();
}
}
interface NodeVisitor
{
void visit(Graph graph, Node node, NodeData nd);
}
// This visits every node in the graph exactly once. A visitor is allowed to modify the graph during the traversal.
void visitAll(NodeVisitor nv)
{
boolean done = false;
// Repeat the traversal until every node has been visited. Since it takes one pass to determine whether or not
// each node has already been visited, this loop always runs at least once.
do
{
done = true;
// Copy entries to array to avoid concurrent modification problem with iterator if the visitor is updating
// the graph.
@SuppressWarnings("unchecked")
Map.Entry<Node, NodeData>[] entries = nodeToData.entrySet().toArray(new Map.Entry[0]);
// Visit each node in the graph that has not already been visited.
// If any node is visited in this pass, we must run at least one more pass.
for (int ctr = 0; ctr < entries.length; ctr++)
{
Map.Entry<Node, NodeData> current = entries[ctr];
Node node = current.getKey();
NodeData nd = current.getValue();
if (!nd.isVisited())
{
nd.visited();
done = false;
nv.visit(this, node, nd);
}
}
}
while (!done);
}
private void markNonRoots()
{
visitAll(new NodeVisitor()
{
public void visit(Graph graph, Node node, NodeData nd)
{
Iterator<Node> iter = node.getChildren().iterator(); // Iterator<Node>
while (iter.hasNext())
{
Node child = iter.next();
// Make sure the child is in the graph so it can be visited later if necessary.
graph.add(child);
// Mark the child as a non-root, since a child is never a root.
NodeData cnd = graph.getNodeData(child);
cnd.notRoot();
}
}
});
}
private Set<Node> collectRootSet()
{
final Set<Node> result = new HashSet<Node>();
Iterator<Map.Entry<Node, NodeData>> iter = nodeToData.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry<Node, NodeData> entry = iter.next();
Node node = entry.getKey();
NodeData nd = entry.getValue();
if (nd.isRoot())
result.add(node);
}
return result;
}
public Set<Node> getRoots()
{
clearNodeData();
markNonRoots();
return collectRootSet();
}
}