1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.env.event.EventSupport;
    18  import org.as2lib.env.event.impulse.FrameImpulse;
    19  import org.as2lib.env.event.impulse.FrameImpulseListener;
    20  import org.as2lib.util.ArrayUtil;
    21  import org.as2lib.app.exec.StepByStepProcess;
    22  
    23  /**
    24   * {@code Processor} executes any {@code StepByStepProcess} for step based code execution.
    25   * 
    26   * <p>MM Flash Player has got a time limit for the execution of code. {@code Processor}
    27   * allows to seperate code into steps.
    28   * 
    29   * <p>It executes every step of a added {@code StepByStepProcess}.
    30   * If the {@code StepByStepProcess} reaches the (@link #MAX_EXECUTION_TIME}
    31   * limit during the exeuction, the {@code Processor} pauses for one frame as
    32   * workaround for this time limitation.
    33   * 
    34   * <p>Within the pause, anything may happen. It may require a complex implemnetation
    35   * strategy within the {@code StepByStepProcess}.
    36   * 
    37   * <p>{@code Processor} observes {@link FrameImpulse} if any {@code StepByStepProcess}
    38   * has to be executed. It automatically removes itself as observer if all 
    39   * processes have finished.
    40   * 
    41   * <p>{@code Processor} is built as singleton. It is possible to access it by
    42   * {@code Processor.getInstance()}.
    43   * 
    44   * @author Martin Heidegger
    45   * @version 1.0
    46   */
    47  class org.as2lib.app.exec.Processor extends EventSupport implements FrameImpulseListener {
    48  	
    49  	/** Time until pause of the execution. */
    50  	public static var MAX_EXECUTION_TIME:Number = 1500;
    51  	
    52  	/** Instance of the used {@code Processor} in {@code getInstance}. */
    53  	private static var instance:Processor;
    54  	
    55  	/**
    56  	 * Singleton - returns the default instance of the {@code Processor}
    57  	 * 
    58  	 * @return instance of the {@code Processor}
    59  	 */
    60  	public static function getInstance(Void):Processor {
    61  		if (!instance) {
    62  			instance = new Processor();
    63  		}
    64  		return instance;
    65  	}
    66  	
    67  	/** Flag if the processor is running (connected to the FrameImpulseListener) */
    68  	private var running:Boolean = false;
    69  	
    70  	/** Current process to handle */
    71  	private var current:Number;
    72  	
    73  	/** List of all processes currently executing */
    74  	private var processList:Array;
    75  	
    76  	/**
    77  	 * Constructs a new {@code Processor} instance.
    78  	 */
    79  	private function Processor(Void) {
    80  		processList = new Array();
    81  	}
    82  	
    83  	/**
    84  	 * Adds a new {@code StepByStepProcess} to the execution list.
    85  	 * 
    86  	 * <p>It is possible that a {@code StepByStepProcess} can be added twice.
    87  	 * 
    88  	 * <p>The {@code Processor} will automatically awake from stand-by.
    89  	 * 
    90  	 * @param p {@code StepByStepProcess} to be added
    91  	 */
    92  	public function addStepByStepProcess(p:StepByStepProcess):Void {
    93  		processList.push(p);
    94  		awakeFromStandBy();
    95  	}
    96  	
    97  	/**
    98  	 * Removes all occurances of {@code StepByStepProcess}.
    99  	 * 
   100  	 * @param p {@code StepByStepProcess} to be removed
   101  	 */
   102  	public function removeStepByStepProcess(p:StepByStepProcess):Void {
   103  		var formerLength = processList.length;
   104  		var result:Array = ArrayUtil.removeElement(processList, p);
   105  		var i:Number = result.length;
   106  		// Shift the current cursor
   107  		// Backward processing to ensure the correct size.
   108  		while (--i-(-1)) {
   109  			if (current > result[i]) {
   110  				current --;
   111  			}
   112  		}
   113  	}
   114  	
   115  	/**
   116  	 * Restart listening to the {@code FrameImpulse}
   117  	 */
   118  	private function awakeFromStandBy(Void):Void {
   119  		if (!running) {
   120  			running = true;
   121  			current = 0;
   122  			FrameImpulse.getInstance().addFrameImpulseListener(this);
   123  		}
   124  	}
   125  	
   126  	/**
   127  	 * Stop listening to the {@code FrameImpulse}.
   128  	 */
   129  	private function gotoStandBy(Void):Void {
   130  		running = false;
   131  		FrameImpulse.getInstance().removeFrameImpulseListener(this);
   132  	}
   133  	
   134  	/**
   135  	 * Handling of the event of {@link FrameImpulse}.
   136  	 * 
   137  	 * @param impulse {@code FrameImpulse} that executes the impulse
   138  	 */
   139  	public function onFrameImpulse(impulse:FrameImpulse):Void {
   140  		var startTime:Number = getTimer();
   141  		while (current < processList.length) {
   142  			
   143  			var currentProcessable:StepByStepProcess = processList[current];
   144  			while (!currentProcessable.hasFinished()) {
   145  				
   146  				if (startTime+MAX_EXECUTION_TIME < getTimer() || currentProcessable.isPaused()) {
   147  					return;
   148  				}
   149  				
   150  				currentProcessable.nextStep();
   151  			}
   152  			current ++;
   153  		}
   154  		processList = new Array();
   155  		gotoStandBy();
   156  	}
   157  
   158  }