You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by gr...@apache.org on 2019/09/22 06:01:49 UTC

[royale-asjs] 04/09: Add support for command pattern to Crux.

This is an automated email from the ASF dual-hosted git repository.

gregdove pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git

commit f555f1c6ca02b48ec771302707585ed843c60847
Author: greg-dove <gr...@gmail.com>
AuthorDate: Sun Sep 22 09:36:30 2019 +1200

    Add support for command pattern to Crux.
---
 .../projects/Crux/src/main/royale/CruxClasses.as   |   3 +
 .../royale/org/apache/royale/crux/BeanFactory.as   |   8 +-
 .../royale/crux/metadata/InjectMetadataTag.as      |   1 +
 .../royale/crux/processors/InjectProcessor.as      |   2 +-
 .../royale/crux/utils/commands/CommandMap.as       | 216 +++++++++++++++++++++
 .../apache/royale/crux/utils/commands/ICommand.as  |  27 +++
 .../crux/utils/commands/IEventAwareCommand.as      |  30 +++
 7 files changed, 282 insertions(+), 5 deletions(-)

diff --git a/frameworks/projects/Crux/src/main/royale/CruxClasses.as b/frameworks/projects/Crux/src/main/royale/CruxClasses.as
index 6516885..705fa3b 100644
--- a/frameworks/projects/Crux/src/main/royale/CruxClasses.as
+++ b/frameworks/projects/Crux/src/main/royale/CruxClasses.as
@@ -48,6 +48,7 @@ package
         import org.apache.royale.crux.ISetUpValidator; ISetUpValidator;
         import org.apache.royale.crux.ITearDownValidator; ITearDownValidator;
         import org.apache.royale.crux.Prototype; Prototype;
+
     
     
     
@@ -108,6 +109,8 @@ package
         import org.apache.royale.crux.utils.async.AsyncTokenOperation; AsyncTokenOperation;
         import org.apache.royale.crux.utils.async.IAsynchronousEvent; IAsynchronousEvent;
         import org.apache.royale.crux.utils.async.IAsynchronousOperation; IAsynchronousOperation;
+        
+        import org.apache.royale.crux.utils.commands.CommandMap; CommandMap;
 
         import org.apache.royale.crux.utils.chain.AbstractChain; AbstractChain;
         import org.apache.royale.crux.utils.chain.AsyncCommandChainStep; AsyncCommandChainStep;
diff --git a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/BeanFactory.as b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/BeanFactory.as
index ccceea2..4c731a8 100644
--- a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/BeanFactory.as
+++ b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/BeanFactory.as
@@ -110,7 +110,7 @@ package org.apache.royale.crux
 			if( waitForSetup )
 				return;
 			
-			trace("BeanFactory completing setup");
+			//trace("BeanFactory completing setup");
 			
 			// bean setup has to be delayed until after all startup beans have been added
 			for each(var bean:Bean in beans)
@@ -352,7 +352,7 @@ package org.apache.royale.crux
 			if( bean.initialized )
 				return;
 			
-			trace("BeanFactory::setUpBean", bean);
+			//trace("BeanFactory::setUpBean", bean);
 			bean.initialized = true;
 			
 			var processor:IProcessor;
@@ -366,7 +366,7 @@ package org.apache.royale.crux
 				// Handle Metadata Processors
 				if(processor is IMetadataProcessor)
 				{
-					trace("processor is IMetadataProcessor");
+					//trace("processor is IMetadataProcessor");
 					var metadataProcessor:IMetadataProcessor = IMetadataProcessor( processor );
 					
 					// get the tags this processor is interested in
@@ -382,7 +382,7 @@ package org.apache.royale.crux
 				// Handle Bean Processors
 				if(processor is IBeanProcessor)
 				{
-					trace("processor is IBeanProcessor");
+					//trace("processor is IBeanProcessor");
 					IBeanProcessor(processor).setUpBean(bean);
 				}
 			}
