Package org.stjs.generator.javac

Source Code of org.stjs.generator.javac.TreeUtils

package org.stjs.generator.javac;

/*>>>
import checkers.nullness.quals.*;
*/

import java.util.EnumSet;
import java.util.Set;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;

import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.PrimitiveTypeTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.tree.JCTree;

//import com.sun.source.tree.AnnotatedTypeTree;

/**
* A utility class made for helping to analyze a given {@code Tree}.
*/
// TODO: This class needs significant restructuring
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
    justification = "copied code", value = "BC_UNCONFIRMED_CAST")
@SuppressWarnings("PMD")
// CHECKSTYLE:OFF
public final class TreeUtils {

  // Class cannot be instantiated.
  private TreeUtils() {
    throw new AssertionError("Class TreeUtils cannot be instantiated.");
  }

  /**
   * Checks if the provided method is a constructor method or no.
   *
   * @param tree
   *            a tree defining the method
   * @return true iff tree describes a constructor
   */
  public static boolean isConstructor(final MethodTree tree) {
    return tree.getName().contentEquals("<init>");
  }

  /**
   * Checks if the method invocation is a call to super.
   *
   * @param tree
   *            a tree defining a method invocation
   *
   * @return true iff tree describes a call to super
   */
  public static boolean isSuperCall(MethodInvocationTree tree) {
    /* @Nullable */ExpressionTree mst = tree.getMethodSelect();
    assert mst != null; /* nninvariant */

    if (mst.getKind() == Tree.Kind.IDENTIFIER) {
      return ((IdentifierTree) mst).getName().contentEquals("super");
    }

    if (mst.getKind() == Tree.Kind.MEMBER_SELECT) {
      MemberSelectTree selectTree = (MemberSelectTree) mst;

      if (selectTree.getExpression().getKind() != Tree.Kind.IDENTIFIER) {
        return false;
      }

      return ((IdentifierTree) selectTree.getExpression()).getName().contentEquals("super");
    }

    return false;
  }

  /**
   * Returns true if the tree is a tree that 'looks like' either an access of a field or an invocation of a method
   * that are owned by the same accessing instance.
   *
   * It would only return true if the access tree is of the form:
   *
   * <pre>
   *   field
   *   this.field
   *
   *   method()
   *   this.method()
   * </pre>
   *
   * It does not perform any semantical check to differentiate between fields and local variables; local methods or
   * imported static methods.
   *
   * @param tree
   *            expression tree representing an access to object member
   * @return {@code true} iff the member is a member of {@code this} instance
   */
  public static boolean isSelfAccess(final ExpressionTree tree) {
    ExpressionTree tr = TreeUtils.skipParens(tree);
    // If method invocation check the method select
    if (tr.getKind() == Tree.Kind.ARRAY_ACCESS) {
      return false;
    }

    if (tree.getKind() == Tree.Kind.METHOD_INVOCATION) {
      tr = ((MethodInvocationTree) tree).getMethodSelect();
    }
    tr = TreeUtils.skipParens(tr);
    if (tr.getKind() == Tree.Kind.TYPE_CAST) {
      tr = ((TypeCastTree) tr).getExpression();
    }
    tr = TreeUtils.skipParens(tr);

    if (tr.getKind() == Tree.Kind.IDENTIFIER) {
      return true;
    }

    if (tr.getKind() == Tree.Kind.MEMBER_SELECT) {
      tr = ((MemberSelectTree) tr).getExpression();
      if (tr.getKind() == Tree.Kind.IDENTIFIER) {
        Name ident = ((IdentifierTree) tr).getName();
        return ident.contentEquals("this") || ident.contentEquals("super");
      }
    }

    return false;
  }

  /**
   * Gets the first enclosing tree in path, of the specified kind.
   *
   * @param path
   *            the path defining the tree node
   * @param kind
   *            the kind of the desired tree
   * @return the enclosing tree of the given type as given by the path
   */
  public static Tree enclosingOfKind(final TreePath path, final Tree.Kind kind) {
    return enclosingOfKind(path, EnumSet.of(kind));
  }

