Reputation:
Using org.as3commons.reflect I can look-up the class name, and instantiate a class at runtime. I also have (non-working) code which invokes a method. However, I really want to set a property value. I'm not sure if properties are realized as methods internally in Flex.
I have a Metadata class which stores 3 pieces of information: name, value, and type (all are strings). I want to be able to loop through an Array of Metadata objects and set the corresponding properties on the instantiated class.
package com.acme.reporting.builders
{
import com.acme.reporting.model.Metadata;
import mx.core.UIComponent;
import org.as3commons.reflect.ClassUtils;
import org.as3commons.reflect.MethodInvoker;
public class UIComponentBuilder implements IUIComponentBuilder
{
public function build(metadata:Array):UIComponent
{
var typeClass:Class = ClassUtils.forName(getTypeName(metadata));
var result:* = ClassUtils.newInstance(typeClass);
for each (var m:Metadata in metadata)
{
if (m.name == "type")
continue;
// Attempting to invoke as method,
// would really like the property though
var methodInvoker:MethodInvoker = new MethodInvoker();
methodInvoker.target = result;
methodInvoker.method = m.name;
methodInvoker.arguments = [m.value];
var returnValue:* = methodInvoker.invoke(); // Fails!
}
return result;
}
private static function getTypeName(metadata:Array):String
{
if (metadata == null || metadata.length == 0)
throw new ArgumentError("metadata is null or empty");
var typeName:String;
// Type is usually the first entry
if (metadata.length > 1 && metadata[0] != null && metadata[0].name == "type")
{
typeName = metadata[0].value;
}
else
{
var typeMetadata:Array = metadata.filter(
function(element:*, index:int, arr:Array):Boolean
{
return element.name == "type";
}
);
if (typeMetadata == null || typeMetadata.length != 1)
throw new ArgumentError("type entry not found in metadata");
typeName = typeMetadata[0].value;
}
if (typeName == null || typeName.length == 0)
throw new Error("typeName is null or blank");
return typeName;
}
}
}
Here's some usage code:
var metadata:Array = new Array();
metadata[0] = new Metadata("type", "mx.controls.Text", null);
metadata[1] = new Metadata("text", "Hello World!", null);
metadata[2] = new Metadata("x", "77", null);
metadata[3] = new Metadata("y", "593", null);
this.addChild(new UIComponentBuilder().build(metadata));
I realize that I have to declare a dummy variable of the type I was to instantiate, or use the -inculde compiler directive. An unfortunate drawback of Flex.
Also, right now there's code to account for typecasting the value to it's specified type.
Upvotes: 2
Views: 2174
Reputation: 74949
Dynamic execution in AS3 is much simpler than in other languages. This code:
var methodInvoker:MethodInvoker = new MethodInvoker();
methodInvoker.target = result;
methodInvoker.method = m.name;
methodInvoker.arguments = [m.value];
var returnValue:* = methodInvoker.invoke(); // Fails!
can be simplified to this:
var returnValue:* = result[method](m.value);
EDIT:
Since it's a property, it would be done like this:
result[method] = m.value;
and there is no return value (well, you can call the getter again but it should just return m.value
unless the setter/getter do something funky.
Upvotes: 1