Coverage Report - org.as3coreaddendum.system.Enum
 
Classes in this File Line Coverage Branch Coverage Complexity
Enum
100%
30/30
N/A
0
 
 1  
 /*
 2  
  * Licensed under the MIT License
 3  
  * 
 4  
  * Copyright 2010 (c) Flávio Silva, http://flsilva.com
 5  
  *
 6  
  * Permission is hereby granted, free of charge, to any person
 7  
  * obtaining a copy of this software and associated documentation
 8  
  * files (the "Software"), to deal in the Software without
 9  
  * restriction, including without limitation the rights to use,
 10  
  * copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  
  * copies of the Software, and to permit persons to whom the
 12  
  * Software is furnished to do so, subject to the following
 13  
  * conditions:
 14  
  *
 15  
  * The above copyright notice and this permission notice shall be
 16  
  * included in all copies or substantial portions of the Software.
 17  
  *
 18  
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 19  
  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 20  
  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 21  
  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 22  
  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 23  
  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 24  
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 25  
  * OTHER DEALINGS IN THE SOFTWARE.
 26  
  * 
 27  
  * http://www.opensource.org/licenses/mit-license.php
 28  
  */
 29  
 
 30  1
 package org.as3coreaddendum.system {
 31  
         import flash.errors.IllegalOperationError;
 32  
         import flash.utils.getQualifiedClassName;
 33  
 
 34  
         /**
 35  
          * This is the base class for implementation of enumeration objects.
 36  
          * <p>This class shouldn't be instantiated directly, rather than enumeration classes must extend this class.</p>
 37  
          * <p><b>What is an Enumerated Type?</b></p>
 38  
          * <p>An Enumerated Type is a data type that consists of a pre-defined set of values. Each enumerator (constant) is a value.
 39  
          * An Enumerated Type groups a set of interrelated logical values.</p>
 40  
          * <p>Actionscript 3.0 doesn't implements Enumerated types. Rather, its uses a class with only constants to define a set of interrelated logical values.</p>
 41  
          * <p>One example is the <code>flash.display.StageAlign</code> class. It is the set of logical values acceptable for the <code>flash.display.Stage.align</code> property.</p>
 42  
          * <p>But even without the support of Actionscript 3.0 for Enumerated types you can simulate this functionality extending this Enum class.</p>
 43  
          * 
 44  
          * <p><b>Why use an Enumerated Type rather than a class with constants?</b></p>
 45  
          * <p>Following the example above, if you open the Actionscript 3.0 Language Reference and quickly look at the class <code>flash.display.Stage</code> and see the <code>align</code> property, you will note that its type is <code>String</code>.
 46  
          * So you cannot immediatly know what values you can assign to it. Only after discovering that there is a class <code>flash.display.StageAlign</code> you note that these are the acceptable values.
 47  
          * So this is the first problem with this approach.</p>
 48  
          * <p>The second problem is that you really can assign any <code>String</code> to the <code>align</code> property.
 49  
          * Nothing guarantees you will pass the values that are actually in the <code>flash.display.StageAlign</code> class.
 50  
          * Nor will there be any error at compile time that say that something is wrong.</p>
 51  
          * <p>But if instead of using this approach was used Enumeration types, these two problems would become two good points.</p>
 52  
          * <p>First, looking at the Actionscript 3.0 Language Reference you would see that the property <code>align</code> is of the type <code>StageAlign</code>.
 53  
          * Automatically you check this class and see how to use it to assign a value for the <code>align</code> property.</p>
 54  
          * <p>Even better, if you make a mistake the compiler will warn you.
 55  
          * You will no longer able to send an invalid value for the property.</p>
 56  
          * <p>But even with this approach remains a problem to be solved. The client of your code can still create instances of your Enumerated Type and use them when needed.
 57  
          * In most cases this is undesirable, because you have already defined the set of acceptable values through constants in its Enumerated Type.</p>
 58  
          * <p>To resolve this problem exists a solution: the type-safe enum pattern. It is described with an example at the end of the page.</p>
 59  
          * <p>You can also check the class NumericRounding listed in the section "See also" below.</p>
 60  
          * <p>So to summarize the benefits of using Enumerated types: they make your code (or API) more readable and safe.</p>
 61  
          * 
 62  
          * @example
 63  
          * 
 64  
          * <b>Simple Enumerated Type</b>
 65  
          * <p>Suppose that we have an API that make loading of files.
 66  
          * The acceptable file types that the API can load can be defined by an Enumerated Type.
 67  
          * In this example the name of the Enumerated Type will be FileType. Note that you don't need to use the suffix "Enum" in its name.</p>
 68  
          * <p>Then we define the types that the API can load. In this example the API can load just image, swf and mp3 files.
 69  
          * So we have just three constants, one to each file type.</p>
 70  
          * <p>When we want to load a file, we use the appropriate constant to tell the API what type of file will be loaded.
 71  
          * If you need a file type that doesn't exists in the Enumerated Type, this means that the API doesn't support that file type.</p>
 72  
          * 
 73  
          * <listing version="3.0">
 74  
          * import org.as3coreaddendum.system.Enum;
 75  
          * 
 76  
          * public class FileType extends Enum
 77  
          * {
 78  
          * 
 79  
          *      public static const IMAGE   :FileType = new FileType("Image", 0);
 80  
          *      public static const SWF     :FileType = new FileType("SWF", 1);
 81  
          *      public static const MP3     :FileType = new FileType("MP3", 2);
 82  
          * 
 83  
          *      public function FileType(name:String, ordinal:int)
 84  
          *      {
 85  
          *           super(name, ordinal);
 86  
          *      }
 87  
          * }
 88  
          * </listing>
 89  
          * 
 90  
          * <b>Type-safe Enumerated Type</b>
 91  
          * <p>In the above example, if the user of the API need to load a file type that doesn't exists in the Enumerated Type, it could instantiate a new object FileType with the desired type, as in the example below:</p>
 92  
          * <listing version="3.0">
 93  
          * import FileType;
 94  
          * 
 95  
          * var videoFileType:FileType = new FileType("Video", 3);
 96  
          * </listing>
 97  
          * 
 98  
          * <p>This behavior would be wrong, but still allowed by the technical point of view.
 99  
          * This would create the illusion that the user can create new types at runtime and send them to the API.</p>
 100  
          * <p>To prevent this mistake, we can implement a pattern named type-safe enum.
 101  
          * This pattern will prevents the user create new types, throwing a runtime error.</p>
 102  
          * <p>There are some ways to do this, we will see just one below:</p>
 103  
          * 
 104  
          * <listing version="3.0">
 105  
          * import org.as3coreaddendum.system.Enum;
 106  
          * 
 107  
          * public class FileType extends Enum
 108  
          * {
 109  
          * 
 110  
          *      public static const IMAGE   :FileType = new FileType("Image", 0);
 111  
          *      public static const SWF     :FileType = new FileType("SWF", 1);
 112  
          *      public static const MP3     :FileType = new FileType("MP3", 2);
 113  
          * 
 114  
          *      private static var _created:Boolean = false;
 115  
          * 
 116  
          *      {
 117  
          *           _created = true;
 118  
          *      }
 119  
          * 
 120  
          *      public function FileType(name:String, ordinal:int)
 121  
          *      {
 122  
          *           super(name, ordinal);
 123  
          *           if (_created) throw new IllegalOperationError("The set of acceptable values by this Enumerated Type has already been created internally.");
 124  
          *      }
 125  
          * }
 126  
          * </listing>
 127  
          * 
 128  
          * <p>What happens above is that when the FileType is loaded in the Flash Player memory at runtime, first all the static members are initialized.
 129  
          * After that the <em>static code block</em> is executed:</p>
 130  
          * 
 131  
          * <listing version="3.0">
 132  
          * {
 133  
          *      _created = true;
 134  
          * }
 135  
          * </listing>
 136  
          * 
 137  
          * <p>So first all the constants are initialized with the FileType instances because the <code>_created</code> static variable is <code>false</code> and then immediately after that the <code>_created</code> is set to <code>true</code>.
 138  
          * Thus no more instances can be created.</p>
 139  
          * <p>Your Enumerated Type is safe to be used only with the set of values pre-defined by you.</p>
 140  
          * 
 141  
          * @see         org.as3coreaddendum.errors.InvalidEnumArgumentError InvalidEnumArgumentError
 142  
          * @see         org.as3coreaddendum.system.IComparable IComparable
 143  
          * @see         org.as3coreaddendum.system.IEquatable IEquatable
 144  
          * @see         org.as3coreaddendum.system.ISerializable ISerializable
 145  
          * @author         Flávio Silva
 146  
          */
 147  
         public class Enum implements IComparable, IEquatable, ISerializable
 148  
         {
 149  
                 private var _name: String;
 150  
                 private var _ordinal: int;
 151  
 
 152  
                 /**
 153  
                  * The name of this enum constant exactly as supplied by the constructor.
 154  
                  * <p><b>Most programmers should use the <code>toString()</code> method in preference to this one, as the <code>toString()</code> method may return a more user-friendly name.</b> This property is designed primarily for use in specialized situations where correctness depends on getting the exact name.</p>
 155  
                  */
 156  
                 public function get name(): String { return _name; }
 157  
 
 158  
                 /**
 159  
                  * The ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero).
 160  
                  */
 161  
                 public function get ordinal(): int { return _ordinal; }
 162  
 
 163  
                 /**
 164  
                  * Constructor, creates a new Enum object.
 165  
                  * 
 166  
                  * @param         name                         The name of this enumeration constant.
 167  
                  * @param         ordinal                 The ordinal of this enumeration constant, that is its position in the enum declaration (where the initial constant is assigned an ordinal of zero).
 168  
                  * @throws         ArgumentError         If the <code>name</code> argument is <code>null</code> or an empty <code>String</code>.
 169  
                  * @throws         IllegalOperationError         If this class is instantiated directly, in other words, if there is <b>not</b> another class extending this class.
 170  
                  */
 171  
                 public function Enum(name:String, ordinal:int)
 172  1
                 {
 173  1
                         if (getClassName(this) == "Enum")  throw new IllegalOperationError("This class shouldn't be instantiated directly, rather than enumeration classes must extend this class.");
 174  1
                         if (name == null || name == "") throw new ArgumentError("The 'name' argument must not be 'null' nor an empty 'String'.");
 175  
                         
 176  1
                         _name = name;
 177  1
                         _ordinal = ordinal;
 178  1
                 }
 179  
 
 180  
                 /**
 181  
                  * Compares this enum with the specified object for order.
 182  
                  * <p>Enum constants are only comparable to other enum constants of the same enum type. The natural order implemented by this method is the order in which the constants are declared.</p>
 183  
                  * 
 184  
                  * @param         o        The target object to be compared.
 185  
                  * @throws        ArgumentError If the type of the argument <code>o</code> is other than this exactly enum type instance.
 186  
                  * @return        a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
 187  
                  * @see                org.as3coreaddendum.system.IComparable        IComparable
 188  
                  */
 189  
                 public function compareTo(o:*): int
 190  
                 {
 191  1
                         var path:String = getClassPath(this);
 192  1
                         var comparePath:String = getClassPath(o);
 193  
                         
 194  1
                         if (path != comparePath) throw new ArgumentError("The 'o' argument must be of type: " + path + ". Received: " + comparePath);
 195  
                         
 196  1
                         var compare:Enum = Enum(o);
 197  
                         
 198  1
                         if (_name == compare.name && _ordinal == compare.ordinal)
 199  
                         {
 200  1
                                 return 0;
 201  
                         }
 202  1
                         else if (_ordinal < compare.ordinal)
 203  
                         {
 204  1
                                 return -1;
 205  
                         }
 206  
                         
 207  1
                         return 1;
 208  
                 }
 209  
 
 210  
                 /**
 211  
                  * Compares the target object for equality with this object.
 212  
                  * 
 213  
                  * @param         other        The object to be compared for equality with this object.
 214  
                  * @return         <code>true</code> if the specified object is equal to this enum constant.
 215  
                  * @see                org.as3coreaddendum.system.IEquatable        IEquatable 
 216  
                  */
 217  
                 public function equals(other:*): Boolean
 218  
                 {
 219  1
                         var path:String = getClassPath(this);
 220  1
                         var comparePath:String = getClassPath(other);
 221  
                         
 222  1
                         if (path != comparePath) return false;
 223  
                         
 224  1
                         var compare:Enum = Enum(other);
 225  1
                         if (_name == compare.name && _ordinal == compare.ordinal) return true;
 226  
                         
 227  1
                         return false;
 228  
                 }
 229  
 
 230  
                 /**
 231  
                  * @inheritDoc
 232  
                  * @see                org.as3coreaddendum.system.ISerializable        ISerializable
 233  
                   */
 234  
                 public function toSource(): String
 235  
                 {
 236  1
                         return getClassName(this) + "." + toString();
 237  
                 }
 238  
 
 239  
                 /**
 240  
                  * Returns the name of this enum constant, as contained in the declaration.
 241  
                  * <p>This method may be overridden, though it typically isn't necessary or desirable. An enum type should override this method when a more "programmer-friendly" string form exists.</p>
 242  
                  * 
 243  
                  * @return The name of this enum constant.
 244  
                   */
 245  
                 public function toString(): String
 246  
                 {
 247  1
                         return _name;
 248  
                 }
 249  
 
 250  
                 /**
 251  
                  * Returns the primitive value of the object.
 252  
                  * <p>This method is called automatically by the Flash Player every time an arithmetic operation occur. Thus, it's possible to perform these operations to check the position between two or more enum constants.</p>
 253  
                  * 
 254  
                  * @return        The primitive value of the object. In this case, the <code>ordinal</code> value.
 255  
                   */
 256  
                 public function valueOf(): int
 257  
                 {
 258  1
                         return _ordinal;
 259  
                 }
 260  
                 
 261  
                 /**
 262  
                  * @private
 263  
                  */
 264  
                 private function getClassName(o:*): String
 265  
                 {
 266  1
                         var path:String = getClassPath(o);
 267  1
                         var a:Array = path.split(".");
 268  1
                         return (a.length > 0) ? a.pop() : path;
 269  
                 }
 270  
                 
 271  
                 /**
 272  
                  * @private
 273  
                  */
 274  
                 private function getClassPath(o:*): String
 275  
                 {
 276  1
                         var path:String = getQualifiedClassName(o);
 277  1
                         return path.split("::").join(".");
 278  
                 }
 279  
 
 280  
         }
 281  
 
 282  
 }