alundblad@2100: /* alundblad@2100: * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. alundblad@2100: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. alundblad@2100: * alundblad@2100: * This code is free software; you can redistribute it and/or modify it alundblad@2100: * under the terms of the GNU General Public License version 2 only, as alundblad@2100: * published by the Free Software Foundation. Oracle designates this alundblad@2100: * particular file as subject to the "Classpath" exception as provided alundblad@2100: * by Oracle in the LICENSE file that accompanied this code. alundblad@2100: * alundblad@2100: * This code is distributed in the hope that it will be useful, but WITHOUT alundblad@2100: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or alundblad@2100: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License alundblad@2100: * version 2 for more details (a copy is included in the LICENSE file that alundblad@2100: * accompanied this code). alundblad@2100: * alundblad@2100: * You should have received a copy of the GNU General Public License version alundblad@2100: * 2 along with this work; if not, write to the Free Software Foundation, alundblad@2100: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. alundblad@2100: * alundblad@2100: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA alundblad@2100: * or visit www.oracle.com if you need additional information or have any alundblad@2100: * questions. alundblad@2100: */ alundblad@2100: package com.sun.tools.javac.code; alundblad@2100: alundblad@2100: import java.lang.annotation.Annotation; jfranck@2154: import java.lang.annotation.Inherited; alundblad@2100: import java.lang.reflect.InvocationTargetException; alundblad@2100: import java.lang.reflect.Method; alundblad@2100: alundblad@2100: import javax.lang.model.AnnotatedConstruct; alundblad@2100: alundblad@2100: import com.sun.tools.javac.model.AnnotationProxyMaker; alundblad@2100: import com.sun.tools.javac.util.List; alundblad@2100: import com.sun.tools.javac.util.ListBuffer; alundblad@2100: alundblad@2100: /** alundblad@2100: * Common super type for annotated constructs such as Types and Symbols. alundblad@2100: * alundblad@2100: * This class should *not* contain any fields since it would have a significant alundblad@2100: * impact on the javac memory footprint. alundblad@2100: * alundblad@2100: *

This is NOT part of any supported API. alundblad@2100: * If you write code that depends on this, you do so at your own alundblad@2100: * risk. This code and its internal interfaces are subject to change alundblad@2100: * or deletion without notice.

alundblad@2100: */ alundblad@2100: public abstract class AnnoConstruct implements AnnotatedConstruct { alundblad@2100: alundblad@2100: alundblad@2100: // Override to enforce a narrower return type. alundblad@2100: @Override alundblad@2100: public abstract List getAnnotationMirrors(); alundblad@2100: alundblad@2100: alundblad@2100: // This method is part of the javax.lang.model API, do not use this in javac code. alundblad@2100: protected Attribute.Compound getAttribute(Class annoType) { alundblad@2100: String name = annoType.getName(); alundblad@2100: alundblad@2100: for (Attribute.Compound anno : getAnnotationMirrors()) { alundblad@2100: if (name.equals(anno.type.tsym.flatName().toString())) alundblad@2100: return anno; alundblad@2100: } alundblad@2100: alundblad@2100: return null; alundblad@2100: } alundblad@2100: alundblad@2100: alundblad@2100: @SuppressWarnings("unchecked") alundblad@2100: protected A[] getInheritedAnnotations(Class annoType) { alundblad@2100: return (A[]) java.lang.reflect.Array.newInstance(annoType, 0); // annoType is the Class for A alundblad@2100: } alundblad@2100: alundblad@2100: alundblad@2100: // This method is part of the javax.lang.model API, do not use this in javac code. alundblad@2100: public A[] getAnnotationsByType(Class annoType) { alundblad@2100: alundblad@2100: if (!annoType.isAnnotation()) alundblad@2100: throw new IllegalArgumentException("Not an annotation type: " alundblad@2100: + annoType); alundblad@2100: // If annoType does not declare a container this is equivalent to wrapping alundblad@2100: // getAnnotation(...) in an array. alundblad@2100: Class containerType = getContainer(annoType); alundblad@2100: if (containerType == null) { alundblad@2100: A res = getAnnotation(annoType); alundblad@2100: int size = res == null ? 0 : 1; alundblad@2100: alundblad@2100: @SuppressWarnings("unchecked") // annoType is the Class for A alundblad@2100: A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); alundblad@2100: if (res != null) alundblad@2100: arr[0] = res; alundblad@2100: return arr; alundblad@2100: } alundblad@2100: alundblad@2100: // So we have a containing type alundblad@2100: String annoTypeName = annoType.getName(); alundblad@2100: String containerTypeName = containerType.getName(); alundblad@2100: int directIndex = -1, containerIndex = -1; alundblad@2100: Attribute.Compound direct = null, container = null; alundblad@2100: // Find directly (explicit or implicit) present annotations alundblad@2100: int index = -1; alundblad@2100: for (Attribute.Compound attribute : getAnnotationMirrors()) { alundblad@2100: index++; alundblad@2100: if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) { alundblad@2100: directIndex = index; alundblad@2100: direct = attribute; alundblad@2100: } else if(containerTypeName != null && alundblad@2100: attribute.type.tsym.flatName().contentEquals(containerTypeName)) { alundblad@2100: containerIndex = index; alundblad@2100: container = attribute; alundblad@2100: } alundblad@2100: } alundblad@2100: alundblad@2100: // Deal with inherited annotations jfranck@2154: if (direct == null && container == null && jfranck@2154: annoType.isAnnotationPresent(Inherited.class)) alundblad@2100: return getInheritedAnnotations(annoType); alundblad@2100: jfranck@2156: Attribute.Compound[] contained = unpackContained(container); jfranck@2156: jfranck@2156: // In case of an empty legacy container we might need to look for jfranck@2156: // inherited annos as well jfranck@2156: if (direct == null && contained.length == 0 && jfranck@2156: annoType.isAnnotationPresent(Inherited.class)) jfranck@2156: return getInheritedAnnotations(annoType); alundblad@2100: alundblad@2100: int size = (direct == null ? 0 : 1) + contained.length; alundblad@2100: @SuppressWarnings("unchecked") // annoType is the Class for A alundblad@2100: A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); alundblad@2100: alundblad@2100: // if direct && container, which is first? alundblad@2100: int insert = -1; alundblad@2100: int length = arr.length; alundblad@2100: if (directIndex >= 0 && containerIndex >= 0) { alundblad@2100: if (directIndex < containerIndex) { alundblad@2100: arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); alundblad@2100: insert = 1; alundblad@2100: } else { alundblad@2100: arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType); alundblad@2100: insert = 0; alundblad@2100: length--; alundblad@2100: } alundblad@2100: } else if (directIndex >= 0) { alundblad@2100: arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); alundblad@2100: return arr; alundblad@2100: } else { alundblad@2100: // Only container alundblad@2100: insert = 0; alundblad@2100: } alundblad@2100: alundblad@2100: for (int i = 0; i + insert < length; i++) alundblad@2100: arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType); alundblad@2100: alundblad@2100: return arr; alundblad@2100: } alundblad@2100: jfranck@2156: private Attribute.Compound[] unpackContained(Attribute.Compound container) { jfranck@2156: // Pack them in an array jfranck@2156: Attribute[] contained0 = null; jfranck@2156: if (container != null) jfranck@2156: contained0 = unpackAttributes(container); jfranck@2156: ListBuffer compounds = new ListBuffer<>(); jfranck@2156: if (contained0 != null) { jfranck@2156: for (Attribute a : contained0) jfranck@2156: if (a instanceof Attribute.Compound) jfranck@2156: compounds = compounds.append((Attribute.Compound)a); jfranck@2156: } jfranck@2156: return compounds.toArray(new Attribute.Compound[compounds.size()]); jfranck@2156: } alundblad@2100: alundblad@2100: // This method is part of the javax.lang.model API, do not use this in javac code. alundblad@2100: public A getAnnotation(Class annoType) { alundblad@2100: alundblad@2100: if (!annoType.isAnnotation()) alundblad@2100: throw new IllegalArgumentException("Not an annotation type: " + annoType); alundblad@2100: alundblad@2100: Attribute.Compound c = getAttribute(annoType); alundblad@2100: return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType); alundblad@2100: } alundblad@2100: alundblad@2100: // Needed to unpack the runtime view of containing annotations alundblad@2100: private static final Class REPEATABLE_CLASS = initRepeatable(); alundblad@2100: private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod(); alundblad@2100: alundblad@2100: private static Class initRepeatable() { alundblad@2100: try { alundblad@2100: // Repeatable will not be available when bootstrapping on alundblad@2100: // JDK 7 so use a reflective lookup instead of a class alundblad@2100: // literal for Repeatable.class. alundblad@2100: return Class.forName("java.lang.annotation.Repeatable").asSubclass(Annotation.class); alundblad@2100: } catch (ClassNotFoundException | SecurityException e) { alundblad@2100: return null; alundblad@2100: } alundblad@2100: } alundblad@2100: alundblad@2100: private static Method initValueElementMethod() { alundblad@2100: if (REPEATABLE_CLASS == null) alundblad@2100: return null; alundblad@2100: alundblad@2100: Method m = null; alundblad@2100: try { alundblad@2100: m = REPEATABLE_CLASS.getMethod("value"); alundblad@2100: if (m != null) alundblad@2100: m.setAccessible(true); alundblad@2100: return m; alundblad@2100: } catch (NoSuchMethodException e) { alundblad@2100: return null; alundblad@2100: } alundblad@2100: } alundblad@2100: alundblad@2100: alundblad@2100: // Helper to getAnnotationsByType alundblad@2100: private static Class getContainer(Class annoType) { alundblad@2100: // Since we can not refer to java.lang.annotation.Repeatable until we are alundblad@2100: // bootstrapping with java 8 we need to get the Repeatable annotation using alundblad@2100: // reflective invocations instead of just using its type and element method. alundblad@2100: if (REPEATABLE_CLASS != null && alundblad@2100: VALUE_ELEMENT_METHOD != null) { alundblad@2100: // Get the Repeatable instance on the annotations declaration alundblad@2100: Annotation repeatable = (Annotation)annoType.getAnnotation(REPEATABLE_CLASS); alundblad@2100: if (repeatable != null) { alundblad@2100: try { alundblad@2100: // Get the value element, it should be a class alundblad@2100: // indicating the containing annotation type alundblad@2100: @SuppressWarnings("unchecked") alundblad@2100: Class containerType = (Class)VALUE_ELEMENT_METHOD.invoke(repeatable); alundblad@2100: if (containerType == null) alundblad@2100: return null; alundblad@2100: alundblad@2100: return containerType; alundblad@2100: } catch (ClassCastException | IllegalAccessException | InvocationTargetException e) { alundblad@2100: return null; alundblad@2100: } alundblad@2100: } alundblad@2100: } alundblad@2100: return null; alundblad@2100: } alundblad@2100: alundblad@2100: alundblad@2100: // Helper to getAnnotationsByType alundblad@2100: private static Attribute[] unpackAttributes(Attribute.Compound container) { alundblad@2100: // We now have an instance of the container, alundblad@2100: // unpack it returning an instance of the alundblad@2100: // contained type or null alundblad@2100: return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values; alundblad@2100: } alundblad@2100: alundblad@2100: }