Mon, 17 Dec 2012 07:47:05 -0800
8004832: Add new doclint package
Reviewed-by: mcimadamore
1 /*
2 * Copyright (c) 2012, 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 */
26 package com.sun.tools.javac.code;
28 import java.util.Map;
30 import javax.tools.JavaFileObject;
32 import com.sun.tools.javac.comp.Annotate;
33 import com.sun.tools.javac.comp.AttrContext;
34 import com.sun.tools.javac.comp.Env;
35 import com.sun.tools.javac.util.*;
36 import com.sun.tools.javac.util.Assert;
37 import com.sun.tools.javac.util.List;
38 import com.sun.tools.javac.util.Log;
39 import com.sun.tools.javac.util.Pair;
40 import static com.sun.tools.javac.code.Kinds.PCK;
42 /**
43 * Container for all annotations (attributes in javac) on a Symbol.
44 *
45 * This class is explicitly mutable. Its contents will change when attributes
46 * are annotated onto the Symbol. However this class depends on the facts that
47 * List (in javac) is immutable.
48 *
49 * An instance of this class can be in one of three states:
50 *
51 * NOT_STARTED indicates that the Symbol this instance belongs to have not been
52 * annotated (yet). Specifically if the declaration is not annotated this
53 * instance will never move past NOT_STARTED. You can never go back to
54 * NOT_STARTED.
55 *
56 * IN_PROGRESS annotations have been found on the declaration. Will be processed
57 * later. You can reset to IN_PROGRESS. While IN_PROGRESS you can set the list
58 * of attributes (and this moves out of the IN_PROGRESS state).
59 *
60 * "unnamed" this Annotations contains some attributes, possibly the final set.
61 * While in this state you can only prepend or append to the attributes not set
62 * it directly. You can also move back to the IN_PROGRESS sate using reset().
63 *
64 * <p><b>This is NOT part of any supported API. If you write code that depends
65 * on this, you do so at your own risk. This code and its internal interfaces
66 * are subject to change or deletion without notice.</b>
67 */
68 public class Annotations {
70 private static final List<Attribute.Compound> NOT_STARTED = List.of(null);
71 private static final List<Attribute.Compound> IN_PROGRESS = List.of(null);
72 /*
73 * This field should never be null
74 */
75 private List<Attribute.Compound> attributes = NOT_STARTED;
76 /*
77 * The Symbol this Annotations belong to
78 */
79 private final Symbol sym;
81 public Annotations(Symbol sym) {
82 this.sym = sym;
83 }
85 public List<Attribute.Compound> getAttributes() {
86 return filterSentinels(attributes);
87 }
89 public void setAttributes(List<Attribute.Compound> a) {
90 Assert.check(pendingCompletion() || !isStarted());
91 if (a == null) {
92 throw new NullPointerException();
93 }
94 attributes = a;
95 }
97 public void setAttributes(Annotations other) {
98 if (other == null) {
99 throw new NullPointerException();
100 }
101 setAttributes(other.getAttributes());
102 }
104 public void setAttributesWithCompletion(final Annotate.AnnotateRepeatedContext ctx) {
105 Assert.check(pendingCompletion() || (!isStarted() && sym.kind == PCK));
107 Map<Symbol.TypeSymbol, ListBuffer<Attribute.Compound>> annotated = ctx.annotated;
108 boolean atLeastOneRepeated = false;
109 List<Attribute.Compound> buf = List.<Attribute.Compound>nil();
110 for (ListBuffer<Attribute.Compound> lb : annotated.values()) {
111 if (lb.size() == 1) {
112 buf = buf.prepend(lb.first());
113 } else { // repeated
114 buf = buf.prepend(new Placeholder(lb.toList(), sym));
115 atLeastOneRepeated = true;
116 }
117 }
119 // Add non-repeating attributes
120 setAttributes(buf.reverse());
122 if (atLeastOneRepeated) {
123 // The Symbol s is now annotated with a combination of
124 // finished non-repeating annotations and placeholders for
125 // repeating annotations.
126 //
127 // We need to do this in two passes because when creating
128 // a container for a repeating annotation we must
129 // guarantee that the @ContainedBy on the
130 // contained annotation is fully annotated
131 //
132 // The way we force this order is to do all repeating
133 // annotations in a pass after all non-repeating are
134 // finished. This will work because @ContainedBy
135 // is non-repeating and therefore will be annotated in the
136 // fist pass.
138 // Queue a pass that will replace Attribute.Placeholders
139 // with Attribute.Compound (made from synthesized containers).
140 ctx.annotateRepeated(new Annotate.Annotator() {
142 @Override
143 public String toString() {
144 return "repeated annotation pass of: " + sym + " in: " + sym.owner;
145 }
147 @Override
148 public void enterAnnotation() {
149 complete(ctx);
150 }
151 });
152 }
153 }
155 public Annotations reset() {
156 attributes = IN_PROGRESS;
157 return this;
158 }
160 public boolean isEmpty() {
161 return !isStarted()
162 || pendingCompletion()
163 || attributes.isEmpty();
164 }
166 public boolean pendingCompletion() {
167 return attributes == IN_PROGRESS;
168 }
170 public Annotations append(List<Attribute.Compound> l) {
171 attributes = filterSentinels(attributes);
173 if (l.isEmpty()) {
174 ; // no-op
175 } else if (attributes.isEmpty()) {
176 attributes = l;
177 } else {
178 attributes = attributes.appendList(l);
179 }
180 return this;
181 }
183 public Annotations prepend(List<Attribute.Compound> l) {
184 attributes = filterSentinels(attributes);
186 if (l.isEmpty()) {
187 ; // no-op
188 } else if (attributes.isEmpty()) {
189 attributes = l;
190 } else {
191 attributes = attributes.prependList(l);
192 }
193 return this;
194 }
196 private List<Attribute.Compound> filterSentinels(List<Attribute.Compound> a) {
197 return (a == IN_PROGRESS || a == NOT_STARTED)
198 ? List.<Attribute.Compound>nil()
199 : a;
200 }
202 private boolean isStarted() {
203 return attributes != NOT_STARTED;
204 }
206 private List<Attribute.Compound> getPlaceholders() {
207 List<Attribute.Compound> res = List.<Attribute.Compound>nil();
208 for (Attribute.Compound a : filterSentinels(attributes)) {
209 if (a instanceof Placeholder) {
210 res = res.prepend(a);
211 }
212 }
213 return res.reverse();
214 }
216 /*
217 * Replace Placeholders for repeating annotations with their containers
218 */
219 private void complete(Annotate.AnnotateRepeatedContext ctx) {
220 Assert.check(!pendingCompletion());
221 Log log = ctx.log;
222 Env<AttrContext> env = ctx.env;
223 JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile);
224 try {
226 if (isEmpty()) {
227 return;
228 }
230 List<Attribute.Compound> result = List.nil();
231 for (Attribute.Compound a : getAttributes()) {
232 if (a instanceof Placeholder) {
233 Attribute.Compound replacement = replaceOne((Placeholder) a, ctx);
235 if (null != replacement) {
236 result = result.prepend(replacement);
237 }
238 } else {
239 result = result.prepend(a);
240 }
241 }
243 attributes = result.reverse();
245 Assert.check(Annotations.this.getPlaceholders().isEmpty());
246 } finally {
247 log.useSource(oldSource);
248 }
249 }
251 private Attribute.Compound replaceOne(Placeholder placeholder, Annotate.AnnotateRepeatedContext ctx) {
252 Log log = ctx.log;
254 // Process repeated annotations
255 Attribute.Compound validRepeated =
256 ctx.processRepeatedAnnotations(placeholder.getPlaceholderFor(), sym);
258 if (validRepeated != null) {
259 // Check that the container isn't manually
260 // present along with repeated instances of
261 // its contained annotation.
262 ListBuffer<Attribute.Compound> manualContainer = ctx.annotated.get(validRepeated.type.tsym);
263 if (manualContainer != null) {
264 log.error(ctx.pos.get(manualContainer.first()), "invalid.containedby.annotation.repeated.and.container.present",
265 manualContainer.first().type.tsym);
266 }
267 }
269 // A null return will delete the Placeholder
270 return validRepeated;
272 }
274 private static class Placeholder extends Attribute.Compound {
276 private List<Attribute.Compound> placeholderFor;
277 private Symbol on;
279 public Placeholder(List<Attribute.Compound> placeholderFor, Symbol on) {
280 super(Type.noType, List.<Pair<Symbol.MethodSymbol, Attribute>>nil());
281 this.placeholderFor = placeholderFor;
282 this.on = on;
283 }
285 @Override
286 public String toString() {
287 return "<placeholder: " + placeholderFor + " on: " + on + ">";
288 }
290 public List<Attribute.Compound> getPlaceholderFor() {
291 return placeholderFor;
292 }
293 }
294 }