855341fca90a89811011f75fb63712439c00b0e6
[idea/community.git] / java / idea-ui / src / com / intellij / facet / impl / ProjectFacetsConfigurator.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
17 package com.intellij.facet.impl;
18
19 import com.intellij.facet.*;
20 import com.intellij.facet.impl.ui.FacetEditorImpl;
21 import com.intellij.facet.impl.ui.FacetTreeModel;
22 import com.intellij.facet.impl.ui.ProjectConfigurableContext;
23 import com.intellij.facet.ui.FacetEditorContext;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.options.ConfigurationException;
27 import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
28 import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
29 import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
30 import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
31 import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
32 import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
33 import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
34 import com.intellij.openapi.util.Disposer;
35 import com.intellij.openapi.util.UserDataHolder;
36 import com.intellij.openapi.util.UserDataHolderBase;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39
40 import java.util.*;
41
42 /**
43  * @author nik
44  */
45 public class ProjectFacetsConfigurator implements FacetsProvider {
46   private static final Logger LOG = Logger.getInstance(ProjectFacetsConfigurator.class);
47   private final Map<Module, ModifiableFacetModel> myModifiableModels = new HashMap<>();
48   private final Map<Facet, FacetEditorImpl> myEditors = new LinkedHashMap<>();
49   private final Map<Module, FacetTreeModel> myTreeModels = new HashMap<>();
50   private final Map<FacetInfo, Facet> myInfo2Facet = new HashMap<>();
51   private final Map<Facet, FacetInfo> myFacet2Info = new HashMap<>();
52   private final Map<Module, UserDataHolder> mySharedModuleData = new HashMap<>();
53   private final Set<Facet> myFacetsToDispose = new HashSet<>();
54   private final Set<Facet> myChangedFacets = new HashSet<>();
55   private final Set<Facet> myCreatedFacets = new HashSet<>();
56   private final StructureConfigurableContext myContext;
57   private UserDataHolderBase myProjectData = new UserDataHolderBase();
58
59   public ProjectFacetsConfigurator(final StructureConfigurableContext context, ProjectFacetsConfigurator facetsConfigurator) {
60     myContext = context;
61
62     if (facetsConfigurator != null) {
63       initFrom(facetsConfigurator);
64     }
65   }
66
67   private void initFrom(ProjectFacetsConfigurator facetsConfigurator) {
68     myFacet2Info.putAll(facetsConfigurator.myFacet2Info);
69     myInfo2Facet.putAll(facetsConfigurator.myInfo2Facet);
70     myTreeModels.putAll(facetsConfigurator.myTreeModels);
71     myEditors.putAll(facetsConfigurator.myEditors);
72   }
73
74   public List<Facet> removeFacet(Facet facet) {
75     FacetTreeModel treeModel = getTreeModel(facet.getModule());
76     FacetInfo facetInfo = myFacet2Info.get(facet);
77     if (facetInfo == null) return Collections.emptyList();
78
79     final List<Facet> removed = new ArrayList<>();
80     List<FacetInfo> childrenList = treeModel.getChildren(facetInfo);
81     FacetInfo[] children = childrenList.toArray(FacetInfo.EMPTY_ARRAY);
82     for (FacetInfo child : children) {
83       Facet childInfo = myInfo2Facet.get(child);
84       if (childInfo != null) {
85         removed.addAll(removeFacet(childInfo));
86       }
87     }
88
89     treeModel.removeFacetInfo(facetInfo);
90     getOrCreateModifiableModel(facet.getModule()).removeFacet(facet);
91     myChangedFacets.remove(facet);
92     if (myCreatedFacets.contains(facet)) {
93       Disposer.dispose(facet);
94     }
95     final FacetEditorImpl facetEditor = myEditors.remove(facet);
96     if (facetEditor != null) {
97       facetEditor.disposeUIResources();
98     }
99     myFacet2Info.remove(facet);
100     myInfo2Facet.remove(facetInfo);
101     removed.add(facet);
102     return removed;
103   }
104
105   public Facet createAndAddFacet(Module module, FacetType<?, ?> type, final @Nullable Facet underlying) {
106     final Collection<? extends Facet> facets = getFacetsByType(module, type.getId());
107     String facetName = type.getDefaultFacetName();
108     int i = 2;
109     while (facetExists(facetName, facets)) {
110       facetName = type.getDefaultFacetName() + i;
111       i++;
112     }
113     final Facet facet = FacetManager.getInstance(module).createFacet(type, facetName, underlying);
114     myCreatedFacets.add(facet);
115     addFacetInfo(facet);
116     getOrCreateModifiableModel(module).addFacet(facet);
117     return facet;
118   }
119
120   private boolean facetExists(final String facetName, final Collection<? extends Facet> facets) {
121     for (Facet facet : facets) {
122       if (getFacetName(facet).equals(facetName)) {
123         return true;
124       }
125     }
126     return false;
127   }
128
129   public void addFacetInfo(final Facet facet) {
130     final FacetInfo exiting = myFacet2Info.get(facet);
131     if (exiting != null) {
132       LOG.assertTrue(exiting.getName().equals(facet.getName()));
133       LOG.assertTrue(exiting.getFacetType().equals(facet.getType()));
134       LOG.assertTrue(exiting.getConfiguration().equals(facet.getConfiguration()));
135       return;
136     }
137
138     FacetInfo info = new FacetInfo(facet.getType(), facet.getName(), facet.getConfiguration(), myFacet2Info.get(facet.getUnderlyingFacet()));
139     myFacet2Info.put(facet, info);
140     myInfo2Facet.put(info, facet);
141     getTreeModel(facet.getModule()).addFacetInfo(info);
142   }
143
144   public void addFacetInfos(final Module module) {
145     final Facet[] facets = getFacetModel(module).getSortedFacets();
146     for (Facet facet : facets) {
147       addFacetInfo(facet);
148     }
149   }
150
151   public void clearMaps() {
152     myModifiableModels.clear();
153     myEditors.clear();
154     myTreeModels.clear();
155     myInfo2Facet.clear();
156     myFacet2Info.clear();
157     myChangedFacets.clear();
158     mySharedModuleData.clear();
159   }
160
161   private boolean isNewFacet(Facet facet) {
162     final ModifiableFacetModel model = myModifiableModels.get(facet.getModule());
163     return model != null && model.isNewFacet(facet);
164   }
165
166   @NotNull
167   public ModifiableFacetModel getOrCreateModifiableModel(final Module module) {
168     ModifiableFacetModel model = myModifiableModels.get(module);
169     if (model == null) {
170       model = FacetManager.getInstance(module).createModifiableModel();
171       myModifiableModels.put(module, model);
172     }
173     return model;
174   }
175
176   @Nullable
177   public FacetEditorImpl getEditor(Facet facet) {
178     return myEditors.get(facet);
179   }
180
181   @NotNull
182   public FacetEditorImpl getOrCreateEditor(Facet facet) {
183     FacetEditorImpl editor = myEditors.get(facet);
184     if (editor == null) {
185       final Facet underlyingFacet = facet.getUnderlyingFacet();
186       final FacetEditorContext parentContext = underlyingFacet != null ? getOrCreateEditor(underlyingFacet).getContext() : null;
187
188       final FacetEditorContext context = createContext(facet, parentContext);
189       editor = new FacetEditorImpl(context, facet.getConfiguration());
190       editor.getComponent();
191       editor.reset();
192       myEditors.put(facet, editor);
193     }
194     return editor;
195   }
196
197   protected FacetEditorContext createContext(final @NotNull Facet facet, final @Nullable FacetEditorContext parentContext) {
198     Module module = facet.getModule();
199     ModulesConfigurator modulesConfigurator = myContext.getModulesConfigurator();
200     ModuleEditor moduleEditor = modulesConfigurator.getModuleEditor(module);
201     if (moduleEditor == null) {
202       LOG.error("ModuleEditor[" + module.getName() + "]==null: disposed = " + module.isDisposed() + ", is in model = "
203                 + Arrays.asList(modulesConfigurator.getModules()).contains(module));
204     }
205
206     final ModuleConfigurationState state = moduleEditor.createModuleConfigurationState();
207     return new MyProjectConfigurableContext(facet, parentContext, state);
208   }
209
210   private UserDataHolder getSharedModuleData(final Module module) {
211     UserDataHolder dataHolder = mySharedModuleData.get(module);
212     if (dataHolder == null) {
213       dataHolder = new UserDataHolderBase();
214       mySharedModuleData.put(module, dataHolder);
215     }
216     return dataHolder;
217   }
218
219   @NotNull
220   public FacetModel getFacetModel(Module module) {
221     final ModifiableFacetModel model = myModifiableModels.get(module);
222     if (model != null) {
223       return model;
224     }
225     return FacetManager.getInstance(module);
226   }
227
228   public void commitFacets() {
229     for (ModifiableFacetModel model : myModifiableModels.values()) {
230       model.commit();
231     }
232
233     for (Map.Entry<Facet, FacetEditorImpl> entry : myEditors.entrySet()) {
234       entry.getValue().onFacetAdded(entry.getKey());
235     }
236
237     myModifiableModels.clear();
238     for (Facet facet : myChangedFacets) {
239       Module module = facet.getModule();
240       if (!module.isDisposed()) {
241         module.getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(facet);
242       }
243     }
244     myChangedFacets.clear();
245   }
246
247   public void resetEditors() {
248     for (FacetEditorImpl editor : myEditors.values()) {
249       editor.reset();
250     }
251   }
252
253   public void applyEditors() throws ConfigurationException {
254     for (Map.Entry<Facet, FacetEditorImpl> entry : myEditors.entrySet()) {
255       final FacetEditorImpl editor = entry.getValue();
256       if (editor.isModified()) {
257         myChangedFacets.add(entry.getKey());
258       }
259       editor.apply();
260     }
261   }
262
263   public boolean isModified() {
264     for (ModifiableFacetModel model : myModifiableModels.values()) {
265       if (model.isModified()) {
266         return true;
267       }
268     }
269     for (FacetEditorImpl editor : myEditors.values()) {
270       if (editor.isModified()) {
271         return true;
272       }
273     }
274     return false;
275   }
276
277   public FacetTreeModel getTreeModel(Module module) {
278     FacetTreeModel treeModel = myTreeModels.get(module);
279     if (treeModel == null) {
280       treeModel = new FacetTreeModel();
281       myTreeModels.put(module, treeModel);
282     }
283     return treeModel;
284   }
285
286   public FacetInfo getFacetInfo(final Facet facet) {
287     return myFacet2Info.get(facet);
288   }
289
290   public Facet getFacet(final FacetInfo facetInfo) {
291     return myInfo2Facet.get(facetInfo);
292   }
293
294   public void disposeEditors() {
295     for (Facet facet : myFacetsToDispose) {
296       Disposer.dispose(facet);
297     }
298     myFacetsToDispose.clear();
299     myCreatedFacets.clear();
300
301     for (FacetEditorImpl editor : myEditors.values()) {
302       editor.disposeUIResources();
303     }
304     myProjectData = null;
305   }
306
307   @Override
308   @NotNull
309   public Facet[] getAllFacets(final Module module) {
310     return getFacetModel(module).getAllFacets();
311   }
312
313   @Override
314   @NotNull
315   public <F extends Facet> Collection<F> getFacetsByType(final Module module, final FacetTypeId<F> type) {
316     return getFacetModel(module).getFacetsByType(type);
317   }
318
319   @Override
320   @Nullable
321   public <F extends Facet> F findFacet(final Module module, final FacetTypeId<F> type, final String name) {
322     return getFacetModel(module).findFacet(type, name);
323   }
324
325   private UserDataHolder getProjectData() {
326     if (myProjectData == null) {
327       myProjectData = new UserDataHolderBase();
328     }
329     return myProjectData;
330   }
331
332   public String getFacetName(Facet facet) {
333     final ModifiableFacetModel model = myModifiableModels.get(facet.getModule());
334     if (model != null) {
335       final String newName = model.getNewName(facet);
336       if (newName != null) {
337         return newName;
338       }
339     }
340     return facet.getName();
341   }
342
343   public List<Facet> removeAllFacets(final Module module) {
344     List<Facet> facets = new ArrayList<>();
345     FacetModel facetModel = getOrCreateModifiableModel(module);
346     for (Facet facet : facetModel.getAllFacets()) {
347       if (!myCreatedFacets.contains(facet)) {
348         myFacetsToDispose.add(facet);
349       }
350       LOG.assertTrue(facet.getModule().equals(module), module + " expected but " + facet.getModule() + " found");
351       facets.addAll(removeFacet(facet));
352     }
353     mySharedModuleData.remove(module);
354     myModifiableModels.remove(module);
355     return facets;
356   }
357
358   public boolean hasFacetOfType(Module module, @Nullable Facet parent, FacetTypeId<?> typeId) {
359     final FacetTreeModel treeModel = getTreeModel(module);
360     final FacetInfo parentInfo = getFacetInfo(parent);
361     return treeModel.hasFacetOfType(parentInfo, typeId);
362   }
363
364   private class MyProjectConfigurableContext extends ProjectConfigurableContext {
365     private final LibrariesContainer myContainer;
366
367     MyProjectConfigurableContext(final Facet facet, final FacetEditorContext parentContext, final ModuleConfigurationState state) {
368       super(facet, ProjectFacetsConfigurator.this.isNewFacet(facet), parentContext, state,
369             ProjectFacetsConfigurator.this.getSharedModuleData(facet.getModule()), getProjectData());
370       myContainer = LibrariesContainerFactory.createContainer(myContext);
371     }
372
373     @Override
374     public LibrariesContainer getContainer() {
375       return myContainer;
376     }
377
378   }
379 }