This commit is contained in:
Lana Steuck 2016-12-14 20:34:19 +00:00
commit 93f097a106
41 changed files with 1626 additions and 3917 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -66,7 +66,8 @@ import java.io.IOException;
* allow the <em>originating elements</em> to be provided as hints to
* the tool infrastructure to better manage dependencies. The
* originating elements are the types or packages (representing {@code
* package-info} files) which caused an annotation processor to
* package-info} files) or modules (representing {@code
* module-info} files) which caused an annotation processor to
* attempt to create a new file. For example, if an annotation
* processor tries to create a source file, {@code
* GeneratedFromUserSource}, in response to processing
@ -111,10 +112,10 @@ import java.io.IOException;
* to overwrite existing files that were not generated.
*
* <p> Processors can indicate a source or class file is generated by
* including an {@link javax.annotation.Generated @Generated}
* annotation.
* including a {@code javax.annotation.Generated} annotation if the
* environment is configured so that that type is accessible.
*
* <p> Note that some of the effect of overwriting a file can be
* @apiNote Some of the effect of overwriting a file can be
* achieved by using a <i>decorator</i>-style pattern. Instead of
* modifying a class directly, the class is designed so that either
* its superclass is generated by annotation processing or subclasses
@ -131,18 +132,35 @@ import java.io.IOException;
public interface Filer {
/**
* Creates a new source file and returns an object to allow
* writing to it. The file's name and path (relative to the
* {@linkplain StandardLocation#SOURCE_OUTPUT root output location
* for source files}) are based on the type to be declared in that
* file. If more than one type is being declared, the name of the
* principal top-level type (the public one, for example) should
* be used. A source file can also be created to hold information
* about a package, including package annotations. To create a
* source file for a named package, have {@code name} be the
* writing to it. A source file for a type, or a package can
* be created.
*
* The file's name and path (relative to the {@linkplain
* StandardLocation#SOURCE_OUTPUT root output location for source
* files}) are based on the name of the item to be declared in
* that file as well as the specified module for the item (if
* any).
*
* If more than one type is being declared in a single file (that
* is, a single compilation unit), the name of the file should
* correspond to the name of the principal top-level type (the
* public one, for example).
*
* <p>A source file can also be created to hold information about
* a package, including package annotations. To create a source
* file for a named package, have the {@code name} argument be the
* package's name followed by {@code ".package-info"}; to create a
* source file for an unnamed package, use {@code "package-info"}.
*
* <p> Note that to use a particular {@linkplain
* <p>The optional module name is prefixed to the type name or
* package name and separated using a "{@code /}" character. For
* example, to create a source file for type {@code a.B} in module
* {@code foo}, use a {@code name} argument of {@code "foo/a.B"}.
*
* <p>Creating a source file in or for an unnamed package in a named
* module is <em>not</em> supported.
*
* @apiNote To use a particular {@linkplain
* java.nio.charset.Charset charset} to encode the contents of the
* file, an {@code OutputStreamWriter} with the chosen charset can
* be created from the {@code OutputStream} from the returned
@ -161,37 +179,51 @@ public interface Filer {
* @param name canonical (fully qualified) name of the principal type
* being declared in this file or a package name followed by
* {@code ".package-info"} for a package information file
* @param originatingElements type or package elements causally
* @param originatingElements type or package or module elements causally
* associated with the creation of this file, may be elided or
* {@code null}
* @return a {@code JavaFileObject} to write the new source file
* @throws FilerException if the same pathname has already been
* created, the same type has already been created, or the name is
* not valid for a type
* otherwise not valid for the entity requested to being created
* @throws IOException if the file cannot be created
* @jls 7.3 Compilation Units
*/
JavaFileObject createSourceFile(CharSequence name,
Element... originatingElements) throws IOException;
/**
* Creates a new class file, and returns an object to allow
* writing to it. The file's name and path (relative to the
* {@linkplain StandardLocation#CLASS_OUTPUT root output location
* for class files}) are based on the name of the type being
* written. A class file can also be created to hold information
* about a package, including package annotations. To create a
* class file for a named package, have {@code name} be the
* writing to it. A class file for a type, or a package can
* be created.
*
* The file's name and path (relative to the {@linkplain
* StandardLocation#CLASS_OUTPUT root output location for class
* files}) are based on the name of the item to be declared as
* well as the specified module for the item (if any).
*
* <p>A class file can also be created to hold information about a
* package, including package annotations. To create a class file
* for a named package, have the {@code name} argument be the
* package's name followed by {@code ".package-info"}; creating a
* class file for an unnamed package is not supported.
*
* <p>To avoid subsequent errors, the contents of the class file
* should be compatible with the {@linkplain
* ProcessingEnvironment#getSourceVersion source version} being used
* for this run.
* <p>The optional module name is prefixed to the type name or
* package name and separated using a "{@code /}" character. For
* example, to create a class file for type {@code a.B} in module
* {@code foo}, use a {@code name} argument of {@code "foo/a.B"}.
*
* <p>Creating a class file in or for an unnamed package in a named
* module is <em>not</em> supported.
*
* @apiNote To avoid subsequent errors, the contents of the class
* file should be compatible with the {@linkplain
* ProcessingEnvironment#getSourceVersion source version} being
* used for this run.
*
* @param name binary name of the type being written or a package name followed by
* {@code ".package-info"} for a package information file
* @param originatingElements type or package elements causally
* @param originatingElements type or package or module elements causally
* associated with the creation of this file, may be elided or
* {@code null}
* @return a {@code JavaFileObject} to write the new class file
@ -210,20 +242,27 @@ public interface Filer {
* other supported location. The locations {@link
* StandardLocation#CLASS_OUTPUT CLASS_OUTPUT} and {@link
* StandardLocation#SOURCE_OUTPUT SOURCE_OUTPUT} must be
* supported. The resource may be named relative to some package
* (as are source and class files), and from there by a relative
* pathname. In a loose sense, the full pathname of the new file
* will be the concatenation of {@code location}, {@code pkg}, and
* {@code relativeName}.
* supported. The resource may be named relative to some module
* and/or package (as are source and class files), and from there
* by a relative pathname. In a loose sense, the full pathname of
* the new file will be the concatenation of {@code location},
* {@code moduleAndPkg}, and {@code relativeName}.
*
* <p>Files created via this method are not registered for
* If {@code moduleAndPkg} contains a "{@code /}" character, the
* prefix before the "{@code /}" character is the module name and
* the suffix after the "{@code /}" character is the package
* name. The package suffix may be empty. If {@code moduleAndPkg}
* does not contain a "{@code /}" character, the entire argument
* is interpreted as a package name.
*
* <p>Files created via this method are <em>not</em> registered for
* annotation processing, even if the full pathname of the file
* would correspond to the full pathname of a new source file
* or new class file.
*
* @param location location of the new file
* @param pkg package relative to which the file should be named,
* or the empty string if none
* @param moduleAndPkg module and/or package relative to which the file
* should be named, or the empty string if none
* @param relativeName final pathname components of the file
* @param originatingElements type or package elements causally
* associated with the creation of this file, may be elided or
@ -233,10 +272,11 @@ public interface Filer {
* @throws FilerException if the same pathname has already been
* created
* @throws IllegalArgumentException for an unsupported location
* @throws IllegalArgumentException if {@code moduleAndPkg} is ill-formed
* @throws IllegalArgumentException if {@code relativeName} is not relative
*/
FileObject createResource(JavaFileManager.Location location,
CharSequence pkg,
CharSequence moduleAndPkg,
CharSequence relativeName,
Element... originatingElements) throws IOException;
@ -246,18 +286,27 @@ public interface Filer {
* and {@link StandardLocation#SOURCE_OUTPUT SOURCE_OUTPUT} must
* be supported.
*
* <p>If {@code moduleAndPkg} contains a "{@code /}" character, the
* prefix before the "{@code /}" character is the module name and
* the suffix after the "{@code /}" character is the package
* name. The package suffix may be empty; however, if a module
* name is present, it must be nonempty. If {@code moduleAndPkg}
* does not contain a "{@code /}" character, the entire argument
* is interpreted as a package name.
*
* @param location location of the file
* @param pkg package relative to which the file should be searched,
* or the empty string if none
* @param moduleAndPkg module and/or package relative to which the file
* should be searched for, or the empty string if none
* @param relativeName final pathname components of the file
* @return an object to read the file
* @throws FilerException if the same pathname has already been
* opened for writing
* @throws IOException if the file cannot be opened
* @throws IllegalArgumentException for an unsupported location
* @throws IllegalArgumentException if {@code moduleAndPkg} is ill-formed
* @throws IllegalArgumentException if {@code relativeName} is not relative
*/
FileObject getResource(JavaFileManager.Location location,
CharSequence pkg,
CharSequence moduleAndPkg,
CharSequence relativeName) throws IOException;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -115,7 +115,17 @@ import javax.lang.model.SourceVersion;
* the root elements of a round. For this purpose, a type parameter is
* considered to be enclosed by its {@linkplain
* TypeParameterElement#getGenericElement generic
* element}. Annotations on {@linkplain
* element}.
* For this purpose, a package element is <em>not</em> considered to
* enclose the top-level types within that package. (A root element
* representing a package is created when a {@code package-info} file
* is processed.) Likewise, for this purpose, a module element is
* <em>not</em> considered to enclose the packages within that
* module. (A root element representing a module is created when a
* {@code module-info} file is processed.)
*
* Annotations on {@linkplain
* java.lang.annotation.ElementType#TYPE_USE type uses}, as opposed to
* annotations on elements, are ignored when computing whether or not
* an annotation type is present.
@ -235,12 +245,20 @@ public interface Processor {
* (fully qualified) name of a supported annotation type.
* Alternately it may be of the form &quot;<tt><i>name</i>.*</tt>&quot;
* representing the set of all annotation types with canonical
* names beginning with &quot;<tt><i>name.</i></tt>&quot;. Finally, {@code
* "*"} by itself represents the set of all annotation types,
* including the empty set. Note that a processor should not
* claim {@code "*"} unless it is actually processing all files;
* claiming unnecessary annotations may cause a performance
* slowdown in some environments.
* names beginning with &quot;<tt><i>name.</i></tt>&quot;.
*
* In either of those cases, the name of the annotation type can
* be optionally preceded by a module name followed by a {@code
* "/"} character. For example, if a processor supports {@code
* "a.B"}, this can include multiple annotation types named {@code
* a.B} which reside in different modules. To only support {@code
* a.B} in the {@code Foo} module, instead use {@code "Foo/a.B"}.
*
* Finally, {@code "*"} by itself represents the set of all
* annotation types, including the empty set. Note that a
* processor should not claim {@code "*"} unless it is actually
* processing all files; claiming unnecessary annotations may
* cause a performance slowdown in some environments.
*
* <p>Each string returned in the set must be accepted by the
* following grammar:
@ -248,9 +266,12 @@ public interface Processor {
* <blockquote>
* <dl>
* <dt><i>SupportedAnnotationTypeString:</i>
* <dd><i>TypeName</i> <i>DotStar</i><sub><i>opt</i></sub>
* <dd><i>ModulePrefix</i><sub><i>opt</i></sub> <i>TypeName</i> <i>DotStar</i><sub><i>opt</i></sub>
* <dd><tt>*</tt>
*
* <dt><i>ModulePrefix:</i>
* <dd><i>TypeName</i> <tt>/</tt>
*
* <dt><i>DotStar:</i>
* <dd><tt>.</tt> <tt>*</tt>
* </dl>

View File

@ -76,14 +76,17 @@ public interface RoundEnvironment {
/**
* Returns the elements annotated with the given annotation type.
* The annotation may appear directly or be inherited. Only
* package elements and type elements <i>included</i> in this
* package elements, module elements, and type elements <i>included</i> in this
* round of annotation processing, or declarations of members,
* constructors, parameters, or type parameters declared within
* those, are returned. Included type elements are {@linkplain
* #getRootElements root types} and any member types nested within
* them. Elements in a package are not considered included simply
* them. Elements of a package are not considered included simply
* because a {@code package-info} file for that package was
* created.
* Likewise, elements of a module are not considered included
* simply because a {@code module-info} file for that module was
* created
*
* @param a annotation type being requested
* @return the elements annotated with the given annotation type,
@ -128,7 +131,7 @@ public interface RoundEnvironment {
/**
* Returns the elements annotated with the given annotation type.
* The annotation may appear directly or be inherited. Only
* package elements and type elements <i>included</i> in this
* package elements, module elements, and type elements <i>included</i> in this
* round of annotation processing, or declarations of members,
* constructors, parameters, or type parameters declared within
* those, are returned. Included type elements are {@linkplain
@ -136,6 +139,9 @@ public interface RoundEnvironment {
* them. Elements in a package are not considered included simply
* because a {@code package-info} file for that package was
* created.
* Likewise, elements of a module are not considered included
* simply because a {@code module-info} file for that module was
* created
*
* @param a annotation type being requested
* @return the elements annotated with the given annotation type,

View File

@ -479,8 +479,10 @@ public interface JavaFileManager extends Closeable, Flushable, OptionChecker {
/**
* Gets a location for the module containing a specific file representing a Java
* source or class, to be found in a module-oriented location.
* The result will be a package-oriented location.
* source or class, to be found within a location, which may be either
* a module-oriented location or an output location.
* The result will be an output location if the given location is
* an output location, or it will be a package-oriented location.
*
* @apiNote the package name is used to identify the position of the file object
* within the <em>module/package/class</em> hierarchy identified by by the location.
@ -494,7 +496,8 @@ public interface JavaFileManager extends Closeable, Flushable, OptionChecker {
*
* @throws IOException if an I/O error occurred
* @throws UnsupportedOperationException if this operation if not supported by this file manager
* @throws IllegalArgumentException if the location is not a module-oriented location
* @throws IllegalArgumentException if the location is neither an output location nor a
* module-oriented location
* @since 9
*/
default Location getLocationForModule(Location location, JavaFileObject fo, String pkgName) throws IOException {
@ -543,8 +546,9 @@ public interface JavaFileManager extends Closeable, Flushable, OptionChecker {
}
/**
* Lists the locations for all the modules in a module-oriented location.
* The locations that are returned will be package-oriented locations.
* Lists the locations for all the modules in a module-oriented location or an output location.
* The locations that are returned will be output locations if the given location is an output,
* or it will be a package-oriented locations.
*
* @implSpec This implementation throws {@code UnsupportedOperationException}.
*

View File

@ -476,8 +476,18 @@ public class Modules extends JCTree.Visitor {
private Location getModuleLocation(JavaFileObject fo, Name pkgName) throws IOException {
// For now, just check module source path.
// We may want to check source path as well.
return fileManager.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH,
fo, (pkgName == null) ? null : pkgName.toString());
Location loc =
fileManager.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH,
fo, (pkgName == null) ? null : pkgName.toString());
if (loc == null) {
Location sourceOutput = fileManager.hasLocation(StandardLocation.SOURCE_OUTPUT) ?
StandardLocation.SOURCE_OUTPUT : StandardLocation.CLASS_OUTPUT;
loc =
fileManager.getLocationForModule(sourceOutput,
fo, (pkgName == null) ? null : pkgName.toString());
}
return loc;
}
private void checkSpecifiedModule(List<JCCompilationUnit> trees, JCDiagnostic.Error error) {
@ -614,6 +624,11 @@ public class Modules extends JCTree.Visitor {
};
}
public boolean isRootModule(ModuleSymbol module) {
Assert.checkNonNull(rootModules);
return rootModules.contains(module);
}
class ModuleVisitor extends JCTree.Visitor {
private ModuleSymbol sym;
private final Set<ModuleSymbol> allRequires = new HashSet<>();

View File

@ -950,12 +950,10 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
@Override @DefinedBy(Api.COMPILER)
public Location getLocationForModule(Location location, String moduleName) throws IOException {
Objects.requireNonNull(location);
if (!location.isOutputLocation() && !location.isModuleOrientedLocation())
throw new IllegalArgumentException(
"location is not an output location or a module-oriented location: "
+ location.getName());
checkModuleOrientedOrOutputLocation(location);
nullCheck(moduleName);
if (location == SOURCE_OUTPUT && getSourceOutDir() == null)
location = CLASS_OUTPUT;
return locations.getLocationForModule(location, moduleName);
}
@ -978,7 +976,7 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
@Override @DefinedBy(Api.COMPILER)
public Location getLocationForModule(Location location, JavaFileObject fo, String pkgName) throws IOException {
checkModuleOrientedLocation(location);
checkModuleOrientedOrOutputLocation(location);
if (!(fo instanceof PathFileObject))
throw new IllegalArgumentException(fo.getName());
int depth = 1; // allow 1 for filename
@ -1012,7 +1010,7 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
@Override @DefinedBy(Api.COMPILER)
public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
checkModuleOrientedLocation(location);
checkModuleOrientedOrOutputLocation(location);
return locations.listLocationsForModules(location);
}
@ -1098,10 +1096,12 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
throw new IllegalArgumentException("location is not an output location: " + location.getName());
}
private void checkModuleOrientedLocation(Location location) {
private void checkModuleOrientedOrOutputLocation(Location location) {
Objects.requireNonNull(location);
if (!location.isModuleOrientedLocation())
throw new IllegalArgumentException("location is not module-oriented: " + location.getName());
if (!location.isModuleOrientedLocation() && !location.isOutputLocation())
throw new IllegalArgumentException(
"location is not an output location or a module-oriented location: "
+ location.getName());
}
private void checkNotModuleOrientedLocation(Location location) {

View File

@ -476,6 +476,7 @@ public class Locations {
private Path outputDir;
private Map<String, Location> moduleLocations;
private Map<Path, Location> pathLocations;
OutputLocationHandler(Location location, Option... options) {
super(location, options);
@ -521,22 +522,51 @@ public class Locations {
outputDir = dir;
}
moduleLocations = null;
pathLocations = null;
}
@Override
Location getLocationForModule(String name) {
if (moduleLocations == null)
if (moduleLocations == null) {
moduleLocations = new HashMap<>();
pathLocations = new HashMap<>();
}
Location l = moduleLocations.get(name);
if (l == null) {
Path out = outputDir.resolve(name);
l = new ModuleLocationHandler(location.getName() + "[" + name + "]",
name,
Collections.singleton(outputDir.resolve(name)),
Collections.singleton(out),
true, false);
moduleLocations.put(name, l);
}
pathLocations.put(out.toAbsolutePath(), l);
}
return l;
}
@Override
Location getLocationForModule(Path dir) {
return pathLocations.get(dir);
}
private boolean listed;
@Override
Iterable<Set<Location>> listLocationsForModules() throws IOException {
if (!listed && outputDir != null) {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(outputDir)) {
for (Path p : stream) {
getLocationForModule(p.getFileName().toString());
}
}
listed = true;
}
if (moduleLocations == null)
return Collections.emptySet();
Set<Location> locns = new LinkedHashSet<>();
moduleLocations.forEach((k, v) -> locns.add(v));
return Collections.singleton(locns);
}
}
/**

View File

@ -51,6 +51,9 @@ import static javax.tools.StandardLocation.SOURCE_OUTPUT;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
import com.sun.tools.javac.code.Lint;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.comp.Modules;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.DefinedBy.Api;
@ -114,10 +117,12 @@ public class JavacFiler implements Filer, Closeable {
*/
private class FilerOutputFileObject extends ForwardingFileObject<FileObject> {
private boolean opened = false;
private ModuleSymbol mod;
private String name;
FilerOutputFileObject(String name, FileObject fileObject) {
FilerOutputFileObject(ModuleSymbol mod, String name, FileObject fileObject) {
super(fileObject);
this.mod = mod;
this.name = name;
}
@ -126,7 +131,7 @@ public class JavacFiler implements Filer, Closeable {
if (opened)
throw new IOException(ALREADY_OPENED);
opened = true;
return new FilerOutputStream(name, fileObject);
return new FilerOutputStream(mod, name, fileObject);
}
@Override @DefinedBy(Api.COMPILER)
@ -134,7 +139,7 @@ public class JavacFiler implements Filer, Closeable {
if (opened)
throw new IOException(ALREADY_OPENED);
opened = true;
return new FilerWriter(name, fileObject);
return new FilerWriter(mod, name, fileObject);
}
// Three anti-literacy methods
@ -161,8 +166,8 @@ public class JavacFiler implements Filer, Closeable {
private class FilerOutputJavaFileObject extends FilerOutputFileObject implements JavaFileObject {
private final JavaFileObject javaFileObject;
FilerOutputJavaFileObject(String name, JavaFileObject javaFileObject) {
super(name, javaFileObject);
FilerOutputJavaFileObject(ModuleSymbol mod, String name, JavaFileObject javaFileObject) {
super(mod, name, javaFileObject);
this.javaFileObject = javaFileObject;
}
@ -248,6 +253,7 @@ public class JavacFiler implements Filer, Closeable {
* when they are closed.
*/
private class FilerOutputStream extends FilterOutputStream {
ModuleSymbol mod;
String typeName;
FileObject fileObject;
boolean closed = false;
@ -256,8 +262,9 @@ public class JavacFiler implements Filer, Closeable {
* @param typeName name of class or {@code null} if just a
* binary file
*/
FilerOutputStream(String typeName, FileObject fileObject) throws IOException {
FilerOutputStream(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException {
super(fileObject.openOutputStream());
this.mod = mod;
this.typeName = typeName;
this.fileObject = fileObject;
}
@ -270,7 +277,7 @@ public class JavacFiler implements Filer, Closeable {
* stream, still try to process the file.
*/
closeFileObject(typeName, fileObject);
closeFileObject(mod, typeName, fileObject);
out.close();
}
}
@ -282,6 +289,7 @@ public class JavacFiler implements Filer, Closeable {
* closed.
*/
private class FilerWriter extends FilterWriter {
ModuleSymbol mod;
String typeName;
FileObject fileObject;
boolean closed = false;
@ -291,8 +299,9 @@ public class JavacFiler implements Filer, Closeable {
* @param typeName name of source file or {@code null} if just a
* text file
*/
FilerWriter(String typeName, FileObject fileObject) throws IOException {
FilerWriter(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException {
super(fileObject.openWriter());
this.mod = mod;
this.typeName = typeName;
this.fileObject = fileObject;
}
@ -305,7 +314,7 @@ public class JavacFiler implements Filer, Closeable {
* Writer, still try to process the file.
*/
closeFileObject(typeName, fileObject);
closeFileObject(mod, typeName, fileObject);
out.close();
}
}
@ -313,6 +322,9 @@ public class JavacFiler implements Filer, Closeable {
JavaFileManager fileManager;
Log log;
Modules modules;
Names names;
Symtab syms;
Context context;
boolean lastRound;
@ -340,7 +352,7 @@ public class JavacFiler implements Filer, Closeable {
* This set must be synchronized. Its iterators should preserve
* insertion order.
*/
private final Map<String, JavaFileObject> generatedClasses;
private final Map<ModuleSymbol, Map<String, JavaFileObject>> generatedClasses;
/**
* JavaFileObjects for source files closed in this round. This
@ -353,13 +365,13 @@ public class JavacFiler implements Filer, Closeable {
* Names of all created source files. Its iterators should
* preserve insertion order.
*/
private final Set<String> aggregateGeneratedSourceNames;
private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedSourceNames;
/**
* Names of all created class files. Its iterators should
* preserve insertion order.
*/
private final Set<String> aggregateGeneratedClassNames;
private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedClassNames;
JavacFiler(Context context) {
@ -367,12 +379,15 @@ public class JavacFiler implements Filer, Closeable {
fileManager = context.get(JavaFileManager.class);
log = Log.instance(context);
modules = Modules.instance(context);
names = Names.instance(context);
syms = Symtab.instance(context);
fileObjectHistory = synchronizedSet(new LinkedHashSet<FileObject>());
generatedSourceNames = synchronizedSet(new LinkedHashSet<String>());
generatedSourceFileObjects = synchronizedSet(new LinkedHashSet<JavaFileObject>());
generatedClasses = synchronizedMap(new LinkedHashMap<String, JavaFileObject>());
generatedClasses = synchronizedMap(new LinkedHashMap<>());
openTypeNames = synchronizedSet(new LinkedHashSet<String>());
@ -382,19 +397,51 @@ public class JavacFiler implements Filer, Closeable {
lint = (Lint.instance(context)).isEnabled(PROCESSING);
}
@DefinedBy(Api.ANNOTATION_PROCESSING)
public JavaFileObject createSourceFile(CharSequence name,
@Override @DefinedBy(Api.ANNOTATION_PROCESSING)
public JavaFileObject createSourceFile(CharSequence nameAndModule,
Element... originatingElements) throws IOException {
return createSourceOrClassFile(true, name.toString());
Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule);
return createSourceOrClassFile(moduleAndClass.fst, true, moduleAndClass.snd);
}
@DefinedBy(Api.ANNOTATION_PROCESSING)
public JavaFileObject createClassFile(CharSequence name,
Element... originatingElements) throws IOException {
return createSourceOrClassFile(false, name.toString());
@Override @DefinedBy(Api.ANNOTATION_PROCESSING)
public JavaFileObject createClassFile(CharSequence nameAndModule,
Element... originatingElements) throws IOException {
Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule);
return createSourceOrClassFile(moduleAndClass.fst, false, moduleAndClass.snd);
}
private JavaFileObject createSourceOrClassFile(boolean isSourceFile, String name) throws IOException {
private Pair<ModuleSymbol, String> checkOrInferModule(CharSequence moduleAndPkg) throws FilerException {
String moduleAndPkgString = moduleAndPkg.toString();
int slash = moduleAndPkgString.indexOf('/');
if (slash != (-1)) {
//module name specified:
String module = moduleAndPkgString.substring(0, slash);
ModuleSymbol explicitModule = syms.getModule(names.fromString(module));
if (explicitModule == null) {
throw new FilerException("Module: " + module + " does not exist.");
}
if (!modules.isRootModule(explicitModule)) {
throw new FilerException("Cannot write to the given module!");
}
return Pair.of(explicitModule, moduleAndPkgString.substring(slash + 1));
} else {
if (modules.multiModuleMode) {
throw new FilerException("No module to write to specified!");
}
return Pair.of(modules.getDefaultModule(), moduleAndPkgString);
}
}
private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name) throws IOException {
Assert.checkNonNull(mod);
if (lint) {
int periodIndex = name.lastIndexOf(".");
if (periodIndex != -1) {
@ -404,8 +451,12 @@ public class JavacFiler implements Filer, Closeable {
log.warning("proc.suspicious.class.name", name, extn);
}
}
checkNameAndExistence(name, isSourceFile);
checkNameAndExistence(mod, name, isSourceFile);
Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT);
if (modules.multiModuleMode) {
loc = this.fileManager.getLocationForModule(loc, mod.name.toString());
}
JavaFileObject.Kind kind = (isSourceFile ?
JavaFileObject.Kind.SOURCE :
JavaFileObject.Kind.CLASS);
@ -418,21 +469,30 @@ public class JavacFiler implements Filer, Closeable {
log.warning("proc.file.create.last.round", name);
if (isSourceFile)
aggregateGeneratedSourceNames.add(name);
aggregateGeneratedSourceNames.add(Pair.of(mod, name));
else
aggregateGeneratedClassNames.add(name);
aggregateGeneratedClassNames.add(Pair.of(mod, name));
openTypeNames.add(name);
return new FilerOutputJavaFileObject(name, fileObject);
return new FilerOutputJavaFileObject(mod, name, fileObject);
}
@DefinedBy(Api.ANNOTATION_PROCESSING)
@Override @DefinedBy(Api.ANNOTATION_PROCESSING)
public FileObject createResource(JavaFileManager.Location location,
CharSequence pkg,
CharSequence moduleAndPkg,
CharSequence relativeName,
Element... originatingElements) throws IOException {
Pair<ModuleSymbol, String> moduleAndPackage = checkOrInferModule(moduleAndPkg);
ModuleSymbol msym = moduleAndPackage.fst;
String pkg = moduleAndPackage.snd;
locationCheck(location);
if (modules.multiModuleMode) {
Assert.checkNonNull(msym);
location = this.fileManager.getLocationForModule(location, msym.name.toString());
}
String strPkg = pkg.toString();
if (strPkg.length() > 0)
checkName(strPkg);
@ -443,9 +503,9 @@ public class JavacFiler implements Filer, Closeable {
checkFileReopening(fileObject, true);
if (fileObject instanceof JavaFileObject)
return new FilerOutputJavaFileObject(null, (JavaFileObject)fileObject);
return new FilerOutputJavaFileObject(msym, null, (JavaFileObject)fileObject);
else
return new FilerOutputFileObject(null, fileObject);
return new FilerOutputFileObject(msym, null, fileObject);
}
private void locationCheck(JavaFileManager.Location location) {
@ -457,13 +517,21 @@ public class JavacFiler implements Filer, Closeable {
}
}
@DefinedBy(Api.ANNOTATION_PROCESSING)
@Override @DefinedBy(Api.ANNOTATION_PROCESSING)
public FileObject getResource(JavaFileManager.Location location,
CharSequence pkg,
CharSequence moduleAndPkg,
CharSequence relativeName) throws IOException {
String strPkg = pkg.toString();
if (strPkg.length() > 0)
checkName(strPkg);
Pair<ModuleSymbol, String> moduleAndPackage = checkOrInferModule(moduleAndPkg);
ModuleSymbol msym = moduleAndPackage.fst;
String pkg = moduleAndPackage.snd;
if (modules.multiModuleMode) {
Assert.checkNonNull(msym);
location = this.fileManager.getLocationForModule(location, msym.name.toString());
}
if (pkg.length() > 0)
checkName(pkg);
// TODO: Only support reading resources in selected output
// locations? Only allow reading of non-source, non-class
@ -478,12 +546,12 @@ public class JavacFiler implements Filer, Closeable {
FileObject fileObject;
if (location.isOutputLocation()) {
fileObject = fileManager.getFileForOutput(location,
pkg.toString(),
pkg,
relativeName.toString(),
null);
} else {
fileObject = fileManager.getFileForInput(location,
pkg.toString(),
pkg,
relativeName.toString());
}
if (fileObject == null) {
@ -524,16 +592,19 @@ public class JavacFiler implements Filer, Closeable {
}
}
private void checkNameAndExistence(String typename, boolean allowUnnamedPackageInfo) throws FilerException {
private void checkNameAndExistence(ModuleSymbol mod, String typename, boolean allowUnnamedPackageInfo) throws FilerException {
// TODO: Check if type already exists on source or class path?
// If so, use warning message key proc.type.already.exists
checkName(typename, allowUnnamedPackageInfo);
if (aggregateGeneratedSourceNames.contains(typename) ||
aggregateGeneratedClassNames.contains(typename)) {
if (aggregateGeneratedSourceNames.contains(Pair.of(mod, typename)) ||
aggregateGeneratedClassNames.contains(Pair.of(mod, typename))) {
if (lint)
log.warning("proc.type.recreate", typename);
throw new FilerException("Attempt to recreate a file for type " + typename);
}
if (!mod.isUnnamed() && !typename.contains(".")) {
throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename);
}
}
/**
@ -565,7 +636,7 @@ public class JavacFiler implements Filer, Closeable {
return generatedSourceFileObjects;
}
public Map<String, JavaFileObject> getGeneratedClasses() {
public Map<ModuleSymbol, Map<String, JavaFileObject>> getGeneratedClasses() {
return generatedClasses;
}
@ -621,7 +692,7 @@ public class JavacFiler implements Filer, Closeable {
* Upon close, register files opened by create{Source, Class}File
* for annotation processing.
*/
private void closeFileObject(String typeName, FileObject fileObject) {
private void closeFileObject(ModuleSymbol mod, String typeName, FileObject fileObject) {
/*
* If typeName is non-null, the file object was opened as a
* source or class file by the user. If a file was opened as
@ -640,7 +711,7 @@ public class JavacFiler implements Filer, Closeable {
break;
case CLASS:
generatedClasses.put(typeName, javaFileObject);
generatedClasses.computeIfAbsent(mod, m -> Collections.synchronizedMap(new LinkedHashMap<>())).put(typeName, javaFileObject);
openTypeNames.remove(typeName);
break;

View File

@ -34,6 +34,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.*;
import java.util.stream.Collectors;
@ -827,12 +828,14 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
private void discoverAndRunProcs(Set<TypeElement> annotationsPresent,
List<ClassSymbol> topLevelClasses,
List<PackageSymbol> packageInfoFiles) {
List<PackageSymbol> packageInfoFiles,
List<ModuleSymbol> moduleInfoFiles) {
Map<String, TypeElement> unmatchedAnnotations = new HashMap<>(annotationsPresent.size());
for(TypeElement a : annotationsPresent) {
unmatchedAnnotations.put(a.getQualifiedName().toString(),
a);
ModuleElement mod = elementUtils.getModuleOf(a);
unmatchedAnnotations.put((mod != null ? mod.getSimpleName() + "/" : "") + a.getQualifiedName().toString(),
a);
}
// Give "*" processors a chance to match
@ -849,6 +852,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
Set<Element> rootElements = new LinkedHashSet<>();
rootElements.addAll(topLevelClasses);
rootElements.addAll(packageInfoFiles);
rootElements.addAll(moduleInfoFiles);
rootElements = Collections.unmodifiableSet(rootElements);
RoundEnvironment renv = new JavacRoundEnvironment(false,
@ -986,7 +990,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
/** The trees that need to be cleaned - includes roots and implicitly parsed trees. */
Set<JCCompilationUnit> treesToClean;
/** The classes to be compiler that have were generated. */
Map<String, JavaFileObject> genClassFiles;
Map<ModuleSymbol, Map<String, JavaFileObject>> genClassFiles;
/** The set of annotations to be processed this round. */
Set<TypeElement> annotationsPresent;
@ -994,6 +998,8 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
List<ClassSymbol> topLevelClasses;
/** The set of package-info files to be processed this round. */
List<PackageSymbol> packageInfoFiles;
/** The set of module-info files to be processed this round. */
List<ModuleSymbol> moduleInfoFiles;
/** Create a round (common code). */
private Round(int number, Set<JCCompilationUnit> treesToClean,
@ -1011,6 +1017,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
// the following will be populated as needed
topLevelClasses = List.nil();
packageInfoFiles = List.nil();
moduleInfoFiles = List.nil();
this.treesToClean = treesToClean;
}
@ -1031,12 +1038,14 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
packageInfoFiles = getPackageInfoFiles(roots);
moduleInfoFiles = getModuleInfoFiles(roots);
findAnnotationsPresent();
}
/** Create a new round. */
private Round(Round prev,
Set<JavaFileObject> newSourceFiles, Map<String,JavaFileObject> newClassFiles) {
Set<JavaFileObject> newSourceFiles, Map<ModuleSymbol, Map<String,JavaFileObject>> newClassFiles) {
this(prev.number+1, prev.treesToClean, null);
prev.newRound();
this.genClassFiles = prev.genClassFiles;
@ -1048,9 +1057,13 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
if (unrecoverableError())
return;
roots = compiler.initModules(roots);
enterClassFiles(genClassFiles);
List<ClassSymbol> newClasses = enterClassFiles(newClassFiles);
genClassFiles.putAll(newClassFiles);
for (Entry<ModuleSymbol, Map<String, JavaFileObject>> moduleAndClassFiles : newClassFiles.entrySet()) {
genClassFiles.computeIfAbsent(moduleAndClassFiles.getKey(), m -> new LinkedHashMap<>()).putAll(moduleAndClassFiles.getValue());
}
enterTrees(roots);
if (unrecoverableError())
@ -1064,11 +1077,13 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
getPackageInfoFiles(parsedFiles),
getPackageInfoFilesFromClasses(newClasses));
moduleInfoFiles = List.nil(); //module-info cannot be generated
findAnnotationsPresent();
}
/** Create the next round to be used. */
Round next(Set<JavaFileObject> newSourceFiles, Map<String, JavaFileObject> newClassFiles) {
Round next(Set<JavaFileObject> newSourceFiles, Map<ModuleSymbol, Map<String, JavaFileObject>> newClassFiles) {
return new Round(this, newSourceFiles, newClassFiles);
}
@ -1121,45 +1136,47 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
annotationComputer.scan(classSym, annotationsPresent);
for (PackageSymbol pkgSym : packageInfoFiles)
annotationComputer.scan(pkgSym, annotationsPresent);
for (ModuleSymbol mdlSym : moduleInfoFiles)
annotationComputer.scan(mdlSym, annotationsPresent);
}
/** Enter a set of generated class files. */
private List<ClassSymbol> enterClassFiles(Map<String, JavaFileObject> classFiles) {
private List<ClassSymbol> enterClassFiles(Map<ModuleSymbol, Map<String, JavaFileObject>> modulesAndClassFiles) {
List<ClassSymbol> list = List.nil();
for (Map.Entry<String,JavaFileObject> entry : classFiles.entrySet()) {
Name name = names.fromString(entry.getKey());
JavaFileObject file = entry.getValue();
if (file.getKind() != JavaFileObject.Kind.CLASS)
throw new AssertionError(file);
ClassSymbol cs;
// TODO: for now, we assume that generated code is in a default module;
// in time, we need a way to be able to specify the module for generated code
if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) {
Name packageName = Convert.packagePart(name);
PackageSymbol p = symtab.enterPackage(defaultModule, packageName);
if (p.package_info == null)
p.package_info = symtab.enterClass(defaultModule, Convert.shortName(name), p);
cs = p.package_info;
cs.reset();
if (cs.classfile == null)
for (Entry<ModuleSymbol, Map<String, JavaFileObject>> moduleAndClassFiles : modulesAndClassFiles.entrySet()) {
for (Map.Entry<String,JavaFileObject> entry : moduleAndClassFiles.getValue().entrySet()) {
Name name = names.fromString(entry.getKey());
JavaFileObject file = entry.getValue();
if (file.getKind() != JavaFileObject.Kind.CLASS)
throw new AssertionError(file);
ClassSymbol cs;
if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) {
Name packageName = Convert.packagePart(name);
PackageSymbol p = symtab.enterPackage(moduleAndClassFiles.getKey(), packageName);
if (p.package_info == null)
p.package_info = symtab.enterClass(moduleAndClassFiles.getKey(), Convert.shortName(name), p);
cs = p.package_info;
cs.reset();
if (cs.classfile == null)
cs.classfile = file;
cs.completer = initialCompleter;
} else {
cs = symtab.enterClass(moduleAndClassFiles.getKey(), name);
cs.reset();
cs.classfile = file;
cs.completer = initialCompleter;
} else {
cs = symtab.enterClass(defaultModule, name);
cs.reset();
cs.classfile = file;
cs.completer = initialCompleter;
cs.owner.members().enter(cs); //XXX - OverwriteBetweenCompilations; syms.getClass is not sufficient anymore
cs.completer = initialCompleter;
cs.owner.members().enter(cs); //XXX - OverwriteBetweenCompilations; syms.getClass is not sufficient anymore
}
list = list.prepend(cs);
}
list = list.prepend(cs);
}
return list.reverse();
}
/** Enter a set of syntax trees. */
private void enterTrees(List<JCCompilationUnit> roots) {
compiler.enterTrees(compiler.initModules(roots));
compiler.enterTrees(roots);
}
/** Run a processing round. */
@ -1179,7 +1196,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
JavacProcessingEnvironment.this);
discoveredProcs.iterator().runContributingProcs(renv);
} else {
discoverAndRunProcs(annotationsPresent, topLevelClasses, packageInfoFiles);
discoverAndRunProcs(annotationsPresent, topLevelClasses, packageInfoFiles, moduleInfoFiles);
}
} catch (Throwable t) {
// we're specifically expecting Abort here, but if any Throwable
@ -1418,6 +1435,18 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
return packages.reverse();
}
private List<ModuleSymbol> getModuleInfoFiles(List<? extends JCCompilationUnit> units) {
List<ModuleSymbol> modules = List.nil();
for (JCCompilationUnit unit : units) {
if (isModuleInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE) &&
unit.defs.nonEmpty() &&
unit.defs.head.hasTag(Tag.MODULEDEF)) {
modules = modules.prepend(unit.modle);
}
}
return modules.reverse();
}
// avoid unchecked warning from use of varargs
private static <T> List<T> join(List<T> list1, List<T> list2) {
return list1.appendList(list2);
@ -1431,6 +1460,10 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym);
}
private boolean isModuleInfo(JavaFileObject fo, JavaFileObject.Kind kind) {
return fo.isNameCompatible("module-info", kind);
}
/*
* Called retroactively to determine if a class loader was required,
* after we have failed to create one.
@ -1625,8 +1658,21 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
* import-style string, return a regex that won't match anything.
*/
private static Pattern importStringToPattern(String s, Processor p, Log log) {
if (MatchingUtils.isValidImportString(s)) {
return MatchingUtils.validImportStringToPattern(s);
String module;
String pkg;
int slash = s.indexOf('/');
if (slash == (-1)) {
if (s.equals("*")) {
return MatchingUtils.validImportStringToPattern(s);
}
module = ".*/";
pkg = s;
} else {
module = Pattern.quote(s.substring(0, slash + 1));
pkg = s.substring(slash + 1);
}
if (MatchingUtils.isValidImportString(pkg)) {
return Pattern.compile(module + MatchingUtils.validImportStringToPatternString(pkg));
} else {
log.warning("proc.malformed.supported.string", s, p.getClass().getName());
return noMatches; // won't match any valid identifier

View File

@ -36,7 +36,9 @@ import javax.lang.model.SourceVersion;
* deletion without notice.</b>
*/
public class MatchingUtils {
private static final Pattern allMatches = Pattern.compile(".*");
private static final String allMatchesString = ".*";
private static final Pattern allMatches = Pattern.compile(allMatchesString);
/**
* Return true if the argument string is a valid import-style
@ -72,9 +74,9 @@ public class MatchingUtils {
return valid;
}
public static Pattern validImportStringToPattern(String s) {
public static String validImportStringToPatternString(String s) {
if (s.equals("*")) {
return allMatches;
return allMatchesString;
} else {
String s_prime = s.replace(".", "\\.");
@ -82,7 +84,17 @@ public class MatchingUtils {
s_prime = s_prime.substring(0, s_prime.length() - 1) + ".+";
}
return Pattern.compile(s_prime);
return s_prime;
}
}
public static Pattern validImportStringToPattern(String s) {
String pattern = validImportStringToPatternString(s);
if (pattern == allMatchesString) {
return allMatches;
} else {
return Pattern.compile(pattern);
}
}

View File

@ -360,7 +360,7 @@ public class AbstractIndexWriter extends HtmlDocletWriter {
*/
protected void addComment(Element element, Content contentTree) {
List<? extends DocTree> tags;
Content span = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content span = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(element));
HtmlTree div = new HtmlTree(HtmlTag.DIV);
div.addStyle(HtmlStyle.block);
if (utils.isDeprecated(element)) {

View File

@ -404,46 +404,6 @@ public abstract class AbstractMemberWriter {
ped.getEnclosingElement().equals(ped.getEnclosingElement())));
}
/**
* Add deprecated information to the documentation tree
*
* @param deprmembers list of deprecated members
* @param headingKey the caption for the deprecated members table
* @param tableSummary the summary for the deprecated members table
* @param tableHeader table headers for the deprecated members table
* @param contentTree the content tree to which the deprecated members table will be added
*/
protected void addDeprecatedAPI(Collection<Element> deprmembers, String headingKey,
String tableSummary, List<String> tableHeader, Content contentTree) {
if (deprmembers.size() > 0) {
Content caption = writer.getTableCaption(configuration.getContent(headingKey));
Content table = (configuration.isOutputHtml5())
? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption)
: HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption);
table.addContent(writer.getSummaryTableHeader(tableHeader, "col"));
Content tbody = new HtmlTree(HtmlTag.TBODY);
boolean altColor = true;
for (Element member : deprmembers) {
HtmlTree thRow = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, getDeprecatedLink(member));
HtmlTree tr = HtmlTree.TR(thRow);
HtmlTree td = new HtmlTree(HtmlTag.TD);
td.addStyle(HtmlStyle.colLast);
List<? extends DocTree> deprTrees = utils.getBlockTags(member, DocTree.Kind.DEPRECATED);
if (!deprTrees.isEmpty()) {
writer.addInlineDeprecatedComment(member, deprTrees.get(0), td);
}
tr.addContent(td);
tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
altColor = !altColor;
tbody.addContent(tr);
}
table.addContent(tbody);
Content li = HtmlTree.LI(HtmlStyle.blockList, table);
Content ul = HtmlTree.UL(HtmlStyle.blockList, li);
contentTree.addContent(ul);
}
}
/**
* Add use information to the documentation tree.
*

View File

@ -317,7 +317,7 @@ public class AnnotationTypeWriterImpl extends SubWriterHolderWriter
List<? extends DocTree> deprs = utils.getBlockTags(annotationType, DocTree.Kind.DEPRECATED);
if (utils.isDeprecated(annotationType)) {
CommentHelper ch = utils.getCommentHelper(annotationType);
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(annotationType));
Content div = HtmlTree.DIV(HtmlStyle.block, deprLabel);
if (!deprs.isEmpty()) {

View File

@ -610,7 +610,7 @@ public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWrite
classInfoTree.addContent(hr);
List<? extends DocTree> deprs = utils.getBlockTags(typeElement, DocTree.Kind.DEPRECATED);
if (utils.isDeprecated(typeElement)) {
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(typeElement));
Content div = HtmlTree.DIV(HtmlStyle.block, deprLabel);
if (!deprs.isEmpty()) {
CommentHelper ch = utils.getCommentHelper(typeElement);

View File

@ -73,6 +73,7 @@ public class Contents {
public final Content deprecatedAPI;
public final Content deprecatedLabel;
public final Content deprecatedPhrase;
public final Content deprecatedForRemovalPhrase;
public final Content descfrmClassLabel;
public final Content descfrmInterfaceLabel;
public final Content descriptionLabel;
@ -186,6 +187,7 @@ public class Contents {
deprecatedAPI = getContent("doclet.Deprecated_API");
deprecatedLabel = getContent("doclet.navDeprecated");
deprecatedPhrase = getContent("doclet.Deprecated");
deprecatedForRemovalPhrase = getContent("doclet.DeprecatedForRemoval");
descfrmClassLabel = getContent("doclet.Description_From_Class");
descfrmInterfaceLabel = getContent("doclet.Description_From_Interface");
descriptionLabel = getContent("doclet.Description");

View File

@ -64,6 +64,8 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
private String getAnchorName(DeprElementKind kind) {
switch (kind) {
case REMOVAL:
return "forRemoval";
case MODULE:
return "module";
case PACKAGE:
@ -97,6 +99,8 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
private String getHeadingKey(DeprElementKind kind) {
switch (kind) {
case REMOVAL:
return "doclet.Deprecated_For_Removal";
case MODULE:
return "doclet.Deprecated_Modules";
case PACKAGE:
@ -130,6 +134,8 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
private String getSummaryKey(DeprElementKind kind) {
switch (kind) {
case REMOVAL:
return "doclet.deprecated_for_removal";
case MODULE:
return "doclet.deprecated_modules";
case PACKAGE:
@ -163,6 +169,8 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
private String getHeaderKey(DeprElementKind kind) {
switch (kind) {
case REMOVAL:
return "doclet.Element";
case MODULE:
return "doclet.Module";
case PACKAGE:
@ -212,6 +220,7 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
writerMap = new EnumMap<>(DeprElementKind.class);
for (DeprElementKind kind : DeprElementKind.values()) {
switch (kind) {
case REMOVAL:
case MODULE:
case PACKAGE:
case INTERFACE:
@ -284,16 +293,8 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
List<String> memberTableHeader = new ArrayList<>();
memberTableHeader.add(resources.getText(getHeaderKey(kind)));
memberTableHeader.add(resources.getText("doclet.Description"));
if (kind == DeprElementKind.MODULE) {
addModuleDeprecatedAPI(deprapi.getSet(kind),
addDeprecatedAPI(deprapi.getSet(kind),
getHeadingKey(kind), memberTableSummary, memberTableHeader, div);
} else if (kind == DeprElementKind.PACKAGE) {
addPackageDeprecatedAPI(deprapi.getSet(kind),
getHeadingKey(kind), memberTableSummary, memberTableHeader, div);
} else {
writerMap.get(kind).addDeprecatedAPI(deprapi.getSet(kind),
getHeadingKey(kind), memberTableSummary, memberTableHeader, div);
}
}
}
if (configuration.allowTag(HtmlTag.MAIN)) {
@ -395,17 +396,17 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
}
/**
* Add module deprecation information to the documentation tree
* Add deprecated information to the documentation tree
*
* @param deprMdles list of deprecated modules
* @param headingKey the caption for the deprecated module table
* @param tableSummary the summary for the deprecated module table
* @param tableHeader table headers for the deprecated module table
* @param contentTree the content tree to which the deprecated module table will be added
* @param deprList list of deprecated API elements
* @param headingKey the caption for the deprecated table
* @param tableSummary the summary for the deprecated table
* @param tableHeader table headers for the deprecated table
* @param contentTree the content tree to which the deprecated table will be added
*/
protected void addModuleDeprecatedAPI(SortedSet<Element> deprMdles, String headingKey,
protected void addDeprecatedAPI(SortedSet<Element> deprList, String headingKey,
String tableSummary, List<String> tableHeader, Content contentTree) {
if (deprMdles.size() > 0) {
if (deprList.size() > 0) {
Content caption = getTableCaption(configuration.getContent(headingKey));
Content table = (configuration.isOutputHtml5())
? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption)
@ -413,16 +414,28 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
table.addContent(getSummaryTableHeader(tableHeader, "col"));
Content tbody = new HtmlTree(HtmlTag.TBODY);
boolean altColor = true;
for (Element e : deprMdles) {
ModuleElement mdle = (ModuleElement) e;
HtmlTree thRow = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst,
getModuleLink(mdle, new StringContent(mdle.getQualifiedName())));
for (Element e : deprList) {
HtmlTree thRow;
switch (e.getKind()) {
case MODULE:
ModuleElement m = (ModuleElement)e;
thRow = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst,
getModuleLink(m, new StringContent(m.getQualifiedName())));
break;
case PACKAGE:
PackageElement pkg = (PackageElement)e;
thRow = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst,
getPackageLink(pkg, getPackageName(pkg)));
break;
default:
thRow = getDeprecatedLink(e);
}
HtmlTree tr = HtmlTree.TR(thRow);
HtmlTree tdDesc = new HtmlTree(HtmlTag.TD);
tdDesc.addStyle(HtmlStyle.colLast);
List<? extends DocTree> tags = utils.getDeprecatedTrees(mdle);
List<? extends DocTree> tags = utils.getDeprecatedTrees(e);
if (!tags.isEmpty()) {
addInlineDeprecatedComment(mdle, tags.get(0), tdDesc);
addInlineDeprecatedComment(e, tags.get(0), tdDesc);
}
tr.addContent(tdDesc);
tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
@ -436,45 +449,30 @@ public class DeprecatedListWriter extends SubWriterHolderWriter {
}
}
/**
* Add package deprecation information to the documentation tree
*
* @param deprPkgs list of deprecated packages
* @param headingKey the caption for the deprecated package table
* @param tableSummary the summary for the deprecated package table
* @param tableHeader table headers for the deprecated package table
* @param contentTree the content tree to which the deprecated package table will be added
*/
protected void addPackageDeprecatedAPI(SortedSet<Element> deprPkgs, String headingKey,
String tableSummary, List<String> tableHeader, Content contentTree) {
if (deprPkgs.size() > 0) {
Content caption = getTableCaption(configuration.getContent(headingKey));
Content table = (configuration.isOutputHtml5())
? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption)
: HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption);
table.addContent(getSummaryTableHeader(tableHeader, "col"));
Content tbody = new HtmlTree(HtmlTag.TBODY);
boolean altColor = true;
for (Element e : deprPkgs) {
PackageElement pkg = (PackageElement) e;
HtmlTree thRow = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst,
getPackageLink(pkg, getPackageName(pkg)));
HtmlTree tr = HtmlTree.TR(thRow);
HtmlTree tdDesc = new HtmlTree(HtmlTag.TD);
tdDesc.addStyle(HtmlStyle.colLast);
List<? extends DocTree> tags = utils.getDeprecatedTrees(pkg);
if (!tags.isEmpty()) {
addInlineDeprecatedComment(pkg, tags.get(0), tdDesc);
}
tr.addContent(tdDesc);
tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
altColor = !altColor;
tbody.addContent(tr);
}
table.addContent(tbody);
Content li = HtmlTree.LI(HtmlStyle.blockList, table);
Content ul = HtmlTree.UL(HtmlStyle.blockList, li);
contentTree.addContent(ul);
protected HtmlTree getDeprecatedLink(Element e) {
AbstractMemberWriter writer;
switch (e.getKind()) {
case INTERFACE:
case CLASS:
case ENUM:
case ANNOTATION_TYPE:
writer = new NestedClassWriterImpl(this);
break;
case FIELD:
writer = new FieldWriterImpl(this);
break;
case METHOD:
writer = new MethodWriterImpl(this);
break;
case CONSTRUCTOR:
writer = new ConstructorWriterImpl(this);
break;
case ENUM_CONSTANT:
writer = new EnumConstantWriterImpl(this);
break;
default:
writer = new AnnotationTypeOptionalMemberWriterImpl(this, null);
}
return HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, writer.getDeprecatedLink(e));
}
}

View File

@ -412,7 +412,7 @@ public class HtmlDocletWriter extends HtmlDocWriter {
HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD);
tdClassDescription.addStyle(HtmlStyle.colLast);
if (utils.isDeprecated(te)) {
tdClassDescription.addContent(contents.deprecatedLabel);
tdClassDescription.addContent(getDeprecatedPhrase(te));
List<? extends DocTree> tags = utils.getDeprecatedTrees(te);
if (!tags.isEmpty()) {
addSummaryDeprecatedComment(te, tags.get(0), tdClassDescription);
@ -1616,6 +1616,18 @@ public class HtmlDocletWriter extends HtmlDocWriter {
addCommentTags(element, tag, description, false, false, htmltree);
}
/**
* Get the deprecated phrase as content.
*
* @param e the Element for which the inline deprecated comment will be added
* @return a content tree for the deprecated phrase.
*/
public Content getDeprecatedPhrase(Element e) {
return (utils.isDeprecatedForRemoval(e))
? contents.deprecatedForRemovalPhrase
: contents.deprecatedPhrase;
}
/**
* Add the inline deprecated comment.
*

View File

@ -506,7 +506,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
CommentHelper ch = utils.getCommentHelper(mdle);
HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV);
deprDiv.addStyle(HtmlStyle.deprecatedContent);
Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(mdle));
deprDiv.addContent(deprPhrase);
if (!deprs.isEmpty()) {
List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0));
@ -648,7 +648,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
deprs = utils.getDeprecatedTrees(pkg);
HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV);
deprDiv.addStyle(HtmlStyle.deprecatedContent);
Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(pkg));
deprDiv.addContent(deprPhrase);
if (!deprs.isEmpty()) {
CommentHelper ch = utils.getCommentHelper(pkg);

View File

@ -172,7 +172,7 @@ public class PackageWriterImpl extends HtmlDocletWriter
CommentHelper ch = utils.getCommentHelper(packageElement);
HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV);
deprDiv.addStyle(HtmlStyle.deprecatedContent);
Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(packageElement));
deprDiv.addContent(deprPhrase);
if (!deprs.isEmpty()) {
List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0));
@ -223,7 +223,7 @@ public class PackageWriterImpl extends HtmlDocletWriter
HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD);
tdClassDescription.addStyle(HtmlStyle.colLast);
if (utils.isDeprecated(klass)) {
tdClassDescription.addContent(contents.deprecatedLabel);
tdClassDescription.addContent(getDeprecatedPhrase(klass));
List<? extends DocTree> tags = utils.getDeprecatedTrees(klass);
if (!tags.isEmpty()) {
addSummaryDeprecatedComment(klass, tags.get(0), tdClassDescription);

View File

@ -187,7 +187,7 @@ public abstract class SubWriterHolderWriter extends HtmlDocletWriter {
List<? extends DocTree> deprs = utils.getBlockTags(member, DocTree.Kind.DEPRECATED);
Content div;
if (utils.isDeprecated(member)) {
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(member));
div = HtmlTree.DIV(HtmlStyle.block, deprLabel);
div.addContent(Contents.SPACE);
if (!deprs.isEmpty()) {
@ -198,7 +198,7 @@ public abstract class SubWriterHolderWriter extends HtmlDocletWriter {
} else {
Element te = member.getEnclosingElement();
if (te != null && utils.isTypeElement(te) && utils.isDeprecated(te)) {
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, contents.deprecatedPhrase);
Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(te));
div = HtmlTree.DIV(HtmlStyle.block, deprLabel);
div.addContent(Contents.SPACE);
tdSummary.addContent(div);

View File

@ -178,7 +178,7 @@ public class TagletWriterImpl extends TagletWriter {
if (utils.isTypeElement(element)) {
if (utils.isDeprecated(element)) {
result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
new StringContent(configuration.getText("doclet.Deprecated"))));
htmlWriter.getDeprecatedPhrase(element)));
result.addContent(RawHtml.nbsp);
if (!deprs.isEmpty()) {
List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0));
@ -190,7 +190,7 @@ public class TagletWriterImpl extends TagletWriter {
} else {
if (utils.isDeprecated(element)) {
result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
new StringContent(configuration.getText("doclet.Deprecated"))));
htmlWriter.getDeprecatedPhrase(element)));
result.addContent(RawHtml.nbsp);
if (!deprs.isEmpty()) {
List<? extends DocTree> bodyTags = ch.getBody(configuration, deprs.get(0));
@ -199,9 +199,10 @@ public class TagletWriterImpl extends TagletWriter {
result.addContent(HtmlTree.SPAN(HtmlStyle.deprecationComment, body));
}
} else {
if (utils.isDeprecated(utils.getEnclosingTypeElement(element))) {
Element ee = utils.getEnclosingTypeElement(element);
if (utils.isDeprecated(ee)) {
result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
new StringContent(configuration.getText("doclet.Deprecated"))));
htmlWriter.getDeprecatedPhrase(ee)));
result.addContent(RawHtml.nbsp);
}
}

View File

@ -3,6 +3,7 @@ doclet.Contents=Contents
doclet.Overview=Overview
doclet.Window_Overview=Overview List
doclet.Window_Overview_Summary=Overview
doclet.Element=Element
doclet.Package=Package
doclet.Module=Module
doclet.All_Packages=All Packages
@ -71,6 +72,7 @@ doclet.see.class_or_package_not_found=Tag {0}: reference not found: {1}
doclet.see.class_or_package_not_accessible=Tag {0}: reference not accessible: {1}
doclet.tag.invalid_usage=invalid usage of tag {0}
doclet.Deprecated_API=Deprecated API
doclet.Deprecated_For_Removal=Deprecated For Removal
doclet.Deprecated_Modules=Deprecated Modules
doclet.Deprecated_Packages=Deprecated Packages
doclet.Deprecated_Classes=Deprecated Classes
@ -84,6 +86,7 @@ doclet.Deprecated_Constructors=Deprecated Constructors
doclet.Deprecated_Methods=Deprecated Methods
doclet.Deprecated_Enum_Constants=Deprecated Enum Constants
doclet.Deprecated_Annotation_Type_Members=Deprecated Annotation Type Elements
doclet.deprecated_for_removal=deprecated for removal
doclet.deprecated_modules=deprecated modules
doclet.deprecated_packages=deprecated packages
doclet.deprecated_classes=deprecated classes

View File

@ -150,6 +150,7 @@ doclet.Property_Detail=Property Detail
doclet.Method_Detail=Method Detail
doclet.Constructor_Detail=Constructor Detail
doclet.Deprecated=Deprecated.
doclet.DeprecatedForRemoval=Deprecated, for removal: This API element is subject to removal in a future version.
doclet.Hidden=Hidden
doclet.Groupname_already_used=In -group option, groupname already used: {0}
doclet.value_tag_invalid_reference={0} (referenced by @value tag) is an unknown reference.

View File

@ -52,6 +52,7 @@ public class DeprecatedAPIListBuilder {
private final Configuration configuration;
private final Utils utils;
public static enum DeprElementKind {
REMOVAL,
MODULE,
PACKAGE,
INTERFACE,
@ -90,9 +91,13 @@ public class DeprecatedAPIListBuilder {
* @param configuration the current configuration of the doclet.
*/
private void buildDeprecatedAPIInfo() {
SortedSet<Element> rset = deprecatedMap.get(DeprElementKind.REMOVAL);
SortedSet<ModuleElement> modules = configuration.modules;
SortedSet<Element> mset = deprecatedMap.get(DeprElementKind.MODULE);
for (Element me : modules) {
if (utils.isDeprecatedForRemoval(me)) {
rset.add(me);
}
if (utils.isDeprecated(me)) {
mset.add(me);
}
@ -100,6 +105,9 @@ public class DeprecatedAPIListBuilder {
SortedSet<PackageElement> packages = configuration.packages;
SortedSet<Element> pset = deprecatedMap.get(DeprElementKind.PACKAGE);
for (Element pe : packages) {
if (utils.isDeprecatedForRemoval(pe)) {
rset.add(pe);
}
if (utils.isDeprecated(pe)) {
pset.add(pe);
}
@ -107,6 +115,9 @@ public class DeprecatedAPIListBuilder {
for (Element e : configuration.getIncludedTypeElements()) {
TypeElement te = (TypeElement)e;
SortedSet<Element> eset;
if (utils.isDeprecatedForRemoval(e)) {
rset.add(e);
}
if (utils.isDeprecated(e)) {
switch (e.getKind()) {
case ANNOTATION_TYPE:
@ -133,18 +144,18 @@ public class DeprecatedAPIListBuilder {
break;
}
}
composeDeprecatedList(deprecatedMap.get(DeprElementKind.FIELD),
composeDeprecatedList(rset, deprecatedMap.get(DeprElementKind.FIELD),
utils.getFields(te));
composeDeprecatedList(deprecatedMap.get(DeprElementKind.METHOD),
composeDeprecatedList(rset, deprecatedMap.get(DeprElementKind.METHOD),
utils.getMethods(te));
composeDeprecatedList(deprecatedMap.get(DeprElementKind.CONSTRUCTOR),
composeDeprecatedList(rset, deprecatedMap.get(DeprElementKind.CONSTRUCTOR),
utils.getConstructors(te));
if (utils.isEnum(e)) {
composeDeprecatedList(deprecatedMap.get(DeprElementKind.ENUM_CONSTANT),
composeDeprecatedList(rset, deprecatedMap.get(DeprElementKind.ENUM_CONSTANT),
utils.getEnumConstants(te));
}
if (utils.isAnnotationType(e)) {
composeDeprecatedList(deprecatedMap.get(DeprElementKind.ANNOTATION_TYPE_MEMBER),
composeDeprecatedList(rset, deprecatedMap.get(DeprElementKind.ANNOTATION_TYPE_MEMBER),
utils.getAnnotationMembers(te));
}
@ -154,11 +165,16 @@ public class DeprecatedAPIListBuilder {
/**
* Add the members into a single list of deprecated members.
*
* @param rset set of elements deprecated for removal.
* @param sset set of deprecated elements.
* @param list List of all the particular deprecated members, e.g. methods.
* @param members members to be added in the list.
*/
private void composeDeprecatedList(SortedSet<Element> sset, List<? extends Element> members) {
private void composeDeprecatedList(SortedSet<Element> rset, SortedSet<Element> sset, List<? extends Element> members) {
for (Element member : members) {
if (utils.isDeprecatedForRemoval(member)) {
rset.add(member);
}
if (utils.isDeprecated(member)) {
sset.add(member);
}

View File

@ -36,6 +36,7 @@ import java.util.stream.Collectors;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
@ -74,10 +75,12 @@ import com.sun.source.tree.LineMap;
import com.sun.source.util.DocSourcePositions;
import com.sun.source.util.DocTrees;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.model.JavacTypes;
import jdk.javadoc.internal.doclets.toolkit.CommentUtils.DocCommentDuo;
import jdk.javadoc.internal.doclets.toolkit.Configuration;
import jdk.javadoc.internal.doclets.toolkit.Messages;
import jdk.javadoc.internal.doclets.toolkit.WorkArounds;
import jdk.javadoc.internal.tool.DocEnvImpl;
import static javax.lang.model.element.ElementKind.*;
import static javax.lang.model.element.Modifier.*;
@ -1476,6 +1479,30 @@ public class Utils {
return elementUtils.isDeprecated(e);
}
/**
* Return true if the given Element is deprecated for removal.
*
* @param e the Element to check.
* @return true if the given Element is deprecated for removal.
*/
public boolean isDeprecatedForRemoval(Element e) {
List<? extends AnnotationMirror> annotationList = e.getAnnotationMirrors();
JavacTypes jctypes = ((DocEnvImpl) configuration.docEnv).toolEnv.typeutils;
for (AnnotationMirror anno : annotationList) {
if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), getDeprecatedType())) {
Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = anno.getElementValues();
if (!pairs.isEmpty()) {
for (ExecutableElement element : pairs.keySet()) {
if (element.getSimpleName().contentEquals("forRemoval")) {
return Boolean.parseBoolean((pairs.get(element)).toString());
}
}
}
}
}
return false;
}
/**
* A convenience method to get property name from the name of the
* getter or setter method.

View File

@ -0,0 +1,26 @@
## JSZip v2.5.0
### MIT License
<pre>
Copyright (c) 2009-2014 Stuart Knightley, David Duponchel, Franz Buchinger, António Afonso
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
</pre>

View File

@ -39,22 +39,28 @@ import java.util.Set;
* Build the profile information.
*/
enum Profile {
COMPACT1("compact1", 1, "java.compact1"),
COMPACT2("compact2", 2, "java.compact2"),
COMPACT3("compact3", 3, "java.compact3", "java.smartcardio", "jdk.sctp",
"jdk.httpserver", "jdk.security.auth",
"jdk.naming.dns", "jdk.naming.rmi",
"jdk.management"),
// need a way to determine JRE modules
SE_JRE("Java SE JRE", 4, "java.se", "jdk.charsets",
"jdk.crypto.ec", "jdk.crypto.pkcs11",
"jdk.crypto.mscapi", "jdk.crypto.ucrypto",
"jdk.localedata", "jdk.scripting.nashorn", "jdk.zipfs"),
FULL_JRE("Full JRE", 5, "java.se.ee", "jdk.charsets",
"jdk.crypto.ec", "jdk.crypto.pkcs11",
"jdk.crypto.mscapi", "jdk.crypto.ucrypto", "jdk.jvmstat",
"jdk.localedata", "jdk.scripting.nashorn",
"jdk.unsupported", "jdk.zipfs");
COMPACT1("compact1", 1, "java.logging",
"java.scripting"),
COMPACT2("compact2", 2, "java.rmi",
"java.sql",
"java.xml",
"jdk.xml.dom",
"jdk.httpserver"),
COMPACT3("compact3", 3, "java.smartcardio",
"java.compiler",
"java.instrument",
"java.management",
"java.naming",
"java.prefs",
"java.security.jgss",
"java.security.sasl",
"java.sql.rowset",
"java.xml.crypto",
"jdk.management",
"jdk.naming.dns",
"jdk.naming.rmi",
"jdk.sctp",
"jdk.security.auth");
final String name;
final int profile;
@ -80,12 +86,6 @@ enum Profile {
return JDK.isEmpty() ? 0 : Profile.values().length;
}
Optional<Module> findModule(String name) {
return modules.containsKey(name)
? Optional.of(modules.get(name))
: Optional.empty();
}
/**
* Returns the Profile for the given package name; null if not found.
*/
@ -137,6 +137,9 @@ enum Profile {
// for debugging
public static void main(String[] args) throws IOException {
// initialize Profiles
new JdepsConfiguration.Builder().allModules().build();
// find platform modules
if (Profile.getProfileCount() == 0) {
System.err.println("No profile is present in this JDK");

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 4927552 8026567 8071982
* @bug 4927552 8026567 8071982 8162674
* @summary <DESC>
* @author jamieh
* @library ../lib
@ -79,14 +79,138 @@ public class TestDeprecatedDocs extends JavadocTester {
"<pre>@Deprecated\n"
+ "public class <span class=\"typeNameLabel\">DeprecatedClassByAnnotation</span>\n"
+ "extends java.lang.Object</pre>",
"<pre>@Deprecated\n"
"<pre>@Deprecated(forRemoval=true)\n"
+ "public&nbsp;int field</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated.</span>&nbsp;</div>",
"<pre>@Deprecated\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;</div>",
"<pre>@Deprecated(forRemoval=true)\n"
+ "public&nbsp;DeprecatedClassByAnnotation()</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated.</span>&nbsp;</div>",
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;</div>",
"<pre>@Deprecated\n"
+ "public&nbsp;void&nbsp;method()</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated.</span>&nbsp;</div>");
checkOutput("pkg/TestAnnotationType.html", true,
"<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">annotation_test1 passes.</span></div>\n"
+ "</div>\n"
+ "<br>\n"
+ "<pre>@Deprecated(forRemoval=true)\n"
+ "@Documented\n"
+ "public @interface <span class=\"memberNameLabel\">TestAnnotationType</span></pre>",
"<pre>@Deprecated(forRemoval=true)\n"
+ "static final&nbsp;int&nbsp;field</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This "
+ "API element is subject to removal in a future version. </span>&nbsp;<span class=\"deprecationComment\">annotation_test4 passes.</span></div>",
"<pre>@Deprecated(forRemoval=true)\n"
+ "int&nbsp;required</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;"
+ "<span class=\"deprecationComment\">annotation_test3 passes.</span></div>",
"<pre>java.lang.String&nbsp;optional</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated.</span>&nbsp;<span class=\"deprecationComment\">annotation_test2 passes.</span></div>");
checkOutput("pkg/TestClass.html", true,
"<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">class_test1 passes.</span></div>\n"
+ "</div>\n"
+ "<br>\n"
+ "<pre>@Deprecated(forRemoval=true)\n"
+ "public class <span class=\"typeNameLabel\">TestClass</span>\n"
+ "extends java.lang.Object</pre>",
"<pre>@Deprecated(forRemoval=true)\n"
+ "public&nbsp;TestClass()</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;"
+ "<span class=\"deprecationComment\">class_test3 passes.</span></div>");
checkOutput("pkg/TestEnum.html", true,
"<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">enum_test1 passes.</span></div>\n"
+ "</div>\n"
+ "<br>\n"
+ "<pre>@Deprecated(forRemoval=true)\n"
+ "public enum <span class=\"typeNameLabel\">TestEnum</span>\n"
+ "extends java.lang.Enum&lt;<a href=\"../pkg/TestEnum.html\" title=\"enum in pkg\">TestEnum</a>&gt;</pre>",
"<pre>@Deprecated(forRemoval=true)\n"
+ "public static final&nbsp;<a href=\"../pkg/TestEnum.html\" title=\"enum in pkg\">TestEnum</a> FOR_REMOVAL</pre>\n"
+ "<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;"
+ "<span class=\"deprecationComment\">enum_test3 passes.</span></div>");
checkOutput("pkg/TestError.html", true,
"<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">error_test1 passes.</span></div>\n"
+ "</div>\n"
+ "<br>\n"
+ "<pre>@Deprecated(forRemoval=true)\n"
+ "public class <span class=\"typeNameLabel\">TestError</span>\n"
+ "extends java.lang.Error</pre>");
checkOutput("pkg/TestException.html", true,
"<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">exception_test1 passes.</span></div>\n"
+ "</div>\n"
+ "<br>\n"
+ "<pre>@Deprecated(forRemoval=true)\n"
+ "public class <span class=\"typeNameLabel\">TestException</span>\n"
+ "extends java.lang.Exception</pre>");
checkOutput("pkg/TestInterface.html", true,
"<div class=\"block\"><span class=\"deprecatedLabel\">Deprecated, for removal: This API element is subject to removal in a future version. </span>&nbsp;\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">interface_test1 passes.</span></div>\n"
+ "</div>\n"
+ "<br>\n"
+ "<pre>@Deprecated(forRemoval=true)\n"
+ "public class <span class=\"typeNameLabel\">TestInterface</span>\n"
+ "extends java.lang.Object</pre>");
checkOutput("deprecated-list.html", true,
"<ul>\n"
+ "<li><a href=\"#forRemoval\">Deprecated For Removal</a></li>\n"
+ "<li><a href=\"#class\">Deprecated Classes</a></li>\n"
+ "<li><a href=\"#enum\">Deprecated Enums</a></li>\n"
+ "<li><a href=\"#exception\">Deprecated Exceptions</a></li>\n"
+ "<li><a href=\"#error\">Deprecated Errors</a></li>\n"
+ "<li><a href=\"#annotation.type\">Deprecated Annotation Types</a></li>\n"
+ "<li><a href=\"#field\">Deprecated Fields</a></li>\n"
+ "<li><a href=\"#method\">Deprecated Methods</a></li>\n"
+ "<li><a href=\"#constructor\">Deprecated Constructors</a></li>\n"
+ "<li><a href=\"#enum.constant\">Deprecated Enum Constants</a></li>\n"
+ "<li><a href=\"#annotation.type.member\">Deprecated Annotation Type Elements</a></li>\n"
+ "</ul>",
"<a name=\"forRemoval\">",
"<table class=\"deprecatedSummary\" summary=\"Deprecated For Removal table, listing deprecated for removal, and an explanation\">\n"
+ "<caption><span>Deprecated For Removal</span><span class=\"tabEnd\">&nbsp;</span></caption>\n"
+ "<tr>\n"
+ "<th class=\"colFirst\" scope=\"col\">Element</th>\n"
+ "<th class=\"colLast\" scope=\"col\">Description</th>\n"
+ "</tr>",
"<table class=\"deprecatedSummary\" summary=\"Deprecated Enums table, listing deprecated enums, and an explanation\">\n"
+ "<caption><span>Deprecated Enums</span><span class=\"tabEnd\">&nbsp;</span></caption>\n"
+ "<tr>\n"
+ "<th class=\"colFirst\" scope=\"col\">Enum</th>\n"
+ "<th class=\"colLast\" scope=\"col\">Description</th>\n"
+ "</tr>\n"
+ "<tbody>\n"
+ "<tr class=\"altColor\">\n"
+ "<th class=\"colFirst\" scope=\"row\"><a href=\"pkg/TestEnum.html\" title=\"enum in pkg\">pkg.TestEnum</a></th>\n"
+ "<td class=\"colLast\">\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">enum_test1 passes.</span></div>\n"
+ "</td>\n"
+ "</tr>\n"
+ "</tbody>\n"
+ "</table>",
"<table class=\"deprecatedSummary\" summary=\"Deprecated Exceptions table, listing deprecated exceptions, and an explanation\">\n"
+ "<caption><span>Deprecated Exceptions</span><span class=\"tabEnd\">&nbsp;</span></caption>\n"
+ "<tr>\n"
+ "<th class=\"colFirst\" scope=\"col\">Exceptions</th>\n"
+ "<th class=\"colLast\" scope=\"col\">Description</th>\n"
+ "</tr>\n"
+ "<tbody>\n"
+ "<tr class=\"altColor\">\n"
+ "<th class=\"colFirst\" scope=\"row\"><a href=\"pkg/TestException.html\" title=\"class in pkg\">pkg.TestException</a></th>\n"
+ "<td class=\"colLast\">\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">exception_test1 passes.</span></div>\n"
+ "</td>\n"
+ "</tr>\n"
+ "</tbody>\n"
+ "</table>");
}
}

View File

@ -26,10 +26,10 @@ package pkg;
@Deprecated()
public class DeprecatedClassByAnnotation {
@Deprecated()
@Deprecated(forRemoval=true)
public int field;
@Deprecated()
@Deprecated(forRemoval=true)
public DeprecatedClassByAnnotation() {}
@Deprecated()

View File

@ -28,6 +28,7 @@ import java.lang.annotation.*;
/**
* @deprecated annotation_test1 passes.
*/
@Deprecated(forRemoval=true)
@Documented public @interface TestAnnotationType {
/**
@ -38,10 +39,12 @@ import java.lang.annotation.*;
/**
* @deprecated annotation_test3 passes.
*/
@Deprecated(forRemoval=true)
int required();
/**
* @deprecated annotation_test4 passes.
*/
@Deprecated(forRemoval=true)
int field = 0;
}

View File

@ -26,6 +26,7 @@ package pkg;
/**
* @deprecated class_test1 passes.
*/
@Deprecated(forRemoval=true)
public class TestClass {
/**
@ -36,6 +37,7 @@ public class TestClass {
/**
* @deprecated class_test3 passes.
*/
@Deprecated(forRemoval=true)
public TestClass() {}
/**

View File

@ -26,10 +26,17 @@ package pkg;
/**
* @deprecated enum_test1 passes.
*/
@Deprecated(forRemoval=true)
public enum TestEnum {
/**
* @deprecated enum_test2 passes.
*/
ONE, TWO, THREE;
ONE, TWO, THREE,
/**
* @deprecated enum_test3 passes.
*/
@Deprecated(forRemoval=true)
FOR_REMOVAL;
}

View File

@ -26,6 +26,7 @@ package pkg;
/**
* @deprecated error_test1 passes.
*/
@Deprecated(forRemoval=true)
public class TestError extends Error {
/**

View File

@ -26,6 +26,7 @@ package pkg;
/**
* @deprecated exception_test1 passes.
*/
@Deprecated(forRemoval=true)
public class TestException extends Exception {
/**

View File

@ -30,6 +30,7 @@ import java.util.Map;
/**
* @deprecated interface_test1 passes.
*/
@Deprecated(forRemoval=true)
public class TestInterface {
/**

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8154119 8154262 8156077 8157987 8154261 8154817 8135291 8155995 8162363 8168766 8168688
* @bug 8154119 8154262 8156077 8157987 8154261 8154817 8135291 8155995 8162363 8168766 8168688 8162674
* @summary Test modules support in javadoc.
* @author bpatel
* @library ../lib
@ -253,7 +253,8 @@ public class TestModules extends JavadocTester {
void checkHtml5Description(boolean found) {
checkOutput("module1-summary.html", found,
"<section role=\"region\">\n"
+ "<div class=\"deprecatedContent\"><span class=\"deprecatedLabel\">Deprecated.</span>\n"
+ "<div class=\"deprecatedContent\"><span class=\"deprecatedLabel\">Deprecated, for removal:"
+ " This API element is subject to removal in a future version. </span>\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">This module is deprecated.</span></div>\n"
+ "</div>\n"
+ "<!-- ============ MODULE DESCRIPTION =========== -->\n"
@ -597,11 +598,13 @@ public class TestModules extends JavadocTester {
void checkModuleDeprecation(boolean found) {
checkOutput("module1-summary.html", found,
"<div class=\"deprecatedContent\"><span class=\"deprecatedLabel\">Deprecated.</span>\n"
"<div class=\"deprecatedContent\"><span class=\"deprecatedLabel\">Deprecated, for removal:"
+ " This API element is subject to removal in a future version. </span>\n"
+ "<div class=\"block\"><span class=\"deprecationComment\">This module is deprecated.</span></div>\n"
+ "</div>");
checkOutput("deprecated-list.html", found,
"<ul>\n"
+ "<li><a href=\"#forRemoval\">Deprecated For Removal</a></li>\n"
+ "<li><a href=\"#module\">Deprecated Modules</a></li>\n"
+ "</ul>",
"<tr class=\"altColor\">\n"

View File

@ -28,7 +28,7 @@
*
* @deprecated This module is deprecated.
*/
@Deprecated
@Deprecated(forRemoval=true)
module module1 {
requires module2;

View File

@ -0,0 +1,213 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8171005
* @summary Verify behavior of JavaFileManager methods w.r.t. module/package oriented locations
* @library /tools/lib
* @modules java.compiler
* @build toolbox.TestRunner ModuleAndPackageLocations
* @run main ModuleAndPackageLocations
*/
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import toolbox.TestRunner;
import toolbox.TestRunner.Test;
public class ModuleAndPackageLocations extends TestRunner {
public static void main(String... args) throws Exception {
new ModuleAndPackageLocations().runTests(m -> new Object[] { Paths.get(m.getName()) });
}
public ModuleAndPackageLocations() {
super(System.err);
}
@Test
public void testListLocations(Path outerBase) throws Exception {
doRunTest(outerBase, (base, fm) -> {
assertLocations(fm.listLocationsForModules(StandardLocation.MODULE_SOURCE_PATH),
toSet("MODULE_SOURCE_PATH[a]:false:false",
"MODULE_SOURCE_PATH[b]:false:false",
"MODULE_SOURCE_PATH[c]:false:false"));
assertLocations(fm.listLocationsForModules(StandardLocation.MODULE_PATH),
toSet("MODULE_PATH[0.X,a]:false:false",
"MODULE_PATH[0.X,b]:false:false"),
toSet("MODULE_PATH[1.X,c]:false:false",
"MODULE_PATH[1.X,b]:false:false"));
assertLocations(fm.listLocationsForModules(StandardLocation.SOURCE_OUTPUT),
toSet("SOURCE_OUTPUT[a]:false:true",
"SOURCE_OUTPUT[b]:false:true"));
fm.getLocationForModule(StandardLocation.SOURCE_OUTPUT, "c");
assertLocations(fm.listLocationsForModules(StandardLocation.SOURCE_OUTPUT),
toSet("SOURCE_OUTPUT[a]:false:true",
"SOURCE_OUTPUT[b]:false:true",
"SOURCE_OUTPUT[c]:false:true"));
});
}
@Test
public void testGetModuleForPath(Path outerBase) throws Exception {
doRunTest(outerBase, (base, fm) -> {
Location cOutput = fm.getLocationForModule(StandardLocation.SOURCE_OUTPUT, "c");
JavaFileObject testFO = fm.getJavaFileForOutput(cOutput, "test.Test", Kind.CLASS, null);
testFO.openOutputStream().close();
Location cOutput2 = fm.getLocationForModule(StandardLocation.SOURCE_OUTPUT, testFO, "test");
if (cOutput != cOutput2) {
throw new AssertionError("Unexpected location: " + cOutput2 + ", expected: " +cOutput);
}
});
}
@Test
public void testRejects(Path outerBase) throws Exception {
doRunTest(outerBase, (base, fm) -> {
assertRefused(() -> fm.getClassLoader(StandardLocation.MODULE_SOURCE_PATH));
assertRefused(() -> fm.getFileForInput(StandardLocation.MODULE_SOURCE_PATH, "", ""));
assertRefused(() -> fm.getFileForOutput(StandardLocation.MODULE_SOURCE_PATH, "", "", null));
assertRefused(() -> fm.getJavaFileForInput(StandardLocation.MODULE_SOURCE_PATH, "", Kind.SOURCE));
assertRefused(() -> fm.getJavaFileForOutput(StandardLocation.MODULE_SOURCE_PATH, "", Kind.SOURCE, null));
assertRefused(() -> fm.getLocationForModule(StandardLocation.SOURCE_PATH, "test"));
JavaFileObject out = fm.getJavaFileForInput(StandardLocation.CLASS_OUTPUT, "test.Test", Kind.CLASS);
assertRefused(() -> fm.getLocationForModule(StandardLocation.SOURCE_PATH, out, "test"));
assertRefused(() -> fm.inferBinaryName(StandardLocation.MODULE_PATH, out));
assertRefused(() -> fm.inferModuleName(StandardLocation.MODULE_SOURCE_PATH));
assertRefused(() -> fm.list(StandardLocation.MODULE_SOURCE_PATH, "test", EnumSet.allOf(Kind.class), false));
assertRefused(() -> fm.listLocationsForModules(StandardLocation.SOURCE_PATH));
});
}
void doRunTest(Path base, TestExec test) throws Exception {
try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
Path msp = base.resolve("msp");
Path msp1 = msp.resolve("1");
Path msp2 = msp.resolve("2");
Files.createDirectories(msp1.resolve("a"));
Files.createDirectories(msp1.resolve("b"));
Files.createDirectories(msp2.resolve("b"));
Files.createDirectories(msp2.resolve("c"));
Path mp = base.resolve("mp");
Path mp1 = mp.resolve("1");
Path mp2 = mp.resolve("2");
touch(mp1.resolve("a/module-info.class"),
mp1.resolve("b/module-info.class"),
mp2.resolve("b/module-info.class"),
mp2.resolve("c/module-info.class"));
Path so = base.resolve("so");
Files.createDirectories(so.resolve("a"));
Files.createDirectories(so.resolve("b"));
List<String> mspOpt = Arrays.asList(msp1.toAbsolutePath().toString() +
File.pathSeparatorChar +
msp2.toAbsolutePath().toString());
List<String> mpOpt = Arrays.asList(mp1.toAbsolutePath().toString() +
File.pathSeparatorChar +
mp2.toAbsolutePath().toString());
fm.handleOption("--module-source-path", mspOpt.iterator());
fm.handleOption("--module-path", mpOpt.iterator());
fm.handleOption("-s", Arrays.asList(so.toString()).iterator());
test.run(base, fm);
}
}
private Set<String> toSet(String... values) {
return new HashSet<>(Arrays.asList(values));
}
private void touch(Path... paths) throws IOException {
for (Path p : paths) {
Files.createDirectories(p.getParent());
Files.newOutputStream(p).close();
}
}
@SafeVarargs
private void assertLocations(Iterable<Set<Location>> locations, Set<String>... expected) {
List<Set<String>> actual =
StreamSupport.stream(locations.spliterator(), true)
.map(locs -> locs.stream()
.map(l -> toString(l))
.collect(Collectors.toSet()))
.collect(Collectors.toList());
if (!Objects.equals(actual, Arrays.asList(expected))) {
throw new AssertionError("Unexpected output: " + actual);
}
}
private void assertRefused(Callable r) throws Exception {
try {
r.call();
throw new AssertionError("Expected exception did not occur");
} catch (IllegalArgumentException ex) {
//ok
}
}
private static String toString(Location l) {
return l.getName().replaceAll("\\[([0-9])\\.[0-9]:", "[$1.X,") + ":" +
l.isModuleOrientedLocation() + ":" + l.isOutputLocation();
}
static interface TestExec {
public void run(Path base, JavaFileManager fm) throws Exception;
}
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
}

View File

@ -33,23 +33,35 @@
* @run main AnnotationProcessing
*/
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.FilerException;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.ModuleElement.ProvidesDirective;
import javax.lang.model.element.ModuleElement.UsesDirective;
@ -60,10 +72,20 @@ import javax.lang.model.type.TypeKind;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.ElementScanner9;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import toolbox.JavacTask;
import toolbox.Task;
import toolbox.Task.Mode;
import toolbox.Task.OutputKind;
public class AnnotationProcessing extends ModuleTestBase {
@ -135,6 +157,7 @@ public class AnnotationProcessing extends ModuleTestBase {
public static final class AP extends AbstractProcessor {
private Map<String, List<String>> module2ExpectedEnclosedElements;
private Set<String> seenModules = new HashSet<>();
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
@ -181,12 +204,16 @@ public class AnnotationProcessing extends ModuleTestBase {
.map(p -> p.getQualifiedName().toString())
.collect(Collectors.toList());
assertEquals(module2ExpectedEnclosedElements.remove(module.getQualifiedName().toString()),
String moduleName = module.getQualifiedName().toString();
assertEquals(module2ExpectedEnclosedElements.get(moduleName),
actualElements);
seenModules.add(moduleName);
}
if (roundEnv.processingOver()) {
assertEquals(true, module2ExpectedEnclosedElements.isEmpty());
assertEquals(module2ExpectedEnclosedElements.keySet(), seenModules);
}
return false;
@ -374,6 +401,617 @@ public class AnnotationProcessing extends ModuleTestBase {
}
@Test
public void testModuleInRootElements(Path base) throws Exception {
Path moduleSrc = base.resolve("module-src");
Path m1 = moduleSrc.resolve("m1");
Path classes = base.resolve("classes");
Files.createDirectories(classes);
tb.writeJavaFiles(m1,
"module m1 { exports api; }",
"package api; public class Api { }");
List<String> log = new JavacTask(tb)
.options("-processor", ModuleInRootElementsAP.class.getName())
.outdir(classes)
.files(findJavaFiles(moduleSrc))
.run()
.writeAll()
.getOutputLines(Task.OutputKind.STDERR);
assertEquals(Arrays.asList("module: m1"), log);
}
@SupportedAnnotationTypes("*")
public static final class ModuleInRootElementsAP extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
roundEnv.getRootElements()
.stream()
.filter(el -> el.getKind() == ElementKind.MODULE)
.forEach(mod -> System.err.println("module: " + mod.getSimpleName()));
return false;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
@Test
public void testAnnotationsInModuleInfo(Path base) throws Exception {
Path moduleSrc = base.resolve("module-src");
Path m1 = moduleSrc.resolve("m1");
tb.writeJavaFiles(m1,
"@Deprecated module m1 { }");
Path m2 = moduleSrc.resolve("m2");
tb.writeJavaFiles(m2,
"@SuppressWarnings(\"\") module m2 { }");
Path classes = base.resolve("classes");
Files.createDirectories(classes);
List<String> log = new JavacTask(tb)
.options("-processor", AnnotationsInModuleInfoPrint.class.getName())
.outdir(classes)
.files(findJavaFiles(m1))
.run()
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expectedLog = Arrays.asList("Note: AP Invoked",
"Note: AP Invoked");
assertEquals(expectedLog, log);
new JavacTask(tb)
.options("-processor", AnnotationsInModuleInfoFail.class.getName())
.outdir(classes)
.files(findJavaFiles(m2))
.run()
.writeAll();
}
@SupportedAnnotationTypes("java.lang.Deprecated")
public static final class AnnotationsInModuleInfoPrint extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
processingEnv.getMessager().printMessage(Kind.NOTE, "AP Invoked");
return false;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
@SupportedAnnotationTypes("java.lang.Deprecated")
public static final class AnnotationsInModuleInfoFail extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
throw new AssertionError();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
@Test
public void testGenerateInMultiModeAPI(Path base) throws Exception {
Path moduleSrc = base.resolve("module-src");
Path classes = base.resolve("classes");
Files.createDirectories(classes);
Path m1 = moduleSrc.resolve("m1");
tb.writeJavaFiles(m1,
"module m1 { exports api1; }",
"package api1; public class Api { GenApi ga; impl.Impl i; }");
writeFile("1", m1, "api1", "api");
writeFile("1", m1, "impl", "impl");
Path m2 = moduleSrc.resolve("m2");
tb.writeJavaFiles(m2,
"module m2 { requires m1; exports api2; }",
"package api2; public class Api { api1.GenApi ga1; GenApi qa2; impl.Impl i;}");
writeFile("2", m2, "api2", "api");
writeFile("2", m2, "impl", "impl");
for (FileType fileType : FileType.values()) {
if (Files.isDirectory(classes)) {
tb.cleanDirectory(classes);
} else {
Files.createDirectories(classes);
}
new JavacTask(tb)
.options("-processor", MultiModeAPITestAP.class.getName(),
"--module-source-path", moduleSrc.toString(),
"-Afiletype=" + fileType.name())
.outdir(classes)
.files(findJavaFiles(moduleSrc))
.run()
.writeAll();
assertFileExists(classes, "m1", "api1", "GenApi.class");
assertFileExists(classes, "m1", "impl", "Impl.class");
assertFileExists(classes, "m1", "api1", "gen1");
assertFileExists(classes, "m2", "api2", "GenApi.class");
assertFileExists(classes, "m2", "impl", "Impl.class");
assertFileExists(classes, "m2", "api2", "gen1");
}
}
enum FileType {
SOURCE,
CLASS;
}
public static abstract class GeneratingAP extends AbstractProcessor {
void createSource(CreateFileObject file, String name, String content) {
try (Writer out = file.create().openWriter()) {
out.write(content);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
void createClass(CreateFileObject file, String name, String content) {
String fileNameStub = name.replace(".", File.separator);
try (OutputStream out = file.create().openOutputStream()) {
Path scratch = Files.createDirectories(Paths.get(""));
Path scratchSrc = scratch.resolve(fileNameStub + ".java").toAbsolutePath();
Files.createDirectories(scratchSrc.getParent());
try (Writer w = Files.newBufferedWriter(scratchSrc)) {
w.write(content);
}
Path scratchClasses = scratch.resolve("classes");
Files.createDirectories(scratchClasses);
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
List<String> options = Arrays.asList("-d", scratchClasses.toString());
Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc);
CompilationTask task = comp.getTask(null, fm, null, options, null, files);
if (!task.call()) {
throw new AssertionError("compilation failed");
}
}
Path classfile = scratchClasses.resolve(fileNameStub + ".class");
Files.copy(classfile, out);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
void doReadResource(CreateFileObject file, String expectedContent) {
try {
StringBuilder actualContent = new StringBuilder();
try (Reader r = file.create().openReader(true)) {
int read;
while ((read = r.read()) != (-1)) {
actualContent.append((char) read);
}
}
assertEquals(expectedContent, actualContent.toString());
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
public interface CreateFileObject {
public FileObject create() throws IOException;
}
void expectFilerException(Callable<Object> c) {
try {
c.call();
throw new AssertionError("Expected exception not thrown");
} catch (FilerException ex) {
//expected
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
@SupportedAnnotationTypes("*")
@SupportedOptions({"filetype", "modulename"})
public static final class MultiModeAPITestAP extends GeneratingAP {
int round;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (round++ != 0)
return false;
createClass("m1", "api1.GenApi", "package api1; public class GenApi {}");
createClass("m1", "impl.Impl", "package impl; public class Impl {}");
createClass("m2", "api2.GenApi", "package api2; public class GenApi {}");
createClass("m2", "impl.Impl", "package impl; public class Impl {}");
createResource("m1", "api1", "gen1");
createResource("m2", "api2", "gen1");
readResource("m1", "api1", "api", "1");
readResource("m1", "impl", "impl", "1");
readResource("m2", "api2", "api", "2");
readResource("m2", "impl", "impl", "2");
Filer filer = processingEnv.getFiler();
expectFilerException(() -> filer.createSourceFile("fail.Fail"));
expectFilerException(() -> filer.createClassFile("fail.Fail"));
expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "fail", "fail"));
expectFilerException(() -> filer.getResource(StandardLocation.MODULE_SOURCE_PATH, "fail", "fail"));
//must not generate to unnamed package:
expectFilerException(() -> filer.createSourceFile("m1/Fail"));
expectFilerException(() -> filer.createClassFile("m1/Fail"));
//cannot generate resources to modules that are not root modules:
expectFilerException(() -> filer.createSourceFile("java.base/fail.Fail"));
expectFilerException(() -> filer.createClassFile("java.base/fail.Fail"));
expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "java.base/fail", "Fail"));
return false;
}
void createClass(String expectedModule, String name, String content) {
Filer filer = processingEnv.getFiler();
FileType filetype = FileType.valueOf(processingEnv.getOptions().getOrDefault("filetype", ""));
switch (filetype) {
case SOURCE:
createSource(() -> filer.createSourceFile(expectedModule + "/" + name), name, content);
break;
case CLASS:
createClass(() -> filer.createClassFile(expectedModule + "/" + name), name, content);
break;
default:
throw new AssertionError("Unexpected filetype: " + filetype);
}
}
void createResource(String expectedModule, String pkg, String relName) {
try {
Filer filer = processingEnv.getFiler();
filer.createResource(StandardLocation.CLASS_OUTPUT, expectedModule + "/" + pkg, relName)
.openOutputStream()
.close();
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
void readResource(String expectedModule, String pkg, String relName, String expectedContent) {
Filer filer = processingEnv.getFiler();
doReadResource(() -> filer.getResource(StandardLocation.MODULE_SOURCE_PATH, expectedModule + "/" + pkg, relName),
expectedContent);
}
}
@Test
public void testGenerateInSingleNameModeAPI(Path base) throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
Path m1 = base.resolve("module-src");
tb.writeJavaFiles(m1,
"module m1 { }");
writeFile("3", m1, "impl", "resource");
new JavacTask(tb)
.options("-processor", SingleNameModeAPITestAP.class.getName(),
"-sourcepath", m1.toString())
.outdir(classes)
.files(findJavaFiles(m1))
.run()
.writeAll();
assertFileExists(classes, "impl", "Impl1.class");
assertFileExists(classes, "impl", "Impl2.class");
assertFileExists(classes, "impl", "Impl3");
assertFileExists(classes, "impl", "Impl4.class");
assertFileExists(classes, "impl", "Impl5.class");
assertFileExists(classes, "impl", "Impl6");
assertFileExists(classes, "impl", "Impl7.class");
assertFileExists(classes, "impl", "Impl8.class");
assertFileExists(classes, "impl", "Impl9");
}
@SupportedAnnotationTypes("*")
public static final class SingleNameModeAPITestAP extends GeneratingAP {
int round;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (round++ != 0)
return false;
Filer filer = processingEnv.getFiler();
createSource(() -> filer.createSourceFile("impl.Impl1"), "impl.Impl1", "package impl; class Impl1 {}");
createClass(() -> filer.createClassFile("impl.Impl2"), "impl.Impl2", "package impl; class Impl2 {}");
createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "impl", "Impl3"), "impl.Impl3", "");
doReadResource(() -> filer.getResource(StandardLocation.SOURCE_PATH, "impl", "resource"), "3");
createSource(() -> filer.createSourceFile("m1/impl.Impl4"), "impl.Impl4", "package impl; class Impl4 {}");
createClass(() -> filer.createClassFile("m1/impl.Impl5"), "impl.Impl5", "package impl; class Impl5 {}");
createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "m1/impl", "Impl6"), "impl.Impl6", "");
doReadResource(() -> filer.getResource(StandardLocation.SOURCE_PATH, "m1/impl", "resource"), "3");
TypeElement jlObject = processingEnv.getElementUtils().getTypeElement("java.lang.Object");
//"broken" originating element:
createSource(() -> filer.createSourceFile("impl.Impl7", jlObject), "impl.Impl7", "package impl; class Impl7 {}");
createClass(() -> filer.createClassFile("impl.Impl8", jlObject), "impl.Impl8", "package impl; class Impl8 {}");
createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "impl", "Impl9", jlObject), "impl.Impl9", "");
//must not generate to unnamed package:
expectFilerException(() -> filer.createSourceFile("Fail"));
expectFilerException(() -> filer.createClassFile("Fail"));
expectFilerException(() -> filer.createSourceFile("m1/Fail"));
expectFilerException(() -> filer.createClassFile("m1/Fail"));
//cannot generate resources to modules that are not root modules:
expectFilerException(() -> filer.createSourceFile("java.base/fail.Fail"));
expectFilerException(() -> filer.createClassFile("java.base/fail.Fail"));
expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "java.base/fail", "Fail"));
return false;
}
}
@Test
public void testGenerateInUnnamedModeAPI(Path base) throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
Path src = base.resolve("src");
tb.writeJavaFiles(src,
"class T {}");
new JavacTask(tb)
.options("-processor", UnnamedModeAPITestAP.class.getName(),
"-sourcepath", src.toString())
.outdir(classes)
.files(findJavaFiles(src))
.run()
.writeAll();
assertFileExists(classes, "Impl1.class");
assertFileExists(classes, "Impl2.class");
}
@Test
public void testGenerateInNoModeAPI(Path base) throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
Path src = base.resolve("src");
tb.writeJavaFiles(src,
"class T {}");
new JavacTask(tb)
.options("-processor", UnnamedModeAPITestAP.class.getName(),
"-source", "8", "-target", "8",
"-sourcepath", src.toString())
.outdir(classes)
.files(findJavaFiles(src))
.run()
.writeAll();
assertFileExists(classes, "Impl1.class");
assertFileExists(classes, "Impl2.class");
}
@SupportedAnnotationTypes("*")
public static final class UnnamedModeAPITestAP extends GeneratingAP {
int round;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (round++ != 0)
return false;
Filer filer = processingEnv.getFiler();
//must not generate to unnamed package:
createSource(() -> filer.createSourceFile("Impl1"), "Impl1", "class Impl1 {}");
createClass(() -> filer.createClassFile("Impl2"), "Impl2", "class Impl2 {}");
return false;
}
}
@Test
public void testDisambiguateAnnotations(Path base) throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
Path src = base.resolve("src");
Path m1 = src.resolve("m1");
tb.writeJavaFiles(m1,
"module m1 { exports api; }",
"package api; public @interface A {}",
"package api; public @interface B {}");
Path m2 = src.resolve("m2");
tb.writeJavaFiles(m2,
"module m2 { exports api; }",
"package api; public @interface A {}",
"package api; public @interface B {}");
Path m3 = src.resolve("m3");
tb.writeJavaFiles(m3,
"module m3 { requires m1; }",
"package impl; import api.*; @A @B public class T {}");
Path m4 = src.resolve("m4");
tb.writeJavaFiles(m4,
"module m4 { requires m2; }",
"package impl; import api.*; @A @B public class T {}");
List<String> log;
List<String> expected;
log = new JavacTask(tb)
.options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
"--module-source-path", src.toString(),
"-m", "m1,m2")
.outdir(classes)
.run()
.writeAll()
.getOutputLines(OutputKind.STDERR);
expected = Arrays.asList("");
if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log);
}
log = new JavacTask(tb)
.options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
"--module-source-path", src.toString(),
"-m", "m3")
.outdir(classes)
.run()
.writeAll()
.getOutputLines(OutputKind.STDERR);
expected = Arrays.asList("SelectAnnotationBTestAP",
"SelectAnnotationBTestAP");
if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log);
}
log = new JavacTask(tb)
.options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
"--module-source-path", src.toString(),
"-m", "m4")
.outdir(classes)
.run()
.writeAll()
.getOutputLines(OutputKind.STDERR);
expected = Arrays.asList("SelectAnnotationATestAP",
"SelectAnnotationBTestAP",
"SelectAnnotationATestAP",
"SelectAnnotationBTestAP");
if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log);
}
}
@SupportedAnnotationTypes("m2/api.A")
public static final class SelectAnnotationATestAP extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.err.println("SelectAnnotationATestAP");
return false;
}
}
@SupportedAnnotationTypes("api.B")
public static final class SelectAnnotationBTestAP extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.err.println("SelectAnnotationBTestAP");
return false;
}
}
private static void writeFile(String content, Path base, String... pathElements) throws IOException {
Path file = resolveFile(base, pathElements);
Files.createDirectories(file.getParent());
try (Writer out = Files.newBufferedWriter(file)) {
out.append(content);
}
}
private static void assertNonNull(String msg, Object val) {
if (val == null) {
throw new AssertionError(msg);
@ -392,4 +1030,22 @@ public class AnnotationProcessing extends ModuleTestBase {
}
}
private static void assertFileExists(Path base, String... pathElements) {
Path file = resolveFile(base, pathElements);
if (!Files.exists(file)) {
throw new AssertionError("Expected file: " + file + " exist, but it does not.");
}
}
static Path resolveFile(Path base, String... pathElements) {
Path file = base;
for (String el : pathElements) {
file = file.resolve(el);
}
return file;
}
}

View File

@ -1,4 +1,4 @@
Processor Test matches [java.lang.Override] and returns true.
Processor Test matches [java.base/java.lang.Override] and returns true.
- compiler.note.proc.messager: round 1
Processor Test matches [] and returns true.
- compiler.note.proc.messager: round 2