Besides the functionalities DJ provides, the alpha version of DJ also adds some new features. Those extensions include: ‘around’ methods on class nodes, ‘before’ and ‘after’ methods on edges,
‘around’ method on edges and ContextVisitor
class to keep track of the objects being traversed.
If you want to use alpha version of DJ, just download the alpha.jar file and put it in your class path (do not unpack the jar file). Please be reminded that this file is not included in DemeterJ, so you need to download that file and setup the class path even you have installed DemeterJ.
One little difference between using DJ and alpha version of DJ is that you should write
import edu.neu.ccs.demeter.dj.alpha.*;
instead of
import edu.neu.ccs.demeter.dj.*;
in your *.cd or *.java files.
Around methods on class nodes provides the users the capabilities to control the continuation of the traversal of the branches beginning from the specified class nodes. Let's illustrate it by a example.
If you have the following class dictionary:
A = B C.
B = D E .
C = F | G.
D = .
E = .
F = "f".
G = "g".
And the traversal strategy is "from A to *". But you want the traversal go further along the 'B' branch only if some conditions met, you can control the traversal by adding a method in your visitor classes like the following:
class MyVisitor extends Visitor
{
// ...
void around(B host, SubTraverser st)
{
// ...
if (host.getCondition( ))
st.apply( ); // condition met, so let the traversal go on
}
// ...
}
The syntax of 'around' methods on nodes is that the first argument is an object of the specified class on which you want to control the traversal, and the second argument is an object of SubTraverser that is added to DJ to control the continuation of traversal. Once you have defined a 'around' method on a class in your visitor classes, the traversal will not go further along that class unless apply( ) method of the SubTraverser object has been called. In the above example, if there was no st.apply( ) in the codes, the traversal along 'B' would have not continued, but the 'before' or 'after' methods (if exists) on 'B' will be called, then the traversal will turn to the class 'C'.
before and after methods on edges
The ‘before’ and ‘after’ methods on edges are very similar to the ‘before’ and ‘after’ methods on nodes except that by methods on edges we can know where is the source of the step of traversal and where is the target of it, so we can do more processing in these methods. Because ‘before’ and ‘after’ methods are symmetry each other, so here we only talk about the ‘before’ methods.
For ‘before’ methods on edges, we now support the construction and repetition edges. For the clarity, we want the users use different name patterns for their construction or repetition edge wrappers in their visitor classes. So, here are our conventions that all the construction edge 'before' wrappers should start with cbefore (cafter for 'after' wrappers); while all the repetition edge 'before' wrappers should start with rbefore (rafter for 'after' wrappers). For edge wrappers, we want to care about three elements, which are source object of the edge, target object of the edge and the label of the edge. Because DJ supports edge patterns, sometimes we can not point out the source or target classes of the edges, or the labels of edges. We support the following eight interface patterns for your 'before' or 'after' wrappers. You can choose them according to your needs. Since cbefore and rbefore are symmetry each other, so here we only list the cbefore interfaces.
cbefore_x(Source s, Target t)
// x is the label of the edge from 'Source' class to 'Target'
//for the pattern of 'Source,x,Target’
cbefore_x(Source s, Object t) //for the pattern of ‘Source,x,*’
cbefore_x(Object s, Target t) //for the pattern of ‘*,x,Target’
cbefore(Source s, String label, Target t) //for the pattern of ‘Source,*,Target’
cbefore(Object s, String label, Object t) //for the pattern of ‘*,*,*’
cbefore(Object s, String label, Target t) //for the pattern of ‘*,*,Target’
cbefore(Source s, String label, Object t) //for the pattern of ‘Source,*,*’
cbefore_x(Object s, Object t) //for the pattern of ‘*,x,*’
In the above interfaces, all the ‘x’ should be replaced by the label of the specified edge, all the ‘Source’ should be replaced by the source class of the specified edge and all the ‘Target’ should be replaced by the target class of the specified edge.
Basically, the first one interface is the basic one which users want to use when they know the exact source class, target class and label of the edge on which they want want to have edge wrapper. The second one is used for the pattern of ‘Source, x, *’, that is to say, all the edges starting from class ‘Source’ labeled ‘x’. The second argument of that interface is the type of ‘Object’ which will be passed to the method according to the actual target object of an edge during traversal. The third one is used for the pattern of ‘*,x,Target’, that is to say, all the edges reaching class ‘Target’ labeled ‘x’. The first argument refers to the source object of the edge. The forth one is used for the pattern of ‘Source,*,Target’, that is to say, all the edges starting from class ‘Source’ to class ‘Target’. The label of the edges will be passed to the method via ‘label’ at runtime. The fifth interface is used for the pattern of ‘*,*,*’, that is to say, any edges. The sixth interface is used for the pattern of ‘*,*,Target’ which is the same as the third one except the label of the edges can be anything. The senventh interface is used for the pattern of ‘Source,*,*’ which is the same as the second one except the label of the edges can be anything. The eighth interface is used for the pattern of ‘*,x,*’, that is, any edges labeled ‘x’.
One thing to remind, when there is ‘Object’ class in class graph, there maybe some ambiguities between some of the above interfaces. Users can tell which interfaces they are in by detecting the actual types of arguments in the methods codes using Java Reflection.
around methods on edges
The objective of around methods on edges are the same as the around method on nodes. And the interfaces of around methods on edges are very similiar to the corresponding ‘before’ and ‘after’ methods on edges except that there are one more operand of type SubTraverser at the end of each interface of ‘around’ methods. Around methods on edges support construction and repetition edges. Here is the list of supported interfaces for construction edges.
caround_x(Source s, Target t, SubTraverser st)
// x is the label of the edge from 'Source' class to 'Target'
//for the pattern of 'Source,x,Target’
caround_x(Source s, Object t, SubTraverser st) //for the pattern of ‘Source,x,*’
caround_x(Object s, Target t, SubTraverser st) //for the pattern of ‘*,x,Target’
caround(Source s, String label, Target t, SubTraverser st)//for the pattern of ‘Source,*,Target’
caround(Object s, String label, Object t, SubTraverser st) //for the pattern of ‘*,*,*’
caround(Object s, String label, Target t, SubTraverser st, SubTraverser st) //for the pattern of ‘*,*,Target’
caround(Source s, String label, Object t, SubTraverser st) //for the pattern of ‘Source,*,*’
caround_x(Object s, Object t, SubTraverser st) //for the pattern of ‘*,x,*’
If you extend your visitor classes from ContextVisitor that is a
subclass of Visitor instead of directly from Visitor
class, then you will have access to all the objects being traversed (means that
the traversal has not left those objects, or you can think of them as those
whose 'before' behaviors have been invocated but 'after' behaviors have not been
invocated.
The most useful methods available in ContextVisitor are
public Object getLastObj(String className)
public int getNumObj(String className)
getLastObj returns the most recently traversed object whose
class is className; while getNumObj returns the number
of objects whose class are className and that are being traversed.
An example

ClassGraph cg = new ClassGraph();
TraversalGraph tg = new TraversalGraph("from Company to
Customer",cg);
tg.traverse(c,new ContextVisitor(){
void before(Customer c){
String cCity = c.getCity();
String aCity =
((Agent)getLastObj("Agent")).getCity();
if(cCity.equals(aCity))
System.out.println(c);
}