AnnotationFactorypublic final class AnnotationFactory extends Object implements Serializable, InvocationHandlerThe annotation implementation based on dynamically generated proxy instances.
It conforms to all requirements stated in public APIs, see in particular
{@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement}
and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}.
Namely, annotation instances are immutable and serializable; they provide
conforming access to annotation member values and required implementations of
methods declared in Annotation interface. |
Fields Summary |
---|
private static final transient Map | cache | private final Class | klazz | private AnnotationMember[] | elements |
Constructors Summary |
---|
private AnnotationFactory(Class klzz, AnnotationMember[] values)New instances should not be created directly, use factory method
{@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()}
instead.
klazz = klzz;
AnnotationMember[] defs = getElementsDescription(klazz);
if (values == null) {
elements = defs;
} else {
//merge default and actual values
elements = new AnnotationMember[defs.length];
next: for (int i = elements.length - 1; i >= 0; i-- ){
for (AnnotationMember val : values){
if (val.name.equals(defs[i].name)) {
elements[i] = val.setDefinition(defs[i]);
continue next;
}
}
elements[i] = defs[i];
}
}
|
Methods Summary |
---|
public static java.lang.annotation.Annotation | createAnnotation(java.lang.Class annotationType, AnnotationMember[] elements)Provides a new annotation instance.
AnnotationFactory antn = new AnnotationFactory(annotationType, elements);
return (Annotation)Proxy.newProxyInstance( annotationType.getClassLoader(),
new Class[]{annotationType}, antn);
| public boolean | equals(java.lang.Object obj)Returns true if the specified object represents the same annotation instance.
That is, if it implements the same annotation type and
returns the same element values.
Note, actual underlying implementation mechanism does not matter - it may
differ completely from this class.
if (obj == this) {
return true;
}
if (!klazz.isInstance(obj)) {
return false;
}
Object handler = null;
if (Proxy.isProxyClass(obj.getClass())
&& (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory ) {
AnnotationFactory other = (AnnotationFactory) handler;
if (elements.length != other.elements.length) {
return false;
}
next: for (AnnotationMember el1 : elements){
for (AnnotationMember el2 : other.elements) {
if (el1.equals(el2)) {
continue next;
}
}
return false;
}
return true;
} else {
// encountered foreign annotation implementaton
// so have to obtain element values via invocation
// of corresponding methods
for (final AnnotationMember el : elements) {
if (el.tag == ERROR) {
// undefined value is incomparable (transcendent)
return false;
}
try {
if (!el.definingMethod.isAccessible()) {
AccessController.doPrivileged(new PrivilegedAction<Object>(){
public Object run() {
try {
el.definingMethod.setAccessible(true);
} catch (Exception ignore) {}
return null;
}
});
}
Object otherValue = el.definingMethod.invoke(obj);
if (otherValue != null ) {
if (el.tag == ARRAY) {
if (!el.equalArrayValue(otherValue)) {
return false;
}
} else {
if (!el.value.equals(otherValue)) {
return false;
}
}
} else if (el.value != AnnotationMember.NO_VALUE) {
return false;
}
} catch (Throwable e) {
return false;
}
}
return true;
}
| public static AnnotationMember[] | getElementsDescription(java.lang.Class annotationType)Reflects specified annotation type and returns an array
of member element definitions with default values.
AnnotationMember[] desc = cache.get(annotationType);
if (desc == null) {
if (!annotationType.isAnnotation()) {
throw new IllegalArgumentException("Type is not annotation: "
+ annotationType.getName());
}
Method[] m = annotationType.getDeclaredMethods();
desc = new AnnotationMember[m.length];
int idx = 0;
for(Method element : m) {
String name = element.getName();
Class<?> type = element.getReturnType();
try {
desc[idx] = new AnnotationMember(name,
element.getDefaultValue(), type, element);
} catch (Throwable t) {
desc[idx] = new AnnotationMember(name, t, type, element);
}
idx++;
}
cache.put(annotationType, desc);
}
return desc;
| public int | hashCode()Returns a hash code composed as a sum of hash codes of member elements,
including elements with default values.
int hash = 0;
for (AnnotationMember element : elements) {
hash += element.hashCode();
}
return hash;
| public java.lang.Object | invoke(java.lang.Object proxy, java.lang.reflect.Method method, java.lang.Object[] args)Processes a method invocation request to this annotation instance.
Recognizes the methods declared in the
{@link java.lang.annotation.Annotation java.lang.annotation.Annotation}
interface, and member-defining methods of the implemented annotation type.
String name = method.getName();
Class[] params = method.getParameterTypes();
if (params.length == 0) {
if ("annotationType".equals(name)) {
return klazz;
} else if ("toString".equals(name)) {
return toString();
} else if ("hashCode".equals(name)) {
return hashCode();
}
// this must be element value request
AnnotationMember element = null;
for (AnnotationMember el : elements) {
if (name.equals(el.name)) {
element = el;
break;
}
}
if (element == null || !method.equals(element.definingMethod)) {
throw new IllegalArgumentException(method.toString());
} else {
Object value = element.validateValue();
if (value == null) {
throw new IncompleteAnnotationException(klazz, name);
}
return value;
}
} else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)){
return Boolean.valueOf(equals(args[0]));
}
throw new IllegalArgumentException(
"Invalid method for annotation type: " + method);
| private void | readObject(java.io.ObjectInputStream os)Reads the object, obtains actual member definitions for the annotation type,
and merges deserialized values with the new definitions.
os.defaultReadObject();
// Annotation type members can be changed arbitrarily
// So there may be zombi elements from the previous life;
// they hardly fit into this new annotation's incarnation,
// as we have no defining methods for them.
// Reasonably just drop such elements,
// but seems better to keep them for compatibility
AnnotationMember[] defs = getElementsDescription(klazz);
AnnotationMember[] old = elements;
List<AnnotationMember> merged = new ArrayList<AnnotationMember>(
defs.length + old.length);
nextOld: for (AnnotationMember el1 : old) {
for (AnnotationMember el2 : defs) {
if (el2.name.equals(el1.name)) {
continue nextOld;
}
}
merged.add(el1); //phantom element
}
nextNew: for (AnnotationMember def : defs){
for (AnnotationMember val : old){
if (val.name.equals(def.name)) {
// nothing to do about cached errors (if any)
// anyway they remain relevant to values
merged.add(val.setDefinition(def));
continue nextNew;
}
}
merged.add(def); // brand new element
}
elements = merged.toArray(new AnnotationMember[merged.size()]);
| public java.lang.String | toString()Provides detailed description of this annotation instance,
including all member name-values pairs.
String res = "@" + klazz.getName() + "(";
for(int i = 0; i < elements.length; i++) {
if ( i != 0 ) {
res += ", ";
}
res += elements[i].toString();;
}
return res + ")";
|
|