Как получить окончательное название пакета?

У меня есть код

private static class MyVisitor extends VoidVisitorAdapter<Object> {
    @Override
    public void visit(MethodCallExpr exp, Object arg) {
        System.out.println("Scope: "  + exp.getScope());
        System.out.println("Method: " + exp.getName());
        if(exp.getArgs() != null)
            for(Expression e : exp.getArgs()) {
                System.out.println("\tArgument: " + e.toString());

            }
        System.out.println();
    }
}

А также

CompilationUnit cu = JavaParser.parse(new File("Test.java"));

for(TypeDeclaration type : cu.getTypes()) {
    for(BodyDeclaration dec : type.getMembers()) {
        if(dec instanceof MethodDeclaration) {
            MethodDeclaration mdec = (MethodDeclaration) dec;
            BlockStmt block = mdec.getBody();
            for(Statement stmt : block.getStmts()) {
                MyVisitor visitor = new MyVisitor();
                s.accept(visitor, null);
            }
        }
    }
}

У меня две проблемы:

  1. Как конвертировать выражение в выражение? Я хочу проверить, является ли этот метод вызовом. Я попытался проверить, что они несовместимые типы таким образом: if(stmt instanceof MethodCallExp) но получил ошибку. Я ввел код, который использую сейчас. Это единственный способ выполнить проверку?
  2. System.out.println("Scope: " + exp.getScope()); будет отображаться объект / пакет / пакет + имя класса. Но как получить его тип? Например для кода System.out.println("Hello world");Должен быть выведен

Тип: java.io.PrintStream

2 ответа

Решение
  1. Между утверждениями и выражениями нет связи 1:1. Например, ExpressionStmt содержит только одно Expression, а WhileStmt содержит условие Expression и тело, содержащее дополнительный оператор.
  2. Нет информации о типе, содержащейся в FieldAccessExpr, возвращенной вызовом getScope(). JavaParser, кажется, не обеспечивает простой способ получения типа. Используя иерархию узлов и их типы, вы можете использовать соответствующие классы Reflection для достижения вашей цели. Для вашего примера это будет означать поиск самой верхней области (тип будет NameExpr) и получение объекта Class (с учетом того, что System является коротким для java.lang.System), для следующего узла (тип будет FieldAccessExpr) вы получите Field класса и может затем получить тип:
    Class.forName("java.lang.System").getField("out").getType();
    В приведенном выше примере кода вы передаете своего посетителя каждому утверждению в теле метода. Если вы создадите посетителя вне цикла, он сможет запомнить информацию, увиденную в предыдущих инструкциях. Таким образом, вы можете добавить поле для вашего посетителя для хранения объявлений переменных:
    final HashMap varNameToType = new HashMap();
    Вы можете хранить объявления переменных, как показано ниже. Когда вы получаете вызов метода, вы можете использовать область действия, чтобы попытаться получить тип через карту. Если карта не содержит тип, вам может понадобиться использовать Reflection, так как переменная может быть частью классов JDK. Я не предполагаю, что это охватывает все случаи. Например, когда вы получаете доступ к полю своего родительского класса, у вас не будет никакой информации об этом поле на вашей позиции в коде - вам может потребоваться посмотреть на различные блоки компиляции для этого.
@Override
public void visit(
        final VariableDeclarationExpr n,
        final Object arg) {
    final Type varType = n.getType();
    n.getVars().forEach(vd ->
        varNameToType.put(vd.getId().getName(),varType)
    );
}

Если вам не нужно придерживаться JavaParser может быть, вы могли бы решить это так, как это делают инструменты Android lint. EcjParserTest.java. Оно использует Lombok под капотом.

На линии 380 в тесте сравнивается вывод, похожий на тот, который вы хотите получить [ExpressionStatement], type: void, resolved method: println java.io.PrintStream,

У меня не было более глубокого взгляда, но это выглядит как многообещающая отправная точка

Другие вопросы по тегам