WILLIAM WOODMAN
WILLIAM WOODMAN

Reputation: 1231

What is the static version of propertyMissing method in Groovy?

ok - tried looking /reading and not sure i have an answer to this.

I have a Utility class which wraps a static ConcurrentLinkedQueue internally.

The utility class itself adds some static methods - i dont expect to call new to create an instance of the Utility.

I want to intercept the getProperty calls the utility class - and implement these internally in the class definition

I can achieve this by adding the following to the utility classes metaclass, before i use it

UnitOfMeasure.metaClass.static.propertyMissing = {name -> println "accessed prop called $name"}

println UnitOfMeasure.'Each'

however what i want to do is declare the interception in the class definition itself. i tried this in the class definition - but it never seems to get called

static def propertyMissing (receiver, String propName) {
    println "prop $propName, saught"
}

i also tried

static def getProperty (String prop) { println "accessed $prop"}

but this isnt called either.

So other than adding to metaClass in my code/script before i use, how can declare the in the utility class that want to capture property accesses

the actual class i have looks like this at present

class UnitOfMeasure {
    static ConcurrentLinkedQueue UoMList = new ConcurrentLinkedQueue(["Each", "Per Month", "Days", "Months", "Years", "Hours", "Minutes", "Seconds" ])

    String uom

    UnitOfMeasure () {
        if (!UoMList.contains(this) )
            UoMList << this
    }

    static list () {
        UoMList.toArray()
    }

    static getAt (index) {
        def value = null
        if (index in 0..(UoMList.size() -1))
            value = UoMList[index]
        else if (index instanceof String) {
            Closure matchClosure = {it.toUpperCase().contains(index.toUpperCase())}
            def position = UoMList.findIndexOf (matchClosure)
            if (position != -1)
                value = UoMList[position]
        }
        value
    }

     static def propertyMissing (receiver, String propName) {
        println "prop $propName, saught"
    }


    //expects either a String or your own closure, with String will do case insensitive find
    static find (match) {
        Closure matchClosure
        if (match instanceof Closure)
            matchClosure = match
        if (match instanceof String) {
            matchClosure = {it.toUpperCase().contains(match.toUpperCase())}
        }
        def inlist = UoMList.find (matchClosure)
    }

    static findWithIndex (match) {
        Closure matchClosure
        if (match instanceof Closure)
            matchClosure = match
        else if (match instanceof String) {
            matchClosure = {it.toUpperCase().contains(match.toUpperCase())}
        }
        def position = UoMList.findIndexOf (matchClosure)
        position != -1  ? [UoMList[position], position] : ["Not In List", -1]
    }
}

i'd appreciate the secret of doing this for a static utility class rather than instance level property interception, and doing it in class declaration - not by adding to metaClass before i make the calls.

just so you can see the actual class, and script that calls - i've attached these below

my script thats calling the class looks like this

println UnitOfMeasure.list()

def (uom, position) = UnitOfMeasure.findWithIndex ("Day")
println "$uom at postition $position"

// works UnitOfMeasure.metaClass.static.propertyMissing = {name -> println "accessed prop called $name"}
println UnitOfMeasure[4]
println UnitOfMeasure.'Per' 

which errors like this

[Each, Per Month, Days, Months, Years, Hours, Minutes, Seconds]
Days at postition 2
Years
Caught: groovy.lang.MissingPropertyException: No such property: Per for class: com.softwood.portfolio.UnitOfMeasure
Possible solutions: uom
groovy.lang.MissingPropertyException: No such property: Per for class: com.softwood.portfolio.UnitOfMeasure
Possible solutions: uom
    at com.softwood.scripts.UoMTest.run(UoMTest.groovy:12)

Upvotes: 2

Views: 325

Answers (1)

Szymon Stepniak
Szymon Stepniak

Reputation: 42184

Static version of propertyMissing method is called $static_propertyMissing:

static def $static_propertyMissing(String name) {
    // do something
}

This method gets invoked by MetaClassImpl at line 1002:

protected static final String STATIC_METHOD_MISSING = "$static_methodMissing";
protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing";

// ...

protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
    MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class) instance) : this;
    if (isGetter) {
        MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
        if (propertyMissing != null) {
            return propertyMissing.invoke(instance, new Object[]{propertyName});
        }
    } else {
        // .....
    }
    // ....
}

Example:

class Hello {
    static def $static_propertyMissing(String name) {
        println "Hello, $name!"
    }
}

Hello.World

Output:

Hello, World!

Upvotes: 4

Related Questions