package ru.vyarus.guice.ext.core.generator;
import com.google.common.collect.ImmutableMap;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.*;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Sets annotation method value.
* <p>Based on code from <a href="http://tapestry.apache.org/">tapestry 5</a>.</p>
*
* @author Vyacheslav Rusakov
* @since 08.12.2014
*/
@SuppressWarnings({"PMD.AvoidThrowingRawExceptionTypes", "PMD.AvoidUsingShortType"})
public class AnnotationMemberValueVisitor implements MemberValueVisitor {
private static final Map<Class<?>, CtClass> PRIMITIVES = ImmutableMap.<Class<?>, CtClass>builder()
.put(boolean.class, CtClass.booleanType)
.put(Boolean.class, CtClass.booleanType)
.put(byte.class, CtClass.byteType)
.put(Byte.class, CtClass.byteType)
.put(char.class, CtClass.charType)
.put(Character.class, CtClass.charType)
.put(short.class, CtClass.shortType)
.put(Short.class, CtClass.shortType)
.put(int.class, CtClass.intType)
.put(Integer.class, CtClass.intType)
.put(long.class, CtClass.longType)
.put(Long.class, CtClass.longType)
.put(float.class, CtClass.floatType)
.put(Float.class, CtClass.floatType)
.put(double.class, CtClass.doubleType)
.put(Double.class, CtClass.doubleType)
.build();
private final ClassPool classPool;
private final ConstPool constPool;
private final Object value;
public AnnotationMemberValueVisitor(final ClassPool classPool, final ConstPool constPool, final Object value) {
this.classPool = classPool;
this.constPool = constPool;
this.value = value;
}
public void visitAnnotationMemberValue(final AnnotationMemberValue mb) {
final Class<?> annotationType = getClass(value);
final Method[] methods = annotationType.getDeclaredMethods();
try {
for (final Method method : methods) {
final Object result = method.invoke(value);
mb.getValue().addMemberValue(method.getName(), createValue(result));
}
} catch (Exception e) {
throw new RuntimeException("Failed to copy annotation value", e);
}
}
public void visitArrayMemberValue(final ArrayMemberValue mb) {
final int length = Array.getLength(this.value);
final MemberValue[] members = new MemberValue[length];
try {
for (int i = 0; i < length; i++) {
final Object object = Array.get(this.value, i);
members[i] = createValue(object);
}
mb.setValue(members);
} catch (final Exception e) {
throw new RuntimeException("Failed to copy array value", e);
}
}
public void visitBooleanMemberValue(final BooleanMemberValue mb) {
mb.setValue((Boolean) this.value);
}
public void visitByteMemberValue(final ByteMemberValue mb) {
mb.setValue((Byte) this.value);
}
public void visitCharMemberValue(final CharMemberValue mb) {
mb.setValue((Character) this.value);
}
public void visitDoubleMemberValue(final DoubleMemberValue mb) {
mb.setValue((Double) this.value);
}
public void visitEnumMemberValue(final EnumMemberValue mb) {
final Enum<?> enumeration = (Enum<?>) this.value;
final Class type = enumeration.getDeclaringClass();
mb.setType(type.getName());
mb.setValue(enumeration.name());
}
public void visitFloatMemberValue(final FloatMemberValue mb) {
mb.setValue((Float) this.value);
}
public void visitIntegerMemberValue(final IntegerMemberValue mb) {
mb.setValue((Integer) this.value);
}
public void visitLongMemberValue(final LongMemberValue mb) {
mb.setValue((Long) this.value);
}
public void visitShortMemberValue(final ShortMemberValue mb) {
mb.setValue((Short) this.value);
}
public void visitStringMemberValue(final StringMemberValue mb) {
mb.setValue((String) this.value);
}
public void visitClassMemberValue(final ClassMemberValue mb) {
mb.setValue(((Class) this.value).getName());
}
private MemberValue createValue(final Object value) throws Exception {
final MemberValue memberValue = Annotation.createMemberValue(
this.constPool, getCtClass(classPool, getClass(value)));
memberValue.accept(new AnnotationMemberValueVisitor(this.classPool, this.constPool, value));
return memberValue;
}
private Class<?> getClass(final Object object) {
final boolean isAnnotation = object instanceof java.lang.annotation.Annotation;
return isAnnotation ? ((java.lang.annotation.Annotation) object).annotationType() : object.getClass();
}
private static CtClass getCtClass(final ClassPool pool, final Class<?> type) throws Exception {
return PRIMITIVES.containsKey(type)
? PRIMITIVES.get(type)
: pool.get(type.getName());
}
}