  /**
   * Gets the first enclosing tree in path, with any one of the specified kinds.
   *
   * @param path
   *            the path defining the tree node
   * @param kinds
   *            the set of kinds of the desired tree
   * @return the enclosing tree of the given type as given by the path
   */
  public static Tree enclosingOfKind(final TreePath path, final Set<Tree.Kind> kinds) {
    TreePath p = path;

    while (p != null) {
      Tree leaf = p.getLeaf();
      assert leaf != null; /* nninvariant */
      if (kinds.contains(leaf.getKind())) {
        return leaf;
      }
      p = p.getParentPath();
    }

    return null;
  }

  /**
   * Gets the first enclosing tree in path, with any one of the specified kinds.
   *
   * @param path
   *            the path defining the tree node
   * @param kinds
   *            the set of kinds of the desired tree
   * @return the enclosing tree of the given type as given by the path
   */
  public static <T extends Tree> TreePath enclosingPathOfType(final TreePath path, final Class<T> clz) {
    TreePath p = path;

    while (p != null) {
      Tree leaf = p.getLeaf();
      assert leaf != null; /* nninvariant */
      if (clz.isAssignableFrom(leaf.getClass())) {
        return p;
      }
      p = p.getParentPath();
    }

    return null;
  }

  /**
   * Gets path to the the first enclosing class tree, where class is defined by the classTreeKinds method.
   *
   * @param path
   *            the path defining the tree node
   * @return the path to the enclosing class tree
   */
  // public static TreePath pathTillClass(final TreePath path) {
  // return pathTillOfKind(path, classTreeKinds());
  // }

  /**
   * Gets path to the the first enclosing tree of the specified kind.
   *
   * @param path
   *            the path defining the tree node
   * @param kind
   *            the kind of the desired tree
   * @return the path to the enclosing tree of the given type
   */
  public static TreePath pathTillOfKind(final TreePath path, final Tree.Kind kind) {
    return pathTillOfKind(path, EnumSet.of(kind));
  }

  /**
   * Gets path to the the first enclosing tree with any one of the specified kinds.
   *
   * @param path
   *            the path defining the tree node
   * @param kinds
   *            the set of kinds of the desired tree
   * @return the path to the enclosing tree of the given type
   */
  public static TreePath pathTillOfKind(final TreePath path, final Set<Tree.Kind> kinds) {
    TreePath p = path;

    while (p != null) {
      Tree leaf = p.getLeaf();
      assert leaf != null; /* nninvariant */
      if (kinds.contains(leaf.getKind())) {
        return p;
      }
      p = p.getParentPath();
    }

    return null;
  }

  /**
   * Gets the first enclosing tree in path, of the specified class
   *
   * @param path
   *            the path defining the tree node
   * @param treeClass
   *            the class of the desired tree
   * @return the enclosing tree of the given type as given by the path
   */
  public static <T extends Tree> T enclosingOfClass(final TreePath path, final Class<T> treeClass) {
    TreePath p = path;

    while (p != null) {
      Tree leaf = p.getLeaf();
      if (treeClass.isInstance(leaf)) {
        return treeClass.cast(leaf);
      }
      p = p.getParentPath();
    }

    return null;
  }

  /**
   * Gets the enclosing class of the tree node defined by the given {@code {@link TreePath} . It returns a
   * {@link Tree}, from which {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be obtained.
   *
   * @param path
   *            the path defining the tree node
   * @return the enclosing class (or interface) as given by the path, or null if one does not exist.
   */
  public static/* @Nullable */ClassTree enclosingClass(final/* @Nullable */TreePath path) {
    return enclosingOfClass(path, ClassTree.class);
  }

