/*****************************************************
*
* Copyright 2009 Adobe Systems Incorporated. All Rights Reserved.
*
*****************************************************
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
*
* The Initial Developer of the Original Code is Adobe Systems Incorporated.
* Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
* Incorporated. All Rights Reserved.
*
*****************************************************/
package org.osmf.elements
{
import __AS3__.vec.Vector;
import flash.errors.IllegalOperationError;
import org.osmf.elements.compositeClasses.CompositeMediaTraitFactory;
import org.osmf.elements.compositeClasses.CompositeMetadata;
import org.osmf.elements.compositeClasses.TraitAggregator;
import org.osmf.elements.compositeClasses.TraitAggregatorEvent;
import org.osmf.events.MediaErrorEvent;
import org.osmf.media.MediaElement;
import org.osmf.media.MediaResourceBase;
import org.osmf.metadata.Metadata;
import org.osmf.traits.MediaTraitBase;
import org.osmf.utils.OSMFStrings;
/**
* CompositeElement is a media element which encapsulates a collection
* of other more granular media elements. It is generally referred to as
* a <b>media composition</b>.
*
* <p>The media elements that make up a media composition are treated
* as a single, unified media element. For example, if a media composition
* encapsulates a sequence of videos, the CompositeElement will behave as
* if it's a single VideoElement, but one which plays several videos in
* sequence.</p>
*
* <p>Because a CompositeElement maintains a list of MediaElement children,
* any of which may be CompositeElements themselves, a media composition
* can be expressed as a tree structure.</p>
*
* <p>Typically a CompositeElement is not instantiated directly but instead is used as
* the base class for creating specific types of media compositions.</p>
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public class CompositeElement extends MediaElement
{
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function CompositeElement()
{
super();
_traitFactory = new CompositeMediaTraitFactory();
setupTraitAggregator();
}
/**
* The number of child MediaElements in this media composition.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get numChildren():int
{
return children.length;
}
/**
* Gets the child at the specified index.
*
* @param index The index in the list from which to retrieve the child.
*
* @return The child at that index or <code>null</code> if there is none.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function getChildAt(index:int):MediaElement
{
if (index >= 0 && index < children.length)
{
return children[index];
}
else
{
return null;
}
}
/**
* Returns the index of the child if it is in the list such that
* <code>getChildAt(index) == child</code>.
*
* @param child The child to find.
*
* @return The index of the child or -1 if the child is not in the
* list.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function getChildIndex(child:MediaElement):int
{
return children.indexOf(child);
}
/**
* Adds the specified child to the end of the list. Equivalent to
* <code>addChildAt(child,numChildren)</code>.
*
* @param child The child to add.
*
* @throws ArgumentError If child is <code>null</code>.
* @throws RangeError If the specified index is less than zero or
* greater than the length of the list.
* @throws IllegalOperation If the child is already a child.
*
* @return The MediaElement that you pass in the child parameter.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function addChild(child:MediaElement):MediaElement
{
return addChildAt(child, numChildren);
}
/**
* Adds the child to the list at the specified index. If a child
* already exists at this index, it and all subsequent children
* will have their index positions increased by one.
*
* @param child The child to add.
* @param index The index position at which to add the child.
*
* @throws ArgumentError If child is <code>null</code>.
* @throws RangeError If the specified index is less than zero or
* greater than the length of the list.
* @throws IllegalOperation If the child is already a child.
*
* @return The MediaElement that you pass in the child parameter.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function addChildAt(child:MediaElement,index:Number):MediaElement
{
if (child == null)
{
throw new ArgumentError(OSMFStrings.getString(OSMFStrings.INVALID_PARAM));
}
if (index < 0 || index > numChildren)
{
throw new RangeError(OSMFStrings.getString(OSMFStrings.INVALID_PARAM));
}
if (children.indexOf(child) != -1)
{
throw new IllegalOperationError(OSMFStrings.getString(OSMFStrings.ALREADY_ADDED));
}
children.splice(index, 0, child);
processAddedChild(child, index);
return child;
}
/**
* Removes the specified child and returns it. Equivalent to
* <code>removeChildAt(child,getChildIndex(child))</code>.
*
* @param child The child MediaElement to remove.
*
* @return The MediaElement that you pass in the child parameter.
*
* @throws ArgumentError If the child is not a child of this composition.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function removeChild(child:MediaElement):MediaElement
{
var index:int = children.indexOf(child);
if (index == -1)
{
throw new ArgumentError(OSMFStrings.getString(OSMFStrings.INVALID_PARAM));
}
return removeChildAt(index);
}
/**
* Removes the child at the specified index and returns it. Any
* children with index positions greater than this index have their index positions decreased by one.
*
* @param index The index from which to remove the child.
*
* @return The child at that index.
*
* @throws RangeError If the specified index is less than zero or
* greater than the length of the list.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function removeChildAt(index:int):MediaElement
{
var child:MediaElement = null;
if (index >= 0 && index < children.length)
{
child = children.splice(index, 1)[0] as MediaElement;
processRemovedChild(child);
}
else
{
throw new RangeError(OSMFStrings.getString(OSMFStrings.INVALID_PARAM));
}
return child;
}
// Overrides
//
/**
* @private
*/
override public function set resource(value:MediaResourceBase):void
{
// No-op -- a CompositeElement has no notion of a media resource.
}
// Protected
//
/**
* @private
*
* Invoked when a child of the composite element is added. Subclasses
* can override to do custom processing.
*/
protected function processAddedChild(child:MediaElement, index:int):void
{
child.addEventListener(MediaErrorEvent.MEDIA_ERROR, onChildError);
compositeMetadata.addChild(child.metadata);
if (traitAggregator)
{
// The Trait Aggregator needs to keep track of the traits for the
// new child.
traitAggregator.addChildAt(child, index);
}
}
/**
* @private
*
* Invoked when a child of the composite element is removed. Subclasses
* can override to do custom processing.
*/
protected function processRemovedChild(child:MediaElement):void
{
child.removeEventListener(MediaErrorEvent.MEDIA_ERROR, onChildError);
if (traitAggregator)
{
// The Trait Aggregator no longer needs to keep track of the traits
// for this child, it has been removed.
traitAggregator.removeChild(child);
}
compositeMetadata.removeChild(child.metadata);
}
/**
* @private
*
* Invoked when the specified trait of the specified MediaTraitType is aggregated
* by the TraitAggregator used by this CompositeElement. Subclasses
* can override to do custom processing.
*/
protected function processAggregatedTrait(traitType:String, trait:MediaTraitBase):void
{
}
/**
* @private
*
* Invoked when the specified trait of the specified MediaTraitType is
* unaggregated by the TraitAggregator used by this CompositeElement.
* Subclasses can override to do custom processing.
*/
protected function processUnaggregatedTrait(traitType:String, trait:MediaTraitBase):void
{
}
/**
* @private
*/
override protected function createMetadata():Metadata
{
return new CompositeMetadata();
}
/**
* @private
*/
protected function get compositeMetadata():CompositeMetadata
{
return CompositeMetadata(metadata);
}
/**
* @private
*
* The TraitAggregator for this CompositeElement. Used to aggregate
* traits across the children of the composition. Subclasses can set
* this via <code>createTraitAggregator()</code>.
*/
protected final function get traitAggregator():TraitAggregator
{
return _traitAggregator;
}
/**
* @private
*
* Factory class used to create composite traits.
*/
protected final function get traitFactory():CompositeMediaTraitFactory
{
return _traitFactory;
}
// Internals
//
private function setupTraitAggregator():void
{
_traitAggregator = new TraitAggregator();
traitAggregator.addEventListener(TraitAggregatorEvent.TRAIT_AGGREGATED, onTraitAggregated);
traitAggregator.addEventListener(TraitAggregatorEvent.TRAIT_UNAGGREGATED, onTraitUnaggregated);
}
private function onTraitAggregated(event:TraitAggregatorEvent):void
{
processAggregatedTrait(event.traitType, event.trait);
}
private function onTraitUnaggregated(event:TraitAggregatorEvent):void
{
processUnaggregatedTrait(event.traitType, event.trait);
}
private function onChildError(event:MediaErrorEvent):void
{
dispatchEvent(event.clone());
}
private var _traitFactory:CompositeMediaTraitFactory;
private var children:Vector.<MediaElement> = new Vector.<MediaElement>();
private var _traitAggregator:TraitAggregator;
}
}