Attribute.java revision 2628:8df25ec8c930
1/*
2 * Copyright (c) 2003, 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
26package com.sun.tools.javac.code;
27
28import java.util.LinkedHashMap;
29import java.util.Map;
30import javax.lang.model.element.AnnotationMirror;
31import javax.lang.model.element.AnnotationValue;
32import javax.lang.model.element.AnnotationValueVisitor;
33import javax.lang.model.type.DeclaredType;
34import com.sun.tools.javac.code.Symbol.*;
35import com.sun.tools.javac.util.*;
36import com.sun.tools.javac.util.DefinedBy.Api;
37
38/** An annotation value.
39 *
40 *  <p><b>This is NOT part of any supported API.
41 *  If you write code that depends on this, you do so at your own risk.
42 *  This code and its internal interfaces are subject to change or
43 *  deletion without notice.</b>
44 */
45public abstract class Attribute implements AnnotationValue {
46
47    /** The type of the annotation element. */
48    public Type type;
49
50    public Attribute(Type type) {
51        this.type = type;
52    }
53
54    public abstract void accept(Visitor v);
55
56    @DefinedBy(Api.LANGUAGE_MODEL)
57    public Object getValue() {
58        throw new UnsupportedOperationException();
59    }
60
61    @DefinedBy(Api.LANGUAGE_MODEL)
62    public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
63        throw new UnsupportedOperationException();
64    }
65
66    public boolean isSynthesized() {
67        return false;
68    }
69
70    public TypeAnnotationPosition getPosition() { return null; }
71
72    /** The value for an annotation element of primitive type or String. */
73    public static class Constant extends Attribute {
74        public final Object value;
75        public void accept(Visitor v) { v.visitConstant(this); }
76        public Constant(Type type, Object value) {
77            super(type);
78            this.value = value;
79        }
80        @DefinedBy(Api.LANGUAGE_MODEL)
81        public String toString() {
82            return Constants.format(value, type);
83        }
84        @DefinedBy(Api.LANGUAGE_MODEL)
85        public Object getValue() {
86            return Constants.decode(value, type);
87        }
88        @DefinedBy(Api.LANGUAGE_MODEL)
89        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
90            if (value instanceof String)
91                return v.visitString((String) value, p);
92            if (value instanceof Integer) {
93                int i = (Integer) value;
94                switch (type.getTag()) {
95                case BOOLEAN:   return v.visitBoolean(i != 0, p);
96                case CHAR:      return v.visitChar((char) i, p);
97                case BYTE:      return v.visitByte((byte) i, p);
98                case SHORT:     return v.visitShort((short) i, p);
99                case INT:       return v.visitInt(i, p);
100                }
101            }
102            switch (type.getTag()) {
103            case LONG:          return v.visitLong((Long) value, p);
104            case FLOAT:         return v.visitFloat((Float) value, p);
105            case DOUBLE:        return v.visitDouble((Double) value, p);
106            }
107            throw new AssertionError("Bad annotation element value: " + value);
108        }
109    }
110
111    /** The value for an annotation element of type java.lang.Class,
112     *  represented as a ClassSymbol.
113     */
114    public static class Class extends Attribute {
115        public final Type classType;
116        public void accept(Visitor v) { v.visitClass(this); }
117        public Class(Types types, Type type) {
118            super(makeClassType(types, type));
119            this.classType = type;
120        }
121        static Type makeClassType(Types types, Type type) {
122            Type arg = type.isPrimitive()
123                ? types.boxedClass(type).type
124                : types.erasure(type);
125            return new Type.ClassType(types.syms.classType.getEnclosingType(),
126                                      List.of(arg),
127                                      types.syms.classType.tsym,
128                                      Type.noAnnotations);
129        }
130        @DefinedBy(Api.LANGUAGE_MODEL)
131        public String toString() {
132            return classType + ".class";
133        }
134        @DefinedBy(Api.LANGUAGE_MODEL)
135        public Type getValue() {
136            return classType;
137        }
138        @DefinedBy(Api.LANGUAGE_MODEL)
139        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
140            return v.visitType(classType, p);
141        }
142    }
143
144    /** A compound annotation element value, the type of which is an
145     *  attribute interface.
146     */
147    public static class Compound extends Attribute implements AnnotationMirror {
148        /** The attributes values, as pairs.  Each pair contains a
149         *  reference to the accessing method in the attribute interface
150         *  and the value to be returned when that method is called to
151         *  access this attribute.
152         */
153        public final List<Pair<MethodSymbol,Attribute>> values;
154        public TypeAnnotationPosition position;
155
156        private boolean synthesized = false;
157
158        @Override
159        public boolean isSynthesized() {
160            return synthesized;
161        }
162
163        public void setSynthesized(boolean synthesized) {
164            this.synthesized = synthesized;
165        }
166
167        public Compound(Type type,
168                        List<Pair<MethodSymbol,Attribute>> values,
169                        TypeAnnotationPosition position) {
170            super(type);
171            this.values = values;
172            this.position = position;
173        }
174
175        public Compound(Type type,
176                        List<Pair<MethodSymbol,Attribute>> values) {
177            this(type, values, null);
178        }
179
180        @Override
181        public TypeAnnotationPosition getPosition() {
182            if (hasUnknownPosition()) {
183                if (values.size() != 0) {
184                    Name valueName = values.head.fst.name.table.names.value;
185                    Pair<MethodSymbol, Attribute> res = getElemPair(valueName);
186                    position = res == null ? null : res.snd.getPosition();
187                }
188            }
189            return position;
190        }
191
192        public boolean isContainerTypeCompound() {
193            if (isSynthesized() && values.size() == 1)
194                return getFirstEmbeddedTC() != null;
195            return false;
196        }
197
198        private Compound getFirstEmbeddedTC() {
199            if (values.size() == 1) {
200                Pair<MethodSymbol, Attribute> val = values.get(0);
201                if (val.fst.getSimpleName().contentEquals("value")
202                        && val.snd instanceof Array) {
203                    Array arr = (Array) val.snd;
204                    if (arr.values.length != 0
205                            && arr.values[0] instanceof Attribute.TypeCompound)
206                        return (Attribute.TypeCompound) arr.values[0];
207                }
208            }
209            return null;
210        }
211
212        public boolean tryFixPosition() {
213            if (!isContainerTypeCompound())
214                return false;
215
216            Compound from = getFirstEmbeddedTC();
217            if (from != null && from.position != null &&
218                    from.position.type != TargetType.UNKNOWN) {
219                position = from.position;
220                return true;
221            }
222            return false;
223        }
224
225        public boolean hasUnknownPosition() {
226            return position.type == TargetType.UNKNOWN;
227        }
228
229        public void accept(Visitor v) { v.visitCompound(this); }
230
231        /**
232         * Returns a string representation of this annotation.
233         * String is of one of the forms:
234         *     @com.example.foo(name1=val1, name2=val2)
235         *     @com.example.foo(val)
236         *     @com.example.foo
237         * Omit parens for marker annotations, and omit "value=" when allowed.
238         */
239        @DefinedBy(Api.LANGUAGE_MODEL)
240        public String toString() {
241            StringBuilder buf = new StringBuilder();
242            buf.append("@");
243            buf.append(type);
244            int len = values.length();
245            if (len > 0) {
246                buf.append('(');
247                boolean first = true;
248                for (Pair<MethodSymbol, Attribute> value : values) {
249                    if (!first) buf.append(", ");
250                    first = false;
251
252                    Name name = value.fst.name;
253                    if (len > 1 || name != name.table.names.value) {
254                        buf.append(name);
255                        buf.append('=');
256                    }
257                    buf.append(value.snd);
258                }
259                buf.append(')');
260            }
261            return buf.toString();
262        }
263
264        public Attribute member(Name member) {
265            Pair<MethodSymbol,Attribute> res = getElemPair(member);
266            return res == null ? null : res.snd;
267        }
268
269        private Pair<MethodSymbol, Attribute> getElemPair(Name member) {
270            for (Pair<MethodSymbol,Attribute> pair : values)
271                if (pair.fst.name == member) return pair;
272            return null;
273        }
274
275        @DefinedBy(Api.LANGUAGE_MODEL)
276        public Attribute.Compound getValue() {
277            return this;
278        }
279
280        @DefinedBy(Api.LANGUAGE_MODEL)
281        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
282            return v.visitAnnotation(this, p);
283        }
284
285        @DefinedBy(Api.LANGUAGE_MODEL)
286        public DeclaredType getAnnotationType() {
287            return (DeclaredType) type;
288        }
289
290        @DefinedBy(Api.LANGUAGE_MODEL)
291        public Map<MethodSymbol, Attribute> getElementValues() {
292            Map<MethodSymbol, Attribute> valmap = new LinkedHashMap<>();
293            for (Pair<MethodSymbol, Attribute> value : values)
294                valmap.put(value.fst, value.snd);
295            return valmap;
296        }
297    }
298
299    public static class TypeCompound extends Compound {
300        public TypeCompound(Compound compound,
301                             TypeAnnotationPosition position) {
302            super(compound.type, compound.values, position);
303        }
304
305        public TypeCompound(Type type,
306                             List<Pair<MethodSymbol,Attribute>> values,
307                             TypeAnnotationPosition position) {
308            super(type, values, position);
309        }
310    }
311
312    /** The value for an annotation element of an array type.
313     */
314    public static class Array extends Attribute {
315        public final Attribute[] values;
316        public Array(Type type, Attribute[] values) {
317            super(type);
318            this.values = values;
319        }
320
321        public Array(Type type, List<Attribute> values) {
322            super(type);
323            this.values = values.toArray(new Attribute[values.size()]);
324        }
325
326        public void accept(Visitor v) { v.visitArray(this); }
327        @DefinedBy(Api.LANGUAGE_MODEL)
328        public String toString() {
329            StringBuilder buf = new StringBuilder();
330            buf.append('{');
331            boolean first = true;
332            for (Attribute value : values) {
333                if (!first)
334                    buf.append(", ");
335                first = false;
336                buf.append(value);
337            }
338            buf.append('}');
339            return buf.toString();
340        }
341        @DefinedBy(Api.LANGUAGE_MODEL)
342        public List<Attribute> getValue() {
343            return List.from(values);
344        }
345        @DefinedBy(Api.LANGUAGE_MODEL)
346        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
347            return v.visitArray(getValue(), p);
348        }
349
350        @Override
351        public TypeAnnotationPosition getPosition() {
352            if (values.length != 0)
353                return values[0].getPosition();
354            else
355                return null;
356        }
357    }
358
359    /** The value for an annotation element of an enum type.
360     */
361    public static class Enum extends Attribute {
362        public VarSymbol value;
363        public Enum(Type type, VarSymbol value) {
364            super(type);
365            this.value = Assert.checkNonNull(value);
366        }
367        public void accept(Visitor v) { v.visitEnum(this); }
368        @DefinedBy(Api.LANGUAGE_MODEL)
369        public String toString() {
370            return value.enclClass() + "." + value;     // qualified name
371        }
372        @DefinedBy(Api.LANGUAGE_MODEL)
373        public VarSymbol getValue() {
374            return value;
375        }
376        @DefinedBy(Api.LANGUAGE_MODEL)
377        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
378            return v.visitEnumConstant(value, p);
379        }
380    }
381
382    public static class Error extends Attribute {
383        public Error(Type type) {
384            super(type);
385        }
386        public void accept(Visitor v) { v.visitError(this); }
387        @DefinedBy(Api.LANGUAGE_MODEL)
388        public String toString() {
389            return "<error>";
390        }
391        @DefinedBy(Api.LANGUAGE_MODEL)
392        public String getValue() {
393            return toString();
394        }
395        @DefinedBy(Api.LANGUAGE_MODEL)
396        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
397            return v.visitString(toString(), p);
398        }
399    }
400
401    public static class UnresolvedClass extends Error {
402        public Type classType;
403        public UnresolvedClass(Type type, Type classType) {
404            super(type);
405            this.classType = classType;
406        }
407    }
408
409    /** A visitor type for dynamic dispatch on the kind of attribute value. */
410    public static interface Visitor {
411        void visitConstant(Attribute.Constant value);
412        void visitClass(Attribute.Class clazz);
413        void visitCompound(Attribute.Compound compound);
414        void visitArray(Attribute.Array array);
415        void visitEnum(Attribute.Enum e);
416        void visitError(Attribute.Error e);
417    }
418
419    /** A mirror of java.lang.annotation.RetentionPolicy. */
420    public static enum RetentionPolicy {
421        SOURCE,
422        CLASS,
423        RUNTIME
424    }
425}
426