[workspace model] provide implementation of FacetManager which stores data in workspa...
authornik <Nikolay.Chashnikov@jetbrains.com>
Mon, 30 Dec 2019 15:42:58 +0000 (18:42 +0300)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Mon, 30 Dec 2019 16:05:39 +0000 (16:05 +0000)
Configuration of each Facet is stored in FacetEntity, changes made via FacetManager are propagated to WorkspaceModel and vice versa. Shared code in FacetManager's implementation is extracted for FacetManagerBase. In order to properly reflect changes in configurations of individual facets, a new method FacetManager::facetConfigurationChanged is introduced which must be called after changing configuration of a facet.

GitOrigin-RevId: 0e866dcc7f5769a810bde17cbdd20e4200dfbd09

21 files changed:
java/idea-ui/src/com/intellij/facet/impl/ProjectFacetsConfigurator.java
java/idea-ui/testSrc/com/intellij/facet/FacetManagerTest.java
java/idea-ui/testSrc/com/intellij/facet/FacetTestCase.java
java/idea-ui/testSrc/com/intellij/facet/ImportedFacetsSerializationTest.kt
java/idea-ui/testSrc/com/intellij/facet/mock/MockFacet.java
platform/lang-api/src/com/intellij/facet/FacetManager.java
platform/lang-impl/src/com/intellij/facet/FacetManagerBase.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/facet/FacetManagerImpl.java
platform/lang-impl/src/com/intellij/facet/impl/FacetUtil.java
platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java
platform/workspaceModel-core/src/ImlModelEntities.kt
platform/workspaceModel-ide/src/com/intellij/workspace/ide/jpsEntitySources.kt
platform/workspaceModel-ide/src/com/intellij/workspace/jps/JpsProjectEntitiesLoader.kt
platform/workspaceModel-ide/src/com/intellij/workspace/jps/JpsProjectModelSynchronizer.kt
platform/workspaceModel-ide/src/com/intellij/workspace/jps/ModuleImlFileEntitiesSerializer.kt
platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetEntityChangeListener.kt [new file with mode: 0644]
platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetManagerViaWorkspaceModel.kt [new file with mode: 0644]
platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/ModifiableFacetModelViaWorkspaceModel.kt [new file with mode: 0644]
platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeModuleImpl.kt
platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeModuleManagerComponent.kt
platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/libraries/libraries/LegacyBridgeProjectModifiableLibraryTableImpl.kt

index 855341fca90a89811011f75fb63712439c00b0e6..c77da8822db53c50eaaeb5cc740b5fba47d8d4c8 100644 (file)
@@ -238,7 +238,7 @@ public class ProjectFacetsConfigurator implements FacetsProvider {
     for (Facet facet : myChangedFacets) {
       Module module = facet.getModule();
       if (!module.isDisposed()) {
-        module.getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(facet);
+        FacetManager.getInstance(module).facetConfigurationChanged(facet);
       }
     }
     myChangedFacets.clear();
index 71b968228ea7bc1a42e0ebade14aa4ead2ea1f8f..19e81cc4e12df94fd64b6f1ea2100166b6533449 100644 (file)
@@ -10,6 +10,7 @@ import org.jdom.Element;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.jps.model.serialization.facet.FacetManagerState;
+import org.junit.Assume;
 
 
 /**
@@ -110,16 +111,17 @@ public class FacetManagerTest extends FacetTestCase {
 
   @Nullable
   private Element write() {
-    FacetManagerState state = getFacetManager().getState();
+    FacetManagerState state = ((FacetManagerImpl)getFacetManager()).getState();
     return XmlSerializer.serialize(state);
   }
 
   private void read(@Nullable Element element) {
-    getFacetManager().loadState(element == null ? new FacetManagerState() : XmlSerializer.deserialize(element, FacetManagerState.class));
+    ((FacetManagerImpl)getFacetManager()).loadState(element == null ? new FacetManagerState() : XmlSerializer.deserialize(element, FacetManagerState.class));
   }
 
   public void testExternalization() {
-    final FacetManager manager = getFacetManager();
+    FacetManager manager = getFacetManager();
+    Assume.assumeTrue("Not applicable to workspace model", manager instanceof FacetManagerImpl);
     assertNull(write());
 
     addFacet();
index c7d9b9532a90702a812a609b07771b0c82574573..17c3aee4cf232748fec57e6f545b2e90b622fd76 100644 (file)
@@ -38,12 +38,13 @@ public abstract class FacetTestCase extends JavaPsiTestCase {
     commit(model);
   }
 
-  protected FacetManagerImpl getFacetManager() {
-    return (FacetManagerImpl)FacetManager.getInstance(myModule);
+  protected FacetManager getFacetManager() {
+    return FacetManager.getInstance(myModule);
   }
 
   protected void commit(final ModifiableFacetModel model) {
     WriteAction.runAndWait(() -> model.commit());
+    ((FacetManagerBase) getFacetManager()).checkConsistency();
   }
 
   protected MockFacet createFacet() {
index 0fee96760563660fe311a4368a764e4afe16cb16..6c80256565e67edcf0eb4020fc8661dee6d22e8f 100644 (file)
@@ -4,6 +4,7 @@ package com.intellij.facet
 import com.intellij.facet.mock.MockFacetConfiguration
 import com.intellij.openapi.roots.ProjectModelExternalSource
 import org.jetbrains.jps.model.serialization.facet.FacetState
+import org.junit.Assume
 
 /**
  * @author nik
@@ -109,6 +110,12 @@ class ImportedFacetsSerializationTest : FacetTestCase() {
     }
   }
 
+  override fun getFacetManager(): FacetManagerImpl {
+    val facetManager = super.getFacetManager()
+    Assume.assumeTrue("Test is ignored in workspace model", facetManager is FacetManagerImpl)
+    return facetManager as FacetManagerImpl
+  }
+
   private fun getExternalFacetStates() = FacetFromExternalSourcesStorage.getInstance(module).state.facets
 }
 
index 6799dd50c5911aadf0a621a9156ccc4a73ebcc16..b56e3c67d5cbb7a652d58f529ac83b88bd27d8b2 100644 (file)
@@ -60,7 +60,7 @@ public class MockFacet extends Facet<MockFacetConfiguration> implements FacetRoo
   }
 
   private void fireFacetChangedEvent() {
-    getModule().getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(this);
+    FacetManager.getInstance(getModule()).facetConfigurationChanged(this);
   }
 
   public void removeRoot(VirtualFile root) {
index a951999284dcbd63e29f498531af550eb4344914..a094bc37702fedad659b43ae2b1272e79841c2f2 100644 (file)
@@ -39,4 +39,8 @@ public abstract class FacetManager implements FacetModel {
   public abstract <F extends Facet<?>, C extends FacetConfiguration> F addFacet(@NotNull FacetType<F, C> type, @NotNull String name,
                                                                                    @Nullable Facet<?> underlying);
 
+  /**
+   * This method must be called when configuration of {@code facet} is changed via its API.
+   */
+  public abstract void facetConfigurationChanged(@NotNull Facet facet);
 }
diff --git a/platform/lang-impl/src/com/intellij/facet/FacetManagerBase.java b/platform/lang-impl/src/com/intellij/facet/FacetManagerBase.java
new file mode 100644 (file)
index 0000000..51acb04
--- /dev/null
@@ -0,0 +1,157 @@
+// Copyright 2000-2019 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.facet;
+
+import com.intellij.facet.impl.FacetLoadingErrorDescription;
+import com.intellij.facet.impl.invalid.InvalidFacet;
+import com.intellij.facet.impl.invalid.InvalidFacetConfiguration;
+import com.intellij.facet.impl.invalid.InvalidFacetManager;
+import com.intellij.facet.impl.invalid.InvalidFacetType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ProjectLoadingErrorsNotifier;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.UnknownFeaturesCollector;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+import org.jetbrains.jps.model.serialization.facet.FacetState;
+
+import java.util.Collection;
+
+@ApiStatus.Internal
+public abstract class FacetManagerBase extends FacetManager {
+  private static final Logger LOG = Logger.getInstance(FacetManagerBase.class);
+
+  @Override
+  @NotNull
+  public <F extends Facet<?>, C extends FacetConfiguration> F createFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @NotNull final C configuration,
+                                                                          @Nullable final Facet<?> underlying) {
+    return createFacet(getModule(), type, name, configuration, underlying);
+  }
+
+  @ApiStatus.Internal
+  public static void setFacetName(Facet<?> facet, String newName) {
+    facet.setName(newName);
+  }
+
+  @NotNull
+  private static <F extends Facet<?>, C extends FacetConfiguration> F createFacet(@NotNull Module module, @NotNull FacetType<F, C> type,
+                                                                                  @NotNull String name,
+                                                                                  @NotNull C configuration,
+                                                                                  @Nullable Facet<?> underlying) {
+    final F facet = type.createFacet(module, name, configuration, underlying);
+    assertTrue(facet.getModule() == module, facet, "module");
+    assertTrue(facet.getConfiguration() == configuration, facet, "configuration");
+    assertTrue(Comparing.equal(facet.getName(), name), facet, "name");
+    assertTrue(facet.getUnderlyingFacet() == underlying, facet, "underlyingFacet");
+    return facet;
+  }
+
+  @Override
+  @NotNull
+  public <F extends Facet<?>, C extends FacetConfiguration> F createFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @Nullable final Facet<?> underlying) {
+    C configuration = ProjectFacetManager.getInstance(getModule().getProject()).createDefaultConfiguration(type);
+    return createFacet(type, name, configuration, underlying);
+  }
+
+  @Override
+  @NotNull
+  public <F extends Facet<?>, C extends FacetConfiguration> F addFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @Nullable final Facet<?> underlying) {
+    final ModifiableFacetModel model = createModifiableModel();
+    final F facet = createFacet(type, name, underlying);
+    model.addFacet(facet);
+    model.commit();
+    return facet;
+  }
+
+  @Override
+  public void facetConfigurationChanged(@NotNull Facet facet) {
+    getModule().getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(facet);
+  }
+
+  @Override
+  @NotNull
+  public Facet<?>[] getAllFacets() {
+    return getModel().getAllFacets();
+  }
+
+  @Override
+  @Nullable
+  public <F extends Facet<?>> F getFacetByType(FacetTypeId<F> typeId) {
+    return getModel().getFacetByType(typeId);
+  }
+
+  protected abstract FacetModel getModel();
+
+  @Override
+  @Nullable
+  public <F extends Facet<?>> F findFacet(final FacetTypeId<F> type, final String name) {
+    return getModel().findFacet(type, name);
+  }
+
+  @Override
+  @Nullable
+  public <F extends Facet<?>> F getFacetByType(@NotNull final Facet<?> underlyingFacet, final FacetTypeId<F> typeId) {
+    return getModel().getFacetByType(underlyingFacet, typeId);
+  }
+
+  @Override
+  @NotNull
+  public <F extends Facet<?>> Collection<F> getFacetsByType(@NotNull final Facet<?> underlyingFacet, final FacetTypeId<F> typeId) {
+    return getModel().getFacetsByType(underlyingFacet, typeId);
+  }
+
+  @Override
+  @NotNull
+  public <F extends Facet<?>> Collection<F> getFacetsByType(FacetTypeId<F> typeId) {
+    return getModel().getFacetsByType(typeId);
+  }
+
+  @Override
+  @NotNull
+  public Facet<?>[] getSortedFacets() {
+    return getModel().getSortedFacets();
+  }
+
+  @Override
+  @NotNull
+  public String getFacetName(@NotNull Facet<?> facet) {
+    return getModel().getFacetName(facet);
+  }
+
+  protected abstract Module getModule();
+
+  @ApiStatus.Internal
+  @NotNull
+  public static InvalidFacet createInvalidFacet(@NotNull Module module, @NotNull FacetState state, @Nullable Facet<?> underlyingFacet,
+                                                @NotNull String errorMessage,
+                                                boolean unknownType) {
+    Project project = module.getProject();
+    final InvalidFacetManager invalidFacetManager = InvalidFacetManager.getInstance(project);
+    final InvalidFacetType type = InvalidFacetType.getInstance();
+    final InvalidFacetConfiguration configuration = new InvalidFacetConfiguration(state, errorMessage);
+    final InvalidFacet facet = createFacet(module, type, StringUtil.notNullize(state.getName()), configuration, underlyingFacet);
+    if (!invalidFacetManager.isIgnored(facet)) {
+      FacetLoadingErrorDescription description = new FacetLoadingErrorDescription(facet);
+      ProjectLoadingErrorsNotifier.getInstance(project).registerError(description);
+      if (unknownType) {
+        UnknownFeaturesCollector.getInstance(project).registerUnknownFeature("com.intellij.facetType", state.getFacetType(), "Facet");
+      }
+    }
+    return facet;
+  }
+
+  private static void assertTrue(final boolean value, final Facet<?> facet, final String parameter) {
+    if (!value) {
+      LOG.error("Facet type " + facet.getType().getClass().getName() + " violates the contract of FacetType.createFacet method about '" +
+               parameter + "' parameter");
+    }
+  }
+
+  @TestOnly
+  public void checkConsistency() {
+  }
+}
index ae4e17d872abde849d1ac5abbe300a4bccad3f9b..25662eaaff0d5ccc47bb42c15f4fed2ef6202188 100644 (file)
@@ -2,14 +2,10 @@
 
 package com.intellij.facet;
 
