duplication of facets definitions in iml fixed
[fedora-idea.git] / platform / lang-impl / src / com / intellij / facet / FacetManagerImpl.java
blob3d838663d35646ae500bcd53822be70940371cab
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.intellij.facet;
19 import com.intellij.facet.impl.*;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.components.PersistentStateComponent;
22 import com.intellij.openapi.components.State;
23 import com.intellij.openapi.components.Storage;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.module.ModuleComponent;
27 import com.intellij.openapi.module.ModuleType;
28 import com.intellij.openapi.module.ProjectLoadingErrorsNotifier;
29 import com.intellij.openapi.project.ProjectBundle;
30 import com.intellij.openapi.util.*;
31 import com.intellij.util.messages.MessageBus;
32 import org.jdom.Element;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
37 import java.util.*;
39 /**
40 * @author nik
42 @State(
43 name = FacetManagerImpl.COMPONENT_NAME,
44 storages = {
45 @Storage(
46 id = "default",
47 file = "$MODULE_FILE$"
51 public class FacetManagerImpl extends FacetManager implements ModuleComponent, PersistentStateComponent<FacetManagerState> {
52 private static final Logger LOG = Logger.getInstance("#com.intellij.facet.FacetManagerImpl");
53 @NonNls public static final String FACET_ELEMENT = "facet";
54 @NonNls public static final String TYPE_ATTRIBUTE = "type";
55 @NonNls public static final String CONFIGURATION_ELEMENT = "configuration";
56 @NonNls public static final String NAME_ATTRIBUTE = "name";
57 @NonNls public static final String COMPONENT_NAME = "FacetManager";
59 private final Module myModule;
60 private final FacetTypeRegistry myFacetTypeRegistry;
61 private final FacetManagerModel myModel = new FacetManagerModel();
62 private final MultiValuesMap<Facet, FacetState> myInvalidFacets = new MultiValuesMap<Facet, FacetState>(true);
63 private boolean myInsideCommit = false;
64 private final MessageBus myMessageBus;
65 private boolean myModuleAdded;
67 public FacetManagerImpl(final Module module, MessageBus messageBus, final FacetTypeRegistry facetTypeRegistry) {
68 myModule = module;
69 myMessageBus = messageBus;
70 myFacetTypeRegistry = facetTypeRegistry;
73 @NotNull
74 public ModifiableFacetModel createModifiableModel() {
75 FacetModelImpl model = new FacetModelImpl(this);
76 model.addFacetsFromManager();
77 return model;
80 @NotNull
81 public Facet[] getAllFacets() {
82 return myModel.getAllFacets();
85 @Nullable
86 public <F extends Facet> F getFacetByType(FacetTypeId<F> typeId) {
87 return myModel.getFacetByType(typeId);
90 @Nullable
91 public <F extends Facet> F findFacet(final FacetTypeId<F> type, final String name) {
92 return myModel.findFacet(type, name);
95 @Nullable
96 public <F extends Facet> F getFacetByType(@NotNull final Facet underlyingFacet, final FacetTypeId<F> typeId) {
97 return myModel.getFacetByType(underlyingFacet, typeId);
100 @NotNull
101 public <F extends Facet> Collection<F> getFacetsByType(@NotNull final Facet underlyingFacet, final FacetTypeId<F> typeId) {
102 return myModel.getFacetsByType(underlyingFacet, typeId);
106 @NotNull
107 public <F extends Facet> Collection<F> getFacetsByType(FacetTypeId<F> typeId) {
108 return myModel.getFacetsByType(typeId);
112 @NotNull
113 public Facet[] getSortedFacets() {
114 return myModel.getSortedFacets();
117 @NotNull
118 public String getFacetName(@NotNull Facet facet) {
119 return myModel.getFacetName(facet);
122 @NotNull
123 public <F extends Facet, C extends FacetConfiguration> F createFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @NotNull final C cofiguration,
124 @Nullable final Facet underlying) {
125 final F facet = type.createFacet(myModule, name, cofiguration, underlying);
126 assertTrue(facet.getModule() == myModule, facet, "module");
127 assertTrue(facet.getConfiguration() == cofiguration, facet, "configuration");
128 assertTrue(Comparing.equal(facet.getName(), name), facet, "name");
129 assertTrue(facet.getUnderlyingFacet() == underlying, facet, "underlyingFacet");
130 return facet;
133 @NotNull
134 public <F extends Facet, C extends FacetConfiguration> F createFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @Nullable final Facet underlying) {
135 C configuration = ProjectFacetManager.getInstance(myModule.getProject()).createDefaultConfiguration(type);
136 return createFacet(type, name, configuration, underlying);
139 @NotNull
140 public <F extends Facet, C extends FacetConfiguration> F addFacet(@NotNull final FacetType<F, C> type, @NotNull final String name, @Nullable final Facet underlying) {
141 final ModifiableFacetModel model = createModifiableModel();
142 final F facet = createFacet(type, name, underlying);
143 model.addFacet(facet);
144 model.commit();
145 return facet;
148 private static void assertTrue(final boolean value, final Facet facet, final String parameter) {
149 if (!value) {
150 LOG.error("Facet type " + facet.getType().getClass().getName() + " violates the contract of FacetType.createFacet method about '" +
151 parameter + "' parameter");
155 public void removeInvalidFacet(@Nullable Facet underlyingFacet, @NotNull FacetState facetState) {
156 myInvalidFacets.remove(underlyingFacet, facetState);
160 private void addFacets(final List<FacetState> facetStates, final Facet underlyingFacet, ModifiableFacetModel model) {
161 for (FacetState child : facetStates) {
162 final String typeId = child.getFacetType();
163 if (typeId == null) {
164 registerLoadingError(underlyingFacet, child, ProjectBundle.message("error.message.facet.type.isn.t.specified"));
165 continue;
168 final FacetType<?,?> type = myFacetTypeRegistry.findFacetType(typeId);
169 if (type == null) {
170 registerLoadingError(underlyingFacet, child, ProjectBundle.message("error.message.unknown.facet.type.0", typeId));
171 continue;
174 ModuleType moduleType = myModule.getModuleType();
175 if (!type.isSuitableModuleType(moduleType)) {
176 registerLoadingError(underlyingFacet, child, ProjectBundle.message("error.message.0.facets.are.not.allowed.in.1",
177 type.getPresentableName(), moduleType.getName()));
178 continue;
181 FacetType<?,?> expectedUnderlyingType = null;
182 FacetTypeId<?> underlyingTypeId = type.getUnderlyingFacetType();
183 if (underlyingTypeId != null) {
184 expectedUnderlyingType = myFacetTypeRegistry.findFacetType(underlyingTypeId);
185 if (expectedUnderlyingType == null) {
186 registerLoadingError(underlyingFacet, child, ProjectBundle.message("error.message.cannot.find.underlying.facet.type.for.0", typeId));
187 continue;
190 FacetType actualUnderlyingType = underlyingFacet != null ? underlyingFacet.getType() : null;
191 if (expectedUnderlyingType != null) {
192 if (!expectedUnderlyingType.equals(actualUnderlyingType)) {
193 registerLoadingError(underlyingFacet, child, ProjectBundle.message("error.message.0.facet.must.be.placed.under.1.facet",
194 type.getPresentableName(), expectedUnderlyingType.getPresentableName()));
195 continue;
198 else if (actualUnderlyingType != null) {
199 registerLoadingError(underlyingFacet, child, ProjectBundle.message("error.message.0.cannot.be.placed.under.1",
200 type.getPresentableName(), actualUnderlyingType.getPresentableName()));
201 continue;
204 try {
205 addFacet(type, child, underlyingFacet, model);
207 catch (InvalidDataException e) {
208 LOG.info(e);
209 registerLoadingError(underlyingFacet, child, ProjectBundle.message("error.message.cannot.load.facet.condiguration.0", e.getMessage()));
214 private void registerLoadingError(final Facet underlyingFacet, final FacetState child, final String errorMessage) {
215 myInvalidFacets.put(underlyingFacet, child);
216 FacetLoadingErrorDescription description = new FacetLoadingErrorDescription(myModule, errorMessage, underlyingFacet, child);
217 ProjectLoadingErrorsNotifier.getInstance(myModule.getProject()).registerError(description);
220 private <C extends FacetConfiguration> void addFacet(final FacetType<?, C> type, final FacetState state, final Facet underlyingFacet,
221 final ModifiableFacetModel model) throws InvalidDataException {
222 if (type.isOnlyOneFacetAllowed() &&
223 (underlyingFacet == null && !model.getFacetsByType(type.getId()).isEmpty() ||
224 underlyingFacet != null && !model.getFacetsByType(underlyingFacet, type.getId()).isEmpty())) {
225 LOG.info("'" + state.getName() + "' facet removed from module " + myModule.getName() + ", because only one "
226 + type.getPresentableName() + " facet allowed");
227 return;
229 final C configuration = type.createDefaultConfiguration();
230 final Element config = state.getConfiguration();
231 FacetUtil.loadFacetConfiguration(configuration, config);
232 String name = state.getName();
233 final Facet facet = createFacet(type, name, configuration, underlyingFacet);
234 if (facet instanceof JDOMExternalizable) {
235 //todo[nik] remove
236 ((JDOMExternalizable)facet).readExternal(config);
238 model.addFacet(facet);
239 addFacets(state.getSubFacets(), facet, model);
242 public void loadState(final FacetManagerState state) {
243 ModifiableFacetModel model = new FacetModelImpl(this);
245 myInvalidFacets.clear();
246 addFacets(state.getFacets(), null, model);
248 commit(model, false);
251 public FacetManagerState getState() {
252 FacetManagerState managerState = new FacetManagerState();
254 final Facet[] facets = getSortedFacets();
256 Map<Facet, List<FacetState>> states = new HashMap<Facet, List<FacetState>>();
257 states.put(null, managerState.getFacets());
259 for (Facet facet : facets) {
260 final Facet underlyingFacet = facet.getUnderlyingFacet();
261 final List<FacetState> parent = states.get(underlyingFacet);
263 FacetState facetState = new FacetState();
264 facetState.setFacetType(facet.getType().getStringId());
265 facetState.setName(facet.getName());
266 final Element config;
267 try {
268 FacetConfiguration configuration = facet.getConfiguration();
269 config = FacetUtil.saveFacetConfiguration(configuration);
270 if (facet instanceof JDOMExternalizable) {
271 //todo[nik] remove
272 ((JDOMExternalizable)facet).writeExternal(config);
275 catch (WriteExternalException e) {
276 continue;
278 facetState.setConfiguration(config);
280 parent.add(facetState);
281 List<FacetState> subFacets = facetState.getSubFacets();
282 addInvalidFacets(facet, subFacets);
283 states.put(facet, subFacets);
285 addInvalidFacets(null, managerState.getFacets());
287 return managerState;
290 private void addInvalidFacets(@Nullable Facet facet, final List<FacetState> subFacets) {
291 Collection<FacetState> invalidFacets = myInvalidFacets.get(facet);
292 if (invalidFacets != null) {
293 subFacets.addAll(invalidFacets);
297 public void commit(final ModifiableFacetModel model) {
298 ApplicationManager.getApplication().assertWriteAccessAllowed();
299 commit(model, true);
302 private void commit(final ModifiableFacetModel model, final boolean fireEvents) {
303 LOG.assertTrue(!myInsideCommit, "Recursive commit");
305 Set<Facet> toRemove = new HashSet<Facet>(Arrays.asList(getAllFacets()));
306 List<Facet> toAdd = new ArrayList<Facet>();
307 List<FacetRenameInfo> toRename = new ArrayList<FacetRenameInfo>();
309 final FacetManagerListener publisher = myMessageBus.syncPublisher(FACETS_TOPIC);
311 try {
312 myInsideCommit = true;
314 for (Facet facet : model.getAllFacets()) {
315 boolean isNew = !toRemove.remove(facet);
316 if (isNew) {
317 toAdd.add(facet);
321 List<Facet> newFacets = new ArrayList<Facet>();
322 for (Facet facet : getAllFacets()) {
323 if (!toRemove.contains(facet)) {
324 newFacets.add(facet);
327 newFacets.addAll(toAdd);
329 for (Facet facet : newFacets) {
330 final String newName = model.getNewName(facet);
331 if (newName != null && !newName.equals(facet.getName())) {
332 toRename.add(new FacetRenameInfo(facet, facet.getName(), newName));
336 if (fireEvents) {
337 for (Facet facet : toAdd) {
338 publisher.beforeFacetAdded(facet);
340 for (Facet facet : toRemove) {
341 publisher.beforeFacetRemoved(facet);
343 for (FacetRenameInfo info : toRename) {
344 publisher.beforeFacetRenamed(info.myFacet);
348 for (Facet facet : toRemove) {
349 myInvalidFacets.removeAll(facet);
352 for (FacetRenameInfo info : toRename) {
353 info.myFacet.setName(info.myNewName);
355 myModel.setAllFacets(newFacets.toArray(new Facet[newFacets.size()]));
357 finally {
358 myInsideCommit = false;
361 if (myModuleAdded) {
362 for (Facet facet : toAdd) {
363 facet.initFacet();
366 for (Facet facet : toRemove) {
367 Disposer.dispose(facet);
370 if (fireEvents) {
371 for (Facet facet : toAdd) {
372 publisher.facetAdded(facet);
374 for (Facet facet : toRemove) {
375 publisher.facetRemoved(facet);
377 for (FacetRenameInfo info : toRename) {
378 publisher.facetRenamed(info.myFacet, info.myOldName);
381 for (Facet facet : toAdd) {
382 final Module module = facet.getModule();
383 if (!module.equals(myModule)) {
384 LOG.error(facet + " is created for module " + module + " but added to module " + myModule);
386 final FacetType<?,?> type = facet.getType();
387 if (type.isOnlyOneFacetAllowed()) {
388 if (type.getUnderlyingFacetType() == null) {
389 final Collection<?> facets = getFacetsByType(type.getId());
390 if (facets.size() > 1) {
391 LOG.error("Only one '" + type.getPresentableName() + "' facet per module allowed, but " + facets.size() + " facets found in module '" +
392 myModule.getName() + "'");
395 else {
396 final Facet underlyingFacet = facet.getUnderlyingFacet();
397 LOG.assertTrue(underlyingFacet != null, "Underlying facet is not specifed for '" + facet.getName() + "'");
398 final Collection<?> facets = getFacetsByType(underlyingFacet, type.getId());
399 if (facets.size() > 1) {
400 LOG.error("Only one '" + type.getPresentableName() + "' facet per parent facet allowed, but " + facets.size() + " sub-facets found in facet " + underlyingFacet.getName());
408 public void projectOpened() {
411 public void projectClosed() {
414 public void moduleAdded() {
415 if (myModuleAdded) return;
417 for (Facet facet : getAllFacets()) {
418 facet.initFacet();
420 myModuleAdded = true;
423 @NonNls
424 @NotNull
425 public String getComponentName() {
426 return COMPONENT_NAME;
429 public void initComponent() {
432 public void disposeComponent() {
435 private static class FacetManagerModel extends FacetModelBase {
436 private Facet[] myAllFacets = Facet.EMPTY_ARRAY;
438 @NotNull
439 public Facet[] getAllFacets() {
440 return myAllFacets;
443 public void setAllFacets(final Facet[] allFacets) {
444 myAllFacets = allFacets;
445 facetsChanged();
449 private static class FacetRenameInfo {
450 private final Facet myFacet;
451 private final String myOldName;
452 private final String myNewName;
454 public FacetRenameInfo(final Facet facet, final String oldName, final String newName) {
455 myFacet = facet;
456 myOldName = oldName;
457 myNewName = newName;