[groovy-user] Problem with the subscript operators

16 views
Skip to first unread message

Ondřej Čada

unread,
Mar 10, 2012, 3:10:33 PM3/10/12
to us...@groovy.codehaus.org
Hello there,

I need to make my own object to interpret [] as Map does, namely, that

def foo=new Foo()
foo['a']='b'
println "${foo['a']}" // prints out 'b'

behaves same way as if Foo was a Map. Based on documentation, namely the "getAt() and setAt() for the subscript operator (e.g. foo[1])" of groovy.codehaus.org/Operators, I've thought that getAt and setAt are the key, but it seems they are not:

===
186 /tmp> <Test.groovy
class Foo {
private def storage=[:]
def getAt(foo) { println "getAt $foo"; storage[foo] }
void setAt(foo,bar) { println "setAt $foo $bar"; storage[foo]=bar }
}
class Test {
static void main(av) {
def foo=new Foo()
foo['a']='b'
println "${foo['a']}"
}
}
187 /tmp> groovyc -v
Groovy compiler version 1.8.5
Copyright 2003-2011 The Codehaus. http://groovy.codehaus.org/

188 /tmp> groovyc Test.groovy && java Test
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: a for class: Foo
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:50)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoMetaMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:310)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.call(PogoMetaMethodSite.java:64)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at Test.main(Test.groovy:9)
189 /tmp>
===

What am I missing here? Note that I really don't want to override get/setProperty -- expressions "foo[bar]" and "foo.bar" should differ for my class; the former should access the internal Map, the latter should simply return the bar property if there's one.

Thanks,
---
Ondra Čada
OCSoftware: o...@ocs.cz http://www.ocs.cz
private on...@ocs.cz http://www.ocs.cz/oc


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


Ondřej Čada

unread,
Mar 10, 2012, 9:57:50 PM3/10/12
to us...@groovy.codehaus.org
Well these inconsistencies will drive me mad. Are the following differences betw. method defined in class, added through a category and added through metaClass the intended behaviour? Shouldn't they all behave the same way?

===
24 /tmp> <Test.groovy
class FooWorksAsMap { // class names padded to be of same length as LinkedHashMap
def getProperty(String foo) { "OK:FooWorksAsMap.$foo" }
}
class BarWorksAsMap {}
class BaxWorksAsMap {}
@Category(BarWorksAsMap) class C {
def getProperty(String foo) { "OK:BarWorksAsMap.$foo" }
}
class Test {
static def main(av) {
BarWorksAsMap.mixin C
BaxWorksAsMap.metaClass.getProperty={ foo -> "OK:BaxWorksAsMap.$foo" }
def a=new FooWorksAsMap(),b=new BarWorksAsMap(),c=new BaxWorksAsMap(),d=[foo:'OK:LinkedHashMap.foo', class:'OK:LinkedHashMap.class']
for (def prop in ['foo','class']) {
for (def f in [a,b,c,d]) {
def op="${f.getClass().getSimpleName()}.$prop"
try { println "$op -> "+f."$prop" } catch (Throwable t) { println "$op failed -> $t" }
}
println ''
}
}
}
25 /tmp> groovyc Test.groovy && java Test
FooWorksAsMap.foo -> OK:FooWorksAsMap.foo
BarWorksAsMap.foo failed -> groovy.lang.MissingPropertyException: No such property: foo for class: BarWorksAsMap
BaxWorksAsMap.foo -> OK:BaxWorksAsMap.foo
LinkedHashMap.foo -> OK:LinkedHashMap.foo

FooWorksAsMap.class -> OK:FooWorksAsMap.class
BarWorksAsMap.class -> class BarWorksAsMap
BaxWorksAsMap.class -> class BaxWorksAsMap
LinkedHashMap.class -> OK:LinkedHashMap.class

26 /tmp>
===

Ať se Vám vše daří,

Paul King

unread,
Mar 11, 2012, 12:00:12 AM3/11/12
to us...@groovy.codehaus.org

Apologies but that wiki page had a typo; it should (and now does) read putAt not setAt.

Your example should look something like this:

def foo = new Foo()


foo['a'] = 'b'
println "${foo['a']}"

println foo.dump()

class Foo {
private storage = [:]
def getAt(String foo) { println "getAt $foo"; storage[foo] }
void putAt(String foo, bar) { println "putAt $foo $bar"; storage[foo] = bar }
}

Cheers, Paul.

