ActionScript 3 Enums

Based on my earlier writings about different ways of faking Enums in ActionScript 3, I decided to do my own based on Scott’s and Barney’s implementation.

Initially I have an Enum.as class from which my application-specific Enums will inherit from…

package com.hello.core
{
     import flash.errors.IllegalOperationError;
     import flash.utils.Dictionary;
     import flash.utils.describeType;
     import flash.utils.getQualifiedClassName;

     /**
     * An abstract class to emulate Enum type.
     */
     public class Enum
     {
          /**
          * To protect from instantiation after static initializing.
          */
          protected static var locks:Dictionary = new Dictionary();

          /**
          * Function to call for each enum type declared and in static init.
          */
          protected static function initEnumConstant(inType:Class):void
          {
               var className:String = getQualifiedClassName(inType);
               var typeXML:XML = describeType(inType);
               for each(var constant:XML in typeXML.constant)
               {
                    inType[constant.@name]._label = constant.@name;
               }
               locks[className] = true;
          }

          /**
          * Enum label.
          */
          private var _label:String;

          public function Enum()
          {
               var className:String = getQualifiedClassName(this);
               if(locks[className])
               {
                    throw new IllegalOperationError(”Cannot instantiate anymore: ” + className);
               }
          }

          public function get label():String
          {
               return _label;
          }
     }
}

As you can see, the implementation is a very crude combination of Scott’s and Barneys implementations.

An application-specific Enum could be something called ApplicationState.as, which might look something like this…

package com.hello.samples.skinning
{
     import com.hello.core.Enum;

     public class ApplicationState extends Enum
     {
          public static const FINAL:ApplicationState = new ApplicationState();
          public static const INITIAL:ApplicationState = new ApplicationState();

          // static ctor
          {
               initEnumConstants( ApplicationState );
          }
     }
}

Please forgive me for not including download links, but the classes are so simple that anyone interested should be able to copy-paste from here.

About these ads
Tagged ,