  /**
   * Gets the enclosing variable of a tree node defined by the given {@link TreePath}.
   *
   * @param path
   *            the path defining the tree node
   * @return the enclosing variable as given by the path, or null if one does not exist
   */
  public static VariableTree enclosingVariable(final TreePath path) {
    return (VariableTree) enclosingOfKind(path, Tree.Kind.VARIABLE);
  }

  /**
   * Gets the enclosing method of the tree node defined by the given {@code {@link TreePath} . It returns a
   * {@link Tree}, from which an {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be obtained.
   *
   * @param path
   *            the path defining the tree node
   * @return the enclosing method as given by the path, or null if one does not exist
   */
  public static/* @Nullable */MethodTree enclosingMethod(final/* @Nullable */TreePath path) {
    return (MethodTree) enclosingOfKind(path, Tree.Kind.METHOD);
  }

  public static/* @Nullable */BlockTree enclosingTopLevelBlock(TreePath path) {
    TreePath parpath = path.getParentPath();
    while (parpath != null && parpath.getLeaf().getKind() != Tree.Kind.CLASS) {
      path = parpath;
      parpath = parpath.getParentPath();
    }
    if (path.getLeaf().getKind() == Tree.Kind.BLOCK) {
      return (BlockTree) path.getLeaf();
    }
    return null;
  }

  /**
   * If the given tree is a parenthesized tree, it returns the enclosed non-parenthesized tree. Otherwise, it returns
   * the same tree.
   *
   * @param tree
   *            an expression tree
   * @return the outermost non-parenthesized tree enclosed by the given tree
   */
  public static ExpressionTree skipParens(final ExpressionTree tree) {
    ExpressionTree t = tree;
    while (t.getKind() == Tree.Kind.PARENTHESIZED) {
      t = ((ParenthesizedTree) t).getExpression();
    }
    return t;
  }

  /**
   * Returns the tree with the assignment context for the treePath leaf node.
   *
   * The assignment context for the treepath is the most enclosing tree of type:
   * <ul>
   * <li>AssignmentTree</li>
   * <li>CompoundAssignmentTree</li>
   * <li>MethodInvocationTree</li>
   * <li>NewArrayTree</li>
   * <li>NewClassTree</li>
   * <li>ReturnTree</li>
   * <li>VariableTree</li>
   * </ul>
   *
   * @param treePath
   * @return the assignment context as described.
   */
  public static Tree getAssignmentContext(final TreePath treePath) {
    TreePath path = treePath.getParentPath();

    if (path == null) {
      return null;
    }
    Tree node = path.getLeaf();
    if ((node instanceof AssignmentTree) || (node instanceof CompoundAssignmentTree) || (node instanceof MethodInvocationTree)
        || (node instanceof NewArrayTree) || (node instanceof NewClassTree) || (node instanceof ReturnTree)
        || (node instanceof VariableTree)) {
      return node;
    }
    return null;
  }

  /**
   * Gets the element for a class corresponding to a declaration.
   *
   * @param node
   * @return the element for the given class
   */
  public static final TypeElement elementFromDeclaration(ClassTree node) {
    TypeElement elt = (TypeElement) InternalUtils.symbol(node);
    return elt;
  }

  /**
   * Gets the element for a method corresponding to a declaration.
   *
   * @param node
   * @return the element for the given method
   */
  public static final ExecutableElement elementFromDeclaration(MethodTree node) {
    ExecutableElement elt = (ExecutableElement) InternalUtils.symbol(node);
    return elt;
  }

  /**
   * Gets the element for a variable corresponding to its declaration.
   *
   * @param node
   * @return the element for the given variable
   */
  public static final VariableElement elementFromDeclaration(VariableTree node) {
    VariableElement elt = (VariableElement) InternalUtils.symbol(node);
    return elt;
  }

  /**
   * Gets the element for the declaration corresponding to this use of an element. To get the element for a
   * declaration, use {@link com.sun.source.util.Trees#getElement(TreePath)} instead.
   *
   * TODO: remove this method, as it really doesn't do anything.
   *
   * @param node
   *            the tree corresponding to a use of an element
   * @return the element for the corresponding declaration
   */
  public static final Element elementFromUse(ExpressionTree node) {
    return InternalUtils.symbol(node);
  }

