Getter和Setter AST转型

时间:2014-11-16 12:41:12

标签: groovy abstract-syntax-tree

我编写了自己的AST Transformation,它应该生成getter和setter方法(这里创建getter方法)。但他们不工作,无法理解理由。

使用属性

创建注释
@Retention(RetentionPolicy.SOURCE)  
@Target([ElementType.FIELD])  
@GroovyASTTransformationClass(['ua.home.gag.ast.GetterAndSetterASTTransformation'])  
public @interface GetterAndSetter {  
}  

我的AST转换代码应该为带注释的字段创建getter方法

@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)  
class GetterAndSetterASTTransformation implements ASTTransformation {  

    @Override  
    void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {  
        if (!checkNodes(astNodes)) return  
            List fields = astNodes.findAll { it instanceof FieldNode }  
            fields.each {  
                MethodNode getter = getterMethod(ClassHelper.make(it));  
                it.declaringClass.addMethod(getter);  
        }  
    }  

    static def checkNodes(ASTNode[] nodes) {  
        nodes &&  
        nodes[0] &&  
        nodes[1] &&  
        nodes[0] instanceof AnnotationNode &&  
        nodes[0].classNode?.name == GetterAndSetter.class.name &&  
        nodes[1] instanceof ClassNode  
    }  

    private MethodNode getterMethod(FieldNode fieldNode) {  
        return new MethodNode(  
            "getMy" + fieldNode.name.capitalize(),  
            Modifier.PUBLIC,  
            new ClassNode(fieldNode.type),  
            new Parameter[0],  
            new ClassNode[0],  
            new BlockStatement(  
                [new ReturnStatement(  
                        new VariableExpression(fieldNode.name)  
                )],  
                new VariableScope())  
        )  
    }  
}

注释检查

import ua.home.gag.ast.GetterAndSetter  

class Example {  

    @GetterAndSetter  
    int counter = 5;  

    static void main(String[] args) {  
        println new Example().getMyCounter();  
    }
} 

我错在哪个地方?

跑步的结果:

  

线程“main”中的异常groovy.lang.MissingMethodException:没有方法签名:ua.home.gag.usage.Example.getMyCounter()适用于参数类型:()值:[]   可能的解决方案:getCounter(),setCounter(int)       在org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:56)       在org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:51)       在org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)       在org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)       在org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)       在ua.home.gag.usage.Example.main(Example.groovy:12)       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)       at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)       at java.lang.reflect.Method.invoke(Method.java:606)       在com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

PS repo https://bitbucket.org/maxrdev/ast-gands

1 个答案:

答案 0 :(得分:2)

我认为问题出在您的checkNodes方法中。此表达式nodes[1] instanceof ClassNode的结果为false,因为nodes[1]instanceof FieldNode

您也不必对字段进行过滤和迭代,因为此转换将应用于使用@GetterAndSetter注释的所有字段。这就是为什么你只需要专注于注释字段的单个案例。通常你所要做的就是:

@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class GetterAndSetterASTTransformation implements ASTTransformation {

    @Override
    void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
        AnnotationNode parent = (AnnotationNode) astNodes[0]
        FieldNode node = (FieldNode) astNodes[1]

        if (!(parent instanceof AnnotationNode) && !(node instanceof FieldNode)) {
            throw new RuntimeException("Internal error: wrong types: ${node.class} / ${parent.class}");
        }

        Statement statement = new BlockStatement([
                new ReturnStatement(new VariableExpression(node))
        ], new VariableScope())

        MethodNode methodNode = new MethodNode("getMy${node.name.capitalize()}",
                Modifier.PUBLIC,
                node.type,
                new Parameter[0],
                new ClassNode[0],
                statement
        )

        node.declaringClass.addMethod(methodNode)
    }
}

然后下面的代码将起作用:

class Example {

    @GetterAndSetter
    int counter = 5;

    @GetterAndSetter
    String lorem = 'asdasd'

    @Deprecated
    @GetterAndSetter
    BigDecimal ipsum = BigDecimal.ONE

    static void main(String[] args) {
        Example example = new Example()

        println example.getMyCounter()

        println example.getMyLorem()

        println example.getMyIpsum()
    }
}

结果是:

/usr/lib/jvm/java-1.8.0/bin/java -Didea.launcher.port=7541 -Didea.launcher.bin.path=/opt/idea-IU-129.1525/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0/jre/lib/rt.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/sunjce_provider.jar:/home/wololock/workspace/idea/ast-gands/target/classes:/home/wololock/.m2/repository/org/codehaus/groovy/groovy-all/2.3.7/groovy-all-2.3.7.jar:/opt/idea-IU-129.1525/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain ua.home.gag.usage.Example
5
asdasd
1

Process finished with exit code 0

您可以在以下网站找到更多示例grails-core存储库 - 转到https://github.com/grails/grails-core/,输入t并搜索ASTTransformation