DynamicAnnotationBuilder.java
package com.gh.mygreen.xlsmapper.xml;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import com.gh.mygreen.xlsmapper.xml.bind.AnnotationInfo;
import ognl.DefaultTypeConverter;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
/**
* Javassitを利用して、{@link Annotation}のインスタンスを動的に作成するクラス。
* <p>独自のClassLoaderを設定することが可能。
* <p>このクラスはシングルトンです。
*
* @version 0.5
* @author Naoki Takezoe
* @author T.TSUCHIE
*
*/
public class DynamicAnnotationBuilder {
/**
* シングルトンのインスタンス。
*/
private static DynamicAnnotationBuilder INSTANCE = new DynamicAnnotationBuilder();
private ClassLoader classLoader;
private OgnlContext ognlContext;
private DynamicAnnotationBuilder() {
this.ognlContext = new OgnlContext(
new MultipleLoaderClassResolver(), new DefaultTypeConverter(), new AllAllowMemberAccess());
}
/**
* インスタンスを取得する。
* @return
*/
public static DynamicAnnotationBuilder getInstance() {
return INSTANCE;
}
/**
* アノテーションが定義された{@link ClassLoader}を設定する。
* <p>nullの場合、標準のClassLoaderが使用される。
* @param classLoader
*/
public static void init(final ClassLoader classLoader) {
getInstance().classLoader = classLoader;
}
/**
* アノテーションが定義されたClassLoaderとJavaBeansを定義したClassLoaderを設定する。
* @param classLoader アノテーションを見つけるために使用するClassLoader
* @param propertyLoaders JavaBeansを見つけるために使用するClassLoader
*/
public static void init(final ClassLoader classLoader, final ClassLoader[] propertyLoaders) {
getInstance().classLoader = classLoader;
if(propertyLoaders != null && propertyLoaders.length != 0) {
Map<Integer, ClassLoader> loaderMap = new HashMap<>();
for(ClassLoader propertyLoader : propertyLoaders) {
loaderMap.put(propertyLoader.hashCode(), propertyLoader);
}
getInstance().ognlContext = new OgnlContext(
new AllAllowMemberAccess(), new MultipleLoaderClassResolver(), new DefaultTypeConverter(), loaderMap);
}
}
/**
* 指定したアノテーションのクラス情報から、アノテーションのインスタンスを組み立てる。
* @param annoClass アノテーションのクラス
* @param info アノテーションの情報
* @return アノテーションのインスタンス。
* @throws AnnotationReadException
*/
public Annotation buildAnnotation(final Class<?> annoClass, final AnnotationInfo info) throws AnnotationReadException {
final Map<String, Object> defaultValues = new HashMap<>();
for(Method method : annoClass.getMethods()) {
final Object defaultValue = method.getDefaultValue();
if(defaultValue != null) {
defaultValues.put(method.getName(), defaultValue);
}
}
final Map<String, Object> xmlValues = new HashMap<>();
for(String key : info.getAttributeKeys()) {
try {
Object value = Ognl.getValue(info.getAttribute(key), ognlContext, new Object());
xmlValues.put(key, value);
} catch(OgnlException e) {
throw new AnnotationReadException(String.format("fail annotation attribute %s with ognl.", key), e);
}
}
ClassLoader loader = classLoader;
if(loader == null){
loader = Thread.currentThread().getContextClassLoader();
}
Object obj = Proxy.newProxyInstance(loader, new Class[]{annoClass},
new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
String name = method.getName();
if (name.equals("annotationType")) {
return annoClass;
} else if(xmlValues.containsKey(name)){
return xmlValues.get(name);
} else {
return defaultValues.get(name);
}
}
});
return (Annotation) obj;
}
}