-import com.intellij.facet.impl.FacetLoadingErrorDescription;
 import com.intellij.facet.impl.FacetModelBase;
 import com.intellij.facet.impl.FacetModelImpl;
 import com.intellij.facet.impl.FacetUtil;
 import com.intellij.facet.impl.invalid.InvalidFacet;
-import com.intellij.facet.impl.invalid.InvalidFacetConfiguration;
-import com.intellij.facet.impl.invalid.InvalidFacetManager;
-import com.intellij.facet.impl.invalid.InvalidFacetType;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.components.PersistentStateComponent;
 import com.intellij.openapi.components.State;
@@ -17,15 +13,14 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleComponent;
 import com.intellij.openapi.module.ModuleType;
-import com.intellij.openapi.module.ProjectLoadingErrorsNotifier;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectBundle;
 import com.intellij.openapi.project.ProjectUtilCore;
 import com.intellij.openapi.roots.ExternalProjectSystemRegistry;
 import com.intellij.openapi.roots.ProjectModelExternalSource;
-import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.UnknownFeaturesCollector;
-import com.intellij.openapi.util.*;
-import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.messages.MessageBus;
 import org.jdom.Element;
@@ -44,7 +39,7 @@ import java.util.function.Predicate;
  */
 @State(name = JpsFacetSerializer.FACET_MANAGER_COMPONENT_NAME, useLoadedStateAsExisting = false)
 @ApiStatus.Internal
-public final class FacetManagerImpl extends FacetManager implements ModuleComponent, PersistentStateComponent<FacetManagerState> {
+public final class FacetManagerImpl extends FacetManagerBase implements ModuleComponent, PersistentStateComponent<FacetManagerState> {
   private static final Logger LOG = Logger.getInstance(FacetManagerImpl.class);
 
   private final Module myModule;
@@ -70,93 +65,16 @@ public final class FacetManagerImpl extends FacetManager implements ModuleCompon
   }
 
   @Override
-  @NotNull
-  public Facet<?>[] getAllFacets() {
-    return myModel.getAllFacets();
-  }
-
-  @Override
-  @Nullable
-  public <F extends Facet<?>> F getFacetByType(FacetTypeId<F> typeId) {
-    return myModel.getFacetByType(typeId);
-  }
-
-  @Override
-  @Nullable
-  public <F extends Facet<?>> F findFacet(final FacetTypeId<F> type, final String name) {
-    return myModel.findFacet(type, name);
-  }
-
-  @Override
-  @Nullable
-  public <F extends Facet<?>> F getFacetByType(@NotNull final Facet<?> underlyingFacet, final FacetTypeId<F> typeId) {
-    return myModel.getFacetByType(underlyingFacet, typeId);
-  }
-
-  @Override
-  @NotNull
-  public <F extends Facet<?>> Collection<F> getFacetsByType(@NotNull final Facet<?> underlyingFacet, final FacetTypeId<F> typeId) {
-    return myModel.getFacetsByType(underlyingFacet, typeId);
-  }
-
-
-  @Override
-  @NotNull
-  public <F extends Facet<?>> Collection<F> getFacetsByType(FacetTypeId<F> typeId) {
-    return myModel.getFacetsByType(typeId);
+  protected FacetModel getModel() {
+    return myModel;
   }
 
-
   @Override
-  @NotNull
-  public Facet<?>[] getSortedFacets() {
-    return myModel.getSortedFacets();
-  }
-
-  @Override
-  @NotNull
-  public String getFacetName(@NotNull Facet<?> facet) {
-    return myModel.getFacetName(facet);
-  }
-
-  @Override
-  @NotNull
-  public <F extends Facet<?>, C extends FacetConfiguration> F createFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @NotNull final C configuration,
-                                                                          @Nullable final Facet<?> underlying) {
-    final F facet = type.createFacet(myModule, name, configuration, underlying);
-    assertTrue(facet.getModule() == myModule, facet, "module");
-    assertTrue(facet.getConfiguration() == configuration, facet, "configuration");
-    assertTrue(Comparing.equal(facet.getName(), name), facet, "name");
-    assertTrue(facet.getUnderlyingFacet() == underlying, facet, "underlyingFacet");
-    return facet;
-  }
-
-  @Override
-  @NotNull
-  public <F extends Facet<?>, C extends FacetConfiguration> F createFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @Nullable final Facet<?> underlying) {
-    C configuration = ProjectFacetManager.getInstance(myModule.getProject()).createDefaultConfiguration(type);
-    return createFacet(type, name, configuration, underlying);
-  }
-
-  @Override
-  @NotNull
-  public <F extends Facet<?>, C extends FacetConfiguration> F addFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @Nullable final Facet<?> underlying) {
-    final ModifiableFacetModel model = createModifiableModel();
-    final F facet = createFacet(type, name, underlying);
-    model.addFacet(facet);
-    model.commit();
-    return facet;
-  }
-
-  private static void assertTrue(final boolean value, final Facet<?> facet, final String parameter) {
-    if (!value) {
-      LOG.error("Facet type " + facet.getType().getClass().getName() + " violates the contract of FacetType.createFacet method about '" +
-                parameter + "' parameter");
-    }
+  protected Module getModule() {
+    return myModule;
   }
 
   private void addFacets(final List<? extends FacetState> facetStates, final Facet<?> underlyingFacet, ModifiableFacetModel model) {
-
     FacetTypeRegistry registry = FacetTypeRegistry.getInstance();
     for (FacetState child : facetStates) {
       final String typeId = child.getFacetType();
@@ -167,7 +85,7 @@ public final class FacetManagerImpl extends FacetManager implements ModuleCompon
 
       final FacetType<?,?> type = registry.findFacetType(typeId);
       if (type == null) {
-        addInvalidFacet(child, model, underlyingFacet, ProjectBundle.message("error.message.unknown.facet.type.0", typeId), typeId);
+        addInvalidFacet(child, model, underlyingFacet, ProjectBundle.message("error.message.unknown.facet.type.0", typeId), true);
         continue;
       }
 
@@ -212,26 +130,14 @@ public final class FacetManagerImpl extends FacetManager implements ModuleCompon
                                ModifiableFacetModel model,
                                final Facet<?> underlyingFacet,
                                final String errorMessage) {
-    addInvalidFacet(state, model, underlyingFacet, errorMessage, null);
+    addInvalidFacet(state, model, underlyingFacet, errorMessage, false);
   }
 
   private void addInvalidFacet(final FacetState state,
                                ModifiableFacetModel model,
                                final Facet<?> underlyingFacet,
-                               final String errorMessage,
-                               final String typeId) {
-    final InvalidFacetManager invalidFacetManager = InvalidFacetManager.getInstance(myModule.getProject());
-    final InvalidFacetType type = InvalidFacetType.getInstance();
-    final InvalidFacetConfiguration configuration = new InvalidFacetConfiguration(state, errorMessage);
-    final InvalidFacet facet = createFacet(type, StringUtil.notNullize(state.getName()), configuration, underlyingFacet);
-    model.addFacet(facet);
-    if (!invalidFacetManager.isIgnored(facet)) {
-      FacetLoadingErrorDescription description = new FacetLoadingErrorDescription(facet);
-      ProjectLoadingErrorsNotifier.getInstance(myModule.getProject()).registerError(description);
-      if (typeId != null) {
-        UnknownFeaturesCollector.getInstance(myModule.getProject()).registerUnknownFeature("com.intellij.facetType", typeId, "Facet");
-      }
-    }
+                               final String errorMessage, boolean unknownType) {
+    model.addFacet(createInvalidFacet(getModule(), state, underlyingFacet, errorMessage, unknownType));
   }
 
   private <F extends Facet<C>, C extends FacetConfiguration> void addFacet(final FacetType<F, C> type, final FacetState state, final Facet<?> underlyingFacet,
@@ -323,18 +229,8 @@ public final class FacetManagerImpl extends FacetManager implements ModuleCompon
 
       FacetState facetState = createFacetState(facet, myModule.getProject());
       if (!(facet instanceof InvalidFacet)) {
-        final Element config;
-        try {
-          FacetConfiguration configuration = facet.getConfiguration();
-          config = FacetUtil.saveFacetConfiguration(configuration);
-          if (facet instanceof JDOMExternalizable) {
-            //todo[nik] remove
-            ((JDOMExternalizable)facet).writeExternal(config);
-          }
-        }
-        catch (WriteExternalException e) {
-          continue;
-        }
+        final Element config = FacetUtil.saveFacetConfiguration(facet);
+        if (config == null) continue;
         facetState.setConfiguration(config);
       }
 
@@ -429,7 +325,7 @@ public final class FacetManagerImpl extends FacetManager implements ModuleCompon
       }
 
       for (FacetRenameInfo info : toRename) {
-        info.myFacet.setName(info.myNewName);
+        setFacetName(info.myFacet, info.myNewName);
       }
       myModel.setAllFacets(newFacets.toArray(Facet.EMPTY_ARRAY));
     }
index e625464f1caedd213a17c8a2b910c1e2e572ebb2..34646d2b28849cf7d11bd80e657467c57a3608e1 100644 (file)
@@ -9,7 +9,10 @@ import com.intellij.openapi.application.WriteAction;
 import com.intellij.openapi.components.PersistentStateComponent;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
 import org.jdom.Element;
+import org.jetbrains.annotations.ApiStatus;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.jps.model.serialization.facet.JpsFacetSerializer;
@@ -81,4 +84,22 @@ public class FacetUtil {
       return config;
     }
   }
+
+  @ApiStatus.Internal
+  @Nullable
+  public static Element saveFacetConfiguration(Facet<?> facet) {
+    final Element config;
+    try {
+      FacetConfiguration configuration = facet.getConfiguration();
+      config = saveFacetConfiguration(configuration);
+      if (facet instanceof JDOMExternalizable) {
+        //todo[nik] remove
+        ((JDOMExternalizable)facet).writeExternal(config);
+      }
+    }
+    catch (WriteExternalException e) {
+      return null;
+    }
+    return config;
+  }
 }
index 777098f5474c7d3f7e12f6ec6f08a653c8310462..eb91370141291a50c9647b96bb23522910b48ed5 100644 (file)
@@ -51,7 +51,6 @@ import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.actionSystem.IdeActions;
 import com.intellij.openapi.actionSystem.Presentation;
-import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ReadAction;
 import com.intellij.openapi.application.WriteAction;
@@ -134,7 +133,6 @@ import java.io.UncheckedIOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.*;
-import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
@@ -1416,7 +1414,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
       Module module = getModule();
       if (module != null) {
         for (Facet<?> facet : FacetManager.getInstance(module).getAllFacets()) {
-          module.getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(facet);
+          FacetManager.getInstance(module).facetConfigurationChanged(facet);
         }
       }
       PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
index 9560b848a943568bb9539d2db08afad6a4900b03..3b6c0c60a9fc0eb7420523c669db37ec8cf090d8 100644 (file)
@@ -193,14 +193,25 @@ interface SdkEntity : TypedEntity {
   val homeUrl: VirtualFileUrl
 }
 
