AnnoConstruct.java revision 2601:8e638f046bf0
11590Srgrimes/*
21590Srgrimes * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
31590Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41590Srgrimes *
51590Srgrimes * This code is free software; you can redistribute it and/or modify it
61590Srgrimes * under the terms of the GNU General Public License version 2 only, as
71590Srgrimes * published by the Free Software Foundation.  Oracle designates this
81590Srgrimes * particular file as subject to the "Classpath" exception as provided
91590Srgrimes * by Oracle in the LICENSE file that accompanied this code.
101590Srgrimes *
111590Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
121590Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131590Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141590Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
151590Srgrimes * accompanied this code).
161590Srgrimes *
171590Srgrimes * You should have received a copy of the GNU General Public License version
181590Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
191590Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
201590Srgrimes *
211590Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
221590Srgrimes * or visit www.oracle.com if you need additional information or have any
231590Srgrimes * questions.
241590Srgrimes */
251590Srgrimespackage com.sun.tools.javac.code;
261590Srgrimes
271590Srgrimesimport java.lang.annotation.Annotation;
281590Srgrimesimport java.lang.annotation.Inherited;
291590Srgrimesimport java.lang.reflect.InvocationTargetException;
301590Srgrimesimport java.lang.reflect.Method;
311590Srgrimes
321590Srgrimesimport javax.lang.model.AnnotatedConstruct;
331590Srgrimes
3487675Smarkmimport com.sun.tools.javac.model.AnnotationProxyMaker;
3587675Smarkmimport com.sun.tools.javac.util.DefinedBy;
3687675Smarkmimport com.sun.tools.javac.util.DefinedBy.Api;
3787675Smarkmimport com.sun.tools.javac.util.List;
381590Srgrimesimport com.sun.tools.javac.util.ListBuffer;
3928695Scharnier
401590Srgrimes/**
411590Srgrimes * Common super type for annotated constructs such as Types and Symbols.
4287675Smarkm *
431590Srgrimes * This class should *not* contain any fields since it would have a significant
441590Srgrimes * impact on the javac memory footprint.
4587675Smarkm *
4628695Scharnier * <p><b>This is NOT part of any supported API.
471590Srgrimes * If you write code that depends on this, you do so at your own
481590Srgrimes * risk.  This code and its internal interfaces are subject to change
491590Srgrimes * or deletion without notice.</b></p>
501590Srgrimes */
511590Srgrimespublic abstract class AnnoConstruct implements AnnotatedConstruct {
521590Srgrimes
531590Srgrimes
541590Srgrimes    // Override to enforce a narrower return type.
551590Srgrimes    @Override @DefinedBy(Api.LANGUAGE_MODEL)
561590Srgrimes    public abstract List<? extends Attribute.Compound> getAnnotationMirrors();
5728695Scharnier
5828695Scharnier
5973255Simp    // This method is part of the javax.lang.model API, do not use this in javac code.
6025777Sache    protected <A extends Annotation> Attribute.Compound getAttribute(Class<A> annoType) {
611590Srgrimes        String name = annoType.getName();
621590Srgrimes
631590Srgrimes        for (Attribute.Compound anno : getAnnotationMirrors()) {
641590Srgrimes            if (name.equals(anno.type.tsym.flatName().toString()))
651590Srgrimes                return anno;
6629434Sache        }
671590Srgrimes
68202200Sed        return null;
691590Srgrimes    }
7083242Sdd
7183242Sdd
7273255Simp    @SuppressWarnings("unchecked")
7373255Simp    protected <A extends Annotation> A[] getInheritedAnnotations(Class<A> annoType) {
741590Srgrimes        return (A[]) java.lang.reflect.Array.newInstance(annoType, 0);  // annoType is the Class for A
7573255Simp    }
7673255Simp
7773255Simp
7873255Simp    // This method is part of the javax.lang.model API, do not use this in javac code.
7973255Simp    @DefinedBy(Api.LANGUAGE_MODEL)
801590Srgrimes    public <A extends Annotation> A[] getAnnotationsByType(Class<A> annoType) {
811590Srgrimes
821590Srgrimes        if (!annoType.isAnnotation())
831590Srgrimes            throw new IllegalArgumentException("Not an annotation type: "
84155875Scognet                                               + annoType);
85200156Sed        // If annoType does not declare a container this is equivalent to wrapping
86155875Scognet        // getAnnotation(...) in an array.
87155875Scognet        Class <? extends Annotation> containerType = getContainer(annoType);
88155875Scognet        if (containerType == null) {
89155875Scognet            A res = getAnnotation(annoType);
90200156Sed            int size = res == null ? 0 : 1;
91155875Scognet
92155875Scognet            @SuppressWarnings("unchecked") // annoType is the Class for A
93155875Scognet            A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
94155875Scognet            if (res != null)
95155875Scognet                arr[0] = res;
96155875Scognet            return arr;
971590Srgrimes        }
9873255Simp
991590Srgrimes        // So we have a containing type
1001590Srgrimes        String annoTypeName = annoType.getName();
101200156Sed        String containerTypeName = containerType.getName();
10273255Simp        int directIndex = -1, containerIndex = -1;
10383082Sru        Attribute.Compound direct = null, container = null;
10473255Simp        // Find directly (explicit or implicit) present annotations
10573255Simp        int index = -1;
10683242Sdd        for (Attribute.Compound attribute : getAnnotationMirrors()) {
10783242Sdd            index++;
10873255Simp            if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) {
1091590Srgrimes                directIndex = index;
11025777Sache                direct = attribute;
11125777Sache            } else if(containerTypeName != null &&
11273255Simp                      attribute.type.tsym.flatName().contentEquals(containerTypeName)) {
1131590Srgrimes                containerIndex = index;
1141590Srgrimes                container = attribute;
1151590Srgrimes            }
1161590Srgrimes        }
1171590Srgrimes
1181590Srgrimes        // Deal with inherited annotations
11973255Simp        if (direct == null && container == null &&
12073255Simp                annoType.isAnnotationPresent(Inherited.class))
12173255Simp            return getInheritedAnnotations(annoType);
12273255Simp
12373255Simp        Attribute.Compound[] contained = unpackContained(container);
12473255Simp
12573255Simp        // In case of an empty legacy container we might need to look for
1261590Srgrimes        // inherited annos as well
1271590Srgrimes        if (direct == null && contained.length == 0 &&
12828695Scharnier                annoType.isAnnotationPresent(Inherited.class))
1291590Srgrimes            return getInheritedAnnotations(annoType);
1301590Srgrimes
1311590Srgrimes        int size = (direct == null ? 0 : 1) + contained.length;
1321590Srgrimes        @SuppressWarnings("unchecked") // annoType is the Class for A
13328695Scharnier        A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
1341590Srgrimes
13573255Simp        // if direct && container, which is first?
13673255Simp        int insert = -1;
13773320Simp        int length = arr.length;
13873255Simp        if (directIndex >= 0 && containerIndex >= 0) {
13973320Simp            if (directIndex < containerIndex) {
14073320Simp                arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
14173255Simp                insert = 1;
14273255Simp            } else {
1431590Srgrimes                arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
1441590Srgrimes                insert = 0;
1451590Srgrimes                length--;
1461590Srgrimes            }
1471590Srgrimes        } else if (directIndex >= 0) {
148200156Sed            arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
149200156Sed            return arr;
1501590Srgrimes        } else {
151200156Sed            // Only container
152155875Scognet            insert = 0;
15373255Simp        }
15483082Sru
155200156Sed        for (int i = 0; i + insert < length; i++)
15673255Simp            arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
15773255Simp
15873255Simp        return arr;
15987675Smarkm    }
16073255Simp
16173255Simp    private Attribute.Compound[] unpackContained(Attribute.Compound container) {
16273255Simp        // Pack them in an array
16383082Sru        Attribute[] contained0 = null;
16483082Sru        if (container != null)
165200156Sed            contained0 = unpackAttributes(container);
16683082Sru        ListBuffer<Attribute.Compound> compounds = new ListBuffer<>();
16783082Sru        if (contained0 != null) {
16883082Sru            for (Attribute a : contained0)
16983082Sru                if (a instanceof Attribute.Compound)
17083082Sru                    compounds = compounds.append((Attribute.Compound)a);
17173255Simp        }
17273255Simp        return compounds.toArray(new Attribute.Compound[compounds.size()]);
17373255Simp    }
17473255Simp
175200156Sed    // This method is part of the javax.lang.model API, do not use this in javac code.
17628695Scharnier    @DefinedBy(Api.LANGUAGE_MODEL)
1771590Srgrimes    public <A extends Annotation> A getAnnotation(Class<A> annoType) {
1781590Srgrimes
1791590Srgrimes        if (!annoType.isAnnotation())
1801590Srgrimes            throw new IllegalArgumentException("Not an annotation type: " + annoType);
18128695Scharnier
182201224Sed        Attribute.Compound c = getAttribute(annoType);
18328695Scharnier        return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
18473320Simp    }
18528695Scharnier
18628695Scharnier    // Needed to unpack the runtime view of containing annotations
18728695Scharnier    private static final Class<? extends Annotation> REPEATABLE_CLASS = initRepeatable();
1881590Srgrimes    private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod();
18973255Simp
1901590Srgrimes    private static Class<? extends Annotation> initRepeatable() {
19173255Simp        try {
19273255Simp            // Repeatable will not be available when bootstrapping on
1931590Srgrimes            // JDK 7 so use a reflective lookup instead of a class
1941590Srgrimes            // literal for Repeatable.class.
1951590Srgrimes            return Class.forName("java.lang.annotation.Repeatable").asSubclass(Annotation.class);
19629434Sache        } catch (ClassNotFoundException | SecurityException e) {
1971590Srgrimes            return null;
1981590Srgrimes        }
19987675Smarkm    }
20087675Smarkm
20169231Skris    private static Method initValueElementMethod() {
20276367Skris        if (REPEATABLE_CLASS == null)
2031590Srgrimes            return null;
20469231Skris
20569231Skris        Method m = null;
20669231Skris        try {
2071590Srgrimes            m = REPEATABLE_CLASS.getMethod("value");
2081590Srgrimes            if (m != null)
2091590Srgrimes                m.setAccessible(true);
21069231Skris            return m;
21169231Skris        } catch (NoSuchMethodException e) {
21266557Sn_hibma            return null;
21366557Sn_hibma        }
2141590Srgrimes    }
2151590Srgrimes
2161590Srgrimes
2171590Srgrimes    // Helper to getAnnotationsByType
2181590Srgrimes    private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) {
2191590Srgrimes        // Since we can not refer to java.lang.annotation.Repeatable until we are
2201590Srgrimes        // bootstrapping with java 8 we need to get the Repeatable annotation using
2211590Srgrimes        // reflective invocations instead of just using its type and element method.
2221590Srgrimes        if (REPEATABLE_CLASS != null &&
2231590Srgrimes            VALUE_ELEMENT_METHOD != null) {
2241590Srgrimes            // Get the Repeatable instance on the annotations declaration
2251590Srgrimes            Annotation repeatable = (Annotation)annoType.getAnnotation(REPEATABLE_CLASS);
2261590Srgrimes            if (repeatable != null) {
2271590Srgrimes                try {
22841717Sdillon                    // Get the value element, it should be a class
22941717Sdillon                    // indicating the containing annotation type
2301590Srgrimes                    @SuppressWarnings("unchecked")
2311590Srgrimes                    Class<? extends Annotation> containerType = (Class)VALUE_ELEMENT_METHOD.invoke(repeatable);
23269231Skris                    if (containerType == null)
23366557Sn_hibma                        return null;
23463909Sasmodai
2351590Srgrimes                    return containerType;
2361590Srgrimes                } catch (ClassCastException | IllegalAccessException | InvocationTargetException e) {
2371590Srgrimes                    return null;
2381590Srgrimes                }
23976367Skris            }
24076367Skris        }
24176367Skris        return null;
24276367Skris    }
24376367Skris
24476367Skris
24576367Skris    // Helper to getAnnotationsByType
246175346Sdas    private static Attribute[] unpackAttributes(Attribute.Compound container) {
2471590Srgrimes        // We now have an instance of the container,
24850776Sdbaker        // unpack it returning an instance of the
249175346Sdas        // contained type or null
25050776Sdbaker        return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values;
251175346Sdas    }
252175346Sdas
2531590Srgrimes}
2541590Srgrimes