  // Specialization for return type.
  public static final ExecutableElement elementFromUse(MethodInvocationTree node) {
    return (ExecutableElement) elementFromUse((ExpressionTree) node);
  }

  // Specialization for return type.
  public static final ExecutableElement elementFromUse(NewClassTree node) {
    return (ExecutableElement) elementFromUse((ExpressionTree) node);
  }

  /**
   * Determine whether the given ExpressionTree has an underlying element.
   *
   * @param node
   *            the ExpressionTree to test
   * @return whether the tree refers to an identifier, member select, or method invocation.
   */
  public static final boolean isUseOfElement(ExpressionTree node) {
    node = TreeUtils.skipParens(node);
    switch (node.getKind()) {
    case IDENTIFIER:
    case MEMBER_SELECT:
    case METHOD_INVOCATION:
    case NEW_CLASS:
      return true;
    default:
      return false;
    }
  }

  /**
   * @return the name of the invoked method
   */
  public static final Name methodName(MethodInvocationTree node) {
    ExpressionTree expr = node.getMethodSelect();
    if (expr.getKind() == Tree.Kind.IDENTIFIER) {
      return ((IdentifierTree) expr).getName();
    } else if (expr.getKind() == Tree.Kind.MEMBER_SELECT) {
      return ((MemberSelectTree) expr).getIdentifier();
    }
    ErrorReporter.errorAbort("TreeUtils.methodName: cannot be here: " + node);
    return null; // dead code
  }

  /**
   * @return true if the first statement in the body is a self constructor invocation within a constructor
   */
  public static final boolean containsThisConstructorInvocation(MethodTree node) {
    if (!TreeUtils.isConstructor(node) || node.getBody().getStatements().isEmpty()) {
      return false;
    }

    StatementTree st = node.getBody().getStatements().get(0);
    if (!(st instanceof ExpressionStatementTree) || !(((ExpressionStatementTree) st).getExpression() instanceof MethodInvocationTree)) {
      return false;
    }

    MethodInvocationTree invocation = (MethodInvocationTree) ((ExpressionStatementTree) st).getExpression();

    return "this".contentEquals(TreeUtils.methodName(invocation));
  }

  public static final Tree firstStatement(Tree tree) {
    Tree first;
    if (tree.getKind() == Tree.Kind.BLOCK) {
      BlockTree block = (BlockTree) tree;
      if (block.getStatements().isEmpty()) {
        first = block;
      } else {
        first = block.getStatements().iterator().next();
      }
    } else {
      first = tree;
    }
    return first;
  }

