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
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
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;
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;
144
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 }