diff --git a/platform/configuration-store-impl/src/ProjectStoreBridge.kt b/platform/configuration-store-impl/src/ProjectStoreBridge.kt index 43d9882c5905..3927f2870c2c 100644 --- a/platform/configuration-store-impl/src/ProjectStoreBridge.kt +++ b/platform/configuration-store-impl/src/ProjectStoreBridge.kt @@ -126,6 +126,11 @@ internal class StorageJpsConfigurationReader(private val project: Project, override fun loadComponent(fileUrl: String, componentName: String, customModuleFilePath: String?): Element? { val filePath = JpsPathUtil.urlToPath(fileUrl) + if (componentName == "") { + //this is currently used for loading Eclipse project configuration from .classpath file + val file = VirtualFileManager.getInstance().findFileByUrl(fileUrl) + return file?.inputStream?.use { JDOMUtil.load(it) } + } if (FileUtil.extensionEquals(filePath, "iml") || isExternalModuleFile(filePath)) { //todo fetch data from ModuleStore (https://jetbrains.team/p/wm/issues/51) return getCachingReader().loadComponent(fileUrl, componentName, customModuleFilePath) diff --git a/platform/workspaceModel/ide/src/META-INF/WorkspaceModelExtensions.xml b/platform/workspaceModel/ide/src/META-INF/WorkspaceModelExtensions.xml index 754c0eadd10e..6f6f5f7bbcfd 100644 --- a/platform/workspaceModel/ide/src/META-INF/WorkspaceModelExtensions.xml +++ b/platform/workspaceModel/ide/src/META-INF/WorkspaceModelExtensions.xml @@ -1,4 +1,12 @@ + + + + diff --git a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomModuleComponentSerializer.kt b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomModuleComponentSerializer.kt new file mode 100644 index 000000000000..98d2c698ca0d --- /dev/null +++ b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomModuleComponentSerializer.kt @@ -0,0 +1,23 @@ +// Copyright 2000-2020 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 com.intellij.workspaceModel.ide.impl.jps.serialization + +import com.intellij.workspaceModel.storage.VirtualFileUrl +import com.intellij.workspaceModel.storage.VirtualFileUrlManager +import com.intellij.workspaceModel.storage.WorkspaceEntityStorageBuilder +import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity +import org.jetbrains.annotations.ApiStatus + +/** + * This extension supports loading and saving additional settings from *.iml files to workspace model. + */ +@ApiStatus.Internal +interface CustomModuleComponentSerializer { + fun loadComponent(builder: WorkspaceEntityStorageBuilder, + moduleEntity: ModuleEntity, + reader: JpsFileContentReader, + imlFileUrl: VirtualFileUrl, + errorReporter: ErrorReporter, + virtualFileManager: VirtualFileUrlManager) + + fun saveComponent(moduleEntity: ModuleEntity, imlFileUrl: VirtualFileUrl, writer: JpsFileContentWriter) +} \ No newline at end of file diff --git a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomModuleRootsSerializer.kt b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomModuleRootsSerializer.kt new file mode 100644 index 000000000000..14747d0b580c --- /dev/null +++ b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomModuleRootsSerializer.kt @@ -0,0 +1,40 @@ +// Copyright 2000-2020 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 com.intellij.workspaceModel.ide.impl.jps.serialization + +import com.intellij.workspaceModel.ide.JpsFileEntitySource +import com.intellij.workspaceModel.storage.* +import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity +import org.jetbrains.annotations.ApiStatus + +/** + * This extension supports loading and saving module's dependencies in custom format instead of the standard NewModuleRootManager component + * in *.iml file. + */ +@ApiStatus.Internal +interface CustomModuleRootsSerializer { + /** + * Corresponds to value of 'classpath' module option + */ + val id: String + + fun createEntitySource(imlFileUrl: VirtualFileUrl, + internalEntitySource: JpsFileEntitySource, + customDir: String?, + virtualFileManager: VirtualFileUrlManager): EntitySource? + + fun loadRoots(builder: WorkspaceEntityStorageBuilder, + moduleEntity: ModuleEntity, + reader: JpsFileContentReader, + customDir: String?, + imlFileUrl: VirtualFileUrl, + internalModuleListSerializer: JpsModuleListSerializer?, + errorReporter: ErrorReporter, + virtualFileManager: VirtualFileUrlManager) + + fun saveRoots(module: ModuleEntity, + entities: Map, List>, + writer: JpsFileContentWriter, + customDir: String?, + imlFileUrl: VirtualFileUrl, + storage: WorkspaceEntityStorage) +} \ No newline at end of file diff --git a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/ModuleImlFileEntitiesSerializer.kt b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/ModuleImlFileEntitiesSerializer.kt index d7018973e763..b09691eae283 100644 --- a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/ModuleImlFileEntitiesSerializer.kt +++ b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/ModuleImlFileEntitiesSerializer.kt @@ -59,12 +59,12 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod reader: JpsFileContentReader, errorReporter: ErrorReporter, virtualFileManager: VirtualFileUrlManager) { val externalStorageEnabled = externalStorageConfigurationManager?.isEnabled ?: false if (!externalStorageEnabled) { - val moduleEntity = loadModuleEntity(reader, builder, virtualFileManager) + val moduleEntity = loadModuleEntity(reader, builder, errorReporter, virtualFileManager) if (moduleEntity != null) createFacetSerializer().loadFacetEntities(builder, moduleEntity, reader) } else { val externalSerializer = externalModuleListSerializer?.createSerializer(internalEntitySource, fileUrl, modulePath.group) as ModuleImlFileEntitiesSerializer? - val moduleEntity = externalSerializer?.loadModuleEntity(reader, builder, virtualFileManager) - ?: loadModuleEntity(reader, builder, virtualFileManager) + val moduleEntity = externalSerializer?.loadModuleEntity(reader, builder, errorReporter, virtualFileManager) + ?: loadModuleEntity(reader, builder, errorReporter, virtualFileManager) if (moduleEntity != null) { createFacetSerializer().loadFacetEntities(builder, moduleEntity, reader) externalSerializer?.createFacetSerializer()?.loadFacetEntities(builder, moduleEntity, reader) @@ -74,6 +74,7 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod private fun loadModuleEntity(reader: JpsFileContentReader, builder: WorkspaceEntityStorageBuilder, + errorReporter: ErrorReporter, virtualFileManager: VirtualFileUrlManager): ModuleEntity? { if (skipLoadingIfFileDoesNotExist) { val fileExists = fileUrl.file?.exists() ?: false @@ -82,7 +83,18 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod val moduleOptions = readModuleOptions(reader) val (externalSystemOptions, externalSystemId) = readExternalSystemOptions(reader, moduleOptions) - val entitySource = createEntitySource(externalSystemId) + + val customRootsSerializer = moduleOptions[JpsProjectLoader.CLASSPATH_ATTRIBUTE]?.let { customSerializerId -> + val serializer = CUSTOM_ROOTS_SERIALIZER_EP.extensions().filter { it.id == customSerializerId }.findAny().orElse(null) + if (serializer == null) { + LOG.warn("Classpath storage provider $customSerializerId not found") + } + return@let serializer + } + + val customDir = moduleOptions[JpsProjectLoader.CLASSPATH_DIR_ATTRIBUTE] + val entitySource = customRootsSerializer?.createEntitySource(fileUrl, internalEntitySource, customDir, virtualFileManager) + ?: createEntitySource(externalSystemId) val moduleEntity = builder.addModuleEntity(modulePath.moduleName, listOf(ModuleDependencyItem.ModuleSourceDependency), entitySource) val moduleGroup = modulePath.group if (moduleGroup != null) { @@ -102,12 +114,21 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod builder.addModuleCustomImlDataEntity(null, customModuleOptions, moduleEntity, entitySource) } - loadExternalSystemOptions(builder, moduleEntity, reader, externalSystemOptions, externalSystemId, entitySource) - - val rootManagerElement = reader.loadComponent(fileUrl.url, MODULE_ROOT_MANAGER_COMPONENT_NAME, getBaseDirPath())?.clone() - if (rootManagerElement != null) { - loadRootManager(rootManagerElement, moduleEntity, builder, virtualFileManager) + CUSTOM_MODULE_COMPONENT_SERIALIZER_EP.extensions().forEach { + it.loadComponent(builder, moduleEntity, reader, fileUrl, errorReporter, virtualFileManager) } + if (customRootsSerializer != null) { + customRootsSerializer.loadRoots(builder, moduleEntity, reader, customDir, fileUrl, internalModuleListSerializer, errorReporter, virtualFileManager) + } + else { + loadExternalSystemOptions(builder, moduleEntity, reader, externalSystemOptions, externalSystemId, entitySource) + + val rootManagerElement = reader.loadComponent(fileUrl.url, MODULE_ROOT_MANAGER_COMPONENT_NAME, getBaseDirPath())?.clone() + if (rootManagerElement != null) { + loadRootManager(rootManagerElement, moduleEntity, builder, virtualFileManager) + } + } + return moduleEntity } @@ -332,14 +353,34 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod entities: Map, List>, storage: WorkspaceEntityStorage, writer: JpsFileContentWriter) { - val rootManagerElement = JDomSerializationUtil.createComponentElement(MODULE_ROOT_MANAGER_COMPONENT_NAME) val externalSystemOptions = module.externalSystemOptions val customImlData = module.customImlData saveModuleOptions(externalSystemOptions, module.type, customImlData, writer) - if (customImlData?.customModuleOptions?.containsKey(JpsProjectLoader.CLASSPATH_ATTRIBUTE) == true) { - return + val moduleOptions = customImlData?.customModuleOptions + val customSerializerId = moduleOptions?.get(JpsProjectLoader.CLASSPATH_ATTRIBUTE) + if (customSerializerId != null) { + val serializer = CUSTOM_ROOTS_SERIALIZER_EP.extensions().filter { it.id == customSerializerId }.findAny().orElse(null) + if (serializer != null) { + val customDir = moduleOptions[JpsProjectLoader.CLASSPATH_DIR_ATTRIBUTE] + serializer.saveRoots(module, entities, writer, customDir, fileUrl, storage) + } + else { + LOG.warn("Classpath storage provider $customSerializerId not found") + } } + else { + saveRootManagerElement(module, customImlData, entities, writer) + } + CUSTOM_MODULE_COMPONENT_SERIALIZER_EP.extensions().forEach { + it.saveComponent(module, fileUrl, writer) + } + } + private fun saveRootManagerElement(module: ModuleEntity, + customImlData: ModuleCustomImlDataEntity?, + entities: Map, List>, + writer: JpsFileContentWriter) { + val rootManagerElement = JDomSerializationUtil.createComponentElement(MODULE_ROOT_MANAGER_COMPONENT_NAME) saveJavaSettings(module.javaSettings, rootManagerElement) if (customImlData != null) { @@ -547,6 +588,8 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod get() = listOf(SourceRootOrderEntity::class.java) companion object { + private val LOG = logger() + // The comparator has reversed priority. So, the last entry of this list will be printed as a first attribute in the xml tag. private val orderOfKnownAttributes = listOf( INHERIT_COMPILER_OUTPUT_ATTRIBUTE, @@ -559,6 +602,9 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod private val knownAttributesComparator = Comparator { o1, o2 -> orderOfKnownAttributes.indexOf(o1.name).compareTo(orderOfKnownAttributes.indexOf(o2.name)) }.reversed() + + private val CUSTOM_ROOTS_SERIALIZER_EP = ExtensionPointName.create("com.intellij.workspaceModel.customModuleRootsSerializer") + private val CUSTOM_MODULE_COMPONENT_SERIALIZER_EP = ExtensionPointName.create("com.intellij.workspaceModel.customModuleComponentSerializer") } }