Ever since I’ve typed my first keystrokes writing in AS3, I never knew even after 6 years I’d still be discovering new things in this powerful language.

I’d like to share with you my most recent discovery, and how it could potentially open new doors to fasten your development time.

Firstly: Metadata tags. What are they?

Well, you may have stumble upon these built-in ones if you’ve been developing in Flex for quite some years.

//Specifies the properties of the SWF when it is launched.
[SWF(backgroundColor="0x000033",width="800",height="600")]

//Designates another class as the main-entry point, for
//preloading this current one.
[Frame(factoryClass="mx.managers.SystemManager")]

//Compiles an external resource (image) into the SWF.
[Embed(source="assets/images/background.jpg")]

//Makes a property available for Flex data-bindings
[Bindable]

//Makes a property exposed in the IDE's Component inspector panel,
//with the option to set certain range values.
[Inspectable]

Some of those have a slew of attributes that can be set (more info available on the livedocs here).

For the longest time, I believed that these Metadata were only available at compile time – and so I assumed we were limited to those offered by Adobe (and Macromedia, of course).

However, that is not the case. The Metadata tags are very much available at runtime as well. See the following example:

package {
  import flash.display.Sprite;
  import flash.utils.describeType;

  /**
   * An example of enumerating a class's Metadata information.
   * @author Pierre Chamberlain
   */
  [CustomMeta]
  public class MetadataTest extends Sprite{
    public function MetadataTest() {
      super();

      var xDesc:XML =    describeType(Object(this).constructor);
      var xMetas:XMLList =  xDesc.factory.metadata;	
      trace(xMetas);
    }
  }
}

This gives me the following trace output:

<metadata name="CustomMeta"/>
<metadata name="__go_to_ctor_definition_help">
  <arg key="file" value="C:\...\src\MetadataTest.as" />
  <arg key="pos" value="214" />
</metadata>
<metadata name="__go_to_definition_help">
  <arg key="file" value="C:\...\src\MetadataTest.as" />
  <arg key="pos" value="162" />
</metadata>

So from what you can see in the output, there is a couple of prefabricated Metadata tags – named “__go_to_ctor_definition_help” and “__go_to_definition_help”. At the time of this writing, I will only assume that they serve some purpose for the debugger to track which line / character the given class starts at. That’s just my guess.

In case you’re wondering, YES you can also call describeType( this ) on the instance itself, but here I’m fetching the Type Description on the Class of the instance instead. The resulting XML definitions aren’t dramatically different from the Class vs. Instance version, BUT you will likely have to traverse the E4X chain a bit differently to obtain only the given class-specific metadata tags (as it may also traverse it’s inherited metadata tags).

You can also notice – our custom Metadata tag has been caught in the list as well. It’s a self-closed XML element in this scenario, because we haven’t provided any extra parameter in the Metadata declaration. We can change that :)

[CustomMeta(param1,param2)]

/* Outputs:
<metadata name="CustomMeta">
  <arg key="" value="param1" />
  <arg key="" value="param2" />
</metadata>
*/

If we take a closer look, we notice that the elements don’t have any data in their key attributes. This happens when only plain words are inserted between the “(” parentheses “)”. This doesn’t make it necessarily wrong – au contraire! It makes it simple to insert Enabled / Disabled toggles in your declarations. Instead of using a key-value pair to enable certain boolean-flags in your use of the metadata tag, you can just “assume”… that if those certain keywords are found in the param list, then it is ON.

The next example shows what happens when we assign values to the keywords:

[CustomMeta(param1="foo",param2="bar")]

/* Outputs:
<metadata name="CustomMeta">
  <arg key="param1" value="foo" />
  <arg key="param2" value="bar" />
</metadata>
*/

If we compare to the previous output, we can notice that each parameter keywords have moved to the key attribute to make their value attributes available to store their data.

Members Metadata: Properties, Methods, Getters & Setters

Metadata tags aren’t limited to be declared only at a Class-level. Flex developers have already seen this (perhaps too many times!) when using [Bindable] on the instance variables, getters and setters of their classes.

If we do that ourselves with Custom Metadata tags, they can be accessed as follows:

package {
  import flash.display.Sprite;
  import flash.utils.describeType;
  
  /**
   * An example of enumerating a class's Metadata information.
   * @author Pierre Chamberlain
   */
  [CustomMeta(param1="foo",param2="bar")]
  public class MetadataTest extends Sprite{
    
    [MemberMetaExamplePrivate]
    private var _myPrivateVar:Boolean;
    
    [MemberMetaExamplePublic]
    public var myPublicVar:String;
    
    [MemberMetaExampleConstructor]
    public function MetadataTest() {
      super();
      
      analyzeMetadata();
    }
    
    private function analyzeMetadata():void{
      var myClass:Class =      Object(this).constructor;
      var xDesc:XML =        describeType(myClass);
      var myClassName:String =  xDesc.@name;
      var xMetas:XMLList =    xDesc.factory..metadata;
      
      //Filter the Metadata Tags belonging to this Class only:
      var xMetaParent:XML;
      var metaParents:Array =  [];
      for each(var xMeta:XML in xMetas) {
        xMetaParent =  xMeta.parent();
        if (xMeta.@name.indexOf("__go_to") > -1) {
          delete xMetaParent.children()[xMeta.childIndex()];
          continue;
        }
        
        if (xMetaParent.name() == "factory") {
          metaParents.push(xMeta);
          continue;
        }
        
        var declaredBy:String =  xMetaParent.attribute("declaredBy");
        if (declaredBy && declaredBy != myClassName) {
          continue;
        }
        
        metaParents.push( xMetaParent );
      }
      
      trace(metaParents.join("\n"));
    }
    
    [MemberMetaExampleMethod]
    public function myMethod():void {
      
    }
    
    [MemberMetaExampleGetter]
    public function get myAccessor():Boolean {
      return _myPrivateVar;
    }
    
    public function set myAccessor(value:Boolean):void {
      _myPrivateVar =  value;
    }
  }
}
/* Outputs:
<variable name="myPublicVar" type="String">
  <metadata name="MemberMetaExamplePublic" />
</variable>
<accessor name="myAccessor" access="readwrite" type="Boolean" declaredBy="MetadataTest">
  <metadata name="MemberMetaExampleGetter" />
</accessor>
<method name="myMethod" declaredBy="MetadataTest" returnType="void">
  <metadata name="MemberMetaExampleMethod" />
</method>
<metadata name="CustomMeta">
  <arg key="param1" value="foo" />
  <arg key="param2" value="bar "/>
</metadata>
*/

I won’t go in depth on the filter-process, but you can see in the output that we’ve captured: CustomMeta, MemberMetaExampleMethod, MemberMetaExampleGetter and MemberMetaExamplePublic. We can come to a conclusion that Private Variables, Methods, Getters & Setters and Class Constructors cannot have Metadata applied to them.

With all that said – where to go from here? How can using Custom Metadata tags enhance our development in ActionScript 3.0? (To be continued…)

NOTE: If you run into an issue where your Custom Metadata tags no longer exists, you are more than likely compiling a “release” version of your SWF – and by default those Metadata tags will be stripped away. To preserve them, add each existing Metadata tags to the compiler argument with “-keep-as3-metadata+=CustomMeta” (Thanks to Damian Connolly to point this out)