[java] for modular JDK, uses per-module class roots instead of a single one

This commit is contained in:
Roman Shevchenko
2016-07-06 17:45:06 +02:00
parent 9831392b62
commit bbed9b3f2d
9 changed files with 122 additions and 105 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,11 +20,9 @@ import com.intellij.ide.projectView.impl.nodes.PackageElement;
import com.intellij.ide.projectView.impl.nodes.PackageUtil;
import com.intellij.ide.projectView.impl.nodes.ProjectViewDirectoryHelper;
import com.intellij.ide.util.treeView.TreeViewUtil;
import com.intellij.lang.LangBundle;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.impl.DirectoryIndex;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.impl.jrt.JrtFileSystem;
import com.intellij.psi.JavaDirectoryService;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiPackage;
@@ -70,10 +68,6 @@ public class JavaProjectViewDirectoryHelper extends ProjectViewDirectoryHelper {
@Nullable
@Override
public String getNodeName(final ViewSettings settings, final Object parentValue, final PsiDirectory directory) {
if (JrtFileSystem.isRoot(directory.getVirtualFile())) {
return LangBundle.message("jrt.node.short");
}
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory);
PsiPackage parentPackage;
@@ -534,23 +534,24 @@ public class JavaSdkImpl extends JavaSdk {
}
private static List<VirtualFile> findClasses(File file, boolean isJre) {
List<File> roots = JavaSdkUtil.getJdkClassesRoots(file, isJre);
List<String> urls = ContainerUtil.newArrayListWithCapacity(roots.size() + 1);
if (JrtFileSystem.isModularJdk(file.getPath())) {
urls.add(VirtualFileManager.constructUrl(JrtFileSystem.PROTOCOL, FileUtil.toSystemIndependentName(file.getPath()) + JrtFileSystem.SEPARATOR));
}
for (File root : roots) {
urls.add(VfsUtil.getUrlForLibraryRoot(root));
}
List<VirtualFile> result = ContainerUtil.newArrayList();
VirtualFileManager fileManager = VirtualFileManager.getInstance();
List<VirtualFile> result = ContainerUtil.newArrayListWithCapacity(urls.size());
for (String url : urls) {
VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(url);
if (vFile != null) {
result.add(vFile);
String path = file.getPath();
if (JrtFileSystem.isModularJdk(path)) {
String url = VirtualFileManager.constructUrl(JrtFileSystem.PROTOCOL, FileUtil.toSystemIndependentName(path) + JrtFileSystem.SEPARATOR);
for (String module : JrtFileSystem.listModules(path)) {
ContainerUtil.addIfNotNull(result, fileManager.findFileByUrl(url + module));
}
}
for (File root : JavaSdkUtil.getJdkClassesRoots(file, isJre)) {
String url = VfsUtil.getUrlForLibraryRoot(root);
ContainerUtil.addIfNotNull(result, fileManager.findFileByUrl(url));
}
Collections.sort(result, (o1, o2) -> o1.getPath().compareTo(o2.getPath()));
return result;
}
@@ -23,10 +23,12 @@ import com.intellij.notification.Notifications;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -41,20 +43,34 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.intellij.util.containers.ContainerUtil.newTroveMap;
public class JrtFileSystem extends ArchiveFileSystem {
public static final String PROTOCOL = StandardFileSystems.JRT_PROTOCOL;
public static final String PROTOCOL_PREFIX = StandardFileSystems.JRT_PROTOCOL_PREFIX;
public static final String SEPARATOR = JarFileSystem.JAR_SEPARATOR;
private static final boolean SUPPORTED =
SystemInfo.isJavaVersionAtLeast("9") || SystemInfo.isJavaVersionAtLeast("1.8") && !SystemInfo.isJavaVersionAtLeast("1.9");
private static final URI ROOT_URI = URI.create("jrt:/");
private final Map<String, ArchiveHandler> myHandlers = newTroveMap(FileUtil.PATH_HASHING_STRATEGY);
private final AtomicBoolean mySubscribed = new AtomicBoolean(false);
@@ -63,18 +79,26 @@ public class JrtFileSystem extends ArchiveFileSystem {
}
private static void scheduleConfiguredSdkCheck() {
if (isSupported()) return;
MessageBusConnection connection = ApplicationManager.getApplication().getMessageBus().connect();
connection.subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
@Override
public void appStarting(Project project) {
for (Sdk sdk : ProjectJdkTable.getInstance().getSdksOfType(JavaSdk.getInstance())) {
String homePath = sdk.getHomePath();
if (homePath != null && isModularJdk(homePath)) {
String title = LangBundle.message("jrt.not.available.title", sdk.getName());
String message = LangBundle.message("jrt.not.available.message");
Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, title, message, NotificationType.WARNING));
}
Stream.of(sdk.getRootProvider().getUrls(OrderRootType.CLASSES))
.filter(url -> url.startsWith(PROTOCOL_PREFIX))
.findFirst()
.ifPresent(url -> {
if (!isSupported()) {
String title = LangBundle.message("jrt.not.available.title", sdk.getName());
String message = LangBundle.message("jrt.not.available.message");
Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, title, message, NotificationType.WARNING));
}
else if (url.endsWith(SEPARATOR)) {
String title = LangBundle.message("jrt.outdated.title", sdk.getName());
String message = LangBundle.message("jrt.outdated.message");
Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, title, message, NotificationType.WARNING));
}
});
}
connection.disconnect();
}
@@ -204,4 +228,36 @@ public class JrtFileSystem extends ArchiveFileSystem {
public static boolean isRoot(@NotNull VirtualFile file) {
return file.getParent() == null && file.getFileSystem() instanceof JrtFileSystem;
}
public static boolean isModuleRoot(@NotNull VirtualFile file) {
VirtualFile parent = file.getParent();
return parent != null && isRoot(parent);
}
@NotNull
public static List<String> listModules(@NotNull String path) {
try {
Path root = getFileSystem(path).getPath("/modules");
return Files.list(root).map(p -> p.getFileName().toString()).collect(Collectors.toList());
}
catch (IOException e) {
Logger.getInstance(JrtFileSystem.class).debug(e);
return Collections.emptyList();
}
}
static FileSystem getFileSystem(String path) throws IOException {
FileSystem fs;
if (SystemInfo.isJavaVersionAtLeast("9")) {
fs = FileSystems.newFileSystem(ROOT_URI, Collections.singletonMap("java.home", path));
}
else {
File file = new File(path, "jrt-fs.jar");
if (!file.exists()) throw new IOException("Missing provider: " + file);
URL url = file.toURI().toURL();
ClassLoader loader = new URLClassLoader(new URL[]{url}, null);
fs = FileSystems.newFileSystem(ROOT_URI, Collections.emptyMap(), loader);
}
return fs;
}
}
@@ -15,39 +15,21 @@
*/
package com.intellij.openapi.vfs.impl.jrt;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.impl.ArchiveHandler;
import com.intellij.reference.SoftReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.StringInterner;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.Map;
class JrtHandler extends ArchiveHandler {
private static final URI ROOT_URI = URI.create("jrt:/");
private static class JrtEntryInfo extends EntryInfo {
private final String myModule;
public JrtEntryInfo(@NotNull String shortName, @NotNull String module, long length, long timestamp, EntryInfo parent) {
super(shortName, false, length, timestamp, parent);
myModule = module;
}
}
private SoftReference<FileSystem> myFileSystem;
private final StringInterner myInterner = new StringInterner();
public JrtHandler(@NotNull String path) {
super(path);
@@ -56,16 +38,7 @@ class JrtHandler extends ArchiveHandler {
private synchronized FileSystem getFileSystem() throws IOException {
FileSystem fs = SoftReference.dereference(myFileSystem);
if (fs == null) {
if (SystemInfo.isJavaVersionAtLeast("9")) {
fs = FileSystems.newFileSystem(ROOT_URI, Collections.singletonMap("java.home", getFile().getPath()));
}
else {
File file = new File(getFile(), "jrt-fs.jar");
if (!file.exists()) throw new IOException("Missing provider: " + file);
URL url = file.toURI().toURL();
ClassLoader loader = new URLClassLoader(new URL[]{url}, null);
fs = FileSystems.newFileSystem(ROOT_URI, Collections.emptyMap(), loader);
}
fs = JrtFileSystem.getFileSystem(getFile().getPath());
myFileSystem = new SoftReference<>(fs);
}
return fs;
@@ -95,23 +68,17 @@ class JrtHandler extends ArchiveHandler {
private void process(Path entry, BasicFileAttributes attrs) throws IOException {
int pathLength = entry.getNameCount();
if (pathLength <= 2) return;
if (pathLength > 1) {
Path relativePath = entry.subpath(1, pathLength);
String path = relativePath.toString();
if (!map.containsKey(path)) {
EntryInfo parent = map.get(pathLength > 2 ? relativePath.getParent().toString() : "");
if (parent == null) throw new IOException("Out of order: " + entry);
Path relativePath = entry.subpath(2, pathLength);
String path = relativePath.toString(), shortName = entry.getFileName().toString();
if (map.containsKey(path) || "module-info.class".equals(shortName)) return;
EntryInfo parent = map.get(pathLength > 3 ? relativePath.getParent().toString() : "");
if (parent == null) throw new IOException("Out of order: " + entry);
long length = attrs.size();
long modified = attrs.lastModifiedTime().toMillis();
if (attrs.isDirectory()) {
map.put(path, new EntryInfo(shortName, true, length, modified, parent));
}
else {
String module = myInterner.intern(entry.getName(1).toString());
map.put(path, new JrtEntryInfo(shortName, module, length, modified, parent));
String shortName = entry.getFileName().toString();
long modified = attrs.lastModifiedTime().toMillis();
map.put(path, new EntryInfo(shortName, attrs.isDirectory(), attrs.size(), modified, parent));
}
}
}
});
@@ -123,8 +90,8 @@ class JrtHandler extends ArchiveHandler {
@Override
public byte[] contentsToByteArray(@NotNull String relativePath) throws IOException {
EntryInfo entry = getEntryInfo(relativePath);
if (!(entry instanceof JrtEntryInfo)) throw new FileNotFoundException(getFile() + " : " + relativePath);
Path path = getFileSystem().getPath("/modules/" + ((JrtEntryInfo)entry).myModule + '/' + relativePath);
if (entry == null) throw new FileNotFoundException(getFile() + " : " + relativePath);
Path path = getFileSystem().getPath("/modules/" + relativePath);
return Files.readAllBytes(path);
}
}