Puzzling Proxy Problem: Solved

Daniel Rinehart, 7 Feb 16:32

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.

  AddThis Social Bookmark Button