package polyglot.visit;
import polyglot.ast.*;
import polyglot.types.*;
import polyglot.util.*;
import java.util.*;
/**
* The <code>NodeScrambler</code> is test case generator of sorts. Since it
* is ofter useful to introduce ``random'' errors into source code, this
* class provides a way of doing so in a semi-structed manner. The process
* takes place in two phases. First, a "FirstPass" is made to collect
* a list of nodes and their parents. Then a second pass is made to randomly
* replace a branch of the tree with another suitable branch.
*/
public class NodeScrambler extends NodeVisitor
{
public FirstPass fp;
protected HashMap pairs;
protected LinkedList nodes;
protected LinkedList currentParents;
protected long seed;
protected Random ran;
protected boolean scrambled = false;
protected CodeWriter cw;
public NodeScrambler()
{
this.fp = new FirstPass();
this.pairs = new HashMap();
this.nodes = new LinkedList();
this.currentParents = new LinkedList();
this.cw = new CodeWriter( System.err, 72);
Random ran = new Random();
seed = ran.nextLong();
System.err.println( "Using seed: " + seed);
this.ran = new Random( seed);
}
/**
* Create a new <code>NodeScrambler</code> with the given random number
* generator seed.
*/
public NodeScrambler( long seed)
{
this.fp = new FirstPass();
this.pairs = new HashMap();
this.nodes = new LinkedList();
this.currentParents = new LinkedList();
this.cw = new CodeWriter( System.err, 72);
this.seed = seed;
this.ran = new Random( seed);
}
/**
* Scans throught the AST, create a list of all nodes present, along with
* the set of parents for each node in the tree. <b>This visitor should be
* run before the main <code>NodeScrambler</code> visits the tree.</b>
*/
public class FirstPass extends NodeVisitor
{
public NodeVisitor enter( Node n)
{
pairs.put( n, currentParents.clone());
nodes.add( n);
currentParents.add( n);
return this;
}
public Node leave( Node old, Node n, NodeVisitor v)
{
currentParents.remove( n);
return n;
}
}
public long getSeed()
{
return seed;
}
public Node override( Node n)
{
if( coinFlip()) {
Node m = potentialScramble( n);
if( m == null) {
/* No potential replacement. */
return null;
}
else {
scrambled = true;
try {
System.err.println( "Replacing:");
n.dump( cw);
cw.newline();
cw.flush();
System.err.println( "With:");
m.dump( cw);
cw.newline();
cw.flush();
}
catch( Exception e)
{
e.printStackTrace();
return null;
}
return m;
}
}
else {
return null;
}
}
protected boolean coinFlip()
{
if( scrambled) {
return false;
}
else {
if( ran.nextDouble() > 0.9) {
return true;
}
else {
return false;
}
}
}
protected Node potentialScramble( Node n)
{
Class required = Node.class;
if( n instanceof SourceFile) {
return null;
}
if( n instanceof Import) {
required = Import.class;
}
else if( n instanceof TypeNode) {
required = TypeNode.class;
}
else if( n instanceof ClassDecl) {
required = ClassDecl.class;
}
else if( n instanceof ClassMember) {
required = ClassMember.class;
}
else if( n instanceof Formal) {
required = Formal.class;
}
else if( n instanceof Expr) {
required = Expr.class;
}
else if( n instanceof Block) {
required = Block.class;
}
else if( n instanceof Catch) {
required = Catch.class;
}
else if( n instanceof LocalDecl) {
required = LocalDecl.class;
}
else if( n instanceof Stmt) {
required = Stmt.class;
}
LinkedList parents = (LinkedList)pairs.get( n);
Iterator iter1 = nodes.iterator(), iter2;
boolean isParent;
while( iter1.hasNext()) {
Node m = (Node)iter1.next();
if( required.isAssignableFrom( m.getClass())) {
isParent = false;
iter2 = parents.iterator();
while( iter2.hasNext()) {
if( m == iter2.next()) {
isParent = true;
}
}
if( !isParent && m != n) {
return m;
}
}
}
return null;
}
}