[Analysis API Standalone] copy JdkClassFinder from IntelliJ sources
^KT-60884
This commit is contained in:
committed by
Space Team
parent
180434951b
commit
8f824dcff9
+214
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.analysis.api.impl.base.util;
|
||||||
|
|
||||||
|
import com.intellij.ide.highlighter.ArchiveFileType;
|
||||||
|
import com.intellij.openapi.fileTypes.FileTypeRegistry;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.openapi.util.registry.Registry;
|
||||||
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
|
import com.intellij.openapi.vfs.StandardFileSystems;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||||
|
import com.intellij.util.containers.CollectionFactory;
|
||||||
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
|
import com.intellij.util.io.URLUtil;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copy of corresponding classes from IntelliJ source repository.
|
||||||
|
We cannot reuse them directly as not all those methods are parts of intellij-core which is used in Analysis API
|
||||||
|
*/
|
||||||
|
class JdkClassFinder {
|
||||||
|
// copy of 'com.intellij.openapi.projectRoots.impl.JavaSdkImpl.findClasses'
|
||||||
|
static @NotNull List<String> findClasses(@NotNull Path jdkHome, boolean isJre) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
|
if (isExplodedModularRuntime(jdkHome)) {
|
||||||
|
try {
|
||||||
|
try (DirectoryStream<Path> roots = Files.newDirectoryStream(jdkHome.resolve("modules"))) {
|
||||||
|
for (Path root : roots) {
|
||||||
|
result.add(getUrlForLibraryRoot(root));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isModularRuntime(jdkHome)) {
|
||||||
|
String jrtBaseUrl = StandardFileSystems.JRT_PROTOCOL_PREFIX + vfsPath(jdkHome) + URLUtil.JAR_SEPARATOR;
|
||||||
|
List<String> modules = readModulesFromReleaseFile(jdkHome);
|
||||||
|
if (modules != null) {
|
||||||
|
for (String module : modules) {
|
||||||
|
result.add(jrtBaseUrl + module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VirtualFile jrt = VirtualFileManager.getInstance().findFileByUrl(jrtBaseUrl);
|
||||||
|
if (jrt != null) {
|
||||||
|
for (VirtualFile virtualFile : jrt.getChildren()) {
|
||||||
|
result.add(virtualFile.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (Path root : getJdkClassesRoots(jdkHome, isJre)) {
|
||||||
|
result.add(getUrlForLibraryRoot(root));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy of 'com.intellij.openapi.projectRoots.impl.JavaSdkImpl.vfsPath'
|
||||||
|
private static String vfsPath(@NotNull Path path) {
|
||||||
|
return FileUtil.toSystemIndependentName(path.toAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// copy of 'com.intellij.openapi.projectRoots.JdkUtil.isExplodedModularRuntime'
|
||||||
|
private static boolean isExplodedModularRuntime(@NotNull Path homePath) {
|
||||||
|
return Files.isDirectory(homePath.resolve("modules/java.base"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy of 'com.intellij.openapi.projectRoots.JdkUtil.isModularRuntime'
|
||||||
|
private static boolean isModularRuntime(@NotNull Path homePath) {
|
||||||
|
return Files.isRegularFile(homePath.resolve("lib/jrt-fs.jar")) || isExplodedModularRuntime(homePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy of 'com.intellij.openapi.vfs.VfsUtil.getUrlForLibraryRoot'
|
||||||
|
private static @NotNull String getUrlForLibraryRoot(@NotNull Path libraryRoot) {
|
||||||
|
return getUrlForLibraryRoot(libraryRoot.toAbsolutePath().toString(), libraryRoot.getFileName().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy of `com.intellij.openapi.vfs.VfsUtil.getUrlForLibraryRoot`
|
||||||
|
private static @NotNull String getUrlForLibraryRoot(@NotNull String libraryRootAbsolutePath, @NotNull String libraryRootFileName) {
|
||||||
|
String path = FileUtil.toSystemIndependentName(libraryRootAbsolutePath);
|
||||||
|
return FileTypeRegistry.getInstance().getFileTypeByFileName(libraryRootFileName) == ArchiveFileType.INSTANCE
|
||||||
|
? VirtualFileManager.constructUrl(StandardFileSystems.JAR_PROTOCOL, path + URLUtil.JAR_SEPARATOR)
|
||||||
|
: VirtualFileManager.constructUrl(StandardFileSystems.FILE_PROTOCOL, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to load the list of modules in the JDK from the 'release' file. Returns null if the 'release' file is not there
|
||||||
|
* or doesn't contain the expected information.
|
||||||
|
* <p>
|
||||||
|
* Copy of `com.intellij.openapi.projectRoots.impl.JavaSdkImpl.readModulesFromReleaseFile`
|
||||||
|
*/
|
||||||
|
private static @Nullable List<String> readModulesFromReleaseFile(@NotNull Path jrtBaseDir) {
|
||||||
|
try (InputStream stream = Files.newInputStream(jrtBaseDir.resolve("release"))) {
|
||||||
|
Properties p = new Properties();
|
||||||
|
p.load(stream);
|
||||||
|
String modules = p.getProperty("MODULES");
|
||||||
|
if (modules != null) {
|
||||||
|
return StringUtil.split(StringUtil.unquoteString(modules), " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException | IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy of `org.jetbrains.jps.model.java.impl.JavaSdkUtil.getJdkClassesRoots`
|
||||||
|
private static @NotNull List<Path> getJdkClassesRoots(@NotNull Path home, boolean isJre) {
|
||||||
|
Path[] jarDirs;
|
||||||
|
Path fileName = home.getFileName();
|
||||||
|
if (fileName != null && "Home".equals(fileName.toString()) && Files.exists(home.resolve("../Classes/classes.jar"))) {
|
||||||
|
Path libDir = home.resolve("lib");
|
||||||
|
Path classesDir = home.resolveSibling("Classes");
|
||||||
|
Path libExtDir = libDir.resolve("ext");
|
||||||
|
Path libEndorsedDir = libDir.resolve("endorsed");
|
||||||
|
jarDirs = new Path[] {libEndorsedDir, libDir, classesDir, libExtDir};
|
||||||
|
}
|
||||||
|
else if (Files.exists(home.resolve("lib/jrt-fs.jar"))) {
|
||||||
|
jarDirs = new Path[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Path libDir = home.resolve(isJre ? "lib" : "jre/lib");
|
||||||
|
Path libExtDir = libDir.resolve("ext");
|
||||||
|
Path libEndorsedDir = libDir.resolve("endorsed");
|
||||||
|
jarDirs = new Path[] {libEndorsedDir, libDir, libExtDir};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Path> rootFiles = new ArrayList<>();
|
||||||
|
|
||||||
|
if (Registry.is("project.structure.add.tools.jar.to.new.jdk", false)) {
|
||||||
|
@SuppressWarnings("IdentifierGrammar") Path toolsJar = home.resolve("lib/tools.jar");
|
||||||
|
if (Files.isRegularFile(toolsJar)) {
|
||||||
|
rootFiles.add(toolsJar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> pathFilter = CollectionFactory.createFilePathSet();
|
||||||
|
for (Path jarDir : jarDirs) {
|
||||||
|
if (jarDir != null && Files.isDirectory(jarDir)) {
|
||||||
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(jarDir, "*.jar")) {
|
||||||
|
for (Path jarFile : stream) {
|
||||||
|
String jarFileName = jarFile.getFileName().toString();
|
||||||
|
if (jarFileName.equals("alt-rt.jar") || jarFileName.equals("alt-string.jar")) {
|
||||||
|
continue; // filter out alternative implementations
|
||||||
|
}
|
||||||
|
String canonicalPath = getCanonicalPath(jarFile);
|
||||||
|
if (canonicalPath == null || !pathFilter.add(canonicalPath)) {
|
||||||
|
continue; // filter out duplicate (symbolically linked) .jar files commonly found in OS X JDK distributions
|
||||||
|
}
|
||||||
|
rootFiles.add(jarFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ContainerUtil.exists(rootFiles, path -> path.getFileName().toString().startsWith("ibm"))) {
|
||||||
|
// ancient IBM JDKs split JRE classes between `rt.jar` and `vm.jar`, and the latter might be anywhere
|
||||||
|
try (Stream<Path> paths = Files.walk(isJre ? home : home.resolve("jre"))) {
|
||||||
|
paths.filter(path -> path.getFileName().toString().equals("vm.jar"))
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(rootFiles::add);
|
||||||
|
}
|
||||||
|
catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Path classesZip = home.resolve("lib/classes.zip");
|
||||||
|
if (Files.isRegularFile(classesZip)) {
|
||||||
|
rootFiles.add(classesZip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootFiles.isEmpty()) {
|
||||||
|
Path classesDir = home.resolve("classes");
|
||||||
|
if (Files.isDirectory(classesDir)) {
|
||||||
|
rootFiles.add(classesDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy of `org.jetbrains.jps.model.java.impl.JavaSdkUtil.getCanonicalPath`
|
||||||
|
private static @Nullable String getCanonicalPath(Path file) {
|
||||||
|
try {
|
||||||
|
return file.toRealPath().toString();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user