Sheel
Sheel

Reputation: 1030

How to get name of callee closure from methodMissing, Groovy Meta Programming

I am trying to use Groovy's Meta Programming, to dynamically read data from closure. Below is the example:

class MyClass {

    static def metadata1 = {
        metadataA(key1: 'value1', key2: "value2")
        metadataB(key1: ['value1', 'value2'], key2: 78978)
    }

    static def metadata2 = {
        metadataA(key1: 'value11', key2: "value21")
        metadataB(key1: ['value11', 'value21'], key2: 78958)
    }

    static void main(def args) {
        new MyClass().setup("metadata1")
        new MyClass().setup("metadata2")
    }

    def setup(fieldName) {
        try {
            Field metaField = this.class.getDeclaredField(fieldName)
            if (metaField != null) {
                metaField.setAccessible(true)
                def metaFieldVal = metaField.get(null)
                if (metaFieldVal != null) {
                    metaFieldVal.delegate = this
                    metaFieldVal() // It will call closure metadata1 or metadata2
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace()
        }
    }

    def methodMissing(String key, args) {
        println(key) // metadataA
        println(args) // [[key1:value1, key2:value2]]

        //println(getNameofCalleeClosure()) //metadata1
    }
} 

In overridden methodMissing I am not able to identify field name of callee closure, here in case i.e. metadata1, metadata2. Is there any way in Groovy to get callee closure name for identification from where it is called?

Upvotes: 1

Views: 79

Answers (1)

Szymon Stepniak
Szymon Stepniak

Reputation: 42272

There is no such thing like name of caller closure. In your example, in method setup(fieldName) you search for a closure stored as a class field, and when you find the closure you execute it with metaFieldVal(). There is no connection between field name metadata1 and the closure executed as metaFieldVal(). Actually this setup(fieldName) knows that this closure is stored as a class field, but closure itself is not aware of it.

If you want to get metadata1 or metadata2 field names inside methodMissing() you have to simply pass it as this non-existing method parameter. Something like this:

class MyClass {

    static def metadata1 = {
        metadataA("metadata1", [key1: 'value1', key2: "value2"])
        metadataB("metadata1", [key1: ['value1', 'value2'], key2: 78978])
    }

    static def metadata2 = {
        metadataA("metadata2", [key1: 'value11', key2: "value21"])
        metadataB("metadata2", [key1: ['value11', 'value21'], key2: 78958])
    }

    static void main(def args) {
        new MyClass().setup("metadata1")
        new MyClass().setup("metadata2")
    }

    def setup(fieldName) {
        try {
            Field metaField = this.class.getDeclaredField(fieldName)
            if (metaField != null) {
                metaField.setAccessible(true)
                def metaFieldVal = metaField.get(null)
                if (metaFieldVal != null) {
                    metaFieldVal.delegate = this
                    metaFieldVal() // It will call closure metadata1 or metadata2
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace()
        }
    }

    def methodMissing(String key, args) {
        println(key) // metadataA
        println(args) // [metadata1, [key1:value1, key2:value2]]

    }
} 

It is also worth mentioning that executing missing method like this:

static def metadata1 = {
    metadataA(this, [key1: 'value1', key2: "value2"])
    metadataB(this, [key1: ['value1', 'value2'], key2: 78978])
}

produces:

[class MyClass, [key1:value1, key2:value2]]

and passing a field metadata1 like this:

static def metadata1 = {
    metadataA(metadata1, [key1: 'value1', key2: "value2"])
    metadataB(metadata1, [key1: ['value1', 'value2'], key2: 78978])
}

will produce:

[MyClass$__clinit__closure1@50d0686, [key1:value1, key2:value2]]

because closure has no name.

Upvotes: 1

Related Questions