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