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.env.overload.Overload; 19 import org.as2lib.env.except.AbstractOperationException; 20 import org.as2lib.aop.Pointcut; 21 import org.as2lib.aop.Aspect; 22 import org.as2lib.aop.JoinPoint; 23 import org.as2lib.aop.AopConfig; 24 import org.as2lib.aop.joinpoint.AbstractJoinPoint; 25 import org.as2lib.env.reflect.MethodInfo; 26 import org.as2lib.env.reflect.PropertyInfo; 27 28 /** 29 * {@code AbstractAdvice} implements methods commonly needed by {@link Adivce} 30 * implementations. 31 * 32 * @author Simon Wacker 33 */ 34 class org.as2lib.aop.advice.AbstractAdvice extends BasicClass { 35 36 /** Signifies a before advice. */ 37 public static var BEFORE:Number = 0; 38 39 /** Signifies an around advice. */ 40 public static var AROUND:Number = 1; 41 42 /** Signifies an after advice. */ 43 public static var AFTER:Number = 2; 44 45 /** Signifies an after returning advice. */ 46 public static var AFTER_RETURNING:Number = 3; 47 48 /** Signifies an after throwing advice. */ 49 public static var AFTER_THROWING:Number = 4; 50 51 /** The pointcut that is responsible for checking if a join point is captured by this advice. */ 52 private var pointcut:Pointcut; 53 54 /** The aspect that contains this advice. */ 55 private var aspect:Aspect; 56 57 /** 58 * Constructs a new {@code AbstractAdvice} instance. 59 * 60 * @param aspect (optional) the aspect that contains this advice 61 */ 62 private function AbstractAdvice(aspect:Aspect) { 63 this.aspect = aspect; 64 } 65 66 /** 67 * Returns a proxy method that can be used instead of the original method of the 68 * {@code joinPoint}. 69 * 70 * <p>The returned proxy invokes the abstract {@code executeJoinPoint} method of 71 * this advice passing an update of the given {@code joinPoint} with the appropriate 72 * logical this and the arguments used for the proxy invocation. Sub-classes are 73 * responsible for implementing this method in the correct way. 74 * 75 * @param joinPoint the join point that represents the original method 76 * @return the proxy method 77 */ 78 public function getProxy(joinPoint:JoinPoint):Function { 79 var owner:AbstractAdvice = this; 80 var result:Function = function() { 81 // MTASC doesn't allow access to private "executeJoinPoint" 82 return owner["executeJoinPoint"](joinPoint.update(this), arguments); 83 }; 84 var method:Function; 85 if (joinPoint.getType() == AbstractJoinPoint.METHOD 86 || joinPoint.getType() == AbstractJoinPoint.CONSTRUCTOR) { 87 method = MethodInfo(joinPoint.getInfo()).getMethod(); 88 } 89 if (joinPoint.getType() == AbstractJoinPoint.SET_PROPERTY) { 90 method = PropertyInfo(joinPoint.getInfo()).getSetter().getMethod(); 91 } 92 if (joinPoint.getType() == AbstractJoinPoint.GET_PROPERTY) { 93 method = PropertyInfo(joinPoint.getInfo()).getGetter().getMethod(); 94 } 95 if (method) { 96 result.__proto__ = method.__proto__; 97 result.prototype = method.prototype; 98 result.__constructor__ = method.__constructor__; 99 result.constructor = method.constructor; 100 // just in case that any state is held in the original method, for classes this 101 // may be static variables, methods or properties 102 result.__resolve = function(name:String) { 103 return method[name]; 104 }; 105 // guarantees that the class info for the original class this proxy overwrites 106 // can still be found 107 result.valueOf = function():Object { 108 return method.valueOf(); 109 }; 110 } 111 return result; 112 } 113 114 /** 115 * Executes the woven-in code and the join point. 116 * 117 * @param joinPoint the reached join point 118 * @param args the arguments that were originally passed-to the join point 119 * @return the result to return to the invoker of the given {@code joinPoint} 120 * @throws AbstractOperationException always, because this is an abstract method 121 * that must be overridden by sub-classes 122 */ 123 private function executeJoinPoint(joinPoint:JoinPoint, args:Array) { 124 throw new AbstractOperationException("This method is marked as abstract and must be overwritten.", this, arguments); 125 } 126 127 /** 128 * Sets the aspect that contains this advice. 129 * 130 * @param aspect the new aspect containing this advice 131 */ 132 private function setAspect(aspect:Aspect):Void { 133 this.aspect = aspect; 134 } 135 136 /** 137 * Returns the aspect that contains this advice. 138 * 139 * @return the aspect that contains this advice 140 */ 141 public function getAspect(Void):Aspect { 142 return this.aspect; 143 } 144 145 /** 146 * @overload #setPointcutByPointcut 147 * @overload #setPointcutByString 148 */ 149 private function setPointcut() { 150 var overload:Overload = new Overload(this); 151 overload.addHandler([Pointcut], setPointcutByPointcut); 152 overload.addHandler([String], setPointcutByString); 153 return overload.forward(arguments); 154 } 155 156 /** 157 * Sets a new pointcut. The pointcut determines which join points are captured. 158 * 159 * @param pointcut the new pointcut to set 160 */ 161 private function setPointcutByPointcut(pointcut:Pointcut):Void { 162 this.pointcut = pointcut; 163 } 164 165 /** 166 * Sets the new pointcut by the pointcut's string representation. 167 * 168 * @param pointcut the string representation of the pointcut 169 * @return the actual pointcut set that was created by the given {@code pointcut} 170 * string 171 */ 172 private function setPointcutByString(pointcut:String):Pointcut { 173 var result:Pointcut = AopConfig.getPointcutFactory().getPointcut(pointcut); 174 setPointcutByPointcut(result); 175 return result; 176 } 177 178 /** 179 * Returns the set pointcut. 180 * 181 * @return the set pointcut 182 */ 183 public function getPointcut(Void):Pointcut { 184 return this.pointcut; 185 } 186 187 /** 188 * Checks whether this advice captures the given {@code joinPoint}. This check is 189 * done with the help of the set pointcut's {@code captures} method. 190 * 191 * <p>If there is no pointcut set, {@code true} will be returned. 192 * 193 * @param joinPoint the join point upon which to make the check 194 * @return {@code true} if the given {@code joinPoint} is captured else {@code false} 195 */ 196 public function captures(joinPoint:JoinPoint):Boolean { 197 if (!this.pointcut) return true; 198 return this.pointcut.captures(joinPoint); 199 } 200 201 }