Как найти объявление типа идентификатора с помощью API компилятора дерева Java? - PullRequest
2 голосов
/ 03 февраля 2011

У меня есть имя переменной / идентификатора, скажем, x, а также JCCompilationUnit и Scope.Есть ли способ найти тип x?

Ответы [ 2 ]

3 голосов
/ 22 февраля 2011
 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;
}

В итоге я написал полный класс с разрешающими методами . Наследуя его или передавая TreeMaker (и другие параметры), он может быть сделан статическим. Если кто-то найдет его достойным, исправление приветствуется.

1 голос
/ 07 февраля 2011

Объект Scope имеет метод getLocalElements(), , который можно повторять.Затем каждый элемент может быть задан по его имени, и когда это правильный (и это тоже переменная), вы можете получить его тип.

Это концепция, не проверенная:

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);
}

Редактировать: Да, действительно не проверено (нет метода getType()).

Итак, как получить из Element (или VariableElement) его тип?

В классе Trees есть несколько служебных методов, позволяющих извлекать Tree или TreePath из Element, и, таким образом, мы можем получить VariableTree (поскольку это происходит из переменнойдекларация).VariableTree теперь имеет метод getType(), который возвращает Tree - но на самом деле это один из PrimitiveTypeTree, ParametrizedTypeTree, ArrayTypeTree и IdentifierTree (для простых ссылочных типов, а также для типапеременные).Так что, если вы хотите напечатать только тип, этого может быть достаточно.Иначе, снова с классом Trees мы теперь можем получить TypeMirror для этого типа.

(я однажды сделал нечто подобное, когда попытался написать новый Doclet, который также выводил бы отформатированный исходный код. Ужасное переключениемежду всеми этими API.)

...