112 /** |
115 /** |
113 * Wrap a JavaFileObject to manage writing by the Filer. |
116 * Wrap a JavaFileObject to manage writing by the Filer. |
114 */ |
117 */ |
115 private class FilerOutputFileObject extends ForwardingFileObject<FileObject> { |
118 private class FilerOutputFileObject extends ForwardingFileObject<FileObject> { |
116 private boolean opened = false; |
119 private boolean opened = false; |
|
120 private ModuleSymbol mod; |
117 private String name; |
121 private String name; |
118 |
122 |
119 FilerOutputFileObject(String name, FileObject fileObject) { |
123 FilerOutputFileObject(ModuleSymbol mod, String name, FileObject fileObject) { |
120 super(fileObject); |
124 super(fileObject); |
|
125 this.mod = mod; |
121 this.name = name; |
126 this.name = name; |
122 } |
127 } |
123 |
128 |
124 @Override @DefinedBy(Api.COMPILER) |
129 @Override @DefinedBy(Api.COMPILER) |
125 public synchronized OutputStream openOutputStream() throws IOException { |
130 public synchronized OutputStream openOutputStream() throws IOException { |
126 if (opened) |
131 if (opened) |
127 throw new IOException(ALREADY_OPENED); |
132 throw new IOException(ALREADY_OPENED); |
128 opened = true; |
133 opened = true; |
129 return new FilerOutputStream(name, fileObject); |
134 return new FilerOutputStream(mod, name, fileObject); |
130 } |
135 } |
131 |
136 |
132 @Override @DefinedBy(Api.COMPILER) |
137 @Override @DefinedBy(Api.COMPILER) |
133 public synchronized Writer openWriter() throws IOException { |
138 public synchronized Writer openWriter() throws IOException { |
134 if (opened) |
139 if (opened) |
135 throw new IOException(ALREADY_OPENED); |
140 throw new IOException(ALREADY_OPENED); |
136 opened = true; |
141 opened = true; |
137 return new FilerWriter(name, fileObject); |
142 return new FilerWriter(mod, name, fileObject); |
138 } |
143 } |
139 |
144 |
140 // Three anti-literacy methods |
145 // Three anti-literacy methods |
141 @Override @DefinedBy(Api.COMPILER) |
146 @Override @DefinedBy(Api.COMPILER) |
142 public InputStream openInputStream() throws IOException { |
147 public InputStream openInputStream() throws IOException { |
246 * Wrap a {@code OutputStream} returned from the {@code |
251 * Wrap a {@code OutputStream} returned from the {@code |
247 * JavaFileManager} to properly register source or class files |
252 * JavaFileManager} to properly register source or class files |
248 * when they are closed. |
253 * when they are closed. |
249 */ |
254 */ |
250 private class FilerOutputStream extends FilterOutputStream { |
255 private class FilerOutputStream extends FilterOutputStream { |
|
256 ModuleSymbol mod; |
251 String typeName; |
257 String typeName; |
252 FileObject fileObject; |
258 FileObject fileObject; |
253 boolean closed = false; |
259 boolean closed = false; |
254 |
260 |
255 /** |
261 /** |
256 * @param typeName name of class or {@code null} if just a |
262 * @param typeName name of class or {@code null} if just a |
257 * binary file |
263 * binary file |
258 */ |
264 */ |
259 FilerOutputStream(String typeName, FileObject fileObject) throws IOException { |
265 FilerOutputStream(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException { |
260 super(fileObject.openOutputStream()); |
266 super(fileObject.openOutputStream()); |
|
267 this.mod = mod; |
261 this.typeName = typeName; |
268 this.typeName = typeName; |
262 this.fileObject = fileObject; |
269 this.fileObject = fileObject; |
263 } |
270 } |
264 |
271 |
265 public synchronized void close() throws IOException { |
272 public synchronized void close() throws IOException { |
280 * Wrap a {@code Writer} returned from the {@code JavaFileManager} |
287 * Wrap a {@code Writer} returned from the {@code JavaFileManager} |
281 * to properly register source or class files when they are |
288 * to properly register source or class files when they are |
282 * closed. |
289 * closed. |
283 */ |
290 */ |
284 private class FilerWriter extends FilterWriter { |
291 private class FilerWriter extends FilterWriter { |
|
292 ModuleSymbol mod; |
285 String typeName; |
293 String typeName; |
286 FileObject fileObject; |
294 FileObject fileObject; |
287 boolean closed = false; |
295 boolean closed = false; |
288 |
296 |
289 /** |
297 /** |
290 * @param fileObject the fileObject to be written to |
298 * @param fileObject the fileObject to be written to |
291 * @param typeName name of source file or {@code null} if just a |
299 * @param typeName name of source file or {@code null} if just a |
292 * text file |
300 * text file |
293 */ |
301 */ |
294 FilerWriter(String typeName, FileObject fileObject) throws IOException { |
302 FilerWriter(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException { |
295 super(fileObject.openWriter()); |
303 super(fileObject.openWriter()); |
|
304 this.mod = mod; |
296 this.typeName = typeName; |
305 this.typeName = typeName; |
297 this.fileObject = fileObject; |
306 this.fileObject = fileObject; |
298 } |
307 } |
299 |
308 |
300 public synchronized void close() throws IOException { |
309 public synchronized void close() throws IOException { |
351 |
363 |
352 /** |
364 /** |
353 * Names of all created source files. Its iterators should |
365 * Names of all created source files. Its iterators should |
354 * preserve insertion order. |
366 * preserve insertion order. |
355 */ |
367 */ |
356 private final Set<String> aggregateGeneratedSourceNames; |
368 private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedSourceNames; |
357 |
369 |
358 /** |
370 /** |
359 * Names of all created class files. Its iterators should |
371 * Names of all created class files. Its iterators should |
360 * preserve insertion order. |
372 * preserve insertion order. |
361 */ |
373 */ |
362 private final Set<String> aggregateGeneratedClassNames; |
374 private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedClassNames; |
363 |
375 |
364 |
376 |
365 JavacFiler(Context context) { |
377 JavacFiler(Context context) { |
366 this.context = context; |
378 this.context = context; |
367 fileManager = context.get(JavaFileManager.class); |
379 fileManager = context.get(JavaFileManager.class); |
368 |
380 |
369 log = Log.instance(context); |
381 log = Log.instance(context); |
|
382 modules = Modules.instance(context); |
|
383 names = Names.instance(context); |
|
384 syms = Symtab.instance(context); |
370 |
385 |
371 fileObjectHistory = synchronizedSet(new LinkedHashSet<FileObject>()); |
386 fileObjectHistory = synchronizedSet(new LinkedHashSet<FileObject>()); |
372 generatedSourceNames = synchronizedSet(new LinkedHashSet<String>()); |
387 generatedSourceNames = synchronizedSet(new LinkedHashSet<String>()); |
373 generatedSourceFileObjects = synchronizedSet(new LinkedHashSet<JavaFileObject>()); |
388 generatedSourceFileObjects = synchronizedSet(new LinkedHashSet<JavaFileObject>()); |
374 |
389 |
375 generatedClasses = synchronizedMap(new LinkedHashMap<String, JavaFileObject>()); |
390 generatedClasses = synchronizedMap(new LinkedHashMap<>()); |
376 |
391 |
377 openTypeNames = synchronizedSet(new LinkedHashSet<String>()); |
392 openTypeNames = synchronizedSet(new LinkedHashSet<String>()); |
378 |
393 |
379 aggregateGeneratedSourceNames = new LinkedHashSet<>(); |
394 aggregateGeneratedSourceNames = new LinkedHashSet<>(); |
380 aggregateGeneratedClassNames = new LinkedHashSet<>(); |
395 aggregateGeneratedClassNames = new LinkedHashSet<>(); |
381 |
396 |
382 lint = (Lint.instance(context)).isEnabled(PROCESSING); |
397 lint = (Lint.instance(context)).isEnabled(PROCESSING); |
383 } |
398 } |
384 |
399 |
385 @DefinedBy(Api.ANNOTATION_PROCESSING) |
400 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) |
386 public JavaFileObject createSourceFile(CharSequence name, |
401 public JavaFileObject createSourceFile(CharSequence nameAndModule, |
387 Element... originatingElements) throws IOException { |
402 Element... originatingElements) throws IOException { |
388 return createSourceOrClassFile(true, name.toString()); |
403 Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule); |
389 } |
404 return createSourceOrClassFile(moduleAndClass.fst, true, moduleAndClass.snd); |
390 |
405 } |
391 @DefinedBy(Api.ANNOTATION_PROCESSING) |
406 |
392 public JavaFileObject createClassFile(CharSequence name, |
407 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) |
393 Element... originatingElements) throws IOException { |
408 public JavaFileObject createClassFile(CharSequence nameAndModule, |
394 return createSourceOrClassFile(false, name.toString()); |
409 Element... originatingElements) throws IOException { |
395 } |
410 Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule); |
396 |
411 return createSourceOrClassFile(moduleAndClass.fst, false, moduleAndClass.snd); |
397 private JavaFileObject createSourceOrClassFile(boolean isSourceFile, String name) throws IOException { |
412 } |
|
413 |
|
414 private Pair<ModuleSymbol, String> checkOrInferModule(CharSequence moduleAndPkg) throws FilerException { |
|
415 String moduleAndPkgString = moduleAndPkg.toString(); |
|
416 int slash = moduleAndPkgString.indexOf('/'); |
|
417 |
|
418 if (slash != (-1)) { |
|
419 //module name specified: |
|
420 String module = moduleAndPkgString.substring(0, slash); |
|
421 |
|
422 ModuleSymbol explicitModule = syms.getModule(names.fromString(module)); |
|
423 |
|
424 if (explicitModule == null) { |
|
425 throw new FilerException("Module: " + module + " does not exist."); |
|
426 } |
|
427 |
|
428 if (!modules.isRootModule(explicitModule)) { |
|
429 throw new FilerException("Cannot write to the given module!"); |
|
430 } |
|
431 |
|
432 return Pair.of(explicitModule, moduleAndPkgString.substring(slash + 1)); |
|
433 } else { |
|
434 if (modules.multiModuleMode) { |
|
435 throw new FilerException("No module to write to specified!"); |
|
436 } |
|
437 |
|
438 return Pair.of(modules.getDefaultModule(), moduleAndPkgString); |
|
439 } |
|
440 } |
|
441 |
|
442 private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name) throws IOException { |
|
443 Assert.checkNonNull(mod); |
|
444 |
398 if (lint) { |
445 if (lint) { |
399 int periodIndex = name.lastIndexOf("."); |
446 int periodIndex = name.lastIndexOf("."); |
400 if (periodIndex != -1) { |
447 if (periodIndex != -1) { |
401 String base = name.substring(periodIndex); |
448 String base = name.substring(periodIndex); |
402 String extn = (isSourceFile ? ".java" : ".class"); |
449 String extn = (isSourceFile ? ".java" : ".class"); |
403 if (base.equals(extn)) |
450 if (base.equals(extn)) |
404 log.warning("proc.suspicious.class.name", name, extn); |
451 log.warning("proc.suspicious.class.name", name, extn); |
405 } |
452 } |
406 } |
453 } |
407 checkNameAndExistence(name, isSourceFile); |
454 checkNameAndExistence(mod, name, isSourceFile); |
408 Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT); |
455 Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT); |
|
456 |
|
457 if (modules.multiModuleMode) { |
|
458 loc = this.fileManager.getLocationForModule(loc, mod.name.toString()); |
|
459 } |
409 JavaFileObject.Kind kind = (isSourceFile ? |
460 JavaFileObject.Kind kind = (isSourceFile ? |
410 JavaFileObject.Kind.SOURCE : |
461 JavaFileObject.Kind.SOURCE : |
411 JavaFileObject.Kind.CLASS); |
462 JavaFileObject.Kind.CLASS); |
412 |
463 |
413 JavaFileObject fileObject = |
464 JavaFileObject fileObject = |
416 |
467 |
417 if (lastRound) |
468 if (lastRound) |
418 log.warning("proc.file.create.last.round", name); |
469 log.warning("proc.file.create.last.round", name); |
419 |
470 |
420 if (isSourceFile) |
471 if (isSourceFile) |
421 aggregateGeneratedSourceNames.add(name); |
472 aggregateGeneratedSourceNames.add(Pair.of(mod, name)); |
422 else |
473 else |
423 aggregateGeneratedClassNames.add(name); |
474 aggregateGeneratedClassNames.add(Pair.of(mod, name)); |
424 openTypeNames.add(name); |
475 openTypeNames.add(name); |
425 |
476 |
426 return new FilerOutputJavaFileObject(name, fileObject); |
477 return new FilerOutputJavaFileObject(mod, name, fileObject); |
427 } |
478 } |
428 |
479 |
429 @DefinedBy(Api.ANNOTATION_PROCESSING) |
480 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) |
430 public FileObject createResource(JavaFileManager.Location location, |
481 public FileObject createResource(JavaFileManager.Location location, |
431 CharSequence pkg, |
482 CharSequence moduleAndPkg, |
432 CharSequence relativeName, |
483 CharSequence relativeName, |
433 Element... originatingElements) throws IOException { |
484 Element... originatingElements) throws IOException { |
|
485 Pair<ModuleSymbol, String> moduleAndPackage = checkOrInferModule(moduleAndPkg); |
|
486 ModuleSymbol msym = moduleAndPackage.fst; |
|
487 String pkg = moduleAndPackage.snd; |
|
488 |
434 locationCheck(location); |
489 locationCheck(location); |
|
490 |
|
491 if (modules.multiModuleMode) { |
|
492 Assert.checkNonNull(msym); |
|
493 location = this.fileManager.getLocationForModule(location, msym.name.toString()); |
|
494 } |
435 |
495 |
436 String strPkg = pkg.toString(); |
496 String strPkg = pkg.toString(); |
437 if (strPkg.length() > 0) |
497 if (strPkg.length() > 0) |
438 checkName(strPkg); |
498 checkName(strPkg); |
439 |
499 |
441 fileManager.getFileForOutput(location, strPkg, |
501 fileManager.getFileForOutput(location, strPkg, |
442 relativeName.toString(), null); |
502 relativeName.toString(), null); |
443 checkFileReopening(fileObject, true); |
503 checkFileReopening(fileObject, true); |
444 |
504 |
445 if (fileObject instanceof JavaFileObject) |
505 if (fileObject instanceof JavaFileObject) |
446 return new FilerOutputJavaFileObject(null, (JavaFileObject)fileObject); |
506 return new FilerOutputJavaFileObject(msym, null, (JavaFileObject)fileObject); |
447 else |
507 else |
448 return new FilerOutputFileObject(null, fileObject); |
508 return new FilerOutputFileObject(msym, null, fileObject); |
449 } |
509 } |
450 |
510 |
451 private void locationCheck(JavaFileManager.Location location) { |
511 private void locationCheck(JavaFileManager.Location location) { |
452 if (location instanceof StandardLocation) { |
512 if (location instanceof StandardLocation) { |
453 StandardLocation stdLoc = (StandardLocation) location; |
513 StandardLocation stdLoc = (StandardLocation) location; |
455 throw new IllegalArgumentException("Resource creation not supported in location " + |
515 throw new IllegalArgumentException("Resource creation not supported in location " + |
456 stdLoc); |
516 stdLoc); |
457 } |
517 } |
458 } |
518 } |
459 |
519 |
460 @DefinedBy(Api.ANNOTATION_PROCESSING) |
520 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) |
461 public FileObject getResource(JavaFileManager.Location location, |
521 public FileObject getResource(JavaFileManager.Location location, |
462 CharSequence pkg, |
522 CharSequence moduleAndPkg, |
463 CharSequence relativeName) throws IOException { |
523 CharSequence relativeName) throws IOException { |
464 String strPkg = pkg.toString(); |
524 Pair<ModuleSymbol, String> moduleAndPackage = checkOrInferModule(moduleAndPkg); |
465 if (strPkg.length() > 0) |
525 ModuleSymbol msym = moduleAndPackage.fst; |
466 checkName(strPkg); |
526 String pkg = moduleAndPackage.snd; |
|
527 |
|
528 if (modules.multiModuleMode) { |
|
529 Assert.checkNonNull(msym); |
|
530 location = this.fileManager.getLocationForModule(location, msym.name.toString()); |
|
531 } |
|
532 |
|
533 if (pkg.length() > 0) |
|
534 checkName(pkg); |
467 |
535 |
468 // TODO: Only support reading resources in selected output |
536 // TODO: Only support reading resources in selected output |
469 // locations? Only allow reading of non-source, non-class |
537 // locations? Only allow reading of non-source, non-class |
470 // files from the supported input locations? |
538 // files from the supported input locations? |
471 |
539 |
476 // with more than one component. So, for now, we use a hybrid |
544 // with more than one component. So, for now, we use a hybrid |
477 // invocation. |
545 // invocation. |
478 FileObject fileObject; |
546 FileObject fileObject; |
479 if (location.isOutputLocation()) { |
547 if (location.isOutputLocation()) { |
480 fileObject = fileManager.getFileForOutput(location, |
548 fileObject = fileManager.getFileForOutput(location, |
481 pkg.toString(), |
549 pkg, |
482 relativeName.toString(), |
550 relativeName.toString(), |
483 null); |
551 null); |
484 } else { |
552 } else { |
485 fileObject = fileManager.getFileForInput(location, |
553 fileObject = fileManager.getFileForInput(location, |
486 pkg.toString(), |
554 pkg, |
487 relativeName.toString()); |
555 relativeName.toString()); |
488 } |
556 } |
489 if (fileObject == null) { |
557 if (fileObject == null) { |
490 String name = (pkg.length() == 0) |
558 String name = (pkg.length() == 0) |
491 ? relativeName.toString() : (pkg + "/" + relativeName); |
559 ? relativeName.toString() : (pkg + "/" + relativeName); |
522 String simple = name.substring(periodIndex+1); |
590 String simple = name.substring(periodIndex+1); |
523 return SourceVersion.isName(prefix) && simple.equals(PKG_INFO); |
591 return SourceVersion.isName(prefix) && simple.equals(PKG_INFO); |
524 } |
592 } |
525 } |
593 } |
526 |
594 |
527 private void checkNameAndExistence(String typename, boolean allowUnnamedPackageInfo) throws FilerException { |
595 private void checkNameAndExistence(ModuleSymbol mod, String typename, boolean allowUnnamedPackageInfo) throws FilerException { |
528 // TODO: Check if type already exists on source or class path? |
596 // TODO: Check if type already exists on source or class path? |
529 // If so, use warning message key proc.type.already.exists |
597 // If so, use warning message key proc.type.already.exists |
530 checkName(typename, allowUnnamedPackageInfo); |
598 checkName(typename, allowUnnamedPackageInfo); |
531 if (aggregateGeneratedSourceNames.contains(typename) || |
599 if (aggregateGeneratedSourceNames.contains(Pair.of(mod, typename)) || |
532 aggregateGeneratedClassNames.contains(typename)) { |
600 aggregateGeneratedClassNames.contains(Pair.of(mod, typename))) { |
533 if (lint) |
601 if (lint) |
534 log.warning("proc.type.recreate", typename); |
602 log.warning("proc.type.recreate", typename); |
535 throw new FilerException("Attempt to recreate a file for type " + typename); |
603 throw new FilerException("Attempt to recreate a file for type " + typename); |
|
604 } |
|
605 if (!mod.isUnnamed() && !typename.contains(".")) { |
|
606 throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename); |
536 } |
607 } |
537 } |
608 } |
538 |
609 |
539 /** |
610 /** |
540 * Check to see if the file has already been opened; if so, throw |
611 * Check to see if the file has already been opened; if so, throw |