如何定义可将类型传播到内部块的类型感知型groovy DSL?

时间:2019-08-29 12:37:09

标签: 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()

文档here建议仅通过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的第二个参数以及其他各种排列,但无济于事:

  • DelegatesTo(MessageX)
  • DelegatesTo(value = MessageX, type = "T")
  • DelegatesTo(value = MessageX, type = "MessageX<T>")
  • DelegatesTo(type = "MessageX<T>")

有人可以帮忙吗?

0 个答案:

没有答案