diff --git a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/metadata/InjectMetadataTag.as b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/metadata/InjectMetadataTag.as
index d746b52..5843045 100644
--- a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/metadata/InjectMetadataTag.as
+++ b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/metadata/InjectMetadataTag.as
@@ -136,6 +136,7 @@ package org.apache.royale.crux.metadata
 		{
 			super.copyFrom( metadataTag );
 			
+			//@todo consider removing the 'bean' arg support or putting it only in debug-only build
 			if( hasArg( "bean" ) && hasArg( "source" ) )
 				throw new Error( "Your metadata tag defines both a bean and source attribute. source has replaced bean, please update accordingly." );
 			
diff --git a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/processors/InjectProcessor.as b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/processors/InjectProcessor.as
index 163e527..d3d130f 100644
--- a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/processors/InjectProcessor.as
+++ b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/processors/InjectProcessor.as
@@ -412,7 +412,7 @@ package org.apache.royale.crux.processors
 			}
 			catch( error:ReferenceError )
 			{
-				trace('todo')
+				trace('todo ReferenceError caught', error)
 			}
 			
 		}
diff --git a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/CommandMap.as b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/CommandMap.as
new file mode 100644
index 0000000..05913d9
--- /dev/null
+++ b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/CommandMap.as
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2010 Swiz Framework Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.royale.crux.utils.commands
+{
+	import org.apache.royale.events.Event;
+
+	import org.apache.royale.crux.Bean;
+	import org.apache.royale.crux.ICrux;
+	import org.apache.royale.crux.ICruxAware;
+	import org.apache.royale.crux.Prototype;
+	import org.apache.royale.crux.reflection.TypeCache;
+	
+	
+	/**
+	 * Class used to map events to the commands they should trigger.
+	 */
+	public class CommandMap implements ICruxAware
+	{
+		// ========================================
+		// protected properties
+		// ========================================
+		
+		/**
+		 * Backing variable for crux setter.
+		 */
+		protected var _crux:ICrux;
+		
+		/**
+		 * Object hash to hold mappings (only string keys needed).
+		 */
+		protected var map:Object = {};
+		
+		
+		// ========================================
+		// public properties
+		// ========================================
+		
+		/**
+		 * Setter to satisfy ICruxAware interface contract.
+		 * 
+		 * @see org.apache.royale.crux.core.ICruxAware
+		 */
+		public function set crux( crux:ICrux ):void
+		{
+			// if crux is null, we are being torn down
+			if( crux )
+			{
+				_crux = crux;
+				mapCommands();
+			}
+			else
+			{
+				unmapCommands();
+				_crux = crux;
+			}
+		}
+		
+		// ========================================
+		// protected methods
+		// ========================================
+		
+		/**
+		 * Handler method triggered when a mapped event is caught.
+		 */
+		protected function handleCommandEvent( event:Event ):void
+		{
+			// make sure we have a mapping
+			if( map[ event.type ] != null )
+			{
+				var indexesToClear:Array = [];
+				var mappings:Array = map[ event.type ] as Array;
+				
+				for( var i:int = 0; i < mappings.length; i++ ) 
+				{
+					// retrieve mapping
+					var commandMapping:CommandMapping = CommandMapping( mappings[ i ] );
+					
+					// validate event class
+					if( !( event is commandMapping.eventClass ) )
+						continue;
+					
+					// get our command bean
+					var commandPrototype:Bean = _crux.beanFactory.getBeanByType( commandMapping.commandClass );
+					
+					if( commandPrototype == null )
+						throw new Error( "Command bean not found for mapped event type." );
+					
+					if( commandPrototype is Prototype )
+					{
+						// get a new instance of the command class
+						var command:Object = Prototype( commandPrototype ).source;
+						
+						if( !( command is ICommand ) )
+							throw new Error( "Commands must implement org.apache.royale.crux.utils.commands.ICommand." );
+						
+						// provide event reference if command is IEventAwareCommand
+						if( command is IEventAwareCommand )
+							IEventAwareCommand( command ).event = event;
+						
+						ICommand( command ).execute();
+					}
+					else
+					{
+						throw new Error( "Commands must be provided as Prototype beans." );
+					}
+					
+					if( commandMapping.oneTime )
+						indexesToClear.push( i );
+				}
+				
+				if( indexesToClear.length > 0 )
+				{
+					for( var j:int = indexesToClear.length - 1; j > -1; j-- )
+					{
+						mappings.splice( indexesToClear[ j ], 1 );
+					}
+					
+					// if no more commands are mapped to this event type, remove from map
+					if( mappings.length == 0 )
+						delete map[ event.type ];
+				}
+			}
+		}
+		
+		/**
+		 * Abstract method that sub classes should override and populate with calls to <code>mapCommand()</code>.
+		 * Mapping commands here (and letting it be called for you) ensures all the necessary pieces have
+		 * been provided before attempting to create any mappings.
+		 */
+		protected function mapCommands():void
+		{
+			// do nothing, subclasses must override
+		}
+		
+		/**
+		 * Method that performs actual event to command mapping.
+		 */
+		protected function mapCommand( eventType:String, commandClass:Class, eventClass:Class = null, oneTime:Boolean = false ):void
+		{
+			if( map[ eventType ] == null )
+			{
+				map[ eventType ] = [ new CommandMapping( eventType, commandClass, eventClass, oneTime ) ];
+			}
+			else
+			{
+				var mappings:Array = map[ eventType ] as Array;
+				
+				for each( var cm:CommandMapping in mappings )
+				{
+					if( cm.commandClass == commandClass )
+						throw new Error( cm.commandClass + " already mapped to " + eventType );
+				}
+				
+				mappings.push( new CommandMapping( eventType, commandClass, eventClass, oneTime ) );
+			}
+			
+			// create Prototype bean for commandClass if it hasn't been created already
+			if( _crux.beanFactory.getBeanByType( commandClass ) == null )
+			{
+				// create a Prototype for adding to the BeanFactory
+				var commandPrototype:Prototype = new Prototype( commandClass );
+				commandPrototype.typeDescriptor = TypeCache.getTypeDescriptor( commandClass/*, _crux.domain*/ );
+				// add command bean for later instantiation
+				_crux.beanFactory.addBean( commandPrototype, false );
+			}
+			
+			// listen for event that will trigger this command
+			_crux.dispatcher.addEventListener( eventType, handleCommandEvent );
+		}
+		
+		protected function unmapCommands():void
+		{
+			for( var eventType:String in map )
+			{
+				_crux.dispatcher.removeEventListener( eventType, handleCommandEvent );
+				
+				delete map[ eventType ];
+			}
+			
+			map = null;
+		}
+	}
+}
+import org.apache.royale.events.Event;
+
+/**
+ * Inner class used to hold the details of a mapping.
+ */
+class CommandMapping
+{
+	public var eventType:String;
+	public var commandClass:Class;
+	public var eventClass:Class;
+	public var oneTime:Boolean;
+	
+	public function CommandMapping( eventType:String, commandClass:Class, eventClass:Class = null, oneTime:Boolean = false )
+	{
+		this.eventType = eventType;
+		this.commandClass = commandClass;
+		this.eventClass = eventClass || Event;
+		this.oneTime = oneTime;
+	}
+}
diff --git a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/ICommand.as b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/ICommand.as
new file mode 100644
index 0000000..fb07806
--- /dev/null
+++ b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/ICommand.as
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 Swiz Framework Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.royale.crux.utils.commands
+{
+	/**
+	 * Base interface that must be implemented in order to be mapped to an event in a CommandMap.
+	 * 
+	 * @see org.apache.royale.crux.utils.commands.CommandMap
+	 */
+	public interface ICommand
+	{
+		function execute():void;
+	}
+}
diff --git a/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/IEventAwareCommand.as b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/IEventAwareCommand.as
new file mode 100644
index 0000000..5c1ae17
--- /dev/null
+++ b/frameworks/projects/Crux/src/main/royale/org/apache/royale/crux/utils/commands/IEventAwareCommand.as
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 Swiz Framework Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.royale.crux.utils.commands
+{
+	import org.apache.royale.events.Event;
+	
+	/**
+	 * Interface that instructs a Crux CommandMap to supply a reference to the event
+	 * which triggered this command's execution.
+	 * 
+	 * @see org.apache.royale.crux.utils.commands.CommandMap
+	 */
+	public interface IEventAwareCommand extends ICommand
+	{
+		function set event( value:Event ):void;
+	}
+}