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.core.BasicClass;
    18  import org.as2lib.aop.advice.DynamicAdviceFactory;
    19  import org.as2lib.env.overload.Overload;
    20  import org.as2lib.app.exec.Call;
    21  import org.as2lib.aop.Advice;
    22  import org.as2lib.aop.Pointcut;
    23  import org.as2lib.aop.advice.AbstractAdvice;
    24  import org.as2lib.aop.advice.AdviceFactory;
    25  import org.as2lib.aop.advice.SimpleAdviceFactory;
    26  import org.as2lib.aop.advice.DynamicBeforeAdvice;
    27  import org.as2lib.aop.advice.DynamicAroundAdvice;
    28  import org.as2lib.aop.advice.DynamicAfterAdvice;
    29  import org.as2lib.aop.advice.DynamicAfterReturningAdvice;
    30  import org.as2lib.aop.advice.DynamicAfterThrowingAdvice;
    31  import org.as2lib.env.except.IllegalArgumentException;
    32  
    33  /**
    34   * {@code SimpleDynamicAdviceFactory} manages the creation of advices for different
    35   * advice types in a simple manner.
    36   * 
    37   * @author Simon Wacker
    38   */
    39  class org.as2lib.aop.advice.SimpleDynamicAdviceFactory extends BasicClass implements DynamicAdviceFactory {
    40  	
    41  	/** All registered advices. */
    42  	private var registry:Array;
    43  	
    44  	/**
    45  	 * Constructs a new {@code SimpleDynamicAdviceFactory} instance.
    46  	 */
    47  	public function SimpleDynamicAdviceFactory(Void) {
    48  		registry = new Array();
    49  		bindAdviceFactoryByAdviceClass(AbstractAdvice.BEFORE, DynamicBeforeAdvice);
    50  		bindAdviceFactoryByAdviceClass(AbstractAdvice.AROUND, DynamicAroundAdvice);
    51  		bindAdviceFactoryByAdviceClass(AbstractAdvice.AFTER, DynamicAfterAdvice);
    52  		bindAdviceFactoryByAdviceClass(AbstractAdvice.AFTER_RETURNING, DynamicAfterReturningAdvice);
    53  		bindAdviceFactoryByAdviceClass(AbstractAdvice.AFTER_THROWING, DynamicAfterThrowingAdvice);
    54  	}
    55  	
    56  	/**
    57  	 * @overload #bindAdviceFactoryByAdviceFactory
    58  	 * @overload #bindAdviceFactoryByAdviceClass
    59  	 */
    60  	public function bindAdviceFactory() {
    61  		var o:Overload = new Overload(this);
    62  		o.addHandler(bindAdviceFactoryByAdviceFactory, [Number, AdviceFactory]);
    63  		o.addHandler(bindAdviceFactoryByAdviceClass, [Number, Function]);
    64  		return o.forward(arguments);
    65  	}
    66  	
    67  	/**
    68  	 * Binds the given {@code adviceFactory} to the {@code adviceType}. If a request is
    69  	 * made with the given {@code adviceType} as type, the bound {@code adviceFactory}
    70  	 * will be used to create the advice that gets returned.
    71  	 * 
    72  	 * <p>If there is already an advice factory bound to the given {@code adviceType}
    73  	 * the old binding gets overwritten.
    74  	 * 
    75  	 * <p>If you want to remove a binding pass an {@code adviceFactory} of value
    76  	 * {@code null} or {@code undefined}
    77  	 * 
    78  	 * @param adviceType the type to bind the {@code adviceFactory} to
    79  	 * @param adviceFactory the advice factory to bind to the given {@code adviceType}
    80  	 * @throws IllegalArgumentException if argument {@code adviceType} is {@code null}
    81  	 * or {@code undefined}
    82  	 */
    83  	public function bindAdviceFactoryByAdviceFactory(adviceType:Number, adviceFactory:AdviceFactory):Void {
    84  		if (adviceType == null) throw new IllegalArgumentException("Argument 'adviceType' must not be 'null' nor 'undefined'.", this, arguments);
    85  		registry[adviceType] = adviceFactory;
    86  	}
    87  	
    88  	/**
    89  	 * Creates a new {@link SimpleAdviceFactory} instance for the given
    90  	 * {@code adviceClass}, binds the created advice factory to the given
    91  	 * {@code adviceType} and returns the factory.
    92  	 * 
    93  	 * <p>If there is already an advice factory bound to the given {@code adviceType}
    94  	 * the old binding gets overwritten.
    95  	 * 
    96  	 * @param adviceType the type to bind the {@code adviceFactory} to
    97  	 * @param adviceClass the class of the advice to create instances of if a advice
    98  	 * request for the given {@code adviceType} is made
    99  	 * @return an advice factory configured for the given {@code adviceClass}
   100  	 * @throws IllegalArgumentException if argument {@code adviceType} is {@code null}
   101  	 * or {@code undefined}
   102  	 * @throws IllegalArgumentException if argument {@code adviceClass} is {@code null}
   103  	 * or {@code undefined}
   104  	 */
   105  	public function bindAdviceFactoryByAdviceClass(adviceType:Number, adviceClass:Function):AdviceFactory {
   106  		var factory:AdviceFactory = new SimpleAdviceFactory(adviceClass);
   107  		bindAdviceFactoryByAdviceFactory(adviceType, factory);
   108  		return factory;
   109  	}
   110  	
   111  	/**
   112  	 * @overload #getAdviceByTypeAndStringAndCall
   113  	 * @overload #getAdviceByTypeAndPointcutAndCall
   114  	 */
   115  	public function getAdvice():Advice {
   116  		var o:Overload = new Overload(this);
   117  		o.addHandler([Number, String, Call], getAdviceByTypeAndStringAndCall);
   118  		o.addHandler([Number, Pointcut, Call], getAdviceByTypeAndPointcutAndCall);
   119  		return o.forward(arguments);
   120  	}
   121  	
   122  	/**
   123  	 * Returns the advice corresponding to the given {@code type}. The returned advice
   124  	 * uses the passed-in {@code pointcut} and {@code callback}.
   125  	 * 
   126  	 * <p>The {@code callback} is invoked if the {@code execute} method of the returned
   127  	 * advice is executed.
   128  	 * 
   129  	 * <p>Commonly supported types are defined as constants in the
   130  	 * {@link AbstractAdvice} class.
   131  	 * 
   132  	 * @param type the type of the advice to return
   133  	 * @param pointcut the string representation of a pointcut used by the returned advice
   134  	 * @param callback the callback that is executed if you invoke the {@code execute}
   135  	 * method on the returned advice
   136  	 * @return the advice corresponding to the type and configured with the given
   137  	 * {@code pointcut} and {@code callback}
   138  	 * @throws IllegalArgumentException if argument {@code type} is {@code null} or
   139  	 * {@code undefined}
   140  	 */
   141  	public function getAdviceByTypeAndStringAndCall(type:Number, pointcut:String, callback:Call):Advice {
   142  		if (type == null) throw new IllegalArgumentException("Argument 'type' must not be 'null' nor 'undefined'.", this, arguments);
   143  		return AdviceFactory(registry[type]).getAdvice(pointcut, callback);
   144  	}
   145  	
   146  	/**
   147  	 * Returns the advice corresponding to the given {@code type}. The returned advice
   148  	 * uses the passed-in {@code pointcut} and {@code callback}.
   149  	 * 
   150  	 * <p>The {@code callback} is invoked if the {@code execute} method of the returned
   151  	 * advice is executed.
   152  	 * 
   153  	 * <p>Commonly supported types are defined as constants in the
   154  	 * {@link AbstractAdvice} class.
   155  	 *
   156  	 * @param type the type of the advice to return
   157  	 * @param pointcut the pointcut used by the returned advice
   158  	 * @param callback the callback that is executed if you invoke the {@code execute}
   159  	 * method on the returned advice
   160  	 * @return the advice corresponding to the type and configured with the given
   161  	 * {@code pointcut} and {@code callback}
   162  	 * @throws IllegalArgumentException if argument {@code type} is {@code null} or
   163  	 * {@code undefined}
   164  	 */
   165  	public function getAdviceByTypeAndPointcutAndCall(type:Number, pointcut:Pointcut, callback:Call):Advice {
   166  		if (type == null) throw new IllegalArgumentException("Argument 'type' must not be 'null' nor 'undefined'.", this, arguments);
   167  		return AdviceFactory(registry[type]).getAdvice(pointcut, callback);
   168  	}
   169  	
   170  }