AnnoConstruct.java revision 3170:dc017a37aac5
1/*
2 * Copyright (c) 2005, 2015, 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
26package com.sun.tools.javac.code;
27
28import java.lang.annotation.Annotation;
29import java.lang.annotation.Inherited;
30import java.lang.annotation.Repeatable;
31import java.lang.reflect.InvocationTargetException;
32import java.lang.reflect.Method;
33
34import javax.lang.model.AnnotatedConstruct;
35
36import com.sun.tools.javac.model.AnnotationProxyMaker;
37import com.sun.tools.javac.util.DefinedBy;
38import com.sun.tools.javac.util.DefinedBy.Api;
39import com.sun.tools.javac.util.List;
40import com.sun.tools.javac.util.ListBuffer;
41
42/**
43 * Common super type for annotated constructs such as Types and Symbols.
44 *
45 * This class should *not* contain any fields since it would have a significant
46 * impact on the javac memory footprint.
47 *
48 * <p><b>This is NOT part of any supported API.
49 * If you write code that depends on this, you do so at your own
50 * risk.  This code and its internal interfaces are subject to change
51 * or deletion without notice.</b></p>
52 */
53public abstract class AnnoConstruct implements AnnotatedConstruct {
54
55
56    // Override to enforce a narrower return type.
57    @Override @DefinedBy(Api.LANGUAGE_MODEL)
58    public abstract List<? extends Attribute.Compound> getAnnotationMirrors();
59
60
61    // This method is part of the javax.lang.model API, do not use this in javac code.
62    protected <A extends Annotation> Attribute.Compound getAttribute(Class<A> annoType) {
63        String name = annoType.getName();
64
65        for (Attribute.Compound anno : getAnnotationMirrors()) {
66            if (name.equals(anno.type.tsym.flatName().toString()))
67                return anno;
68        }
69
70        return null;
71    }
72
73
74    @SuppressWarnings("unchecked")
75    protected <A extends Annotation> A[] getInheritedAnnotations(Class<A> annoType) {
76        return (A[]) java.lang.reflect.Array.newInstance(annoType, 0);  // annoType is the Class for A
77    }
78
79
80    // This method is part of the javax.lang.model API, do not use this in javac code.
81    @DefinedBy(Api.LANGUAGE_MODEL)
82    public <A extends Annotation> A[] getAnnotationsByType(Class<A> annoType) {
83
84        if (!annoType.isAnnotation())
85            throw new IllegalArgumentException("Not an annotation type: "
86                                               + annoType);
87        // If annoType does not declare a container this is equivalent to wrapping
88        // getAnnotation(...) in an array.
89        Class <? extends Annotation> containerType = getContainer(annoType);
90        if (containerType == null) {
91            A res = getAnnotation(annoType);
92            int size = res == null ? 0 : 1;
93
94            @SuppressWarnings("unchecked") // annoType is the Class for A
95            A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
96            if (res != null)
97                arr[0] = res;
98            return arr;
99        }
100
101        // So we have a containing type
102        String annoTypeName = annoType.getName();
103        String containerTypeName = containerType.getName();
104        int directIndex = -1, containerIndex = -1;
105        Attribute.Compound direct = null, container = null;
106        // Find directly (explicit or implicit) present annotations
107        int index = -1;
108        for (Attribute.Compound attribute : getAnnotationMirrors()) {
109            index++;
110            if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) {
111                directIndex = index;
112                direct = attribute;
113            } else if(containerTypeName != null &&
114                      attribute.type.tsym.flatName().contentEquals(containerTypeName)) {
115                containerIndex = index;
116                container = attribute;
117            }
118        }
119
120        // Deal with inherited annotations
121        if (direct == null && container == null &&
122                annoType.isAnnotationPresent(Inherited.class))
123            return getInheritedAnnotations(annoType);
124
125        Attribute.Compound[] contained = unpackContained(container);
126
127        // In case of an empty legacy container we might need to look for
128        // inherited annos as well
129        if (direct == null && contained.length == 0 &&
130                annoType.isAnnotationPresent(Inherited.class))
131            return getInheritedAnnotations(annoType);
132
133        int size = (direct == null ? 0 : 1) + contained.length;
134        @SuppressWarnings("unchecked") // annoType is the Class for A
135        A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
136
137        // if direct && container, which is first?
138        int insert = -1;
139        int length = arr.length;
140        if (directIndex >= 0 && containerIndex >= 0) {
141            if (directIndex < containerIndex) {
142                arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
143                insert = 1;
144            } else {
145                arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
146                insert = 0;
147                length--;
148            }
149        } else if (directIndex >= 0) {
150            arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
151            return arr;
152        } else {
153            // Only container
154            insert = 0;
155        }
156
157        for (int i = 0; i + insert < length; i++)
158            arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
159
160        return arr;
161    }
162
163    private Attribute.Compound[] unpackContained(Attribute.Compound container) {
164        // Pack them in an array
165        Attribute[] contained0 = null;
166        if (container != null)
167            contained0 = unpackAttributes(container);
168        ListBuffer<Attribute.Compound> compounds = new ListBuffer<>();
169        if (contained0 != null) {
170            for (Attribute a : contained0)
171                if (a instanceof Attribute.Compound)
172                    compounds = compounds.append((Attribute.Compound)a);
173        }
174        return compounds.toArray(new Attribute.Compound[compounds.size()]);
175    }
176
177    // This method is part of the javax.lang.model API, do not use this in javac code.
178    @DefinedBy(Api.LANGUAGE_MODEL)
179    public <A extends Annotation> A getAnnotation(Class<A> annoType) {
180
181        if (!annoType.isAnnotation())
182            throw new IllegalArgumentException("Not an annotation type: " + annoType);
183
184        Attribute.Compound c = getAttribute(annoType);
185        return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
186    }
187
188    // Helper to getAnnotationsByType
189    private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) {
190        Repeatable repeatable = annoType.getAnnotation(Repeatable.class);
191        return (repeatable == null) ? null : repeatable.value();
192    }
193
194    // Helper to getAnnotationsByType
195    private static Attribute[] unpackAttributes(Attribute.Compound container) {
196        // We now have an instance of the container,
197        // unpack it returning an instance of the
198        // contained type or null
199        return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values;
200    }
201
202}
203