Java 在注释处理器中访问常量字段

Java 在注释处理器中访问常量字段,java,annotations,annotation-processing,java-compiler-api,Java,Annotations,Annotation Processing,Java Compiler Api,假设一个类定义了一个常量字段: public class Foo { public static final int CONSTANT_FIELD = 3; } 假设注释接口声明如下: public @interface Something { int value(); } 最后,假设注释的使用方式如下: @Something(Foo.CONSTANT_FIELD) 问题:在注释处理器中,如何从设置@Something的值中获取常量字段的元素 编辑:在问题本身中包含一个具体的例子

假设一个类定义了一个常量字段:

public class Foo {
  public static final int CONSTANT_FIELD = 3;
}
假设注释接口声明如下:

public @interface Something {
  int value();
}
最后,假设注释的使用方式如下:

@Something(Foo.CONSTANT_FIELD)
问题:在注释处理器中,如何从设置
@Something
的值中获取
常量字段的元素


编辑:在问题本身中包含一个具体的例子。

我有这样一个注释:

@RuleDependency(recognizer = BQLParser.class,
                rule = BQLParser.RULE_statement,
                version = 0)
注释处理器需要知道
RULE_语句
是在
BQLParser
类中定义的常量。如果我可以直接通过设置注释的
RULE
属性来访问
BQLParser.RULE\u语句的
元素
,则无需使用
识别器
属性。此注释在实际应用程序中使用了数千次,
识别器
始终只是
规则
常量的声明类型。解决此问题可以将注释的使用简化为:

@RuleDependency(rule = BQLParser.RULE_statement, version = 0)

如果
规则
是一个
int
,则注释处理器不可能找到该
int
的定义位置。但是,对于
规则
字段,您可以使用
枚举
而不是
int
,并在其中使用对规则解析器的引用对规则进行分组。也许是这样的:

@RuleDependency(recognizer = BQLParser.class,
                rule = BQLParser.RULE_statement,
                version = 0)
解析器接口:

public interface RuleParser {
}
实施示例:

public class RuleParserImpl implements RuleParser {
}
规则枚举:

public enum Rule {

    RULE_STATEMENT(RuleParserImpl.class);

    private final Class<? extends RuleParser> ruleParserClass;

    private Rule(Class<? extends RuleParser> ruleParser) {
        this.ruleParserClass = ruleParser;
    }

    public Class<? extends RuleParser> getRuleParserClass() {
        return ruleParserClass;
    }
}
用法示例:

@RuleDependency(rule = Rule.RULE_STATEMENT)
public class RuleProcessor {

