/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.graph.traverse.standard;
import java.util.Map;
import junit.framework.TestCase;
import org.geotools.graph.GraphTestUtil;
import org.geotools.graph.build.GraphBuilder;
import org.geotools.graph.build.basic.BasicGraphBuilder;
import org.geotools.graph.structure.Graph;
import org.geotools.graph.structure.GraphVisitor;
import org.geotools.graph.structure.Graphable;
import org.geotools.graph.structure.Node;
import org.geotools.graph.traverse.GraphTraversal;
import org.geotools.graph.traverse.basic.BasicGraphTraversal;
import org.geotools.graph.traverse.basic.CountingWalker;
import org.geotools.graph.util.FIFOQueue;
public class DepthFirstIteratorTest extends TestCase {
private GraphBuilder m_builder;
public DepthFirstIteratorTest(String name) {
super(name);
}
protected void setUp() throws Exception {
super.setUp();
m_builder = createBuilder();
}
/**
* Create a simple graph which has no bifurcations and do a normal traversal.
* <BR>
* <BR>
* Expected: 1. Every node should be visited.
* 2. Nodes should be visited in order.
*/
public void test_0() {
int nnodes = 100;
Node[] ends = GraphTestUtil.buildNoBifurcations(builder(), nnodes);
CountingWalker walker = new CountingWalker() {
public int visit(Graphable element, GraphTraversal traversal) {
super.visit(element, traversal);
element.setCount(getCount()-1);
//nodes should be visited in order
assertTrue(element.getID() == getCount()-1);
return(GraphTraversal.CONTINUE);
}
};
DepthFirstIterator iterator = createIterator();
BasicGraphTraversal traversal = new BasicGraphTraversal(
builder().getGraph(), walker, iterator
);
traversal.init();
iterator.setSource(ends[0]);
traversal.traverse();
//every node should have been visited
assertTrue(walker.getCount() == builder().getGraph().getNodes().size());
//ensure nodes visited only once
assertTrue(walker.getCount() == nnodes);
}
/**
* Create a simple graph which has no bifurcations and do a traversal
* suspending at some intermediate node. Then continue traversal.
*
* Expected: After suspend:
* 1. Every node of with an id greater than the id of the suspending
* node should not be visited.
* After continue:
* 1. First node visited after continue should have id =
* id + suspend node + 1
* 2. Every node should be visited.
*
*/
public void test_1() {
int nnodes = 100;
Node[] ends = GraphTestUtil.buildNoBifurcations(builder(), nnodes);
final int suspend = 50;;
CountingWalker walker = new CountingWalker() {
private int m_mode = 0;
public int visit(Graphable element, GraphTraversal traversal) {
super.visit(element, traversal);
if (m_mode == 0) {
//check for stopping node
if (element.getID() == suspend) {
m_mode++;
return(GraphTraversal.SUSPEND);
}
}
else if (m_mode == 1) {
//check first node after continue
assertTrue(element.getID() == suspend + 1);
m_mode++;
}
return(GraphTraversal.CONTINUE);
}
};
DepthFirstIterator iterator = createIterator();
BasicGraphTraversal traversal = new BasicGraphTraversal(
builder().getGraph(), walker, iterator
);
traversal.init();
iterator.setSource(ends[0]);
traversal.traverse();
//stopping node should be visited and nodes with greater id should not
GraphVisitor visitor = new GraphVisitor() {
public int visit(Graphable component) {
if (component.getID() <= suspend) assertTrue(component.isVisited());
else assertTrue(!component.isVisited());
return(0);
}
};
builder().getGraph().visitNodes(visitor);
//ensure nodes only visited once
assertTrue(walker.getCount() == nnodes-suspend+1);
traversal.traverse();
//every node should now be visited
visitor = new GraphVisitor() {
public int visit(Graphable component) {
assertTrue(component.isVisited());
return(0);
}
};
builder().getGraph().visitNodes(visitor);
//ensure nodes only visited once
assertTrue(walker.getCount() == nnodes);
}
/**
* Create a simple graph which has no bifurcations and do a kill branch at
* some intermediate node. Then continue the traversal.
*
* Expected: After kill:
* 1. Every node of with an id greater than the id of the killing
* node should not be visited.
* After continue:
* 2. No more nodes should be visited.
*
*/
public void test_2() {
int nnodes = 100;
Node[] ends = GraphTestUtil.buildNoBifurcations(builder(), nnodes);
final int kill = 50;
CountingWalker walker = new CountingWalker() {
private int m_mode = 0;
public int visit(Graphable element, GraphTraversal traversal) {
super.visit(element, traversal);
if (m_mode == 0) {
//check for stopping node
if (element.getID() == kill) {
m_mode++;
return(GraphTraversal.KILL_BRANCH);
}
}
else if (m_mode == 1) {
//should never get here
assertTrue(false);
}
return(GraphTraversal.CONTINUE);
}
};
DepthFirstIterator iterator = createIterator();
BasicGraphTraversal traversal = new BasicGraphTraversal(
builder().getGraph(), walker, iterator
);
traversal.init();
iterator.setSource(ends[0]);
traversal.traverse();
//kill node should be visited and nodes with greater id should not
GraphVisitor visitor = new GraphVisitor() {
public int visit(Graphable component) {
if (component.getID() <= kill) assertTrue(component.isVisited());
else assertTrue(!component.isVisited());
return(0);
}
};
builder().getGraph().visitNodes(visitor);
//ensure nodes only visited once
assertTrue(walker.getCount() == nnodes-kill+1);
//continue, no more nodes should be visited
traversal.traverse();
}
/**
* Create a balanced binary tree and do a normal traversal starting at root.
* <BR>
* <BR>
* Expected: 1. Every node should be visited.
* 2. For each node, every node in one of its two subtrees should
* have been visited before every node in the other subtree.
*/
public void test_3() {
int k = 4;
Object[] obj = GraphTestUtil.buildPerfectBinaryTree(builder(), k);
Node root = (Node)obj[0];
final Map obj2node = (Map)obj[1];
GraphVisitor initializer = new GraphVisitor() {
public int visit(Graphable component) {
component.setCount(-1);
return 0;
}
};
CountingWalker walker = new CountingWalker() {
public int visit(Graphable element, GraphTraversal traversal) {
element.setCount(getCount());
return super.visit(element, traversal);
}
};
DepthFirstIterator iterator = createIterator();
BasicGraphTraversal traversal = new BasicGraphTraversal(
builder().getGraph(), walker, iterator
);
traversal.init();
iterator.setSource(root);
traversal.traverse();
GraphVisitor visitor = new GraphVisitor() {
public int visit(Graphable component) {
//ensure component visited
assertTrue(component.isVisited());
Node ln = (Node)obj2node.get(component.getObject().toString() + ".0");
Node rn = (Node)obj2node.get(component.getObject().toString() + ".1");
if (ln != null) {
Node n1 = ln;
Node n2 = rn;
if (ln.getCount() > rn.getCount()) {
n1 = rn;
n2 = ln;
}
FIFOQueue queue = new FIFOQueue(256);
queue.enq(n1);
//ensure all subnodes of n1 visited before n2
while(!queue.isEmpty()) {
Node n = (Node)queue.deq();
ln = (Node)obj2node.get(n.getObject().toString() + ".0");
rn = (Node)obj2node.get(n.getObject().toString() + ".1");
if (ln == null) continue;
assertTrue(ln.getCount() < n2.getCount());
assertTrue(rn.getCount() < n2.getCount());
queue.enq(ln);
queue.enq(rn);
}
}
return 0;
}
};
builder().getGraph().visitNodes(visitor);
//ensure nodes only visited once
assertTrue(walker.getCount() == (int)Math.pow(2,k+1)-1);
}
/**
* Create a balanced binary tree and do a traversal starting at root and
* suspending at the first node seen that is not the root, (should be left
* child). Then continue the traversal.
* <BR>
* <BR>
* Expected: After suspend:
* 1. Only root and first non root should be visited.
*
* After continue:
* 1. First node visited should be child of suspending node.
* 2. Every node should be visited.
*/
public void test_4() {
int k = 4;
Object[] obj = GraphTestUtil.buildPerfectBinaryTree(builder(), k);
final Node root = (Node)obj[0];
final Map obj2node = (Map)obj[1];
final Node ln = (Node)obj2node.get(root.getObject().toString() + ".0");
final Node rn = (Node)obj2node.get(root.getObject().toString() + ".1");
CountingWalker walker = new CountingWalker() {
private int m_mode = 0;
public int visit(Graphable element, GraphTraversal traversal) {
super.visit(element, traversal);
if (m_mode == 0) {
if (element != root) {
//check which child of root was first visited
m_mode++;
return(GraphTraversal.SUSPEND);
}
}
else if (m_mode == 1) {
String eid = element.getObject().toString();
if (ln.isVisited()) {
assertTrue(
eid.equals(ln.getObject().toString()+".0")
|| eid.equals(ln.getObject().toString()+".1")
);
}
else {
assertTrue(
eid.equals(rn.getObject().toString()+".0")
|| eid.equals(rn.getObject().toString()+".1")
);
}
m_mode++;
}
return(GraphTraversal.CONTINUE);
}
};
DepthFirstIterator iterator = createIterator();
BasicGraphTraversal traversal = new BasicGraphTraversal(
builder().getGraph(), walker, iterator
);
traversal.init();
iterator.setSource(root);
traversal.traverse();
//ensure that only root and one of children is visited
assertTrue(root.isVisited());
assertTrue(
(rn.isVisited() && !ln.isVisited()) ||
(!rn.isVisited() && ln.isVisited())
);
assertTrue(walker.getCount() == 2);
GraphVisitor visitor = new GraphVisitor() {
public int visit(Graphable component) {
if (component != root && component != ln && component != rn) {
assertTrue(!component.isVisited());
}
return(0);
}
};
builder().getGraph().visitNodes(visitor);
traversal.traverse();
//ensure all nodes visited
visitor = new GraphVisitor() {
public int visit(Graphable component) {
assertTrue(component.isVisited());
return(0);
}
};
builder().getGraph().visitNodes(visitor);
assertTrue(walker.getCount() == (int)Math.pow(2,k+1)-1);
}
/**
* Create a balanced binary tree and do a traversal starting at root and kill
* branch at the first node seen that is not the root, (should be left child).
* Then continue the traversal.
* <BR>
* <BR>
* Expected: After kill:
* 1. All nodes should be visited except for sub nodes of first
* child of root visited. (the kill node)
*
* After continue:
* 1. Same as after kill.
*/
public void test_5() {
int k = 4;
Object[] obj = GraphTestUtil.buildPerfectBinaryTree(builder(), k);
final Node root = (Node)obj[0];
final Map obj2node = (Map)obj[1];
final Node ln = (Node)obj2node.get(root.getObject().toString() + ".0");
final Node rn = (Node)obj2node.get(root.getObject().toString() + ".1");
CountingWalker walker = new CountingWalker() {
private int m_mode = 0;
public int visit(Graphable element, GraphTraversal traversal) {
element.setCount(getCount());
super.visit(element, traversal); //set count
if (m_mode == 0) {
if (element != root) {
m_mode++;
return(GraphTraversal.KILL_BRANCH);
}
}
else if (m_mode == 1) {
assertTrue(
(ln.isVisited() && element == rn) ||
(rn.isVisited() && element == ln)
);
m_mode++;
}
return(GraphTraversal.CONTINUE);
}
};
DepthFirstIterator iterator = createIterator();
BasicGraphTraversal traversal = new BasicGraphTraversal(
builder().getGraph(), walker, iterator
);
traversal.init();
iterator.setSource(root);
traversal.traverse();
//ensure that subnodes of first visited after root are not visited
final String id = (ln.getCount() < rn.getCount()) ?
ln.getObject().toString() :
rn.getObject().toString();
GraphVisitor visitor = new GraphVisitor() {
public int visit(Graphable component) {
String eid = component.getObject().toString();
if (eid.length() <= id.length()) assertTrue(component.isVisited());
else if (eid.startsWith(id)) assertTrue(!component.isVisited());
else assertTrue(component.isVisited());
return(0);
}
};
builder().getGraph().visitNodes(visitor);
assertTrue(walker.getCount() == (int)Math.pow(2,k)+1);
traversal.traverse();
builder().getGraph().visitNodes(visitor);
assertTrue(walker.getCount() == (int)Math.pow(2,k)+1);
}
/**
* Create a graph that contains a cycle and do a full traversal.<BR>
* <BR>
* Expected: 1. All nodes visited.
*/
public void test_6() {
int nnodes = 100;
GraphTestUtil.buildCircular(builder(), nnodes);
GraphVisitor visitor = new GraphVisitor() {
public int visit(Graphable component) {
if (component.getID() == 50) return(Graph.PASS_AND_CONTINUE);
return(Graph.FAIL_QUERY);
}
};
Node source = (Node)builder().getGraph().queryNodes(visitor).get(0);
CountingWalker walker = new CountingWalker();
DepthFirstIterator iterator = createIterator();
BasicGraphTraversal traversal = new BasicGraphTraversal(
builder().getGraph(), walker, iterator
);
traversal.init();
iterator.setSource(source);
traversal.traverse();
//ensure all nodes visisited
visitor = new GraphVisitor() {
public int visit(Graphable component) {
assertTrue(component.isVisited());
return(0);
}
};
builder().getGraph().visitNodes(visitor);
assertTrue(walker.getCount() == nnodes);
}
protected DepthFirstIterator createIterator() {
return(new DepthFirstIterator());
}
protected GraphBuilder createBuilder() {
return(new BasicGraphBuilder());
}
protected GraphBuilder builder() {
return(m_builder);
}
}