How do I find the type declaration of an identifier using the Java Tree Compiler API?

对着背影说爱祢 提交于 2019-12-06 12:45:31
 public Symbol getSymbol(CompilationUnitTree cut, JCStatement stmt, List<JCExpression> typeParams, Name varName, List<JCExpression> args) {
    java.util.List<Type> typeSyms = getArgTypes(typeParams, cut, stmt);
    java.util.List<Type> argsSyms = getArgTypes(args, cut, stmt);
    final Scope scope = getScope(cut, stmt);
    Symbol t = contains(scope, typeSyms, varName, argsSyms); //first lookup scope for all public identifiers
    TypeElement cl = scope.getEnclosingClass();
    while (t == null && cl != null) { //lookup hierarchy for inacessible identifiers too
        t = contains(elementUtils.getAllMembers(cl), typeSyms, varName, argsSyms);
        final TypeMirror superclass = cl.getSuperclass();
        if (superclass != null) {
            cl = (TypeElement) ((Type) superclass).asElement();
        }
    }
    return t;
}

public Symbol getSymbol(Name varName, Symbol accessor, CompilationUnitTree cut, JCStatement stmt) {
    if (varName.contentEquals("class")) {
        Symbol javaLangClassSym = getSymbol(cut, stmt, null, elementUtils.getName("java.lang.Class"), null);
        JCIdent id = tm.Ident(javaLangClassSym);
        JCExpression mName = tm.Select(id, elementUtils.getName("forName"));
        JCLiteral idLiteral = tm.Literal(accessor.toString());
        JCMethodInvocation mi = tm.Apply(List.<JCExpression>nil(), mName, List.<JCExpression>of(idLiteral));
        Symbol s = getSymbol(mi, cut, stmt);
        return s;
    }
    accessor = getTypeSymbol(accessor);
    java.util.List<Symbol> enclosedElements = getEnclosedElements(accessor, cut, stmt);
    Symbol s = contains(enclosedElements, null, varName, null);
    return s;
}

I ended up writing a full class with resolving methods. Inheriting it, or passing in the TreeMaker (and other parameters) it can be made static. Should someone find it worthwile, a patch is welcome.

Paŭlo Ebermann

The Scope object has a method getLocalElements(), which can be iterated over. Each element can then be asked by its name, and when this is the right one (and it is a variable, too), you can get its type.

This is the concept, untested:

private final static Set<ElementKind> variableKinds =
       Collections.unmodifiableSet(EnumSet.of(ElementKind.FIELD, ElementKind.ENUM_CONSTANT,
                                              ElementKind.PARAMETER, ElementKind.LOCAL_VARIABLE));

public Type getTypeOfVariable(Scope scope, String varName)
{
   for(Element e : scope.getLocalElements()) {
       if(variableKinds.contains(e.getKind()) && e.getName().equals(varName)) {
           return e.getType();
       }
   }
   throw new NoSuchElementException("No variable " + varName + " in " + scope);
}

Edit: Yeah, really untested (there is no getType() method).

So, how to get from a Element (or VariableElement) from its type?

The Trees class has some utility methods allowing the retrieval of a Tree or TreePath from the Element, and thus we can get a VariableTree (since this came from a variable declaration). The VariableTree now has a getType() method, which returns a Tree - but in fact this is one of PrimitiveTypeTree, ParametrizedTypeTree, ArrayTypeTree and IdentifierTree (for simple reference types as well as type variables). So, if you only want to print the type, this may be enough. Otherwise, again with the Trees class we now can get a TypeMirror for this type.

(I did something similar once when I tried to write a new Doclet which would also output the formatted source code. Terrible flipping between all those APIs.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!