JpsJavacFileManager: add filtering for ZipEntries according to the requested file kind (IDEA-206390)

This commit is contained in:
Eugene Zhuravlev
2019-02-11 18:19:51 +01:00
parent 647c745eb7
commit 635600fd60
5 changed files with 154 additions and 18 deletions

View File

@@ -20,6 +20,7 @@
<orderEntry type="module" module-name="intellij.spellchecker" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.platform.jps.model.impl" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.testExtensions" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.jps.build.javac.rt" scope="TEST" />
</component>
<component name="copyright">
<Base>

View File

@@ -5,12 +5,19 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.io.Compressor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.javac.JpsJavacFileManager;
import org.jetbrains.jps.javac.OutputFileObject;
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import static com.intellij.util.io.TestFileSystemItem.fs;
@@ -41,6 +48,64 @@ public class JavaCompilerBasicTest extends BaseCompilerTestCase {
assertOutput(module, fs().file("B.class"));
}
public void testFilterSourcesFromLibraries() throws IOException {
final VirtualFile libJavaFile = createFile("lib/ppp/B.java", "package ppp; public class B {}");
final VirtualFile libClsFile = createFile("lib/ppp/B.class", "package ppp; public class B {}");
final File jarFile = new File(libJavaFile.getParent().getParent().getPath(), "lib.jar");
try (Compressor.Jar jar = new Compressor.Jar(jarFile)) {
jar.addFile("ppp/B.java", new File(libJavaFile.getPath()));
jar.addFile("ppp/B.class", new File(libClsFile.getPath()));
}
final VirtualFile srcFile = createFile("src/A.java", "import ppp.B; public class A { B b; }");
final StandardJavaFileManager stdFileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(new DiagnosticListener<JavaFileObject>() {
@Override
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
}
}, Locale.US, null);
final JpsJavacFileManager fileManager = new JpsJavacFileManager(new JpsJavacFileManager.Context() {
@Override
public boolean isCanceled() {
return false;
}
@NotNull
@Override
public StandardJavaFileManager getStandardFileManager() {
return stdFileManager;
}
@Override
public void consumeOutputFile(@NotNull OutputFileObject obj) {
}
@Override
public void reportMessage(Diagnostic.Kind kind, String message) {
}
}, true, Collections.emptyList());
fileManager.setLocation(StandardLocation.CLASS_PATH, Collections.singleton(jarFile));
fileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.emptyList());
final File src = new File(srcFile.getPath());
final Iterable<? extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(Collections.singleton(src));
final Iterator<? extends JavaFileObject> it = sources.iterator();
assertTrue(it.hasNext());
final JavaFileObject srcFileObject = it.next();
assertFalse(it.hasNext());
assertEquals(JavaFileObject.Kind.SOURCE, srcFileObject.getKind());
assertEquals(src.toURI().getPath(), srcFileObject.toUri().getPath());
final Iterable<JavaFileObject> libClasses = fileManager.list(StandardLocation.CLASS_PATH, "ppp", Collections.singleton(JavaFileObject.Kind.CLASS), false);
final Iterator<JavaFileObject> clsIterator = libClasses.iterator();
assertTrue(clsIterator.hasNext());
final JavaFileObject aClass = clsIterator.next();
assertEquals(JavaFileObject.Kind.CLASS, aClass.getKind());
assertEquals(jarFile.toURI().getPath() + "!/ppp/B.class", aClass.toUri().getPath());
assertFalse(clsIterator.hasNext());
}
public void testSymlinksInSources() throws IOException {
final VirtualFile file = createFile("src/A.java", "public class A {}");

View File

@@ -160,6 +160,12 @@ class DefaultFileOperations implements FileOperations {
private final ZipFile myZip;
private final Map<String, Collection<ZipEntry>> myPaths = new THashMap<String, Collection<ZipEntry>>();
private final Function<ZipEntry, JavaFileObject> myToFileObjectConverter;
private static final FileObjectKindFilter<ZipEntry> ourEntryFilter = new FileObjectKindFilter<ZipEntry>(new Function<ZipEntry, String>() {
@Override
public String fun(ZipEntry zipEntry) {
return zipEntry.getName();
}
});
ZipArchive(final File root, final String encodingName) throws IOException {
myZip = new ZipFile(root, ZipFile.OPEN_READ);
@@ -184,7 +190,6 @@ class DefaultFileOperations implements FileOperations {
};
}
@NotNull
@Override
public Iterable<JavaFileObject> list(final String relPath, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException{
@@ -206,9 +211,9 @@ class DefaultFileOperations implements FileOperations {
}
}
}
return JpsJavacFileManager.convert(JpsJavacFileManager.merge(allChildren), myToFileObjectConverter);
return JpsJavacFileManager.convert(JpsJavacFileManager.filter(JpsJavacFileManager.merge(allChildren), ourEntryFilter.getFor(kinds)), myToFileObjectConverter);
}
return JpsJavacFileManager.convert(entries, myToFileObjectConverter);
return JpsJavacFileManager.convert(JpsJavacFileManager.filter(entries, ourEntryFilter.getFor(kinds)), myToFileObjectConverter);
}
@Override