    public static void main(String[] args) {
        RuleDependency ruleDependency = RuleProcessor.class.getAnnotation(RuleDependency.class);
        Rule rule = ruleDependency.rule();
        Class<? extends RuleParser> ruleParserClass = rule.getRuleParserClass();
        System.out.println(ruleParserClass); //Prints "class RuleParserImpl"
    }
}
@RuleDependency(rule=rule.rule\u语句)
公共类规则处理器{
公共静态void main(字符串[]args){
RuleDependency=RuleProcessor.class.getAnnotation(RuleDependency.class);
Rule=ruleDependency.Rule();

类我能够使用编译器树API实现这个特性

  • 更新pom.xml以包括以下配置文件,以确保在默认情况下未引用tools.jar的系统上引用tools.jar

    <profiles>
        <profile>
            <!-- Java 6 and earlier have java.vendor set to "Sun Microsystems Inc." -->
            <id>default-tools-6.jar</id>
            <activation>
                <property>
                    <name>java.vendor</name>
                    <value>Sun Microsystems Inc.</value>
                </property>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>com.sun</groupId>
                    <artifactId>tools</artifactId>
                    <version>1.6</version>
                    <scope>system</scope>
                    <systemPath>${java.home}/../lib/tools.jar</systemPath>
                </dependency>
            </dependencies>
        </profile>
    
        <profile>
            <!-- Java 7 and later have java.vendor set to "Oracle Corporation" -->
            <id>default-tools.jar</id>
            <activation>
                <property>
                    <name>java.vendor</name>
                    <value>Oracle Corporation</value>
                </property>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>com.sun</groupId>
                    <artifactId>tools</artifactId>
                    <version>1.6</version>
                    <scope>system</scope>
                    <systemPath>${java.home}/../lib/tools.jar</systemPath>
                </dependency>
            </dependencies>
        </profile>
    </profiles>
    
  • 实现一个
    TreePathScanner
    ,用于获取注释中分配给
    规则
    属性的声明类型的
    TypeMirror

    private class AnnotationVisitor extends TreePathScanner<TypeMirror, Void> {
        @Override
        public TypeMirror visitAnnotation(AnnotationTree node, Void p) {
            for (ExpressionTree expressionTree : node.getArguments()) {
                if (!(expressionTree instanceof AssignmentTree)) {
                    continue;
                }
    
                AssignmentTree assignmentTree = (AssignmentTree)expressionTree;
                ExpressionTree variable = assignmentTree.getVariable();
                if (!(variable instanceof IdentifierTree) || !((IdentifierTree)variable).getName().contentEquals("rule")) {
                    continue;
                }
    
                return scan(expressionTree, p);
            }
    
            return null;
        }
    
        @Override
        public TypeMirror visitAssignment(AssignmentTree at, Void p) {
            return scan(at.getExpression(), p);
        }
    
        @Override
        public TypeMirror visitMemberSelect(MemberSelectTree mst, Void p) {
            return scan(mst.getExpression(), p);
        }
    
        @Override
        public TypeMirror visitIdentifier(IdentifierTree it, Void p) {
            return trees.getTypeMirror(this.getCurrentPath());
        }
    }
    
  • 更新收集有关应用于代码中特定
    元素
    实例的
    规则相关性
    注释信息的代码,以首先尝试访问
    识别器
    属性,如果未指定该属性,请改为使用
    规则
    属性中常量的声明类型。错误提示为简洁起见,此代码示例中省略了ling

    RuleDependency dependency = element.getAnnotation(RuleDependency.class);
    
    // first try to get the parser type from the annotation
    TypeMirror recognizerType = getRecognizerType(dependency);
    if (recognizerType != null && !recognizerType.toString().equals(Parser.class.getName())) {
        result.add(new Triple<RuleDependency, TypeMirror, Element>(dependency, recognizerType, element));
        continue;
    }
    
    // fallback to compiler tree API
    AnnotationMirror annotationMirror = null;
    for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
        if (processingEnv.getTypeUtils().isSameType(ruleDependencyTypeElement.asType(), mirror.getAnnotationType())) {
            annotationMirror = mirror;
            break;
        }
    }
    
    AnnotationValue annotationValue = null;
    for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
        if (entry.getKey().getSimpleName().contentEquals("rule")) {
            annotationValue = entry.getValue();
            break;
        }
    }
    
    TreePath treePath = trees.getPath(element, annotationMirror, annotationValue);
    AnnotationVisitor visitor = new AnnotationVisitor();
    recognizerType = visitor.scan(treePath, null);
    
    result.add(new Triple<RuleDependency, TypeMirror, Element>(dependency, recognizerType, element));
    
    RuleDependency=element.getAnnotation(RuleDependency.class);
    //首先尝试从注释中获取解析器类型
    TypeMirror识别器类型=getRecognizerType(依赖项);
    if(recognizerType!=null&&!recognizerType.toString().equals(Parser.class.getName()){
    添加(新的三元组(依赖项、识别器类型、元素));
    继续;
    }
    //回退到编译器树API
    AnnotationMirror AnnotationMirror=null;
    对于(AnnotationMirror:element.getAnnotationMirrors()){
    if(processingEnv.getTypeUtils().isSameType(ruleDependencyTypeElement.asType(),mirror.getAnnotationType()){
    注释镜像=镜像;
    打破
    }
    }
    AnnotationValue AnnotationValue=null;
    
    对于(Map.Entry无法更改这些常量值的声明。是否可以从注释处理器访问编译器源文件的内部树表示形式,或者通过常规注释处理器用例之外的方式派生信息?很好!另外,请注意,在扩展AbstractProcess时或者,您可以始终通过受保护的字段
    processingEnv
    访问ProcessingEnvironment-无需重写
    Processor.init
    。您到底为什么要这么做?当然
    enum
    是更好的解决方案。@Java我无法控制代码生成的其他部分(例如所讨论的常数的声明)。所以回答你的问题“因为我希望它工作,而不是不工作。”:)
    /**
     * Gets the recognizer class where the dependent parser rules are defined.
     * This may reference the generated parser class directly, or for simplicity
     * in certain cases, any class derived from it.
     * <p>
     * If this value is not specified, the default value {@link Parser}
     * indicates that the declaring type of the constant value specified for
     * {@link #rule} should be used as the recognizer type.
     * </p>
     */
    Class<? extends Recognizer<?, ?>> recognizer() default Parser.class;
    
    RuleDependency dependency = element.getAnnotation(RuleDependency.class);
    
    // first try to get the parser type from the annotation
    TypeMirror recognizerType = getRecognizerType(dependency);
    if (recognizerType != null && !recognizerType.toString().equals(Parser.class.getName())) {
        result.add(new Triple<RuleDependency, TypeMirror, Element>(dependency, recognizerType, element));
        continue;
    }
    
    // fallback to compiler tree API
    AnnotationMirror annotationMirror = null;
    for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
        if (processingEnv.getTypeUtils().isSameType(ruleDependencyTypeElement.asType(), mirror.getAnnotationType())) {
            annotationMirror = mirror;
            break;
        }
    }
    
    AnnotationValue annotationValue = null;
    for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
        if (entry.getKey().getSimpleName().contentEquals("rule")) {
            annotationValue = entry.getValue();
            break;
        }
    }
    
    TreePath treePath = trees.getPath(element, annotationMirror, annotationValue);
    AnnotationVisitor visitor = new AnnotationVisitor();
    recognizerType = visitor.scan(treePath, null);
    
    result.add(new Triple<RuleDependency, TypeMirror, Element>(dependency, recognizerType, element));