/*
* 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.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import org.seasar.framework.exception.CannotCompileRuntimeException;
import org.seasar.framework.exception.IORuntimeException;
import org.seasar.framework.exception.IllegalAccessRuntimeException;
import org.seasar.framework.exception.InvocationTargetRuntimeException;
import org.seasar.framework.exception.NoSuchMethodRuntimeException;
import org.seasar.framework.exception.NotFoundRuntimeException;
import org.seasar.framework.util.ClassPoolUtil;
import org.seasar.framework.util.ClassUtil;
/**
* バイトコードを生成するための抽象クラスです。
*
* @author koichik
*/
public class AbstractGenerator {
/**
* defineClassです。
*/
protected static final String DEFINE_CLASS_METHOD_NAME = "defineClass";
/**
* 保護ドメインです。
*/
protected static final ProtectionDomain protectionDomain;
/**
* defineClassメソッドです。
*/
protected static Method defineClassMethod;
// static initializer
static {
protectionDomain = (ProtectionDomain) AccessController
.doPrivileged(new PrivilegedAction() {
public Object run() {
return AspectWeaver.class.getProtectionDomain();
}
});
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
final Class[] paramTypes = new Class[] { String.class,
byte[].class, int.class, int.class,
ProtectionDomain.class };
try {
final Class loader = ClassUtil.forName(ClassLoader.class
.getName());
defineClassMethod = loader.getDeclaredMethod(
DEFINE_CLASS_METHOD_NAME, paramTypes);
defineClassMethod.setAccessible(true);
} catch (final NoSuchMethodException e) {
throw new NoSuchMethodRuntimeException(ClassLoader.class,
DEFINE_CLASS_METHOD_NAME, paramTypes, e);
}
return null;
}
});
}
/**
* クラスプールです。
*/
protected final ClassPool classPool;
/**
* オブジェクトの表現から文字列表現に変換します。
*
* @param type
* 型
* @param expr
* 値
* @return 文字列表現
*/
protected static String fromObject(final Class type, final String expr) {
if (type.equals(void.class) || type.equals(Object.class)) {
return expr;
}
if (type.equals(boolean.class) || type.equals(char.class)) {
final Class wrapper = ClassUtil.getWrapperClass(type);
return "((" + wrapper.getName() + ") " + expr + ")."
+ type.getName() + "Value()";
}
if (type.isPrimitive()) {
return "((java.lang.Number) " + expr + ")." + type.getName()
+ "Value()";
}
return "(" + ClassUtil.getSimpleClassName(type) + ") " + expr;
}
/**
* オブジェクトの文字列表現に変換します。
*
* @param type
* 型
* @param expr
* 値
* @return 文字列表現
*/
protected static String toObject(final Class type, final String expr) {
if (type.isPrimitive()) {
final Class wrapper = ClassUtil.getWrapperClass(type);
return "new " + wrapper.getName() + "(" + expr + ")";
}
return expr;
}
/**
* {@link AbstractGenerator}を作成します。
*
* @param classPool
* クラスプール
*/
protected AbstractGenerator(final ClassPool classPool) {
this.classPool = classPool;
}
/**
* コンパイル時のクラスに変換します。
*
* @param clazz
* 元のクラス
* @return コンパイル時のクラス
*/
protected CtClass toCtClass(final Class clazz) {
return ClassPoolUtil.toCtClass(classPool, clazz);
}
/**
* コンパイル時のクラスに変換します。
*
* @param className
* クラス名
* @return コンパイル時のクラス
*/
protected CtClass toCtClass(final String className) {
return ClassPoolUtil.toCtClass(classPool, className);
}
/**
* コンパイル時のクラスの配列に変換します。
*
* @param classNames
* 元のクラス名の配列
* @return コンパイル時のクラスの配列
*/
protected CtClass[] toCtClassArray(final String[] classNames) {
return ClassPoolUtil.toCtClassArray(classPool, classNames);
}
/**
* コンパイル時のクラスの配列に変換します。
*
* @param classes
* 元のクラスの配列
* @return コンパイル時のクラスの配列
*/
protected CtClass[] toCtClassArray(final Class[] classes) {
return ClassPoolUtil.toCtClassArray(classPool, classes);
}
/**
* コンパイル時のクラスを作成します。
*
* @param name
* クラス名
* @return コンパイル時のクラス
*/
protected CtClass createCtClass(final String name) {
return ClassPoolUtil.createCtClass(classPool, name);
}
/**
* コンパイル時のクラスを作成します。
*
* @param name
* クラス名
* @param superClass
* 親クラス
* @return コンパイル時のクラス
*/
protected CtClass createCtClass(final String name, final Class superClass) {
return ClassPoolUtil.createCtClass(classPool, name, superClass);
}
/**
* コンパイル時のクラスを作成します。
*
* @param name
* クラス名
* @param superClass
* 親クラス
* @return コンパイル時のクラス
*/
protected CtClass createCtClass(final String name, final CtClass superClass) {
return ClassPoolUtil.createCtClass(classPool, name, superClass);
}
/**
* コンパイル時のクラスを取得して名前を変えます。
*
* @param orgClass
* 元のクラス
* @param newName
* 新しい名前
* @return コンパイル時のクラス
*/
protected CtClass getAndRenameCtClass(final Class orgClass,
final String newName) {
return getAndRenameCtClass(ClassUtil.getSimpleClassName(orgClass),
newName);
}
/**
* コンパイル時のクラスを取得して名前を変えます。
*
* @param orgName
* 元の名前
* @param newName
* 新しい名前
* @return コンパイル時のクラス
*/
protected CtClass getAndRenameCtClass(final String orgName,
final String newName) {
try {
return classPool.getAndRename(orgName, newName);
} catch (final NotFoundException e) {
throw new NotFoundRuntimeException(e);
}
}
/**
* <code>CtClass</code>を<code>Class</code>に変更します。
*
* @param classLoader
* クラスローダ
* @param ctClass
* コンパイル時のクラス
* @return クラス
*/
public Class toClass(final ClassLoader classLoader, final CtClass ctClass) {
try {
final byte[] bytecode = ctClass.toBytecode();
return (Class) defineClassMethod.invoke(classLoader, new Object[] {
ctClass.getName(), bytecode, new Integer(0),
new Integer(bytecode.length), protectionDomain });
} catch (final CannotCompileException e) {
throw new CannotCompileRuntimeException(e);
} catch (final IOException e) {
throw new IORuntimeException(e);
} catch (final IllegalAccessException e) {
throw new IllegalAccessRuntimeException(ClassLoader.class, e);
} catch (final InvocationTargetException e) {
throw new InvocationTargetRuntimeException(ClassLoader.class, e);
}
}
/**
* インターフェースを設定します。
*
* @param clazz
* 対象のコンパイル時クラス
* @param interfaceType
* インターフェース
*/
protected void setInterface(final CtClass clazz, final Class interfaceType) {
clazz.setInterfaces(new CtClass[] { toCtClass(interfaceType) });
}
/**
* インターフェースの配列を設定します。
*
* @param clazz
* 対象のコンパイル時クラス
* @param interfaces
* インターフェースの配列
*/
protected void setInterfaces(final CtClass clazz, final Class[] interfaces) {
clazz.setInterfaces(toCtClassArray(interfaces));
}
/**
* デフォルトコンストラクタを作成します。
*
* @param clazz
* 元のクラス
* @return コンパイル時コンストラクタ
*/
protected CtConstructor createDefaultConstructor(final Class clazz) {
return createDefaultConstructor(toCtClass(clazz));
}
/**
* デフォルトコンストラクタを作成します。
*
* @param clazz
* 対象のコンパイル時クラス
* @return コンパイル時コンストラクタ
*/
protected CtConstructor createDefaultConstructor(final CtClass clazz) {
try {
final CtConstructor ctConstructor = CtNewConstructor
.defaultConstructor(clazz);
clazz.addConstructor(ctConstructor);
return ctConstructor;
} catch (final CannotCompileException e) {
throw new CannotCompileRuntimeException(e);
}
}
/**
* コンストラクタを作成します。
*
* @param clazz
* 対象となるコンパイル時クラス
* @param constructor
* 元のコンストラクタ
* @return コンパイル時コンストラクタ
*/
protected CtConstructor createConstructor(final CtClass clazz,
final Constructor constructor) {
return createConstructor(clazz, toCtClassArray(constructor
.getParameterTypes()), toCtClassArray(constructor
.getExceptionTypes()));
}
/**
* コンストラクタを作成します。
*
* @param clazz
* 対象となるコンパイル時クラス
* @param parameterTypes
* パラメータの型の配列
* @param exceptionTypes
* 例外の型の配列
* @return コンパイル時コンストラクタ
*/
protected CtConstructor createConstructor(final CtClass clazz,
final CtClass[] parameterTypes, final CtClass[] exceptionTypes) {
try {
final CtConstructor ctConstructor = CtNewConstructor.make(
parameterTypes, exceptionTypes, clazz);
clazz.addConstructor(ctConstructor);
return ctConstructor;
} catch (final CannotCompileException e) {
throw new CannotCompileRuntimeException(e);
}
}
/**
* 宣言されているメソッドを返します。
*
* @param clazz
* 対象のコンパイル時クラス
* @param name
* メソッド名
* @param argTypes
* パラメータの型の配列
* @return コンパイル時メソッド
*/
protected CtMethod getDeclaredMethod(final CtClass clazz,
final String name, final CtClass[] argTypes) {
try {
return clazz.getDeclaredMethod(name, argTypes);
} catch (final NotFoundException e) {
throw new NotFoundRuntimeException(e);
}
}
/**
* メソッドを作成します。
*
* @param clazz
* 対象のコンパイル時クラス
* @param src
* ソース
* @return コンパイル時メソッド
*/
protected CtMethod createMethod(final CtClass clazz, final String src) {
try {
final CtMethod ctMethod = CtNewMethod.make(src, clazz);
clazz.addMethod(ctMethod);
return ctMethod;
} catch (final CannotCompileException e) {
throw new CannotCompileRuntimeException(e);
}
}
/**
* メソッドを作成します。
*
* @param clazz
* 対象のコンパイル時クラス
* @param method
* 元のメソッド
* @param body
* メソッドの中身
* @return コンパイル時メソッド
*/
protected CtMethod createMethod(final CtClass clazz, final Method method,
final String body) {
return createMethod(clazz, method.getModifiers(), method
.getReturnType(), method.getName(), method.getParameterTypes(),
method.getExceptionTypes(), body);
}
/**
* メソッドを作成します。
*
* @param clazz
* 対象となるコンパイル時クラス
* @param modifier
* アクセス修飾子
* @param returnType
* 戻り値の型
* @param methodName
* メソッド名
* @param parameterTypes
* パラメータの型の配列
* @param exceptionTypes
* 例外の型の配列
* @param body
* メソッドの中身
* @return コンパイル時メソッド
*/
protected CtMethod createMethod(final CtClass clazz, final int modifier,
final Class returnType, final String methodName,
final Class[] parameterTypes, final Class[] exceptionTypes,
final String body) {
try {
final CtMethod ctMethod = CtNewMethod.make(modifier
& ~(Modifier.ABSTRACT | Modifier.NATIVE),
toCtClass(returnType), methodName,
toCtClassArray(parameterTypes),
toCtClassArray(exceptionTypes), body, clazz);
clazz.addMethod(ctMethod);
return ctMethod;
} catch (final CannotCompileException e) {
throw new CannotCompileRuntimeException(e);
}
}
/**
* メソッドの中身を設定します。
*
* @param method
* コンパイル時メソッド
* @param src
* ソース
*/
protected void setMethodBody(final CtMethod method, final String src) {
try {
method.setBody(src);
} catch (final CannotCompileException e) {
throw new CannotCompileRuntimeException(e);
}
}
}