-
Notifications
You must be signed in to change notification settings - Fork 0
APT Visitor
The visitor pattern is a design pattern for software engineering. It was defined in 1994 in the book: "Design Patterns: Elements of Reusable Object-Oriented Software". The visitor pattern is designed to traverse recurring structures. In our case, it is used to traverse the APT. Our implementation traverses the APT similar to depth first search, with a priority on the on nodes that are executed before. A visitor can be implemented by creating a new class which implements the interface APTVisitor.
The visitor pattern consists of 5 defining functions.
The accept function is defined in the data structure to be traversed. It defines the entry point for a visitor i.e. the starting node for the traversion. The accept function takes a visitor as an argument. The following example shows how the instantiation node of an APT node calls the accept method on the instantiation someVisitor of a visitor.
node.accept(someVisitor);
The accept function is defined in the data structure to be traversed. It defines the entry point for a visitor i.e. the starting node for the traversion. The accept function takes a visitor as an argument. The following example shows how the instantiation node of an APT node calls the accept method on the instantiation someVisitor of a visitor.
node.accept(someVisitor);
The handle function defines how the visitor operates and should not be changed.
The the visit function is always call when the visitor first encounters a node. To define the behaviour of a visitor, this function can be overwritten in a specific visitor implementation. This function does nothing, if not explicitly changed. The following example shows how the function can be overwritten. In this example the visit function prints the name of the functions that are called.
@Override
public void visit(CallNode node) {
print(node.getFunctionIdentifier());
}
The traverse function recursivly defines the traversal strategy. It is called in succession to the visit function. The traverse function can be overwritten for each node individually to redefine the traversal strategie. The example shows the predefined traversal strategie.
public void traverse(CallNode node){
for (PatternNode child: node.getChildren()) {
child.accept(getRealThis());
}
}
The endVisit function is similar to the visit function with the difference, that it is called after traversing the child nodes.
The following example shows how we could print the nesting of function calls. Where for each call we open a section denoted by "[ ]" where all descendent calls are enclosed in these brackets. The real this parameter and functions are used clarify that always the same instantiation is used.
public class CallNesting implements APTVisitor{
String nesting = "";
@Override
public void visit(Call node) {
nesting = nesting + node.getgetFunctionIdentifier() + "\n[\n";
}
@Override
public void endVisit(Call node) {
nesting = nesting + "]\n";
}
/**
* Visitor support functions.
*/
private PatternDSLVisitor realThis = this;
@Override
public PatternDSLVisitor getRealThis() {
return realThis;
}
@Override
public void setRealThis(PatternDSLVisitor realThis) {
this.realThis = realThis;
}
}