/*******************************************************************************
* Copyright (c) 2013 Spring IDE Developers
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.core.java.annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.ide.eclipse.core.type.asm.EmptyAnnotationVisitor;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* @author Christian Dupuis
* @author Martin Lippert
* @since 3.2.0
*/
public class AnnotationMemberVisitor extends AnnotationVisitor {
private final Annotation annotation;
private final ClassLoader classloader;
private boolean advancedValueProcessing;
public AnnotationMemberVisitor(Annotation annotation, ClassLoader classloader, boolean advancedValueProcessing) {
super(SpringAsmInfo.ASM_VERSION);
this.annotation = annotation;
this.classloader = classloader;
this.advancedValueProcessing = advancedValueProcessing;
}
@Override
public void visitEnd() {
if (this.advancedValueProcessing) {
try {
Class<?> annotationClass = this.classloader.loadClass(annotation.getAnnotationClass());
registerDefaultValues(annotationClass);
} catch (ClassNotFoundException ex) {
}
}
}
private void registerDefaultValues(Class<?> annotationClass) {
// Check declared default values of attributes in the annotation
// type.
Method[] annotationAttributes = annotationClass.getMethods();
for (Method annotationAttribute : annotationAttributes) {
String attributeName = annotationAttribute.getName();
Object defaultValue = annotationAttribute.getDefaultValue();
// special case for Enum values, load them from IDE classloader
// space to avoid conflicts between Spring framework
// running in IDE and Spring framework classes from the projects
// classpath
if (defaultValue != null && defaultValue.getClass().isEnum()) {
try {
Class<?> annotationClassInIdeSpace = this.getClass().getClassLoader().loadClass(annotation.getAnnotationClass());
Method annotationAttributeInIdeSpace = annotationClassInIdeSpace.getMethod(annotationAttribute.getName(),
annotationAttribute.getParameterTypes());
defaultValue = annotationAttributeInIdeSpace.getDefaultValue();
} catch (ClassNotFoundException ex) {
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
}
}
if (defaultValue != null && !annotation.hasMember(attributeName)) {
if (defaultValue instanceof java.lang.annotation.Annotation) {
defaultValue = AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes(
(java.lang.annotation.Annotation) defaultValue, false, true));
} else if (defaultValue instanceof java.lang.annotation.Annotation[]) {
java.lang.annotation.Annotation[] realAnnotations = (java.lang.annotation.Annotation[]) defaultValue;
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
for (int i = 0; i < realAnnotations.length; i++) {
mappedAnnotations[i] = AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes(realAnnotations[i], false,
true));
}
defaultValue = mappedAnnotations;
}
annotation.addMember(new AnnotationMemberValuePair(attributeName, defaultValue.toString(), defaultValue));
}
}
}
@Override
public void visit(String name, Object value) {
if ("value".equals(name)) {
annotation.addMember(new AnnotationMemberValuePair(null, value.toString()));
} else {
annotation.addMember(new AnnotationMemberValuePair(name, value.toString()));
}
}
@Override
public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
return AnnotationMetadataReadingVisitor.EMPTY_ANNOTATION_VISITOR;
}
@Override
public AnnotationVisitor visitArray(final String name) {
final Set<Object> values = new LinkedHashSet<Object>();
return new EmptyAnnotationVisitor() {
@Override
public void visit(String arg0, Object arg1) {
if (arg1 instanceof Type && advancedValueProcessing) {
try {
Class<?> clazz = classloader.loadClass(((Type) arg1).getClassName());
values.add(clazz);
} catch (ClassNotFoundException ex) {
}
} else {
values.add(arg1);
}
}
/**
* @Controller("/index.htm") -> value = /index.htm
*
* @Controller({"/index1.htm" , "/index2.htm"}) -> value =
* /index1.htm, /index2.htm
*
* @Controller({ RequestMapping.GET, RequestMapping.POST})
* @Controller({ org.swf.RequestMapping.GET,
* org.swf.RequestMapping.POST}) -> value =
* RequestMapping.GET, RequestMapping.POST
*
* @Controller(RequestMapping.GET)
* @Controller(org.swf.RequestMapping.GET) -> value =
* RequestMapping.GET
*/
@Override
public void visitEnd() {
StringBuilder buf = new StringBuilder();
Class typeOfArray = null;
for (Object value : values) {
typeOfArray = value.getClass();
buf.append(value.toString());
buf.append(", ");
}
String value = buf.toString();
if (value.length() > 0) {
value = value.substring(0, value.length() - 2);
}
if (typeOfArray != null && name.equals("value") && !advancedValueProcessing) {
annotation.addMember(new AnnotationMemberValuePair(null, value, values.toArray(new Object[values.size()])));
} else if (typeOfArray != null && !annotation.hasMember(name)) {
annotation.addMember(new AnnotationMemberValuePair(name, value, values.toArray(new Object[values.size()])));
}
}
@Override
public void visitEnum(String enumName, String type, String enumValue) {
String className = Type.getType(type).getClassName();
String value = ClassUtils.getShortName(className) + "." + enumValue;
values.add(value);
}
};
}
@Override
public void visitEnum(String name, String type, String enumValue) {
String className = Type.getType(type).getClassName();
Object valueAsObject = null;
try {
Class<?> enumType = this.getClass().getClassLoader().loadClass(className);
Field enumConstant = ReflectionUtils.findField(enumType, enumValue);
if (enumConstant != null) {
valueAsObject = enumConstant.get(null);
}
} catch (ClassNotFoundException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
annotation.addMember(new AnnotationMemberValuePair(name, ClassUtils.getShortName(className) + "." + enumValue, valueAsObject));
}
}