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 tg2Yet 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