I'm not checking this in yet. I wrote a patch for the annotation inheritance bug (PR 28203) for the gcj-eclipse branch of gcj. I also fixed a buglet (IMO) in AnnotationInvocationHandler, and added a helper 'create' method to that class.
This patch ports this work back to Classpath. The only trouble is, I had to break Class.getAnnotations() to make this compile against cvs head -- the Inherited class doesn't exist there and is required for proper operation of the code. So, the real patch can only work on the generics branch. What would you prefer? The hacked patch on the trunk and the real patch on the branch? No change on the trunk and the real patch on the branch? Something else? Note that the 1.4-ification is much uglier than the original, too, as the original used foreach. Finally, this adds a new native method, Method.getDefaultValue. Not sure if that is best either; but I have noticed that our Method and Field implementations are missing a number of things related to annotations. Tom 2006-10-18 Tom Tromey <[EMAIL PROTECTED]> PR classpath/28203: * java/lang/Class.java (getAnnotations): Rewrote. * sun/reflect/annotation/AnnotationInvocationHandler.java (create): New method. (arrayClone): New method. (invoke): Clone array return results. * vm/reference/java/lang/reflect/Method.java (getDefaultValue): New native method. Index: java/lang/Class.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/lang/Class.java,v retrieving revision 1.49 diff -u -r1.49 Class.java --- java/lang/Class.java 28 Apr 2006 13:43:02 -0000 1.49 +++ java/lang/Class.java 19 Oct 2006 04:52:57 -0000 @@ -66,6 +66,7 @@ import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -1514,15 +1515,29 @@ */ public Annotation[] getAnnotations() { - HashSet set = new HashSet(); - set.addAll(Arrays.asList(getDeclaredAnnotations())); - Class[] interfaces = getInterfaces(); - for (int i = 0; i < interfaces.length; i++) - set.addAll(Arrays.asList(interfaces[i].getAnnotations())); - Class superClass = getSuperclass(); - if (superClass != null) - set.addAll(Arrays.asList(superClass.getAnnotations())); - return (Annotation[]) set.toArray(new Annotation[set.size()]); + HashMap/*<Class, Annotation>*/ map = new HashMap/*<Class, Annotation>*/(); + Annotation[] as = getDeclaredAnnotations(); + for (int i = 0; i < as.length; ++i) + { + Annotation a = as[i]; + map.put(a.annotationType(), a); + } + for (Class s = getSuperclass(); + s != null; + s = s.getSuperclass()) + { + as = s.getAnnotations(); + for (int i = 0; i < as.length; ++i) + { + Annotation a = as[i]; + Class k = a.annotationType(); + // FIXME: only generics has Inherited. + if (! map.containsKey(k) /* && k.isAnnotationPresent(Inherited.class) */) + map.put(k, a); + } + } + Collection/*<Annotation>*/ v = map.values(); + return (Annotation[]) v.toArray(new Annotation[v.size()]); } /** Index: sun/reflect/annotation/AnnotationInvocationHandler.java =================================================================== RCS file: /cvsroot/classpath/classpath/sun/reflect/annotation/AnnotationInvocationHandler.java,v retrieving revision 1.1 diff -u -r1.1 AnnotationInvocationHandler.java --- sun/reflect/annotation/AnnotationInvocationHandler.java 9 Jun 2006 06:59:21 -0000 1.1 +++ sun/reflect/annotation/AnnotationInvocationHandler.java 19 Oct 2006 04:53:00 -0000 @@ -39,11 +39,13 @@ package sun.reflect.annotation; import java.io.Serializable; +import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationTypeMismatchException; import java.lang.annotation.IncompleteAnnotationException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Iterator; import java.util.Map; @@ -74,6 +76,26 @@ this.memberValues = memberValues; } + public static Annotation create(Class type, Map memberValues) + { + Method[] ms = type.getDeclaredMethods(); + for (int i = 0; i < ms.length; ++i) + { + Method m = ms[i]; + String name = m.getName(); + if (! memberValues.containsKey(name)) + { + // FIXME: what to do about exceptions here? + memberValues.put(name, m.getDefaultValue()); + } + } + AnnotationInvocationHandler handler + = new AnnotationInvocationHandler(type, memberValues); + return (Annotation) Proxy.newProxyInstance(type.getClassLoader(), + new Class[] { type }, + handler); + } + /** * Compare an instance of AnnotationInvocationHandler with another object. * Note that the other object does not have to be an @@ -295,6 +317,38 @@ return returnType; } + private Object arrayClone(Object obj) + { + if (obj instanceof boolean[]) + return ((boolean[]) obj).clone(); + + if (obj instanceof byte[]) + return ((byte[]) obj).clone(); + + if (obj instanceof char[]) + return ((char[]) obj).clone(); + + if (obj instanceof short[]) + return ((short[]) obj).clone(); + + if (obj instanceof int[]) + return ((int[]) obj).clone(); + + if (obj instanceof float[]) + return ((float[]) obj).clone(); + + if (obj instanceof long[]) + return ((long[]) obj).clone(); + + if (obj instanceof double[]) + return ((double[]) obj).clone(); + + if (obj instanceof Object[]) + return ((Object[]) obj).clone(); + + return obj; + } + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -325,6 +379,10 @@ throw new AnnotationTypeMismatchException(method, val.getClass().getName()); } + if (val.getClass().isArray()) + { + val = arrayClone(val); + } return val; } } Index: vm/reference/java/lang/reflect/Method.java =================================================================== RCS file: /cvsroot/classpath/classpath/vm/reference/java/lang/reflect/Method.java,v retrieving revision 1.19 diff -u -r1.19 Method.java --- vm/reference/java/lang/reflect/Method.java 22 Apr 2006 21:52:18 -0000 1.19 +++ vm/reference/java/lang/reflect/Method.java 19 Oct 2006 04:53:00 -0000 @@ -450,5 +450,18 @@ MethodSignatureParser p = new MethodSignatureParser(this, sig); return p.getGenericReturnType(); } + + /** + * If this method is an annotation method, returns the default + * value for the method. If there is no default value, or if the + * method is not a member of an annotation type, returns null. + * Primitive types are wrapped. + * + * @throws TypeNotPresentException if the method returns a Class, + * and the class cannot be found + * + * @since 1.5 + */ + public native Object getDefaultValue(); }