On 11/03/2012 6:10 AM, Ondřej Čada wrote:
> Hello there,
>
> I need to make my own object to interpret [] as Map does, namely, that
>
> def foo=new Foo()
> foo['a']='b'
> println "${foo['a']}" // prints out 'b'
>
> behaves same way as if Foo was a Map. Based on documentation, namely the "getAt() and setAt() for the subscript operator (e.g. foo[1])" of groovy.codehaus.org/Operators, I've thought that getAt and setAt are the key, but it seems they are not:
>
> ===
> 186 /tmp> <Test.groovy
> class Foo {
> private def storage=[:]
> def getAt(foo) { println "getAt $foo"; storage[foo] }
> void setAt(foo,bar) { println "setAt $foo $bar"; storage[foo]=bar }
> }
> class Test {
> static void main(av) {
> def foo=new Foo()
> foo['a']='b'
> println "${foo['a']}"
> }
> }
> 187 /tmp> groovyc -v
> Groovy compiler version 1.8.5
> Copyright 2003-2011 The Codehaus. http://groovy.codehaus.org/
>

> 188 /tmp> groovyc Test.groovy&& java Test

Paul King

unread,
Mar 11, 2012, 5:13:39 AM3/11/12
to us...@groovy.codehaus.org

I would raise a jira for these inconsistencies too.
There is a possibility that the MissingPropertyException is
a slight variation on the earlier bug you found but we can
always close both issues if the fix solves both problems.
Here is a very slightly tweaked version of your script:

class FooWorksAsMap { // class names the same length as LinkedHashMap to make output pretty


def getProperty(String foo) { "OK:FooWorksAsMap.$foo" }
}
class BarWorksAsMap {}
class BaxWorksAsMap {}
@Category(BarWorksAsMap) class C {
def getProperty(String foo) { "OK:BarWorksAsMap.$foo" }
}

BarWorksAsMap.mixin C
BaxWorksAsMap.metaClass.getProperty = { foo -> "OK:BaxWorksAsMap.$foo" }
def maps = [new FooWorksAsMap(), new BarWorksAsMap(), new BaxWorksAsMap(),


[foo:'OK:LinkedHashMap.foo', class:'OK:LinkedHashMap.class']]
for (def prop in ['foo','class']) {

for (def m in maps) {
def op = "${m.getClass().getSimpleName()}.$prop"
try { println "$op -> " + m."$prop" }
catch (t) { println "$op -> FAIL:$t" }
}
}

Cheers, Paul.

On 11/03/2012 12:57 PM, Ondřej Čada wrote:
> Well these inconsistencies will drive me mad. Are the following differences betw. method defined in class, added through a category and added through metaClass the intended behaviour? Shouldn't they all behave the same way?
>
> ===
> 24 /tmp> <Test.groovy
> class FooWorksAsMap { // class names padded to be of same length as LinkedHashMap
> def getProperty(String foo) { "OK:FooWorksAsMap.$foo" }
> }
> class BarWorksAsMap {}
> class BaxWorksAsMap {}
> @Category(BarWorksAsMap) class C {
> def getProperty(String foo) { "OK:BarWorksAsMap.$foo" }
> }
> class Test {
> static def main(av) {
> BarWorksAsMap.mixin C
> BaxWorksAsMap.metaClass.getProperty={ foo -> "OK:BaxWorksAsMap.$foo" }
> def a=new FooWorksAsMap(),b=new BarWorksAsMap(),c=new BaxWorksAsMap(),d=[foo:'OK:LinkedHashMap.foo', class:'OK:LinkedHashMap.class']
> for (def prop in ['foo','class']) {
> for (def f in [a,b,c,d]) {
> def op="${f.getClass().getSimpleName()}.$prop"
> try { println "$op -> "+f."$prop" } catch (Throwable t) { println "$op failed -> $t" }
> }
> println ''
> }
> }
> }

> 25 /tmp> groovyc Test.groovy&& java Test

>> 188 /tmp> groovyc Test.groovy&& java Test

Ondřej Čada

unread,
Mar 11, 2012, 10:30:37 AM3/11/12
to us...@groovy.codehaus.org, Paul King
Paul,

On Mar 11, 2012, at 10:13 AM, Paul King wrote:
> I would raise a jira for these inconsistencies too. ...

OK, thanks a lot, I will.

Meantime I've bumped into another behaviour that seems rather strange to me -- looks like a generic class-level propertyMissing catches calls of inherited static _methods_? Is that another jira stuff, or do I as a groovy newbie overlook something obvious?

See please the marked last printout below. It seems to happen with any inherited static method, regardless its signature.

===
61 /tmp> <Test.groovy
class Foo {
static def inheritedStaticMethod() {}
}
class Test extends Foo {
static def main(av) {
// need to catch all class-level missing properties
Object.metaClass.static.propertyMissing={ println "MISSING $it"; "(${delegate.simpleName}.$it OK)" }
// check it works properly -- it does indeed
println "${Object.foo} ${Foo.foo} ${Test.foo} ${String.foo}"
// this does not seem quite right though -- propertyMissing catches a method call!
inheritedStaticMethod()
}
}
62 /tmp> groovyc Test.groovy && java Test
MISSING foo
MISSING foo
MISSING foo
MISSING foo
(Object.foo OK) (Foo.foo OK) (Test.foo OK) (String.foo OK)
MISSING inheritedStaticMethod <=====================================
63 /tmp>
===