8 thoughts on “ActionScript 3 Enums

  1. Hadrien says:

    This way, it should be impossible to use multiple different Enum type as the lock is static and shared by all Enum types.

    Am I wrong ?

    A workaround is to use a dictionary to lock Enum instantiation :

    package com.social2u.utils
    {
    import flash.errors.IllegalOperationError;
    import flash.utils.Dictionary;
    import flash.utils.describeType;
    import flash.utils.getQualifiedClassName;

    /**
    * An abstract class to emulate Enum type.
    */
    public class Enum
    {
    /**
    * To protect from instantiation after static initializing.
    */
    protected static var locks:Dictionary = new Dictionary();

    /**
    * Function to call for each enum type declared and in static init.
    */
    protected static function initEnumConstant(inType:Class):void
    {
    var className:String = getQualifiedClassName(inType);
    var typeXML:XML = describeType(inType);
    for each(var constant:XML in typeXML.constant)
    {
    inType[constant.@name]._label = constant.@name;
    }
    locks[className] = true;
    }

    /**
    * Enum label.
    */
    private var _label:String;

    public function Enum()
    {
    var className:String = getQualifiedClassName(this);
    if(locks[className])
    {
    throw new IllegalOperationError(“Cannot instantiate anymore: ” + className);
    }
    }

    public function get label():String
    {
    return _label;
    }
    }
    }

    Thank you for posting your solution, I did not even notice the existence of flash.utils.describeType … :D really useful!!!

    Hadrien

  2. Peter Andreas Molgaard says:

    Hello Hadrien,

    I suppose your right about the “locked” variable being set upon loading the first class extending Enum…

    Thanks for your input… I immediately adopt your “locks” dictionary, great stuff :-)

    // Peter

  3. Hadrien says:

    Also, if enum class contains others constant like :

    public class MyEnum extends Enum
    {
    public const ENUM1:MyEnum = new MyEnum();
    public const ENUM2:MyEnum = new MyEnum();

    public const whyNot:String = “Why Not ?”;
    }

    Then Enum.initEnumConstant must also check constant type :

    protected static function initEnumConstant(inType:Class):void
    {
    var className:String = getQualifiedClassName(inType);
    var typeXML:XML = describeType(inType);
    for each(var constant:XML in typeXML.constant)
    {
    if(constant.@type == className)
    {
    inType[constant.@name]._label = constant.@name;
    }
    }
    locks[className] = true;
    }

    Hadrien (who got too many enums in his head)

    Thank u for blogging!

  4. I added some code so that I can check whether a string matches a valid constant within a given namespace, which is very useful for error checking. I’m not sure if my approach is the best, but it seems to work.

    1. added this code:

    /**
    * Stores arrays of allowed constants within a given class namespace
    */
    protected static var constants:Dictionary = new Dictionary();

    2. added a line to the initEnumConstant method
    /**
    * Function to call for each enum type declared and in static init.
    * @param inType The class namespace where the constants are being defined
    */
    protected static function initEnumConstant(inType:Class):void {
    var className:String = getQualifiedClassName(inType);
    var typeXML:XML = describeType(inType);
    constants[className] = new Array();
    for each(var constant:XML in typeXML.constant) {
    var name:String = String(constant.@name);
    inType[name]._label = String(name);
    constants[className].push(String(name));
    }
    locks[className] = true;
    }

    3. Added two new methods:
    /**
    * Retrieves an array of allowed constants within a given class namespace
    * @param inType Class namespace to search for constants
    * @return an array of strings representing allowed constants
    */
    public static function getConstants(inType:Class):Array {
    var className:String = getQualifiedClassName(inType);
    return constants[className];
    }

    /**
    * Determines whether a particular string is a valid constant within a given class namespace
    * @param constantName The name of the
    * @param inType
    * @return true if the constant exists
    */
    public static function isValid(constantName:String, inType:Class):Boolean {
    var className:String = getQualifiedClassName(inType);
    if (constants[className].indexOf(constantName) == -1) { return false; }
    else { return true; }
    }

    4. Here’s how I implemented a class that inherits from Enum:

    public class CourseModuleType extends Enum {

    public static const INTRO:CourseModuleType = new CourseModuleType();
    public static const CONCLUSION:CourseModuleType = new CourseModuleType();

    { initEnumConstant(CourseModuleType); }

    public static function isValid(constantName:String):Boolean {
    if (Enum.isValid(constantName,CourseModuleType)) { return true; }
    else { return false; }
    }
    public static function get array():Array {
    return Enum.getConstants(CourseModuleType);
    }

    }

  5. Adrian says:

    I edited the Enum class to have an _index as well as a _label. I was hoping to be able to use this in for loops, but there seems to be a weird behaviour with the order of the constants when they get picked up by initEnumConstant().
    Here is the code and its output:

    protected static function initEnumConstant(inType:Class):void
    {
    var className:String = getQualifiedClassName(inType);
    var typeXML:XML = describeType(inType);
    var iCounter:int = 0;
    for each(var constant:XML in typeXML.constant)
    {
    inType[constant.@name]._label = constant.@name;
    inType[constant.@name]._index = iCounter++;
    trace(inType[constant.@name]._label +” = ” +inType[constant.@name]._index);
    }
    locks[className] = true;
    }

    /**
    FOUR = 0
    QUEEN = 1
    KING = 2
    FIVE = 3
    ACE = 4
    JACK = 5
    THREE = 6
    TWO = 7
    TEN = 8
    NINE = 9
    SEVEN = 10
    SIX = 11
    EIGHT = 12
    */

    The class I wrote had these constants in order from low to high, but they have been shifted into this weird order somehow. Any idea why?

  6. Peter Andreas Molgaard says:

    Hi Adrian,

    Due to the “prototype” architecture of the class implementation in the Flash Player and due to how objects are instantiated, you can not be sure in which order constants are set and allocated space in memory.
    There is some material about this, you should be able to find it via search engine.
    Let me know if it works out for you.

    Great work, btw…

    Cheers,
    Peter

  7. Szymon Bialy says:

    You don’t need to use Dictionary object for locks as you’re storing only String keys. Simple Object instance (acting as map) would be enough.

    Cheers,
    Szymon

  8. […] of Java code which use Java’s robust enumerations. This has been discussed elsewhere quite a bit so I’ll just do a quick recap and then give my new updated […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 522 other followers

%d bloggers like this: