[workspace model] IDEA-247859 Fix NPE at getting libraries model in `IdeModifiableMod...
[idea/community.git] / platform / external-system-impl / src / com / intellij / openapi / externalSystem / service / project / IdeModifiableModelsProviderImpl.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.externalSystem.service.project;
17
18 import com.intellij.facet.FacetManager;
19 import com.intellij.facet.ModifiableFacetModel;
20 import com.intellij.openapi.application.ReadAction;
21 import com.intellij.openapi.module.ModifiableModuleModel;
22 import com.intellij.openapi.module.Module;
23 import com.intellij.openapi.module.ModuleManager;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.roots.ModifiableRootModel;
26 import com.intellij.openapi.roots.ModuleRootManagerEx;
27 import com.intellij.openapi.roots.TestModuleProperties;
28 import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
29 import com.intellij.openapi.roots.impl.RootConfigurationAccessor;
30 import com.intellij.openapi.roots.impl.libraries.LibraryEx;
31 import com.intellij.openapi.roots.libraries.Library;
32 import com.intellij.openapi.roots.libraries.LibraryTable;
33 import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
34 import com.intellij.openapi.util.Disposer;
35 import com.intellij.workspaceModel.ide.WorkspaceModel;
36 import com.intellij.workspaceModel.ide.impl.legacyBridge.facet.FacetManagerBridge;
37 import com.intellij.workspaceModel.ide.impl.legacyBridge.library.LibraryBridge;
38 import com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleManagerComponentBridge;
39 import com.intellij.workspaceModel.ide.legacyBridge.ModifiableRootModelBridge;
40 import com.intellij.workspaceModel.ide.impl.legacyBridge.module.roots.ModuleRootComponentBridge;
41 import com.intellij.workspaceModel.ide.legacyBridge.*;
42 import com.intellij.workspaceModel.storage.WorkspaceEntity;
43 import com.intellij.workspaceModel.storage.WorkspaceEntityStorage;
44 import com.intellij.workspaceModel.storage.WorkspaceEntityStorageBuilder;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47
48 import java.util.Collection;
49 import java.util.Map;
50
51 public class IdeModifiableModelsProviderImpl extends AbstractIdeModifiableModelsProvider {
52   private LibraryTable.ModifiableModel myLibrariesModel;
53   private WorkspaceEntityStorageBuilder diff;
54
55   public IdeModifiableModelsProviderImpl(Project project) {
56     super(project);
57   }
58
59   @NotNull
60   @Override
61   public LibraryTable.ModifiableModel getModifiableProjectLibrariesModel() {
62     if (myLibrariesModel != null) return myLibrariesModel;
63     LibraryTable libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(myProject);
64     if (WorkspaceModel.isEnabled()) {
65       return myLibrariesModel = ((ProjectLibraryTableBridge)libraryTable).getModifiableModel(getActualStorageBuilder());
66     }
67     return myLibrariesModel = libraryTable.getModifiableModel();
68   }
69
70   @Override
71   protected ModifiableModuleModel doGetModifiableModuleModel() {
72     return ReadAction.compute(() -> {
73       ModuleManager moduleManager = ModuleManager.getInstance(myProject);
74       if (WorkspaceModel.isEnabled()) {
75         return ((ModuleManagerComponentBridge)moduleManager).getModifiableModel(getActualStorageBuilder());
76       }
77       return moduleManager.getModifiableModel();
78     });
79   }
80
81   @Override
82   @NotNull
83   protected ModifiableRootModel doGetModifiableRootModel(@NotNull final Module module) {
84     RootConfigurationAccessor rootConfigurationAccessor = new RootConfigurationAccessor() {
85       @Nullable
86       @Override
87       public Library getLibrary(Library library, String libraryName, String libraryLevel) {
88         if (LibraryTablesRegistrar.PROJECT_LEVEL.equals(libraryLevel)) {
89           return getModifiableProjectLibrariesModel().getLibraryByName(libraryName);
90         }
91         return library;
92       }
93     };
94
95     return ReadAction.compute(() -> {
96       ModuleRootManagerEx rootManager = ModuleRootManagerEx.getInstanceEx(module);
97       if (WorkspaceModel.isEnabled()) {
98         return ((ModuleRootComponentBridge)rootManager).getModifiableModel(getActualStorageBuilder(), rootConfigurationAccessor);
99       }
100       return rootManager.getModifiableModel(rootConfigurationAccessor);
101     });
102   }
103
104   @Override
105   protected ModifiableFacetModel doGetModifiableFacetModel(Module module) {
106     FacetManager facetManager = FacetManager.getInstance(module);
107     if (WorkspaceModel.isEnabled()) {
108       return ((FacetManagerBridge)facetManager).createModifiableModel(getActualStorageBuilder());
109     }
110     return facetManager.createModifiableModel();
111   }
112
113   @Override
114   protected Library.ModifiableModel doGetModifiableLibraryModel(Library library) {
115     if (WorkspaceModel.isEnabled()) {
116       return ((LibraryBridge)library).getModifiableModel(getActualStorageBuilder());
117     }
118     return library.getModifiableModel();
119   }
120
121   @Override
122   public void commit() {
123     if (WorkspaceModel.isEnabled()) {
124       workspaceModelCommit();
125     } else {
126       super.commit();
127     }
128   }
129
130   private void workspaceModelCommit() {
131     ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(() -> {
132       if (ExternalProjectsWorkspaceImpl.isDependencySubstitutionEnabled()) {
133         updateSubstitutions();
134       }
135       for (Map.Entry<Library, Library.ModifiableModel> entry: myModifiableLibraryModels.entrySet()) {
136         Library fromLibrary = entry.getKey();
137         Library.ModifiableModel modifiableModel = entry.getValue();
138         // removed and (previously) not committed library is being disposed by LibraryTableBase.LibraryModel.removeLibrary
139         // the modifiable model of such library shouldn't be committed
140         if (fromLibrary instanceof LibraryEx && ((LibraryEx)fromLibrary).isDisposed()) {
141           Disposer.dispose(modifiableModel);
142         }
143         else {
144           ((LibraryModifiableModelBridge)modifiableModel).prepareForCommit();
145         }
146       }
147       ((ProjectModifiableLibraryTableBridge)getModifiableProjectLibrariesModel()).prepareForCommit();
148
149       Collection<ModifiableRootModel> rootModels = myModifiableRootModels.values();
150       ModifiableRootModel[] rootModels1 = rootModels.toArray(new ModifiableRootModel[0]);
151       for (ModifiableRootModel model: rootModels1) {
152         assert !model.isDisposed() : "Already disposed: " + model;
153       }
154       if (myModifiableModuleModel != null) ((ModifiableModuleModelBridge)myModifiableModuleModel).prepareForCommit();
155       for (ModifiableRootModel model : rootModels1) {
156         ((ModifiableRootModelBridge)model).prepareForCommit();
157       }
158
159       for (Map.Entry<Module, String> entry: myProductionModulesForTestModules.entrySet()) {
160         TestModuleProperties.getInstance(entry.getKey()).setProductionModuleName(entry.getValue());
161       }
162
163       for (Map.Entry<Module, ModifiableFacetModel> each: myModifiableFacetModels.entrySet()) {
164         if (!each.getKey().isDisposed()) {
165           ((ModifiableFacetModelBridge)each.getValue()).prepareForCommit();
166         }
167       }
168       myModifiableModels.values().forEach(ModifiableModel::commit);
169       Map<WorkspaceEntity, WorkspaceEntity> res = WorkspaceModel.getInstance(myProject).updateProjectModel(builder -> builder.addDiff(getActualStorageBuilder()));
170       for (Map.Entry<Module, ModifiableFacetModel> each: myModifiableFacetModels.entrySet()) {
171         if (!each.getKey().isDisposed()) {
172           ((ModifiableFacetModelBridge)each.getValue()).populateFacetManager(res);
173         }
174       }
175     });
176     myUserData.clear();
177   }
178
179   private WorkspaceEntityStorageBuilder getActualStorageBuilder() {
180     if (diff != null) return diff;
181     WorkspaceEntityStorage entityStorage = WorkspaceModel.getInstance(myProject).getEntityStorage().getCurrent();
182     return diff = WorkspaceEntityStorageBuilder.Companion.from(entityStorage);
183   }
184 }