[jps model] support loading project and module components in the new implementation (IJPL-409)

Data which is present in the workspace model is taken from it, other components are loaded from xml files as before.

GitOrigin-RevId: 368e2b15aa055bc3489fb944c334379f10f2725e
This commit is contained in:
Nikolay Chashnikov
2024-06-27 18:23:06 +02:00
committed by intellij-monorepo-bot
parent c6681283f6
commit c0393d751a
11 changed files with 260 additions and 73 deletions

View File

@@ -25,11 +25,15 @@ public class JpsComponentLoader {
protected final @Nullable Path myExternalConfigurationDirectory;
private final JpsMacroExpander myMacroExpander;
public JpsComponentLoader(JpsMacroExpander macroExpander, @Nullable Path externalConfigurationDirectory) {
public JpsComponentLoader(@NotNull JpsMacroExpander macroExpander, @Nullable Path externalConfigurationDirectory) {
myMacroExpander = macroExpander;
myExternalConfigurationDirectory = externalConfigurationDirectory;
}
public @NotNull JpsMacroExpander getMacroExpander() {
return myMacroExpander;
}
/**
* Returns null if file doesn't exist
*/
@@ -37,10 +41,10 @@ public class JpsComponentLoader {
return loadRootElement(file, myMacroExpander);
}
protected <E extends JpsElement> void loadComponents(@NotNull Path dir,
@NotNull Path defaultConfigFile,
JpsElementExtensionSerializerBase<E> serializer,
final E element) {
public <E extends JpsElement> void loadComponents(@NotNull Path dir,
@NotNull Path defaultConfigFile,
JpsElementExtensionSerializerBase<E> serializer,
E element) {
String fileName = serializer.getConfigFileName();
Path configFile = fileName == null ? defaultConfigFile : dir.resolve(fileName);
Runnable timingLog = TimingLog.startActivity("loading: " + configFile.getFileName() + ":" + serializer.getComponentName());

View File

@@ -1,7 +1,9 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.model.serialization;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.Element;
import org.jetbrains.annotations.ApiStatus;
@@ -14,13 +16,12 @@ import org.jetbrains.jps.model.serialization.impl.JpsProjectSerializationDataExt
import org.jetbrains.jps.model.serialization.runConfigurations.JpsRunConfigurationSerializer;
import org.jetbrains.jps.util.JpsPathUtil;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
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.*;
/**
* Contains helper methods which are used to load parts of the project configuration which aren't stored in the workspace model.
@@ -136,9 +137,29 @@ public final class JpsProjectConfigurationLoading {
String sdkName = rootManagerElement.getAttributeValue("project-jdk-name");
String sdkTypeId = rootManagerElement.getAttributeValue("project-jdk-type");
if (sdkName != null) {
sdkTypeIdAndName = new Pair<>(sdkTypeId, sdkName);
sdkTypeIdAndName = new Pair<>(Objects.requireNonNullElse(sdkTypeId, "JavaSDK"), sdkName);
}
}
return sdkTypeIdAndName;
}
public static JpsMacroExpander createModuleMacroExpander(final Map<String, String> pathVariables, @NotNull Path moduleFile) {
JpsMacroExpander expander = new JpsMacroExpander(pathVariables);
String moduleDirPath = PathMacroUtil.getModuleDir(moduleFile.toAbsolutePath().toString());
if (moduleDirPath != null) {
expander.addFileHierarchyReplacements(PathMacroUtil.MODULE_DIR_MACRO_NAME, new File(FileUtilRt.toSystemDependentName(moduleDirPath)));
}
return expander;
}
public static @NotNull Set<String> readNamesOfUnloadedModules(@NotNull Path workspaceFile, @NotNull JpsComponentLoader componentLoader) {
Set<String> unloadedModules = new HashSet<>();
if (workspaceFile.toFile().exists()) {
Element unloadedModulesList = JDomSerializationUtil.findComponent(componentLoader.loadRootElement(workspaceFile), "UnloadedModulesList");
for (Element element : JDOMUtil.getChildren(unloadedModulesList, "module")) {
unloadedModules.add(element.getAttributeValue("name"));
}
}
return unloadedModules;
}
}

View File

@@ -29,7 +29,6 @@ import org.jetbrains.jps.model.serialization.module.JpsModulePropertiesSerialize
import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer;
import org.jetbrains.jps.service.SharedThreadPool;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -248,13 +247,8 @@ public final class JpsProjectLoader {
return;
}
Set<String> unloadedModules = new HashSet<>();
if (!myLoadUnloadedModules && workspaceFile.toFile().exists()) {
Element unloadedModulesList = JDomSerializationUtil.findComponent(myComponentLoader.loadRootElement(workspaceFile), "UnloadedModulesList");
for (Element element : JDOMUtil.getChildren(unloadedModulesList, "module")) {
unloadedModules.add(element.getAttributeValue("name"));
}
}
Set<String> unloadedModules = !myLoadUnloadedModules ? JpsProjectConfigurationLoading.readNamesOfUnloadedModules(workspaceFile, myComponentLoader)
: Collections.emptySet();
final Set<Path> foundFiles = CollectionFactory.createSmallMemoryFootprintSet();
final List<Path> moduleFiles = new ArrayList<>();
@@ -297,7 +291,7 @@ public final class JpsProjectLoader {
for (Path file : moduleFiles) {
futureModuleFilesContents.add(CompletableFuture.supplyAsync(() -> {
JpsMacroExpander expander = createModuleMacroExpander(pathVariables, file);
JpsMacroExpander expander = JpsProjectConfigurationLoading.createModuleMacroExpander(pathVariables, file);
Element data = JpsComponentLoader.loadRootElement(file, expander);
if (externalModuleDir != null) {
@@ -409,7 +403,7 @@ public final class JpsProjectLoader {
JpsModuleClasspathSerializer classpathSerializer = extension.getClasspathSerializer();
if (classpathSerializer != null && classpathSerializer.getClasspathId().equals(classpath)) {
String classpathDir = moduleRoot.getAttributeValue(CLASSPATH_DIR_ATTRIBUTE);
final JpsMacroExpander expander = createModuleMacroExpander(pathVariables, file);
final JpsMacroExpander expander = JpsProjectConfigurationLoading.createModuleMacroExpander(pathVariables, file);
classpathSerializer.loadClasspath(module, classpathDir, baseModulePath, expander, paths, projectSdkType);
}
}
@@ -434,15 +428,6 @@ public final class JpsProjectLoader {
return FileUtilRt.getNameWithoutExtension(file.getFileName().toString());
}
static JpsMacroExpander createModuleMacroExpander(final Map<String, String> pathVariables, @NotNull Path moduleFile) {
JpsMacroExpander expander = new JpsMacroExpander(pathVariables);
String moduleDirPath = PathMacroUtil.getModuleDir(moduleFile.toAbsolutePath().toString());
if (moduleDirPath != null) {
expander.addFileHierarchyReplacements(PathMacroUtil.MODULE_DIR_MACRO_NAME, new File(FileUtilRt.toSystemDependentName(moduleDirPath)));
}
return expander;
}
private static <P extends JpsElement> JpsModule createModule(String name, Element moduleRoot, JpsModulePropertiesSerializer<P> loader) {
String componentName = loader.getComponentName();
Element component = componentName != null ? JDomSerializationUtil.findComponent(moduleRoot, componentName) : null;

View File

@@ -27,7 +27,7 @@ public final class JpsSerializationManagerImpl extends JpsSerializationManager {
boolean loadUnloadedModules) throws IOException {
JpsSerializationViaWorkspaceModel serializationViaWorkspaceModel = JpsSerializationViaWorkspaceModel.getInstance();
if (serializationViaWorkspaceModel != null) {
return serializationViaWorkspaceModel.loadModel(projectPath, optionsPath, loadUnloadedModules);
return serializationViaWorkspaceModel.loadModel(projectPath, externalConfigurationDirectory, optionsPath, loadUnloadedModules);
}
JpsModel model = JpsElementFactory.getInstance().createModel();
@@ -61,7 +61,7 @@ public final class JpsSerializationManagerImpl extends JpsSerializationManager {
boolean loadUnloadedModules) throws IOException {
JpsSerializationViaWorkspaceModel serializationViaWorkspaceModel = JpsSerializationViaWorkspaceModel.getInstance();
if (serializationViaWorkspaceModel != null) {
return serializationViaWorkspaceModel.loadProject(projectPath, pathVariables, loadUnloadedModules);
return serializationViaWorkspaceModel.loadProject(projectPath, externalConfigurationDirectory, pathVariables, loadUnloadedModules);
}
JpsModel model = JpsElementFactory.getInstance().createModel();

View File

@@ -26,8 +26,10 @@ public interface JpsSerializationViaWorkspaceModel {
return null;
}
@NotNull JpsModel loadModel(@NotNull Path projectPath, @Nullable Path optionsPath, boolean loadUnloadedModules) throws IOException;
@NotNull JpsModel loadModel(@NotNull Path projectPath,
@Nullable Path externalConfigurationDirectory,
@Nullable Path optionsPath, boolean loadUnloadedModules) throws IOException;
@NotNull JpsProject loadProject(@NotNull Path projectPath, @NotNull Map<String, String> pathVariables,
@NotNull JpsProject loadProject(@NotNull Path projectPath, @Nullable Path externalConfigurationDirectory, @NotNull Map<String, String> pathVariables,
boolean loadUnloadedModules) throws IOException;
}

View File

@@ -15,7 +15,7 @@ internal class JpsModelBridge(
private val project: JpsProjectBridge = JpsProjectBridge(this, projectStorage, projectAdditionalData);
private val global: JpsGlobalBridge = JpsGlobalBridge(this, globalStorage);
override fun getProject(): JpsProject = project
override fun getProject(): JpsProjectBridge = project
override fun getGlobal(): JpsGlobal = global
override fun getGlobal(): JpsGlobalBridge = global
}

View File

@@ -33,7 +33,7 @@ internal class JpsProjectBridge(modelBridge: JpsModelBridge,
}
private val sdkReferencesTable = JpsSdkReferencesTableBridge(additionalData.projectSdkId, this)
override fun getModules(): List<JpsModule> = modules
override fun getModules(): List<JpsModuleBridge> = modules
override fun <P : JpsElement?> getModules(type: JpsModuleType<P>): Iterable<JpsTypedModule<P>> {
return modules.asSequence()

View File

@@ -10,7 +10,10 @@ import org.jetbrains.jps.model.serialization.JpsMacroExpander
import org.jetbrains.jps.util.JpsPathUtil
import kotlin.io.path.Path
internal class DirectJpsFileContentReader(private val macroExpander: JpsMacroExpander) : JpsFileContentReader {
/**
* Loads global settings used in the workspace model directly from XML configuration files.
*/
internal class GlobalDirectJpsFileContentReader(private val macroExpander: JpsMacroExpander) : JpsFileContentReader {
private val componentLoader = JpsComponentLoader(macroExpander, null)
override fun loadComponent(fileUrl: String, componentName: String, customModuleFilePath: String?): Element? {

View File

@@ -2,70 +2,114 @@
package com.intellij.platform.workspace.jps.bridge.impl.serialization
import com.intellij.openapi.util.io.FileUtil
import com.intellij.platform.workspace.jps.JpsProjectConfigLocation
import com.intellij.platform.workspace.jps.JpsProjectFileEntitySource
import com.intellij.platform.workspace.jps.UnloadedModulesNameHolder
import com.intellij.platform.workspace.jps.bridge.impl.JpsModelBridge
import com.intellij.platform.workspace.jps.bridge.impl.JpsProjectAdditionalData
import com.intellij.platform.workspace.jps.bridge.impl.JpsProjectBridge
import com.intellij.platform.workspace.jps.bridge.impl.library.sdk.JpsSdkLibraryBridge
import com.intellij.platform.workspace.jps.serialization.impl.ErrorReporter
import com.intellij.platform.workspace.jps.serialization.impl.JpsGlobalEntitiesSerializers
import com.intellij.platform.workspace.jps.serialization.impl.JpsProjectEntitiesLoader
import com.intellij.platform.workspace.jps.bridge.impl.module.JpsModuleBridge
import com.intellij.platform.workspace.jps.entities.SdkId
import com.intellij.platform.workspace.jps.entities.customImlData
import com.intellij.platform.workspace.jps.entities.exModuleOptions
import com.intellij.platform.workspace.jps.serialization.impl.*
import com.intellij.platform.workspace.jps.serialization.impl.toConfigLocation
import com.intellij.platform.workspace.storage.EntityStorage
import com.intellij.platform.workspace.storage.MutableEntityStorage
import com.intellij.platform.workspace.storage.impl.url.VirtualFileUrlManagerImpl
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
import kotlinx.coroutines.runBlocking
import org.jdom.Element
import org.jetbrains.jps.model.JpsModel
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.jps.model.serialization.JpsGlobalLoader
import org.jetbrains.jps.model.serialization.JpsGlobalSettingsLoading
import org.jetbrains.jps.model.serialization.JpsMacroExpander
import org.jetbrains.jps.model.serialization.*
import org.jetbrains.jps.model.serialization.JpsProjectConfigurationLoading.*
import org.jetbrains.jps.model.serialization.impl.JpsModuleSerializationDataExtensionImpl
import org.jetbrains.jps.model.serialization.impl.JpsSerializationViaWorkspaceModel
import java.nio.file.Path
import kotlin.io.path.Path
internal class JpsSerializationViaWorkspaceModelImpl : JpsSerializationViaWorkspaceModel {
override fun loadModel(projectPath: Path, optionsPath: Path?, loadUnloadedModules: Boolean): JpsModel {
override fun loadModel(projectPath: Path, externalConfigurationDirectory: Path?, optionsPath: Path?, loadUnloadedModules: Boolean): JpsModel {
val virtualFileUrlManager = VirtualFileUrlManagerImpl()
val errorReporter = object : ErrorReporter {
override fun reportError(message: String, file: VirtualFileUrl) {
error(message)
}
}
val errorReporter = createErrorReporter()
val globalStorage = MutableEntityStorage.create()
val globalMacroExpander = if (optionsPath != null) {
val (globalMacroExpander, pathVariables) = if (optionsPath != null) {
loadGlobalStorage(optionsPath, virtualFileUrlManager, errorReporter, globalStorage)
}
else {
null
null to emptyMap()
}
val (projectStorage, additionalData) = loadProjectStorage(projectPath, virtualFileUrlManager, errorReporter)
val model = JpsModelBridge(projectStorage, globalStorage, additionalData)
val model = loadProject(projectPath, externalConfigurationDirectory, virtualFileUrlManager, pathVariables, errorReporter, globalStorage,
loadUnloadedModules)
if (optionsPath != null) {
val globalLoader = JpsGlobalLoader(globalMacroExpander!!, model.global, arrayOf(JpsGlobalLoader.FILE_TYPES_SERIALIZER))
globalLoader.load(optionsPath)
}
return model
}
private fun loadProjectStorage(projectPath: Path, virtualFileUrlManager: VirtualFileUrlManagerImpl, errorReporter: ErrorReporter): Pair<EntityStorage, JpsProjectAdditionalData> {
private fun loadProject(
projectPath: Path,
externalConfigurationDirectory: Path?,
virtualFileUrlManager: VirtualFileUrlManagerImpl,
pathVariables: Map<String, String>,
errorReporter: ErrorReporter,
globalStorage: EntityStorage,
loadUnloadedModules: Boolean,
): JpsModelBridge {
val configLocation = toConfigLocation(projectPath, virtualFileUrlManager)
val externalStoragePath = Path("")//todo
val context = SerializationContextImpl(virtualFileUrlManager)
val contentReader = ProjectDirectJpsFileContentReader(configLocation.baseDirectoryUrl.toPath(), externalConfigurationDirectory, pathVariables)
val (projectStorage, additionalData) = loadProjectStorage(virtualFileUrlManager, errorReporter, configLocation, contentReader, loadUnloadedModules)
val model = JpsModelBridge(projectStorage, globalStorage, additionalData)
loadOtherProjectComponents(model.project, contentReader.projectComponentLoader, configLocation, externalConfigurationDirectory)
loadOtherModuleComponents(model.project)
return model
}
private fun createErrorReporter() = object : ErrorReporter {
override fun reportError(message: String, file: VirtualFileUrl) {
throw CannotLoadJpsModelException(file.toPath().toFile(), message, null)
}
}
private fun loadProjectStorage(
virtualFileUrlManager: VirtualFileUrlManagerImpl, errorReporter: ErrorReporter,
configLocation: JpsProjectConfigLocation, fileContentReader: ProjectDirectJpsFileContentReader, loadUnloadedModules: Boolean,
): Pair<EntityStorage, JpsProjectAdditionalData> {
/* JpsProjectEntitiesLoader requires non-null value of externalStoragePath even if the external storage is not used, so use some
artificial path if it isn't specified; externalStoragePath will be eliminated when IJPL-10518 is fixed
*/
val externalStoragePath = fileContentReader.externalConfigurationDirectory
?: configLocation.baseDirectoryUrl.toPath().resolve("fake_external_build_system")
val context = SerializationContextImpl(virtualFileUrlManager, fileContentReader)
val serializers = JpsProjectEntitiesLoader.createProjectSerializers(configLocation, externalStoragePath, context)
val mainStorage = MutableEntityStorage.create()
val orphanageStorage = MutableEntityStorage.create()
val unloadedStorage = MutableEntityStorage.create()
val unloadedModuleNames = UnloadedModulesNameHolder.DUMMY
val unloadedModuleNames = if (loadUnloadedModules) UnloadedModulesNameHolder.DUMMY else JpsUnloadedModulesNameHolder(readNamesOfUnloadedModules(configLocation.workspaceFile, fileContentReader.projectComponentLoader))
@Suppress("SSBasedInspection")
runBlocking {
serializers.loadAll(context.fileContentReader, mainStorage, orphanageStorage, unloadedStorage, unloadedModuleNames, errorReporter)
}
return mainStorage to JpsProjectAdditionalData(TODO(), TODO())
val projectName = when (configLocation) {
is JpsProjectConfigLocation.DirectoryBased -> getDirectoryBaseProjectName(configLocation.ideaFolder.toPath())
is JpsProjectConfigLocation.FileBased -> FileUtil.getNameWithoutExtension(configLocation.iprFile.fileName)
}
val projectRootComponentFileElement = when (configLocation) {
is JpsProjectConfigLocation.DirectoryBased -> fileContentReader.projectComponentLoader.loadRootElement(configLocation.ideaFolder.toPath().resolve("misc.xml"))
is JpsProjectConfigLocation.FileBased -> fileContentReader.projectComponentLoader.loadRootElement(configLocation.iprFile.toPath())
}
val projectSdkId = readProjectSdkTypeAndName(projectRootComponentFileElement)?.let {
SdkId(name = it.second, type = it.first)
}
return mainStorage to JpsProjectAdditionalData(projectName, projectSdkId)
}
private fun loadGlobalStorage(
@@ -73,9 +117,10 @@ internal class JpsSerializationViaWorkspaceModelImpl : JpsSerializationViaWorksp
virtualFileUrlManager: VirtualFileUrlManagerImpl,
errorReporter: ErrorReporter,
globalStorage: MutableEntityStorage,
): JpsMacroExpander {
val macroExpander = JpsMacroExpander(JpsGlobalSettingsLoading.computeAllPathVariables(optionsPath))
val reader = DirectJpsFileContentReader(macroExpander)
): Pair<JpsMacroExpander, Map<String, String>> {
val pathVariables = JpsGlobalSettingsLoading.computeAllPathVariables(optionsPath)
val macroExpander = JpsMacroExpander(pathVariables)
val reader = GlobalDirectJpsFileContentReader(macroExpander)
val rootsTypes = JpsSdkLibraryBridge.serializers.map { it.typeId }
val serializers = JpsGlobalEntitiesSerializers.createApplicationSerializers(virtualFileUrlManager, rootsTypes)
for (serializer in serializers) {
@@ -83,10 +128,79 @@ internal class JpsSerializationViaWorkspaceModelImpl : JpsSerializationViaWorksp
serializer.checkAndAddToBuilder(globalStorage, globalStorage, loaded.data)
loaded.exception?.let { throw it }
}
return macroExpander
return macroExpander to pathVariables
}
override fun loadProject(projectPath: Path, pathVariables: Map<String, String>, loadUnloadedModules: Boolean): JpsProject {
TODO("not implemented")
override fun loadProject(projectPath: Path, externalConfigurationDirectory: Path?, pathVariables: Map<String, String>, loadUnloadedModules: Boolean): JpsProject {
val model = loadProject(projectPath, externalConfigurationDirectory, VirtualFileUrlManagerImpl(), pathVariables, createErrorReporter(),
MutableEntityStorage.create(), loadUnloadedModules)
return model.project
}
private fun loadOtherProjectComponents(project: JpsProject, componentLoader: JpsComponentLoader, configLocation: JpsProjectConfigLocation, externalStoragePath: Path?) {
setupSerializationExtension(project, configLocation.baseDirectoryUrl.toPath())
when (configLocation) {
is JpsProjectConfigLocation.DirectoryBased -> {
val dotIdea = configLocation.ideaFolder.toPath()
val defaultConfigFile = dotIdea.resolve("misc.xml")
for (extension in JpsModelSerializerExtension.getExtensions()) {
for (serializer in extension.projectExtensionSerializers) {
componentLoader.loadComponents(dotIdea, defaultConfigFile, serializer, project)
}
}
loadArtifactsFromDirectory(project, componentLoader, dotIdea, externalStoragePath)
loadRunConfigurationsFromDirectory(project, componentLoader, dotIdea, configLocation.workspaceFile)
}
is JpsProjectConfigLocation.FileBased -> {
val iprFile = configLocation.iprFile.toPath()
val iprRoot = componentLoader.loadRootElement(iprFile)
val iwsRoot = componentLoader.loadRootElement(configLocation.workspaceFile)
loadProjectExtensionsFromIpr(project, iprRoot, iwsRoot)
loadArtifactsFromIpr(project, iprRoot)
loadRunConfigurationsFromIpr(project, iprRoot, iwsRoot)
}
}
}
private fun loadOtherModuleComponents(project: JpsProjectBridge) {
project.modules.forEach { module ->
loadOtherModuleComponents(module)
}
}
private fun loadOtherModuleComponents(module: JpsModuleBridge) {
val moduleEntity = module.entity
val entitySource = moduleEntity.entitySource
if (entitySource is JpsProjectFileEntitySource.FileInDirectory) {
module.container.setChild(JpsModuleSerializationDataExtensionImpl.ROLE,
JpsModuleSerializationDataExtensionImpl(entitySource.directory.toPath()))
}
for (serializerExtension in JpsModelSerializerExtension.getExtensions()) {
val rootElement = Element("module")
//todo is it enough?
moduleEntity.customImlData?.customModuleOptions?.forEach { (key, value) ->
rootElement.setAttribute(key, value)
}
moduleEntity.exModuleOptions?.let { externalSystemOptions ->
externalSystemOptions.externalSystem?.let {
rootElement.setAttribute("external.system.id", it)
rootElement.setAttribute("ExternalSystem", it)
}
}
serializerExtension.loadModuleOptions(module, rootElement)
}
}
private val JpsProjectConfigLocation.workspaceFile: Path
get() = when (this) {
is JpsProjectConfigLocation.DirectoryBased -> ideaFolder.toPath().resolve("workspace.xml")
is JpsProjectConfigLocation.FileBased -> iprFileParent.toPath().resolve("${iprFile.fileName.substringBeforeLast('.')}.iws")
}
}
private class JpsUnloadedModulesNameHolder(private val unloadedModuleNames: Set<String>) : UnloadedModulesNameHolder {
override fun isUnloaded(name: String): Boolean = unloadedModuleNames.contains(name)
override fun hasUnloaded(): Boolean = unloadedModuleNames.isNotEmpty()
}

View File

@@ -0,0 +1,57 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.workspace.jps.bridge.impl.serialization
import com.intellij.openapi.components.ExpandMacroToPathMap
import com.intellij.platform.workspace.jps.serialization.impl.JpsFileContentReader
import org.jdom.Element
import org.jetbrains.jps.model.serialization.JDomSerializationUtil
import org.jetbrains.jps.model.serialization.JpsComponentLoader
import org.jetbrains.jps.model.serialization.JpsProjectConfigurationLoading
import org.jetbrains.jps.model.serialization.JpsProjectConfigurationLoading.createProjectMacroExpander
import org.jetbrains.jps.util.JpsPathUtil
import java.nio.file.Path
import kotlin.io.path.Path
/**
* Loads data of module-level and project-level components directly from XML and iml configuration files.
*/
internal class ProjectDirectJpsFileContentReader(
projectBaseDir: Path,
val externalConfigurationDirectory: Path?,
private val pathVariables: Map<String, String>
) : JpsFileContentReader {
private val projectMacroExpander = createProjectMacroExpander(pathVariables, projectBaseDir)
val projectComponentLoader = JpsComponentLoader(projectMacroExpander, externalConfigurationDirectory)
override fun loadComponent(fileUrl: String, componentName: String, customModuleFilePath: String?): Element? {
if (fileUrl.endsWith(".iml")) {
//todo support external storage
val loader = getModuleLoader(fileUrl)
return JDomSerializationUtil.findComponent(loader.loadRootElement(Path(JpsPathUtil.urlToPath(fileUrl))), componentName)
}
return loadProjectLevelComponent(fileUrl, componentName)
}
private fun loadProjectLevelComponent(fileUrl: String, componentName: String): Element? {
val rootElement = projectComponentLoader.loadRootElement(Path(JpsPathUtil.urlToPath(fileUrl))) ?: return null
if (rootElement.name == JDomSerializationUtil.COMPONENT_ELEMENT && JDomSerializationUtil.isComponent(componentName, rootElement)) {
return rootElement
}
return JDomSerializationUtil.findComponent(rootElement, componentName)
}
override fun getExpandMacroMap(fileUrl: String): ExpandMacroToPathMap {
if (fileUrl.endsWith(".iml")) {
return getModuleLoader(fileUrl).macroExpander.expandMacroMap
}
return projectMacroExpander.expandMacroMap
}
private fun getModuleLoader(imlFileUrl: String): JpsComponentLoader {
val moduleFile = Path(JpsPathUtil.urlToPath(imlFileUrl))
val macroExpander = JpsProjectConfigurationLoading.createModuleMacroExpander(pathVariables, moduleFile)
//todo cache
return JpsComponentLoader(macroExpander, null)
}
}

View File

@@ -7,23 +7,24 @@ import com.intellij.platform.workspace.jps.serialization.impl.*
import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager
internal class SerializationContextImpl(
override val virtualFileUrlManager: VirtualFileUrlManager
override val virtualFileUrlManager: VirtualFileUrlManager,
override val fileContentReader: JpsFileContentReader,
) : SerializationContext {
override val fileContentReader: JpsFileContentReader
get() = TODO("not implemented")
override val isExternalStorageEnabled: Boolean
get() = false //todo
override val fileInDirectorySourceNames: FileInDirectorySourceNames
get() = FileInDirectorySourceNames.empty()
override val isJavaPluginPresent: Boolean
get() = true //todo?
get() = true
override val customModuleComponentSerializers: List<CustomModuleComponentSerializer>
get() = emptyList() //todo
override val customModuleRootsSerializers: List<CustomModuleRootsSerializer>
get() = emptyList() //todo
override val customFacetRelatedEntitySerializers: List<CustomFacetRelatedEntitySerializer<*>>
get() = listOf(DefaultFacetEntitySerializer()) //todo
}