All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scaffold.libs_as.org.osflash.signals.OnceSignal.as Maven / Gradle / Ivy

package org.osflash.signals
{
	import flash.errors.IllegalOperationError;
	import flash.utils.getQualifiedClassName;

	/**
	 * Allows the valueClasses to be set in MXML, e.g.
	 * {[String, uint]}
	 */
	[DefaultProperty("valueClasses")]
	/**
	 * Signal dispatches events to multiple listeners.
	 * It is inspired by C# events and delegates, and by
	 * signals and slots
	 * in Qt.
	 * A Signal adds event dispatching functionality through composition and interfaces,
	 * rather than inheriting from a dispatcher.
	 * 

* Project home: http://github.com/robertpenner/as3-signals/ */ public class OnceSignal implements IOnceSignal { protected var slots : SlotList = SlotList.NIL; // of Class protected var _valueClasses : Array; /** * @inheritDoc * @throws ArgumentError ArgumentError: Invalid valueClasses argument: item at index should be a Class but was not. */ [ArrayElementType("Class")] public function get valueClasses() : Array { return _valueClasses; } public function set valueClasses( value : Array ) : void { // Clone so the Array cannot be affected from outside. _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 ] ) ); } } } /** @inheritDoc */ public function get numListeners() : uint { return slots.length; } /** * Creates a Signal instance to dispatch value objects. * @param valueClasses Any number of class references that enable type checks in dispatch(). * For example, new Signal(String, uint) * would allow: signal.dispatch("the Answer", 42) * but not: signal.dispatch(true, 42.5) * nor: signal.dispatch() * * NOTE: In AS3, subclasses cannot call super.apply(null, valueClasses), * but this constructor has logic to support super(valueClasses). */ public function OnceSignal( ...valueClasses ) { // Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses). this.valueClasses = (valueClasses.length == 1 && valueClasses[ 0 ] is Array) ? valueClasses[ 0 ] : valueClasses; } /** * @inheritDoc * @throws flash.errors.IllegalOperationError IllegalOperationError: You cannot addOnce() then add() the same listener without removing the relationship first. * @throws ArgumentError ArgumentError: Given listener is null. */ public function addOnce( listener : Function ) : ISlot { return registerListener( listener, true ); } /** @inheritDoc */ public function remove( listener : Function ) : ISlot { const slot : ISlot = slots.find( listener ); if( !slot ) return null; slots = slots.filterNot( listener ); return slot; } /** @inheritDoc */ public function removeAll() : void { slots = SlotList.NIL; } /** * @inheritDoc * @throws ArgumentError ArgumentError: Incorrect number of arguments. * @throws ArgumentError ArgumentError: Value object is not an instance of the appropriate valueClasses Class. */ public function dispatch( ...valueObjects ) : void { // If valueClasses is empty, value objects are not type-checked. const numValueClasses : int = _valueClasses.length; const numValueObjects : int = valueObjects.length; // Cannot dispatch fewer objects than declared classes. if( numValueObjects < numValueClasses ) { throw new ArgumentError( 'Incorrect number of arguments. ' + 'Expected at least ' + numValueClasses + ' but received ' + numValueObjects + '.' ); } // Cannot dispatch differently typed objects than declared classes. for( var i : int = 0; i < numValueClasses; i++ ) { // Optimized for the optimistic case that values are correct. if( valueObjects[ i ] is _valueClasses[ i ] || valueObjects[ i ] === null ) continue; throw new ArgumentError( 'Value object <' + valueObjects[ i ] + '> is not an instance of <' + _valueClasses[ i ] + '>.' ); } // Broadcast to listeners. var slotsToProcess : SlotList = slots; if( slotsToProcess.nonEmpty ) { while( slotsToProcess.nonEmpty ) { slotsToProcess.head.execute( valueObjects ); slotsToProcess = slotsToProcess.tail; } } } protected function registerListener( listener : Function, once : Boolean = false ) : ISlot { if( registrationPossible( listener, once ) ) { const newSlot : ISlot = new Slot( listener, this, once ); slots = slots.prepend( newSlot ); return newSlot; } return slots.find( listener ); } protected function registrationPossible( listener : Function, once : Boolean ) : Boolean { if( !slots.nonEmpty ) return true; const existingSlot : ISlot = slots.find( listener ); if( !existingSlot ) return true; if( existingSlot.once != once ) { // If the listener was previously added, definitely don't add it again. // But throw an exception if their once values differ. throw new IllegalOperationError( 'You cannot addOnce() then add() the same listener without removing the relationship first.' ); } return false; // Listener was already registered. } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy