DJ: Dynamic Structure-Shy Traversals and Visitors in Pure Java


Demeter Research Group, Northeastern University, College of Computer Science, Boston

Please read first the DJ Fact Sheet.

DJ API | DJ Information for Software Contributors.

DJ: Dynamic Adaptive Programming in Java. This is a good introductory paper for potential DJ users.
Aspect-Oriented Programming with Adaptive Methods. This is a good introductory paper for potential DJ users who are interested in the emerging field of Aspect-Oriented Programming.
Traversal Semantics in Object Graphs. This is a good paper to learn about the precise definition and semantics of traversal strategies.

DJ already enjoys an active user community. We have substantial teaching material available. DJ is in pre 1.0 phase and is stable enough to be used in undergraduate classes. We don't expect the DJ interface to change in a non-upward-compatible way.
Home Directory of an undergraduate class that uses DJ (Winter 2001) .
Home Directory of a graduate class that uses DJ (Fall 2000) .
Home Directory of an undergraduate class that uses DJ (Fall 2000) .

Note: Demeter/Java is now called DemeterJ.

The Visitor pattern from GOF has significantly gained in popularity in recent years. The Visitor pattern allows you to add code to your classes without changing the classes. We have used various instantiations of the Visitor pattern since 1985. Based on this experience, we offer DJ as a tool that makes programming with visitors significantly easier. DJ allows you to both name traversals and visitors and to reuse them. Traversal reuse is useful: for example, if you buy a HP printer you buy printer installation software that uses traversal-visitor programming where each traversal is reused at least four times (check the Hewlett-Packard success story (http://www.ccs.neu.edu/research/demeter/evaluation/conventional-env/hp.html) for further information). One of the distinguishing features of DJ is that it makes traversal programming very easy. DJ uses the traversal strategy language for specifying navigation through objects. This language has evolved since 1991 from a small sublanguage of Demeter/C++ to a small sublanguage of DemeterJ. DJ uses the same traversal strategy language as DemeterJ.

Note: A traversal strategy has nothing to do with the GOF strategy pattern. A traversal strategy is about strategically defining a traversal through objects. Sometimes we use "strategy" instead of "traversal strategy". You can think of a strategy as a specification of an iterator through a structure more complex that a collection.

DJ effectively lets you write each behavior against a generic data model and lets you map the behavior into a concrete data model. This is a well-known pattern used in modern component-based sofware development and leads to both simpler and more reusable designs and programs.

DJ allows you to define Java Collections using traversal strategies. What is important is that the iterators operate directly on the original objects and no extra collection objects are created.

A word of caution regarding the applicability of DJ: DJ heavily uses reflection in its current implementation. This makes the implementation slow but simple. DJ is an ideal rapid prototyping tool for the Java programmer. A Java program using DJ can be systematically rewritten into a more efficient Java program by writing traversals manually.

DJ is the little sister of DemeterJ. While DemeterJ requires you to use a non-standard environment with class dictionary files and behavior files, DJ fits seamlessly into Java development environments. All you need to do is to import the edu.neu.ccs.demeter.dj package into your Java programs and you will be able to program in a traversal-visitor style. The DJ package provides essentially the following method:

// class ClassGraph
Object traverse(Object o, Strategy s, Visitor v);
where s is a traversal strategy that says where to navigate and v is a visitor that says what to do before and after certain objects are visited. A traversal strategy is an abstraction of a UML class graph and is therefore itself a graph that can be represented as a UML class graph. The intent of the higher level UML class graph is to define where to navigate in the lower level UML class graph. An edge in the strategy graph must correspond to one or more paths in the lower level class graph. Two often useful special cases of this interface are:
// class ClassGraph
Object fetch(Object o, Strategy s);
to retrieve a unique object deep down in an object graph following strategy s.
// class ClassGraph
List gather(Object o, Strategy s);
to retrieve a collection of objects deep down in an object graph following strategy s. The implementation of DJ builds on the AP Library that contains the core compilation algorithms for Adaptive Programming. They are based on automata theory and were developed by Jens Palsberg, Boaz Patt-Shamir, and Karl Lieberherr.

To give users feedback about the traversals the strategies define, DJ allows users to print the traversal graphs.

DJ uses a reflective approach to implementing strategies and visitors. Visitor classes must be subclasses of class Visitor which contains the following methods:

void before(A host) {}; // code executed before entering A-objects
void after(A host) {}; // code executed after leaving A-objects

void before_p(P source, Q dest) {}; // unimplemented as of
void after_p(P source, Q dest) {};  // version 0.8.1

void start() {};
void finish() {};

Object getReturnValue() {};
DJ provides the classes A simple class using DJ is the following class Main.java:
import edu.neu.ccs.demeter.dj.*;

class Main {
  public static void main(String[] args) {
    ClassGraph cg = new ClassGraph(); // constructed by reflection from
                                      // the classes in the default package
    // construct some object
    A a = new A(new B(new D()), new C());

    Strategy sg = new Strategy("from A to D");
    TraversalGraph tg = new TraversalGraph(sg, cg);

    // notice that Java allows you to inline the visitor
    // if you don't need the visitor object after the traversal
    tg.traverse(a, new Visitor() {
	public void start() { System.out.println("begin");}
	public void finish() { System.out.println("end"); }

        public void before(A o) { System.out.println("before A"); }
        public void after(A o)  { System.out.println("after A"); }

        public void before(D o) { System.out.println("before D"); }
        public void after(D o)  { System.out.println("after D"); }
    }); 

    // Print out the traversal graph so that you 
    // can check the detailed traversal
    System.out.println("Traversal Graph for from A to D");
    System.out.println(tg);        
  }
}
Here is a simple example of a program that counts the number of inheritance edges in a UML class diagram:
// from class Main:
ClassGraph cg=new ClassGraph();
TraversalGraph tg
  = new TraversalGraph("from UMLDiagram via Abstract to Vertex", cg);
CountInhRelsVisitor v = new CountInhRelsVisitor();
tg.traverse(cd_graph, v);

// class CountInhRelsVisitor
import edu.neu.ccs.demeter.dj.Visitor;
public class CountInhRelsVisitor extends Visitor
{	
	public int iCount;
	public void start() { System.out.println("begin"); iCount = 0;}
	public void finish() {	
		 System.out.println("The total count = " + iCount);
		 System.out.println("end"); }
	public void before(Vertex o) { iCount++; }
}
An alternative would be to return the result from the visitor using getReturnValue(). Note that traverse returns an object of class Object.
  • Teaching Material
  • Documentation:
  • Foundations
  • DJ Developer's Corner Copyright

    Serbo-Croatian language by Jovana Milutinovich from Webhostinggeeks.com