View File

@@ -0,0 +1,65 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.javac;
import com.intellij.util.BooleanFunction;
import com.intellij.util.Function;
import javax.tools.JavaFileObject;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: 11-Feb-19
*/
public class FileObjectKindFilter<T> {
private final Function<? super T, String> myToNameConverter;
private final Map<JavaFileObject.Kind, BooleanFunction<T>> myFilterMap;
public FileObjectKindFilter(Function<? super T, String> toNameConverter) {
myToNameConverter = toNameConverter;
final EnumMap<JavaFileObject.Kind, BooleanFunction<T>> filterMap = new EnumMap<JavaFileObject.Kind, BooleanFunction<T>>(JavaFileObject.Kind.class);
for (final JavaFileObject.Kind kind : JavaFileObject.Kind.values()) {
if (kind == JavaFileObject.Kind.OTHER) {
filterMap.put(kind, new BooleanFunction<T>() {
@Override
public boolean fun(T data) {
return JpsFileObject.findKind(myToNameConverter.fun(data)) == JavaFileObject.Kind.OTHER;
}
});
}
else {
filterMap.put(kind, new BooleanFunction<T>() {
@Override
public boolean fun(T data) {
return myToNameConverter.fun(data).endsWith(kind.extension);
}
});
}
}
myFilterMap = Collections.unmodifiableMap(filterMap);
}
public BooleanFunction<T> getFor(final Set<JavaFileObject.Kind> kinds) {
// optimization for a single-element collection
final Iterator<JavaFileObject.Kind> it = kinds.iterator();
if (it.hasNext()) {
final JavaFileObject.Kind kind = it.next();
if (!it.hasNext()) {
return myFilterMap.get(kind);
}
}
// OR-filter, quite rare case
return new BooleanFunction<T>() {
@Override
public boolean fun(T data) {
for (JavaFileObject.Kind kind : kinds) {
if (myFilterMap.get(kind).fun(data)) {
return true;
}
}
return false;
}
};
}
}

View File

@@ -37,6 +37,12 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
StandardLocation.SOURCE_PATH,
StandardLocation.ANNOTATION_PROCESSOR_PATH
);
private static final FileObjectKindFilter<File> ourKindFilter = new FileObjectKindFilter<File>(new Function<File, String>() {
@Override
public String fun(File file) {
return file.getName();
}
});
private final Context myContext;
private final boolean myJavacBefore9;
private final Collection<JavaSourceTransformer> mySourceTransformers;
@@ -242,7 +248,7 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
return name.toString().replace('.', File.separatorChar);
}
interface Context {
public interface Context {
boolean isCanceled();
@NotNull
@@ -430,20 +436,14 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
else {
// is a directory or does not exist
final File dir = new File(root, packageName.replace('.', '/'));
final BooleanFunction<File> filter = recurse?
new BooleanFunction<File>() {
@Override
public boolean fun(File file) {
return kinds.contains(getKind(file.getName()));
}
}:
new BooleanFunction<File>() {
final boolean acceptUnknownFiles = kinds.contains(JavaFileObject.Kind.OTHER);
@Override
public boolean fun(File file) {
return kinds.contains(getKind(file.getName())) && (!acceptUnknownFiles || myFileOperations.isFile(file));
}
};
final BooleanFunction<File> kindsFilter = ourKindFilter.getFor(kinds);
final boolean acceptUnknownFiles = kinds.contains(JavaFileObject.Kind.OTHER);
final BooleanFunction<File> filter = recurse || !acceptUnknownFiles? kindsFilter : new BooleanFunction<File>() {
@Override
public boolean fun(File file) {
return kindsFilter.fun(dir) && myFileOperations.isFile(file);
}
};
result.add(convert(filter(myFileOperations.listFiles(dir, recurse), filter), myFileToInputFileObjectConverter));
}
}