src/share/classes/com/sun/tools/javac/model/JavacAnnoConstructs.java

Thu, 25 Jul 2013 11:02:27 +0200

author
jfranck
date
Thu, 25 Jul 2013 11:02:27 +0200
changeset 1918
a218f7befd55
parent 1709
bae8387d16aa
child 1930
051e64d0816e
permissions
-rw-r--r--

8007961: javax.lang.model tests for repeating annotations fail in getAnnotationsByType
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 package com.sun.tools.javac.model;
    27 import java.lang.annotation.Annotation;
    28 import java.lang.annotation.Inherited;
    29 import java.lang.reflect.InvocationTargetException;
    30 import java.lang.reflect.Method;
    32 import com.sun.tools.javac.code.Attribute;
    33 import com.sun.tools.javac.code.Kinds;
    34 import com.sun.tools.javac.code.Symbol;
    35 import com.sun.tools.javac.code.Symbol.ClassSymbol;
    36 import com.sun.tools.javac.code.Symbol.TypeVariableSymbol;
    37 import com.sun.tools.javac.code.TargetType;
    38 import com.sun.tools.javac.code.Type;
    39 import com.sun.tools.javac.code.Type.AnnotatedType;
    40 import com.sun.tools.javac.util.ListBuffer;
    41 import static com.sun.tools.javac.code.TypeTag.CLASS;
    42 import com.sun.tools.javac.util.List;
    44 /**
    45  * Utility methods for operating on annotated constructs.
    46  *
    47  * <p><b>This is NOT part of any supported API.
    48  * If you write code that depends on this, you do so at your own
    49  * risk.  This code and its internal interfaces are subject to change
    50  * or deletion without notice.</b></p>
    51  */
    52 public class JavacAnnoConstructs {
    54     // <editor-fold defaultstate="collapsed" desc="Symbols">
    56     /**
    57      * An internal-use utility that creates a runtime view of an
    58      * annotation. This is the implementation of
    59      * Element.getAnnotation(Class).
    60      */
    61     public static <A extends Annotation> A getAnnotation(Symbol annotated,
    62                                                          Class<A> annoType) {
    63         if (!annoType.isAnnotation())
    64             throw new IllegalArgumentException("Not an annotation type: "
    65                                                + annoType);
    66         Attribute.Compound c;
    67         if (annotated.kind == Kinds.TYP &&
    68                 annotated instanceof ClassSymbol) {
    69             c = getAttributeOnClass((ClassSymbol)annotated, annoType);
    70         } else if (annotated.kind == Kinds.TYP &&
    71                    annotated instanceof TypeVariableSymbol) {
    72             c = getAttributeOnTypeVariable((TypeVariableSymbol)annotated, annoType);
    73         } else {
    74             c = getAttribute(annotated, annoType);
    75         }
    76         return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
    77     }
    79     // Helper to getAnnotation[s]
    80     private static <A extends Annotation> Attribute.Compound getAttribute(Symbol annotated,
    81                                                                           Class<A> annoType) {
    82         String name = annoType.getName();
    84         for (Attribute.Compound anno : annotated.getRawAttributes()) {
    85             if (name.equals(anno.type.tsym.flatName().toString()))
    86                 return anno;
    87         }
    89         return null;
    90     }
    92     // Helper to getAnnotation[s]
    93     private static <A extends Annotation> Attribute.Compound
    94             getAttributeOnTypeVariable(TypeVariableSymbol annotated, Class<A> annoType) {
    95         String name = annoType.getName();
    97         // Declaration annotations on type variables are stored in type attributes
    98         // on the owner of the TypeVariableSymbol
    99         List<Attribute.Compound> res = List.nil();
   100         List<Attribute.TypeCompound> candidates = annotated.owner.getRawTypeAttributes();
   101         for (Attribute.TypeCompound anno : candidates)
   102             if (anno.position.type == TargetType.CLASS_TYPE_PARAMETER ||
   103                     anno.position.type == TargetType.METHOD_TYPE_PARAMETER)
   104                 if (name.equals(anno.type.tsym.flatName().toString()))
   105                     return anno;
   107         return null;
   108     }
   110     // Helper to getAnnotation[s]
   111     private static <A extends Annotation> Attribute.Compound getAttributeOnClass(
   112             ClassSymbol annotated,
   113             final Class<A> annoType)
   114     {
   115         boolean inherited = annoType.isAnnotationPresent(Inherited.class);
   116         Attribute.Compound result = null;
   118         result = getAttribute(annotated, annoType);
   119         if (result != null || !inherited)
   120             return result;
   122         while ((annotated = nextSupertypeToSearch(annotated)) != null) {
   123             result = getAttribute(annotated, annoType);
   124             if (result != null)
   125                 return result;
   126         }
   127         return null; // no more supertypes to search
   128     }
   130     /**
   131      * Returns the next type to search for inherited annotations or {@code null}
   132      * if the next type can't be found.
   133      */
   134     private static ClassSymbol nextSupertypeToSearch(ClassSymbol annotated) {
   135         if (annotated.name == annotated.name.table.names.java_lang_Object)
   136             return null;
   138         Type sup = annotated.getSuperclass();
   139         if (!sup.hasTag(CLASS) || sup.isErroneous())
   140             return null;
   142         return (ClassSymbol) sup.tsym;
   143     }
   145     /**
   146      * An internal-use utility that creates a runtime view of
   147      * annotations. This is the implementation of
   148      * Element.getAnnotations(Class).
   149      */
   150     public static <A extends Annotation> A[] getAnnotationsByType(Symbol annotated,
   151             Class<A> annoType)
   152     {
   153         if (!annoType.isAnnotation())
   154             throw new IllegalArgumentException("Not an annotation type: "
   155                                                + annoType);
   156         // If annoType does not declare a container this is equivalent to wrapping
   157         // getAnnotation(...) in an array.
   158         Class <? extends Annotation> containerType = getContainer(annoType);
   159         if (containerType == null) {
   160             A res = getAnnotation(annotated, annoType);
   161             int size;
   162             if (res == null) {
   163                 size = 0;
   164             } else {
   165                 size = 1;
   166             }
   167             @SuppressWarnings("unchecked") // annoType is the Class for A
   168             A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
   169             if (res != null)
   170                 arr[0] = res;
   171             return arr;
   172         }
   174         // So we have a containing type
   175         String annoTypeName = annoType.getSimpleName();
   176         String containerTypeName = containerType.getSimpleName();
   177         int directIndex = -1, containerIndex = -1;
   178         Attribute.Compound direct = null, container = null;
   179         // Find directly (explicit or implicit) present annotations
   180         int index = -1;
   181         for (List<Attribute.Compound> list = annotated.getAnnotationMirrors();
   182                 !list.isEmpty();
   183                 list = list.tail) {
   184             Attribute.Compound attribute = list.head;
   185             index++;
   186             if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) {
   187                 directIndex = index;
   188                 direct = attribute;
   189             } else if(containerTypeName != null &&
   190                       attribute.type.tsym.flatName().contentEquals(containerTypeName)) {
   191                 containerIndex = index;
   192                 container = attribute;
   193             }
   194         }
   196         // Deal with inherited annotations
   197         if (direct == null && container == null) {
   198             if (annotated.kind == Kinds.TYP &&
   199                     (annotated instanceof ClassSymbol)) {
   200                 ClassSymbol s = nextSupertypeToSearch((ClassSymbol)annotated);
   201                 if (s != null)
   202                     return getAnnotationsByType(s, annoType);
   203             }
   204         }
   206         // Pack them in an array
   207         Attribute[] contained0 = null;
   208         if (container != null)
   209             contained0 = unpackAttributes(container);
   210         ListBuffer<Attribute.Compound> compounds = ListBuffer.lb();
   211         if (contained0 != null) {
   212             for (Attribute a : contained0)
   213                 if (a instanceof Attribute.Compound)
   214                     compounds = compounds.append((Attribute.Compound)a);
   215         }
   216         Attribute.Compound[] contained = compounds.toArray(new Attribute.Compound[compounds.size()]);
   218         int size = (direct == null ? 0 : 1) + contained.length;
   219         @SuppressWarnings("unchecked") // annoType is the Class for A
   220         A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
   222         // if direct && container, which is first?
   223         int insert = -1;
   224         int length = arr.length;
   225         if (directIndex >= 0 && containerIndex >= 0) {
   226             if (directIndex < containerIndex) {
   227                 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
   228                 insert = 1;
   229             } else {
   230                 arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
   231                 insert = 0;
   232                 length--;
   233             }
   234         } else if (directIndex >= 0) {
   235             arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
   236             return arr;
   237         } else {
   238             // Only container
   239             insert = 0;
   240         }
   242         for (int i = 0; i + insert < length; i++)
   243             arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
   245         return arr;
   246     }
   248     // </editor-fold>
   250     // <editor-fold defaultstate="collapsed" desc="Types">
   252     /**
   253      * An internal-use utility that creates a runtime view of an
   254      * annotation. This is the implementation of
   255      * TypeMirror.getAnnotation(Class).
   256      */
   257     public static <A extends Annotation> A getAnnotation(AnnotatedType annotated, Class<A> annoType) {
   258         if (!annoType.isAnnotation())
   259             throw new IllegalArgumentException("Not an annotation type: "
   260                                                + annoType);
   261         Attribute.Compound c = getAttribute(annotated, annoType);
   262         return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
   263     }
   265     // Helper to getAnnotation[s]
   266     private static <A extends Annotation> Attribute.Compound getAttribute(Type annotated,
   267                                                                           Class<A> annoType) {
   268         String name = annoType.getName();
   270         for (Attribute.Compound anno : annotated.getAnnotationMirrors()) {
   271             if (name.equals(anno.type.tsym.flatName().toString()))
   272                 return anno;
   273         }
   275         return null;
   276     }
   278     /**
   279      * An internal-use utility that creates a runtime view of
   280      * annotations. This is the implementation of
   281      * TypeMirror.getAnnotationsByType(Class).
   282      */
   283     public static <A extends Annotation> A[] getAnnotationsByType(AnnotatedType annotated, Class<A> annoType) {
   284         if (!annoType.isAnnotation())
   285             throw new IllegalArgumentException("Not an annotation type: "
   286                                                + annoType);
   287         // If annoType does not declare a container this is equivalent to wrapping
   288         // getAnnotation(...) in an array.
   289         Class <? extends Annotation> containerType = getContainer(annoType);
   290         if (containerType == null) {
   291             A res = getAnnotation(annotated, annoType);
   292             int size;
   293             if (res == null) {
   294                 size = 0;
   295             } else {
   296                 size = 1;
   297             }
   298             @SuppressWarnings("unchecked") // annoType is the Class for A
   299             A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
   300             if (res != null)
   301                 arr[0] = res;
   302             return arr;
   303         }
   305         // So we have a containing type
   306         String annoTypeName = annoType.getSimpleName();
   307         String containerTypeName = containerType.getSimpleName();
   308         int directIndex = -1, containerIndex = -1;
   309         Attribute.Compound direct = null, container = null;
   310         // Find directly (explicit or implicit) present annotations
   311         int index = -1;
   312         for (List<? extends Attribute.Compound> list = annotated.getAnnotationMirrors();
   313                 !list.isEmpty();
   314                 list = list.tail) {
   315             Attribute.Compound attribute = list.head;
   316             index++;
   317             if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) {
   318                 directIndex = index;
   319                 direct = attribute;
   320             } else if(containerTypeName != null &&
   321                       attribute.type.tsym.flatName().contentEquals(containerTypeName)) {
   322                 containerIndex = index;
   323                 container = attribute;
   324             }
   325         }
   327         // Pack them in an array
   328         Attribute[] contained0 = null;
   329         if (container != null)
   330             contained0 = unpackAttributes(container);
   331         ListBuffer<Attribute.Compound> compounds = ListBuffer.lb();
   332         if (contained0 != null) {
   333             for (Attribute a : contained0)
   334                 if (a instanceof Attribute.Compound)
   335                     compounds = compounds.append((Attribute.Compound)a);
   336         }
   337         Attribute.Compound[] contained = compounds.toArray(new Attribute.Compound[compounds.size()]);
   339         int size = (direct == null ? 0 : 1) + contained.length;
   340         @SuppressWarnings("unchecked") // annoType is the Class for A
   341         A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
   343         // if direct && container, which is first?
   344         int insert = -1;
   345         int length = arr.length;
   346         if (directIndex >= 0 && containerIndex >= 0) {
   347             if (directIndex < containerIndex) {
   348                 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
   349                 insert = 1;
   350             } else {
   351                 arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
   352                 insert = 0;
   353                 length--;
   354             }
   355         } else if (directIndex >= 0) {
   356             arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
   357             return arr;
   358         } else {
   359             // Only container
   360             insert = 0;
   361         }
   363         for (int i = 0; i + insert < length; i++)
   364             arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
   366         return arr;
   367     }
   369     // </editor-fold>
   371     // <editor-fold defaultstate="collapsed" desc="Container support">
   373     // Needed to unpack the runtime view of containing annotations
   374     private static final Class<? extends Annotation> REPEATABLE_CLASS = initRepeatable();
   375     private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod();
   377     private static Class<? extends Annotation> initRepeatable() {
   378         try {
   379             // Repeatable will not be available when bootstrapping on
   380             // JDK 7 so use a reflective lookup instead of a class
   381             // literal for Repeatable.class.
   382             return Class.forName("java.lang.annotation.Repeatable").asSubclass(Annotation.class);
   383         } catch (ClassNotFoundException e) {
   384             return null;
   385         } catch (SecurityException e) {
   386             return null;
   387         }
   388     }
   390     private static Method initValueElementMethod() {
   391         if (REPEATABLE_CLASS == null)
   392             return null;
   394         Method m = null;
   395         try {
   396             m = REPEATABLE_CLASS.getMethod("value");
   397             if (m != null)
   398                 m.setAccessible(true);
   399             return m;
   400         } catch (NoSuchMethodException e) {
   401             return null;
   402         }
   403     }
   405     // Helper to getAnnotations
   406     private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) {
   407         // Since we can not refer to java.lang.annotation.Repeatable until we are
   408         // bootstrapping with java 8 we need to get the Repeatable annotation using
   409         // reflective invocations instead of just using its type and element method.
   410         if (REPEATABLE_CLASS != null &&
   411             VALUE_ELEMENT_METHOD != null) {
   412             // Get the Repeatable instance on the annotations declaration
   413             Annotation repeatable = (Annotation)annoType.getAnnotation(REPEATABLE_CLASS);
   414             if (repeatable != null) {
   415                 try {
   416                     // Get the value element, it should be a class
   417                     // indicating the containing annotation type
   418                     @SuppressWarnings("unchecked")
   419                     Class<? extends Annotation> containerType = (Class)VALUE_ELEMENT_METHOD.invoke(repeatable);
   420                     if (containerType == null)
   421                         return null;
   423                     return containerType;
   424                 } catch (ClassCastException e) {
   425                     return null;
   426                 } catch (IllegalAccessException e) {
   427                     return null;
   428                 } catch (InvocationTargetException e ) {
   429                     return null;
   430                 }
   431             }
   432         }
   433         return null;
   434     }
   436     // Helper to getAnnotations
   437     private static Attribute[] unpackAttributes(Attribute.Compound container) {
   438         // We now have an instance of the container,
   439         // unpack it returning an instance of the
   440         // contained type or null
   441         return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values;
   442     }
   444     // </editor-fold>
   445 }

mercurial