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         * <pre>
234         *     {@code @com.example.foo(name1=val1, name2=val2)}
235         *     {@code @com.example.foo(val)}
236         *     {@code @com.example.foo}
237         * </pre>
238         * Omit parens for marker annotations, and omit "value=" when allowed.
239         */
240        @DefinedBy(Api.LANGUAGE_MODEL)
241        public String toString() {
242            StringBuilder buf = new StringBuilder();
243            buf.append("@");
244            buf.append(type);
245            int len = values.length();
246            if (len > 0) {
247                buf.append('(');
248                boolean first = true;
249                for (Pair<MethodSymbol, Attribute> value : values) {
250                    if (!first) buf.append(", ");
251                    first = false;
252
253                    Name name = value.fst.name;
254                    if (len > 1 || name != name.table.names.value) {
255                        buf.append(name);
256                        buf.append('=');
257                    }
258                    buf.append(value.snd);
259                }
260                buf.append(')');
261            }
262            return buf.toString();
263        }
264
265        public Attribute member(Name member) {
266            Pair<MethodSymbol,Attribute> res = getElemPair(member);
267            return res == null ? null : res.snd;
268        }
269
270        private Pair<MethodSymbol, Attribute> getElemPair(Name member) {
271            for (Pair<MethodSymbol,Attribute> pair : values)
272                if (pair.fst.name == member) return pair;
273            return null;
274        }
275
276        @DefinedBy(Api.LANGUAGE_MODEL)
277        public Attribute.Compound getValue() {
278            return this;
279        }
280
281        @DefinedBy(Api.LANGUAGE_MODEL)
282        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
283            return v.visitAnnotation(this, p);
284        }
285
286        @DefinedBy(Api.LANGUAGE_MODEL)
287        public DeclaredType getAnnotationType() {
288            return (DeclaredType) type;
289        }
290
291        @DefinedBy(Api.LANGUAGE_MODEL)
292        public Map<MethodSymbol, Attribute> getElementValues() {
293            Map<MethodSymbol, Attribute> valmap = new LinkedHashMap<>();
294            for (Pair<MethodSymbol, Attribute> value : values)
295                valmap.put(value.fst, value.snd);
296            return valmap;
297        }
298    }
299
300    public static class TypeCompound extends Compound {
301        public TypeCompound(Compound compound,
302                             TypeAnnotationPosition position) {
303            super(compound.type, compound.values, position);
304        }
305
306        public TypeCompound(Type type,
307                             List<Pair<MethodSymbol,Attribute>> values,
308                             TypeAnnotationPosition position) {
309            super(type, values, position);
310        }
311    }
312
313    /** The value for an annotation element of an array type.
314     */
315    public static class Array extends Attribute {
316        public final Attribute[] values;
317        public Array(Type type, Attribute[] values) {
318            super(type);
319            this.values = values;
320        }
321
322        public Array(Type type, List<Attribute> values) {
323            super(type);
324            this.values = values.toArray(new Attribute[values.size()]);
325        }
326
327        public void accept(Visitor v) { v.visitArray(this); }
328        @DefinedBy(Api.LANGUAGE_MODEL)
329        public String toString() {
330            StringBuilder buf = new StringBuilder();
331            buf.append('{');
332            boolean first = true;
333            for (Attribute value : values) {
334                if (!first)
335                    buf.append(", ");
336                first = false;
337                buf.append(value);
338            }
339            buf.append('}');
340            return buf.toString();
341        }
342        @DefinedBy(Api.LANGUAGE_MODEL)
343        public List<Attribute> getValue() {
344            return List.from(values);
345        }
346        @DefinedBy(Api.LANGUAGE_MODEL)
347        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
348            return v.visitArray(getValue(), p);
349        }
350
351        @Override
352        public TypeAnnotationPosition getPosition() {
353            if (values.length != 0)
354                return values[0].getPosition();
355            else
356                return null;
357        }
358    }
359
360    /** The value for an annotation element of an enum type.
361     */
362    public static class Enum extends Attribute {
363        public VarSymbol value;
364        public Enum(Type type, VarSymbol value) {
365            super(type);
366            this.value = Assert.checkNonNull(value);
367        }
368        public void accept(Visitor v) { v.visitEnum(this); }
369        @DefinedBy(Api.LANGUAGE_MODEL)
370        public String toString() {
371            return value.enclClass() + "." + value;     // qualified name
372        }
373        @DefinedBy(Api.LANGUAGE_MODEL)
374        public VarSymbol getValue() {
375            return value;
376        }
377        @DefinedBy(Api.LANGUAGE_MODEL)
378        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
379            return v.visitEnumConstant(value, p);
380        }
381    }
382
383    public static class Error extends Attribute {
384        public Error(Type type) {
385            super(type);
386        }
387        public void accept(Visitor v) { v.visitError(this); }
388        @DefinedBy(Api.LANGUAGE_MODEL)
389        public String toString() {
390            return "<error>";
391        }
392        @DefinedBy(Api.LANGUAGE_MODEL)
393        public String getValue() {
394            return toString();
395        }
396        @DefinedBy(Api.LANGUAGE_MODEL)
397        public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
398            return v.visitString(toString(), p);
399        }
400    }
401
402    public static class UnresolvedClass extends Error {
403        public Type classType;
404        public UnresolvedClass(Type type, Type classType) {
405            super(type);
406            this.classType = classType;
407        }
408    }
409
410    /** A visitor type for dynamic dispatch on the kind of attribute value. */
411    public static interface Visitor {
412        void visitConstant(Attribute.Constant value);
413        void visitClass(Attribute.Class clazz);
414        void visitCompound(Attribute.Compound compound);
415        void visitArray(Attribute.Array array);
416        void visitEnum(Attribute.Enum e);
417        void visitError(Attribute.Error e);
418    }
419
420    /** A mirror of java.lang.annotation.RetentionPolicy. */
421    public static enum RetentionPolicy {
422        SOURCE,
423        CLASS,
424        RUNTIME
425    }
426}
427