/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.seasar.framework.aop.javassist;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.Map;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.S2MethodInvocation;
import org.seasar.framework.util.ClassUtil;
import org.seasar.framework.util.MethodUtil;
/**
* {@link MethodInvocation}をエンハンスするクラスです。
*
* @author koichik
*/
public class MethodInvocationClassGenerator extends AbstractGenerator {
/**
* エンハンスされるクラス名
*/
protected final String enhancedClassName;
/**
* メソッド呼び出しクラス
*/
protected CtClass methodInvocationClass;
/**
* {@link MethodInvocationClassGenerator}を作成します。
*
* @param classPool
* @param invocationClassName
* @param targetClassName
*/
public MethodInvocationClassGenerator(final ClassPool classPool,
final String invocationClassName, final String targetClassName) {
super(classPool);
this.enhancedClassName = targetClassName;
this.methodInvocationClass = getAndRenameCtClass(
MethodInvocationTemplate.class, invocationClassName);
}
/**
* proceedメソッドを作成します。
*
* @param targetMethod
* @param invokeSuperMethodName
*/
public void createProceedMethod(final Method targetMethod,
final String invokeSuperMethodName) {
final CtMethod method = getDeclaredMethod(methodInvocationClass,
"proceed", null);
setMethodBody(method, createProceedMethodSource(targetMethod,
enhancedClassName, invokeSuperMethodName));
}
/**
* <code>CtClass</code>を<code>Class</code>に変換します。
*
* @param classLoader
* @return
*/
public Class toClass(final ClassLoader classLoader) {
final Class clazz = toClass(classLoader, methodInvocationClass);
methodInvocationClass.detach();
methodInvocationClass = null;
return clazz;
}
/**
* <code>proceed</code>メソッドのソースを作成します。
*
* @param targetMethod
* @param enhancedClassName
* @param invokeSuperMethodName
* @return <code>proceed</code>メソッドのソース
*/
public static String createProceedMethodSource(final Method targetMethod,
final String enhancedClassName, final String invokeSuperMethodName) {
final StringBuffer buf = new StringBuffer(1000);
buf.append("{");
buf.append("if (interceptorsIndex < interceptors.length) {");
buf.append("return interceptors[interceptorsIndex++].invoke(this);");
buf.append("}");
buf.append(createReturnStatement(targetMethod, enhancedClassName,
invokeSuperMethodName));
buf.append("}");
return new String(buf);
}
/**
* <code>return</code>文用のソースを作成します。
*
* @param targetMethod
* @param enhancedClassName
* @param invokeSuperMethodName
* @return <code>return</code>文用のソース
*/
public static String createReturnStatement(final Method targetMethod,
final String enhancedClassName, final String invokeSuperMethodName) {
if (MethodUtil.isAbstract(targetMethod)) {
return createThrowStatement(targetMethod, enhancedClassName);
}
final String invokeSuper = "((" + enhancedClassName + ") target)."
+ invokeSuperMethodName + "("
+ createArgumentString(targetMethod.getParameterTypes()) + ")";
final Class returnType = targetMethod.getReturnType();
if (returnType.equals(void.class)) {
return invokeSuper + ";" + "return null;";
}
return "return " + toObject(returnType, invokeSuper) + ";";
}
/**
* <code>throws</code>句用のソースを作成します。
*
* @param targetMethod
* @param enhancedClassName
* @return <code>throws</code>句用のソース
*/
public static String createThrowStatement(final Method targetMethod,
final String enhancedClassName) {
return "throw new java.lang.NoSuchMethodError(\"" + enhancedClassName
+ "." + targetMethod.getName() + "("
+ createArgumentTypeString(targetMethod.getParameterTypes())
+ ")\");";
}
/**
* 引数用のソースを作成します。
*
* @param argTypes
* @return 引数用のソース
*/
public static String createArgumentString(final Class[] argTypes) {
if (argTypes == null || argTypes.length == 0) {
return "";
}
final StringBuffer buf = new StringBuffer(1000);
for (int i = 0; i < argTypes.length; ++i) {
buf.append(fromObject(argTypes[i], "arguments[" + i + "]")).append(
", ");
}
buf.setLength(buf.length() - 2);
return new String(buf);
}
/**
* 引数の型用のソースを作成します。
*
* @param argTypes
* @return 引数の型用のソース
*/
public static String createArgumentTypeString(final Class[] argTypes) {
if (argTypes == null || argTypes.length == 0) {
return "";
}
final StringBuffer buf = new StringBuffer(1000);
for (int i = 0; i < argTypes.length; ++i) {
buf.append(ClassUtil.getSimpleClassName(argTypes[i])).append(", ");
}
buf.setLength(buf.length() - 2);
return new String(buf);
}
/**
* {@link MethodInvocation}のテンプレートです。
*
*/
public static class MethodInvocationTemplate implements S2MethodInvocation {
private static Class targetClass;
private static Method method;
static MethodInterceptor[] interceptors;
private static Map parameters;
private Object target;
private Object[] arguments;
int interceptorsIndex;
/**
* インスタンスを構築します。
*
* @param target
* @param arguments
*/
public MethodInvocationTemplate(final Object target,
final Object[] arguments) {
this.target = target;
this.arguments = arguments;
}
public Class getTargetClass() {
return targetClass;
}
public Method getMethod() {
return method;
}
public AccessibleObject getStaticPart() {
return method;
}
public Object getParameter(String name) {
if (parameters == null) {
return null;
}
return parameters.get(name);
}
public Object getThis() {
return target;
}
public Object[] getArguments() {
return arguments;
}
public Object proceed() throws Throwable {
return null;
}
}
}