# HG changeset patch # User jfranck # Date 1358189556 -3600 # Node ID 9f42a06a49c0fc0fe6cca57bb8dec6217df34486 # Parent fc4cb1577ad679085e7cbfb447177a8c675a623d 7193719: Support repeating annotations in javax.lang.model Reviewed-by: jjg diff -r fc4cb1577ad6 -r 9f42a06a49c0 src/share/classes/com/sun/tools/javac/code/Symbol.java --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Thu Jan 10 19:38:57 2013 -0800 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Mon Jan 14 19:52:36 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -450,7 +450,7 @@ * This is the implementation for {@code * javax.lang.model.element.Element.getAnnotationMirrors()}. */ - public final List getAnnotationMirrors() { + public final List getAnnotationMirrors() { return getRawAttributes(); } @@ -462,6 +462,11 @@ return JavacElements.getAnnotation(this, annoType); } + // This method is part of the javax.lang.model API, do not use this in javac code. + public A[] getAnnotations(Class annoType) { + return JavacElements.getAnnotations(this, annoType); + } + // TODO: getEnclosedElements should return a javac List, fix in FilteredMemberList public java.util.List getEnclosedElements() { return List.nil(); diff -r fc4cb1577ad6 -r 9f42a06a49c0 src/share/classes/com/sun/tools/javac/model/JavacElements.java --- a/src/share/classes/com/sun/tools/javac/model/JavacElements.java Thu Jan 10 19:38:57 2013 -0800 +++ b/src/share/classes/com/sun/tools/javac/model/JavacElements.java Mon Jan 14 19:52:36 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,8 @@ import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Map; import javax.lang.model.SourceVersion; @@ -96,32 +98,43 @@ enter = Enter.instance(context); } - /** - * An internal-use utility that creates a reified annotation. + * An internal-use utility that creates a runtime view of an + * annotation. This is the implementation of + * Element.getAnnotation(Class). */ public static A getAnnotation(Symbol annotated, Class annoType) { if (!annoType.isAnnotation()) throw new IllegalArgumentException("Not an annotation type: " + annoType); + Attribute.Compound c; + if (annotated.kind == Kinds.TYP && annotated instanceof ClassSymbol) { + c = getAttributeOnClass((ClassSymbol)annotated, annoType); + } else { + c = getAttribute(annotated, annoType); + } + return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType); + } + + // Helper to getAnnotation[s] + private static Attribute.Compound getAttribute(Symbol annotated, + Class annoType) { String name = annoType.getName(); - for (Attribute.Compound anno : annotated.getAnnotationMirrors()) + + for (Attribute.Compound anno : annotated.getRawAttributes()) if (name.equals(anno.type.tsym.flatName().toString())) - return AnnotationProxyMaker.generateAnnotation(anno, annoType); + return anno; + return null; } - - /** - * An internal-use utility that creates a reified annotation. - * This overloaded version take annotation inheritance into account. - */ - public static A getAnnotation(ClassSymbol annotated, - Class annoType) { + // Helper to getAnnotation[s] + private static Attribute.Compound getAttributeOnClass(ClassSymbol annotated, + Class annoType) { boolean inherited = annoType.isAnnotationPresent(Inherited.class); - A result = null; + Attribute.Compound result = null; while (annotated.name != annotated.name.table.names.java_lang_Object) { - result = getAnnotation((Symbol)annotated, annoType); + result = getAttribute(annotated, annoType); if (result != null || !inherited) break; Type sup = annotated.getSuperclass(); @@ -132,6 +145,188 @@ return result; } + /** + * An internal-use utility that creates a runtime view of + * annotations. This is the implementation of + * Element.getAnnotations(Class). + */ + public static A[] getAnnotations(Symbol annotated, + Class annoType) { + if (!annoType.isAnnotation()) + throw new IllegalArgumentException("Not an annotation type: " + + annoType); + // If annoType does not declare a container this is equivalent to wrapping + // getAnnotation(...) in an array. + Class containerType = getContainer(annoType); + if (containerType == null) { + A res = getAnnotation(annotated, annoType); + int size; + if (res == null) { + size = 0; + } else { + size = 1; + } + @SuppressWarnings("unchecked") // annoType is the Class for A + A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); + if (res != null) + arr[0] = res; + return arr; + } + + // So we have a containing type + String name = annoType.getName(); + String annoTypeName = annoType.getSimpleName(); + String containerTypeName = containerType.getSimpleName(); + int directIndex = -1, containerIndex = -1; + Attribute.Compound direct = null, container = null; + Attribute.Compound[] rawAttributes = annotated.getRawAttributes().toArray(new Attribute.Compound[0]); + + // Find directly present annotations + for (int i = 0; i < rawAttributes.length; i++) { + if (annoTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) { + directIndex = i; + direct = rawAttributes[i]; + } else if(containerTypeName != null && + containerTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) { + containerIndex = i; + container = rawAttributes[i]; + } + } + // Deal with inherited annotations + if (annotated.kind == Kinds.TYP && + (annotated instanceof ClassSymbol)) { + ClassSymbol s = (ClassSymbol)annotated; + if (direct == null && container == null) { + direct = getAttributeOnClass(s, annoType); + container = getAttributeOnClass(s, containerType); + + // both are inherited and found, put container last + if (direct != null && container != null) { + directIndex = 0; + containerIndex = 1; + } else if (direct != null) { + directIndex = 0; + } else { + containerIndex = 0; + } + } else if (direct == null) { + direct = getAttributeOnClass(s, annoType); + if (direct != null) + directIndex = containerIndex + 1; + } else if (container == null) { + container = getAttributeOnClass(s, containerType); + if (container != null) + containerIndex = directIndex + 1; + } + } + + // Pack them in an array + Attribute[] contained0 = new Attribute[0]; + if (container != null) + contained0 = unpackAttributes(container); + ListBuffer compounds = ListBuffer.lb(); + for (Attribute a : contained0) + if (a instanceof Attribute.Compound) + compounds = compounds.append((Attribute.Compound)a); + Attribute.Compound[] contained = compounds.toArray(new Attribute.Compound[0]); + + int size = (direct == null ? 0 : 1) + contained.length; + @SuppressWarnings("unchecked") // annoType is the Class for A + A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); + + // if direct && container, which is first? + int insert = -1; + int length = arr.length; + if (directIndex >= 0 && containerIndex >= 0) { + if (directIndex < containerIndex) { + arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); + insert = 1; + } else { + arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType); + insert = 0; + length--; + } + } else if (directIndex >= 0) { + arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); + return arr; + } else { + // Only container + insert = 0; + } + + for (int i = 0; i + insert < length; i++) + arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType); + + return arr; + } + + // Needed to unpack the runtime view of containing annotations + private static final Class CONTAINED_BY_CLASS = initContainedBy(); + private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod(); + + private static Class initContainedBy() { + try { + @SuppressWarnings("unchecked") // java.lang.annotation.ContainedBy extends Annotation by being an annotation type + Class c = (Class)Class.forName("java.lang.annotation.ContainedBy"); + return c; + } catch (ClassNotFoundException e) { + return null; + } catch (SecurityException e) { + return null; + } + } + private static Method initValueElementMethod() { + if (CONTAINED_BY_CLASS == null) + return null; + + Method m = null; + try { + m = CONTAINED_BY_CLASS.getMethod("value"); + if (m != null) + m.setAccessible(true); + return m; + } catch (NoSuchMethodException e) { + return null; + } + } + + // Helper to getAnnotations + private static Class getContainer(Class annoType) { + // Since we can not refer to java.lang.annotation.ContainedBy until we are + // bootstrapping with java 8 we need to get the ContainedBy annotation using + // reflective invocations instead of just using its type and element method. + if (CONTAINED_BY_CLASS != null && + VALUE_ELEMENT_METHOD != null) { + // Get the ContainedBy instance on the annotations declaration + Annotation containedBy = (Annotation)annoType.getAnnotation(CONTAINED_BY_CLASS); + if (containedBy != null) { + try { + // Get the value element, it should be a class + // indicating the containing annotation type + @SuppressWarnings("unchecked") + Class containerType = (Class)VALUE_ELEMENT_METHOD.invoke(containedBy); + if (containerType == null) + return null; + + return containerType; + } catch (ClassCastException e) { + return null; + } catch (IllegalAccessException e) { + return null; + } catch (InvocationTargetException e ) { + return null; + } + } + } + return null; + } + // Helper to getAnnotations + private static Attribute[] unpackAttributes(Attribute.Compound container) { + // We now have an instance of the container, + // unpack it returning an instance of the + // contained type or null + return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values; + } public PackageSymbol getPackageElement(CharSequence name) { String strName = name.toString(); @@ -238,8 +433,10 @@ tree.accept(vis); if (vis.result == null) return null; + + List annos = sym.getRawAttributes(); return matchAnnoToTree(cast(Attribute.Compound.class, findme), - sym.getAnnotationMirrors(), + annos, vis.result); } @@ -442,7 +639,7 @@ */ public List getAllAnnotationMirrors(Element e) { Symbol sym = cast(Symbol.class, e); - List annos = sym.getAnnotationMirrors(); + List annos = sym.getRawAttributes(); while (sym.getKind() == ElementKind.CLASS) { Type sup = ((ClassSymbol) sym).getSuperclass(); if (!sup.hasTag(CLASS) || sup.isErroneous() || @@ -451,7 +648,8 @@ } sym = sup.tsym; List oldAnnos = annos; - for (Attribute.Compound anno : sym.getAnnotationMirrors()) { + List newAnnos = sym.getRawAttributes(); + for (Attribute.Compound anno : newAnnos) { if (isInherited(anno.type) && !containsAnnoOfType(oldAnnos, anno.type)) { annos = annos.prepend(anno); @@ -465,11 +663,7 @@ * Tests whether an annotation type is @Inherited. */ private boolean isInherited(Type annotype) { - for (Attribute.Compound anno : annotype.tsym.getAnnotationMirrors()) { - if (anno.type.tsym == syms.inheritedType.tsym) - return true; - } - return false; + return annotype.tsym.attribute(syms.inheritedType.tsym) != null; } /** diff -r fc4cb1577ad6 -r 9f42a06a49c0 src/share/classes/javax/lang/model/element/Element.java --- a/src/share/classes/javax/lang/model/element/Element.java Thu Jan 10 19:38:57 2013 -0800 +++ b/src/share/classes/javax/lang/model/element/Element.java Mon Jan 14 19:52:36 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -149,6 +149,56 @@ A getAnnotation(Class annotationType); /** + * Returns an array of all of this element's annotation for the + * specified type if such annotations are present, else an empty + * array. The annotation may be either inherited or directly + * present on this element. This method will look through a container + * annotation (if present) if the supplied annotation type is + * repeatable. + * + *

