Rosettas‎ > ‎

ActionScript

Rosetta.as

// Copyright (c) 2007 Art & Logic, Inc. All Rights Reserved.
// $Id: $

// Above: [sf02], [sf03]

// a note on the stuff inside the square brackets:
// Each of the codes inside brackets is a reference to the rule(s) in the
// Style Guide that's being illustrated, as follows:
// [sf##] - StyleGuide/SourceFiles
// [cas##] - StyleGuide/ClassesAndStructures
// [nr##] - StyleGuide/NamingRules
// [cg##] - StyleGuide/Commenting
// [fmt##] - StyleGuide/Formatting

// Throughout: [fmt01]; all indentation done with space characters (0x20),
// three spaces per logical tab stop.

// [nr21] C/C++ guard to prevent recursive inclusion of header files
// !!! ActionScript3 - no preprocessor syntax !!!


// [nr25] the 'q' prefix indicates that we are controlling conditional
// !!! ActionScript3 - no precompilation macros !!!

// [sf07], only one class declared/defined in this source file.

// General Note about coding in Flex:
//
// Unlike most languages, Flex applications are written with a sometimes 
// arbitrary combination of ActionScript3 and MXML.  One can write a GUI class 
// definition using ActionScript3 (Rosetta.as), or as an mxml file (RosettaView.mxml), 
// with an accompanying script block (to implement the classes functions).   In 
// general, mxml is used for more static GUI components, and can often be thought 
// of as the "View" portion of a component.  It generally makes sense to implement 
// at least some of the control logic in the mxml file, since one will be referencing 
// objects identified in the mxml.  This is how the Adobe examples are 
// implemented.  The Flex compiler actually converts/compiles the mxml into a class 
// definition, so aside from the language peculiarities, there is no functional 
// difference between a class defined in mxml vs. ActionScript3 (though there are some
// limitiations when defining a class using MXML - see Adobe documentation).  One can 
// inherit from a class defined in mxml, or extend in mxml a class defined in ActionScript3.  
// This leaves the developer with a powerful myriad of choices about how to implement 
// a class hierarchy.
//
// It's very possible and probable that this will be used in ways that some developers
// may not like. There are no absolutes, but as a general rule it's a good idea to
// keep ActionScript3 code in an MXML file to a reasonable minimum.  This will encourage
// a clean separation of View code from control logic.  It also has other benefits, such
// as keeping the static markup portion of the code, which may be more relevant to a 
// graphic designer, separate from the C-style ActionScript3 code, which may confuse them.
//
// There are various techniques for organizing code in this way.  A couple of good
// possibilities are a "code-behind" pattern, where the application control logic is 
// implemented as a base class which the MXML extends.  Alternatively, the bulk of the
// application control logic could extend the MXML.

package com.artlogic.devpractices.rosetta
{
   // [sf05] Generally best to import individual classes
   import flash.filesystem.File;
   
   import mx.logging.Log;
   import mx.logging.LogEventLevel;
   
   /**
   * Rosetta
   * Simple, but meaningless class to demonstrate Art & Logic programming
   * style conventions as they are applied to the various programming languages
   * we use. In several places, you're asked to imagine a block of useful code
   * to avoid distracting you from the issues of formatting (etc) that are being
   * demonstrated.
   *
   * As you've already seen above, the thing about to be shown in this file is
   * called out in comments using the A&L-style markup '!!!'
   * 
   * [nr11], [nr18] class names prefixed with "A"  
   * !!! ActionScript3 - Unlike some other languages, the ActionScript3 
   * compiler expects the classname to match the filename.  So classnames should 
   * not be prefixed with an 'A'.  !!!
   * 
   * [cas01] public member functions/methods listed first
   * !!! ActionScript3 - Should be listed after Constructor function for clarity !!!
   */
   public class Rosetta
   {
      // [nr08] new non-class types (enumerations, data structures, etc) are
      // named with MixedCase and no special prefix 
      // [nr23] identifiers specifying constants for the program are always
      // prefixed with a lowercase 'k'.
      // !!! ActionScript3 - no enumeration syntax, constants declared as follows. !!!
      public const kSomeConstant = 50;
      public const kSomeOtherConstant = 100;

      /**
       * Construct the Rosetta object.  
       * !!! ActionScript3 - Although arguments can be passed into a constructor to 
       * initialize the object, it is usually better to have no arguments to the 
       * constructor, and use properties to initialize the object.  This allows use 
       * of the object from MXML and makes serialization/deserialization of the 
       * object easier. !!!
       */      
      public function Rosetta()
      {
      }
      
      // !!! ActionScript3 - No destructors in ActionScript3 !!!
      
      // [cas01] public methods (functions) are declared first.  
      // !!! ActionScript3 - it's best to declare public properties before 
      //public functions.
      // Note! Use explicit get and set properties, rather than creating
      // functions like setFilename and getFilename in ActionScript3.
      /**
      * Set the filename to be used by this object.
      * @param filename The filename.
      */
      public function set filename (filename:String):void
      {
         fFilename = filename;
         fInputFile = new File(filename);   
      }
      
      /**
       * [nr07] Member functions are named using CamelCase, with a leading 
       * upper case letter.
       * !!! ActionScript3 - Like java, class methods 
       * (called functions in ActionScript3), begin with a lowercase letter. !!!
       */
      public function memberFunctionWithMultipleWords():Boolean
      {
         var ok:Boolean = true;
         // [fmt08] -- formatting  of a for() loop.
         for (var i:Number = 0; i < kSomeConstant; ++i)
         {
            for (var j:Number = 0; j < kSomeOtherConstant; ++j)
            {
// [fmt02] -- preprocessor directives go in column zero.
// !!! ActionScript3 - No preprocessor directives. There ActionScript3 compiler does
// however support conditional compilation, controlled from build tool. !!!
// Note use of logger for verbosity control.  Further initialization
// of the Log would be needed to actually use it this way...
               Log.getLogger("Rosetta").log(LogEventLevel.INFO, 
                "testing: " + i + ", " + j);

               // [nr07] -- Calls to member functions of the current object are
               // made through an explicit this pointer.
               ok = this.test(i, j);
               if (!ok)
               {
                  Log.getLogger("Rosetta").log(LogEventLevel.ERROR, 
                   "Fatal error testing: " + i + ", " + j);
                  break;
               }
            }
            if (!ok)
            {
               break;
            }
         }
         return ok;
      
      // [fmt02] -- labels used as the target of a goto are placed in column zero.
      // !!! ActionScript3 - No goto or labels !!!
      }

      
      /**
      * !!! Here's how we handle very long lines (i.e., longer than 79
      * characters)
      */
      public function longLineWrapping():Boolean
      {
         // [fmt03] -- source lines should be less than 80 characters long.
         // [fmt04] -- break lines at the most logical point
//       1         2         3         4         5         6         7
//34567890123456789012345678901234567890123456789012345678901234567890123456789
         var retval:Boolean = SomeStaticClass.FunctionWithManyArguments(
          longArgumentName1, longArgumentName2, longArgumentName3, 
          longArgumentName4);

         // alternative format #1
         retval = SomeStaticClass.FunctionWithManyArguments(longArgumentName1, 
          longArgumentName2,
          longArgumentName3, 
          longArgumentName4);

         // alternative format #2
         retval = SomeStaticClass.FunctionWithManyArguments(
          longArgumentName1, 
          longArgumentName2,
          longArgumentName3, 
          longArgumentName4);

         // [fmt05] -- prefer to break after a comma
         retval = SomeStaticClass.SomeUsefulFunction(x.leftMargin(),
          x.topMargin() + x.bottomMargin() - 1);

         // [fmt05] -- if that's not possible, break after an operator
         retval = SomeStaticClass.SomeUsefulFunction(x.LeftMargin() + 
          anObject.HorizontalPadding(), x.TopMargin() + x.BottomMargin() - 1);
          
          return retVal;         
      }  

      /**
       * !!! Loop formatting
       */
      public function loops():void
      {
         // [fmt08] -- for() loop; note the pre-incremented form of the loop control 
         for (var i:Number = 0; i < kSomeConstant; ++i)
         {
            // loop code here...
         }
      
         // [fmt09]
         while (someBoolean)
         {
            // useful code...
         }
      
         // [fmt09] -- while loop with an empty body makes the emptiness explicit
         while (this.someFuncReturningBool())
         {
            // intentionally empty...
         }
      
         // [fmt10] -- do/while loop.
         // [fmt18] -- exception to the bracing rule saying that braces should
         // always be on a line by themselves
         do
         {
            // loop code here..
         } while (someBoolean);
      }

      // [cas01] Protected member functions declared after public 
      /**
       * [nr03] parameters passed to functions are named using lowerCamelCase
       */
      protected function test(firstVal:Number, secondVal:Number):Boolean
      {
         var retval:Boolean = true;
         // [fmt07] -- if/else formatting
         if (firstVal < secondVal)
         {
            // [cg06] -- use '$$$' as a marker to indicate a request that the
            // following block of code be reviewed before a change is considered
            // complete
            // $$$ before
            // this.process(secondVal);
            // $$$ after
            // Shouldn't this be processing the first value in this case?
            this.process(firstVal);
            // $$$ end
         }
         else if (secondVal > kSomeConstant)
         {
            this.process(secondVal);
         }
         else
         {
            // [fmt18] -- use curly braces even in cases where the language doesn't
            // require it to prevent maintenance errors.
            retval = false;
         }
      
         return retval;
      }


      // [cas01]  Private member function declared after protected 

      /**
       * Useful function to process something or other.
       */
      private function process(val:Number):void
      {
         // [cg05] -- use '!!!' as a marker to indicate a section of code that
         // requires attention
         // !!!... removing old hardcoded value
         // fProcessResult = 131;
         // ...!!!
         var result:Number = val;
         while (--val)
         {
            result *= val;
         }
         fProcessResult = result;
      }
      
      /**
       * Demonstrating the formatting of a switch statement.
       */
      private function decide(midiEventType:Number):void
      {
         // [fmt11] -- switch/case formatting.
         switch (midiEventType)
         {
            // comments precede the case
            case kNoteOn:
            this.noteOn(now);
            if (duration > 0)
            {
               this.noteOff(now + duration);
            }
            break;
      
            case kNoteOff:
            this.noteOff(now);
            break;
            
            case  kPolyAfter:
            after = channelValue;
            // [fmt35] include, but comment out the break when falling through intentionally.
            // break;
      
            case kChannelAfter:
            this.afterTouch(after);
            break;
      
            // [fmt36] no need to include a commented out break when falling through an empty case
            case kPitchbend:
            // blank line...
      
            // [fmt12] -- switch statements should always have a default case.
            default:
            // !!! ActionScript3 - Currently no native support for asserts.
            // One could implement assert as a runtime error, and use the 
            // DEBUG configuration flag to compile out the implementation
            // but there would be a performance hit. !!!
            //ASSERT(false);
            Log.getLogger("Rosetta").log(LogEventLevel.ERROR, 
               "Unsupported event type!");            
            break;
         }
      }

   
      // [cas02] member variables declared in the same order: public, protected, private.
      // [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed 
      // by an  uppercase letter.
      // !!! ActionScript3 - Applies only to protected and private variables.  Public
      //     variables have no prefix, to enable later conversion to get/set function
      //     in a way that is transparent to clients of this class.
      public var modValue:Number;
      
      // [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed 
      // by an  uppercase letter.
      // !!! ActionScript3 - Applies only to protected and private variables.  Public
      //     variables have no prefix, see above.
      
      private var fFilename:String;

      // [fmt28] -- when declaring pointer variables, attach the '*' to the
      // typename, not the variable name.
      // !!! ActionScript3 - All variables are "pointers" in ActionScript3 !!! //
      private var fInputFile:File;
      
      private var fProcessResult:Number = 0;

   }
}

RosettaView.mxml

<?xml version="1.0" encoding="utf-8"?>
<!-- 
   Copyright (c) 2007 Art & Logic, Inc. All Rights Reserved.
   $Id: $

   Above: [sf02], [sf03]

   a note on the stuff inside the square brackets:
   Each of the codes inside brackets is a reference to the rule(s) in the
   Style Guide that's being illustrated, as follows:
   [sf##] - StyleGuide/SourceFiles
   [cas##] - StyleGuide/ClassesAndStructures
   [nr##] - StyleGuide/NamingRules
   [cg##] - StyleGuide/Commenting
   [fmt##] - StyleGuide/Formatting

   Throughout: [fmt01]; all indentation done with space characters (0x20),
   three spaces per logical tab stop.
   
   This is a simple, and meaningless View component showing some of the style guide
   formatting appropriate for MXML.  Wherever it makes sense to do so, apply 
   ActionScript3 syntax to MXML.
-->
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="400" height="300">
   <mx:Script>
      <![CDATA[
         import com.artlogic.devpractices.rosetta.Rosetta;
         // !!! ActionScript3/MXML - Formatting inside a script block should be treated
         // identically to formatting in an ActionScript file.  See Rosetta.as for
         // more info.
         
         /**
         * Handle click event.
         */
         private function onDoItClick(event:Event):void
         {
            // [nr07] -- Calls to member functions of the current object are
            // made through an explicit this pointer.
            // !!! ActionScript3 - This applies to public properties as well.  All id'd 
            // members in MXML are defined as public properties.              
            var filename:String = this.filenameInput.text;
            if ((null != filename) && (filename.length > 0))
            {
               //Create a Rosetta object
               if (null == fRosetta)
               {
                  fRosetta = new Rosetta();
               }
               
               //Initialize the filename property
               fRosetta.filename = filename;
               
               //Do something with the object.
               fRosetta.memberFunctionWithMultipleWords();      
            } 
         }

         // [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed 
         // by an  uppercase letter.         
         private var fRosetta:Rosetta;
      ]]>
   </mx:Script>
   <mx:Text text="Enter filename below:"/>
   
   <!-- [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed 
        by an  uppercase letter.
        !!! MXML - Public properties do not require (and should not have) an 'f' prefix.
            Members defined in MXML are by definition public. !!!
    -->
   <mx:TextInput id="filenameInput" width="379"/>
   
   <!--[nr07] -- Calls to member functions of the current object are
        made through an explicit this pointer.
        !!! MXML - Use the this. syntax when referencing member functions and 
        variables. !!!
   -->
   <mx:Button label="Do it!" width="161" click="this.onDoItClick(event)"/>
</mx:Panel>