Разрешение класса времени компиляции Java - PullRequest
0 голосов
/ 22 марта 2012

Как я могу получить ссылку на класс / TypeElement определенного идентификатора во время компиляции в Java?

Допустим, у меня есть следующий исходный файл, и я хочу получить ссылку на класс Pixel, чтобы я мог получить список его полей-членов.

package com.foo.bar;
class Test {
    class Pixel {
        int x,y,r,g,b;
    }
    Pixel saturate(Pixel p, int value) {...}
}

Определение класса Pixel может быть вложено в класс Test или включено из другого пакета, где источник недоступен.

Я использую API javax.tools для компиляции исходных файлов, и я определяю методы посетителя, чтобы я мог просматривать аргументы каждой функции. Аргументы функции могут быть повторены с использованием VariableTree var : node.getParameters(), но информация о типе из var.getType() только вызывает visitIdentifier для имен классов. Этот идентификатор - только простое имя Pixel, а не полное имя com.foo.bar.Pixel.

Мне нужен способ обратить этот идентификатор в Pixel.class или в TypeElement для определения класса Pixel, или в полностью определенную строку com.foo.bar.Pixel, чтобы я мог затем использовать ClassLoader это.

Грубым способом было бы записать все определения классов, а затем попытаться выполнить поиск типов во время компиляции, но это не будет работать для внешне определенных классов.

Ответы [ 2 ]

0 голосов
/ 31 марта 2012

В итоге я создал свой собственный инструмент поиска классов. Для тех, кто заинтересован, я включу это здесь.

Назовите это с именем пути каждого исходного файла, который будет включен в поиск:

  public void populateClassDefinitions(String path) {
    Iterable<? extends JavaFileObject> files = fileManager.getJavaFileObjects(path);
    CompilationTask task =
        compiler.getTask(null, fileManager, diagnosticsCollector, null, null, files);
    final JavacTask javacTask = (JavacTask) task;
    parseResult = null;
    try {
      parseResult = javacTask.parse();
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }
    for (CompilationUnitTree tree : parseResult) {
      tree.accept(new TreeScanner<Void, Void>() {
        @Override
        public Void visitCompilationUnit(CompilationUnitTree node, Void p) {
          currentPackage = "";
          ExpressionTree packageName = node.getPackageName();
          if (packageName != null) {
            String packageNameString = String.valueOf(packageName);
            if (packageNameString.length() > 0) {
              currentPackage = packageNameString;
            }
          }
          TreeScanner<Void, String> visitor = new TreeScanner<Void, String>() {
            @Override
            public Void visitClass(ClassTree node, String packagePrefix) {
              if (classDefinitions.get(currentPackage) == null) {
                classDefinitions.put(currentPackage, new HashMap<String, ClassTree>());
              }
              classDefinitions.get(currentPackage).put(packagePrefix + node.getSimpleName(), node);
              return super.visitClass(node, packagePrefix + node.getSimpleName() + ".");
            }
          };
          for (Tree decls : node.getTypeDecls()) {
            decls.accept(visitor, "");
          }
          return super.visitCompilationUnit(node, p);
        }
      }, null);
    }
  }

Позвоните, чтобы найти классы.

  /**
   * Lookup the definition of a class.
   * 
   * Lookup order: 1. Search in the current file: within the current class scope upwards to the
   * root. 2. Search laterally across files with the same package value for implicitly included
   * classes. 3. Check all import statements.
   * 
   * @param pack
   *          Current package ex "edu.illinois.crhc"
   * @param scope
   *          Current scope ex "Test.InnerClass"
   * @param identifier
   *          The partial class name to search for
   * @return ClassTree the definition of this class if found
   */
  ClassLookup lookupClass(CompilationUnitTree packTree, String scope, String identifier) {
    dumpClassTable();
    String pack = packTree.getPackageName().toString();
    System.out.println("Looking for class " + pack + " - " + scope + " - " + identifier);
    // Search nested scope and within same package
    HashMap<String, ClassTree> packClasses = classDefinitions.get(pack);
    if (packClasses != null) {
      String[] scopeWalk = scope.split("\\.");
      for (int i = scopeWalk.length; i >= 0; i--) {
        StringBuilder scopeTest = new StringBuilder();
        for (int j = 0; j < i; j++) {
          scopeTest.append(scopeWalk[j] + ".");
        }
        scopeTest.append(identifier);
        System.out.println("Testing scope " + pack + " - " + scopeTest.toString());
        if (packClasses.containsKey(scopeTest.toString())) {
          return new ClassLookup(packClasses.get(scopeTest.toString()), pack.replace(".", "/")
              + "/" + scopeTest.toString().replace(".", "$"));
        }
      }
    }
    /*
     * Check if fully-qualified identifier (foo.bar.Widget) is used. This needs to search all
     * combinations of package and class nesting.
     */
    StringBuilder packTest = new StringBuilder();
    String[] qualifiedName = identifier.split("\\.");
    for (int i = 0; i < qualifiedName.length - 1; i++) {
      packTest.append(qualifiedName[i]);
      if (i != qualifiedName.length - 2) {
        packTest.append(".");
      }
    }
    String clazz = qualifiedName[qualifiedName.length - 1];
    System.out.println("Testing absolute identifier: " + packTest.toString() + " " + clazz);
    if (classDefinitions.containsKey(packTest.toString())) {
      HashMap<String, ClassTree> foundPack = classDefinitions.get(packTest.toString());
      if (foundPack.containsKey(clazz)) {
        return new ClassLookup(foundPack.get(clazz), packTest.toString().replace(".", "/") + "/"
            + clazz.replace(".", "$"));
      }
    }

    /*
     * Search import statements. Last identifier segment must be class name. Search all of the
     * packages for the identifier by splitting off the class name. a.b.c.Tree Tree.Branch
     * Tree.Branch.Leaf
     */
    for (ImportTree imp : currentPackTree.getImports()) {
      pack = imp.getQualifiedIdentifier().toString();
      System.out.println(pack);
      String[] importName = pack.split("\\.");
      // Split off class name.
      // TODO: (edge case) no package
      StringBuilder importTest = new StringBuilder();
      for (int i = 0; i < importName.length - 1; i++) {
        importTest.append(importName[i]);
        if (i != importName.length - 2) {
          importTest.append(".");
        }
      }
      // See if the last import segment is * or matches the first segment of the identifier.

      System.out.println("Testing globally " + importTest.toString() + " - " + identifier);
      if (classDefinitions.containsKey(importTest.toString())) {
        HashMap<String, ClassTree> foundPack = classDefinitions.get(importTest.toString());
        String[] identifierParts = identifier.split(".");
        String importClass = importName[importName.length-1];
        if (importClass.equals("*") || identifierParts[0].equals(importClass)) {
          if (foundPack.containsKey(identifier)) {
            return new ClassLookup(foundPack.get(identifier), importTest.toString().replace(".", "/")
                + "/" + identifier.replace(".", "$"));
          }
        }
      }
    }

    return null;
  }
0 голосов
/ 22 марта 2012

Насколько я помню var.getType().toString() возвращает вам полное имя класса. К сожалению, я не могу проверить это прямо сейчас, но попробуйте сами.

Да, я знаю, что это очень плохой стиль , использовать toString() для чего-то, кроме ведения журнала, но, похоже, они не дали нам другого выбора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...