  /**
   * Determine whether the given class contains an explicit constructor.
   *
   * @param node
   *            A class tree.
   * @return True, iff there is an explicit constructor.
   */
  public static boolean hasExplicitConstructor(ClassTree node) {
    TypeElement elem = TreeUtils.elementFromDeclaration(node);

    for (ExecutableElement ee : ElementFilter.constructorsIn(elem.getEnclosedElements())) {
      MethodSymbol ms = (MethodSymbol) ee;
      long mod = ms.flags();

      if ((mod & Flags.SYNTHETIC) == 0) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns true if the tree is of a diamond type
   */
  public static final boolean isDiamondTree(Tree tree) {
    switch (tree.getKind()) {
    // case ANNOTATED_TYPE:
    // return isDiamondTree(((AnnotatedTypeTree) tree).getUnderlyingType());
    case PARAMETERIZED_TYPE:
      return ((ParameterizedTypeTree) tree).getTypeArguments().isEmpty();
    case NEW_CLASS:
      return isDiamondTree(((NewClassTree) tree).getIdentifier());
    default:
      return false;
    }
  }

  /**
   * Returns true if the tree represents a {@code String} concatenation operation
   */
  public static final boolean isStringConcatenation(Tree tree) {
    return (tree.getKind() == Tree.Kind.PLUS && TypesUtils.isString(InternalUtils.typeOf(tree)));
  }

  /**
   * Returns true if the compound assignment tree is a string concatenation
   */
  public static final boolean isStringCompoundConcatenation(CompoundAssignmentTree tree) {
    return (tree.getKind() == Tree.Kind.PLUS_ASSIGNMENT && TypesUtils.isString(InternalUtils.typeOf(tree)));
  }

  /**
   * Returns true if the node is a constant-time expression.
   *
   * A tree is a constant-time expression if it is:
   * <ol>
   * <li>a literal tree
   * <li>a reference to a final variable initialized with a compile time constant
   * <li>a String concatenation of two compile time constants
   * </ol>
   */
  public static boolean isCompileTimeString(ExpressionTree node) {
    ExpressionTree tree = TreeUtils.skipParens(node);
    if (tree instanceof LiteralTree) {
      return true;
    }

    if (TreeUtils.isUseOfElement(tree)) {
      Element elt = TreeUtils.elementFromUse(tree);
      return ElementUtils.isCompileTimeConstant(elt);
    } else if (TreeUtils.isStringConcatenation(tree)) {
      BinaryTree binOp = (BinaryTree) tree;
      return isCompileTimeString(binOp.getLeftOperand()) && isCompileTimeString(binOp.getRightOperand());
    } else {
      return false;
    }
  }

  /**
   * Returns the receiver tree of a field access or a method invocation
   */
  public static ExpressionTree getReceiverTree(ExpressionTree expression) {
    ExpressionTree receiver = TreeUtils.skipParens(expression);

    if (!(receiver.getKind() == Tree.Kind.METHOD_INVOCATION || receiver.getKind() == Tree.Kind.MEMBER_SELECT
        || receiver.getKind() == Tree.Kind.IDENTIFIER || receiver.getKind() == Tree.Kind.ARRAY_ACCESS)) {
      // No receiver tree for anything but these four kinds.
      return null;
    }

    if (receiver.getKind() == Tree.Kind.METHOD_INVOCATION) {
      // Trying to handle receiver calls to trees of the form
      // ((m).getArray())
      // returns the type of 'm' in this case
      receiver = ((MethodInvocationTree) receiver).getMethodSelect();

      if (receiver.getKind() == Tree.Kind.IDENTIFIER) {
        // It's a method call "m(foo)" without an explicit receiver
        return null;
      } else if (receiver.getKind() == Tree.Kind.MEMBER_SELECT) {
        receiver = ((MemberSelectTree) receiver).getExpression();
      } else {
        // Otherwise, e.g. a NEW_CLASS: nothing to do.
      }
    } else if (receiver.getKind() == Tree.Kind.IDENTIFIER) {
      // It's a field access on implicit this or a local variable/parameter.
      return null;
    } else if (receiver.getKind() == Tree.Kind.ARRAY_ACCESS) {
      return TreeUtils.skipParens(((ArrayAccessTree) receiver).getExpression());
    } else if (receiver.getKind() == Tree.Kind.MEMBER_SELECT) {
      receiver = ((MemberSelectTree) receiver).getExpression();
      // Avoid int.class
      if (receiver instanceof PrimitiveTypeTree) {
        return null;
      }
    }

    // Receiver is now really just the receiver tree.
    return TreeUtils.skipParens(receiver);
  }

  private final static Set<Tree.Kind> typeTreeKinds = EnumSet.of(Tree.Kind.PRIMITIVE_TYPE, Tree.Kind.PARAMETERIZED_TYPE,
      Tree.Kind.TYPE_PARAMETER, Tree.Kind.ARRAY_TYPE, Tree.Kind.UNBOUNDED_WILDCARD, Tree.Kind.EXTENDS_WILDCARD, Tree.Kind.SUPER_WILDCARD);

  public static Set<Tree.Kind> typeTreeKinds() {
    return typeTreeKinds;
  }

  /**
   * Is the given tree a type instantiation?
   *
   * TODO: this is an under-approximation: e.g. an identifier could be either a type use or an expression. How can we
   * distinguish.
   *
   * @param tree
   *            the tree to test
   * @return true, iff the given tree is a type
   */
  public static boolean isTypeTree(Tree tree) {
    return typeTreeKinds().contains(tree.getKind());
  }

  /**
   * Returns true if the given element is an invocation of the method, or of any method that overrides that one.
   */
  public static boolean isMethodInvocation(Tree tree, ExecutableElement method, ProcessingEnvironment env) {
    if (!(tree instanceof MethodInvocationTree)) {
      return false;
    }
    MethodInvocationTree methInvok = (MethodInvocationTree) tree;
    ExecutableElement invoked = TreeUtils.elementFromUse(methInvok);
    return isMethod(invoked, method, env);
  }

  /** Returns true if the given element is, or overrides, method. */
  private static boolean isMethod(ExecutableElement questioned, ExecutableElement method, ProcessingEnvironment env) {
    return (questioned.equals(method) || env.getElementUtils().overrides(questioned, method, (TypeElement) questioned.getEnclosingElement()));
  }

  /**
   * Returns the ExecutableElement for a method declaration of methodName, in class typeName, with params parameters.
   *
   * TODO: to precisely resolve method overloading, we should use parameter types and not just the number of
   * parameters!
   */
  public static ExecutableElement getMethod(String typeName, String methodName, int params, ProcessingEnvironment env) {
    TypeElement mapElt = env.getElementUtils().getTypeElement(typeName);
    for (ExecutableElement exec : ElementFilter.methodsIn(mapElt.getEnclosedElements())) {
      if (exec.getSimpleName().contentEquals(methodName) && exec.getParameters().size() == params) {
        return exec;
      }
    }
    ErrorReporter.errorAbort("TreeUtils.getMethod: shouldn't be here!");
    return null; // dead code
  }

  /**
   * Determine whether <code>tree</code> is a field access expressions, such as
   *
   * <pre>
   *   <em>f</em>
   *   <em>obj</em> . <em>f</em>
   * </pre>
   *
   * @return true iff if tree is a field access expression (implicit or explicit).
   */
  public static boolean isFieldAccess(Tree tree) {
    if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
      // explicit field access
      MemberSelectTree memberSelect = (MemberSelectTree) tree;
      Element el = TreeUtils.elementFromUse(memberSelect);
      return el.getKind().isField();
    } else if (tree.getKind().equals(Tree.Kind.IDENTIFIER)) {
      // implicit field access
      IdentifierTree ident = (IdentifierTree) tree;
      Element el = TreeUtils.elementFromUse(ident);
      return el.getKind().isField() && !ident.getName().contentEquals("this") && !ident.getName().contentEquals("super");
    }
    return false;
  }

  /**
   * Compute the name of the field that the field access <code>tree</code> accesses. Requires <code>tree</code> to be
   * a field access, as determined by <code>isFieldAccess</code>.
   *
   * @return The name of the field accessed by <code>tree</code>.
   */
  public static String getFieldName(Tree tree) {
    assert isFieldAccess(tree);
    if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
      MemberSelectTree mtree = (MemberSelectTree) tree;
      return mtree.getIdentifier().toString();
    } else {
      IdentifierTree itree = (IdentifierTree) tree;
      return itree.getName().toString();
    }
  }