Thanks and best,

Paul King

unread,
Mar 11, 2012, 4:54:45 PM3/11/12
to us...@groovy.codehaus.org

This is kind of a side effect of how priorities are given to method selection.
We currently look for a closure property called "inheritedStaticMethod" earlier
than finding a real inherited static method. As a side effect of looking for
the closure, the possibility of a MissingPropertyException arises but our logic
will ignore such an exception if it is thrown - it is just that you have a
println in your handler that you even see it. Perhaps putting a println also
in "inheritedStaticMethod" will make things a little clearer. Having said
that, we could potentially avoid calling the handler in this case altogether.
I think it is worth a Jira to investigate whether a change is possible even if
we eventually decide that the current behaviour can't be changed.

Cheers, Paul.

On 12/03/2012 12:30 AM, Ondřej Čada wrote:
> Paul,
>
> On Mar 11, 2012, at 10:13 AM, Paul King wrote:
>> I would raise a jira for these inconsistencies too. ...
>
> OK, thanks a lot, I will.
>
> Meantime I've bumped into another behaviour that seems rather strange to me -- looks like a generic class-level propertyMissing catches calls of inherited static _methods_? Is that another jira stuff, or do I as a groovy newbie overlook something obvious?
>
> See please the marked last printout below. It seems to happen with any inherited static method, regardless its signature.
>
> ===
> 61 /tmp> <Test.groovy
> class Foo {
> static def inheritedStaticMethod() {}
> }
> class Test extends Foo {
> static def main(av) {
> // need to catch all class-level missing properties
> Object.metaClass.static.propertyMissing={ println "MISSING $it"; "(${delegate.simpleName}.$it OK)" }
> // check it works properly -- it does indeed
> println "${Object.foo} ${Foo.foo} ${Test.foo} ${String.foo}"
> // this does not seem quite right though -- propertyMissing catches a method call!
> inheritedStaticMethod()
> }
> }

> 62 /tmp> groovyc Test.groovy&& java Test

Ondřej Čada

unread,
Mar 11, 2012, 5:29:31 PM3/11/12
to us...@groovy.codehaus.org, Paul King
Paul,

On Mar 11, 2012, at 9:54 PM, Paul King wrote:

> This is kind of a side effect of how priorities are given to method selection.
> We currently look for a closure property called "inheritedStaticMethod" earlier
> than finding a real inherited static method. As a side effect of looking for
> the closure, the possibility of a MissingPropertyException arises but our logic
> will ignore such an exception if it is thrown

It would not ignore other exceptions though. Namely, my propertyMissing code searches a list of valid dynamic properties, and if none is found, it throws. If this happens, the static methods is not called; instead, my application crashes on uncatched exception.

Here's a code sample to show my problem:

===


class Foo {
static def inheritedStaticMethod() {

println "Allright, Kilroy's here"


}
}
class Test extends Foo { static def main(av) {
// need to catch all class-level missing properties
Object.metaClass.static.propertyMissing={

// simulated 'found valid dynamic property' case
if (it.startsWith('f')) return "(${delegate.simpleName}.$it OK)"
// oops, we did not find valid d.p, so we
throw new Exception("Oops, no valid dynamic property for ${delegate.simpleName}.$it")


}
println "${Object.foo} ${Foo.foo} ${Test.foo} ${String.foo}"

inheritedStaticMethod()
println "Never get here, never see the 'Allright' printout :("
}}
===

Well of course, thanks to you now the fix is self-evident -- instead of "new Exception" I'll throw "new MissingPropertyException" (which happens to be much cleaner anyway) and all is well :)

Incidentally, there's another quirk I've bumped into: I've tried your concise way with the implicit Test class and main method. Never mind I forgot it can't work since the implicit class would not extend Foo; what's weird is that the "${Test.foo}" dynamic property stopped working?!? :-O

> - it is just that you have a
> println in your handler that you even see it. Perhaps putting a println also
> in "inheritedStaticMethod" will make things a little clearer. Having said
> that, we could potentially avoid calling the handler in this case altogether.

Since I've found it works excellently with the MissingPropertyException, I don't really mind. But...

> I think it is worth a Jira to investigate whether a change is possible even if
> we eventually decide that the current behaviour can't be changed.

... OK, I'll add another one, pointing out that in my opinion, quite sufficient way of fixing the problem would be pointing out in the documentation (a) that this happens, (b) that MissingPropertyException gets silently caught.

(Well actually perhaps this is already documented somewhere -- but if it is, I haven't bumped into that.)

Thanks a lot and all the best,

Reply all
Reply to author
Forward
0 new messages