10
|
1 |
/*
|
|
2 |
* Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
|
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
|
9 |
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
22 |
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
23 |
* have any questions.
|
|
24 |
*/
|
|
25 |
|
|
26 |
package com.sun.tools.javac.processing;
|
|
27 |
|
|
28 |
import com.sun.source.util.TaskEvent;
|
|
29 |
import com.sun.source.util.TaskListener;
|
|
30 |
import com.sun.tools.javac.api.JavacTaskImpl;
|
|
31 |
import com.sun.tools.javac.util.List;
|
|
32 |
import com.sun.tools.javac.util.*;
|
|
33 |
import com.sun.tools.javac.code.*;
|
|
34 |
import com.sun.tools.javac.code.Symbol.*;
|
|
35 |
import com.sun.tools.javac.comp.*;
|
|
36 |
import com.sun.tools.javac.jvm.*;
|
|
37 |
import com.sun.tools.javac.tree.*;
|
|
38 |
import com.sun.tools.javac.parser.*;
|
|
39 |
import com.sun.tools.javac.code.Symbol.*;
|
|
40 |
import com.sun.tools.javac.model.JavacElements;
|
|
41 |
import com.sun.tools.javac.model.JavacTypes;
|
|
42 |
import com.sun.tools.javac.tree.JCTree.*;
|
|
43 |
import com.sun.tools.javac.main.JavaCompiler;
|
|
44 |
import java.io.StringWriter;
|
|
45 |
|
|
46 |
import javax.annotation.processing.*;
|
|
47 |
import javax.lang.model.SourceVersion;
|
|
48 |
import javax.lang.model.element.AnnotationMirror;
|
|
49 |
import javax.lang.model.element.Element;
|
|
50 |
import javax.lang.model.element.TypeElement;
|
|
51 |
import javax.lang.model.element.PackageElement;
|
|
52 |
import javax.lang.model.util.*;
|
|
53 |
|
|
54 |
import javax.tools.JavaFileManager;
|
|
55 |
import javax.tools.StandardJavaFileManager;
|
|
56 |
import javax.tools.JavaFileObject;
|
|
57 |
import javax.tools.DiagnosticListener;
|
|
58 |
import static javax.tools.StandardLocation.*;
|
|
59 |
|
|
60 |
import java.lang.reflect.*;
|
|
61 |
import java.util.*;
|
|
62 |
import java.util.regex.*;
|
|
63 |
|
|
64 |
import java.net.URLClassLoader;
|
|
65 |
import java.net.URL;
|
|
66 |
import java.io.Closeable;
|
|
67 |
import java.io.File;
|
|
68 |
import java.io.PrintWriter;
|
|
69 |
import java.io.IOException;
|
|
70 |
import java.net.MalformedURLException;
|
|
71 |
|
|
72 |
/**
|
|
73 |
* Objects of this class hold and manage the state needed to support
|
|
74 |
* annotation processing.
|
|
75 |
*
|
|
76 |
* <p><b>This is NOT part of any API supported by Sun Microsystems.
|
|
77 |
* If you write code that depends on this, you do so at your own risk.
|
|
78 |
* This code and its internal interfaces are subject to change or
|
|
79 |
* deletion without notice.</b>
|
|
80 |
*/
|
|
81 |
public class JavacProcessingEnvironment implements ProcessingEnvironment, Closeable {
|
|
82 |
Options options;
|
|
83 |
|
|
84 |
private final boolean printProcessorInfo;
|
|
85 |
private final boolean printRounds;
|
|
86 |
private final boolean verbose;
|
|
87 |
private final boolean lint;
|
|
88 |
private final boolean procOnly;
|
|
89 |
private final boolean fatalErrors;
|
|
90 |
|
|
91 |
private final JavacFiler filer;
|
|
92 |
private final JavacMessager messager;
|
|
93 |
private final JavacElements elementUtils;
|
|
94 |
private final JavacTypes typeUtils;
|
|
95 |
|
|
96 |
/**
|
|
97 |
* Holds relevant state history of which processors have been
|
|
98 |
* used.
|
|
99 |
*/
|
|
100 |
private DiscoveredProcessors discoveredProcs;
|
|
101 |
|
|
102 |
/**
|
|
103 |
* Map of processor-specific options.
|
|
104 |
*/
|
|
105 |
private final Map<String, String> processorOptions;
|
|
106 |
|
|
107 |
/**
|
|
108 |
*/
|
|
109 |
private final Set<String> unmatchedProcessorOptions;
|
|
110 |
|
|
111 |
/**
|
|
112 |
* Annotations implicitly processed and claimed by javac.
|
|
113 |
*/
|
|
114 |
private final Set<String> platformAnnotations;
|
|
115 |
|
|
116 |
/**
|
|
117 |
* Set of packages given on command line.
|
|
118 |
*/
|
|
119 |
private Set<PackageSymbol> specifiedPackages = Collections.emptySet();
|
|
120 |
|
|
121 |
/** The log to be used for error reporting.
|
|
122 |
*/
|
|
123 |
Log log;
|
|
124 |
|
|
125 |
/**
|
|
126 |
* Source level of the compile.
|
|
127 |
*/
|
|
128 |
Source source;
|
|
129 |
|
|
130 |
private Context context;
|
|
131 |
|
|
132 |
public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
|
|
133 |
options = Options.instance(context);
|
|
134 |
this.context = context;
|
|
135 |
log = Log.instance(context);
|
|
136 |
source = Source.instance(context);
|
|
137 |
printProcessorInfo = options.get("-XprintProcessorInfo") != null;
|
|
138 |
printRounds = options.get("-XprintRounds") != null;
|
|
139 |
verbose = options.get("-verbose") != null;
|
|
140 |
lint = options.lint("processing");
|
|
141 |
procOnly = options.get("-proc:only") != null ||
|
|
142 |
options.get("-Xprint") != null;
|
|
143 |
fatalErrors = options.get("fatalEnterError") != null;
|
|
144 |
platformAnnotations = initPlatformAnnotations();
|
|
145 |
|
|
146 |
// Initialize services before any processors are initialzied
|
|
147 |
// in case processors use them.
|
|
148 |
filer = new JavacFiler(context);
|
|
149 |
messager = new JavacMessager(context, this);
|
|
150 |
elementUtils = new JavacElements(context);
|
|
151 |
typeUtils = new JavacTypes(context);
|
|
152 |
processorOptions = initProcessorOptions(context);
|
|
153 |
unmatchedProcessorOptions = initUnmatchedProcessorOptions();
|
|
154 |
initProcessorIterator(context, processors);
|
|
155 |
}
|
|
156 |
|
|
157 |
private Set<String> initPlatformAnnotations() {
|
|
158 |
Set<String> platformAnnotations = new HashSet<String>();
|
|
159 |
platformAnnotations.add("java.lang.Deprecated");
|
|
160 |
platformAnnotations.add("java.lang.Override");
|
|
161 |
platformAnnotations.add("java.lang.SuppressWarnings");
|
|
162 |
platformAnnotations.add("java.lang.annotation.Documented");
|
|
163 |
platformAnnotations.add("java.lang.annotation.Inherited");
|
|
164 |
platformAnnotations.add("java.lang.annotation.Retention");
|
|
165 |
platformAnnotations.add("java.lang.annotation.Target");
|
|
166 |
return Collections.unmodifiableSet(platformAnnotations);
|
|
167 |
}
|
|
168 |
|
|
169 |
private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
|
|
170 |
Paths paths = Paths.instance(context);
|
|
171 |
Log log = Log.instance(context);
|
|
172 |
Iterator<? extends Processor> processorIterator;
|
|
173 |
|
|
174 |
if (options.get("-Xprint") != null) {
|
|
175 |
try {
|
|
176 |
Processor processor = PrintingProcessor.class.newInstance();
|
|
177 |
processorIterator = List.of(processor).iterator();
|
|
178 |
} catch (Throwable t) {
|
|
179 |
AssertionError assertError =
|
|
180 |
new AssertionError("Problem instantiating PrintingProcessor.");
|
|
181 |
assertError.initCause(t);
|
|
182 |
throw assertError;
|
|
183 |
}
|
|
184 |
} else if (processors != null) {
|
|
185 |
processorIterator = processors.iterator();
|
|
186 |
} else {
|
|
187 |
String processorNames = options.get("-processor");
|
|
188 |
JavaFileManager fileManager = context.get(JavaFileManager.class);
|
|
189 |
try {
|
|
190 |
// If processorpath is not explicitly set, use the classpath.
|
|
191 |
ClassLoader processorCL = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
|
|
192 |
? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
|
|
193 |
: fileManager.getClassLoader(CLASS_PATH);
|
|
194 |
|
|
195 |
/*
|
|
196 |
* If the "-processor" option is used, search the appropriate
|
|
197 |
* path for the named class. Otherwise, use a service
|
|
198 |
* provider mechanism to create the processor iterator.
|
|
199 |
*/
|
|
200 |
if (processorNames != null) {
|
|
201 |
processorIterator = new NameProcessIterator(processorNames, processorCL, log);
|
|
202 |
} else {
|
|
203 |
processorIterator = new ServiceIterator(processorCL, log);
|
|
204 |
}
|
|
205 |
} catch (SecurityException e) {
|
|
206 |
/*
|
|
207 |
* A security exception will occur if we can't create a classloader.
|
|
208 |
* Ignore the exception if, with hindsight, we didn't need it anyway
|
|
209 |
* (i.e. no processor was specified either explicitly, or implicitly,
|
|
210 |
* in service configuration file.) Otherwise, we cannot continue.
|
|
211 |
*/
|
|
212 |
processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
|
|
213 |
}
|
|
214 |
}
|
|
215 |
discoveredProcs = new DiscoveredProcessors(processorIterator);
|
|
216 |
}
|
|
217 |
|
|
218 |
/**
|
|
219 |
* Returns an empty processor iterator if no processors are on the
|
|
220 |
* relevant path, otherwise if processors are present, logs an
|
|
221 |
* error. Called when a service loader is unavailable for some
|
|
222 |
* reason, either because a service loader class cannot be found
|
|
223 |
* or because a security policy prevents class loaders from being
|
|
224 |
* created.
|
|
225 |
*
|
|
226 |
* @param key The resource key to use to log an error message
|
|
227 |
* @param e If non-null, pass this exception to Abort
|
|
228 |
*/
|
|
229 |
private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) {
|
|
230 |
JavaFileManager fileManager = context.get(JavaFileManager.class);
|
|
231 |
|
|
232 |
if (fileManager instanceof JavacFileManager) {
|
|
233 |
StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager;
|
|
234 |
Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
|
|
235 |
? standardFileManager.getLocation(ANNOTATION_PROCESSOR_PATH)
|
|
236 |
: standardFileManager.getLocation(CLASS_PATH);
|
|
237 |
|
|
238 |
if (needClassLoader(options.get("-processor"), workingPath) )
|
|
239 |
handleException(key, e);
|
|
240 |
|
|
241 |
} else {
|
|
242 |
handleException(key, e);
|
|
243 |
}
|
|
244 |
|
|
245 |
java.util.List<Processor> pl = Collections.emptyList();
|
|
246 |
return pl.iterator();
|
|
247 |
}
|
|
248 |
|
|
249 |
/**
|
|
250 |
* Handle a security exception thrown during initializing the
|
|
251 |
* Processor iterator.
|
|
252 |
*/
|
|
253 |
private void handleException(String key, Exception e) {
|
|
254 |
if (e != null) {
|
|
255 |
log.error(key, e.getLocalizedMessage());
|
|
256 |
throw new Abort(e);
|
|
257 |
} else {
|
|
258 |
log.error(key);
|
|
259 |
throw new Abort();
|
|
260 |
}
|
|
261 |
}
|
|
262 |
|
|
263 |
/**
|
|
264 |
* Use a service loader appropriate for the platform to provide an
|
|
265 |
* iterator over annotations processors. If
|
|
266 |
* java.util.ServiceLoader is present use it, otherwise, use
|
|
267 |
* sun.misc.Service, otherwise fail if a loader is needed.
|
|
268 |
*/
|
|
269 |
private class ServiceIterator implements Iterator<Processor> {
|
|
270 |
// The to-be-wrapped iterator.
|
|
271 |
private Iterator<?> iterator;
|
|
272 |
private Log log;
|
|
273 |
|
|
274 |
ServiceIterator(ClassLoader classLoader, Log log) {
|
|
275 |
Class<?> loaderClass;
|
|
276 |
String loadMethodName;
|
|
277 |
boolean jusl;
|
|
278 |
|
|
279 |
this.log = log;
|
|
280 |
try {
|
|
281 |
try {
|
|
282 |
loaderClass = Class.forName("java.util.ServiceLoader");
|
|
283 |
loadMethodName = "load";
|
|
284 |
jusl = true;
|
|
285 |
} catch (ClassNotFoundException cnfe) {
|
|
286 |
try {
|
|
287 |
loaderClass = Class.forName("sun.misc.Service");
|
|
288 |
loadMethodName = "providers";
|
|
289 |
jusl = false;
|
|
290 |
} catch (ClassNotFoundException cnfe2) {
|
|
291 |
// Fail softly if a loader is not actually needed.
|
|
292 |
this.iterator = handleServiceLoaderUnavailability("proc.no.service",
|
|
293 |
null);
|
|
294 |
return;
|
|
295 |
}
|
|
296 |
}
|
|
297 |
|
|
298 |
// java.util.ServiceLoader.load or sun.misc.Service.providers
|
|
299 |
Method loadMethod = loaderClass.getMethod(loadMethodName,
|
|
300 |
Class.class,
|
|
301 |
ClassLoader.class);
|
|
302 |
|
|
303 |
Object result = loadMethod.invoke(null,
|
|
304 |
Processor.class,
|
|
305 |
classLoader);
|
|
306 |
|
|
307 |
// For java.util.ServiceLoader, we have to call another
|
|
308 |
// method to get the iterator.
|
|
309 |
if (jusl) {
|
|
310 |
Method m = loaderClass.getMethod("iterator");
|
|
311 |
result = m.invoke(result); // serviceLoader.iterator();
|
|
312 |
}
|
|
313 |
|
|
314 |
// The result should now be an iterator.
|
|
315 |
this.iterator = (Iterator<?>) result;
|
|
316 |
} catch (Throwable t) {
|
|
317 |
log.error("proc.service.problem");
|
|
318 |
throw new Abort(t);
|
|
319 |
}
|
|
320 |
}
|
|
321 |
|
|
322 |
public boolean hasNext() {
|
|
323 |
try {
|
|
324 |
return iterator.hasNext();
|
|
325 |
} catch (Throwable t) {
|
|
326 |
if ("ServiceConfigurationError".
|
|
327 |
equals(t.getClass().getSimpleName())) {
|
|
328 |
log.error("proc.bad.config.file", t.getLocalizedMessage());
|
|
329 |
}
|
|
330 |
throw new Abort(t);
|
|
331 |
}
|
|
332 |
}
|
|
333 |
|
|
334 |
public Processor next() {
|
|
335 |
try {
|
|
336 |
return (Processor)(iterator.next());
|
|
337 |
} catch (Throwable t) {
|
|
338 |
if ("ServiceConfigurationError".
|
|
339 |
equals(t.getClass().getSimpleName())) {
|
|
340 |
log.error("proc.bad.config.file", t.getLocalizedMessage());
|
|
341 |
} else {
|
|
342 |
log.error("proc.processor.constructor.error", t.getLocalizedMessage());
|
|
343 |
}
|
|
344 |
throw new Abort(t);
|
|
345 |
}
|
|
346 |
}
|
|
347 |
|
|
348 |
public void remove() {
|
|
349 |
throw new UnsupportedOperationException();
|
|
350 |
}
|
|
351 |
}
|
|
352 |
|
|
353 |
|
|
354 |
private static class NameProcessIterator implements Iterator<Processor> {
|
|
355 |
Processor nextProc = null;
|
|
356 |
Iterator<String> names;
|
|
357 |
ClassLoader processorCL;
|
|
358 |
Log log;
|
|
359 |
|
|
360 |
NameProcessIterator(String names, ClassLoader processorCL, Log log) {
|
|
361 |
this.names = Arrays.asList(names.split(",")).iterator();
|
|
362 |
this.processorCL = processorCL;
|
|
363 |
this.log = log;
|
|
364 |
}
|
|
365 |
|
|
366 |
public boolean hasNext() {
|
|
367 |
if (nextProc != null)
|
|
368 |
return true;
|
|
369 |
else {
|
|
370 |
if (!names.hasNext())
|
|
371 |
return false;
|
|
372 |
else {
|
|
373 |
String processorName = names.next();
|
|
374 |
|
|
375 |
Processor processor;
|
|
376 |
try {
|
|
377 |
try {
|
|
378 |
processor =
|
|
379 |
(Processor) (processorCL.loadClass(processorName).newInstance());
|
|
380 |
} catch (ClassNotFoundException cnfe) {
|
|
381 |
log.error("proc.processor.not.found", processorName);
|
|
382 |
return false;
|
|
383 |
} catch (ClassCastException cce) {
|
|
384 |
log.error("proc.processor.wrong.type", processorName);
|
|
385 |
return false;
|
|
386 |
} catch (Exception e ) {
|
|
387 |
log.error("proc.processor.cant.instantiate", processorName);
|
|
388 |
return false;
|
|
389 |
}
|
|
390 |
} catch(Throwable t) {
|
|
391 |
throw new AnnotationProcessingError(t);
|
|
392 |
}
|
|
393 |
nextProc = processor;
|
|
394 |
return true;
|
|
395 |
}
|
|
396 |
|
|
397 |
}
|
|
398 |
}
|
|
399 |
|
|
400 |
public Processor next() {
|
|
401 |
if (hasNext()) {
|
|
402 |
Processor p = nextProc;
|
|
403 |
nextProc = null;
|
|
404 |
return p;
|
|
405 |
} else
|
|
406 |
throw new NoSuchElementException();
|
|
407 |
}
|
|
408 |
|
|
409 |
public void remove () {
|
|
410 |
throw new UnsupportedOperationException();
|
|
411 |
}
|
|
412 |
}
|
|
413 |
|
|
414 |
public boolean atLeastOneProcessor() {
|
|
415 |
return discoveredProcs.iterator().hasNext();
|
|
416 |
}
|
|
417 |
|
|
418 |
private Map<String, String> initProcessorOptions(Context context) {
|
|
419 |
Options options = Options.instance(context);
|
|
420 |
Set<String> keySet = options.keySet();
|
|
421 |
Map<String, String> tempOptions = new LinkedHashMap<String, String>();
|
|
422 |
|
|
423 |
for(String key : keySet) {
|
|
424 |
if (key.startsWith("-A") && key.length() > 2) {
|
|
425 |
int sepIndex = key.indexOf('=');
|
|
426 |
String candidateKey = null;
|
|
427 |
String candidateValue = null;
|
|
428 |
|
|
429 |
if (sepIndex == -1)
|
|
430 |
candidateKey = key.substring(2);
|
|
431 |
else if (sepIndex >= 3) {
|
|
432 |
candidateKey = key.substring(2, sepIndex);
|
|
433 |
candidateValue = (sepIndex < key.length()-1)?
|
|
434 |
key.substring(sepIndex+1) : null;
|
|
435 |
}
|
|
436 |
tempOptions.put(candidateKey, candidateValue);
|
|
437 |
}
|
|
438 |
}
|
|
439 |
|
|
440 |
return Collections.unmodifiableMap(tempOptions);
|
|
441 |
}
|
|
442 |
|
|
443 |
private Set<String> initUnmatchedProcessorOptions() {
|
|
444 |
Set<String> unmatchedProcessorOptions = new HashSet<String>();
|
|
445 |
unmatchedProcessorOptions.addAll(processorOptions.keySet());
|
|
446 |
return unmatchedProcessorOptions;
|
|
447 |
}
|
|
448 |
|
|
449 |
/**
|
|
450 |
* State about how a processor has been used by the tool. If a
|
|
451 |
* processor has been used on a prior round, its process method is
|
|
452 |
* called on all subsequent rounds, perhaps with an empty set of
|
|
453 |
* annotations to process. The {@code annotatedSupported} method
|
|
454 |
* caches the supported annotation information from the first (and
|
|
455 |
* only) getSupportedAnnotationTypes call to the processor.
|
|
456 |
*/
|
|
457 |
static class ProcessorState {
|
|
458 |
public Processor processor;
|
|
459 |
public boolean contributed;
|
|
460 |
private ArrayList<Pattern> supportedAnnotationPatterns;
|
|
461 |
private ArrayList<String> supportedOptionNames;
|
|
462 |
|
|
463 |
ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
|
|
464 |
processor = p;
|
|
465 |
contributed = false;
|
|
466 |
|
|
467 |
try {
|
|
468 |
processor.init(env);
|
|
469 |
|
|
470 |
checkSourceVersionCompatibility(source, log);
|
|
471 |
|
|
472 |
supportedAnnotationPatterns = new ArrayList<Pattern>();
|
|
473 |
for (String importString : processor.getSupportedAnnotationTypes()) {
|
|
474 |
supportedAnnotationPatterns.add(importStringToPattern(importString,
|
|
475 |
processor,
|
|
476 |
log));
|
|
477 |
}
|
|
478 |
|
|
479 |
supportedOptionNames = new ArrayList<String>();
|
|
480 |
for (String optionName : processor.getSupportedOptions() ) {
|
|
481 |
if (checkOptionName(optionName, log))
|
|
482 |
supportedOptionNames.add(optionName);
|
|
483 |
}
|
|
484 |
|
|
485 |
} catch (Throwable t) {
|
|
486 |
throw new AnnotationProcessingError(t);
|
|
487 |
}
|
|
488 |
}
|
|
489 |
|
|
490 |
/**
|
|
491 |
* Checks whether or not a processor's source version is
|
|
492 |
* compatible with the compilation source version. The
|
|
493 |
* processor's source version needs to be greater than or
|
|
494 |
* equal to the source version of the compile.
|
|
495 |
*/
|
|
496 |
private void checkSourceVersionCompatibility(Source source, Log log) {
|
|
497 |
SourceVersion procSourceVersion = processor.getSupportedSourceVersion();
|
|
498 |
|
|
499 |
if (procSourceVersion.compareTo(Source.toSourceVersion(source)) < 0 ) {
|
|
500 |
log.warning("proc.processor.incompatible.source.version",
|
|
501 |
procSourceVersion,
|
|
502 |
processor.getClass().getName(),
|
|
503 |
source.name);
|
|
504 |
}
|
|
505 |
}
|
|
506 |
|
|
507 |
private boolean checkOptionName(String optionName, Log log) {
|
|
508 |
boolean valid = isValidOptionName(optionName);
|
|
509 |
if (!valid)
|
|
510 |
log.error("proc.processor.bad.option.name",
|
|
511 |
optionName,
|
|
512 |
processor.getClass().getName());
|
|
513 |
return valid;
|
|
514 |
}
|
|
515 |
|
|
516 |
public boolean annotationSupported(String annotationName) {
|
|
517 |
for(Pattern p: supportedAnnotationPatterns) {
|
|
518 |
if (p.matcher(annotationName).matches())
|
|
519 |
return true;
|
|
520 |
}
|
|
521 |
return false;
|
|
522 |
}
|
|
523 |
|
|
524 |
/**
|
|
525 |
* Remove options that are matched by this processor.
|
|
526 |
*/
|
|
527 |
public void removeSupportedOptions(Set<String> unmatchedProcessorOptions) {
|
|
528 |
unmatchedProcessorOptions.removeAll(supportedOptionNames);
|
|
529 |
}
|
|
530 |
}
|
|
531 |
|
|
532 |
// TODO: These two classes can probably be rewritten better...
|
|
533 |
/**
|
|
534 |
* This class holds information about the processors that have
|
|
535 |
* been discoverd so far as well as the means to discover more, if
|
|
536 |
* necessary. A single iterator should be used per round of
|
|
537 |
* annotation processing. The iterator first visits already
|
|
538 |
* discovered processors then fails over to the service provided
|
|
539 |
* mechanism if additional queries are made.
|
|
540 |
*/
|
|
541 |
class DiscoveredProcessors implements Iterable<ProcessorState> {
|
|
542 |
|
|
543 |
class ProcessorStateIterator implements Iterator<ProcessorState> {
|
|
544 |
DiscoveredProcessors psi;
|
|
545 |
Iterator<ProcessorState> innerIter;
|
|
546 |
boolean onProcInterator;
|
|
547 |
|
|
548 |
ProcessorStateIterator(DiscoveredProcessors psi) {
|
|
549 |
this.psi = psi;
|
|
550 |
this.innerIter = psi.procStateList.iterator();
|
|
551 |
this.onProcInterator = false;
|
|
552 |
}
|
|
553 |
|
|
554 |
public ProcessorState next() {
|
|
555 |
if (!onProcInterator) {
|
|
556 |
if (innerIter.hasNext())
|
|
557 |
return innerIter.next();
|
|
558 |
else
|
|
559 |
onProcInterator = true;
|
|
560 |
}
|
|
561 |
|
|
562 |
if (psi.processorIterator.hasNext()) {
|
|
563 |
ProcessorState ps = new ProcessorState(psi.processorIterator.next(),
|
|
564 |
log, source, JavacProcessingEnvironment.this);
|
|
565 |
psi.procStateList.add(ps);
|
|
566 |
return ps;
|
|
567 |
} else
|
|
568 |
throw new NoSuchElementException();
|
|
569 |
}
|
|
570 |
|
|
571 |
public boolean hasNext() {
|
|
572 |
if (onProcInterator)
|
|
573 |
return psi.processorIterator.hasNext();
|
|
574 |
else
|
|
575 |
return innerIter.hasNext() || psi.processorIterator.hasNext();
|
|
576 |
}
|
|
577 |
|
|
578 |
public void remove () {
|
|
579 |
throw new UnsupportedOperationException();
|
|
580 |
}
|
|
581 |
|
|
582 |
/**
|
|
583 |
* Run all remaining processors on the procStateList that
|
|
584 |
* have not already run this round with an empty set of
|
|
585 |
* annotations.
|
|
586 |
*/
|
|
587 |
public void runContributingProcs(RoundEnvironment re) {
|
|
588 |
if (!onProcInterator) {
|
|
589 |
Set<TypeElement> emptyTypeElements = Collections.emptySet();
|
|
590 |
while(innerIter.hasNext()) {
|
|
591 |
ProcessorState ps = innerIter.next();
|
|
592 |
if (ps.contributed)
|
|
593 |
callProcessor(ps.processor, emptyTypeElements, re);
|
|
594 |
}
|
|
595 |
}
|
|
596 |
}
|
|
597 |
}
|
|
598 |
|
|
599 |
Iterator<? extends Processor> processorIterator;
|
|
600 |
ArrayList<ProcessorState> procStateList;
|
|
601 |
|
|
602 |
public ProcessorStateIterator iterator() {
|
|
603 |
return new ProcessorStateIterator(this);
|
|
604 |
}
|
|
605 |
|
|
606 |
DiscoveredProcessors(Iterator<? extends Processor> processorIterator) {
|
|
607 |
this.processorIterator = processorIterator;
|
|
608 |
this.procStateList = new ArrayList<ProcessorState>();
|
|
609 |
}
|
|
610 |
}
|
|
611 |
|
|
612 |
private void discoverAndRunProcs(Context context,
|
|
613 |
Set<TypeElement> annotationsPresent,
|
|
614 |
List<ClassSymbol> topLevelClasses,
|
|
615 |
List<PackageSymbol> packageInfoFiles) {
|
|
616 |
// Writer for -XprintRounds and -XprintProcessorInfo data
|
|
617 |
PrintWriter xout = context.get(Log.outKey);
|
|
618 |
|
|
619 |
Map<String, TypeElement> unmatchedAnnotations =
|
|
620 |
new HashMap<String, TypeElement>(annotationsPresent.size());
|
|
621 |
|
|
622 |
for(TypeElement a : annotationsPresent) {
|
|
623 |
unmatchedAnnotations.put(a.getQualifiedName().toString(),
|
|
624 |
a);
|
|
625 |
}
|
|
626 |
|
|
627 |
// Give "*" processors a chance to match
|
|
628 |
if (unmatchedAnnotations.size() == 0)
|
|
629 |
unmatchedAnnotations.put("", null);
|
|
630 |
|
|
631 |
DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
|
|
632 |
// TODO: Create proper argument values; need past round
|
|
633 |
// information to fill in this constructor. Note that the 1
|
|
634 |
// st round of processing could be the last round if there
|
|
635 |
// were parse errors on the initial source files; however, we
|
|
636 |
// are not doing processing in that case.
|
|
637 |
|
|
638 |
Set<Element> rootElements = new LinkedHashSet<Element>();
|
|
639 |
rootElements.addAll(topLevelClasses);
|
|
640 |
rootElements.addAll(packageInfoFiles);
|
|
641 |
rootElements = Collections.unmodifiableSet(rootElements);
|
|
642 |
|
|
643 |
RoundEnvironment renv = new JavacRoundEnvironment(false,
|
|
644 |
false,
|
|
645 |
rootElements,
|
|
646 |
JavacProcessingEnvironment.this);
|
|
647 |
|
|
648 |
while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
|
|
649 |
ProcessorState ps = psi.next();
|
|
650 |
Set<String> matchedNames = new HashSet<String>();
|
|
651 |
Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
|
|
652 |
for (String unmatchedAnnotationName : unmatchedAnnotations.keySet()) {
|
|
653 |
if (ps.annotationSupported(unmatchedAnnotationName) ) {
|
|
654 |
matchedNames.add(unmatchedAnnotationName);
|
|
655 |
TypeElement te = unmatchedAnnotations.get(unmatchedAnnotationName);
|
|
656 |
if (te != null)
|
|
657 |
typeElements.add(te);
|
|
658 |
}
|
|
659 |
}
|
|
660 |
|
|
661 |
if (matchedNames.size() > 0 || ps.contributed) {
|
|
662 |
boolean processingResult = callProcessor(ps.processor, typeElements, renv);
|
|
663 |
ps.contributed = true;
|
|
664 |
ps.removeSupportedOptions(unmatchedProcessorOptions);
|
|
665 |
|
|
666 |
if (printProcessorInfo || verbose) {
|
|
667 |
xout.println(Log.getLocalizedString("x.print.processor.info",
|
|
668 |
ps.processor.getClass().getName(),
|
|
669 |
matchedNames.toString(),
|
|
670 |
processingResult));
|
|
671 |
}
|
|
672 |
|
|
673 |
if (processingResult) {
|
|
674 |
unmatchedAnnotations.keySet().removeAll(matchedNames);
|
|
675 |
}
|
|
676 |
|
|
677 |
}
|
|
678 |
}
|
|
679 |
unmatchedAnnotations.remove("");
|
|
680 |
|
|
681 |
if (lint && unmatchedAnnotations.size() > 0) {
|
|
682 |
// Remove annotations processed by javac
|
|
683 |
unmatchedAnnotations.keySet().removeAll(platformAnnotations);
|
|
684 |
if (unmatchedAnnotations.size() > 0) {
|
|
685 |
log = Log.instance(context);
|
|
686 |
log.warning("proc.annotations.without.processors",
|
|
687 |
unmatchedAnnotations.keySet());
|
|
688 |
}
|
|
689 |
}
|
|
690 |
|
|
691 |
// Run contributing processors that haven't run yet
|
|
692 |
psi.runContributingProcs(renv);
|
|
693 |
|
|
694 |
// Debugging
|
|
695 |
if (options.get("displayFilerState") != null)
|
|
696 |
filer.displayState();
|
|
697 |
}
|
|
698 |
|
|
699 |
/**
|
|
700 |
* Computes the set of annotations on the symbol in question.
|
|
701 |
* Leave class public for external testing purposes.
|
|
702 |
*/
|
|
703 |
public static class ComputeAnnotationSet extends
|
|
704 |
ElementScanner6<Set<TypeElement>, Set<TypeElement>> {
|
|
705 |
final Elements elements;
|
|
706 |
|
|
707 |
public ComputeAnnotationSet(Elements elements) {
|
|
708 |
super();
|
|
709 |
this.elements = elements;
|
|
710 |
}
|
|
711 |
|
|
712 |
@Override
|
|
713 |
public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
|
|
714 |
// Don't scan enclosed elements of a package
|
|
715 |
return p;
|
|
716 |
}
|
|
717 |
|
|
718 |
@Override
|
|
719 |
public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
|
|
720 |
for (AnnotationMirror annotationMirror :
|
|
721 |
elements.getAllAnnotationMirrors(e) ) {
|
|
722 |
Element e2 = annotationMirror.getAnnotationType().asElement();
|
|
723 |
p.add((TypeElement) e2);
|
|
724 |
}
|
|
725 |
return super.scan(e, p);
|
|
726 |
}
|
|
727 |
}
|
|
728 |
|
|
729 |
private boolean callProcessor(Processor proc,
|
|
730 |
Set<? extends TypeElement> tes,
|
|
731 |
RoundEnvironment renv) {
|
|
732 |
try {
|
|
733 |
return proc.process(tes, renv);
|
|
734 |
} catch (CompletionFailure ex) {
|
|
735 |
StringWriter out = new StringWriter();
|
|
736 |
ex.printStackTrace(new PrintWriter(out));
|
169
|
737 |
log.error("proc.cant.access", ex.sym, ex.getDetailValue(), out.toString());
|
10
|
738 |
return false;
|
|
739 |
} catch (Throwable t) {
|
|
740 |
throw new AnnotationProcessingError(t);
|
|
741 |
}
|
|
742 |
}
|
|
743 |
|
|
744 |
|
|
745 |
// TODO: internal catch clauses?; catch and rethrow an annotation
|
|
746 |
// processing error
|
|
747 |
public JavaCompiler doProcessing(Context context,
|
|
748 |
List<JCCompilationUnit> roots,
|
|
749 |
List<ClassSymbol> classSymbols,
|
|
750 |
Iterable<? extends PackageSymbol> pckSymbols)
|
|
751 |
throws IOException {
|
|
752 |
|
|
753 |
log = Log.instance(context);
|
|
754 |
// Writer for -XprintRounds and -XprintProcessorInfo data
|
|
755 |
PrintWriter xout = context.get(Log.outKey);
|
|
756 |
TaskListener taskListener = context.get(TaskListener.class);
|
|
757 |
|
|
758 |
|
|
759 |
AnnotationCollector collector = new AnnotationCollector();
|
|
760 |
|
|
761 |
JavaCompiler compiler = JavaCompiler.instance(context);
|
|
762 |
compiler.todo.clear(); // free the compiler's resources
|
|
763 |
|
|
764 |
int round = 0;
|
|
765 |
|
|
766 |
// List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots);
|
|
767 |
List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots);
|
|
768 |
|
|
769 |
for (ClassSymbol classSym : classSymbols)
|
|
770 |
topLevelClasses = topLevelClasses.prepend(classSym);
|
|
771 |
List<PackageSymbol> packageInfoFiles =
|
|
772 |
getPackageInfoFiles(roots);
|
|
773 |
|
|
774 |
Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
|
|
775 |
for (PackageSymbol psym : pckSymbols)
|
|
776 |
specifiedPackages.add(psym);
|
|
777 |
this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
|
|
778 |
|
|
779 |
// Use annotation processing to compute the set of annotations present
|
|
780 |
Set<TypeElement> annotationsPresent = new LinkedHashSet<TypeElement>();
|
|
781 |
ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
|
|
782 |
for (ClassSymbol classSym : topLevelClasses)
|
|
783 |
annotationComputer.scan(classSym, annotationsPresent);
|
|
784 |
for (PackageSymbol pkgSym : packageInfoFiles)
|
|
785 |
annotationComputer.scan(pkgSym, annotationsPresent);
|
|
786 |
|
|
787 |
Context currentContext = context;
|
|
788 |
|
|
789 |
int roundNumber = 0;
|
|
790 |
boolean errorStatus = false;
|
|
791 |
|
|
792 |
runAround:
|
|
793 |
while(true) {
|
|
794 |
if (fatalErrors && compiler.errorCount() != 0) {
|
|
795 |
errorStatus = true;
|
|
796 |
break runAround;
|
|
797 |
}
|
|
798 |
|
|
799 |
this.context = currentContext;
|
|
800 |
roundNumber++;
|
|
801 |
printRoundInfo(xout, roundNumber, topLevelClasses, annotationsPresent, false);
|
|
802 |
|
|
803 |
if (taskListener != null)
|
|
804 |
taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
|
|
805 |
|
|
806 |
try {
|
|
807 |
discoverAndRunProcs(currentContext, annotationsPresent, topLevelClasses, packageInfoFiles);
|
|
808 |
} finally {
|
|
809 |
if (taskListener != null)
|
|
810 |
taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
|
|
811 |
}
|
|
812 |
|
|
813 |
/*
|
|
814 |
* Processors for round n have run to completion. Prepare
|
|
815 |
* for round (n+1) by checked for errors raised by
|
|
816 |
* annotation processors and then checking for syntax
|
|
817 |
* errors on any generated source files.
|
|
818 |
*/
|
|
819 |
if (messager.errorRaised()) {
|
|
820 |
errorStatus = true;
|
|
821 |
break runAround;
|
|
822 |
} else {
|
|
823 |
if (moreToDo()) {
|
|
824 |
// annotationsPresentInSource = List.nil();
|
|
825 |
annotationsPresent = new LinkedHashSet<TypeElement>();
|
|
826 |
topLevelClasses = List.nil();
|
|
827 |
packageInfoFiles = List.nil();
|
|
828 |
|
|
829 |
compiler.close();
|
|
830 |
currentContext = contextForNextRound(currentContext, true);
|
|
831 |
|
|
832 |
JavaFileManager fileManager = currentContext.get(JavaFileManager.class);
|
|
833 |
|
|
834 |
List<JavaFileObject> fileObjects = List.nil();
|
|
835 |
for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
|
|
836 |
fileObjects = fileObjects.prepend(jfo);
|
|
837 |
}
|
|
838 |
|
|
839 |
|
|
840 |
compiler = JavaCompiler.instance(currentContext);
|
|
841 |
List<JCCompilationUnit> parsedFiles = compiler.parseFiles(fileObjects);
|
|
842 |
roots = cleanTrees(roots).reverse();
|
|
843 |
|
|
844 |
|
|
845 |
for (JCCompilationUnit unit : parsedFiles)
|
|
846 |
roots = roots.prepend(unit);
|
|
847 |
roots = roots.reverse();
|
|
848 |
|
|
849 |
// Check for errors after parsing
|
|
850 |
if (compiler.parseErrors()) {
|
|
851 |
errorStatus = true;
|
|
852 |
break runAround;
|
|
853 |
} else {
|
|
854 |
ListBuffer<ClassSymbol> classes = enterNewClassFiles(currentContext);
|
|
855 |
compiler.enterTrees(roots);
|
|
856 |
|
|
857 |
// annotationsPresentInSource =
|
|
858 |
// collector.findAnnotations(parsedFiles);
|
|
859 |
classes.appendList(getTopLevelClasses(parsedFiles));
|
|
860 |
topLevelClasses = classes.toList();
|
|
861 |
packageInfoFiles = getPackageInfoFiles(parsedFiles);
|
|
862 |
|
|
863 |
annotationsPresent = new LinkedHashSet<TypeElement>();
|
|
864 |
for (ClassSymbol classSym : topLevelClasses)
|
|
865 |
annotationComputer.scan(classSym, annotationsPresent);
|
|
866 |
for (PackageSymbol pkgSym : packageInfoFiles)
|
|
867 |
annotationComputer.scan(pkgSym, annotationsPresent);
|
|
868 |
|
|
869 |
updateProcessingState(currentContext, false);
|
|
870 |
}
|
|
871 |
} else
|
|
872 |
break runAround; // No new files
|
|
873 |
}
|
|
874 |
}
|
|
875 |
runLastRound(xout, roundNumber, errorStatus, taskListener);
|
|
876 |
|
|
877 |
compiler.close();
|
|
878 |
currentContext = contextForNextRound(currentContext, true);
|
|
879 |
compiler = JavaCompiler.instance(currentContext);
|
|
880 |
filer.newRound(currentContext, true);
|
|
881 |
filer.warnIfUnclosedFiles();
|
|
882 |
warnIfUnmatchedOptions();
|
|
883 |
|
|
884 |
/*
|
|
885 |
* If an annotation processor raises an error in a round,
|
|
886 |
* that round runs to completion and one last round occurs.
|
|
887 |
* The last round may also occur because no more source or
|
|
888 |
* class files have been generated. Therefore, if an error
|
|
889 |
* was raised on either of the last *two* rounds, the compile
|
|
890 |
* should exit with a nonzero exit code. The current value of
|
|
891 |
* errorStatus holds whether or not an error was raised on the
|
|
892 |
* second to last round; errorRaised() gives the error status
|
|
893 |
* of the last round.
|
|
894 |
*/
|
|
895 |
errorStatus = errorStatus || messager.errorRaised();
|
|
896 |
|
|
897 |
|
|
898 |
// Free resources
|
|
899 |
this.close();
|
|
900 |
|
|
901 |
if (taskListener != null)
|
|
902 |
taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
|
|
903 |
|
|
904 |
if (errorStatus) {
|
|
905 |
compiler.log.nerrors += messager.errorCount();
|
|
906 |
if (compiler.errorCount() == 0)
|
|
907 |
compiler.log.nerrors++;
|
|
908 |
} else if (procOnly) {
|
|
909 |
compiler.todo.clear();
|
|
910 |
} else { // Final compilation
|
|
911 |
compiler.close();
|
|
912 |
currentContext = contextForNextRound(currentContext, true);
|
|
913 |
compiler = JavaCompiler.instance(currentContext);
|
|
914 |
|
|
915 |
if (true) {
|
|
916 |
compiler.enterTrees(cleanTrees(roots));
|
|
917 |
} else {
|
|
918 |
List<JavaFileObject> fileObjects = List.nil();
|
|
919 |
for (JCCompilationUnit unit : roots)
|
|
920 |
fileObjects = fileObjects.prepend(unit.getSourceFile());
|
|
921 |
roots = null;
|
|
922 |
compiler.enterTrees(compiler.parseFiles(fileObjects.reverse()));
|
|
923 |
}
|
|
924 |
}
|
|
925 |
|
|
926 |
return compiler;
|
|
927 |
}
|
|
928 |
|
|
929 |
// Call the last round of annotation processing
|
|
930 |
private void runLastRound(PrintWriter xout,
|
|
931 |
int roundNumber,
|
|
932 |
boolean errorStatus,
|
|
933 |
TaskListener taskListener) throws IOException {
|
|
934 |
roundNumber++;
|
|
935 |
List<ClassSymbol> noTopLevelClasses = List.nil();
|
|
936 |
Set<TypeElement> noAnnotations = Collections.emptySet();
|
|
937 |
printRoundInfo(xout, roundNumber, noTopLevelClasses, noAnnotations, true);
|
|
938 |
|
|
939 |
Set<Element> emptyRootElements = Collections.emptySet(); // immutable
|
|
940 |
RoundEnvironment renv = new JavacRoundEnvironment(true,
|
|
941 |
errorStatus,
|
|
942 |
emptyRootElements,
|
|
943 |
JavacProcessingEnvironment.this);
|
|
944 |
if (taskListener != null)
|
|
945 |
taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
|
|
946 |
|
|
947 |
try {
|
|
948 |
discoveredProcs.iterator().runContributingProcs(renv);
|
|
949 |
} finally {
|
|
950 |
if (taskListener != null)
|
|
951 |
taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
|
|
952 |
}
|
|
953 |
}
|
|
954 |
|
|
955 |
private void updateProcessingState(Context currentContext, boolean lastRound) {
|
|
956 |
filer.newRound(currentContext, lastRound);
|
|
957 |
messager.newRound(currentContext);
|
|
958 |
|
|
959 |
elementUtils.setContext(currentContext);
|
|
960 |
typeUtils.setContext(currentContext);
|
|
961 |
}
|
|
962 |
|
|
963 |
private void warnIfUnmatchedOptions() {
|
|
964 |
if (!unmatchedProcessorOptions.isEmpty()) {
|
|
965 |
log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
|
|
966 |
}
|
|
967 |
}
|
|
968 |
|
|
969 |
private void printRoundInfo(PrintWriter xout,
|
|
970 |
int roundNumber,
|
|
971 |
List<ClassSymbol> topLevelClasses,
|
|
972 |
Set<TypeElement> annotationsPresent,
|
|
973 |
boolean lastRound) {
|
|
974 |
if (printRounds || verbose) {
|
|
975 |
xout.println(Log.getLocalizedString("x.print.rounds",
|
|
976 |
roundNumber,
|
|
977 |
"{" + topLevelClasses.toString(", ") + "}",
|
|
978 |
annotationsPresent,
|
|
979 |
lastRound));
|
|
980 |
}
|
|
981 |
}
|
|
982 |
|
|
983 |
private ListBuffer<ClassSymbol> enterNewClassFiles(Context currentContext) {
|
|
984 |
ClassReader reader = ClassReader.instance(currentContext);
|
|
985 |
Name.Table names = Name.Table.instance(currentContext);
|
|
986 |
ListBuffer<ClassSymbol> list = new ListBuffer<ClassSymbol>();
|
|
987 |
|
|
988 |
for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) {
|
|
989 |
Name name = names.fromString(entry.getKey());
|
|
990 |
JavaFileObject file = entry.getValue();
|
|
991 |
if (file.getKind() != JavaFileObject.Kind.CLASS)
|
|
992 |
throw new AssertionError(file);
|
|
993 |
ClassSymbol cs = reader.enterClass(name, file);
|
|
994 |
list.append(cs);
|
|
995 |
}
|
|
996 |
return list;
|
|
997 |
}
|
|
998 |
|
|
999 |
/**
|
|
1000 |
* Free resources related to annotation processing.
|
|
1001 |
*/
|
|
1002 |
public void close() {
|
|
1003 |
filer.close();
|
|
1004 |
discoveredProcs = null;
|
|
1005 |
}
|
|
1006 |
|
|
1007 |
private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
|
|
1008 |
List<ClassSymbol> classes = List.nil();
|
|
1009 |
for (JCCompilationUnit unit : units) {
|
|
1010 |
for (JCTree node : unit.defs) {
|
|
1011 |
if (node.getTag() == JCTree.CLASSDEF) {
|
|
1012 |
classes = classes.prepend(((JCClassDecl) node).sym);
|
|
1013 |
}
|
|
1014 |
}
|
|
1015 |
}
|
|
1016 |
return classes.reverse();
|
|
1017 |
}
|
|
1018 |
|
|
1019 |
private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) {
|
|
1020 |
List<PackageSymbol> packages = List.nil();
|
|
1021 |
for (JCCompilationUnit unit : units) {
|
|
1022 |
boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info",
|
|
1023 |
JavaFileObject.Kind.SOURCE);
|
|
1024 |
if (isPkgInfo) {
|
|
1025 |
packages = packages.prepend(unit.packge);
|
|
1026 |
}
|
|
1027 |
}
|
|
1028 |
return packages.reverse();
|
|
1029 |
}
|
|
1030 |
|
|
1031 |
private Context contextForNextRound(Context context, boolean shareNames)
|
|
1032 |
throws IOException
|
|
1033 |
{
|
|
1034 |
Context next = new Context();
|
|
1035 |
|
|
1036 |
Options options = Options.instance(context);
|
|
1037 |
assert options != null;
|
|
1038 |
next.put(Options.optionsKey, options);
|
|
1039 |
|
|
1040 |
PrintWriter out = context.get(Log.outKey);
|
|
1041 |
assert out != null;
|
|
1042 |
next.put(Log.outKey, out);
|
|
1043 |
|
|
1044 |
if (shareNames) {
|
|
1045 |
Name.Table names = Name.Table.instance(context);
|
|
1046 |
assert names != null;
|
|
1047 |
next.put(Name.Table.namesKey, names);
|
|
1048 |
}
|
|
1049 |
|
|
1050 |
DiagnosticListener dl = context.get(DiagnosticListener.class);
|
|
1051 |
if (dl != null)
|
|
1052 |
next.put(DiagnosticListener.class, dl);
|
|
1053 |
|
|
1054 |
TaskListener tl = context.get(TaskListener.class);
|
|
1055 |
if (tl != null)
|
|
1056 |
next.put(TaskListener.class, tl);
|
|
1057 |
|
|
1058 |
JavaFileManager jfm = context.get(JavaFileManager.class);
|
|
1059 |
assert jfm != null;
|
|
1060 |
next.put(JavaFileManager.class, jfm);
|
|
1061 |
if (jfm instanceof JavacFileManager) {
|
|
1062 |
((JavacFileManager)jfm).setContext(next);
|
|
1063 |
}
|
|
1064 |
|
|
1065 |
Name.Table names = Name.Table.instance(context);
|
|
1066 |
assert names != null;
|
|
1067 |
next.put(Name.Table.namesKey, names);
|
|
1068 |
|
|
1069 |
Keywords keywords = Keywords.instance(context);
|
|
1070 |
assert(keywords != null);
|
|
1071 |
next.put(Keywords.keywordsKey, keywords);
|
|
1072 |
|
|
1073 |
JavaCompiler oldCompiler = JavaCompiler.instance(context);
|
|
1074 |
JavaCompiler nextCompiler = JavaCompiler.instance(next);
|
|
1075 |
nextCompiler.initRound(oldCompiler);
|
|
1076 |
|
|
1077 |
JavacTaskImpl task = context.get(JavacTaskImpl.class);
|
|
1078 |
if (task != null) {
|
|
1079 |
next.put(JavacTaskImpl.class, task);
|
|
1080 |
task.updateContext(next);
|
|
1081 |
}
|
|
1082 |
|
|
1083 |
context.clear();
|
|
1084 |
return next;
|
|
1085 |
}
|
|
1086 |
|
|
1087 |
/*
|
|
1088 |
* Called retroactively to determine if a class loader was required,
|
|
1089 |
* after we have failed to create one.
|
|
1090 |
*/
|
|
1091 |
private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) {
|
|
1092 |
if (procNames != null)
|
|
1093 |
return true;
|
|
1094 |
|
|
1095 |
String procPath;
|
|
1096 |
URL[] urls = new URL[1];
|
|
1097 |
for(File pathElement : workingpath) {
|
|
1098 |
try {
|
|
1099 |
urls[0] = pathElement.toURI().toURL();
|
|
1100 |
if (ServiceProxy.hasService(Processor.class, urls))
|
|
1101 |
return true;
|
|
1102 |
} catch (MalformedURLException ex) {
|
|
1103 |
throw new AssertionError(ex);
|
|
1104 |
}
|
|
1105 |
catch (ServiceProxy.ServiceConfigurationError e) {
|
|
1106 |
log.error("proc.bad.config.file", e.getLocalizedMessage());
|
|
1107 |
return true;
|
|
1108 |
}
|
|
1109 |
}
|
|
1110 |
|
|
1111 |
return false;
|
|
1112 |
}
|
|
1113 |
|
|
1114 |
private class AnnotationCollector extends TreeScanner {
|
|
1115 |
List<JCTree> path = List.nil();
|
|
1116 |
static final boolean verbose = false;
|
|
1117 |
List<JCAnnotation> annotations = List.nil();
|
|
1118 |
|
|
1119 |
public List<JCAnnotation> findAnnotations(List<? extends JCTree> nodes) {
|
|
1120 |
annotations = List.nil();
|
|
1121 |
scan(nodes);
|
|
1122 |
List<JCAnnotation> found = annotations;
|
|
1123 |
annotations = List.nil();
|
|
1124 |
return found.reverse();
|
|
1125 |
}
|
|
1126 |
|
|
1127 |
public void scan(JCTree node) {
|
|
1128 |
if (node == null)
|
|
1129 |
return;
|
|
1130 |
Symbol sym = TreeInfo.symbolFor(node);
|
|
1131 |
if (sym != null)
|
|
1132 |
path = path.prepend(node);
|
|
1133 |
super.scan(node);
|
|
1134 |
if (sym != null)
|
|
1135 |
path = path.tail;
|
|
1136 |
}
|
|
1137 |
|
|
1138 |
public void visitAnnotation(JCAnnotation node) {
|
|
1139 |
annotations = annotations.prepend(node);
|
|
1140 |
if (verbose) {
|
|
1141 |
StringBuilder sb = new StringBuilder();
|
|
1142 |
for (JCTree tree : path.reverse()) {
|
|
1143 |
System.err.print(sb);
|
|
1144 |
System.err.println(TreeInfo.symbolFor(tree));
|
|
1145 |
sb.append(" ");
|
|
1146 |
}
|
|
1147 |
System.err.print(sb);
|
|
1148 |
System.err.println(node);
|
|
1149 |
}
|
|
1150 |
}
|
|
1151 |
}
|
|
1152 |
|
|
1153 |
private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
|
|
1154 |
for (T node : nodes)
|
|
1155 |
treeCleaner.scan(node);
|
|
1156 |
return nodes;
|
|
1157 |
}
|
|
1158 |
|
|
1159 |
private static TreeScanner treeCleaner = new TreeScanner() {
|
|
1160 |
public void scan(JCTree node) {
|
|
1161 |
super.scan(node);
|
|
1162 |
if (node != null)
|
|
1163 |
node.type = null;
|
|
1164 |
}
|
|
1165 |
public void visitTopLevel(JCCompilationUnit node) {
|
|
1166 |
node.packge = null;
|
|
1167 |
super.visitTopLevel(node);
|
|
1168 |
}
|
|
1169 |
public void visitClassDef(JCClassDecl node) {
|
|
1170 |
node.sym = null;
|
|
1171 |
super.visitClassDef(node);
|
|
1172 |
}
|
|
1173 |
public void visitMethodDef(JCMethodDecl node) {
|
|
1174 |
node.sym = null;
|
|
1175 |
super.visitMethodDef(node);
|
|
1176 |
}
|
|
1177 |
public void visitVarDef(JCVariableDecl node) {
|
|
1178 |
node.sym = null;
|
|
1179 |
super.visitVarDef(node);
|
|
1180 |
}
|
|
1181 |
public void visitNewClass(JCNewClass node) {
|
|
1182 |
node.constructor = null;
|
|
1183 |
super.visitNewClass(node);
|
|
1184 |
}
|
|
1185 |
public void visitAssignop(JCAssignOp node) {
|
|
1186 |
node.operator = null;
|
|
1187 |
super.visitAssignop(node);
|
|
1188 |
}
|
|
1189 |
public void visitUnary(JCUnary node) {
|
|
1190 |
node.operator = null;
|
|
1191 |
super.visitUnary(node);
|
|
1192 |
}
|
|
1193 |
public void visitBinary(JCBinary node) {
|
|
1194 |
node.operator = null;
|
|
1195 |
super.visitBinary(node);
|
|
1196 |
}
|
|
1197 |
public void visitSelect(JCFieldAccess node) {
|
|
1198 |
node.sym = null;
|
|
1199 |
super.visitSelect(node);
|
|
1200 |
}
|
|
1201 |
public void visitIdent(JCIdent node) {
|
|
1202 |
node.sym = null;
|
|
1203 |
super.visitIdent(node);
|
|
1204 |
}
|
|
1205 |
};
|
|
1206 |
|
|
1207 |
|
|
1208 |
private boolean moreToDo() {
|
|
1209 |
return filer.newFiles();
|
|
1210 |
}
|
|
1211 |
|
|
1212 |
/**
|
|
1213 |
* {@inheritdoc}
|
|
1214 |
*
|
|
1215 |
* Command line options suitable for presenting to annotation
|
|
1216 |
* processors. "-Afoo=bar" should be "-Afoo" => "bar".
|
|
1217 |
*/
|
|
1218 |
public Map<String,String> getOptions() {
|
|
1219 |
return processorOptions;
|
|
1220 |
}
|
|
1221 |
|
|
1222 |
public Messager getMessager() {
|
|
1223 |
return messager;
|
|
1224 |
}
|
|
1225 |
|
|
1226 |
public Filer getFiler() {
|
|
1227 |
return filer;
|
|
1228 |
}
|
|
1229 |
|
|
1230 |
public JavacElements getElementUtils() {
|
|
1231 |
return elementUtils;
|
|
1232 |
}
|
|
1233 |
|
|
1234 |
public JavacTypes getTypeUtils() {
|
|
1235 |
return typeUtils;
|
|
1236 |
}
|
|
1237 |
|
|
1238 |
public SourceVersion getSourceVersion() {
|
|
1239 |
return Source.toSourceVersion(source);
|
|
1240 |
}
|
|
1241 |
|
|
1242 |
public Locale getLocale() {
|
|
1243 |
return Locale.getDefault();
|
|
1244 |
}
|
|
1245 |
|
|
1246 |
public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
|
|
1247 |
return specifiedPackages;
|
|
1248 |
}
|
|
1249 |
|
|
1250 |
// Borrowed from DocletInvoker and apt
|
|
1251 |
// TODO: remove from apt's Main
|
|
1252 |
/**
|
|
1253 |
* Utility method for converting a search path string to an array
|
|
1254 |
* of directory and JAR file URLs.
|
|
1255 |
*
|
|
1256 |
* @param path the search path string
|
|
1257 |
* @return the resulting array of directory and JAR file URLs
|
|
1258 |
*/
|
|
1259 |
public static URL[] pathToURLs(String path) {
|
|
1260 |
StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
|
|
1261 |
URL[] urls = new URL[st.countTokens()];
|
|
1262 |
int count = 0;
|
|
1263 |
while (st.hasMoreTokens()) {
|
|
1264 |
URL url = fileToURL(new File(st.nextToken()));
|
|
1265 |
if (url != null) {
|
|
1266 |
urls[count++] = url;
|
|
1267 |
}
|
|
1268 |
}
|
|
1269 |
if (urls.length != count) {
|
|
1270 |
URL[] tmp = new URL[count];
|
|
1271 |
System.arraycopy(urls, 0, tmp, 0, count);
|
|
1272 |
urls = tmp;
|
|
1273 |
}
|
|
1274 |
return urls;
|
|
1275 |
}
|
|
1276 |
|
|
1277 |
/**
|
|
1278 |
* Returns the directory or JAR file URL corresponding to the specified
|
|
1279 |
* local file name.
|
|
1280 |
*
|
|
1281 |
* @param file the File object
|
|
1282 |
* @return the resulting directory or JAR file URL, or null if unknown
|
|
1283 |
*/
|
|
1284 |
private static URL fileToURL(File file) {
|
|
1285 |
String name;
|
|
1286 |
try {
|
|
1287 |
name = file.getCanonicalPath();
|
|
1288 |
} catch (IOException e) {
|
|
1289 |
name = file.getAbsolutePath();
|
|
1290 |
}
|
|
1291 |
name = name.replace(File.separatorChar, '/');
|
|
1292 |
if (!name.startsWith("/")) {
|
|
1293 |
name = "/" + name;
|
|
1294 |
}
|
|
1295 |
// If the file does not exist, then assume that it's a directory
|
|
1296 |
if (!file.isFile()) {
|
|
1297 |
name = name + "/";
|
|
1298 |
}
|
|
1299 |
try {
|
|
1300 |
return new URL("file", "", name);
|
|
1301 |
} catch (MalformedURLException e) {
|
|
1302 |
throw new IllegalArgumentException("file");
|
|
1303 |
}
|
|
1304 |
}
|
|
1305 |
|
|
1306 |
|
|
1307 |
|
|
1308 |
private static final Pattern allMatches = Pattern.compile(".*");
|
|
1309 |
|
|
1310 |
private static final Pattern noMatches = Pattern.compile("(\\P{all})+");
|
|
1311 |
/**
|
|
1312 |
* Convert import-style string to regex matching that string. If
|
|
1313 |
* the string is a valid import-style string, return a regex that
|
|
1314 |
* won't match anything.
|
|
1315 |
*/
|
|
1316 |
// TODO: remove version in Apt.java
|
|
1317 |
public static Pattern importStringToPattern(String s, Processor p, Log log) {
|
|
1318 |
if (s.equals("*")) {
|
|
1319 |
return allMatches;
|
|
1320 |
} else {
|
|
1321 |
String t = s;
|
|
1322 |
boolean star = false;
|
|
1323 |
|
|
1324 |
/*
|
|
1325 |
* Validate string from factory is legal. If the string
|
|
1326 |
* has more than one asterisks or the asterisks does not
|
|
1327 |
* appear as the last character (preceded by a period),
|
|
1328 |
* the string is not legal.
|
|
1329 |
*/
|
|
1330 |
|
|
1331 |
boolean valid = true;
|
|
1332 |
int index = t.indexOf('*');
|
|
1333 |
if (index != -1) {
|
|
1334 |
// '*' must be last character...
|
|
1335 |
if (index == t.length() -1) {
|
|
1336 |
// ... and preceeding character must be '.'
|
|
1337 |
if ( index-1 >= 0 ) {
|
|
1338 |
valid = t.charAt(index-1) == '.';
|
|
1339 |
// Strip off ".*$" for identifier checks
|
|
1340 |
t = t.substring(0, t.length()-2);
|
|
1341 |
}
|
|
1342 |
} else
|
|
1343 |
valid = false;
|
|
1344 |
}
|
|
1345 |
|
|
1346 |
// Verify string is off the form (javaId \.)+ or javaId
|
|
1347 |
if (valid) {
|
|
1348 |
String[] javaIds = t.split("\\.", t.length()+2);
|
|
1349 |
for(String javaId: javaIds)
|
|
1350 |
valid &= SourceVersion.isIdentifier(javaId);
|
|
1351 |
}
|
|
1352 |
|
|
1353 |
if (!valid) {
|
|
1354 |
log.warning("proc.malformed.supported.string", s, p.getClass().getName());
|
|
1355 |
return noMatches; // won't match any valid identifier
|
|
1356 |
}
|
|
1357 |
|
|
1358 |
String s_prime = s.replaceAll("\\.", "\\\\.");
|
|
1359 |
|
|
1360 |
if (s_prime.endsWith("*")) {
|
|
1361 |
s_prime = s_prime.substring(0, s_prime.length() - 1) + ".+";
|
|
1362 |
}
|
|
1363 |
|
|
1364 |
return Pattern.compile(s_prime);
|
|
1365 |
}
|
|
1366 |
}
|
|
1367 |
|
|
1368 |
/**
|
|
1369 |
* For internal use by Sun Microsystems only. This method will be
|
|
1370 |
* removed without warning.
|
|
1371 |
*/
|
|
1372 |
public Context getContext() {
|
|
1373 |
return context;
|
|
1374 |
}
|
|
1375 |
|
|
1376 |
public String toString() {
|
|
1377 |
return "javac ProcessingEnvironment";
|
|
1378 |
}
|
|
1379 |
|
|
1380 |
public static boolean isValidOptionName(String optionName) {
|
|
1381 |
for(String s : optionName.split("\\.", -1)) {
|
|
1382 |
if (!SourceVersion.isIdentifier(s))
|
|
1383 |
return false;
|
|
1384 |
}
|
|
1385 |
return true;
|
|
1386 |
}
|
|
1387 |
}
|