package net.sourceforge.javautil.bytecode.api.type;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import net.sourceforge.javautil.bytecode.api.IBytecodeAnnotated;
import net.sourceforge.javautil.bytecode.api.IBytecodeAnnotation;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable;
import net.sourceforge.javautil.bytecode.api.IBytecodeSource;
import net.sourceforge.javautil.bytecode.api.LiteralValue;
import net.sourceforge.javautil.bytecode.api.TypeDescriptor;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod;
import net.sourceforge.javautil.common.CollectionUtil;
/**
* An implementation for declaring on {@link IBytecodeSource}'s that are marked {@link IBytecodeAnnotated}.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class BytecodeAnnotationDeclaration implements IBytecodeAnnotation {
protected final IBytecodeResolvable type;
protected final Set<BytecodeAnnotationDeclaration> annotations = new LinkedHashSet<BytecodeAnnotationDeclaration>();
protected final Map<String, AnnotationValue> values = new LinkedHashMap<String, AnnotationValue>();
protected final Map<String, IBytecodeMethod> methods = new LinkedHashMap<String, IBytecodeMethod>();
public BytecodeAnnotationDeclaration(IBytecodeResolvable type) {
this.type = type;
for (IBytecodeMethod dm : type.getDeclaredMethods()) {
methods.put(dm.getName(), dm);
}
}
public IBytecodeResolvable getType() {
return type;
}
public Map<String, AnnotationValue> getValues() {
return Collections.unmodifiableMap(values);
}
public Set<BytecodeAnnotationDeclaration> getDeclaredAnnotations() {
return Collections.unmodifiableSet(annotations);
}
public Set<String> getDeclaredMethodNames() { return methods.keySet(); }
/**
* @param annotationType The type to use for annotating this annotation
* @return The new declaration
*/
public BytecodeAnnotationDeclaration annotate (IBytecodeResolvable annotationType) {
BytecodeAnnotationDeclaration bad = new BytecodeAnnotationDeclaration(annotationType);
this.annotations.add(bad);
return bad;
}
public BytecodeAnnotationDeclaration setValue (String name, int... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, long... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, double... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, short... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, byte... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, char... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, boolean... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, String... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, Enum... value) { this.setValue(name, value); return this; }
public BytecodeAnnotationDeclaration setValue (String name, Class... value) { this.setValue(name, value); return this; }
/**
* This will perform type safety checks.
*
* @param name The name of the annotation value
* @param value The value for the annotation
*/
protected void setValue (String name, Object value) {
if (value == null) throw new IllegalArgumentException("Annotation values cannot be null");
if (!this.methods.containsKey(name)) throw new IllegalArgumentException("No such annotation value named: " + name + " for " + type);
TypeDescriptor type = this.methods.get(name).getDescriptor().getReturnType();
TypeDescriptor pt = TypeDescriptor.getFor(value.getClass());
if (type.compareTo(pt) != 0) {
if (pt.isArray() && type.compareTo(pt.getComponentType()) == 0) {
value = Array.get(value, 0);
} else if (type.isArray() && pt.compareTo(type.getComponentType()) == 0) {
Object array = Array.newInstance(value.getClass(), 1);
Array.set(array, 0, value);
value = array;
} else {
throw new IllegalArgumentException(value + " is not of the expected annotation value type: " + type);
}
}
this.values.put(name, new AnnotationValue(name, value));
}
}