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.app.exec.Executable;
    18  import org.as2lib.app.exec.Call;
    19  import org.as2lib.core.BasicClass;
    20  import org.as2lib.env.event.impulse.ImpulseListener;
    21  import org.as2lib.env.except.IllegalArgumentException;
    22  import org.as2lib.util.ArrayUtil;
    23  
    24  /**
    25   * {@code AbstractImpulse} is a helper class that contains shared API to be used
    26   * by different {@link org.as2lib.env.event.impulse.Impulse} Implementations.
    27   * 
    28   * @author Martin Heidegger
    29   * @version 1.0
    30   */
    31  class org.as2lib.env.event.impulse.AbstractImpulse extends BasicClass {
    32  	
    33  	/** Broadcaster for connected executables */
    34  	private var execBroadcaster:Object;
    35  	
    36  	/** Broadcaster for connected impulselisteners */
    37  	private var impulseBroadcaster:Object;
    38  	
    39  	/**
    40  	 * Constructs a new impulse.
    41  	 */
    42  	public function AbstractImpulse() {
    43  		// Creation of ASBroadcasters for the events.
    44  		execBroadcaster = new Object();
    45  		AsBroadcaster.initialize(execBroadcaster);
    46  		impulseBroadcaster = new Object();
    47  		AsBroadcaster.initialize(impulseBroadcaster);
    48  	}	
    49  	
    50  	/**
    51  	 * Connects a executable as listener to the frame execution.
    52  	 * 
    53  	 * @param exe Executable to be added as listener
    54  	 */
    55  	public function connectExecutable(exe:Executable):Void {
    56  		addListener(exe);
    57  	}
    58  	
    59  	/**
    60  	 * Adds a list of listeners to listen to the impulse events.
    61  	 * 
    62  	 * @param listeners List of listeners to be added.
    63  	 * @throws IllegalArgumentException if a listener could not be added.
    64  	 */
    65  	public function addAllListeners(listeners:Array):Void {
    66  		for(var i=0; i<listeners.length; i++) {
    67  			addListener(listeners[i]);
    68  		}
    69  	}
    70  	
    71  	/**
    72  	 * Method to add any supported listener to the FrameImpulse.
    73  	 * 
    74  	 * <p>Adds a listener to the Impulse. The listener will be informed on
    75  	 * each frame change.
    76  	 * 
    77  	 * <p>Note: If a certain listener implements more than one supported event it
    78  	 * will listen to all of them at one execution (execute, onFrameImpulse,
    79  	 * onImpulse).
    80  	 * 
    81  	 * @param listener to be added.
    82  	 * @throws IllegalArgumentException if the listener doesn't match any type.
    83  	 */
    84  	public function addListener(listener):Void {
    85  		var notAdded:Boolean = true;
    86  		if(listener instanceof Executable) {
    87  			execBroadcaster.addListener(listener);
    88  			notAdded = false;
    89  		}
    90  		if(listener instanceof ImpulseListener) {
    91  			impulseBroadcaster.addListener(listener);
    92  			notAdded = false;
    93  		}
    94  		if(notAdded) {
    95  			throw new IllegalArgumentException("Passed listener doesn't match"
    96  				+" any possible listener type.", this
    97  				, arguments);
    98  		}
    99  	}
   100  	
   101  	/**
   102  	 * Methode to add a {@link ImpulseListener} as listener to the Impulse. 
   103  	 * 
   104  	 * <p>Some parts of the code get better readable if you use a complete
   105  	 * clear name like "onImpulse" to define your code. With
   106  	 * {@code .addImpulseListener} you can add a listener that specially
   107  	 * listens only to this naming of the same event that will be executed as
   108  	 * "execute".
   109  	 * 
   110  	 * <p>Example:
   111  	 * 
   112  	 * <p>Listener:
   113  	 * <code>
   114  	 *   import org.as2lib.env.event.impulse.ImpulseListener;
   115  	 *   import org.as2lib.env.event.impulse.Impulse;
   116  	 *   
   117  	 *   class TraceTimeImpulseListener implements ImpulseListener {
   118  	 *     public function onImpulse(impulse:Impulse):Void {
   119  	 *       trace("Impulse executed at "+getTimer());
   120  	 *     }
   121  	 *   }
   122  	 * </code>
   123  	 * 
   124  	 * <p>Usage:
   125  	 * <code>
   126  	 *   import org.as2lib.env.event.impulse.FrameImpulse;
   127  	 *   
   128  	 *   var impulse:FrameImpulse = FrameImpulse.getInstance();
   129  	 *   impulse.addImpulseListener(new TraceTimeImpulseListener());
   130  	 * </code>
   131  	 * 
   132  	 * @param listener Listener to be added.
   133  	 */
   134  	public function addImpulseListener(listener:ImpulseListener):Void {
   135  		addListener(listener);
   136  	}
   137  	
   138  	/**
   139  	 * Removes a listener of any type that might be added.
   140  	 * 
   141  	 * @param listener Listener to be removed.
   142  	 * @throws IllegalArgumentException if you pass a listener that is of a
   143  	 *         illegal type.
   144  	 */
   145  	public function removeListener(listener):Void {
   146  		var notRemoved:Boolean = true;
   147  		if(listener instanceof Executable) {
   148  			execBroadcaster.removeListener(listener);
   149  			notRemoved = false;
   150  		}
   151  		if(listener instanceof ImpulseListener) {
   152  			impulseBroadcaster.removeListener(listener);
   153  			notRemoved = false;
   154  		}
   155  		if(notRemoved) {
   156  			throw new IllegalArgumentException("Passed listener doesn't match"
   157  				+" any possible listener type.", this
   158  				, arguments);
   159  		}
   160  	}
   161  	
   162  	/**
   163  	 * Disconnects a {@link Executable} from listening to the impulse.
   164  	 * 
   165  	 * @param exe {@link Executable} to disconnect.
   166  	 */
   167  	public function disconnectExecutable(exe:Executable):Void {
   168  		removeListener(exe);
   169  	}
   170  		
   171  	/**
   172  	 * Removes a {@link ImpulseListener} from listening to the impulse.
   173  	 * 
   174  	 * @param listener {@link ImpulseListener} to remove from listening.
   175  	 */
   176  	public function removeImpulseListener(listener:ImpulseListener):Void {
   177  		removeListener(listener);
   178  	}
   179  	
   180  	/**
   181  	 * Removes all added Listeners from listening to the impulse.
   182  	 */
   183  	public function removeAllListeners(Void):Void {
   184  		removeAllImpulseListeners();
   185  		disconnectAllExecutables();
   186  	}
   187  	
   188  	/**
   189  	 * Removes all added {@link ImpulseListener}s from listening to the impulse.
   190  	 */
   191  	public function removeAllImpulseListeners(Void):Void {
   192  		var c:Call = new Call(this, removeListener);
   193  		c.forEach(impulseBroadcaster._listeners);
   194  	}
   195  	
   196  	/**
   197  	 * Disconnects all connected {@link Executable}s from the impulse.
   198  	 */
   199  	public function disconnectAllExecutables(Void):Void {
   200  		var c:Call = new Call(this, removeListener);
   201  		c.forEach(execBroadcaster._listeners);
   202  	}
   203  	
   204  	
   205  	/**
   206  	 * Getter for the list of all added listeners.
   207  	 * 
   208  	 * <p>This method returns a list of all listeners added with eihter
   209  	 * {@link #connectExecutable}, {@link #addListener} or
   210  	 * {@link #addImpulseListener}
   211  	 * 
   212  	 * @return List that contains all added listeners.
   213  	 */
   214  	public function getAllListeners(Void):Array {
   215  		var result:Array = new Array();
   216  		result = result.concat(getAllConnectedExecutables());
   217  		result = result.concat(getAllImpulseListeners());
   218  		return result;
   219  	}
   220  	
   221  	/**
   222  	 * Getter for the list of all connected {@link Executable}s.
   223  	 * 
   224  	 * @return List that contains all connected {@link Executable}s.
   225  	 */
   226  	public function getAllConnectedExecutables(Void):Array {
   227  		return execBroadcaster._listeners.concat();
   228  	}
   229  	
   230  	/**
   231  	 * Getter for the list of all added {@link ImpulseListener}s.
   232  	 * 
   233  	 * @return List that contains all added {@link ImpulseListener}s.
   234  	 */
   235  	public function getAllImpulseListeners(Void):Array {
   236  		return impulseBroadcaster._listeners.concat();
   237  	}
   238  	
   239  	/**
   240  	 * Adds a list of {@link ImpulseListener}s as listener to the events.
   241  	 * 
   242  	 * @param listeners List of all listeners to add.
   243  	 * @throws IllegalArgumentException if one listener didn't match to any listener type.
   244  	 * @see #addListener
   245  	 */
   246  	public function addAllImpulseListeners(listeners:Array):Void {
   247  		for(var i=0; i<listeners.length; i++) {
   248  			addListener(listeners[i]);
   249  		}
   250  	}
   251  	
   252  	/**
   253  	 * Connects a list of {@link Executables}s to the impulse.
   254  	 * 
   255  	 * @param listeners List of all listeners to add.
   256  	 * @throws IllegalArgumentException if one listener didn't match to any listener type.
   257  	 * @see #addListener
   258  	 */
   259  	public function connectAllExecutables(executables:Array):Void {
   260  		for(var i=0; i<executables.length; i++) {
   261  			addListener(executables[i]);
   262  		}
   263  	}
   264  	
   265  	/**
   266  	 * Validates if a certain listener of any type is currently added to the
   267  	 * impulse.
   268  	 * 
   269  	 * @param listener Listener to be validated.
   270  	 * @return {@code true} if the certain executable is connected.
   271  	 * @see #addListener
   272  	 */
   273  	public function hasListener(listener):Boolean {
   274  		if (hasImpulseListener(listener)) {
   275  			return true;
   276  		}
   277  		if (isExecutableConnected(listener)) {
   278  			return true;
   279  		}
   280  		return false;
   281  	}	
   282  	
   283  	/**
   284  	 * Validates if a certain {@link ImpulseListener} is currently added to the
   285  	 * impulse.
   286  	 * 
   287  	 * @param listener {@link ImpulseListener} to be validated.
   288  	 * @return {@code true} if the certain executable is connected.
   289  	 */
   290  	public function hasImpulseListener(listener:ImpulseListener):Boolean {
   291  		return ArrayUtil.contains(impulseBroadcaster._listeners, listener);
   292  	}
   293  	
   294  	/**
   295  	 * Validates if a certain {@link Executable} is currently connected to the
   296  	 * impulse.
   297  	 * 
   298  	 * @param exe {@link Executable} to be validated.
   299  	 * @return {@code true} if the certain executable is connected.
   300  	 */
   301  	public function isExecutableConnected(exe:Executable):Boolean {
   302  		return ArrayUtil.contains(execBroadcaster._listeners, exe);
   303  	}
   304  	
   305  }