/flight/domain/MacroCommand.as

https://code.google.com/ · ActionScript · 228 lines · 160 code · 29 blank · 39 comment · 34 complexity · 784e900ef7fbb885f7c0638030d45482 MD5 · raw file

  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2009 Tyler Wright, Robert Taylor, Jacob Wright
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. ////////////////////////////////////////////////////////////////////////////////
  24. package flight.domain
  25. {
  26. import flash.events.Event;
  27. import flight.commands.IAsyncCommand;
  28. import flight.commands.ICommand;
  29. import flight.commands.IUndoableCommand;
  30. import flight.errors.CommandError;
  31. import flight.events.PropertyEvent;
  32. import flight.list.ArrayList;
  33. import flight.utils.getClassName;
  34. /**
  35. * The MacroCommand class is a single command that executes
  36. * a list of many commands.
  37. */
  38. [DefaultProperty("commands")]
  39. public class MacroCommand extends AsyncCommand
  40. {
  41. public var queue:Boolean = true;
  42. public var atomic:Boolean = true;
  43. private var currentCommand:ICommand;
  44. private var undone:Boolean;
  45. private var _commands:ArrayList = new ArrayList();
  46. private var _merging:Boolean = false;
  47. public function MacroCommand(commands:Array = null)
  48. {
  49. this.commands = commands;
  50. }
  51. public function get merging():Boolean
  52. {
  53. return _merging;
  54. }
  55. public function set merging(value:Boolean):void
  56. {
  57. _merging = value;
  58. }
  59. /**
  60. * The list of commands to be executed.
  61. */
  62. [ArrayElementType("flight.commands.ICommand")]
  63. [Bindable(event="commandsChange")]
  64. public function get commands():ArrayList
  65. {
  66. return _commands;
  67. }
  68. public function set commands(value:*):void
  69. {
  70. if (_commands == value) {
  71. return;
  72. }
  73. if ( !(value is ArrayList) ) {
  74. _commands.source = value;
  75. return;
  76. }
  77. var oldValue:Object = _commands;
  78. _commands = value;
  79. PropertyEvent.dispatchChange(this, "commands", oldValue, _commands);
  80. }
  81. /**
  82. * Runs through the command list in order, executing each.
  83. */
  84. override public function execute():void
  85. {
  86. currentCommand = null;
  87. executeNext();
  88. }
  89. /**
  90. * Runs through the list of commands in reverse order, verifying that
  91. * each is undable before calling undo on the individual command.
  92. */
  93. public function undo():void
  94. {
  95. var i:int = (currentCommand != null) ? _commands.getItemIndex(currentCommand) :
  96. _commands.length - 1;
  97. if (i == _commands.length - 1) {
  98. for (i; i >= 0; i--) {
  99. var command:ICommand = _commands.getItemAt(i) as ICommand;
  100. if (command is IUndoableCommand) {
  101. IUndoableCommand(command).undo();
  102. }
  103. }
  104. currentCommand = null;
  105. undone = true;
  106. }
  107. }
  108. public function redo():void
  109. {
  110. if (undone) {
  111. for (var i:int = 0; i < _commands.length; i++) {
  112. var command:ICommand = _commands.getItemAt(i) as ICommand;
  113. if (command is IUndoableCommand) {
  114. IUndoableCommand(command).redo();
  115. }
  116. }
  117. currentCommand = command;
  118. undone = false;
  119. }
  120. }
  121. public function merge(source:Object):Boolean
  122. {
  123. if (source is MacroCommand) {
  124. var sourceCommands:ArrayList = MacroCommand(source)._commands;
  125. var num:int = sourceCommands.length;
  126. for (var i:int = 0; i < num; i++) {
  127. var command:ICommand = sourceCommands.getItemAt(i) as ICommand;
  128. _commands.addItem(command);
  129. }
  130. } else if (source is ICommand) {
  131. _commands.addItem(source);
  132. } else {
  133. return false;
  134. }
  135. return true;
  136. }
  137. protected function executeNext(command:ICommand = null):void
  138. {
  139. var i:int = 0;
  140. if (command != null) {
  141. i = _commands.getItemIndex(command);
  142. if (i == -1) {
  143. throw new Error("Comand " + getClassName(command) + " does not exist in macro " + getClassName(this));
  144. }
  145. } else if (currentCommand != null) {
  146. i = _commands.getItemIndex(currentCommand) + 1;
  147. }
  148. if (i < _commands.length) {
  149. currentCommand = _commands.getItemAt(i) as ICommand;
  150. if (currentCommand is IAsyncCommand && queue) {
  151. var asyncCommand:IAsyncCommand = currentCommand as IAsyncCommand;
  152. // give internal listeners a negative priority to allow external
  153. // listeners to interrupt regular flow.
  154. asyncCommand.addEventListener(Event.COMPLETE, onAsyncComplete, false, -1)
  155. asyncCommand.addEventListener(Event.CANCEL, onAsyncCancel, false, -1);
  156. asyncCommand.execute();
  157. } else {
  158. try {
  159. currentCommand.execute();
  160. executeNext();
  161. } catch (error:CommandError) {
  162. onCommandFault(error);
  163. }
  164. }
  165. } else {
  166. response.complete(this);
  167. }
  168. }
  169. private function releaseAsyncCommand(command:IAsyncCommand):void
  170. {
  171. command.removeEventListener(Event.COMPLETE, onAsyncComplete);
  172. command.removeEventListener(Event.CANCEL, onAsyncCancel);
  173. }
  174. private function onAsyncComplete(event:Event):void
  175. {
  176. var asyncCommand:IAsyncCommand = event.target as IAsyncCommand;
  177. releaseAsyncCommand(asyncCommand);
  178. if (asyncCommand == currentCommand) {
  179. executeNext();
  180. }
  181. }
  182. private function onAsyncCancel(event:Event):void
  183. {
  184. var asyncCommand:IAsyncCommand = event.target as IAsyncCommand;
  185. releaseAsyncCommand(asyncCommand);
  186. if (asyncCommand == currentCommand) {
  187. asyncCommand.response.addFaultHandler(onCommandFault);
  188. }
  189. }
  190. private function onCommandFault(error:Error):void
  191. {
  192. if (atomic) {
  193. response.cancel(error);
  194. } else {
  195. executeNext();
  196. }
  197. }
  198. }
  199. }