Puzzling Proxy Problem: Solved
The short version is what AS3 operator triggers a call to the isAttribute() method of a Proxy? The answer, none. isAttribute() is a utility function provided by Proxy that can be called to determine if the name passed to a function was specified as an attribute. A couple of Proxy's methods can't be called in an attribute context, as noted below. I've created a sample Proxy class called MyProxy to help show what's going on:
package com.neophi.test { import flash.utils.Proxy; import flash.utils.flash_proxy; public dynamic class MyProxy extends Proxy { override flash_proxy function callProperty(name:*, ... rest):* { trace("callProperty", name, rest); flash_proxy::isAttribute(name); return null; } override flash_proxy function deleteProperty(name:*):Boolean { trace("deleteProperty", name); flash_proxy::isAttribute(name); return false; } override flash_proxy function getDescendants(name:*):* { trace("getDescendants", name); flash_proxy::isAttribute(name); return null; } override flash_proxy function getProperty(name:*):* { trace("getProperty", name); flash_proxy::isAttribute(name); return null; } override flash_proxy function hasProperty(name:*):Boolean { trace("hasProperty", name); flash_proxy::isAttribute(name); return false; } // Don't override isAttribute(), it is a utility function // used by methods that specify a name:* parameter // to determine if the name argument was specified as an // attribute. It doesn't look like there is any other // way to determine if a name was specified as an // attribute besides calling Proxy's isAttribute() // implementation. override flash_proxy function isAttribute(name:*):Boolean { var result:Boolean = super.flash_proxy::isAttribute(name); trace("isAttribute", name, result); return result; } override flash_proxy function nextName(index:int):String { trace("nextName", index); return null; } override flash_proxy function nextNameIndex(index:int):int { trace("nextNameIndex", index); return (1 - index); } override flash_proxy function nextValue(index:int):* { trace("nextValue", index); return null; } override flash_proxy function setProperty(name:*, value:*):void { trace("setProperty", name, value); flash_proxy::isAttribute(name); } } }
I then exercise all of the methods (with and without namespaces) on MyProxy with this code:
private function go():void { namespace myNamespace = "com.neophi.test"; var myProxy:MyProxy = new MyProxy(); // callProperty() myProxy.foo(); // Compiler error: 1041: Attributes are not callable. // myProxy.@foo(); myProxy.myNamespace::foo(); // Compiler error: 1041: Attributes are not callable. // myProxy.@myNamespace::foo(); // deleteProperty(): uses isAttribute() delete myProxy.foo; delete myProxy.@foo; delete myProxy.myNamespace::foo; delete myProxy.@myNamespace::foo; // getDescendents(): uses isAttribute() myProxy..foo; myProxy..@foo; myProxy..myNamespace::foo; myProxy..@myNamespace::foo; // getProperty(): uses isAttribute() myProxy.foo; myProxy.@foo; myProxy.myNamespace::foo; myProxy.@myNamespace::foo; // hasProperty() "foo" in myProxy; // Compiler error: 1084: Syntax error: expecting identifier before foo. // @"foo" in myProxy; new QName(myNamespace, "foo") in myProxy; // Compiler error: 1084: Syntax error: expecting identifier before new. // @new QName(myNamespace, "foo") in myProxy; // nextName(): uses nextNameIndex() for (var string:String in myProxy) { trace(string); } // nextValue(): uses nextNameIndex() for each (var object:Object in myProxy) { trace(object); } // setProperty(): uses isAttribute() myProxy.foo = "bar"; myProxy.@foo = "bar"; myProxy.myNamespace::foo = "bar"; myProxy.@myNamespace::foo = "bar"; }
The output of running is below (whitespace added to match up with blocks above):
callProperty foo isAttribute foo false callProperty com.neophi.test::foo isAttribute com.neophi.test::foo false deleteProperty foo isAttribute foo false deleteProperty foo isAttribute foo true deleteProperty com.neophi.test::foo isAttribute com.neophi.test::foo false deleteProperty com.neophi.test::foo isAttribute com.neophi.test::foo true getDescendants foo isAttribute foo false getDescendants foo isAttribute foo true getDescendants com.neophi.test::foo isAttribute com.neophi.test::foo false getDescendants com.neophi.test::foo isAttribute com.neophi.test::foo true getProperty foo isAttribute foo false getProperty foo isAttribute foo true getProperty com.neophi.test::foo isAttribute com.neophi.test::foo false getProperty com.neophi.test::foo isAttribute com.neophi.test::foo true hasProperty foo isAttribute foo false hasProperty com.neophi.test::foo isAttribute com.neophi.test::foo false nextNameIndex 0 nextName 1 null nextNameIndex 1 nextNameIndex 0 nextValue 1 null nextNameIndex 1 setProperty foo bar isAttribute foo false setProperty foo bar isAttribute foo true setProperty com.neophi.test::foo bar isAttribute com.neophi.test::foo false setProperty com.neophi.test::foo bar isAttribute com.neophi.test::foo true
That then is a complete run down of all the Proxy methods and how to use them. I'd like to thank Jacob Wright for the pointer on how isAttribute() is used.
Comments
myProxy.myNamespace::myMethod()
Which really makes no sense. Proxy is nice in theory, but lacking in practice. I would really like to see something that didn't require subclassing, for example allowing you to declare a method called callProperty in the flash_proxy namespace on any class and having it work as the old __resolve.
Posted by: Theo | February 7, 2008 3:01 AM
Posted by: DanielR | February 7, 2008 8:35 AM
Posted by: Jacob Wright | February 15, 2008 4:31 PM
Posted by: DanielR | February 15, 2008 11:41 PM