From e48687b0256b3216bc8fe9c2fd81387be2143bba Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 27 Apr 2017 15:35:26 +0300 Subject: [PATCH] project model: distinguish libraries imported from external models This information can be used to show a warning if user tries to manually change roots of a library imported from an external model in the Project Structure dialog (IDEA-171948). This information won't be saved in library configuration files until we implement functionality to store such libraries separately (and enable 'store.imported.project.elements.separately' registry key), because otherwise it would lead to massive changes in files under .idea/libraries. --- .../projectRoot/LibrariesModifiableModel.java | 10 ++++- .../project/IdeModifiableModelsProvider.java | 3 ++ .../util/ExternalSystemApiUtil.java | 6 +++ .../AbstractIdeModifiableModelsProvider.java | 5 +++ .../project/manage/LibraryDataService.java | 2 +- .../ExternalProjectSystemRegistryImpl.java | 31 +++++++++++++-- .../roots/ExternalProjectSystemRegistry.java | 8 ++++ .../roots/ProjectModelExternalSource.java | 3 ++ .../openapi/roots/libraries/Library.java | 3 +- .../openapi/roots/libraries/LibraryTable.java | 3 ++ .../impl/ModuleLibraryOrderEntryImpl.java | 5 ++- .../roots/impl/ModuleLibraryTable.java | 10 ++++- .../roots/impl/libraries/LibraryImpl.java | 38 +++++++++++++++---- .../impl/libraries/LibraryTableBase.java | 10 ++++- .../impl/libraries/LibraryTableImplUtil.java | 8 ++-- .../impl/library/JpsLibraryDelegate.java | 8 ++++ .../impl/library/JpsLibraryTableImpl.java | 6 +++ .../util/resources/misc/registry.properties | 4 ++ .../maven/importing/MavenModuleImporter.java | 2 +- .../importing/MavenRootModelAdapter.java | 7 +++- 20 files changed, 143 insertions(+), 29 deletions(-) diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesModifiableModel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesModifiableModel.java index dd4cd4212c45..f95672041fd8 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesModifiableModel.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesModifiableModel.java @@ -17,6 +17,7 @@ package com.intellij.openapi.roots.ui.configuration.projectRoot; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectModelExternalSource; import com.intellij.openapi.roots.impl.libraries.LibraryImpl; import com.intellij.openapi.roots.impl.libraries.LibraryTableBase; import com.intellij.openapi.roots.libraries.Library; @@ -62,8 +63,13 @@ public class LibrariesModifiableModel implements LibraryTableBase.ModifiableMode @Override public Library createLibrary(String name, @Nullable PersistentLibraryKind type) { - final Library library = getLibrariesModifiableModel().createLibrary(name, type); - //createLibraryEditor(library); \ + return createLibrary(name, type, null); + } + + @Override + public Library createLibrary(String name, @Nullable PersistentLibraryKind type, @Nullable ProjectModelExternalSource externalSource) { + final Library library = getLibrariesModifiableModel().createLibrary(name, type, externalSource); + //createLibraryEditor(library); final BaseLibrariesConfigurable configurable = ProjectStructureConfigurable.getInstance(myProject).getConfigurableFor(library); configurable.createLibraryNode(library); return library; diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java index 03c918416c70..170f507097a5 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java @@ -21,6 +21,7 @@ import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.ProjectModelExternalSource; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.packaging.artifacts.ModifiableArtifactModel; @@ -59,6 +60,8 @@ public interface IdeModifiableModelsProvider extends IdeModelsProvider { Library createLibrary(String name); + Library createLibrary(String name, @Nullable ProjectModelExternalSource externalSource); + void removeLibrary(Library library); ModalityState getModalityStateForQuestionDialogs(); diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java index fff1bfd5ced6..3531aa280f30 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java @@ -37,8 +37,10 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ExternalProjectSystemRegistry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ProjectModelExternalSource; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Conditions; @@ -794,6 +796,10 @@ public class ExternalSystemApiUtil { return (T)loader.loadClass(clazz.getName()).newInstance(); } + public static ProjectModelExternalSource toExternalSource(@NotNull ProjectSystemId systemId) { + return ExternalProjectSystemRegistry.getInstance().getSourceById(systemId.getId()); + } + @Contract(value = "_, null -> false", pure=true) public static boolean isExternalSystemAwareModule(@NotNull ProjectSystemId systemId, @Nullable Module module) { return module != null && !module.isDisposed() && systemId.getId().equals(module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY)); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java index 11e0b0c999c8..90e214954583 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java @@ -236,6 +236,11 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi return getModifiableProjectLibrariesModel().createLibrary(name); } + @Override + public Library createLibrary(String name, @Nullable ProjectModelExternalSource externalSource) { + return getModifiableProjectLibrariesModel().createLibrary(name, null, externalSource); + } + @Override public void removeLibrary(Library library) { getModifiableProjectLibrariesModel().removeLibrary(library); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java index 00d5b7fa93a8..b48cc4e9304d 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java @@ -78,7 +78,7 @@ public class LibraryDataService extends AbstractProjectDataService () -> StringUtil.capitalize(sourceId.toLowerCase(Locale.US))); + public ProjectModelExternalSource getSourceById(String id) { + return myExternalSources.computeIfAbsent(id, ProjectModelExternalSourceImpl::new); + } + + private static class ProjectModelExternalSourceImpl implements ProjectModelExternalSource { + private final String myId; + private final String myDisplayName; + + public ProjectModelExternalSourceImpl(String id) { + myId = id; + //todo[nik] specify display name explicitly instead, the current code is copied from ProjectSystemId constructor + myDisplayName = StringUtil.capitalize(myId.toLowerCase(Locale.US)); + } + + @NotNull + @Override + public String getDisplayName() { + return myDisplayName; + } + + @NotNull + @Override + public String getId() { + return myId; + } } } diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/ExternalProjectSystemRegistry.java b/platform/projectModel-api/src/com/intellij/openapi/roots/ExternalProjectSystemRegistry.java index a173d106c1b5..166c4d2b0b7b 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/roots/ExternalProjectSystemRegistry.java +++ b/platform/projectModel-api/src/com/intellij/openapi/roots/ExternalProjectSystemRegistry.java @@ -18,6 +18,7 @@ package com.intellij.openapi.roots; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.Module; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -29,9 +30,16 @@ public interface ExternalProjectSystemRegistry { return ServiceManager.getService(ExternalProjectSystemRegistry.class); } + @NotNull + ProjectModelExternalSource getSourceById(String id); + @Nullable ProjectModelExternalSource getExternalSource(Module module); + /** + * These fields are temporary added to API until we have proper extension points for different external systems. + */ + String MAVEN_EXTERNAL_SOURCE_ID = "Maven"; String EXTERNAL_SYSTEM_ID_KEY = "external.system.id"; String IS_MAVEN_MODULE_KEY = "org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule"; } diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectModelExternalSource.java b/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectModelExternalSource.java index 5aa3d9279806..4d116febd617 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectModelExternalSource.java +++ b/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectModelExternalSource.java @@ -28,4 +28,7 @@ import org.jetbrains.annotations.NotNull; public interface ProjectModelExternalSource { @NotNull String getDisplayName(); + + @NotNull + String getId(); } diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/Library.java b/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/Library.java index 8a2bda7ade0f..66ee8e2f570d 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/Library.java +++ b/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/Library.java @@ -17,6 +17,7 @@ package com.intellij.openapi.roots.libraries; import com.intellij.openapi.Disposable; import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ProjectModelElement; import com.intellij.openapi.roots.RootProvider; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.vfs.VirtualFile; @@ -27,7 +28,7 @@ import org.jetbrains.annotations.Nullable; /** * @author dsl */ -public interface Library extends JDOMExternalizable, Disposable { +public interface Library extends JDOMExternalizable, Disposable, ProjectModelElement { Library[] EMPTY_ARRAY = new Library[0]; @Nullable String getName(); diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/LibraryTable.java b/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/LibraryTable.java index 02e0d9faa6c9..c8da4d68f2ad 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/LibraryTable.java +++ b/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/LibraryTable.java @@ -16,6 +16,7 @@ package com.intellij.openapi.roots.libraries; import com.intellij.openapi.Disposable; +import com.intellij.openapi.roots.ProjectModelExternalSource; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -71,6 +72,8 @@ public interface LibraryTable { Library createLibrary(String name, @Nullable PersistentLibraryKind type); + Library createLibrary(String name, @Nullable PersistentLibraryKind type, @Nullable ProjectModelExternalSource externalSource); + void removeLibrary(@NotNull Library library); void commit(); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryOrderEntryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryOrderEntryImpl.java index 94304fa8941d..10cd2130a301 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryOrderEntryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryOrderEntryImpl.java @@ -55,9 +55,10 @@ public class ModuleLibraryOrderEntryImpl extends LibraryOrderEntryBaseImpl imple myScope = scope; } - ModuleLibraryOrderEntryImpl(String name, final PersistentLibraryKind kind, @NotNull RootModelImpl rootModel, @NotNull ProjectRootManagerImpl projectRootManager) { + ModuleLibraryOrderEntryImpl(String name, final PersistentLibraryKind kind, @NotNull RootModelImpl rootModel, + @NotNull ProjectRootManagerImpl projectRootManager, ProjectModelExternalSource externalSource) { super(rootModel, projectRootManager); - myLibrary = LibraryTableImplUtil.createModuleLevelLibrary(name, kind, getRootModel()); + myLibrary = LibraryTableImplUtil.createModuleLevelLibrary(name, kind, getRootModel(), externalSource); doinit(); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryTable.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryTable.java index beed83d17e78..a3f3807e52ce 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryTable.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleLibraryTable.java @@ -21,6 +21,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.LibraryOrderEntry; import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.roots.ProjectModelExternalSource; import com.intellij.openapi.roots.impl.libraries.LibraryTableBase; import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil; import com.intellij.openapi.roots.libraries.Library; @@ -90,8 +91,13 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi } @Override - public Library createLibrary(String name, @Nullable PersistentLibraryKind kind) { - LibraryOrderEntry orderEntry = new ModuleLibraryOrderEntryImpl(name, kind, myRootModel, myProjectRootManager); + public Library createLibrary(String name, @Nullable PersistentLibraryKind type) { + return createLibrary(name, type, null); + } + + @Override + public Library createLibrary(String name, @Nullable PersistentLibraryKind kind, @Nullable ProjectModelExternalSource externalSource) { + LibraryOrderEntry orderEntry = new ModuleLibraryOrderEntryImpl(name, kind, myRootModel, myProjectRootManager, externalSource); myRootModel.addOrderEntry(orderEntry); return orderEntry.getLibrary(); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java index b4a101d56b2f..bdd6dc80671c 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java @@ -20,13 +20,12 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ComponentSerializationUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; -import com.intellij.openapi.roots.ModifiableRootModel; -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.RootProvider; +import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.RootModelImpl; import com.intellij.openapi.roots.impl.RootProviderBaseImpl; import com.intellij.openapi.roots.libraries.*; import com.intellij.openapi.util.*; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -64,6 +63,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi @NonNls public static final String PROPERTIES_ELEMENT = "properties"; private static final SkipDefaultValuesSerializationFilters SERIALIZATION_FILTERS = new SkipDefaultValuesSerializationFilters(); private static final String EXCLUDED_ROOTS_TAG = "excluded"; + private static final String EXTERNAL_SYSTEM_ID_ATTRIBUTE = "external-system-id"; private String myName; private final LibraryTable myLibraryTable; private final Map myRoots; @@ -78,15 +78,22 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi private boolean myDisposed; private final Disposable myPointersDisposable = Disposer.newDisposable(); private final JarDirectoryWatcher myRootsWatcher = JarDirectoryWatcherFactory.getInstance().createWatcher(myJarDirectories, myRootProvider); + private final ProjectModelExternalSource myExternalSource; LibraryImpl(LibraryTable table, @NotNull Element element, ModifiableRootModel rootModel) throws InvalidDataException { - this(table, rootModel, null, element.getAttributeValue(LIBRARY_NAME_ATTR), findPersistentLibraryKind(element)); + this(table, rootModel, null, element.getAttributeValue(LIBRARY_NAME_ATTR), findPersistentLibraryKind(element), findExternalSource(element)); readProperties(element); myJarDirectories.readExternal(element); readRoots(element); myRootsWatcher.updateWatchedRoots(); } + @Nullable + private static ProjectModelExternalSource findExternalSource(Element element) { + @Nullable String externalSourceId = element.getAttributeValue(EXTERNAL_SYSTEM_ID_ATTRIBUTE); + return externalSourceId != null ? ExternalProjectSystemRegistry.getInstance().getSourceById(externalSourceId) : null; + } + @Nullable private static PersistentLibraryKind findPersistentLibraryKind(@NotNull Element element) { String typeString = element.getAttributeValue(LIBRARY_TYPE_ATTR); @@ -98,15 +105,16 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi return (PersistentLibraryKind)kind; } - LibraryImpl(String name, @Nullable final PersistentLibraryKind kind, LibraryTable table, ModifiableRootModel rootModel) { - this(table, rootModel, null, name, kind); + LibraryImpl(String name, @Nullable final PersistentLibraryKind kind, LibraryTable table, ModifiableRootModel rootModel, + ProjectModelExternalSource externalSource) { + this(table, rootModel, null, name, kind, externalSource); if (kind != null) { myProperties = kind.createDefaultProperties(); } } private LibraryImpl(@NotNull LibraryImpl from, LibraryImpl newSource, ModifiableRootModel rootModel) { - this(from.myLibraryTable, rootModel, newSource, from.myName, from.myKind); + this(from.myLibraryTable, rootModel, newSource, from.myName, from.myKind, from.myExternalSource); from.checkDisposed(); if (from.myKind != null && from.myProperties != null) { @@ -126,13 +134,15 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi } // primary - private LibraryImpl(LibraryTable table, ModifiableRootModel rootModel, LibraryImpl newSource, String name, @Nullable final PersistentLibraryKind kind) { + private LibraryImpl(LibraryTable table, ModifiableRootModel rootModel, LibraryImpl newSource, String name, + @Nullable final PersistentLibraryKind kind, @Nullable ProjectModelExternalSource externalSource) { super(true); myLibraryTable = table; myRootModel = rootModel; mySource = newSource; myKind = kind; myName = name; + myExternalSource = externalSource; //init roots depends on my myKind myRoots = initRoots(); Disposer.register(this, myRootsWatcher); @@ -279,6 +289,12 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi return result; } + @Nullable + @Override + public ProjectModelExternalSource getExternalSource() { + return myExternalSource; + } + @Override public void readExternal(Element element) throws InvalidDataException { readName(element); @@ -355,6 +371,12 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi } } } + + if (myExternalSource != null && Registry.is("store.imported.project.elements.separately")) { + //we can add this attribute only if the library configuration will be stored separately, otherwise we will get modified files in .idea/libraries. + element.setAttribute(EXTERNAL_SYSTEM_ID_ATTRIBUTE, myExternalSource.getId()); + } + ArrayList storableRootTypes = new ArrayList<>(); storableRootTypes.addAll(Arrays.asList(OrderRootType.getAllTypes())); if (myKind != null) { diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java index 46b43ef33ea7..af09b6e2781a 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java @@ -22,6 +22,7 @@ import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ProjectModelExternalSource; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.roots.libraries.PersistentLibraryKind; @@ -268,7 +269,7 @@ public abstract class LibraryTableBase implements PersistentStateComponent