RudolphEst RudolphEst - 1 month ago 14
Groovy Question

Use groovy categories to add dynamic properties

Expanding on this blog post, I am trying to use a category to create a simple DSL for use with the

javax.measure
(JSR-275) classes (similar to TimeCategory for time intervals)

However, I do not want to add boilerplate code for each of the possible available methods (getMeter, getMilliMeter, getKelvin, getSecond etc.). I thought overriding the
getProperty(String)
method would work, but alas, it looks like the
getProperty
method defined in the category is not used when accessing the property directly.

Here is some simplified code to demonstrate:
import javax.measure.quantity.Length;
import javax.measure.unit.Unit;
import javax.measure.Measure;

@Category(Number)
class LengthCategory {
public Measure<BigDecimal, Length> getProperty(String unit){
return Measure.valueOf(this,Unit.valueOf(unit));
}
}

use(LengthCategory){
println 3.getProperty("m") // this works
println 3.m // this reports a non-exisiting property
prinlln 3.'m' // as does this
}


Assuming other methods of dynamically adding properties to a runtime object (e.g.
Expando
, subclassing
GroovyInterceptible
, mixins and other metaclass manipulations) is not viable and I would really rather not have to manually code getters for every possible unit and SI prefix combination. There are obviously other ways to go about creating a DSL for measurements, but I would still like to understand why this method would not work.

Could someone explain why the
getProperty
method of the category does not override
.propertyName
usage? I am obviously missing something important about the resolution of property names using the metaclass during runtime.

Answer

I don't know why getProperty doesn't work on categories. But you can define a get method on them that does basically the same (i think). This works:

@Category(Number)
class LengthCategory {      
    def get(String unit) {
        "$this $unit"
    }
}

use (LengthCategory) {
    println 3.m   // 3 m
    println 3.'m' // 3 m
}