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