scaffold.libs_as.org.osflash.signals.natives.NativeMappedSignal.as Maven / Gradle / Ivy
package org.osflash.signals.natives
{
import org.osflash.signals.SlotList;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.utils.getQualifiedClassName;
/**
*
* The NativeMappedSignal class is used to map/transform a native Event,
* relayed from an IEventDispatcher, into other forms of data,
* which are dispatched to all listeners.
*
* This can be used to form a border where native flash Events do not cross.
*/
public class NativeMappedSignal extends NativeRelaySignal
{
/**
* @default is null, no mapping will occur
*/
private var _mappingFunction : Function = null;
/*open for extension but closed for modifications*/
protected function get mappingFunction() : Function
{
return _mappingFunction;
}
/**
* @inheritDoc
*/
override public function get eventClass() : Class
{
return _eventClass;
}
override public function set eventClass( value : Class ) : void
{
_eventClass = value;
}
/**
* @inheritDoc
* @throws ArgumentError ArgumentError
: Invalid valueClasses argument: item at index should be a Class but was not.
*/
override public function set valueClasses( value : Array ) : void
{
_valueClasses = value ? value.slice() : [];
for( var i : int = _valueClasses.length; i--; )
{
if( !(_valueClasses[ i ] is Class) )
{
throw new ArgumentError( 'Invalid valueClasses argument: ' + 'item at index ' + i + ' should be a Class but was:<' + _valueClasses[ i ] + '>.' + getQualifiedClassName( _valueClasses[ i ] ) );
}
}
}
/**
* Creates a new NativeMappedSignal instance to map/transform a native Event,
* relayed from an IEventDispatcher, into other forms of data,
* which are dispatched to all listeners.
*
* @param target An object that implements the flash.events.IEventDispatcher interface.
* @param eventType The event string name that would normally be passed to IEventDispatcher.addEventListener().
* @param eventClass An optional class reference that enables an event type check in dispatch().
* @param mappedTypes an optional list of types that enables the checking of the types mapped from an Event.
*/
public function NativeMappedSignal( target : IEventDispatcher, eventType : String, eventClass : Class = null, ...mappedTypes )
{
super( target, eventType, eventClass );
valueClasses = mappedTypes;
}
/**
* Sets the mapping function or literal object list.
* If the argument is a list of object literals then this list is dispatched to listeners.
*
*
* signal = new NativeMappedSignal(button, MouseEvent.CLICK, MouseEvent, String).mapTo("ping")
* signal.add(function(arg:String):void { trace(arg) }) // prints "ping"
*
*
* And an example passing a list of literals:
*
*
* signal = new NativeMappedSignal(button, MouseEvent.CLICK, MouseEvent, String, int, Number).mapTo("ping", 3, 3.1415)
* signal.add(function(arg1:String, arg2:int, arg3:Number):void { trace(arg1, arg2, arg3) }) // prints "ping", 3, 3.1415
*
*
* If the argument is a function then it is called when the event this NativeMappedSignal is listening for is dispatched.
* The function should return an Array or a single object. The data returned from the function is passed along as arguments in the Signal dispatch.
* Lets look at some examples of mapping functions and the function that is called back:
*
*
* signal = new NativeMappedSignal(button, MouseEvent.CLICK, MouseEvent, String).mapTo(function():void {
* return "ping"
* })
* signal.add(function(arg:String):void { trace(arg) }) // prints "ping"
*
*
* and here's an example using a list of arguments:
*
*
* signal = new NativeMappedSignal(button, MouseEvent.CLICK, MouseEvent, String, int, Number).mapTo(function():void {
* return ["ping", 3, 3.1415]
* })
* signal.add(function(arg1:String, arg2:int, arg3:Number):void { trace(arg1, arg2, arg3) }) // prints "ping", 3, 3.1415
*
*
* You can also state your wish to receive the native Event in th mapping function by simply including an argument of type Event:
*
*
* signal = new NativeMappedSignal(button, MouseEvent.CLICK, MouseEvent, Point).mapTo(function(event:MouseEvent):void {
* return new Point(event.localX, event.localY)
* })
* signal.add(function(arg:Point):void { trace(arg) }) // prints "(x=128, y=256)"
*
*
* @param objectListOrFunction This can either be a list of object literals or a function that returns list of objects.
* @return The NativeMappedSignal object this method was called on. This allows the Signal to be defined and mapped in one statement.
* @throws ArgumentError ArgumentError
: Mapping function needs zero or one arguments of type Event
*/
public function mapTo( ...objectListOrFunction ) : NativeMappedSignal
{
if( objectListOrFunction.length == 1 && objectListOrFunction[ 0 ] is Function )
{
_mappingFunction = objectListOrFunction[ 0 ] as Function;
if( _mappingFunction.length > 1 )
{
throw new ArgumentError( 'Mapping function has ' + _mappingFunction.length + ' arguments but it needs zero or one of type Event' );
}
}
else
{
_mappingFunction = function () : Object
{
return objectListOrFunction;
};
}
return this;
}
override public function dispatchEvent( event : Event ) : Boolean
{
// TODO: this is only required for backwards compatibility
const mappedData : Object = mapEvent( event );
const numValueClasses : int = valueClasses.length;
if( mappedData is Array )
{
const valueObjects : Array = mappedData as Array;
var valueObject : Object;
var valueClass : Class;
for( var i : int = 0; i < numValueClasses; i++ )
{
valueObject = valueObjects[ i ];
valueClass = valueClasses[ i ];
if( valueObject === null || valueObject is valueClass ) continue;
throw new ArgumentError( 'Value object <' + valueObject + '> is not an instance of <' + valueClass + '>.' );
}
}
else if( numValueClasses > 1 )
{
throw new ArgumentError( 'Expected more than one value.' );
}
else if( !(mappedData is valueClasses[ 0 ]) )
{
throw new ArgumentError( 'Mapping returned ' + getQualifiedClassName( mappedData ) + ', expected ' + valueClasses[ 0 ] + '.' );
}
return super.dispatchEvent( event );
}
/**
* For usage without extension, instances of NativeMappedSignal
that are dispatching any values ( valueClasses.length > 0
),
* needs to be provided with a either a mapping function or a list of object literals.
* See mapTo
for more info.
*
* Subclasses could override this one instead of letting the environment set the mapTo,
* MAKE SURE to also override mapTo(...)
if it should not be allowed.
*
* @parameter eventFromTarget the event that was dispatched from target.
* @return An object or Array of objects mapped from an Event. The mapping of Event to data will be performed by the mapping function
* if it is set. A list of object literals can also be supplied in place of the mapping function.
* If no mapping function or object literals are supplied then an empty Array is returned or
* if valueClasses.length > 0
an ArgumentError is thrown.
*
* @see #mapTo()
*/
protected function mapEvent( eventFromTarget : Event ) : Object
{
if( mappingFunction != null )
{
if( mappingFunction.length == 1 )// todo invariant
{
return (mappingFunction)( eventFromTarget );
}
else
{
return mappingFunction();
}
}
else if( valueClasses.length == 0 )
{
return [];
}
throw new ArgumentError( "There are valueClasses set to be dispatched <" + valueClasses + "> but mappingFunction is null." );
}
override protected function onNativeEvent( event : Event ) : void
{
const mappedData : Object = mapEvent( event );
var slotsToProcess : SlotList = slots;
if( mappedData is Array )
{
if( valueClasses.length == 1 && valueClasses[ 0 ] == Array )// TODO invariant
{
while( slotsToProcess.nonEmpty )
{
slotsToProcess.head.execute1( mappedData );
slotsToProcess = slotsToProcess.tail;
}
}
else
{
const mappedDataArray : Array = mappedData as Array;
while( slotsToProcess.nonEmpty )
{
slotsToProcess.head.execute( mappedDataArray );
slotsToProcess = slotsToProcess.tail;
}
}
}
else
{
while( slotsToProcess.nonEmpty )
{
slotsToProcess.head.execute1( mappedData );
slotsToProcess = slotsToProcess.tail;
}
}
}
}
}