The annotations returned by this method could contain an element + * whose value is of type {@code Class}. + * This value cannot be returned directly: information necessary to + * locate and load a class (such as the class loader to use) is + * not available, and the class might not be loadable at all. + * Attempting to read a {@code Class} object by invoking the relevant + * method on the returned annotation + * will result in a {@link MirroredTypeException}, + * from which the corresponding {@link TypeMirror} may be extracted. + * Similarly, attempting to read a {@code Class[]}-valued element + * will result in a {@link MirroredTypesException}. + * + *

+ * Note: This method is unlike others in this and related + * interfaces. It operates on runtime reflective information — + * representations of annotation types currently loaded into the + * VM — rather than on the representations defined by and used + * throughout these interfaces. Consequently, calling methods on + * the returned annotation object can throw many of the exceptions + * that can be thrown when calling methods on an annotation object + * returned by core reflection. This method is intended for + * callers that are written to operate on a known, fixed set of + * annotation types. + *
+ * + * @param
the annotation type + * @param annotationType the {@code Class} object corresponding to + * the annotation type + * @return this element's annotations for the specified annotation + * type if present on this element, else an empty array + * + * @see #getAnnotationMirrors() + * @see #getAnnotation() + * @see java.lang.reflect.AnnotatedElement#getAnnotations + * @see EnumConstantNotPresentException + * @see AnnotationTypeMismatchException + * @see IncompleteAnnotationException + * @see MirroredTypeException + * @see MirroredTypesException + */ + A[] getAnnotations(Class annotationType); + + /** * Returns the modifiers of this element, excluding annotations. * Implicit modifiers, such as the {@code public} and {@code static} * modifiers of interface members, are included.