  /**
   * Determine whether <code>tree</code> refers to a method element, such as
   *
   * <pre>
   *   <em>m</em>(...)
   *   <em>obj</em> . <em>m</em>(...)
   * </pre>
   *
   * @return true iff if tree is a method access expression (implicit or explicit).
   */
  public static boolean isMethodAccess(Tree tree) {
    if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
      // explicit method access
      MemberSelectTree memberSelect = (MemberSelectTree) tree;
      Element el = TreeUtils.elementFromUse(memberSelect);
      return el.getKind() == ElementKind.METHOD || el.getKind() == ElementKind.CONSTRUCTOR;
    } else if (tree.getKind().equals(Tree.Kind.IDENTIFIER)) {
      // implicit method access
      IdentifierTree ident = (IdentifierTree) tree;
      // The field "super" and "this" are also legal methods
      if (ident.getName().contentEquals("super") || ident.getName().contentEquals("this")) {
        return true;
      }
      Element el = TreeUtils.elementFromUse(ident);
      return el.getKind() == ElementKind.METHOD || el.getKind() == ElementKind.CONSTRUCTOR;
    }
    return false;
  }

  /**
   * Compute the name of the method that the method access <code>tree</code> accesses. Requires <code>tree</code> to
   * be a method access, as determined by <code>isMethodAccess</code>.
   *
   * @return The name of the method accessed by <code>tree</code>.
   */
  public static String getMethodName(Tree tree) {
    assert isMethodAccess(tree);
    if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
      MemberSelectTree mtree = (MemberSelectTree) tree;
      return mtree.getIdentifier().toString();
    } else {
      IdentifierTree itree = (IdentifierTree) tree;
      return itree.getName().toString();
    }
  }

  /**
   * @return {@code true} if and only if {@code tree} can have a type annotation.
   *
   *         TODO: is this implementation precise enough? E.g. does a .class literal work correctly?
   */
  public static boolean canHaveTypeAnnotation(Tree tree) {
    return ((JCTree) tree).type != null;
  }

  /**
   * Returns true if and only if the given {@code tree} represents a field access of the given {@link VariableElement}
   * .
   */
  public static boolean isSpecificFieldAccess(Tree tree, VariableElement var) {
    if (tree instanceof MemberSelectTree) {
      MemberSelectTree memSel = (MemberSelectTree) tree;
      Element field = TreeUtils.elementFromUse(memSel);
      return field.equals(var);
    } else if (tree instanceof IdentifierTree) {
      IdentifierTree idTree = (IdentifierTree) tree;
      Element field = TreeUtils.elementFromUse(idTree);
      return field.equals(var);
    } else {
      return false;
    }
  }

  /**
   * Returns the VariableElement for a field declaration.
   *
   * @param typeName
   *            the class where the field is declared.
   * @param fieldName
   *            the name of the field.
   * @param env
   *            the processing environment.
   * @return the VariableElement for typeName.fieldName
   */
  public static VariableElement getField(String typeName, String fieldName, ProcessingEnvironment env) {
    TypeElement mapElt = env.getElementUtils().getTypeElement(typeName);
    for (VariableElement var : ElementFilter.fieldsIn(mapElt.getEnclosedElements())) {
      if (var.getSimpleName().contentEquals(fieldName)) {
        return var;
      }
    }
    ErrorReporter.errorAbort("TreeUtils.getField: shouldn't be here!");
    return null; // dead code
  }

  /**
   * Determine whether the given tree represents an ExpressionTree.
   *
   * TODO: is there a nicer way than an instanceof?
   *
   * @param tree
   *            the Tree to test.
   * @return whether the tree is an ExpressionTree
   */
  public static boolean isExpressionTree(Tree tree) {
    return tree instanceof ExpressionTree;
  }

  /**
   * @param node
   *            the method invocation to check
   * @return true if this is a super call to the {@link Enum} constructor
   */
  public static boolean isEnumSuper(MethodInvocationTree node) {
    ExecutableElement ex = TreeUtils.elementFromUse(node);
    Name name = ElementUtils.getQualifiedClassName(ex);
    boolean correctClass = "java.lang.Enum".contentEquals(name);
    boolean correctMethod = "<init>".contentEquals(ex.getSimpleName());
    return correctClass && correctMethod;
  }

}
// CHECKSTYLE:ON
TOP

Related Classes of org.stjs.generator.javac.TreeUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.