The other thing about Proxy class, and why it is not so popular, is that it is rather difficult to fathom a problem that exactly needs a dynamic class with a controllable access to its dynamic properties and methods as a most appropriate solution. Every time I tried to use Proxy, I have ended up resorting to something else, simpler and more controllable.
However, let us not be discouraged. I like the idea of addressing the last array elements by [-1], [-2], etc indices in Python. Might be not a big feat, but it feels nice to use that rather than long and clumsy someArray[someArray.length - 1]. Lets see what we can do about it.
package
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
/**
* Pyaray the Tentacled Whisperer of Impossible Secrets.
*/
dynamic public class PyArray extends Proxy
{
private var data:Array;
public function PyArray(...args:Array)
{
if (args.length == 0)
{
data = new Array;
}
else if ((args.length == 1) && (args[0] is Array))
{
data = args[0];
}
else
{
data = args;
}
}
// This is a getter proxy to all the available Array
// elements and properties and, sometimes, methods.
override flash_proxy function getProperty(name:*):*
{
var anIndex:int = name;
// Handle the int indices of the Array elements.
if (anIndex == name)
{
// Handle the -1, -2, etc indexing.
if (anIndex < 0) anIndex += data.length;
if (anIndex >= data.length) return null;
if (anIndex < 0) return null;
return data[anIndex];
}
// Handle the existing public Array properties.
if (data.hasOwnProperty(name)) return data[name];
// Handle the Array methods addressed via ["member"] access.
try
{
if (data[name] is Function) return data[name];
else throw new Error;
}
catch (fail:Error)
{
trace("[PyArray] is unable to resolve property \"" + name + "\".");
}
return null;
}
// This will set either elements, or settable properties.
override flash_proxy function setProperty(name:*, value:*):void
{
var anIndex:int = name;
// Handle the int indices of the Array elements.
if (anIndex == name)
{
// Handle the -1, -2, etc indexing.
if (anIndex < 0) anIndex += data.length;
// In case the element index is out of range,
// the PyArray will extend its data Array.
// if (anIndex >= data.length) return;
if (anIndex < 0) return;
data[anIndex] = value;
return;
}
// Handle the existing (or dynamic) public Array properties.
try
{
data[name] = value;
}
catch (fail:Error)
{
trace("[PyArray] is unable to set property \"" + name + "\".");
}
return;
}
// This allows to delete PyArray elements with "delete" operator.
override flash_proxy function deleteProperty(name:*):Boolean
{
var anIndex:int = name;
// Handle the int indices of the Array elements.
if (anIndex == name)
{
// Handle the -1, -2, etc indexing.
if (anIndex < 0) anIndex += data.length;
if (anIndex >= data.length) return false;
if (anIndex < 0) return false;
data.splice(anIndex, 1);
return true;
}
// Handle the dynamic public Array properties.
try
{
delete data[name];
return true;
}
catch (fail:Error)
{
trace("[PyArray] is unable to delete the \"" + name + "\" property.");
}
return false;
}
// This proxies any attempt to call a method on PyArray directly to data Array, thus
// all Array methods (including "toString" method called through trace) are available.
override flash_proxy function callProperty(name:*, ...rest):*
{
try
{
return (data[name] as Function).apply(data, rest);
}
catch (fail:Error)
{
trace("[PyArray] is unable to resolve method \"" + name + "\".");
return null;
}
}
// This allows PyArray to handle for..in and for..each..in loops.
// The initial call starts with zero, so we need to do this +1 -1 magic
// in order for enumeration to work correctly. I'm not happy with this either.
override flash_proxy function nextNameIndex(index:int):int
{
if (index >= data.length) return 0;
else return index + 1;
}
// This method handles the for..in loop.
override flash_proxy function nextName(index:int):String
{
return (index - 1).toString();
}
// This method handles the for..each..in loop.
override flash_proxy function nextValue(index:int):*
{
return data[index - 1];
}
}
}