Anonymous avatar Anonymous committed 180d6e1

Simplified SingleSignal.

Comments (0)

Files changed (2)

src/org/osflash/signals/SingleSignal.as

 	import flash.errors.IllegalOperationError;
 	import flash.utils.getQualifiedClassName;
 	
-
 	/** 
 	 * Allows the valueClasses to be set in MXML, e.g.
 	 * <signals:Signal id="nameChanged">{[String, uint]}</signals:Signal>
 	[DefaultProperty("valueClasses")]	
 	
 	/**
-	 * Signal dispatches events to a single listener.
-	 * It is inspired by C# events and delegates, and by
-	 * <a target="_top" href="http://en.wikipedia.org/wiki/Signals_and_slots">signals and slots</a>
-	 * in Qt.
-	 * A Signal adds event dispatching functionality through composition and interfaces,
-	 * rather than inheriting from a dispatcher.
-	 * <br/><br/>
-	 * Project home: <a target="_top" href="http://github.com/robertpenner/as3-signals/">http://github.com/robertpenner/as3-signals/</a>
+	 * A SingleSignal can have only one listener.
 	 */
 	public class SingleSignal implements ISignal
 	{
 		protected var slot:Slot;
 		
 		/**
-		 * Creates a Signal instance to dispatch value objects.
+		 * Creates a SingleSignal 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)
 		}
 		
 		/** @inheritDoc */
-		public function get numListeners():uint { return null == slot ? 0 : 1; }
+		public function get numListeners():uint { return slot ? 1 : 0; }
 		
 		/** @inheritDoc */
 		//TODO: @throws
 		/** @inheritDoc */
 		public function remove(listener:Function):ISlot
 		{
-			if(slot && slot.listener == listener)
+			if (slot && slot.listener == listener)
 			{
-				// This will need to be a clone I think
-				const bind:ISlot = slot;
-				
+				const theSlot:ISlot = slot;
 				slot = null;
-				
-				return bind;
+				return theSlot;
 			}
 
 			return null;
 		/** @inheritDoc */
 		public function removeAll():void
 		{
-			if(slot) slot.remove();
-			slot = null;
+			if (slot) slot.remove();
 		}
 		
 		/** @inheritDoc */
 		public function dispatch(...valueObjects):void
 		{
-			//
-			// Validate value objects against pre-defined value classes.
-			//
+			// If valueClasses is empty, value objects are not type-checked. 
+			const numValueClasses:int = _valueClasses.length;
+			const numValueObjects:int = valueObjects.length;
 
-			var valueObject:Object;
-			var valueClass:Class;
-
-			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. '+
 					numValueObjects+'.');
 			}
 			
-			for (var i: int = 0; i < numValueClasses; ++i)
+			// Cannot dispatch differently typed objects than declared classes.
+			for (var i:int = 0; i < numValueClasses; i++)
 			{
-				valueObject = valueObjects[i];
-				valueClass = valueClasses[i];
-
-				if (valueObject === null || valueObject is valueClass) continue;
+				// Optimized for the optimistic case that values are correct.
+				if (valueObjects[i] is _valueClasses[i] || valueObjects[i] === null) 
+					continue;
 					
-				throw new ArgumentError('Value object <'+valueObject
-					+'> is not an instance of <'+valueClass+'>.');
+				throw new ArgumentError('Value object <'+valueObjects[i]
+					+'> is not an instance of <'+_valueClasses[i]+'>.');
 			}
 
-			//
-			// Broadcast to listeners.
-			//
-			
-			if (null != slot)
+			// Broadcast to the one listener.
+			if (slot)
 			{
 				slot.execute(valueObjects);
 			}
 		
 		protected function registerListener(listener:Function, once:Boolean = false):ISlot
 		{
-			if (null != slot) 
+			if (slot) 
 			{
-				//
 				// If the listener exits previously added, definitely don't add it.
-				//
-				
 				throw new IllegalOperationError('You cannot add or addOnce with a listener already added, remove the current listener first.');
 			}
 			
-			if (!slot || verifyRegistrationOf(listener, once))
-			{
-				slot = new Slot(listener, this, once);
-				
-				return slot;
-			}
-			
-			return slot;
+			return (slot = new Slot(listener, this, once));
 		}
 
-		protected function verifyRegistrationOf(listener: Function,  once: Boolean): Boolean
-		{
-			if(!slot || slot.listener != listener) return false;
-			
-			const existingSlot:ISlot = slot;
-
-			if (null != 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;
-		}
 	}
 }

tests/org/osflash/signals/SingleSignalTest.as

 
 		
 		[Test(expects="flash.errors.IllegalOperationError")]
-		public function addOnce_same_listener_twice_should_only_add_it_once():void
+		public function addOnce_same_listener_twice_should_throw_error():void
 		{
 			var func:Function = newEmptyHandler();
 			signal.addOnce(func);
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.