////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2005-2007 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package mx.core
{

/**
 *  ComponentDescriptor is the base class for the UIComponentDescriptor class,
 *  which encapsulates the information that you specified in an MXML tag
 *  for an instance of a visual component.
 *  In Flex, non-visual components are treated differently and do not
 *  have descriptors, but in a future version the ComponentDescriptor
 *  base class may be used for them as well.
 *
 *  <p>Most of the tags in an MXML file describe a tree of UIComponent objects.
 *  For example, the <code>&lt;mx:Application&gt;</code> tag represents a
 *  UIComponent object, and its child containers and controls are all
 *  UIComponent objects.</p>
 *
 *  <p>The MXML compiler compiles each of these MXML tags into a
 *  UIComponentDescriptor instance.
 *  To be precise, the MXML compiler autogenerates an ActionScript
 *  data structure which is a tree of UIComponentDescriptor objects.</p>
 *
 *  <p>At runtime, the <code>createComponentsFromDescriptors()</code> method
 *  of the Container class uses the information in the UIComponentDescriptor
 *  objects in the container's <code>childDescriptors</code> array to create
 *  the actual UIComponent objects that are the container's children,
 *  plus deeper descendants as well.
 *  Depending on the value of the container's <code>creationPolicy</code>,
 *  property, the descendants might be created at application startup,
 *  when some part of the component is about to become visible,
 *  or when the application developer manually calls
 *  the <code>createComponentsFromDescriptors()</code> method.</p>
 *
 *  <p>You do not typically create ComponentDescriptor or UIComponentDescriptor
 *  instances yourself; you can access the ones that the MXML compiler
 *  autogenerates, via the <code>childDescriptors</code> array
 *  of the Container class.</p>
 *
 *  @see mx.core.UIComponentDescriptor
 *  @see mx.core.Container#childDescriptors
 *  @see mx.core.Container#creationPolicy
 *  @see mx.core.Container#createComponentsFromDescriptors()
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */ 
public class ComponentDescriptor
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor.
     *
     *  @param descriptorProperties An Object containing name/value pairs
     *  for the properties of the ComponentDescriptor object, such as its
     *  <code>type</code>, <code>id</code>, <code>propertiesFactory</code>
     *  and <code>events</code>.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function ComponentDescriptor(descriptorProperties:Object)
    {
        super();

        for (var p:String in descriptorProperties)
        {
            this[p] = descriptorProperties[p];
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  document
    //----------------------------------

    /**
     *  A reference to the document Object in which the component
     *  is to be created.
     *
     *  @see mx.core.IUIComponent#document
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public var document:Object;

    //----------------------------------
    //  events
    //----------------------------------

    /**
     *  An Object containing name/value pairs for the component's
     *  event handlers, as specified in MXML.
     *
     *  <p>For example, if you write</p>
     *
     *  <pre>
     *  &lt;mx:DataGrid id="dg" initialize="fetchData(); initDataGrid();"  change="changeHandler(event);"/&gt;
     *  </pre>
     *
     *  <p>then the descriptor's <code>events</code> property is the Object</p>
     *
     *  <pre>
     *  { initialize: "__dg_initialize", change: "__dg_change" }
     *  </pre>
     *
     *  <p>The <code>event</code>property is <code>null</code>
     *  if no MXML event handlers were specified for the component</p>
     *
     *  <p>The strings <code>"__dg_initialize"</code>
     *  and <code>"__dg_change"</code> are the names of event handler
     *  methods that the MXML compiler autogenerates.
     *  The body of these methods contain the ActionScript statements
     *  that you specified as the values of the event attributes.
     *  For example, the autogenerated <code>initialize</code> handler is</p>
     *
     *  <pre>
     *  public function __dg_initialize(event:mx.events.FlexEvent):void
     *  {
     *      fetchData();
     *      initDataGrid();
     *  }
     *  </pre>
     *
     *  <p>You should not assume that the autogenerated event handlers
     *  will always be specified by name; this may change in a future
     *  version of Flex.</p>
     *  
     *  <p>This property is used by the Container method
     *  <code>createComponentsFromDescriptors()</code>
     *  to register the autogenerated event handlers
     *  using the <code>addEventListener()</code> method.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public var events:Object;

    //----------------------------------
    //  id
    //----------------------------------

    /**
     *  The identifier for the component, as specified in MXML. 
     *
     *  <p>For example, if you write</p>
     *
     *  <pre>
     *  &lt;mx:TextInput id="firstName" text="Enter your first name here"/&gt;
     *  </pre>
     *
     *  <p>then the descriptor's <code>id</code> property is the String
     *  <code>"firstName"</code>.</p>
     *
     *  <p>The <code>id</code> property is <code>null</code>
     *  if no MXML id was specified for the component.</p>
     *
     *  <p>The value of the <code>id</code> property becomes the name
     *  of a public variable in the MXML document object,
     *  autogenerated by the MXML compiler.
     *  The value of this variable is a reference to the UIComponent object
     *  created from this descriptor.
     *  This is why you can, for example, reference the TextInput control's 
     *  <code>text</code> property as <code>firstName.text</code>
     *  from anywhere within the document containing this TextInput instance.</p>
     *
     *  <p>If an <code>id</code> is specified, and it isn't the empty string,
     *  it also becomes the <code>name</code> of the DisplayObject object.
     *  If an <code>id</code> is not specified or is empty, the DisplayObject
     *  object's <code>name</code> remains an autogenerated string,
     *  such as <code>"Button3"</code>, as returned by the
     *  <code>NameUtil.createUniqueName()</code> method.
     *  The <code>name</code> is used in generating the string returned
     *  by the <code>toString()</code> method.
     *  It can also be used to find the component from its parent
     *  by calling <code>getChildByName()</code>.</p>
     *
     *  @see flash.display.DisplayObject#name
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public var id:String;

    //----------------------------------
    //  properties
    //----------------------------------

    /**
     *  @private
     */
    private var _properties:Object;

    /**
     *  An Object containing name/value pairs for the component's properties,
     *  as specified in MXML.
     *
     *  <p>For example, if you write</p>
     *
     *  <pre>
     *  &lt;mx:TextInput width="150" text="Hello"/&gt;
     *  </pre>
     *
     *  <p>then the descriptor's <code>properties</code> property
     *  is the Object</p>
     *
     *  <pre>
     *  { width: 150, text: "Hello" }
     *  </pre>
     *
     *  <p>The <code>properties</code> property is <code>null</code>
     *  if no MXML properties were specified for the component.
     *  In this case, the component will use default property values.</p>
     *
     *  <p> This Object is produced by calling the function specified by the
     *  <code>propertiesFactory</code> property, and then cached
     *  for subsequent access.
     *  However, when a Repeater produces multiple instances of a component
     *  from the same descriptor, a fresh copy of the <code>properties</code>
     *  Object should be produced for each component instance so that they
     *  don't share property values which are Arrays or Object references.
     *  The Repeater accomplishes this by calling the 
     *  <code>invalidateProperties()</code> method on the descriptor.</p>
     *
     *  @see #propertiesFactory
     *  @see #invalidateProperties()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get properties():Object
    {
        if (_properties)
            return _properties;

        if (propertiesFactory != null)
            _properties = propertiesFactory.call(document);
        
        // Propagate the 'document' property, set by the MXML compiler
        // on the document descriptor, down to all descendant descriptors.
        if (_properties)
        {   
            var cd:Array = _properties.childDescriptors; 
            if (cd)
            {
                var n:int = cd.length;
                for (var i:int = 0; i < n; i++)
                {
                    cd[i].document = document;
                }
            }
        }
        else
        {
            _properties = {};
        }
        
        return _properties;
    }

    //----------------------------------
    //  propertiesFactory
    //----------------------------------

    /**
     *  A Function that returns an Object containing name/value pairs
     *  for the component's properties, as specified in MXML.
     *
     *  <p>For example, if you write</p>
     *  
     *  <pre>
     *  &lt;mx:TextInput width="150" text="Hello"&gt;
     *  </pre>
     *
     *  <p>then the descriptor's <code>propertiesFactory</code> property 
     *  is the Function:</p>
     *
     *  <pre>
     *  function():Object { return { width: 150, text: "Hello" }; }
     *  </pre>
     *
     *  <p>The <code>propertiesFactory</code>property is <code>null</code>
     *  if no MXML properties were specified for the component.
     *  In this case, the component will use default property values.</p>
     *
     *  <p>The reason that <code>propertyFactory</code> is a
     *  Function returning an Object rather than an actual Object
     *  is to allow the tree of ComponentDescriptor objects
     *  to "unfold" incrementally.
     *  If all the descriptors in the descriptor tree for the document
     *  were created at launch time, the time to launch would be greater.</p>
     *
     *  <p>The <code>properties</code> property returns a cached Object
     *  that was produced by this factory function.</p>
     *  
     *  <p>Note: Event handlers such as <code>click="doSomething();"</code>
     *  appear in the <code>events</code> Object,
     *  not in the <code>properties</code> Object.</p>
     *
     *  @see #properties
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public var propertiesFactory:Function;

    //----------------------------------
    //  type
    //----------------------------------

    /**
     *  The Class of the component, as specified in MXML.
     *
     *  <p>For example, if you write</p>
     *
     *  <pre>
     *  &lt;mx:TextInput/&gt;
     *  </pre>
     *
     *  <p>then the descriptor's <code>type</code> property
     *  the Class mx.controls.TextInput.</p>
     *
     *  <p>The property is never <code>null</code> for the
     *  ComponentDescriptor objects created by the MXML compiler,
     *  because every MXML tag has a tag name such as mx:TextInput.</p>
     *
     *  <p>The mapping between an MXML tag and its corresponding class
     *  is determined by the XML namespace and the "manifest" file,
     *  if any, that is associated with that namespace.
     *  For example, the standard Flex namespace
     *  <code>http://www.adobe.com/2006/mxml</code>
     *  represented by the mx: prefix is associated (in the flex-config.xml
     *  file) with the manifest file mxml-manifest.xml,
     *  and this file has the tag</p>
     *
     *  <pre>
     *  &lt;component id="TextInput" class="mx.controls.TextInput"/&gt;
     *  </pre>
     *
     *  <p>which maps the tag name mx:TextInput
     *  to the Class mx.controls.TextInput.
     *  Note that the use of a manifest file allows components in single
     *  XML namespace to map to classes in multiple ActionScript packages.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public var type:Class;

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  Invalidates the cached <code>properties</code> property.
     *  The next time you read the <code>properties</code> property,
     *  the properties are regenerated from the function specified by the 
     *  value of the <code>propertiesFactory</code> property.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function invalidateProperties():void
    {
        _properties = null;
    }
    
    /**
     *  Returns the string "ComponentDescriptor_" plus the value of the  
     *  <code>id</code> property.
     *
     *  @return The string "ComponentDescriptor_" plus the value of the  
     *  <code>id</code> property.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function toString():String
    {
        return "ComponentDescriptor_" + id;
    }
}

}
