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

scaffold.libs_as.org.osflash.signals.natives.NativeSignal.as Maven / Gradle / Ivy

package org.osflash.signals.natives
{
	import org.osflash.signals.ISlot;
	import org.osflash.signals.Slot;
	import org.osflash.signals.SlotList;

	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.IEventDispatcher;

	/**
	 * Allows the eventClass to be set in MXML, e.g.
	 * {MouseEvent}
	 */
	[DefaultProperty("eventClass")]
	/**
	 * The NativeSignal class provides a strongly-typed facade for an IEventDispatcher.
	 * A NativeSignal is essentially a mini-dispatcher locked to a specific event type and class.
	 * It can become part of an interface.
	 */ public class NativeSignal implements INativeDispatcher
	{
		protected var slots : SlotList;

		protected var _target : IEventDispatcher;

		/** @inheritDoc */
		public function get target() : IEventDispatcher
		{
			return _target;
		}

		public function set target( value : IEventDispatcher ) : void
		{
			if( value == _target ) return;
			if( _target ) removeAll();
			_target = value;
		}

		protected var _eventType : String;

		/** @inheritDoc */
		public function get eventType() : String
		{
			return _eventType;
		}

		public function set eventType( value : String ) : void
		{
			_eventType = value;
		}

		protected var _eventClass : Class;

		/** @inheritDoc */
		public function get eventClass() : Class
		{
			return _eventClass;
		}

		public function set eventClass( value : Class ) : void
		{
			_eventClass = value || Event;
			_valueClasses = [ _eventClass ];
		}

		protected var _valueClasses : Array;

		/** @inheritDoc */
		[ArrayElementType("Class")]
		public function get valueClasses() : Array
		{
			return _valueClasses;
		}

		public function set valueClasses( value : Array ) : void
		{
			eventClass = value && value.length > 0 ? value[ 0 ] : null;
		}

		/** @inheritDoc */
		public function get numListeners() : uint
		{
			return slots.length;
		}

		/**
		 * Creates a NativeSignal instance to dispatch events on behalf of a target object.
		 * @param    target The object on whose behalf the signal is dispatching events.
		 * @param    eventType The type of Event permitted to be dispatched from this signal. Corresponds to Event.type.
		 * @param    eventClass An optional class reference that enables an event type check in dispatch(). Defaults to flash.events.Event if omitted.
		 */
		public function NativeSignal( target : IEventDispatcher = null, eventType : String = "", eventClass : Class = null )
		{
			slots = SlotList.NIL;
			this.target = target;
			this.eventType = eventType;
			this.eventClass = eventClass;
		}

		/**
		 * @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.
		 * @throws ArgumentError ArgumentError: Target object cannot be null.
		 */
		public function add( listener : Function ) : ISlot
		{
			return addWithPriority( listener );
		}

		/**
		 * @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.
		 * @throws ArgumentError ArgumentError: Target object cannot be null.
		 */
		public function addWithPriority( listener : Function, priority : int = 0 ) : ISlot
		{
			return registerListenerWithPriority( listener, false, priority );
		}

		/**
		 * @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.
		 * @throws ArgumentError ArgumentError: Target object cannot be null.
		 */
		public function addOnce( listener : Function ) : ISlot
		{
			return addOnceWithPriority( listener );
		}

		/**
		 * @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.
		 * @throws ArgumentError ArgumentError: Target object cannot be null.
		 */
		public function addOnceWithPriority( listener : Function, priority : int = 0 ) : ISlot
		{
			return registerListenerWithPriority( listener, true, priority );
		}

		/** @inheritDoc */
		public function remove( listener : Function ) : ISlot
		{
			const slot : ISlot = slots.find( listener );
			if( !slot ) return null;
			_target.removeEventListener( _eventType, slot.execute1 );
			slots = slots.filterNot( listener );
			return slot;
		}

		/** @inheritDoc */
		public function removeAll() : void
		{
			var slotsToProcess : SlotList = slots;
			while( slotsToProcess.nonEmpty )
			{
				target.removeEventListener( _eventType, slotsToProcess.head.execute1 );
				slotsToProcess = slotsToProcess.tail;
			}
			slots = SlotList.NIL;
		}

		/**
		 * @inheritDoc
		 * @throws ArgumentError ArgumentError: Event object expected.
		 * @throws ArgumentError ArgumentError: No more than one Event object expected.
		 * @throws ArgumentError ArgumentError: Target object cannot be null.
		 * @throws ArgumentError ArgumentError: Event object cannot be null.
		 * @throws ArgumentError ArgumentError: Event object [event] is not an instance of [eventClass].
		 * @throws ArgumentError ArgumentError: Event object has incorrect type. Expected [eventType] but was [event.type].
		 */
		public function dispatch( ...valueObjects ) : void
		{
			// TODO: check if ...valueObjects can ever be null.
			if( null == valueObjects ) throw new ArgumentError( 'Event object expected.' );

			if( valueObjects.length != 1 ) throw new ArgumentError( 'No more than one Event object expected.' );

			dispatchEvent( valueObjects[ 0 ] as Event );
		}

		protected function registerListenerWithPriority( listener : Function, once : Boolean = false, priority : int = 0 ) : ISlot
		{
			if( !target ) throw new ArgumentError( 'Target object cannot be null.' );

			if( registrationPossible( listener, once ) )
			{
				const slot : ISlot = new Slot( listener, this, once, priority );
				// Not necessary to insertWithPriority() because the target takes care of ordering.
				slots = slots.prepend( slot );
				_target.addEventListener( _eventType, slot.execute1, false, priority );
				return slot;
			}

			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 )
			{
				if( existingSlot.once != once )
				{
					// If the listener was previously added, definitely don't add it again.
					// But throw an exception if their once value differs.
					throw new IllegalOperationError( 'You cannot addOnce() then add() the same listener without removing the relationship first.' );
				}

				// Listener was already added.
				return false;
			}

			// This listener has not been added before.
			return true;
		}

		/**
		 * Unlike other signals, NativeSignal does not dispatch null
		 * because it causes an exception in EventDispatcher.
		 * @inheritDoc
		 * @throws ArgumentError ArgumentError: Target object cannot be null.
		 * @throws ArgumentError ArgumentError: Event object cannot be null.
		 * @throws ArgumentError ArgumentError: Event object [event] is not an instance of [eventClass].
		 * @throws ArgumentError ArgumentError: Event object has incorrect type. Expected [eventType] but was [event.type].
		 */
		public function dispatchEvent( event : Event ) : Boolean
		{
			if( !target ) throw new ArgumentError( 'Target object cannot be null.' );
			if( !event ) throw new ArgumentError( 'Event object cannot be null.' );

			if( !(event is eventClass) )
				throw new ArgumentError( 'Event object ' + event + ' is not an instance of ' + eventClass + '.' );

			if( event.type != eventType )
				throw new ArgumentError( 'Event object has incorrect type. Expected <' + eventType + '> but was <' + event.type + '>.' );

			return target.dispatchEvent( event );
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy