首页 > 解决方案 > 如何定义可以将类型传播到内部块的类型感知 groovy DSL?

问题描述

我正在尝试实现一个 Groovy DSL,它在顶级块参数中采用类名,然后允许在内部块中针对该类的方法进行静态类型检查,而无需冗余地重新声明该类名。

(我使用的是 Groovy v2.5.6)

例如,给定这个类:

// A message data type to parse
@Canonical
class MessageX {
    int size
    boolean hasName

    // an optional field to read, depending on whether #hasName is true
    String name 
}

我希望能够在 DSL 中定义如下内容:

parserFor(MessageX) {
    readInt 'size'
    readBool 'hasName'

    // #hasName is a property of MessageX
    when { hasName }  { 
        readString 'name'
    }
}

实现这一点的尝试可能是:

import groovy.transform.Canonical
import groovy.transform.CompileStatic

@CompileStatic
@Canonical
class MessageX {
    boolean hasName
    String name
    int size
}

/** Generic message builder class for parsing messages */
@CompileStatic
class MessageBlock<T> {
    MessageBlock(Map<String, Object> values) {
        this.value = values
    }

    private final Map<String, Object> value

    void readString(String field) {
        // ...
    }
    void readInt(String field) {
        // ..
    }
    void readBool(String field) {
        // ...
    }
    /** Defines a condition */
    void when(@DelegatesTo(type = "T") Closure conditionBlock, @DelegatesTo(value = MessageBlock, type = "MessageBlock<T>") Closure bodyBlock) {
        // ...
    }
}


@CompileStatic
class Parser {
    static final <T> Closure<T> parserFor(Class<T> type, @DelegatesTo(value = MessageBlock, type = "MessageBlock<T>") Closure block) {
        println "parser get type $type.name"
        return {
            Map<String, Object> values = [:]
            MessageBlock<T> blockImpl = new MessageBlock<T>(values);
            block.delegate = blockImpl
            block()
            return type.newInstance(values)
        }
    }

    static void build() {

        // Define a parser using our DSL
        Closure<MessageX> messageXParser = parserFor(MessageX) {
            readBool 'hasName'

            when { hasName }  {
                readString 'name'
            }
        }

        messageXParser()
    }
}

Parser.build()

此处的文档建议仅type = "MessageBlock<T>"使用DelegatesTo.

但是,这在编译时给了我空指针异常。

Caught: BUG! exception in phase 'instruction selection' in source unit 'test.groovy' unexpected NullpointerException
BUG! exception in phase 'instruction selection' in source unit 'test.groovy' unexpected NullpointerException
Caused by: java.lang.NullPointerException

在上面的例子中,我也有value = MessageBlock标签,它至少避免了 NPE - 但我仍然得到一个错误:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
test.groovy: 57: [Static type checking] - The variable [hasName] is undeclared.
 @ line 57, column 20.
               when { hasName }  {
                      ^

我还没有找到一种方法来让#when方法的闭包块正确地委托给MessageX类。我已经为第二个参数#parserFor和其他各种排列尝试了这些注释,但无济于事:

任何人都可以帮忙吗?

标签: groovydsl

解决方案


推荐阅读