1/*
2 * Copyright (c) 2013, 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 */
25package sun.reflect.annotation;
26
27import java.lang.annotation.Annotation;
28import java.lang.annotation.AnnotationFormatError;
29import java.lang.reflect.AnnotatedElement;
30import java.nio.ByteBuffer;
31import java.util.ArrayList;
32import java.util.List;
33
34/**
35 * A TypeAnnotation contains all the information needed to transform type
36 * annotations on declarations in the class file to actual Annotations in
37 * AnnotatedType instances.
38 *
39 * TypeAnnotaions contain a base Annotation, location info (which lets you
40 * distinguish between '@A Inner.@B Outer' in for example nested types),
41 * target info and the declaration the TypeAnnotaiton was parsed from.
42 */
43public final class TypeAnnotation {
44    private final TypeAnnotationTargetInfo targetInfo;
45    private final LocationInfo loc;
46    private final Annotation annotation;
47    private final AnnotatedElement baseDeclaration;
48
49    public TypeAnnotation(TypeAnnotationTargetInfo targetInfo,
50                          LocationInfo loc,
51                          Annotation annotation,
52                          AnnotatedElement baseDeclaration) {
53        this.targetInfo = targetInfo;
54        this.loc = loc;
55        this.annotation = annotation;
56        this.baseDeclaration = baseDeclaration;
57    }
58
59    public TypeAnnotationTargetInfo getTargetInfo() {
60        return targetInfo;
61    }
62    public Annotation getAnnotation() {
63        return annotation;
64    }
65    public AnnotatedElement getBaseDeclaration() {
66        return baseDeclaration;
67    }
68    public LocationInfo getLocationInfo() {
69        return loc;
70    }
71
72    public static List<TypeAnnotation> filter(TypeAnnotation[] typeAnnotations,
73                                              TypeAnnotationTarget predicate) {
74        ArrayList<TypeAnnotation> typeAnnos = new ArrayList<>(typeAnnotations.length);
75        for (TypeAnnotation t : typeAnnotations)
76            if (t.getTargetInfo().getTarget() == predicate)
77                typeAnnos.add(t);
78        typeAnnos.trimToSize();
79        return typeAnnos;
80    }
81
82    public static enum TypeAnnotationTarget {
83        CLASS_TYPE_PARAMETER,
84        METHOD_TYPE_PARAMETER,
85        CLASS_EXTENDS,
86        CLASS_IMPLEMENTS, // Not in the spec
87        CLASS_TYPE_PARAMETER_BOUND,
88        METHOD_TYPE_PARAMETER_BOUND,
89        FIELD,
90        METHOD_RETURN,
91        METHOD_RECEIVER,
92        METHOD_FORMAL_PARAMETER,
93        THROWS;
94    }
95
96    public static final class TypeAnnotationTargetInfo {
97        private final TypeAnnotationTarget target;
98        private final int count;
99        private final int secondaryIndex;
100        private static final int UNUSED_INDEX = -2; // this is not a valid index in the 308 spec
101
102        public TypeAnnotationTargetInfo(TypeAnnotationTarget target) {
103            this(target, UNUSED_INDEX, UNUSED_INDEX);
104        }
105
106        public TypeAnnotationTargetInfo(TypeAnnotationTarget target,
107                                        int count) {
108            this(target, count, UNUSED_INDEX);
109        }
110
111        public TypeAnnotationTargetInfo(TypeAnnotationTarget target,
112                                        int count,
113                                        int secondaryIndex) {
114            this.target = target;
115            this.count = count;
116            this.secondaryIndex = secondaryIndex;
117        }
118
119        public TypeAnnotationTarget getTarget() {
120            return target;
121        }
122        public int getCount() {
123            return count;
124        }
125        public int getSecondaryIndex() {
126            return secondaryIndex;
127        }
128
129        @Override
130        public String toString() {
131            return "" + target + ": " + count + ", " + secondaryIndex;
132        }
133    }
134
135    public static final class LocationInfo {
136        private final int depth;
137        private final Location[] locations;
138
139        private LocationInfo() {
140            this(0, new Location[0]);
141        }
142        private LocationInfo(int depth, Location[] locations) {
143            this.depth = depth;
144            this.locations = locations;
145        }
146
147        public static final LocationInfo BASE_LOCATION = new LocationInfo();
148
149        public static LocationInfo parseLocationInfo(ByteBuffer buf) {
150            int depth = buf.get() & 0xFF;
151            if (depth == 0)
152                return BASE_LOCATION;
153            Location[] locations = new Location[depth];
154            for (int i = 0; i < depth; i++) {
155                byte tag = buf.get();
156                short index = (short)(buf.get() & 0xFF);
157                if (!(tag == 0 || tag == 1 | tag == 2 || tag == 3))
158                    throw new AnnotationFormatError("Bad Location encoding in Type Annotation");
159                if (tag != 3 && index != 0)
160                    throw new AnnotationFormatError("Bad Location encoding in Type Annotation");
161                locations[i] = new Location(tag, index);
162            }
163            return new LocationInfo(depth, locations);
164        }
165
166        public LocationInfo pushArray() {
167            return pushLocation((byte)0, (short)0);
168        }
169
170        public LocationInfo pushInner() {
171            return pushLocation((byte)1, (short)0);
172        }
173
174        public LocationInfo pushWildcard() {
175            return pushLocation((byte) 2, (short) 0);
176        }
177
178        public LocationInfo pushTypeArg(short index) {
179            return pushLocation((byte) 3, index);
180        }
181
182        public LocationInfo pushLocation(byte tag, short index) {
183            int newDepth = this.depth + 1;
184            Location[] res = new Location[newDepth];
185            System.arraycopy(this.locations, 0, res, 0, depth);
186            res[newDepth - 1] = new Location(tag, (short)(index & 0xFF));
187            return new LocationInfo(newDepth, res);
188        }
189
190        /** Pop a series of locations matching {@code tag}. Stop poping as soon as a non-matching tag is found. */
191        public LocationInfo popAllLocations(byte tag) {
192            LocationInfo l = this;
193            int newDepth = l.depth;
194            while(newDepth > 0 && l.locations[newDepth - 1].tag == tag) {
195                newDepth--;
196            }
197            if (newDepth != l.depth) {
198                Location[] res = new Location[newDepth];
199                System.arraycopy(this.locations, 0, res, 0, newDepth);
200                return new LocationInfo(newDepth, res);
201            } else
202                return l;
203        }
204
205        public TypeAnnotation[] filter(TypeAnnotation[] ta) {
206            ArrayList<TypeAnnotation> l = new ArrayList<>(ta.length);
207            for (TypeAnnotation t : ta) {
208                if (isSameLocationInfo(t.getLocationInfo()))
209                    l.add(t);
210            }
211            return l.toArray(AnnotatedTypeFactory.EMPTY_TYPE_ANNOTATION_ARRAY);
212        }
213
214        boolean isSameLocationInfo(LocationInfo other) {
215            if (depth != other.depth)
216                return false;
217            for (int i = 0; i < depth; i++)
218                if (!locations[i].isSameLocation(other.locations[i]))
219                    return false;
220            return true;
221        }
222
223        public static final class Location {
224            public final byte tag;
225            public final short index;
226
227            boolean isSameLocation(Location other) {
228                return tag == other.tag && index == other.index;
229            }
230
231            public Location(byte tag, short index) {
232                this.tag = tag;
233                this.index = index;
234            }
235        }
236    }
237
238    @Override
239    public String toString() {
240        return annotation.toString() + " with Targetnfo: " +
241            targetInfo.toString() + " on base declaration: " +
242            baseDeclaration.toString();
243    }
244}
245