public abstract class GenericVisitor<BaseType>
extends java.lang.Object
An implementation will typically derive from this class an abstract class for a given object
model which defines how to visit children by providing methods annotated with
Accepts. A concrete visitor is then defined by deriving another class which defines
visitors annotated with Visits and/or VisitsBefore and VisitsAfter.
The generic visit(BaseType) method first calls @VisitsBefore if present, then
calls @Visit if present, and finally @VisitsAfter if present. If no method annotated
with @Visits is defined, defaultVisit(BaseType) will be called instead. This method in turn
calls accept(BaseType) to traverse down the tree.
As a methodological guidance, you should use @VisitsBefore if you do a proper pre-order traversal, @VisitsAfter if you do a proper post-order traversal, and @Visits only if you do a mix (i.e. need to do some mutual dependent computation before and after descending).
The method annotated with @VisitsBefore can return a boolean value, which if false will prevent calls to any @Visits and @VisitsAfter methods for the current element (which will prevent the automatic traversal of child elements).
More formally, the abstract pseudo code for the visit(BaseType) method is:
void visit(x) {
if (hasVisitsBefore(x) && !visitsBefore(x)) return;
if (hasVisits(x))
visits(x);
else
defaultVisit(x);
if (hasVisitsAfter(x)) visitsAfter(x);
}
void defaultVisit(x) {
accept(x);
}
Example: Suppose a simple object model for expressions:
abstract Expr { ... }
class Add extends Expr {
Expr left;
Expr right;
}
class Value extends Expr {
int value;
}
We can now define a general expression visitor as follows (this base class can be used for various concrete visitors on expressions):
abstract class ExprVisitor extends GenericVisitor<Expr> {
ExprVisitor() { super(Expr.class) }
@Accepts void accept(Add add) {
visit(add.left); visit(add.right);
}
}
Next we define a concrete visitor as below:
class LeafCounter extends ExprVisitor {
int counter = 0;
@VisitsAfter void count(Value val) {
counter++;
}
}
As another example, consider an evaluator:
class Evaluator extends ExprVisitor {
Stack<Integer> stack;
@VisitsAfter void eval(Add add) {
stack.push(stack.pop() + stack.pop());
}
@VisitsAfter void eval(Value val) {
stack.push(val.value));
}
}
The following funny evaluator uses the @Visits annotation instead of @VisitsAfter as it does some computation before descending:
class FunnyEvaluator extends ExprVisitor {
Stack<Integer> stack;
@Visits void eval(Add add) {
int x = someFunction(); // compute some stuff
accept(add);
stack.push(x + stack.pop() + stack.pop());
}
@VisitsAfter void eval(Value val) {
stack.push(val.value));
}
}
Here is a visitor which uses @VisitsBefore to replace the current visitor with another visitor if some condition is met:
class LeafCounter extends ExprVisitor {
int counter = 0;
@VisitsBefore boolean count(Expr expr) {
if (someCondition(expr)) {
// use a different visitor
new DecreasingLeafCounter().visit(expr);
return false; // stop with this visitor
}
return true; // continue with this visitor
}
@VisitsAfter void count(Value val) {
counter++;
}
class DecreasingLeafCounter extends ExprVisitor {
@VisitsAfter void count(Value val) {
counter--;
}
}
}
A few additional remarks:
Object as your base type if the object model doesn't have a more
specific root type.
defaultVisit(Object) and defaultAccept(Object). See
the javadoc of the available methods for more details.
| Modifier | Constructor and Description |
|---|---|
protected |
GenericVisitor(java.lang.Class<BaseType> baseType)
Constructs a generic visitor with
baseType being the root type of the
object model. |
| Modifier and Type | Method and Description |
|---|---|
protected void |
accept(BaseType instance)
Visits the children of a given instance.
|
protected void |
defaultAccept(BaseType instance)
The default method which is invoked if no explicit @Accepts method is found for
a type.
|
protected void |
defaultVisit(BaseType instance)
The default method which is invoked if no explicit @Visits method is found for
a type.
|
void |
visit(BaseType instance)
Visits a given instance.
|
protected GenericVisitor(java.lang.Class<BaseType> baseType)
baseType being the root type of the
object model. Pass Object.class if the object model doesn't has such a type.protected void defaultVisit(BaseType instance)
accept(Object). Override this to change this
behavior.protected void defaultAccept(BaseType instance)
public final void visit(BaseType instance)
defaultVisit(Object).
If a @BeforeVisits method with matching type is defined, it will be executed before @Visits. Such a method can return a boolean which, if false, lets visitation stop at this point.
If a @AfterVisits method with matching type is defined, it will be executed after @Visits.
protected final void accept(BaseType instance)
defaultAccept(Object). Call this in your @Visits
method to traverse children. Depending on where the call is placed, pre-order or
post-order traversal can be realized.
@Accepts methods are usually provided in a base class visitor for the particular data structure to be visited, from which a concrete visitor derives.