View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.transform.delegation;
9   
10  import org.codehaus.aspectwerkz.definition.IntroductionDefinition;
11  import org.codehaus.aspectwerkz.definition.SystemDefinition;
12  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
13  import org.codehaus.aspectwerkz.expression.ExpressionContext;
14  import org.codehaus.aspectwerkz.expression.PointcutType;
15  import org.codehaus.aspectwerkz.reflect.ClassInfo;
16  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
17  import org.codehaus.aspectwerkz.reflect.MethodInfo;
18  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistClassInfo;
19  import org.codehaus.aspectwerkz.transform.Context;
20  import org.codehaus.aspectwerkz.transform.TransformationUtil;
21  import org.codehaus.aspectwerkz.transform.Transformer;
22  import org.codehaus.aspectwerkz.transform.TransformationConstants;
23  import org.codehaus.aspectwerkz.transform.TransformationConstants;
24  
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import javassist.CtClass;
29  import javassist.CtMethod;
30  import javassist.CtNewMethod;
31  import javassist.Modifier;
32  import javassist.NotFoundException;
33  
34  /***
35   * Adds an Introductions to classes.
36   * 
37   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
38   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
39   */
40  public class AddImplementationTransformer implements Transformer {
41      /***
42       * Adds introductions to a class.
43       * 
44       * @param context the transformation context
45       * @param klass the class
46       */
47      public void transform(final Context context, final Klass klass) throws NotFoundException {
48          List definitions = context.getDefinitions();
49  
50          // loop over all the definitions
51          for (Iterator it = definitions.iterator(); it.hasNext();) {
52              SystemDefinition definition = (SystemDefinition) it.next();
53              final CtClass ctClass = klass.getCtClass();
54              ClassInfo classInfo = JavassistClassInfo.getClassInfo(ctClass, context.getLoader());
55              ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo);
56              if (classFilter(ctClass, ctx, definition)) {
57                  continue;
58              }
59              addMethodIntroductions(definition, context, ctx, ctClass);
60          }
61      }
62  
63      /***
64       * Adds introductions to the class.
65       * 
66       * @param definition the definition
67       * @param context the transformation context
68       * @param ctx the context
69       * @param ctClass the class gen
70       */
71      private void addMethodIntroductions(
72          final SystemDefinition definition,
73          final Context context,
74          final ExpressionContext ctx,
75          final CtClass ctClass) {
76          List introductionDefs = definition.getIntroductionDefinitions(ctx);
77          boolean isClassAdvised = false;
78          for (Iterator it = introductionDefs.iterator(); it.hasNext();) {
79              IntroductionDefinition introDef = (IntroductionDefinition) it.next();
80              int methodIndex = 0;
81              List methodsToIntroduce = introDef.getMethodsToIntroduce();
82  
83              for (Iterator mit = methodsToIntroduce.iterator(); mit.hasNext(); methodIndex++) {
84                  MethodInfo methodToIntroduce = (MethodInfo) mit.next();
85                  if (methodToIntroduce == null) {
86                      continue;
87                  }
88                  createProxyMethod(
89                      ctClass,
90                      methodToIntroduce,
91                      definition.getMixinIndexByName(introDef.getName()),
92                      methodIndex,
93                      definition,
94                      context);
95                  isClassAdvised = true;
96              }
97          }
98          if (isClassAdvised) {
99              context.markAsAdvised();
100             // weaved class might use the added interface to match pointcuts so mark the class info as dirty
101             JavassistClassInfo.markDirty(ctClass, context.getLoader());
102         }
103     }
104 
105     /***
106      * Creates a proxy method for the introduced method.
107      * 
108      * @param ctClass the class gen
109      * @param methodInfo the info for the method
110      * @param mixinIndex the mixin index
111      * @param methodIndex the method index
112      * @param definition the definition
113      * @param context the context
114      */
115     private void createProxyMethod(
116         final CtClass ctClass,
117         final MethodInfo methodInfo,
118         final int mixinIndex,
119         final int methodIndex,
120         final SystemDefinition definition,
121         final Context context) {
122 
123         try {
124             String methodName = methodInfo.getName();
125             ClassInfo[] parameters = methodInfo.getParameterTypes();
126             ClassInfo returnType = methodInfo.getReturnType();
127             ClassInfo[] exceptionTypes = methodInfo.getExceptionTypes();
128             final String[] parameterNames = new String[parameters.length];
129             final CtClass[] classParameterTypes = new CtClass[parameters.length];
130             final CtClass[] classExceptionTypes = new CtClass[exceptionTypes.length];
131             final CtClass javassistReturnType = ctClass.getClassPool().get(returnType.getName());
132             if (javassistReturnType == null) {
133                 return; // we have a constructor => skip
134             }
135             for (int i = 0; i < parameters.length; i++) {
136                 classParameterTypes[i] = ctClass.getClassPool().get(parameters[i].getName());
137                 parameterNames[i] = "arg" + i;
138             }
139             for (int i = 0; i < exceptionTypes.length; i++) {
140                 classExceptionTypes[i] = ctClass.getClassPool().get(exceptionTypes[i].getName());
141             }
142             if (ClassInfoHelper.isMethodStatic(methodInfo)) {
143                 return; // introductions can't be static (not for the moment at
144                 // least)
145             }
146             if (JavassistHelper.hasMethod(ctClass, methodName, classParameterTypes)) {
147                 return;
148             }
149 
150             JavassistHelper.addStaticClassField(ctClass, context);
151             JavassistHelper.addAspectManagerField(ctClass, definition, context);
152             StringBuffer body = new StringBuffer("{");
153             if (parameters.length > 0) {
154                 body.append("Object[] aobj = $args;");
155             }
156             body.append("return ($r)");
157             body.append(TransformationConstants.ASPECT_MANAGER_FIELD);
158             body.append(".").append(TransformationConstants.GET_MIXIN_METHOD);
159             body.append("(").append(mixinIndex).append(")");
160             body.append(".").append(TransformationConstants.INVOKE_MIXIN_METHOD);
161             body.append("(").append(methodIndex).append(",");
162             if (parameters.length > 0) {
163                 body.append("aobj").append(",");
164             }
165             body.append("this").append(");");
166             body.append("}");
167             CtMethod method = CtNewMethod.make(
168                 javassistReturnType,
169                 methodName,
170                 classParameterTypes,
171                 classExceptionTypes,
172                 body.toString(),
173                 ctClass);
174             method.setModifiers(Modifier.PUBLIC);
175             ctClass.addMethod(method);
176         } catch (Exception e) {
177             throw new WrappedRuntimeException(e);
178         }
179     }
180 
181     /***
182      * Filters the classes to be transformed.
183      * 
184      * @param cg the class to filter
185      * @param ctx the context
186      * @param definition the definition
187      * @return boolean true if the method should be filtered away
188      */
189     public static boolean classFilter(final CtClass cg, final ExpressionContext ctx, final SystemDefinition definition) {
190         if (cg.isInterface()) {
191             return true;
192         }
193         String className = cg.getName().replace('/', '.');
194         if (definition.inExcludePackage(className)) {
195             return true;
196         }
197         if (definition.inExcludePackage(className)) {
198             return true;
199         }
200         if (!definition.inIncludePackage(className)) {
201             return true;
202         }
203         if (definition.isIntroduced(ctx)) {
204             return false;
205         }
206         return true;
207     }
208 }