IDEA-150835: Provide an API which tells which production module corresponding to...
[idea/community.git] / platform / external-system-impl / src / com / intellij / openapi / externalSystem / service / project / AbstractIdeModifiableModelsProvider.java
1 /*
2  * Copyright 2000-2009 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.Facet;
19 import com.intellij.facet.FacetModel;
20 import com.intellij.facet.FacetTypeId;
21 import com.intellij.facet.ModifiableFacetModel;
22 import com.intellij.openapi.application.ModalityState;
23 import com.intellij.openapi.externalSystem.model.project.LibraryData;
24 import com.intellij.openapi.module.ModifiableModuleModel;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.roots.*;
28 import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
29 import com.intellij.openapi.roots.impl.ModifiableModelCommitter;
30 import com.intellij.openapi.roots.libraries.Library;
31 import com.intellij.openapi.roots.libraries.LibraryTable;
32 import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
33 import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
34 import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
35 import com.intellij.openapi.util.Disposer;
36 import com.intellij.openapi.vfs.VirtualFile;
37 import com.intellij.packaging.artifacts.ArtifactModel;
38 import com.intellij.packaging.artifacts.ModifiableArtifactModel;
39 import com.intellij.packaging.elements.ManifestFileProvider;
40 import com.intellij.packaging.elements.PackagingElementResolvingContext;
41 import com.intellij.packaging.impl.artifacts.DefaultManifestFileProvider;
42 import com.intellij.util.containers.ContainerUtil;
43 import com.intellij.util.graph.CachingSemiGraph;
44 import com.intellij.util.graph.Graph;
45 import com.intellij.util.graph.GraphGenerator;
46 import gnu.trove.THashMap;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
49
50 import java.util.*;
51
52 import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.isRelated;
53
54 public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProviderImpl implements IdeModifiableModelsProvider {
55   private ModifiableModuleModel myModifiableModuleModel;
56   private Map<Module, ModifiableRootModel> myModifiableRootModels = new THashMap<Module, ModifiableRootModel>();
57   private Map<Module, ModifiableFacetModel> myModifiableFacetModels = new THashMap<Module, ModifiableFacetModel>();
58   private Map<Module, String> myProductionModulesForTestModules = new THashMap<Module, String>();
59   private Map<Library, Library.ModifiableModel> myModifiableLibraryModels = new IdentityHashMap<Library, Library.ModifiableModel>();
60   private ModifiableArtifactModel myModifiableArtifactModel;
61   private AbstractIdeModifiableModelsProvider.MyPackagingElementResolvingContext myPackagingElementResolvingContext;
62   private final ArtifactExternalDependenciesImporter myArtifactExternalDependenciesImporter;
63
64   public AbstractIdeModifiableModelsProvider(@NotNull Project project) {
65     super(project);
66     myArtifactExternalDependenciesImporter = new ArtifactExternalDependenciesImporterImpl();
67   }
68
69   protected abstract ModifiableArtifactModel doGetModifiableArtifactModel();
70
71   protected abstract ModifiableModuleModel doGetModifiableModuleModel();
72
73   protected abstract ModifiableRootModel doGetModifiableRootModel(Module module);
74
75   protected abstract ModifiableFacetModel doGetModifiableFacetModel(Module module);
76
77   protected abstract Library.ModifiableModel doGetModifiableLibraryModel(Library library);
78
79   @NotNull
80   @Override
81   public abstract LibraryTable.ModifiableModel getModifiableProjectLibrariesModel();
82
83   @NotNull
84   @Override
85   public Module[] getModules() {
86     return getModifiableModuleModel().getModules();
87   }
88
89   protected void processExternalArtifactDependencies() {
90     myArtifactExternalDependenciesImporter.applyChanges(getModifiableArtifactModel(), getPackagingElementResolvingContext());
91   }
92
93   @Override
94   public PackagingElementResolvingContext getPackagingElementResolvingContext() {
95     if (myPackagingElementResolvingContext == null) {
96       myPackagingElementResolvingContext = new MyPackagingElementResolvingContext();
97     }
98     return myPackagingElementResolvingContext;
99   }
100
101   @NotNull
102   @Override
103   public OrderEntry[] getOrderEntries(@NotNull Module module) {
104     return getRootModel(module).getOrderEntries();
105   }
106
107   @NotNull
108   @Override
109   public Module newModule(@NotNull final String filePath, final String moduleTypeId) {
110     Module module = getModifiableModuleModel().newModule(filePath, moduleTypeId);
111     // set module type id explicitly otherwise it can not be set if there is an existing module (with the same filePath) and w/o 'type' attribute
112     module.setOption(Module.ELEMENT_TYPE, moduleTypeId);
113     return module;
114   }
115
116   @Nullable
117   @Override
118   public Library findIdeLibrary(@NotNull LibraryData libraryData) {
119     final LibraryTable.ModifiableModel libraryTable = getModifiableProjectLibrariesModel();
120     for (Library ideLibrary : libraryTable.getLibraries()) {
121       if (isRelated(ideLibrary, libraryData)) return ideLibrary;
122     }
123     return null;
124   }
125
126   @Override
127   @NotNull
128   public VirtualFile[] getContentRoots(Module module) {
129     return getRootModel(module).getContentRoots();
130   }
131
132   @NotNull
133   @Override
134   public VirtualFile[] getSourceRoots(Module module) {
135     return getRootModel(module).getSourceRoots();
136   }
137
138   @NotNull
139   @Override
140   public VirtualFile[] getSourceRoots(Module module, boolean includingTests) {
141     return getRootModel(module).getSourceRoots(includingTests);
142   }
143
144   @NotNull
145   @Override
146   public ModifiableModuleModel getModifiableModuleModel() {
147     if (myModifiableModuleModel == null) {
148       myModifiableModuleModel = doGetModifiableModuleModel();
149     }
150     return myModifiableModuleModel;
151   }
152
153   @Override
154   @NotNull
155   public ModifiableRootModel getModifiableRootModel(Module module) {
156     return (ModifiableRootModel)getRootModel(module);
157   }
158
159   @NotNull
160   private ModuleRootModel getRootModel(Module module) {
161     ModifiableRootModel result = myModifiableRootModels.get(module);
162     if (result == null) {
163       result = doGetModifiableRootModel(module);
164       myModifiableRootModels.put(module, result);
165     }
166     return result;
167   }
168
169   @Override
170   @NotNull
171   public ModifiableFacetModel getModifiableFacetModel(Module module) {
172     ModifiableFacetModel result = myModifiableFacetModels.get(module);
173     if (result == null) {
174       result = doGetModifiableFacetModel(module);
175       myModifiableFacetModels.put(module, result);
176     }
177     return result;
178   }
179
180   @Override
181   @NotNull
182   public ModifiableArtifactModel getModifiableArtifactModel() {
183     if (myModifiableArtifactModel == null) {
184       myModifiableArtifactModel = doGetModifiableArtifactModel();
185     }
186     return myModifiableArtifactModel;
187   }
188
189   @Override
190   @NotNull
191   public Library[] getAllLibraries() {
192     return getModifiableProjectLibrariesModel().getLibraries();
193   }
194
195   @Override
196   @Nullable
197   public Library getLibraryByName(String name) {
198     return getModifiableProjectLibrariesModel().getLibraryByName(name);
199   }
200
201   @Override
202   public Library createLibrary(String name) {
203     return getModifiableProjectLibrariesModel().createLibrary(name);
204   }
205
206   @Override
207   public void removeLibrary(Library library) {
208     getModifiableProjectLibrariesModel().removeLibrary(library);
209   }
210
211   @Override
212   public Library.ModifiableModel getModifiableLibraryModel(Library library) {
213     Library.ModifiableModel result = myModifiableLibraryModels.get(library);
214     if (result == null) {
215       result = doGetModifiableLibraryModel(library);
216       myModifiableLibraryModels.put(library, result);
217     }
218     return result;
219   }
220
221   @NotNull
222   @Override
223   public String[] getLibraryUrls(@NotNull Library library, @NotNull OrderRootType type) {
224     final Library.ModifiableModel model = myModifiableLibraryModels.get(library);
225     if (model != null) {
226       return model.getUrls(type);
227     }
228     return library.getUrls(type);
229   }
230
231   @Override
232   public ModalityState getModalityStateForQuestionDialogs() {
233     return ModalityState.NON_MODAL;
234   }
235
236   @Override
237   public ArtifactExternalDependenciesImporter getArtifactExternalDependenciesImporter() {
238     return myArtifactExternalDependenciesImporter;
239   }
240
241   @NotNull
242   @Override
243   public List<Module> getAllDependentModules(@NotNull Module module) {
244     final ArrayList<Module> list = new ArrayList<Module>();
245     final Graph<Module> graph = getModuleGraph(true);
246     for (Iterator<Module> i = graph.getOut(module); i.hasNext();) {
247       list.add(i.next());
248     }
249     return list;
250   }
251
252   private Graph<Module> getModuleGraph(final boolean includeTests) {
253     return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<Module>() {
254       @Override
255       public Collection<Module> getNodes() {
256         return ContainerUtil.list(getModules());
257       }
258
259       @Override
260       public Iterator<Module> getIn(Module m) {
261         Module[] dependentModules = getModifiableRootModel(m).getModuleDependencies(includeTests);
262         return Arrays.asList(dependentModules).iterator();
263       }
264     }));
265   }
266
267   private class MyPackagingElementResolvingContext implements PackagingElementResolvingContext {
268     private final ModulesProvider myModulesProvider = new MyModulesProvider();
269     private final MyFacetsProvider myFacetsProvider = new MyFacetsProvider();
270     private final ManifestFileProvider myManifestFileProvider = new DefaultManifestFileProvider(this);
271
272     @NotNull
273     public Project getProject() {
274       return myProject;
275     }
276
277     @NotNull
278     public ArtifactModel getArtifactModel() {
279       return AbstractIdeModifiableModelsProvider.this.getModifiableArtifactModel();
280     }
281
282     @NotNull
283     public ModulesProvider getModulesProvider() {
284       return myModulesProvider;
285     }
286
287     @NotNull
288     public FacetsProvider getFacetsProvider() {
289       return myFacetsProvider;
290     }
291
292     public Library findLibrary(@NotNull String level, @NotNull String libraryName) {
293       if (level.equals(LibraryTablesRegistrar.PROJECT_LEVEL)) {
294         return getLibraryByName(libraryName);
295       }
296       final LibraryTable table = LibraryTablesRegistrar.getInstance().getLibraryTableByLevel(level, myProject);
297       return table != null ? table.getLibraryByName(libraryName) : null;
298     }
299
300     @NotNull
301     @Override
302     public ManifestFileProvider getManifestFileProvider() {
303       return myManifestFileProvider;
304     }
305   }
306
307   private class MyModulesProvider implements ModulesProvider {
308     @NotNull
309     public Module[] getModules() {
310       return AbstractIdeModifiableModelsProvider.this.getModules();
311     }
312
313     public Module getModule(String name) {
314       return AbstractIdeModifiableModelsProvider.this.findIdeModule(name);
315     }
316
317     public ModuleRootModel getRootModel(@NotNull Module module) {
318       return AbstractIdeModifiableModelsProvider.this.getModifiableRootModel(module);
319     }
320
321     public FacetModel getFacetModel(@NotNull Module module) {
322       return AbstractIdeModifiableModelsProvider.this.getModifiableFacetModel(module);
323     }
324   }
325
326   private class MyFacetsProvider implements FacetsProvider {
327     @NotNull
328     public Facet[] getAllFacets(Module module) {
329       return getModifiableFacetModel(module).getAllFacets();
330     }
331
332     @NotNull
333     public <F extends Facet> Collection<F> getFacetsByType(Module module, FacetTypeId<F> type) {
334       return getModifiableFacetModel(module).getFacetsByType(type);
335     }
336
337     public <F extends Facet> F findFacet(Module module, FacetTypeId<F> type, String name) {
338       return getModifiableFacetModel(module).findFacet(type, name);
339     }
340   }
341
342   @Override
343   public void commit() {
344     ((ProjectRootManagerEx)ProjectRootManager.getInstance(myProject)).mergeRootsChangesDuring(new Runnable() {
345       public void run() {
346         processExternalArtifactDependencies();
347         for (Library.ModifiableModel each : myModifiableLibraryModels.values()) {
348           each.commit();
349         }
350         getModifiableProjectLibrariesModel().commit();
351
352         Collection<ModifiableRootModel> rootModels = myModifiableRootModels.values();
353         ModifiableRootModel[] rootModels1 = rootModels.toArray(new ModifiableRootModel[rootModels.size()]);
354         for (ModifiableRootModel model : rootModels1) {
355           assert !model.isDisposed() : "Already disposed: " + model;
356         }
357
358         if (myModifiableModuleModel != null) {
359           ModifiableModelCommitter.multiCommit(rootModels1, myModifiableModuleModel);
360         } else {
361           for (ModifiableRootModel model : rootModels1) {
362             model.commit();
363           }
364         }
365         for (Map.Entry<Module, String> entry : myProductionModulesForTestModules.entrySet()) {
366           TestModuleProperties.getInstance(entry.getKey()).setProductionModuleName(entry.getValue());
367         }
368
369         for (Map.Entry<Module, ModifiableFacetModel> each : myModifiableFacetModels.entrySet()) {
370           if(!each.getKey().isDisposed()) {
371             each.getValue().commit();
372           }
373         }
374         if (myModifiableArtifactModel != null) {
375           myModifiableArtifactModel.commit();
376         }
377       }
378     });
379   }
380
381   @Override
382   public void dispose() {
383     for (ModifiableRootModel each : myModifiableRootModels.values()) {
384       if (each.isDisposed()) continue;
385       each.dispose();
386     }
387     Disposer.dispose(getModifiableProjectLibrariesModel());
388
389     for (Library.ModifiableModel each : myModifiableLibraryModels.values()) {
390       Disposer.dispose(each);
391     }
392
393     if(myModifiableModuleModel != null) {
394       myModifiableModuleModel.dispose();
395     }
396     if (myModifiableArtifactModel != null) {
397       myModifiableArtifactModel.dispose();
398     }
399
400     myModifiableRootModels.clear();
401     myModifiableFacetModels.clear();
402     myModifiableLibraryModels.clear();
403   }
404
405   @Override
406   public void setTestModuleProperties(Module testModule, String productionModuleName) {
407     myProductionModulesForTestModules.put(testModule, productionModuleName);
408   }
409 }