The purpose of this example is to show you how easy it is to evolve Java programs that are written with DJ. We implement an algorithm on two very different class structures. The complete source code for the example is in the TBR directory in subdirectories TBR1 and TBR2.

This program implements the terminal buffer rule described in the AP book on page 393 and in an AP course assignment. The terminal buffer rule says that for all classes C a terminal class (like String, Integer, etc) should be used only as the only part class of C.

We implement this buffer rule on two very different class structures that model simple forms of UML class diagrams. The first class structure is:

public class Cd_graph
{
	public Adj first;
	public Cd_graph(Adj first)
	{
		this.first = first;	
	}
}

public class Adj
{
	public Vertex vertex;
	public Construct ns;

	public Adj(Vertex vertex, Construct ns) 
	{	
		this.vertex = vertex;
		this.ns = ns;
    }
}

public class Construct
{	
	public Labeled_vertex l1, l2;
	public Construct(Labeled_vertex l1, Labeled_vertex l2)
	{    
		this.l1 = l1;
		this.l2 = l2;
    }
}

public class Labeled_vertex
{
	public Ident label_name;
	public Vertex class_name;	
	public Labeled_vertex(Ident label_name,Vertex class_name)
	{
		this.label_name = label_name;
		this.class_name = class_name;
    }
}

class Vertex
{
	public Ident name;

	public Vertex(Ident name)
	{
		this.name = name;
	}
}

Class Ident is used from some library. This is a very simple class structure to start out with. We can only define one class with two parts.

To solve the TBR checking problem we write two visitors DefinedClassVisitor.java and TBRVisitor.java.

public class DefinedClassVisitor extends Visitor
{	
 	private Vector vNonTerminals = new Vector();
	public void before(Adj o)
	{	
		Ident idCurrentAdj;
		idCurrentAdj = o.vertex.name;			
		System.out.println("Adj: " + idCurrentAdj.toString());
		vNonTerminals.addElement(idCurrentAdj);						
	}
	public Vector GetDefinedClasses() {return vNonTerminals;}
}


public class TBRVisitor extends Visitor
{	
	private Vector vNonTerminals;
	private Vector vConstructParts = new Vector();
	private Ident idCurrentAdj;

	TBRVisitor(Vector vDefinedClasses)
	{ vNonTerminals = vDefinedClasses; }

	public void before(Adj o)
	{ idCurrentAdj = o.vertex.name;}
	// to transport class name

	public void before(Labeled_vertex o)
	{ vConstructParts.addElement(o.class_name.name); }
	
	public void after(Construct o) {
        int pi = vConstructParts.size();
        int pti = 0;
        // Check each part to see if it is a terminal
        for (int i = 0; i < pi; i++) {
          if (IsTerminal((Ident)vConstructParts.elementAt(i)) ) {
            pti++;
	    if (pi > 1) {
              System.out.print( "\tClass violates the" +
              " Terminal Buffer Rule: " );
              System.out.println(vConstructParts.elementAt(i).toString() );} } }
	System.out.println("\n\t" + idCurrentAdj.toString() + 
	  " has " + pi + " part(s) " );
	System.out.println("\twith " + pti + " terminal part(s). ");
        vConstructParts.removeAllElements(); // Empty the vector.
	}

	public boolean IsTerminal(Ident o)
	{ return !vNonTerminals.contains(o); }
}
The two visitors are used together with two traversals to solve the problem (in file Main.java):
// The purpose of traversal tg1 is to
// collect all the class names that are defined
// in the cd
TraversalGraph tg1 = TraversalGraph.compute(cg,
new Strategy("from Cd_graph to Adj"));

// The purpose of traversal tg2 is to
// visit all parts of all classes
TraversalGraph tg2 = TraversalGraph.compute(cg,
new Strategy("from Cd_graph via Construct to Vertex"));

DefinedClassVisitor v1 = new DefinedClassVisitor();

// find defined classes
tg1.traverse( cd_graph, v1);

// check for violations
TBRVisitor v2 = new TBRVisitor(v1.GetDefinedClasses());
tg2.traverse(cd_graph, v2);
The interesting property of this example is that we can now drastically change the complexity of the classes allowed and we almost triple (from 5 to 14) the number of classes. The following table gives the classes (new and old) and their membership in the traversals.
Adj.java                old tg2 tg1
Adj_list.java           new tg2 tg1
Alternat.java           new tg2
Any_vertex.java         new tg2
Any_vertex_list.java    new tg2
Cd_graph.java           old tg2 tg1
Construct.java          old tg2
Empty.java              new
Empty_cd_graph.java     new
Labeled_vertex.java     old tg2
Nany_vertex_list.java   new tg2
Neighbors.java          new tg2
Syntax_vertex.java      new
Vertex.java             old tg2
Yet the two traversal strategies and the two visitors DefinedClassVisitor.java and TBRVisitor.java are identical although we did a significant structural evolution. Also the part of Main.java shown above is identical for both class structures.

Had we written the program without DJ, tedious effort would be required to update the traversal code and the visitor code to achieve the desired result.

This example was rewritten in DJ from the Demeter/Java solution in the Demeter/Java example directory subdirectories j-check-TBR1 and j-check-TBR2 by Younian Zhang.

Professor Karl Lieberherr