8272234: Pass originating elements from Filer to JavaFileManager
Reviewed-by: jjg
This commit is contained in:
parent
0fbd2713f1
commit
890700320a
src
java.compiler/share/classes/javax/tools
jdk.compiler/share/classes/com/sun/tools/javac
test/langtools/tools/javac
@ -26,6 +26,7 @@
|
||||
package javax.tools;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.ServiceLoader;
|
||||
@ -144,6 +145,39 @@ public class ForwardingJavaFileManager<M extends JavaFileManager> implements Jav
|
||||
return fileManager.getJavaFileForOutput(location, className, kind, sibling);
|
||||
}
|
||||
|
||||
/**{@inheritDoc}
|
||||
*
|
||||
* @implSpec If the subclass of the {@code ForwardingJavaFileManager} overrides the
|
||||
* {@link #getJavaFileForOutput} method, this method will delegate to it as per the
|
||||
* general contract of {@link JavaFileManager#getJavaFileForOutputForOriginatingFiles}.
|
||||
* If the subclass does not override the method, the call will be delegated to the
|
||||
* {@code fileManager}.
|
||||
*
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @throws IllegalStateException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location,
|
||||
String className,
|
||||
Kind kind,
|
||||
FileObject... originatingFiles) throws IOException {
|
||||
try {
|
||||
Method delegate = getClass().getMethod("getJavaFileForOutput",
|
||||
Location.class, String.class,
|
||||
Kind.class, FileObject.class);
|
||||
if (delegate.getDeclaringClass() == ForwardingJavaFileManager.class) {
|
||||
return fileManager.getJavaFileForOutputForOriginatingFiles(location, className,
|
||||
kind, originatingFiles);
|
||||
} else {
|
||||
return JavaFileManager.super
|
||||
.getJavaFileForOutputForOriginatingFiles(location, className,
|
||||
kind, originatingFiles);
|
||||
}
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new InternalError("This should never happen.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @throws IllegalStateException {@inheritDoc}
|
||||
@ -171,6 +205,39 @@ public class ForwardingJavaFileManager<M extends JavaFileManager> implements Jav
|
||||
return fileManager.getFileForOutput(location, packageName, relativeName, sibling);
|
||||
}
|
||||
|
||||
/**{@inheritDoc}
|
||||
*
|
||||
* @implSpec If the subclass of the {@code ForwardingJavaFileManager} overrides the
|
||||
* {@link #getFileForOutput} method, this method will delegate to it as per the
|
||||
* general contract of {@link JavaFileManager#getFileForOutputForOriginatingFiles}.
|
||||
* If the subclass does not override the method, the call will be delegated to the
|
||||
* {@code fileManager}.
|
||||
*
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @throws IllegalStateException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public FileObject getFileForOutputForOriginatingFiles(Location location,
|
||||
String packageName,
|
||||
String relativeName,
|
||||
FileObject... originatingFiles) throws IOException {
|
||||
try {
|
||||
Method delegate = getClass().getMethod("getFileForOutput",
|
||||
Location.class, String.class,
|
||||
String.class, FileObject.class);
|
||||
if (delegate.getDeclaringClass() == ForwardingJavaFileManager.class) {
|
||||
return fileManager.getFileForOutputForOriginatingFiles(location, packageName,
|
||||
relativeName, originatingFiles);
|
||||
} else {
|
||||
return JavaFileManager.super
|
||||
.getFileForOutputForOriginatingFiles(location, packageName,
|
||||
relativeName, originatingFiles);
|
||||
}
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new InternalError("This should never happen.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
fileManager.flush();
|
||||
|
@ -31,6 +31,8 @@ import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.Filer;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import static javax.tools.JavaFileObject.Kind;
|
||||
|
||||
@ -338,6 +340,51 @@ public interface JavaFileManager extends Closeable, Flushable, OptionChecker {
|
||||
FileObject sibling)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a {@linkplain JavaFileObject file object} for output
|
||||
* representing the specified class of the specified kind in the
|
||||
* given package-oriented location.
|
||||
*
|
||||
* <p>The provided {@code originatingFiles} represent files that
|
||||
* were, in an unspecified way, used to create the content of
|
||||
* the file created by this method. See {@code originatingElements}
|
||||
* in {@link Filer#createSourceFile}. {@link Elements#getFileObjectOf}
|
||||
* may be used to convert the {@code Element}s to {@code FileObject}s.
|
||||
*
|
||||
* @implSpec The default implementation calls
|
||||
* {@link #getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject) }
|
||||
* with the first element of the {@code originatingFiles}, if any, as a {@code sibling}.
|
||||
*
|
||||
* @param location a package-oriented location
|
||||
* @param className the name of a class
|
||||
* @param kind the kind of file, must be one of {@link
|
||||
* JavaFileObject.Kind#SOURCE SOURCE} or {@link
|
||||
* JavaFileObject.Kind#CLASS CLASS}
|
||||
* @param originatingFiles the files which are contributing to this newly created file;
|
||||
* {@code null} is equivalent to empty {@code originatingFiles},
|
||||
* meaning no known originating files
|
||||
* @return a file object for output
|
||||
* @throws IllegalArgumentException if sibling is not known to
|
||||
* this file manager, or if the location is not known to this file
|
||||
* manager and the file manager does not support unknown
|
||||
* locations, or if the kind is not valid, or if the location is
|
||||
* not an output location
|
||||
* @throws IOException if an I/O error occurred, or if {@link
|
||||
* #close} has been called and this file manager cannot be
|
||||
* reopened
|
||||
* @throws IllegalStateException {@link #close} has been called
|
||||
* and this file manager cannot be reopened
|
||||
* @since 18
|
||||
* @see Filer#createSourceFile
|
||||
*/
|
||||
default JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location,
|
||||
String className,
|
||||
Kind kind,
|
||||
FileObject... originatingFiles)
|
||||
throws IOException {
|
||||
return getJavaFileForOutput(location, className, kind, siblingFrom(originatingFiles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@linkplain FileObject file object} for input
|
||||
* representing the specified <a href="JavaFileManager.html#relative_name">relative
|
||||
@ -430,6 +477,59 @@ public interface JavaFileManager extends Closeable, Flushable, OptionChecker {
|
||||
FileObject sibling)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a {@linkplain FileObject file object} for output
|
||||
* representing the specified <a href="JavaFileManager.html#relative_name">relative
|
||||
* name</a> in the specified package in the given location.
|
||||
*
|
||||
* <p>The provided {@code originatingFiles} represent files that
|
||||
* were, in an unspecified way, used to create the content of
|
||||
* the file created by this method. See {@code originatingElements}
|
||||
* in {@link Filer#createResource}. {@link Elements#getFileObjectOf}
|
||||
* may be used to convert the {@code Element}s to {@code FileObject}s.
|
||||
*
|
||||
* <p>If the returned object represents a {@linkplain
|
||||
* JavaFileObject.Kind#SOURCE source} or {@linkplain
|
||||
* JavaFileObject.Kind#CLASS class} file, it must be an instance
|
||||
* of {@link JavaFileObject}.
|
||||
*
|
||||
* <p>Informally, the file object returned by this method is
|
||||
* located in the concatenation of the location, package name, and
|
||||
* relative name or in a location inferred from the {@code originatingFiles}.
|
||||
* See {@link #getFileForInput getFileForInput} for an example.
|
||||
*
|
||||
* @implSpec The default implementation calls
|
||||
* {@link #getFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, java.lang.String, javax.tools.FileObject) }
|
||||
* with the first element of the {@code originatingFiles}, if any, as a {@code sibling}.
|
||||
*
|
||||
* @param location an output location
|
||||
* @param packageName a package name
|
||||
* @param relativeName a relative name
|
||||
* @param originatingFiles the files which are contributing to this newly created file;
|
||||
* {@code null} is equivalent to empty {@code originatingFiles},
|
||||
* meaning no known originating files
|
||||
* @return a file object
|
||||
* @throws IllegalArgumentException if sibling is not known to
|
||||
* this file manager, or if the location is not known to this file
|
||||
* manager and the file manager does not support unknown
|
||||
* locations, or if {@code relativeName} is not valid,
|
||||
* or if the location is not an output location
|
||||
* @throws IOException if an I/O error occurred, or if {@link
|
||||
* #close} has been called and this file manager cannot be
|
||||
* reopened
|
||||
* @throws IllegalStateException if {@link #close} has been called
|
||||
* and this file manager cannot be reopened
|
||||
* @since 18
|
||||
* @see Filer#createResource
|
||||
*/
|
||||
default FileObject getFileForOutputForOriginatingFiles(Location location,
|
||||
String packageName,
|
||||
String relativeName,
|
||||
FileObject... originatingFiles)
|
||||
throws IOException {
|
||||
return getFileForOutput(location, packageName, relativeName, siblingFrom(originatingFiles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes any resources opened for output by this file manager
|
||||
* directly or indirectly. Flushing a closed file manager has no
|
||||
@ -594,4 +694,8 @@ public interface JavaFileManager extends Closeable, Flushable, OptionChecker {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static FileObject siblingFrom(FileObject[] originatingFiles) {
|
||||
return originatingFiles != null && originatingFiles.length > 0 ? originatingFiles[0] : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -305,6 +305,17 @@ public class ClientCodeWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER)
|
||||
public JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location, String className, Kind kind, FileObject... originatingFiles) throws IOException {
|
||||
try {
|
||||
return wrap(clientJavaFileManager.getJavaFileForOutputForOriginatingFiles(location, className, kind, originatingFiles));
|
||||
} catch (ClientCodeException e) {
|
||||
throw e;
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw new ClientCodeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER)
|
||||
public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
|
||||
try {
|
||||
@ -316,6 +327,17 @@ public class ClientCodeWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER)
|
||||
public FileObject getFileForOutputForOriginatingFiles(Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException {
|
||||
try {
|
||||
return wrap(clientJavaFileManager.getFileForOutputForOriginatingFiles(location, packageName, relativeName, originatingFiles));
|
||||
} catch (ClientCodeException e) {
|
||||
throw e;
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw new ClientCodeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER)
|
||||
public boolean contains(Location location, FileObject file) throws IOException {
|
||||
try {
|
||||
|
@ -115,6 +115,12 @@ public class DelegatingJavaFileManager implements JavaFileManager {
|
||||
return delegate(location).getJavaFileForOutput(location, className, kind, sibling);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location, String className, Kind kind,
|
||||
FileObject... originatingFiles) throws IOException {
|
||||
return delegate(location).getJavaFileForOutputForOriginatingFiles(location, className, kind, originatingFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileObject getFileForInput(Location location, String packageName,
|
||||
String relativeName) throws IOException {
|
||||
@ -127,6 +133,12 @@ public class DelegatingJavaFileManager implements JavaFileManager {
|
||||
return delegate(location).getFileForOutput(location, packageName, relativeName, sibling);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileObject getFileForOutputForOriginatingFiles(Location location, String packageName, String relativeName,
|
||||
FileObject... originatingFiles) throws IOException {
|
||||
return delegate(location).getFileForOutputForOriginatingFiles(location, packageName, relativeName, originatingFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
releaseFM.flush();
|
||||
|
@ -36,6 +36,7 @@ import java.io.FilterWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
|
||||
@ -62,8 +63,10 @@ import com.sun.tools.javac.util.*;
|
||||
import com.sun.tools.javac.util.DefinedBy.Api;
|
||||
|
||||
import static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.PackageSymbol;
|
||||
import com.sun.tools.javac.main.Option;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The FilerImplementation class must maintain a number of
|
||||
@ -432,14 +435,14 @@ public class JavacFiler implements Filer, Closeable {
|
||||
public JavaFileObject createSourceFile(CharSequence nameAndModule,
|
||||
Element... originatingElements) throws IOException {
|
||||
Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule);
|
||||
return createSourceOrClassFile(moduleAndClass.fst, true, moduleAndClass.snd);
|
||||
return createSourceOrClassFile(moduleAndClass.fst, true, moduleAndClass.snd, originatingElements);
|
||||
}
|
||||
|
||||
@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);
|
||||
return createSourceOrClassFile(moduleAndClass.fst, false, moduleAndClass.snd, originatingElements);
|
||||
}
|
||||
|
||||
private Pair<ModuleSymbol, String> checkOrInferModule(CharSequence moduleAndPkg) throws FilerException {
|
||||
@ -483,7 +486,7 @@ public class JavacFiler implements Filer, Closeable {
|
||||
return Pair.of(explicitModule, pkg);
|
||||
}
|
||||
|
||||
private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name) throws IOException {
|
||||
private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name, Element... originatingElements) throws IOException {
|
||||
Assert.checkNonNull(mod);
|
||||
|
||||
if (lint) {
|
||||
@ -506,7 +509,7 @@ public class JavacFiler implements Filer, Closeable {
|
||||
JavaFileObject.Kind.CLASS);
|
||||
|
||||
JavaFileObject fileObject =
|
||||
fileManager.getJavaFileForOutput(loc, name, kind, null);
|
||||
fileManager.getJavaFileForOutputForOriginatingFiles(loc, name, kind, originatingFiles(originatingElements));
|
||||
checkFileReopening(fileObject, true);
|
||||
|
||||
if (lastRound)
|
||||
@ -521,6 +524,17 @@ public class JavacFiler implements Filer, Closeable {
|
||||
return new FilerOutputJavaFileObject(mod, name, fileObject);
|
||||
}
|
||||
|
||||
private JavaFileObject[] originatingFiles(Element[] originatingElements) {
|
||||
if (originatingElements == null) {
|
||||
return new JavaFileObject[0];
|
||||
}
|
||||
JavaFileObject[] originatingFiles = Stream.of(originatingElements)
|
||||
.map(elementUtils::getFileObjectOf)
|
||||
.filter(fo -> fo != null)
|
||||
.toArray(s -> new JavaFileObject[s]);
|
||||
return originatingFiles;
|
||||
}
|
||||
|
||||
@Override @DefinedBy(Api.ANNOTATION_PROCESSING)
|
||||
public FileObject createResource(JavaFileManager.Location location,
|
||||
CharSequence moduleAndPkg,
|
||||
@ -538,8 +552,8 @@ public class JavacFiler implements Filer, Closeable {
|
||||
checkName(strPkg);
|
||||
|
||||
FileObject fileObject =
|
||||
fileManager.getFileForOutput(location, strPkg,
|
||||
relativeName.toString(), null);
|
||||
fileManager.getFileForOutputForOriginatingFiles(location, strPkg,
|
||||
relativeName.toString(), originatingFiles(originatingElements));
|
||||
checkFileReopening(fileObject, true);
|
||||
|
||||
if (fileObject instanceof JavaFileObject javaFileObject)
|
||||
@ -579,10 +593,9 @@ public class JavacFiler implements Filer, Closeable {
|
||||
// invocation.
|
||||
FileObject fileObject;
|
||||
if (location.isOutputLocation()) {
|
||||
fileObject = fileManager.getFileForOutput(location,
|
||||
fileObject = fileManager.getFileForOutputForOriginatingFiles(location,
|
||||
pkg,
|
||||
relativeName.toString(),
|
||||
null);
|
||||
relativeName.toString());
|
||||
} else {
|
||||
fileObject = fileManager.getFileForInput(location,
|
||||
pkg,
|
||||
|
@ -62,7 +62,8 @@ public class TestClientCodeWrapper extends JavacTestingAbstractProcessor {
|
||||
defaultFileManager = fm;
|
||||
|
||||
for (Method m: getMethodsExcept(JavaFileManager.class,
|
||||
"close", "getJavaFileForInput", "getLocationForModule", "getServiceLoader", "contains")) {
|
||||
"close", "getJavaFileForInput", "getLocationForModule", "getServiceLoader",
|
||||
"contains", "getFileForOutput", "siblingFrom")) {
|
||||
test(m);
|
||||
}
|
||||
|
||||
@ -370,6 +371,12 @@ public class TestClientCodeWrapper extends JavacTestingAbstractProcessor {
|
||||
return wrap(super.getJavaFileForOutput(location, className, kind, sibling));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location, String className, Kind kind, FileObject... originatingFiles) throws IOException {
|
||||
throwUserExceptionIfNeeded(fileManagerMethod, "getJavaFileForOutputForOriginatingFiles");
|
||||
return wrap(super.getJavaFileForOutputForOriginatingFiles(location, className, kind, originatingFiles));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
|
||||
throwUserExceptionIfNeeded(fileManagerMethod, "getFileForInput");
|
||||
@ -382,6 +389,12 @@ public class TestClientCodeWrapper extends JavacTestingAbstractProcessor {
|
||||
return wrap(super.getFileForOutput(location, packageName, relativeName, sibling));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileObject getFileForOutputForOriginatingFiles(Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException {
|
||||
throwUserExceptionIfNeeded(fileManagerMethod, "getFileForOutputForOriginatingFiles");
|
||||
return wrap(super.getFileForOutputForOriginatingFiles(location, packageName, relativeName, originatingFiles));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
throwUserExceptionIfNeeded(fileManagerMethod, "flush");
|
||||
|
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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 8272234
|
||||
* @summary Verify proper handling of originating elements in javac's Filer.
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* @build toolbox.TestRunner toolbox.ToolBox TestOriginatingElements
|
||||
* @run main TestOriginatingElements
|
||||
*/
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Iterator;
|
||||
import javax.annotation.processing.Filer;
|
||||
import javax.annotation.processing.SupportedOptions;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ModuleElement;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import toolbox.JavacTask;
|
||||
import toolbox.TestRunner;
|
||||
import toolbox.TestRunner.Test;
|
||||
import toolbox.ToolBox;
|
||||
import toolbox.ToolBox.MemoryFileManager;
|
||||
|
||||
public class TestOriginatingElements extends TestRunner {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
new TestOriginatingElements().runTests(m -> new Object[] { Paths.get(m.getName()) });
|
||||
}
|
||||
|
||||
private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
private final ToolBox tb = new ToolBox();
|
||||
|
||||
public TestOriginatingElements() {
|
||||
super(System.err);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOriginatingElements(Path outerBase) throws Exception {
|
||||
Path libSrc = outerBase.resolve("lib-src");
|
||||
tb.writeJavaFiles(libSrc,
|
||||
"""
|
||||
module lib { exports lib1; exports lib2; }
|
||||
""",
|
||||
"""
|
||||
package lib1;
|
||||
public @interface A {
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package lib2;
|
||||
public class Lib {
|
||||
}
|
||||
""");
|
||||
tb.writeFile(libSrc.resolve("lib1/package-info.java"), "@A package lib1;");
|
||||
Path libClasses = outerBase.resolve("lib-classes");
|
||||
Path libClassesModule = libClasses.resolve("lib");
|
||||
Files.createDirectories(libClassesModule);
|
||||
|
||||
new JavacTask(tb)
|
||||
.files(tb.findJavaFiles(libSrc))
|
||||
.outdir(libClassesModule)
|
||||
.run();
|
||||
|
||||
Path src = outerBase.resolve("src");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
module m {}
|
||||
""",
|
||||
"""
|
||||
package t;
|
||||
public class T1 {
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package t;
|
||||
public class T2 {
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package t;
|
||||
public class T3 {
|
||||
}
|
||||
""");
|
||||
tb.writeFile(src.resolve("p/package-info.java"), "package p;");
|
||||
Path classes = outerBase.resolve("classes");
|
||||
Files.createDirectories(classes);
|
||||
try (StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null)) {
|
||||
List<String> testOutput = new ArrayList<>();
|
||||
JavaFileManager fm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location,
|
||||
String className,
|
||||
JavaFileObject.Kind kind,
|
||||
FileObject... originatingFiles) throws IOException {
|
||||
List.of(originatingFiles)
|
||||
.stream()
|
||||
.map(fo -> getInfo(fo))
|
||||
.forEach(testOutput::add);
|
||||
return super.getJavaFileForOutputForOriginatingFiles(location, className, kind, originatingFiles);
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutputForOriginatingFiles(Location location,
|
||||
String packageName,
|
||||
String relativeName,
|
||||
FileObject... originatingFiles) throws IOException {
|
||||
List.of(originatingFiles)
|
||||
.stream()
|
||||
.map(fo -> getInfo(fo))
|
||||
.forEach(testOutput::add);
|
||||
return super.getFileForOutputForOriginatingFiles(location, packageName, relativeName, originatingFiles);
|
||||
}
|
||||
private String getInfo(FileObject fo) {
|
||||
try {
|
||||
JavaFileObject jfo = (JavaFileObject) fo; //the test only expects JavaFileObjects here:
|
||||
JavaFileManager.Location location = jfo.getKind() == JavaFileObject.Kind.SOURCE
|
||||
? StandardLocation.SOURCE_PATH
|
||||
: sjfm.getLocationForModule(StandardLocation.SYSTEM_MODULES, "java.base");
|
||||
String binaryName = inferBinaryName(location, jfo);
|
||||
return binaryName + "(" + jfo.getKind() + ")";
|
||||
} catch (IOException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
String generatedData;
|
||||
try (MemoryFileManager mfm = new MemoryFileManager(sjfm)) {
|
||||
compiler.getTask(null, mfm, null, null, null,
|
||||
List.of(new ToolBox.JavaSource("package test; public class Generated2 {}")))
|
||||
.call();
|
||||
generatedData =
|
||||
Base64.getEncoder().encodeToString(mfm.getFileBytes(StandardLocation.CLASS_OUTPUT, "test.Generated2"));
|
||||
}
|
||||
List<String> options = List.of("-sourcepath", src.toString(),
|
||||
"-processor", "TestOriginatingElements$P",
|
||||
"-processorpath", System.getProperty("test.class.path"),
|
||||
"--module-path", libClasses.toString(),
|
||||
"--add-modules", "lib",
|
||||
"-d", classes.toString(),
|
||||
"-AgeneratedData=" + generatedData);
|
||||
ToolProvider.getSystemJavaCompiler()
|
||||
.getTask(null, fm, null, options, null, sjfm.getJavaFileObjects(tb.findJavaFiles(src)))
|
||||
.call();
|
||||
List<String> expectedOriginatingFiles = List.of("t.T1(SOURCE)",
|
||||
"java.lang.String(CLASS)",
|
||||
"p.package-info(SOURCE)",
|
||||
"lib1.package-info(CLASS)",
|
||||
"module-info(SOURCE)",
|
||||
"module-info(CLASS)",
|
||||
"t.T2(SOURCE)",
|
||||
"java.lang.CharSequence(CLASS)",
|
||||
"p.package-info(SOURCE)",
|
||||
"lib1.package-info(CLASS)",
|
||||
"module-info(SOURCE)",
|
||||
"module-info(CLASS)",
|
||||
"t.T3(SOURCE)",
|
||||
"java.lang.Exception(CLASS)",
|
||||
"p.package-info(SOURCE)",
|
||||
"lib1.package-info(CLASS)",
|
||||
"module-info(SOURCE)",
|
||||
"module-info(CLASS)");
|
||||
assertEquals(expectedOriginatingFiles, testOutput);
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SupportedAnnotationTypes("*")
|
||||
@SupportedOptions("generatedData")
|
||||
public static class P extends AbstractProcessor {
|
||||
int round;
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
if (round++ == 0) {
|
||||
Elements elems = processingEnv.getElementUtils();
|
||||
ModuleElement mdl = elems.getModuleElement("m");
|
||||
ModuleElement java_base = elems.getModuleElement("java.base");
|
||||
PackageElement pack = elems.getPackageElement("p");
|
||||
PackageElement lib1Pack = elems.getPackageElement("lib1");
|
||||
PackageElement lib2Pack = elems.getPackageElement("lib2");
|
||||
Filer filer = processingEnv.getFiler();
|
||||
try {
|
||||
filer.createSourceFile("test.Generated1",
|
||||
element("t.T1"),
|
||||
element("java.lang.String"),
|
||||
pack,
|
||||
lib1Pack,
|
||||
lib2Pack,
|
||||
mdl,
|
||||
java_base).openOutputStream().close();
|
||||
try (OutputStream out = filer.createClassFile("test.Generated2",
|
||||
element("t.T2"),
|
||||
element("java.lang.CharSequence"),
|
||||
pack,
|
||||
lib1Pack,
|
||||
lib2Pack,
|
||||
mdl,
|
||||
java_base).openOutputStream()) {
|
||||
out.write(Base64.getDecoder().decode(processingEnv.getOptions().get("generatedData")));
|
||||
}
|
||||
filer.createResource(StandardLocation.CLASS_OUTPUT,
|
||||
"test",
|
||||
"Generated3.txt",
|
||||
element("t.T3"),
|
||||
element("java.lang.Exception"),
|
||||
pack,
|
||||
lib1Pack,
|
||||
lib2Pack,
|
||||
mdl,
|
||||
java_base).openOutputStream().close();
|
||||
} catch (IOException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Element element(String type) {
|
||||
return processingEnv.getElementUtils().getTypeElement(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latest();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVacuousJavaFileManager(Path outerBase) throws Exception {
|
||||
List<String> log = new ArrayList<>();
|
||||
JavaFileObject expectedOut = new SimpleJavaFileObject(new URI("Out.java"), JavaFileObject.Kind.SOURCE) {};
|
||||
JavaFileManager fm = new MinimalJavaFileManager() {
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
log.add("getJavaFileForOutput(" + location + ", " + className + ", " + kind + ", " + sibling);
|
||||
return expectedOut;
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
|
||||
log.add("getFileForOutput(" + location + ", " + packageName + ", " + relativeName + ", " + sibling);
|
||||
return expectedOut;
|
||||
}
|
||||
};
|
||||
|
||||
FileObject fo1 = new SimpleJavaFileObject(new URI("Test1.java"), JavaFileObject.Kind.SOURCE) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Test1 - FO";
|
||||
}
|
||||
};
|
||||
FileObject fo2 = new SimpleJavaFileObject(new URI("Test2.java"), JavaFileObject.Kind.SOURCE) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Test2 - FO";
|
||||
}
|
||||
};
|
||||
|
||||
assertEquals(expectedOut,
|
||||
fm.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2));
|
||||
assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, Test1 - FO"), log); log.clear();
|
||||
|
||||
assertEquals(expectedOut,
|
||||
fm.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE));
|
||||
assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, null"), log); log.clear();
|
||||
|
||||
assertEquals(expectedOut,
|
||||
fm.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2));
|
||||
assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, Test1 - FO"), log); log.clear();
|
||||
assertEquals(expectedOut,
|
||||
fm.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java"));
|
||||
assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, null"), log); log.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForwardingJavaFileManager(Path outerBase) throws Exception {
|
||||
List<String> log = new ArrayList<>();
|
||||
JavaFileObject expectedOut = new SimpleJavaFileObject(new URI("Out.java"), JavaFileObject.Kind.SOURCE) {};
|
||||
|
||||
FileObject fo1 = new SimpleJavaFileObject(new URI("Test1.java"), JavaFileObject.Kind.SOURCE) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Test1 - FO";
|
||||
}
|
||||
};
|
||||
FileObject fo2 = new SimpleJavaFileObject(new URI("Test2.java"), JavaFileObject.Kind.SOURCE) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Test2 - FO";
|
||||
}
|
||||
};
|
||||
|
||||
JavaFileManager forwardingWithOverride = new ForwardingJavaFileManager<>(new MinimalJavaFileManager() {
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
log.add("getJavaFileForOutput(" + location + ", " + className + ", " + kind + ", " + sibling);
|
||||
return expectedOut;
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutputForOriginatingFiles(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject... originatingFiles) throws IOException {
|
||||
throw new AssertionError("Should not be called.");
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
|
||||
log.add("getFileForOutput(" + location + ", " + packageName + ", " + relativeName + ", " + sibling);
|
||||
return expectedOut;
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutputForOriginatingFiles(JavaFileManager.Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException {
|
||||
throw new AssertionError("Should not be called.");
|
||||
}
|
||||
}) {
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
return super.getJavaFileForOutput(location, className, kind, sibling);
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
|
||||
return super.getFileForOutput(location, packageName, relativeName, sibling);
|
||||
}
|
||||
};
|
||||
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2));
|
||||
assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, Test1 - FO"), log); log.clear();
|
||||
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE));
|
||||
assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, null"), log); log.clear();
|
||||
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2));
|
||||
assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, Test1 - FO"), log); log.clear();
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java"));
|
||||
assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, null"), log); log.clear();
|
||||
|
||||
JavaFileManager forwardingWithOutOverride = new ForwardingJavaFileManager<>(new MinimalJavaFileManager() {
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
throw new AssertionError("Should not be called.");
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutputForOriginatingFiles(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject... originatingFiles) throws IOException {
|
||||
log.add("getJavaFileForOutputForOriginatingFiles(" + location + ", " + className + ", " + kind + ", " + List.of(originatingFiles));
|
||||
return expectedOut;
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
|
||||
throw new AssertionError("Should not be called.");
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutputForOriginatingFiles(JavaFileManager.Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException {
|
||||
log.add("getFileForOutputForOriginatingFiles(" + location + ", " + packageName + ", " + relativeName + ", " + List.of(originatingFiles));
|
||||
return expectedOut;
|
||||
}
|
||||
}) {};
|
||||
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOutOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2));
|
||||
assertEquals(List.of("getJavaFileForOutputForOriginatingFiles(CLASS_OUTPUT, test.Test, SOURCE, [Test1 - FO, Test2 - FO]"), log); log.clear();
|
||||
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOutOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE));
|
||||
assertEquals(List.of("getJavaFileForOutputForOriginatingFiles(CLASS_OUTPUT, test.Test, SOURCE, []"), log); log.clear();
|
||||
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOutOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2));
|
||||
assertEquals(List.of("getFileForOutputForOriginatingFiles(CLASS_OUTPUT, test, Test.java, [Test1 - FO, Test2 - FO]"), log); log.clear();
|
||||
assertEquals(expectedOut,
|
||||
forwardingWithOutOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java"));
|
||||
assertEquals(List.of("getFileForOutputForOriginatingFiles(CLASS_OUTPUT, test, Test.java, []"), log); log.clear();
|
||||
}
|
||||
|
||||
class MinimalJavaFileManager implements JavaFileManager {
|
||||
@Override
|
||||
public ClassLoader getClassLoader(JavaFileManager.Location location) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public boolean isSameFile(FileObject a, FileObject b) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public boolean handleOption(String current, Iterator<String> remaining) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public boolean hasLocation(JavaFileManager.Location location) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relativeName) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
@Override
|
||||
public int isSupportedOption(String option) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
};
|
||||
|
||||
private void assertEquals(Object expected, Object actual) throws AssertionError {
|
||||
if (!expected.equals(actual)) {
|
||||
throw new AssertionError("Unexpected output: " + actual + ", expected: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user