当前位置: 首页 > 知识库问答 >
问题:

Groovy DSL:如何让两个委托类处理DSLScript的不同部分?

孟华晖
2023-03-14

假设我有这样的DSL

setup {name = "aDSLScript"}
println "this is common groovy code"
doStuff {println "I'm doing dsl stuff"}

通常会有一个实现方法“设置”和“doStuff”的委托类。此外,还可以编写要执行的通用Groovy代码(println...)。

我正在寻找的是一种分两步执行的方法。在第一步中,只应处理setup方法(无论是println还是println)。第二步处理其他部件。

目前,我有两个授权课程。一个实现“setup”,另一个实现“doStuff”。当然,两者都执行println语句。

共有2个答案

何修能
2023-03-14

我找到了一种拆分DSL脚本执行的方法。我使用CompilationCustomizer从AST中删除除doFirst{}之外的所有语句。所以第一次运行只会执行doFirst。第二次运行执行其他所有操作。以下是一些代码:

class DoFirstProcessor {
    def doFirst(Closure c) {
        c()
    }
}

class TheRestProcessor {
    def doStuff(Closure c) {
        c()
    }

    def methodMissing(String name, args) {
        //nothing to do
    }
}

def dsl = "
println 'this is text that will not be printed out in first line!'

doFirst { println 'First things first: e.g. setting up environment' }

doStuff { println 'doing some stuff now' }

println 'That is it!'
"


class HighlanderCustomizer extends CompilationCustomizer {
    def methodName

    HighlanderCustomizer(def methodName) {
        super(CompilePhase.SEMANTIC_ANALYSIS)
        this.methodName = methodName
    }

    @Override
    void call(SourceUnit sourceUnit, GeneratorContext generatorContext, ClassNode classNode) throws CompilationFailedException {
        def methods = classNode.getMethods()
        methods.each { MethodNode m ->
            m.code.each { Statement st ->
                if (!(st instanceof BlockStatement)) {
                    return
                }
                def removeStmts = []
                st.statements.each { Statement bst ->
                    if (bst instanceof ExpressionStatement) {
                        def ex = bst.expression
                        if (ex instanceof MethodCallExpression) {
                            if (!ex.methodAsString.equals(methodName)) {
                                removeStmts << bst
                            }
                        } else {
                            removeStmts << bst
                        }
                    } else {
                        removeStmts << bst
                    }
                }
                st.statements.removeAll(removeStmts)
            }
        }
    }
}

def cc = new CompilerConfiguration()
cc.addCompilationCustomizers new HighlanderCustomizer("doFirst")
cc.scriptBaseClass = DelegatingScript.class.name

def doFirstShell = new GroovyShell(new Binding(), cc)
def doFirstScript = doFirstShell.parse dsl
doFirstScript.setDelegate new DoFirstProcessor()
doFirstScript.run()

cc.compilationCustomizers.clear()
def shell = new GroovyShell(new Binding(), cc)
def script = shell.parse dsl
script.setDelegate new TheRestProcessor()
script.run()

我做了另一个变化,我在一个步骤中执行DSL。查看我的博客帖子:http://hackserei.metacode.de/?p=247

浦出野
2023-03-14

您可以创建一个类来拦截脚本中的方法调用,并让它协调以下方法调用。我是通过反射来完成的,但如果您愿意,您可以采用声明式。这些是模型和脚本类:

class FirstDelegate {
  def setup(closure) { "firstDelegate.setup" }
}

class SecondDelegate {
  def doStuff(closure) { "secondDelegate.doStuff" }
}


class MethodInterceptor {
  def invokedMethods = []

  def methodMissing(String method, args) {
    invokedMethods << [method: method, args: args]
  }

  def delegate() {
    def lookupCalls = { instance ->
      def invokes = instance.metaClass.methods.findResults { method ->
        invokedMethods.findResult { invocation ->
          invocation.method == method.name ? 
              [method: method, invocation: invocation] : null 
        }
      }

      invokes.collect { invoked ->
        invoked.method.invoke(instance, invoked.invocation.args) 
      }
    }

    return lookupCalls(new FirstDelegate()) + lookupCalls(new SecondDelegate())
  }
}

下面是脚本和断言:

import org.codehaus.groovy.control.CompilerConfiguration

def dsl = '''
setup {name = "aDSLScript"}
println "this is common groovy code"
doStuff {println "Ima doing dsl stuff"}
'''


def compiler = new CompilerConfiguration()
compiler.scriptBaseClass = DelegatingScript.class.name

def shell = new GroovyShell(this.class.classLoader, new Binding(), compiler)

script = shell.parse dsl

interceptor = new MethodInterceptor()

script.setDelegate interceptor

script.run()

assert interceptor.invokedMethods*.method == [ 'setup', 'doStuff' ]

assert interceptor.delegate() == 
    ['firstDelegate.setup', 'secondDelegate.doStuff']

注意,我没有费心拦截println调用,这是一种默认的groovymethods,因此处理起来有点麻烦。

让类Method odInterceptor实现方法委托()也不是一个好主意,因为这允许用户定义的脚本调用它。

 类似资料:
  • 我有一个名为< code > globalcexceptionhandler 的类,它由< code>ControllerAdvice注释。它正确处理所有< code > NoHandlerFoundExceptions 。我向添加了一个新方法来处理< code>InternalError异常,但它不处理此类异常;因此,我仍然收到< code>HTTP状态500。 基于此链接,< code>500

  • 我需要创建三个类:客户、产品和订单。我希望我的orders类接受来自客户和产品的实例(即,能够让一个订单包含关于客户和他们正在订购的产品的信息)。在JavaScript中设置这一点的最佳方法是什么?我最初想让我的customer和products类扩展order类,但我是否能够让order同时扩展customer和products类?

  • JVM如何进行内部管理 如果在两个不同的用户定义的类装入器中装入相同版本的类装入? 2.如果在两个不同的用户定义类加载程序(相同的类名和包)中加载不同版本的类。如果JVM加载了将在我的应用程序中引用的两个类。 我们知道JVM在类、包 我想知道JVM内部如何处理这些情况。

  • 问题内容: 我正在子类化中添加一些功能,例如双击缩放和用于图库目的的image属性。但是为了做图像部分,我的子类必须是自己的委托并实现。 但是,当有人使用我的滚动视图子类时,他们可能还希望获得委托通知,以查看或了解您的情况。 在Swift中,我如何同时获得这两个? 问题答案: 这是此模式的Swift版本: 尽管已在Swift中禁用,但我们仍然可以使用

  • 我正在开发一个仅支持iPhone的iOS 8应用程序。我想只支持iPhone 4S的纵向模式设备。 我有一个图形设计,它采用全屏内容。我使用自动布局来处理视图,并使其适用于所有屏幕。但是,我使用一个小资产来管理从iPhone4S工作的设计。当我在iPhone6S/6看到它时,内容非常小,大量额外的空间被浪费了。 当设备分辨率变大时,我想增加字体大小、图像大小。这个应用程序中有很多屏幕。 例如,我在