diff --git a/platform/configuration-store-impl/src/ProjectStoreBridge.kt b/platform/configuration-store-impl/src/ProjectStoreBridge.kt index 18c9ee73367e..43d9882c5905 100644 --- a/platform/configuration-store-impl/src/ProjectStoreBridge.kt +++ b/platform/configuration-store-impl/src/ProjectStoreBridge.kt @@ -2,6 +2,8 @@ package com.intellij.configurationStore import com.intellij.openapi.components.* +import com.intellij.openapi.components.impl.ModulePathMacroManager +import com.intellij.openapi.components.impl.ProjectPathMacroManager import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil import com.intellij.openapi.components.impl.stores.IProjectStore import com.intellij.openapi.project.Project @@ -16,6 +18,7 @@ import com.intellij.workspaceModel.ide.impl.jps.serialization.* import org.jdom.Element import org.jetbrains.jps.util.JpsPathUtil import java.util.concurrent.ConcurrentHashMap +import java.util.function.Supplier class ProjectStoreBridge(private val project: Project) : ModuleSavingCustomizer { override fun createSaveSessionProducerManager(): ProjectSaveSessionProducerManager { @@ -56,6 +59,16 @@ private class JpsStorageContentWriter(private val session: ProjectWithModulesSav } } } + + override fun getReplacePathMacroMap(fileUrl: String): PathMacroMap { + val filePath = JpsPathUtil.urlToPath(fileUrl) + return if (FileUtil.extensionEquals(filePath, "iml") || isExternalModuleFile(filePath)) { + ModulePathMacroManager.createInstance(Supplier { filePath }).replacePathMap + } + else { + ProjectPathMacroManager.getInstance(project).replacePathMap + } + } } private val MODULE_FILE_STORAGE_ANNOTATION = FileStorageAnnotation(StoragePathMacros.MODULE_FILE, false) diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java index cc0efd100625..4690e50667f3 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java @@ -6,29 +6,42 @@ import com.intellij.openapi.application.PathMacros; import com.intellij.openapi.components.ExpandMacroToPathMap; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.module.Module; +import com.intellij.serviceContainer.NonInjectable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.SystemIndependent; import org.jetbrains.jps.model.serialization.PathMacroUtil; +import java.util.function.Supplier; + public final class ModulePathMacroManager extends PathMacroManager { - private final Module myModule; + private final Supplier<@SystemIndependent String> myModuleDirPointer; public ModulePathMacroManager(@NotNull Module module) { super(PathMacros.getInstance()); - myModule = module; + myModuleDirPointer = module::getModuleFilePath; + } + + @NonInjectable + private ModulePathMacroManager(Supplier<@SystemIndependent String> moduleDirPointer) { + super(PathMacros.getInstance()); + myModuleDirPointer = moduleDirPointer; } @Override public @NotNull ExpandMacroToPathMap getExpandMacroMap() { ExpandMacroToPathMap result = super.getExpandMacroMap(); - addFileHierarchyReplacements(result, PathMacroUtil.MODULE_DIR_MACRO_NAME, PathMacroUtil.getModuleDir(myModule.getModuleFilePath())); + addFileHierarchyReplacements(result, PathMacroUtil.MODULE_DIR_MACRO_NAME, PathMacroUtil.getModuleDir(myModuleDirPointer.get())); return result; } @Override public @NotNull ReplacePathToMacroMap computeReplacePathMap() { ReplacePathToMacroMap result = super.computeReplacePathMap(); - String modulePath = PathMacroUtil.getModuleDir(myModule.getModuleFilePath()); - addFileHierarchyReplacements(result, PathMacroUtil.MODULE_DIR_MACRO_NAME, modulePath, PathMacroUtil.getUserHomePath()); + addFileHierarchyReplacements(result, PathMacroUtil.MODULE_DIR_MACRO_NAME, PathMacroUtil.getModuleDir(myModuleDirPointer.get()), PathMacroUtil.getUserHomePath()); return result; } + + public static ModulePathMacroManager createInstance(Supplier<@SystemIndependent String> moduleDirPointer) { + return new ModulePathMacroManager(moduleDirPointer); + } } \ No newline at end of file diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ProjectPathMacroManager.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ProjectPathMacroManager.java index 5218adceba97..bd97349ca0bb 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ProjectPathMacroManager.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ProjectPathMacroManager.java @@ -6,25 +6,39 @@ import com.intellij.openapi.application.PathMacros; import com.intellij.openapi.components.ExpandMacroToPathMap; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.project.Project; +import com.intellij.serviceContainer.NonInjectable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.SystemIndependent; import org.jetbrains.jps.model.serialization.PathMacroUtil; +import java.util.function.Supplier; + public class ProjectPathMacroManager extends PathMacroManager { - private final Project myProject; + private final Supplier<@SystemIndependent String> myBasePathPointer; + private final @Nullable Supplier<@SystemIndependent String> myNamePointer; public ProjectPathMacroManager(@NotNull Project project) { super(PathMacros.getInstance()); + myBasePathPointer = project::getBasePath; + myNamePointer = !project.isDefault() ? project::getName : null; + } - myProject = project; + @NonInjectable + private ProjectPathMacroManager(Supplier<@SystemIndependent String> basePathPointer, + @Nullable Supplier<@SystemIndependent String> namePointer) { + super(PathMacros.getInstance()); + myBasePathPointer = basePathPointer; + myNamePointer = namePointer; } @NotNull @Override public ExpandMacroToPathMap getExpandMacroMap() { ExpandMacroToPathMap result = super.getExpandMacroMap(); - addFileHierarchyReplacements(result, PathMacroUtil.PROJECT_DIR_MACRO_NAME, myProject.getBasePath()); - if (!myProject.isDefault()) { - result.addMacroExpand(PathMacroUtil.PROJECT_NAME_MACRO_NAME, myProject.getName()); + addFileHierarchyReplacements(result, PathMacroUtil.PROJECT_DIR_MACRO_NAME, myBasePathPointer.get()); + if (myNamePointer != null) { + result.addMacroExpand(PathMacroUtil.PROJECT_NAME_MACRO_NAME, myNamePointer.get()); } return result; } @@ -33,7 +47,12 @@ public class ProjectPathMacroManager extends PathMacroManager { @Override protected ReplacePathToMacroMap computeReplacePathMap() { ReplacePathToMacroMap result = super.computeReplacePathMap(); - addFileHierarchyReplacements(result, PathMacroUtil.PROJECT_DIR_MACRO_NAME, myProject.getBasePath(), null); + addFileHierarchyReplacements(result, PathMacroUtil.PROJECT_DIR_MACRO_NAME, myBasePathPointer.get(), null); return result; } + + public static ProjectPathMacroManager createInstance(Supplier<@SystemIndependent String> basePathPointer, + @Nullable Supplier<@SystemIndependent String> namePointer) { + return new ProjectPathMacroManager(basePathPointer, namePointer); + } } diff --git a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSerializersImpl.kt b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSerializersImpl.kt index 78d59b7e7ba7..13e5dad0a8b0 100644 --- a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSerializersImpl.kt +++ b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSerializersImpl.kt @@ -1,10 +1,10 @@ // 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.application.options.ReplacePathToMacroMap -import com.intellij.openapi.application.PathMacros import com.intellij.openapi.components.ExpandMacroToPathMap import com.intellij.openapi.components.PathMacroManager +import com.intellij.openapi.components.impl.ModulePathMacroManager +import com.intellij.openapi.components.impl.ProjectPathMacroManager import com.intellij.openapi.module.impl.ModulePath import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.VfsUtilCore @@ -25,7 +25,6 @@ import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import org.jdom.Element import org.jetbrains.annotations.TestOnly -import org.jetbrains.jps.model.serialization.PathMacroUtil import org.jetbrains.jps.util.JpsPathUtil import java.nio.file.Files import java.nio.file.Path @@ -457,7 +456,7 @@ class JpsProjectSerializersImpl(directorySerializersFactories: List>() override fun loadComponent(fileUrl: String, componentName: String, customModuleFilePath: String?): Element? { @@ -482,46 +481,12 @@ class CachingJpsFileContentReader(projectBaseDirUrl: String) : JpsFileContentRea customModuleFilePath: String?): PathMacroManager { val path = JpsPathUtil.urlToPath(fileUrl) return if (FileUtil.extensionEquals(fileUrl, "iml") || isExternalModuleFile(path)) { - ModulePathMacroManagerBridge(PathMacros.getInstance(), customModuleFilePath ?: path) + ModulePathMacroManager.createInstance { customModuleFilePath ?: path } } else { projectPathMacroManager } } - - internal class ModulePathMacroManagerBridge(pathMacros: PathMacros, private val moduleFilePath: String) : PathMacroManager( - pathMacros) { - override fun getExpandMacroMap(): ExpandMacroToPathMap { - val result = super.getExpandMacroMap() - addFileHierarchyReplacements( - result, PathMacroUtil.MODULE_DIR_MACRO_NAME, - PathMacroUtil.getModuleDir(moduleFilePath)) - return result - } - - public override fun computeReplacePathMap(): ReplacePathToMacroMap { - val result = super.computeReplacePathMap() - val modulePath = PathMacroUtil.getModuleDir(moduleFilePath) - addFileHierarchyReplacements( - result, PathMacroUtil.MODULE_DIR_MACRO_NAME, modulePath, - PathMacroUtil.getUserHomePath()) - return result - } - } - - internal class ProjectPathMacroManagerBridge(private val projectDirPath: String) : PathMacroManager(PathMacros.getInstance()) { - override fun getExpandMacroMap(): ExpandMacroToPathMap { - val result = super.getExpandMacroMap() - addFileHierarchyReplacements(result, PathMacroUtil.PROJECT_DIR_MACRO_NAME, projectDirPath) - return result - } - - override fun computeReplacePathMap(): ReplacePathToMacroMap { - val result = super.computeReplacePathMap() - addFileHierarchyReplacements(result, PathMacroUtil.PROJECT_DIR_MACRO_NAME, projectDirPath, null) - return result - } - } } // TODO Add more diagnostics: file path, line etc diff --git a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsFormatEntitiesSerialization.kt b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsFormatEntitiesSerialization.kt index 33cecc12a76d..aa7ceb9527b8 100644 --- a/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsFormatEntitiesSerialization.kt +++ b/platform/workspaceModel/ide/src/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsFormatEntitiesSerialization.kt @@ -2,6 +2,7 @@ package com.intellij.workspaceModel.ide.impl.jps.serialization import com.intellij.openapi.components.ExpandMacroToPathMap +import com.intellij.openapi.components.PathMacroMap import com.intellij.openapi.module.impl.ModulePath import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity import com.intellij.workspaceModel.ide.JpsFileEntitySource @@ -21,6 +22,7 @@ interface JpsFileContentReader { interface JpsFileContentWriter { fun saveComponent(fileUrl: String, componentName: String, componentTag: Element?) + fun getReplacePathMacroMap(fileUrl: String): PathMacroMap } /** diff --git a/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSaveAfterChangesTest.kt b/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSaveAfterChangesTest.kt index d8d7704f46e7..60e32894b0f0 100644 --- a/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSaveAfterChangesTest.kt +++ b/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/JpsProjectSaveAfterChangesTest.kt @@ -157,9 +157,9 @@ class JpsProjectSaveAfterChangesTest { is EntityChange.Replaced -> listOf(change.oldEntity, change.newEntity) } }.map { it.entitySource }} - val writer = JpsFileContentWriterImpl() + val writer = JpsFileContentWriterImpl(projectData.projectDir) projectData.serializers.saveEntities(builder.toStorage(), changedSources, writer) - writer.writeFiles(projectData.projectDir) + writer.writeFiles() projectData.serializers.checkConsistency(projectData.projectDirUrl, builder.toStorage(), virtualFileManager) val expectedDir = FileUtil.createTempDirectory("jpsProjectTest", "expected") diff --git a/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsTestUtils.kt b/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsTestUtils.kt index e9d61b14e2fc..fa4ecc603e65 100644 --- a/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsTestUtils.kt +++ b/platform/workspaceModel/ide/tests/testSrc/com/intellij/workspaceModel/ide/impl/jps/serialization/jpsTestUtils.kt @@ -3,6 +3,9 @@ package com.intellij.workspaceModel.ide.impl.jps.serialization import com.intellij.openapi.application.PathMacros import com.intellij.openapi.application.ex.PathManagerEx +import com.intellij.openapi.components.PathMacroMap +import com.intellij.openapi.components.impl.ModulePathMacroManager +import com.intellij.openapi.components.impl.ProjectPathMacroManager import com.intellij.openapi.util.JDOMUtil import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.systemIndependentPath @@ -62,74 +65,9 @@ internal fun loadProject(configLocation: JpsProjectConfigLocation, originalBui } internal fun JpsProjectSerializersImpl.saveAllEntities(storage: WorkspaceEntityStorage, projectDir: File) { - val writer = JpsFileContentWriterImpl() + val writer = JpsFileContentWriterImpl(projectDir) saveAllEntities(storage, writer) - writer.writeFiles(projectDir) -} - -internal fun JpsFileContentWriterImpl.writeFiles(baseProjectDir: File) { - urlToComponents.forEach { (url, newComponents) -> - val components = HashMap(newComponents) - val file = JpsPathUtil.urlToFile(url) - - val isModuleFile = FileUtil.extensionEquals(file.absolutePath, "iml") - || file.parentFile.name == "modules" && file.parentFile.parentFile.name != ".idea" - val replaceMacroMap = if (isModuleFile) - CachingJpsFileContentReader.ModulePathMacroManagerBridge(PathMacros.getInstance(), JpsPathUtil.urlToOsPath(url)).replacePathMap - else - CachingJpsFileContentReader.ProjectPathMacroManagerBridge(baseProjectDir.systemIndependentPath).replacePathMap - - - val newRootElement = when { - isModuleFile -> Element("module") - FileUtil.filesEqual(File(baseProjectDir, ".idea"), file.parentFile.parentFile) -> null - else -> Element("project") - } - - fun isEmptyComponentTag(componentTag: Element) = componentTag.contentSize == 0 && componentTag.attributes.all { it.name == "name" } - - val rootElement: Element? - if (newRootElement != null) { - if (file.exists()) { - val oldElement = JDOMUtil.load(file) - oldElement.getChildren("component") - .filterNot { it.getAttributeValue("name") in components } - .map { it.clone() } - .associateByTo(components) { it.getAttributeValue("name") } - } - components.entries.sortedBy { it.key }.forEach { (name, element) -> - if (element != null && !isEmptyComponentTag(element)) { - if (name == "DeprecatedModuleOptionManager") { - element.getChildren("option").forEach { - newRootElement.setAttribute(it.getAttributeValue("key")!!, it.getAttributeValue("value")!!) - } - } - else { - newRootElement.addContent(element) - } - } - } - if (!JDOMUtil.isEmpty(newRootElement)) { - newRootElement.setAttribute("version", "4") - rootElement = newRootElement - } - else { - rootElement = null - } - } - else { - val singleComponent = components.values.single() - rootElement = if (singleComponent != null && !isEmptyComponentTag(singleComponent)) singleComponent else null - } - if (rootElement != null) { - replaceMacroMap.substitute(rootElement, true, true) - FileUtil.createParentDirs(file) - JDOMUtil.write(rootElement, file) - } - else { - FileUtil.delete(file) - } - } + writer.writeFiles() } internal fun assertDirectoryMatches(actualDir: File, expectedDir: File, filesToIgnore: Set, componentsToIgnore: List) { @@ -228,12 +166,79 @@ internal fun toConfigLocation(file: Path, virtualFileManager: VirtualFileUrlMana } } -internal class JpsFileContentWriterImpl : JpsFileContentWriter { +internal class JpsFileContentWriterImpl(private val baseProjectDir: File) : JpsFileContentWriter { val urlToComponents = LinkedHashMap>() override fun saveComponent(fileUrl: String, componentName: String, componentTag: Element?) { urlToComponents.computeIfAbsent(fileUrl) { LinkedHashMap() }[componentName] = componentTag } + + override fun getReplacePathMacroMap(fileUrl: String): PathMacroMap { + return if (isModuleFile(JpsPathUtil.urlToFile(fileUrl))) + ModulePathMacroManager.createInstance { JpsPathUtil.urlToOsPath(fileUrl) }.replacePathMap + else + ProjectPathMacroManager.createInstance({ baseProjectDir.systemIndependentPath }, null).replacePathMap + } + + internal fun writeFiles() { + urlToComponents.forEach { (url, newComponents) -> + val components = HashMap(newComponents) + val file = JpsPathUtil.urlToFile(url) + val replaceMacroMap = getReplacePathMacroMap(url) + val newRootElement = when { + isModuleFile(file) -> Element("module") + FileUtil.filesEqual(File(baseProjectDir, ".idea"), file.parentFile.parentFile) -> null + else -> Element("project") + } + + fun isEmptyComponentTag(componentTag: Element) = componentTag.contentSize == 0 && componentTag.attributes.all { it.name == "name" } + + val rootElement: Element? + if (newRootElement != null) { + if (file.exists()) { + val oldElement = JDOMUtil.load(file) + oldElement.getChildren("component") + .filterNot { it.getAttributeValue("name") in components } + .map { it.clone() } + .associateByTo(components) { it.getAttributeValue("name") } + } + components.entries.sortedBy { it.key }.forEach { (name, element) -> + if (element != null && !isEmptyComponentTag(element)) { + if (name == "DeprecatedModuleOptionManager") { + element.getChildren("option").forEach { + newRootElement.setAttribute(it.getAttributeValue("key")!!, it.getAttributeValue("value")!!) + } + } + else { + newRootElement.addContent(element) + } + } + } + if (!JDOMUtil.isEmpty(newRootElement)) { + newRootElement.setAttribute("version", "4") + rootElement = newRootElement + } + else { + rootElement = null + } + } + else { + val singleComponent = components.values.single() + rootElement = if (singleComponent != null && !isEmptyComponentTag(singleComponent)) singleComponent else null + } + if (rootElement != null) { + replaceMacroMap.substitute(rootElement, true, true) + FileUtil.createParentDirs(file) + JDOMUtil.write(rootElement, file) + } + else { + FileUtil.delete(file) + } + } + } + + private fun isModuleFile(file: File) = (FileUtil.extensionEquals(file.absolutePath, "iml") + || file.parentFile.name == "modules" && file.parentFile.parentFile.name != ".idea") } internal object TestErrorReporter : ErrorReporter {