-interface FacetEntity : TypedEntity {
+interface FacetEntity : TypedEntityWithPersistentId, ReferableTypedEntity {
   val name: String
   val facetType: String
   val configurationXmlTag: String?
   val module: ModuleEntity
   val underlyingFacet: FacetEntity?
+
+  @JvmDefault
+  override fun persistentId(): FacetId = FacetId(name, facetType, module.persistentId())
 }
 
+data class FacetId(val name: String, val type: String, override val parentId: ModuleId) : PersistentEntityId<FacetEntity>() {
+  override val presentableName: String
+    get() = name
+}
+
+val FacetEntity.subFacets: Sequence<FacetEntity>
+  get() = referrers(FacetEntity::underlyingFacet)
+
 val ModuleEntity.facets: Sequence<FacetEntity>
   get() = referrers(FacetEntity::module)
 
index 0cf4697453290cd51bd77c23fce50b23549b82cb..4ed252d8def6d26991e12da497548ea69581dfa6 100644 (file)
@@ -70,6 +70,8 @@ sealed class JpsFileEntitySource : EntitySource {
 
 data class ExternalEntitySource(val displayName: String, val id: String) : EntitySource
 
+fun ProjectModelExternalSource.toEntitySource() = ExternalEntitySource(displayName, id)
+
 /**
  * Represents entities added by user in IDE (either via Project Structure or Settings dialog, or by invoking an action like 'Create Library from Files').
  */
index c649e357b7708d1f344bac14dfe70b84e8dfc489..43554c175afd86d66a50bb9102a53c56882bcc46 100644 (file)
@@ -42,7 +42,7 @@ object JpsProjectEntitiesLoader {
                           source: JpsFileEntitySource.FileInDirectory,
                           storagePlace: JpsProjectStoragePlace,
                           builder: TypedEntityStorageBuilder) {
-    val mainFactories = createProjectEntitiesSerializers(storagePlace, false, false)
+    val mainFactories = createProjectEntitiesSerializers(storagePlace, false, true)
     val reader = CachingJpsFileContentReader(storagePlace.baseDirectoryUrl)
     val moduleSerializerFactory = mainFactories.fileSerializerFactories.filterIsInstance<ModuleSerializersFactory>().single()
 
index eba1090abd3f61b7996dd9c0e5f9940aa2bbde99..e8fdb191a510fd474c721f04e570fefd95deec8a 100644 (file)
@@ -117,7 +117,7 @@ class JpsProjectModelSynchronizer(private val project: Project) : Disposable {
   internal fun loadInitialProject(storagePlace: JpsProjectStoragePlace) {
     val baseDirUrl = storagePlace.baseDirectoryUrl
     fileContentReader = StorageJpsConfigurationReader(project, baseDirUrl)
-    val serializationData = JpsProjectEntitiesLoader.createProjectSerializers(storagePlace, fileContentReader, false, false)
+    val serializationData = JpsProjectEntitiesLoader.createProjectSerializers(storagePlace, fileContentReader, false, true)
     this.serializationData.set(serializationData)
     registerListener()
     val builder = TypedEntityStorageBuilder.create()
index 3cf590b9b88a93ab8bf62dbe27923a77d15aa10a..d0fda4f54170cc21cf98b88a328a05e8ccdb728e 100644 (file)
@@ -37,52 +37,56 @@ internal class ModuleImlFileEntitiesSerializer(private val modulePath: ModulePat
 
   override fun loadEntities(builder: TypedEntityStorageBuilder,
                             reader: JpsFileContentReader) {
-    val moduleName = modulePath.moduleName
-    val source = entitySource
-    val moduleEntity = builder.addModuleEntity(moduleName, listOf(ModuleDependencyItem.ModuleSourceDependency), source)
+    val moduleEntity = builder.addModuleEntity(modulePath.moduleName, listOf(ModuleDependencyItem.ModuleSourceDependency), entitySource)
 
     val rootManagerElement = reader.loadComponent(fileUrl.url, MODULE_ROOT_MANAGER_COMPONENT_NAME)?.clone()
-    if (rootManagerElement == null) {
-      return
+    if (rootManagerElement != null) {
+      loadRootManager(rootManagerElement, moduleEntity, builder)
     }
 
+    if (serializeFacets) {
+      FacetEntitiesSerializer(fileUrl, entitySource).loadFacetEntities(builder, moduleEntity, reader)
+    }
+  }
+
+  private fun loadRootManager(rootManagerElement: Element,
+                              moduleEntity: ModuleEntity,
+                              builder: TypedEntityStorageBuilder) {
     for (contentElement in rootManagerElement.getChildrenAndDetach(CONTENT_TAG)) {
       for (sourceRootElement in contentElement.getChildren(SOURCE_FOLDER_TAG)) {
         val url = sourceRootElement.getAttributeValueStrict(URL_ATTRIBUTE)
         val isTestSource = sourceRootElement.getAttributeValue(IS_TEST_SOURCE_ATTRIBUTE)?.toBoolean() == true
-        val type = sourceRootElement.getAttributeValue(SOURCE_ROOT_TYPE_ATTRIBUTE) ?: (if (isTestSource) JAVA_TEST_ROOT_TYPE_ID else JAVA_SOURCE_ROOT_TYPE_ID)
+        val type = sourceRootElement.getAttributeValue(SOURCE_ROOT_TYPE_ATTRIBUTE)
+                   ?: (if (isTestSource) JAVA_TEST_ROOT_TYPE_ID else JAVA_SOURCE_ROOT_TYPE_ID)
         val virtualFileUrl = VirtualFileUrlManager.fromUrl(url)
         val sourceRoot = builder.addSourceRootEntity(moduleEntity, virtualFileUrl,
                                                      type == JAVA_TEST_ROOT_TYPE_ID || type == JAVA_TEST_RESOURCE_ROOT_ID,
-                                                     type, source)
+                                                     type, entitySource)
         if (type == JAVA_SOURCE_ROOT_TYPE_ID || type == JAVA_TEST_ROOT_TYPE_ID) {
           builder.addJavaSourceRootEntity(sourceRoot, sourceRootElement.getAttributeValue(IS_GENERATED_ATTRIBUTE)?.toBoolean() ?: false,
-                                          sourceRootElement.getAttributeValue(PACKAGE_PREFIX_ATTRIBUTE) ?: "", source)
+                                          sourceRootElement.getAttributeValue(PACKAGE_PREFIX_ATTRIBUTE) ?: "", entitySource)
         }
         else if (type == JAVA_RESOURCE_ROOT_ID || type == JAVA_TEST_RESOURCE_ROOT_ID) {
           builder.addJavaResourceRootEntity(sourceRoot, sourceRootElement.getAttributeValue(IS_GENERATED_ATTRIBUTE)?.toBoolean() ?: false,
-                                            sourceRootElement.getAttributeValue(RELATIVE_OUTPUT_PATH_ATTRIBUTE) ?: "", source)
+                                            sourceRootElement.getAttributeValue(RELATIVE_OUTPUT_PATH_ATTRIBUTE) ?: "", entitySource)
         }
         else {
           val elem = sourceRootElement.clone()
           elem.removeAttribute(URL_ATTRIBUTE)
           elem.removeAttribute(SOURCE_ROOT_TYPE_ATTRIBUTE)
-          builder.addCustomSourceRootPropertiesEntity(sourceRoot, JDOMUtil.write(elem), source)
+          builder.addCustomSourceRootPropertiesEntity(sourceRoot, JDOMUtil.write(elem), entitySource)
         }
       }
       val excludeRootsUrls = contentElement.getChildren(EXCLUDE_FOLDER_TAG)
-        .map { it.getAttributeValueStrict(URL_ATTRIBUTE) }
-        .map { VirtualFileUrlManager.fromUrl(it) }
+        .map { VirtualFileUrlManager.fromUrl(it.getAttributeValueStrict(URL_ATTRIBUTE)) }
       val excludePatterns = contentElement.getChildren(EXCLUDE_PATTERN_TAG)
         .map { it.getAttributeValue(EXCLUDE_PATTERN_ATTRIBUTE) }
-      val contentRootUrl = contentElement
-        .getAttributeValueStrict(URL_ATTRIBUTE)
+      val contentRootUrl = contentElement.getAttributeValueStrict(URL_ATTRIBUTE)
         .let { VirtualFileUrlManager.fromUrl(it) }
-      builder.addContentRootEntity(contentRootUrl, excludeRootsUrls, excludePatterns, moduleEntity, source)
+      builder.addContentRootEntity(contentRootUrl, excludeRootsUrls, excludePatterns, moduleEntity, entitySource)
     }
     fun Element.readScope(): ModuleDependencyItem.DependencyScope {
-      val attributeValue = getAttributeValue(SCOPE_ATTRIBUTE)
-                           ?: return ModuleDependencyItem.DependencyScope.COMPILE
+      val attributeValue = getAttributeValue(SCOPE_ATTRIBUTE) ?: return ModuleDependencyItem.DependencyScope.COMPILE
       return try {
         ModuleDependencyItem.DependencyScope.valueOf(attributeValue)
       }
@@ -109,10 +113,12 @@ internal class ModuleImlFileEntitiesSerializer(private val modulePath: ModulePat
           val libraryElement = dependencyElement.getChild(LIBRARY_TAG)!!
           // TODO. Probably we want a fixed name based on hashed library roots
           val nameAttributeValue = libraryElement.getAttributeValue(NAME_ATTRIBUTE)
-          val name = LegacyBridgeLibraryImpl.generateLibraryEntityName(nameAttributeValue) { nameToCheck -> moduleLibraryNames.contains(nameToCheck) }
+          val name = LegacyBridgeLibraryImpl.generateLibraryEntityName(nameAttributeValue) { nameToCheck ->
+            moduleLibraryNames.contains(nameToCheck)
+          }
           moduleLibraryNames.add(name)
-          val tableId = LibraryTableId.ModuleLibraryTableId(ModuleId(moduleName))
-          loadLibrary(name, libraryElement, tableId, builder, source)
+          val tableId = LibraryTableId.ModuleLibraryTableId(moduleEntity.persistentId())
+          loadLibrary(name, libraryElement, tableId, builder, entitySource)
           val libraryId = LibraryId(name, tableId)
           ModuleDependencyItem.Exportable.LibraryDependency(libraryId, dependencyElement.isExported(), dependencyElement.readScope())
         }
@@ -141,22 +147,18 @@ internal class ModuleImlFileEntitiesSerializer(private val modulePath: ModulePat
       compilerOutput = compilerOutput?.let { VirtualFileUrlManager.fromUrl(it) },
       compilerOutputForTests = compilerOutputForTests?.let { VirtualFileUrlManager.fromUrl(it) },
       module = moduleEntity,
-      source = source
+      source = entitySource
     )
     if (!rootManagerElement.isEmpty()) {
       builder.addModuleCustomImlDataEntity(
         rootManagerTagCustomData = JDOMUtil.write(rootManagerElement),
         module = moduleEntity,
-        source = source
+        source = entitySource
       )
     }
     builder.modifyEntity(ModifiableModuleEntity::class.java, moduleEntity) {
       dependencies = dependencyItems
     }
-
-    if (serializeFacets) {
-      FacetEntitiesSerializer(fileUrl, source).loadFacetEntities(builder, moduleEntity, reader)
-    }
   }
 
   private fun Element.getChildrenAndDetach(cname: String): List<Element> {
diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetEntityChangeListener.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetEntityChangeListener.kt
new file mode 100644 (file)
index 0000000..27c01c2
--- /dev/null
@@ -0,0 +1,94 @@
+// Copyright 2000-2019 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.workspace.legacyBridge.facet
+
+import com.intellij.facet.FacetManager
+import com.intellij.facet.FacetManagerBase
+import com.intellij.openapi.module.ModuleManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Disposer
+import com.intellij.workspace.api.EntityChange
+import com.intellij.workspace.api.EntityStoreChanged
+import com.intellij.workspace.api.FacetEntity
+import com.intellij.workspace.api.ModuleEntity
+import com.intellij.workspace.ide.WorkspaceModelChangeListener
+
+internal class FacetEntityChangeListener(private val project: Project) : WorkspaceModelChangeListener {
+  override fun beforeChanged(event: EntityStoreChanged) {
+    val changes = event.getChanges(FacetEntity::class.java)
+    for (change in changes) {
+      when (change) {
+        is EntityChange.Added -> {
+          val manager = getFacetManager(change.entity.module)
+          manager?.publisher?.beforeFacetAdded(manager.model.getOrCreateFacet(change.entity))
+        }
+        is EntityChange.Removed -> {
+          val manager = getFacetManager(change.entity.module)
+          if (manager != null) {
+            val facet = manager.model.getFacet(change.entity)
+            if (facet != null) {
+              manager.publisher.beforeFacetRemoved(facet)
+            }
+          }
+        }
+        is EntityChange.Replaced -> {
+          val manager = getFacetManager(change.newEntity.module)
+          if (manager != null && change.newEntity.name != change.oldEntity.name) {
+              manager.publisher.beforeFacetRenamed(manager.model.getOrCreateFacet(change.newEntity))
+          }
+        }
+      }
+    }
+  }
+
+
+  private val FacetManagerViaWorkspaceModel.publisher
+    get() = module.messageBus.syncPublisher(FacetManager.FACETS_TOPIC)
+
+  private fun getFacetManager(entity: ModuleEntity): FacetManagerViaWorkspaceModel? {
+    val module = ModuleManager.getInstance(project).findModuleByName(entity.name) ?: return null
+    return FacetManager.getInstance(module) as? FacetManagerViaWorkspaceModel
+  }
+
+  override fun changed(event: EntityStoreChanged) {
+    val changes = event.getChanges(FacetEntity::class.java)
+    for (change in changes) {
+      when (change) {
+        is EntityChange.Added -> {
+          val manager = getFacetManager(change.entity.module)
+          if (manager != null) {
+            val facet = manager.model.getOrCreateFacet(change.entity)
+            manager.model.updateEntity(change.entity, change.entity)
+            FacetManagerBase.setFacetName(facet, change.entity.name)
+            facet.initFacet()
+            manager.model.facetsChanged()
+            manager.publisher.facetAdded(facet)
+          }
+        }
+        is EntityChange.Removed -> {
+          val manager = getFacetManager(change.entity.module)
+          if (manager != null) {
+            val facet = manager.model.removeEntity(change.entity)
+            if (facet != null) {
+              Disposer.dispose(facet)
+              manager.model.facetsChanged()
+              manager.publisher.facetRemoved(facet)
+            }
+          }
+        }
+        is EntityChange.Replaced -> {
+          val manager = getFacetManager(change.newEntity.module)
+          if (manager != null) {
+            val facet = manager.model.updateEntity(change.oldEntity, change.newEntity)
+            if (facet != null) {
+              FacetManagerBase.setFacetName(facet, change.newEntity.name)
+              manager.model.facetsChanged()
+              if (change.oldEntity.name != change.newEntity.name) {
+                manager.publisher.facetRenamed(facet, change.oldEntity.name)
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetManagerViaWorkspaceModel.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetManagerViaWorkspaceModel.kt
new file mode 100644 (file)
index 0000000..fdc2cdc
--- /dev/null
@@ -0,0 +1,133 @@
+// Copyright 2000-2019 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.workspace.legacyBridge.facet
+
+import com.google.common.collect.HashBiMap
+import com.intellij.facet.*
+import com.intellij.facet.impl.FacetModelBase
+import com.intellij.facet.impl.FacetUtil
+import com.intellij.openapi.application.runWriteAction
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.project.ProjectBundle
+import com.intellij.openapi.util.JDOMUtil
+import com.intellij.workspace.api.*
+import com.intellij.workspace.ide.WorkspaceModel
+import com.intellij.workspace.legacyBridge.intellij.LegacyBridgeModule
+import org.jetbrains.jps.model.serialization.facet.FacetState
+
+class FacetManagerViaWorkspaceModel(module: Module) : FacetManagerBase() {
+  internal val module = module as LegacyBridgeModule
+  internal val model = FacetModelViaWorkspaceModel(this.module)
+
+  private fun isThisModule(moduleEntity: ModuleEntity) = moduleEntity.name == module.name
+
+  override fun checkConsistency() {
+    model.checkConsistency(module.entityStore.current.entities(FacetEntity::class.java).filter { isThisModule(it.module) }.toList())
+  }
+
+  override fun facetConfigurationChanged(facet: Facet<*>) {
+    val facetEntity = model.getEntity(facet)
+    if (facetEntity != null) {
+      val facetConfigurationXml = FacetUtil.saveFacetConfiguration(facet)?.let { JDOMUtil.write(it) }
+      if (facetConfigurationXml != facetEntity.configurationXmlTag) {
+        runWriteAction {
+          WorkspaceModel.getInstance(module.project).updateProjectModel {
+            it.modifyEntity(ModifiableFacetEntity::class.java, facetEntity) {
+              this.configurationXmlTag = facetConfigurationXml
+            }
+          }
+        }
+      }
+    }
+    super.facetConfigurationChanged(facet)
+  }
+
+  override fun getModel(): FacetModel = model
+  override fun getModule(): Module = module
+  override fun createModifiableModel(): ModifiableFacetModel {
+    val diff = TypedEntityStorageDiffBuilder.create(module.entityStore.current)
+    return ModifiableFacetModelViaWorkspaceModel(module.entityStore.current, diff, module, this)
+  }
+
+}
+
+internal open class FacetModelViaWorkspaceModel(protected val legacyBridgeModule: LegacyBridgeModule) : FacetModelBase() {
+  protected val entityToFacet: HashBiMap<FacetEntity, Facet<*>> = HashBiMap.create<FacetEntity, Facet<*>>()
+
+  override fun getAllFacets(): Array<Facet<*>> {
+    return entityToFacet.values.toTypedArray()
+  }
+
+  internal fun getOrCreateFacet(entity: FacetEntity): Facet<*> {
+    return entityToFacet.getOrPut(entity) { createFacet(entity) }
+  }
+
+  internal fun getFacet(entity: FacetEntity): Facet<*>? = entityToFacet[entity]
+
+  internal fun getEntity(facet: Facet<*>): FacetEntity? = entityToFacet.inverse()[facet]
+
+  private fun createFacet(entity: FacetEntity): Facet<*> {
+    val registry = FacetTypeRegistry.getInstance()
+    val facetType = registry.findFacetType(entity.facetType)
+    val underlyingFacet = entity.underlyingFacet?.let { getOrCreateFacet(it) }
+    if (facetType == null) {
+      return FacetManagerBase.createInvalidFacet(legacyBridgeModule, FacetState().apply {
+        name = entity.name
+        setFacetType(entity.facetType)
+        configuration = entity.configurationXmlTag?.let { JDOMUtil.load(it) }
+      }, underlyingFacet, ProjectBundle.message("error.message.unknown.facet.type.0", entity.facetType), true)
+    }
+
+    val configuration = facetType.createDefaultConfiguration()
+    val configurationXmlTag = entity.configurationXmlTag
+    if (configurationXmlTag != null) {
+      FacetUtil.loadFacetConfiguration(configuration, JDOMUtil.load(configurationXmlTag))
+    }
+    return facetType.createFacet(legacyBridgeModule, entity.name, configuration, underlyingFacet)
+  }
+
+  fun populateFrom(mapping: HashBiMap<FacetEntity, Facet<*>>) {
+    entityToFacet.putAll(mapping)
+  }
+
+  internal fun populateFrom(mapping: FacetModelViaWorkspaceModel) {
+    entityToFacet.putAll(mapping.entityToFacet)
+  }
+
+  fun removeEntity(entity: FacetEntity): Facet<*>? {
+    return entityToFacet.remove(entity)
+  }
+
+  fun updateEntity(oldEntity: FacetEntity, newEntity: FacetEntity): Facet<*>? {
+    val oldFacet = entityToFacet.remove(oldEntity)
+    if (oldFacet != null) {
+      entityToFacet[newEntity] = oldFacet
+    }
+    return entityToFacet[newEntity]
+  }
+
+  public override fun facetsChanged() {
+    super.facetsChanged()
+  }
+
+  fun checkConsistency(facetEntities: List<FacetEntity>) {
+    val facetEntitiesSet = facetEntities.toSet()
+    for (entity in facetEntities) {
+      val facet = entityToFacet[entity]
+      if (facet == null) {
+        throw IllegalStateException("No facet registered for $entity (name = ${entity.name})")
+      }
+      if (facet.name != entity.name) {
+        throw IllegalStateException("Different name")
+      }
+      val entityFromMapping = entityToFacet.inverse()[facet]!!
+      val facetsFromStorage = entityFromMapping.module.facets.toSet()
+      if (facetsFromStorage != facetEntitiesSet) {
+        throw IllegalStateException("Different set of facets from $entity storage: expected $facetEntitiesSet but was $facetsFromStorage")
+      }
+    }
+    val staleEntity = (entityToFacet.keys - facetEntities).firstOrNull()
+    if (staleEntity != null) {
+      throw IllegalStateException("Stale entity $staleEntity (name = ${staleEntity.name}) in the mapping")
+    }
+  }
+}
diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/ModifiableFacetModelViaWorkspaceModel.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/ModifiableFacetModelViaWorkspaceModel.kt
new file mode 100644 (file)
index 0000000..6d56f6e
--- /dev/null
@@ -0,0 +1,103 @@
+// Copyright 2000-2019 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.workspace.legacyBridge.facet
+
+import com.intellij.facet.Facet
+import com.intellij.facet.ModifiableFacetModel
+import com.intellij.facet.impl.FacetUtil
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.roots.ProjectModelExternalSource
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.util.JDOMUtil
+import com.intellij.util.containers.ContainerUtil
+import com.intellij.workspace.api.*
+import com.intellij.workspace.ide.WorkspaceModel
+import com.intellij.workspace.ide.toEntitySource
+import com.intellij.workspace.legacyBridge.intellij.LegacyBridgeModule
+
+internal class ModifiableFacetModelViaWorkspaceModel(private val initialStorage: TypedEntityStorage,
+                                                     private val diff: TypedEntityStorageDiffBuilder,
+                                                     legacyBridgeModule: LegacyBridgeModule,
+                                                     private val facetManager: FacetManagerViaWorkspaceModel)
+  : FacetModelViaWorkspaceModel(legacyBridgeModule), ModifiableFacetModel {
+  private val listeners: MutableList<ModifiableFacetModel.Listener> = ContainerUtil.createLockFreeCopyOnWriteList()
+
+  init {
+    populateFrom(facetManager.model)
+  }
+
+  private fun getModuleEntity() = initialStorage.resolve(legacyBridgeModule.moduleEntityId)!!
+
+  override fun addFacet(facet: Facet<*>) {
+    addFacet(facet, null)
+  }
+
+  override fun addFacet(facet: Facet<*>, externalSource: ProjectModelExternalSource?) {
+    val moduleEntity = getModuleEntity()
+    val source = externalSource?.toEntitySource() ?: moduleEntity.entitySource
+    val facetConfigurationXml = FacetUtil.saveFacetConfiguration(facet)?.let { JDOMUtil.write(it) }
+    val underlyingEntity = facet.underlyingFacet?.let { entityToFacet.inverse()[it]!! }
+    val entity = diff.addFacetEntity(facet.name, facet.type.stringId, facetConfigurationXml, moduleEntity, underlyingEntity, source)
+    entityToFacet[entity] = facet
+    facetsChanged()
+  }
+
+  override fun removeFacet(facet: Facet<*>?) {
+    val facetEntity = entityToFacet.inverse()[facet] ?: return
+    removeFacetEntityWithSubFacets(facetEntity)
+    facetsChanged()
+  }
+
+  private fun removeFacetEntityWithSubFacets(entity: FacetEntity) {
+    entity.subFacets.forEach {
+      removeFacetEntityWithSubFacets(it)
+    }
+    entityToFacet.remove(entity)
+    diff.removeEntity(entity)
+  }
+
+  override fun rename(facet: Facet<*>, newName: String) {
+    val entity = entityToFacet.inverse()[facet]!!
+    val newEntity = diff.modifyEntity(ModifiableFacetEntity::class.java, entity) {
+      this.name = newName
+    }
+    entityToFacet.inverse()[facet] = newEntity
+    facetsChanged()
+  }
+
+  override fun getNewName(facet: Facet<*>): String? {
+    val entity = entityToFacet.inverse()[facet]!!
+    return entity.name
+  }
+
+  override fun commit() {
+    facetManager.model.populateFrom(entityToFacet)
+    val moduleDiff = legacyBridgeModule.diff
+    if (moduleDiff != null) {
+      moduleDiff.addDiff(diff)
+    }
+    else {
+      WorkspaceModel.getInstance(legacyBridgeModule.project).updateProjectModel {
+        it.addDiff(diff)
+      }
+    }
+  }
+
+  override fun isModified(): Boolean {
+    return !diff.isEmpty()
+  }
+
+  override fun isNewFacet(facet: Facet<*>): Boolean {
+    val entity = entityToFacet.inverse()[facet]
+    return entity != null && initialStorage.resolve(entity.persistentId()) == null
+  }
+
+  override fun addListener(listener: ModifiableFacetModel.Listener, parentDisposable: Disposable) {
+    listeners += listener
+    Disposer.register(parentDisposable, Disposable { listeners -= listener })
+  }
+
+  override fun facetsChanged() {
+    super.facetsChanged()
+    listeners.forEach { it.onChanged() }
+  }
+}
\ No newline at end of file
index 5e1aedad8861cafdc8c2d518e63c1de556a0e0c2..64404a507920046390464c99325d64fd61cad092 100644 (file)
@@ -1,6 +1,7 @@
 // Copyright 2000-2019 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.workspace.legacyBridge.intellij
 
+import com.intellij.facet.FacetManager
 import com.intellij.ide.plugins.IdeaPluginDescriptorImpl
 import com.intellij.ide.plugins.PluginManagerCore
 import com.intellij.openapi.components.impl.stores.IComponentStore
@@ -12,6 +13,7 @@ import com.intellij.openapi.vfs.VirtualFile
 import com.intellij.workspace.api.ModuleId
 import com.intellij.workspace.api.TypedEntityStorageDiffBuilder
 import com.intellij.workspace.api.TypedEntityStore
+import com.intellij.workspace.legacyBridge.facet.FacetManagerViaWorkspaceModel
 import java.io.File
 
 internal class LegacyBridgeModuleImpl(
@@ -36,6 +38,7 @@ internal class LegacyBridgeModuleImpl(
                            ?: error("Could not find plugin by id: ${PluginManagerCore.CORE_ID}")
 
     registerComponent(ModuleRootManager::class.java, LegacyBridgeModuleRootComponent::class.java, pluginDescriptor, true)
+    registerComponent(FacetManager::class.java, FacetManagerViaWorkspaceModel::class.java, pluginDescriptor, true)
 
     registerService(LegacyBridgeFilePointerProvider::class.java, LegacyBridgeFilePointerProviderImpl::class.java, pluginDescriptor, false)
     registerService(IComponentStore::class.java, LegacyBridgeModuleStoreImpl::class.java, pluginDescriptor, true)
index b817acc28af2887eb72a44c4aee14bcd80b6f844..4fd4667d4c3ead949be37af4a659fc194c79715d 100644 (file)
@@ -30,6 +30,7 @@ import com.intellij.workspace.bracket
 import com.intellij.workspace.executeOrQueueOnDispatchThread
 import com.intellij.workspace.ide.*
 import com.intellij.workspace.jps.JpsProjectEntitiesLoader
+import com.intellij.workspace.legacyBridge.facet.FacetEntityChangeListener
 import org.jetbrains.annotations.ApiStatus
 import java.io.File
 import java.util.*
@@ -241,6 +242,7 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM
           }
         }
       })
+      myMessageBusConnection.subscribe(WorkspaceModelTopics.CHANGED, FacetEntityChangeListener(project))
     }
   }
 
index 0e5f4fc4ed5c1a74bc4214fc130c69f9b36a4367..f2f16b8961681a90987691a0ccc4660f48c759ab 100644 (file)
@@ -8,9 +8,9 @@ import com.intellij.openapi.roots.libraries.LibraryTable
 import com.intellij.openapi.roots.libraries.PersistentLibraryKind
 import com.intellij.openapi.util.Disposer
 import com.intellij.workspace.api.*
-import com.intellij.workspace.ide.ExternalEntitySource
 import com.intellij.workspace.ide.IdeUiEntitySource
 import com.intellij.workspace.ide.WorkspaceModel
+import com.intellij.workspace.ide.toEntitySource
 import com.intellij.workspace.legacyBridge.typedModel.library.LibraryViaTypedEntity
 
 internal class LegacyBridgeProjectModifiableLibraryTableImpl(
@@ -64,7 +64,7 @@ internal class LegacyBridgeProjectModifiableLibraryTableImpl(
       tableId = LibraryTableId.ProjectLibraryTableId,
       name = name,
       excludedRoots = emptyList(),
-      source = if (externalSource != null) ExternalEntitySource(externalSource.displayName, externalSource.id) else IdeUiEntitySource
+      source = if (externalSource != null) externalSource.toEntitySource() else IdeUiEntitySource
     )
 
     if (type != null) {