Attribute.java revision 2646:ff1998c1ecab
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        }
129        @DefinedBy(Api.LANGUAGE_MODEL)
130        public String toString() {
131            return classType + ".class";
132        }
133        @DefinedBy(Api.LANGUAGE_MODEL)
134        public Type getValue() {
135            return classType;
136        }
137        @DefinedBy(Api.LANGUAGE_MODEL)
138        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
139            return v.visitType(classType, p);
140        }
141    }
142
143    /** A compound annotation element value, the type of which is an
144     *  attribute interface.
145     */
146    public static class Compound extends Attribute implements AnnotationMirror {
147        /** The attributes values, as pairs.  Each pair contains a
148         *  reference to the accessing method in the attribute interface
149         *  and the value to be returned when that method is called to
150         *  access this attribute.
151         */
152        public final List<Pair<MethodSymbol,Attribute>> values;
153        public TypeAnnotationPosition position;
154
155        private boolean synthesized = false;
156
157        @Override
158        public boolean isSynthesized() {
159            return synthesized;
160        }
161
162        public void setSynthesized(boolean synthesized) {
163            this.synthesized = synthesized;
164        }
165
166        public Compound(Type type,
167                        List<Pair<MethodSymbol,Attribute>> values,
168                        TypeAnnotationPosition position) {
169            super(type);
170            this.values = values;
171            this.position = position;
172        }
173
174        public Compound(Type type,
175                        List<Pair<MethodSymbol,Attribute>> values) {
176            this(type, values, null);
177        }
178
179        @Override
180        public TypeAnnotationPosition getPosition() {
181            if (hasUnknownPosition()) {
182                if (values.size() != 0) {
183                    Name valueName = values.head.fst.name.table.names.value;
184                    Pair<MethodSymbol, Attribute> res = getElemPair(valueName);
185                    position = res == null ? null : res.snd.getPosition();
186                }
187            }
188            return position;
189        }
190
191        public boolean isContainerTypeCompound() {
192            if (isSynthesized() && values.size() == 1)
193                return getFirstEmbeddedTC() != null;
194            return false;
195        }
196
197        private Compound getFirstEmbeddedTC() {
198            if (values.size() == 1) {
199                Pair<MethodSymbol, Attribute> val = values.get(0);
200                if (val.fst.getSimpleName().contentEquals("value")
201                        && val.snd instanceof Array) {
202                    Array arr = (Array) val.snd;
203                    if (arr.values.length != 0
204                            && arr.values[0] instanceof Attribute.TypeCompound)
205                        return (Attribute.TypeCompound) arr.values[0];
206                }
207            }
208            return null;
209        }
210
211        public boolean tryFixPosition() {
212            if (!isContainerTypeCompound())
213                return false;
214
215            Compound from = getFirstEmbeddedTC();
216            if (from != null && from.position != null &&
217                    from.position.type != TargetType.UNKNOWN) {
218                position = from.position;
219                return true;
220            }
221            return false;
222        }
223
224        public boolean hasUnknownPosition() {
225            return position.type == TargetType.UNKNOWN;
226        }
227
228        public void accept(Visitor v) { v.visitCompound(this); }
229
230        /**
231         * Returns a string representation of this annotation.
232         * String is of one of the forms:
233         *     @com.example.foo(name1=val1, name2=val2)
234         *     @com.example.foo(val)
235         *     @com.example.foo
236         * Omit parens for marker annotations, and omit "value=" when allowed.
237         */
238        @DefinedBy(Api.LANGUAGE_MODEL)
239        public String toString() {
240            StringBuilder buf = new StringBuilder();
241            buf.append("@");
242            buf.append(type);
243            int len = values.length();
244            if (len > 0) {
245                buf.append('(');
246                boolean first = true;
247                for (Pair<MethodSymbol, Attribute> value : values) {
248                    if (!first) buf.append(", ");
249                    first = false;
250
251                    Name name = value.fst.name;
252                    if (len > 1 || name != name.table.names.value) {
253                        buf.append(name);
254                        buf.append('=');
255                    }
256                    buf.append(value.snd);
257                }
258                buf.append(')');
259            }
260            return buf.toString();
261        }
262
263        public Attribute member(Name member) {
264            Pair<MethodSymbol,Attribute> res = getElemPair(member);
265            return res == null ? null : res.snd;
266        }
267
268        private Pair<MethodSymbol, Attribute> getElemPair(Name member) {
269            for (Pair<MethodSymbol,Attribute> pair : values)
270                if (pair.fst.name == member) return pair;
271            return null;
272        }
273
274        @DefinedBy(Api.LANGUAGE_MODEL)
275        public Attribute.Compound getValue() {
276            return this;
277        }
278
279        @DefinedBy(Api.LANGUAGE_MODEL)
280        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
281            return v.visitAnnotation(this, p);
282        }
283
284        @DefinedBy(Api.LANGUAGE_MODEL)
285        public DeclaredType getAnnotationType() {
286            return (DeclaredType) type;
287        }
288
289        @DefinedBy(Api.LANGUAGE_MODEL)
290        public Map<MethodSymbol, Attribute> getElementValues() {
291            Map<MethodSymbol, Attribute> valmap = new LinkedHashMap<>();
292            for (Pair<MethodSymbol, Attribute> value : values)
293                valmap.put(value.fst, value.snd);
294            return valmap;
295        }
296    }
297
298    public static class TypeCompound extends Compound {
299        public TypeCompound(Compound compound,
300                             TypeAnnotationPosition position) {
301            super(compound.type, compound.values, position);
302        }
303
304        public TypeCompound(Type type,
305                             List<Pair<MethodSymbol,Attribute>> values,
306                             TypeAnnotationPosition position) {
307            super(type, values, position);
308        }
309    }
310
311    /** The value for an annotation element of an array type.
312     */
313    public static class Array extends Attribute {
314        public final Attribute[] values;
315        public Array(Type type, Attribute[] values) {
316            super(type);
317            this.values = values;
318        }
319
320        public Array(Type type, List<Attribute> values) {
321            super(type);
322            this.values = values.toArray(new Attribute[values.size()]);
323        }
324
325        public void accept(Visitor v) { v.visitArray(this); }
326        @DefinedBy(Api.LANGUAGE_MODEL)
327        public String toString() {
328            StringBuilder buf = new StringBuilder();
329            buf.append('{');
330            boolean first = true;
331            for (Attribute value : values) {
332                if (!first)
333                    buf.append(", ");
334                first = false;
335                buf.append(value);
336            }
337            buf.append('}');
338            return buf.toString();
339        }
340        @DefinedBy(Api.LANGUAGE_MODEL)
341        public List<Attribute> getValue() {
342            return List.from(values);
343        }
344        @DefinedBy(Api.LANGUAGE_MODEL)
345        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
346            return v.visitArray(getValue(), p);
347        }
348
349        @Override
350        public TypeAnnotationPosition getPosition() {
351            if (values.length != 0)
352                return values[0].getPosition();
353            else
354                return null;
355        }
356    }
357
358    /** The value for an annotation element of an enum type.
359     */
360    public static class Enum extends Attribute {
361        public VarSymbol value;
362        public Enum(Type type, VarSymbol value) {
363            super(type);
364            this.value = Assert.checkNonNull(value);
365        }
366        public void accept(Visitor v) { v.visitEnum(this); }
367        @DefinedBy(Api.LANGUAGE_MODEL)
368        public String toString() {
369            return value.enclClass() + "." + value;     // qualified name
370        }
371        @DefinedBy(Api.LANGUAGE_MODEL)
372        public VarSymbol getValue() {
373            return value;
374        }
375        @DefinedBy(Api.LANGUAGE_MODEL)
376        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
377            return v.visitEnumConstant(value, p);
378        }
379    }
380
381    public static class Error extends Attribute {
382        public Error(Type type) {
383            super(type);
384        }
385        public void accept(Visitor v) { v.visitError(this); }
386        @DefinedBy(Api.LANGUAGE_MODEL)
387        public String toString() {
388            return "<error>";
389        }
390        @DefinedBy(Api.LANGUAGE_MODEL)
391        public String getValue() {
392            return toString();
393        }
394        @DefinedBy(Api.LANGUAGE_MODEL)
395        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
396            return v.visitString(toString(), p);
397        }
398    }
399
400    public static class UnresolvedClass extends Error {
401        public Type classType;
402        public UnresolvedClass(Type type, Type classType) {
403            super(type);
404            this.classType = classType;
405        }
406    }
407
408    /** A visitor type for dynamic dispatch on the kind of attribute value. */
409    public static interface Visitor {
410        void visitConstant(Attribute.Constant value);
411        void visitClass(Attribute.Class clazz);
412        void visitCompound(Attribute.Compound compound);
413        void visitArray(Attribute.Array array);
414        void visitEnum(Attribute.Enum e);
415        void visitError(Attribute.Error e);
416    }
417
418    /** A mirror of java.lang.annotation.RetentionPolicy. */
419    public static enum RetentionPolicy {
420        SOURCE,
421        CLASS,
422        RUNTIME
423    }
424}
425