Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.jetbrains.plugin.structure.ide
import com.jetbrains.plugin.structure.base.plugin.PluginCreationFail
import com.jetbrains.plugin.structure.base.plugin.PluginCreationSuccess
import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.base.utils.exists
import com.jetbrains.plugin.structure.base.utils.isDirectory
import com.jetbrains.plugin.structure.base.utils.isFile
import com.jetbrains.plugin.structure.base.utils.listFiles
import com.jetbrains.plugin.structure.base.utils.listJars
import com.jetbrains.plugin.structure.base.utils.readLines
import com.jetbrains.plugin.structure.base.utils.readText
import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin
import com.jetbrains.plugin.structure.intellij.plugin.IdePluginManager
import com.jetbrains.plugin.structure.intellij.plugin.JarFilesResourceResolver
import com.jetbrains.plugin.structure.intellij.resources.CompiledModulesResourceResolver
import com.jetbrains.plugin.structure.intellij.resources.CompositeResourceResolver
import com.jetbrains.plugin.structure.intellij.resources.ResourceResolver
import com.jetbrains.plugin.structure.intellij.version.IdeVersion
import org.slf4j.LoggerFactory
import java.io.IOException
import java.nio.file.FileSystems
import java.nio.file.Path
class IdeManagerImpl : IdeManager() {
override fun createIde(idePath: Path): Ide = createIde(idePath, null)
override fun createIde(idePath: Path, version: IdeVersion?): Ide {
if (!idePath.isDirectory) {
throw IOException("Specified path does not exist or is not a directory: $idePath")
}
val fromCompiled = isCompiledCommunity(idePath) || isCompiledUltimate(idePath)
val fromDistribution = isDistributionIde(idePath)
if (!fromCompiled && !fromDistribution) {
throw InvalidIdeException(idePath, "IDE directory content is invalid")
}
val readIdeVersion = version ?: if (fromCompiled) {
readVersionFromIdeSources(idePath)
} else {
readIdeVersionFromDistribution(idePath)
}
val ideVersion = if (readIdeVersion.productCode.isNotEmpty()) {
readIdeVersion
} else {
//MPS builds' "build.txt" file does not specify product code.
//MPS builds contain "build.number" file whose "build.number" key-value contains the product code.
readIdeVersionFromBuildNumberFile(idePath) ?: readIdeVersion
}
val product = IntelliJPlatformProduct.fromIdeVersion(ideVersion) ?: IntelliJPlatformProduct.IDEA
val bundledPlugins = if (fromCompiled) {
readCompiledBundledPlugins(idePath, ideVersion)
} else {
readDistributionBundledPlugins(idePath, product, ideVersion)
}
return IdeImpl(idePath, ideVersion, bundledPlugins)
}
private fun readDistributionBundledPlugins(idePath: Path, product: IntelliJPlatformProduct, ideVersion: IdeVersion): List {
val platformJarFiles = idePath.resolve("lib").listJars()
val platformModuleJarFiles = idePath.resolve("lib").resolve("modules").listJars()
val platformResourceResolver = PlatformResourceResolver(platformJarFiles + platformModuleJarFiles)
val bundledPlugins = readBundledPlugins(idePath, platformResourceResolver, ideVersion)
val platformPlugins = readPlatformPlugins(idePath, product, platformJarFiles, platformResourceResolver, ideVersion)
return bundledPlugins + platformPlugins
}
/**
* IDE uses platform class loader to resolve resources. That class loader includes jar files under `/lib` directory.
* By default, for `relativePath` is resolved against jar files' roots. For example, if there are
* `a.jar`, `b.jar`, `c.jar` files in `/lib` directory, `/META-INF/someInclude.xml`
* will be searched by the following URLs:
* - `jar:file:/a.jar!/META-INF/someInclude.xml`
* - `jar:file:/b.jar!/META-INF/someInclude.xml`
* - `jar:file:/c.jar!/META-INF/someInclude.xml`
*
* But if the `relativePath` is not absolute it can still be relative to `META-INF`, not relative to jars' roots.
*
* Usually, `` of bundled plugins specify either:
* - Absolute paths to xml files that reside either in the same .jar file or in a different .jar file of the platform.
* For example, `` declared in `plugin.xml` where `plugin.xml` resides in `one.jar`
* and `include.xml` resides in `two.jar`.
*
* - Non-absolute paths to xml files that reside in the same .jar file as the xml containg such ``.
* For example, `` declared in `plugin.xml` where `plugin.xml` and `nearby.xml`
* reside in the same jar file.
* But rarely `nearby.xml` may reside in another platform's jar file. Apparently, IDE handles such a case accidentally:
* an ugly fallback hack is used `com.intellij.util.io.URLUtil.openResourceStream(URL)`.
*/
private class PlatformResourceResolver(platformJarFiles: List) : ResourceResolver {
private val jarFilesResourceResolver = JarFilesResourceResolver(platformJarFiles)
override fun resolveResource(relativePath: String, basePath: Path): ResourceResolver.Result {
val resolveResult = jarFilesResourceResolver.resolveResource(relativePath, basePath)
if (resolveResult !is ResourceResolver.Result.NotFound) {
return resolveResult
}
if (basePath.startsWith("META-INF")) {
var metaInf = basePath
while (!metaInf.endsWith("META-INF")) metaInf = metaInf.parent
val metaInfResult = jarFilesResourceResolver.resolveResource(relativePath, metaInf)
if (metaInfResult !is ResourceResolver.Result.NotFound) {
return metaInfResult
}
}
if (!relativePath.startsWith("/")) {
return jarFilesResourceResolver.resolveResource("/META-INF/$relativePath", basePath)
}
return ResourceResolver.Result.NotFound
}
}
private fun readIdeVersionFromDistribution(idePath: Path): IdeVersion {
val locations = listOf(
idePath.resolve("build.txt"),
idePath.resolve("Resources").resolve("build.txt"),
idePath.resolve("community").resolve("build.txt"),
idePath.resolve("ultimate").resolve("community").resolve("build.txt")
)
val buildTxtFile = locations.find { it.exists() }
?: throw InvalidIdeException(
idePath,
"Build number is not found in the following files relative to $idePath: " +
locations.joinToString { "'" + idePath.relativize(it) + "'" }
)
return readBuildNumber(buildTxtFile)
}
private fun readIdeVersionFromBuildNumberFile(idePath: Path): IdeVersion? {
val buildNumberFile = idePath.resolve("build.number")
if (buildNumberFile.exists()) {
val lines = buildNumberFile.readLines()
for (line in lines) {
if (line.startsWith("build.number=")) {
return IdeVersion.createIdeVersionIfValid(line.substringAfter("build.number="))
}
}
}
return null
}
private fun readVersionFromIdeSources(idePath: Path): IdeVersion {
val locations = listOf(
idePath.resolve("build.txt"),
idePath.resolve("community").resolve("build.txt")
)
val buildTxtFile = locations.find { it.exists() }
?: throw InvalidIdeException(idePath, "Unable to find IDE version file 'build.txt' or 'community/build.txt'")
return readBuildNumber(buildTxtFile)
}
private fun readBuildNumber(versionFile: Path): IdeVersion {
val buildNumberString = versionFile.readText().trim()
return IdeVersion.createIdeVersion(buildNumberString)
}
private fun readCompiledBundledPlugins(idePath: Path, ideVersion: IdeVersion): List {
val compilationRoot = getCompiledClassesRoot(idePath)!!
val moduleRoots = compilationRoot.listFiles().toList()
val librariesJars = getRepositoryLibrariesJars(idePath)
val pathResolver = CompositeResourceResolver(
listOf(
CompiledModulesResourceResolver(moduleRoots),
JarFilesResourceResolver(librariesJars)
)
)
return readCompiledBundledPlugins(idePath, moduleRoots, pathResolver, ideVersion)
}
private fun readCompiledBundledPlugins(
idePath: Path,
moduleRoots: List,
pathResolver: ResourceResolver,
ideVersion: IdeVersion
): List {
val plugins = arrayListOf()
for (moduleRoot in moduleRoots) {
val pluginXmlFile = moduleRoot.resolve(IdePluginManager.META_INF).resolve(IdePluginManager.PLUGIN_XML)
if (pluginXmlFile.isFile) {
plugins += createBundledPluginExceptionally(idePath, moduleRoot, pathResolver, IdePluginManager.PLUGIN_XML, ideVersion)
}
}
return plugins
}
private fun readPlatformPlugins(
idePath: Path,
product: IntelliJPlatformProduct,
jarFiles: List,
platformResourceResolver: ResourceResolver,
ideVersion: IdeVersion
): List {
val platformPlugins = arrayListOf()
val descriptorPaths = listOf(product.platformPrefix + "Plugin.xml", IdePluginManager.PLUGIN_XML, PLATFORM_PLUGIN_XML)
for (jarFile in jarFiles) {
val descriptorPath = FileSystems.newFileSystem(jarFile, IdeManagerImpl::class.java.classLoader).use { jarFs ->
descriptorPaths.find { jarFs.getPath(IdePluginManager.META_INF).resolve(it).exists() }
}
if (descriptorPath != null) {
platformPlugins += createBundledPluginExceptionally(idePath, jarFile, platformResourceResolver, descriptorPath, ideVersion)
}
}
if (platformPlugins.none { it.pluginId == "com.intellij" }) {
throw InvalidIdeException(idePath, "Platform plugins are not found. They must be declared in one of ${descriptorPaths.joinToString()}")
}
return platformPlugins
}
private fun readBundledPlugins(
idePath: Path,
platformResourceResolver: ResourceResolver,
ideVersion: IdeVersion
): List {
return idePath
.resolve("plugins")
.listFiles()
.filter { it.isDirectory }
.mapNotNull { readBundledPlugin(idePath, it, platformResourceResolver, ideVersion) }
}
private fun readBundledPlugin(
idePath: Path,
pluginFile: Path,
pathResolver: ResourceResolver,
ideVersion: IdeVersion
): IdePlugin? = try {
createBundledPluginExceptionally(idePath, pluginFile, pathResolver, IdePluginManager.PLUGIN_XML, ideVersion)
} catch (e: InvalidIdeException) {
LOG.warn("Failed to read bundled plugin '${idePath.relativize(pluginFile)}': ${e.reason}")
null
}
private fun createBundledPluginExceptionally(
idePath: Path,
pluginFile: Path,
pathResolver: ResourceResolver,
descriptorPath: String,
ideVersion: IdeVersion
): IdePlugin = when (val creationResult = IdePluginManager
.createManager(pathResolver)
.createBundledPlugin(pluginFile, ideVersion, descriptorPath)
) {
is PluginCreationSuccess -> creationResult.plugin
is PluginCreationFail -> throw InvalidIdeException(
idePath,
"Plugin '${idePath.relativize(pluginFile)}' is invalid: " +
creationResult.errorsAndWarnings.filter { it.level == PluginProblem.Level.ERROR }.joinToString { it.message }
)
}
companion object {
private val LOG = LoggerFactory.getLogger(IdeManagerImpl::class.java)
// in idea-IE com.intellij plugin is defined in PlatformLangPlugin.xml file
internal const val PLATFORM_PLUGIN_XML = "PlatformLangPlugin.xml"
fun isCompiledUltimate(ideaDir: Path) = getCompiledClassesRoot(ideaDir) != null &&
ideaDir.resolve(".idea").isDirectory &&
ideaDir.resolve("community").resolve(".idea").isDirectory
fun isCompiledCommunity(ideaDir: Path) = getCompiledClassesRoot(ideaDir) != null &&
ideaDir.resolve(".idea").isDirectory &&
!ideaDir.resolve("community").resolve(".idea").isDirectory
fun isDistributionIde(ideaDir: Path) = ideaDir.resolve("lib").isDirectory &&
!ideaDir.resolve(".idea").isDirectory
fun getCompiledClassesRoot(ideaDir: Path): Path? =
listOf(
ideaDir.resolve("out").resolve("production"),
ideaDir.resolve("out").resolve("classes").resolve("production"),
ideaDir.resolve("out").resolve("compilation").resolve("classes").resolve("production")
).find { it.isDirectory }
}
}