AnnoConstruct.java revision 2601:8e638f046bf0
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 */
25package com.sun.tools.javac.code;
26
27import java.lang.annotation.Annotation;
28import java.lang.annotation.Inherited;
29import java.lang.reflect.InvocationTargetException;
30import java.lang.reflect.Method;
31
32import javax.lang.model.AnnotatedConstruct;
33
34import com.sun.tools.javac.model.AnnotationProxyMaker;
35import com.sun.tools.javac.util.DefinedBy;
36import com.sun.tools.javac.util.DefinedBy.Api;
37import com.sun.tools.javac.util.List;
38import com.sun.tools.javac.util.ListBuffer;
39
40/**
41 * Common super type for annotated constructs such as Types and Symbols.
42 *
43 * This class should *not* contain any fields since it would have a significant
44 * impact on the javac memory footprint.
45 *
46 * <p><b>This is NOT part of any supported API.
47 * If you write code that depends on this, you do so at your own
48 * risk.  This code and its internal interfaces are subject to change
49 * or deletion without notice.</b></p>
50 */
51public abstract class AnnoConstruct implements AnnotatedConstruct {
52
53
54    // Override to enforce a narrower return type.
55    @Override @DefinedBy(Api.LANGUAGE_MODEL)
56    public abstract List<? extends Attribute.Compound> getAnnotationMirrors();
57
58
59    // This method is part of the javax.lang.model API, do not use this in javac code.
60    protected <A extends Annotation> Attribute.Compound getAttribute(Class<A> annoType) {
61        String name = annoType.getName();
62
63        for (Attribute.Compound anno : getAnnotationMirrors()) {
64            if (name.equals(anno.type.tsym.flatName().toString()))
65                return anno;
66        }
67
68        return null;
69    }
70
71
72    @SuppressWarnings("unchecked")
73    protected <A extends Annotation> A[] getInheritedAnnotations(Class<A> annoType) {
74        return (A[]) java.lang.reflect.Array.newInstance(annoType, 0);  // annoType is the Class for A
75    }
76
77
78    // This method is part of the javax.lang.model API, do not use this in javac code.
79    @DefinedBy(Api.LANGUAGE_MODEL)
80    public <A extends Annotation> A[] getAnnotationsByType(Class<A> annoType) {
81
82        if (!annoType.isAnnotation())
83            throw new IllegalArgumentException("Not an annotation type: "
84                                               + annoType);
85        // If annoType does not declare a container this is equivalent to wrapping
86        // getAnnotation(...) in an array.
87        Class <? extends Annotation> containerType = getContainer(annoType);
88        if (containerType == null) {
89            A res = getAnnotation(annoType);
90            int size = res == null ? 0 : 1;
91
92            @SuppressWarnings("unchecked") // annoType is the Class for A
93            A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
94            if (res != null)
95                arr[0] = res;
96            return arr;
97        }
98
99        // So we have a containing type
100        String annoTypeName = annoType.getName();
101        String containerTypeName = containerType.getName();
102        int directIndex = -1, containerIndex = -1;
103        Attribute.Compound direct = null, container = null;
104        // Find directly (explicit or implicit) present annotations
105        int index = -1;
106        for (Attribute.Compound attribute : getAnnotationMirrors()) {
107            index++;
108            if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) {
109                directIndex = index;
110                direct = attribute;
111            } else if(containerTypeName != null &&
112                      attribute.type.tsym.flatName().contentEquals(containerTypeName)) {
113                containerIndex = index;
114                container = attribute;
115            }
116        }
117
118        // Deal with inherited annotations
119        if (direct == null && container == null &&
120                annoType.isAnnotationPresent(Inherited.class))
121            return getInheritedAnnotations(annoType);
122
123        Attribute.Compound[] contained = unpackContained(container);
124
125        // In case of an empty legacy container we might need to look for
126        // inherited annos as well
127        if (direct == null && contained.length == 0 &&
128                annoType.isAnnotationPresent(Inherited.class))
129            return getInheritedAnnotations(annoType);
130
131        int size = (direct == null ? 0 : 1) + contained.length;
132        @SuppressWarnings("unchecked") // annoType is the Class for A
133        A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
134
135        // if direct && container, which is first?
136        int insert = -1;
137        int length = arr.length;
138        if (directIndex >= 0 && containerIndex >= 0) {
139            if (directIndex < containerIndex) {
140                arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
141                insert = 1;
142            } else {
143                arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
144                insert = 0;
145                length--;
146            }
147        } else if (directIndex >= 0) {
148            arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
149            return arr;
150        } else {
151            // Only container
152            insert = 0;
153        }
154
155        for (int i = 0; i + insert < length; i++)
156            arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
157
158        return arr;
159    }
160
161    private Attribute.Compound[] unpackContained(Attribute.Compound container) {
162        // Pack them in an array
163        Attribute[] contained0 = null;
164        if (container != null)
165            contained0 = unpackAttributes(container);
166        ListBuffer<Attribute.Compound> compounds = new ListBuffer<>();
167        if (contained0 != null) {
168            for (Attribute a : contained0)
169                if (a instanceof Attribute.Compound)
170                    compounds = compounds.append((Attribute.Compound)a);
171        }
172        return compounds.toArray(new Attribute.Compound[compounds.size()]);
173    }
174
175    // This method is part of the javax.lang.model API, do not use this in javac code.
176    @DefinedBy(Api.LANGUAGE_MODEL)
177    public <A extends Annotation> A getAnnotation(Class<A> annoType) {
178
179        if (!annoType.isAnnotation())
180            throw new IllegalArgumentException("Not an annotation type: " + annoType);
181
182        Attribute.Compound c = getAttribute(annoType);
183        return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
184    }
185
186    // Needed to unpack the runtime view of containing annotations
187    private static final Class<? extends Annotation> REPEATABLE_CLASS = initRepeatable();
188    private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod();
189
190    private static Class<? extends Annotation> initRepeatable() {
191        try {
192            // Repeatable will not be available when bootstrapping on
193            // JDK 7 so use a reflective lookup instead of a class
194            // literal for Repeatable.class.
195            return Class.forName("java.lang.annotation.Repeatable").asSubclass(Annotation.class);
196        } catch (ClassNotFoundException | SecurityException e) {
197            return null;
198        }
199    }
200
201    private static Method initValueElementMethod() {
202        if (REPEATABLE_CLASS == null)
203            return null;
204
205        Method m = null;
206        try {
207            m = REPEATABLE_CLASS.getMethod("value");
208            if (m != null)
209                m.setAccessible(true);
210            return m;
211        } catch (NoSuchMethodException e) {
212            return null;
213        }
214    }
215
216
217    // Helper to getAnnotationsByType
218    private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) {
219        // Since we can not refer to java.lang.annotation.Repeatable until we are
220        // bootstrapping with java 8 we need to get the Repeatable annotation using
221        // reflective invocations instead of just using its type and element method.
222        if (REPEATABLE_CLASS != null &&
223            VALUE_ELEMENT_METHOD != null) {
224            // Get the Repeatable instance on the annotations declaration
225            Annotation repeatable = (Annotation)annoType.getAnnotation(REPEATABLE_CLASS);
226            if (repeatable != null) {
227                try {
228                    // Get the value element, it should be a class
229                    // indicating the containing annotation type
230                    @SuppressWarnings("unchecked")
231                    Class<? extends Annotation> containerType = (Class)VALUE_ELEMENT_METHOD.invoke(repeatable);
232                    if (containerType == null)
233                        return null;
234
235                    return containerType;
236                } catch (ClassCastException | IllegalAccessException | InvocationTargetException e) {
237                    return null;
238                }
239            }
240        }
241        return null;
242    }
243
244
245    // Helper to getAnnotationsByType
246    private static Attribute[] unpackAttributes(Attribute.Compound container) {
247        // We now have an instance of the container,
248        // unpack it returning an instance of the
249        // contained type or null
250        return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values;
251    }
252
253}
254