76d88df7abbaff0ef47ee626f3937f78e54db76c
[fedora-idea.git] / platform / lang-impl / src / com / intellij / facet / FacetManagerImpl.java
blob76d88df7abbaff0ef47ee626f3937f78e54db76c
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 addFacets(state.getFacets(), null, model);
247 commit(model, false);
250 public FacetManagerState getState() {
251 FacetManagerState managerState = new FacetManagerState();
253 final Facet[] facets = getSortedFacets();
255 Map<Facet, List<FacetState>> states = new HashMap<Facet, List<FacetState>>();
256 states.put(null, managerState.getFacets());
258 for (Facet facet : facets) {
259 final Facet underlyingFacet = facet.getUnderlyingFacet();
260 final List<FacetState> parent = states.get(underlyingFacet);
262 FacetState facetState = new FacetState();
263 facetState.setFacetType(facet.getType().getStringId());
264 facetState.setName(facet.getName());
265 final Element config;
266 try {
267 FacetConfiguration configuration = facet.getConfiguration();
268 config = FacetUtil.saveFacetConfiguration(configuration);
269 if (facet instanceof JDOMExternalizable) {
270 //todo[nik] remove
271 ((JDOMExternalizable)facet).writeExternal(config);
274 catch (WriteExternalException e) {
275 continue;
277 facetState.setConfiguration(config);
279 parent.add(facetState);
280 List<FacetState> subFacets = facetState.getSubFacets();
281 addInvalidFacets(facet, subFacets);
282 states.put(facet, subFacets);
284 addInvalidFacets(null, managerState.getFacets());
286 return managerState;
289 private void addInvalidFacets(@Nullable Facet facet, final List<FacetState> subFacets) {
290 Collection<FacetState> invalidFacets = myInvalidFacets.get(facet);
291 if (invalidFacets != null) {
292 subFacets.addAll(invalidFacets);
296 public void commit(final ModifiableFacetModel model) {
297 ApplicationManager.getApplication().assertWriteAccessAllowed();
298 commit(model, true);
301 private void commit(final ModifiableFacetModel model, final boolean fireEvents) {
302 LOG.assertTrue(!myInsideCommit, "Recursive commit");
304 Set<Facet> toRemove = new HashSet<Facet>(Arrays.asList(getAllFacets()));
305 List<Facet> toAdd = new ArrayList<Facet>();
306 List<FacetRenameInfo> toRename = new ArrayList<FacetRenameInfo>();
308 final FacetManagerListener publisher = myMessageBus.syncPublisher(FACETS_TOPIC);
310 try {
311 myInsideCommit = true;
313 for (Facet facet : model.getAllFacets()) {
314 boolean isNew = !toRemove.remove(facet);
315 if (isNew) {
316 toAdd.add(facet);
320 List<Facet> newFacets = new ArrayList<Facet>();
321 for (Facet facet : getAllFacets()) {
322 if (!toRemove.contains(facet)) {
323 newFacets.add(facet);
326 newFacets.addAll(toAdd);
328 for (Facet facet : newFacets) {
329 final String newName = model.getNewName(facet);
330 if (newName != null && !newName.equals(facet.getName())) {
331 toRename.add(new FacetRenameInfo(facet, facet.getName(), newName));
335 if (fireEvents) {
336 for (Facet facet : toAdd) {
337 publisher.beforeFacetAdded(facet);
339 for (Facet facet : toRemove) {
340 publisher.beforeFacetRemoved(facet);
342 for (FacetRenameInfo info : toRename) {
343 publisher.beforeFacetRenamed(info.myFacet);
347 for (Facet facet : toRemove) {
348 myInvalidFacets.removeAll(facet);
351 for (FacetRenameInfo info : toRename) {
352 info.myFacet.setName(info.myNewName);
354 myModel.setAllFacets(newFacets.toArray(new Facet[newFacets.size()]));
356 finally {
357 myInsideCommit = false;
360 if (myModuleAdded) {
361 for (Facet facet : toAdd) {
362 facet.initFacet();
365 for (Facet facet : toRemove) {
366 Disposer.dispose(facet);
369 if (fireEvents) {
370 for (Facet facet : toAdd) {
371 publisher.facetAdded(facet);
373 for (Facet facet : toRemove) {
374 publisher.facetRemoved(facet);
376 for (FacetRenameInfo info : toRename) {
377 publisher.facetRenamed(info.myFacet, info.myOldName);
380 for (Facet facet : toAdd) {
381 final Module module = facet.getModule();
382 if (!module.equals(myModule)) {
383 LOG.error(facet + " is created for module " + module + " but added to module " + myModule);
385 final FacetType<?,?> type = facet.getType();
386 if (type.isOnlyOneFacetAllowed()) {
387 if (type.getUnderlyingFacetType() == null) {
388 final Collection<?> facets = getFacetsByType(type.getId());
389 if (facets.size() > 1) {
390 LOG.error("Only one '" + type.getPresentableName() + "' facet per module allowed, but " + facets.size() + " facets found in module '" +
391 myModule.getName() + "'");
394 else {
395 final Facet underlyingFacet = facet.getUnderlyingFacet();
396 LOG.assertTrue(underlyingFacet != null, "Underlying facet is not specifed for '" + facet.getName() + "'");
397 final Collection<?> facets = getFacetsByType(underlyingFacet, type.getId());
398 if (facets.size() > 1) {
399 LOG.error("Only one '" + type.getPresentableName() + "' facet per parent facet allowed, but " + facets.size() + " sub-facets found in facet " + underlyingFacet.getName());
407 public void projectOpened() {
410 public void projectClosed() {
413 public void moduleAdded() {
414 if (myModuleAdded) return;
416 for (Facet facet : getAllFacets()) {
417 facet.initFacet();
419 myModuleAdded = true;
422 @NonNls
423 @NotNull
424 public String getComponentName() {
425 return COMPONENT_NAME;
428 public void initComponent() {
431 public void disposeComponent() {
434 private static class FacetManagerModel extends FacetModelBase {
435 private Facet[] myAllFacets = Facet.EMPTY_ARRAY;
437 @NotNull
438 public Facet[] getAllFacets() {
439 return myAllFacets;
442 public void setAllFacets(final Facet[] allFacets) {
443 myAllFacets = allFacets;
444 facetsChanged();
448 private static class FacetRenameInfo {
449 private final Facet myFacet;
450 private final String myOldName;
451 private final String myNewName;
453 public FacetRenameInfo(final Facet facet, final String oldName, final String newName) {
454 myFacet = facet;
455 myOldName = oldName;
456 myNewName = newName;