All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.tools.framework.util.GenericVisitor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Copyright 2012 Google Inc. All Rights Reserved.

package com.google.api.tools.framework.util;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.reflect.FastMethod;

/**
 * A generic visitor class. Implements the visitor style pattern for depth-first traversal
 * of object graphs using dynamic dispatching based on method annotations.
 *
 * 

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 * {@link Accepts}. A concrete visitor is then defined by deriving another class which defines * visitors annotated with {@link Visits} and/or {@link VisitsBefore} and {@link VisitsAfter}. * *

The generic {@link #visit} method first calls @VisitsBefore if present, then * calls @Visit if present, and finally @VisitsAfter if present. If no method annotated * with @Visits is defined, {@link #defaultVisit} will be called instead. This method in turn * calls {@link #accept} 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 {@link #visit} 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) }
 *     {@literal @}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;
 *     {@literal @}VisitsAfter void count(Value val) {
 *       counter++;
 *     }
 *   }
* *

As another example, consider an evaluator: *

 *   class Evaluator extends ExprVisitor {
 *     Stack<Integer> stack;
 *     {@literal @}VisitsAfter void eval(Add add) {
 *       stack.push(stack.pop() + stack.pop());
 *     }
 *     {@literal @}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;
 *     {@literal @}Visits void eval(Add add) {
 *       int x = someFunction(); // compute some stuff
 *       accept(add);
 *       stack.push(x + stack.pop() + stack.pop());
 *     }
 *     {@literal @}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;
 *     {@literal @}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
 *     }
 *     {@literal @}VisitsAfter void count(Value val) {
 *       counter++;
 *     }
 *     class DecreasingLeafCounter extends ExprVisitor {
 *       {@literal @}VisitsAfter void count(Value val) {
 *         counter--;
 *       }
 *     }
 *   }
* *

A few additional remarks: *

    *
  • Methods are dispatched based on the type of their argument. If a precise match * is not found, the superclass is tried, and so on, until the base type of the visitor * is reached. This matches the standard behavior for method dispatching in Java. *
  • *
  • Methods from base types are considered for resolution. *
  • *
  • Methods must not be private or protected and not static to be able to be called via cglib's * fast reflection support. *
  • *
  • Use {@code Object} as your base type if the object model doesn't have a more * specific root type. *
  • *
  • You can customize default visitors for elements and children by overriding the * methods {@link #defaultVisit(Object)} and {@link #defaultAccept(Object)}. See * the javadoc of the available methods for more details. *
  • *
* */ public abstract class GenericVisitor { private final Class baseType; private final Dispatcher visits; private final Dispatcher accepts; private final Dispatcher before; private final Dispatcher after; /** * Constructs a generic visitor with {@code baseType} being the root type of the * object model. Pass {@code Object.class} if the object model doesn't has such a type. */ protected GenericVisitor(Class baseType) { this.baseType = Preconditions.checkNotNull(baseType); this.visits = Dispatcher.getDispatcher(baseType, Visits.class, this.getClass()); this.accepts = Dispatcher.getDispatcher(baseType, Accepts.class, this.getClass()); this.before = Dispatcher.getDispatcher(baseType, VisitsBefore.class, this.getClass()); this.after = Dispatcher.getDispatcher(baseType, VisitsAfter.class, this.getClass()); } /** * The default method which is invoked if no explicit @Visits method is found for * a type. Forwards to {@link #accept(Object)}. Override this to change this * behavior. */ protected void defaultVisit(BaseType instance) { accept(instance); } /** * The default method which is invoked if no explicit @Accepts method is found for * a type. Does nothing. Override this to change this behavior. */ protected void defaultAccept(BaseType instance) { } /** * Visits a given instance. Dispatches to a @Visits method if one * is found, otherwise calls {@link #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. * */ public final void visit(BaseType instance) { if (!dispatchBefore(before, instance)) { // stop visitation at this point return; } if (!dispatch(visits, instance)) { defaultVisit(instance); } dispatch(after, instance); } /** * Visits the children of a given instance. Dispatches to a @Accepts method if one * is found, otherwise calls {@link #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. */ protected final void accept(BaseType instance) { if (!dispatch(accepts, instance)) { defaultAccept(instance); } } private boolean dispatch(Dispatcher dispatcher, BaseType instance) { FastMethod method = getMethod(dispatcher, instance); if (method == null) { return false; } try { method.invoke(this, new Object[]{ instance }); return true; } catch (InvocationTargetException e) { throw Throwables.propagate(e.getCause()); } } // Dispatching the @VisitsBefore is slightly different then the reset, // as we need to determine whether to continue private boolean dispatchBefore(Dispatcher dispatcher, BaseType instance) { FastMethod method = getMethod(dispatcher, instance); if (method == null) { return true; // non-presence of before method means continue execution } try { Object result = method.invoke(this, new Object[]{ instance }); if (method.getReturnType().equals(Boolean.TYPE)) { return Boolean.class.cast(result); // method determines whether to continue } return true; } catch (InvocationTargetException e) { throw Throwables.propagate(e.getCause()); } } @SuppressWarnings("unchecked") // ensure by construction private FastMethod getMethod(Dispatcher dispatcher, BaseType instance) { Preconditions.checkNotNull(instance, "instance"); return dispatcher.getMethod((Class) instance.getClass()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy