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
;
43 name
= FacetManagerImpl
.COMPONENT_NAME
,
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
) {
69 myMessageBus
= messageBus
;
70 myFacetTypeRegistry
= facetTypeRegistry
;
74 public ModifiableFacetModel
createModifiableModel() {
75 FacetModelImpl model
= new FacetModelImpl(this);
76 model
.addFacetsFromManager();
81 public Facet
[] getAllFacets() {
82 return myModel
.getAllFacets();
86 public <F
extends Facet
> F
getFacetByType(FacetTypeId
<F
> typeId
) {
87 return myModel
.getFacetByType(typeId
);
91 public <F
extends Facet
> F
findFacet(final FacetTypeId
<F
> type
, final String name
) {
92 return myModel
.findFacet(type
, name
);
96 public <F
extends Facet
> F
getFacetByType(@NotNull final Facet underlyingFacet
, final FacetTypeId
<F
> typeId
) {
97 return myModel
.getFacetByType(underlyingFacet
, typeId
);
101 public <F
extends Facet
> Collection
<F
> getFacetsByType(@NotNull final Facet underlyingFacet
, final FacetTypeId
<F
> typeId
) {
102 return myModel
.getFacetsByType(underlyingFacet
, typeId
);
107 public <F
extends Facet
> Collection
<F
> getFacetsByType(FacetTypeId
<F
> typeId
) {
108 return myModel
.getFacetsByType(typeId
);
113 public Facet
[] getSortedFacets() {
114 return myModel
.getSortedFacets();
118 public String
getFacetName(@NotNull Facet facet
) {
119 return myModel
.getFacetName(facet
);
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");
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
);
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
);
148 private static void assertTrue(final boolean value
, final Facet facet
, final String parameter
) {
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"));
168 final FacetType
<?
,?
> type
= myFacetTypeRegistry
.findFacetType(typeId
);
170 registerLoadingError(underlyingFacet
, child
, ProjectBundle
.message("error.message.unknown.facet.type.0", typeId
));
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()));
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
));
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()));
198 else if (actualUnderlyingType
!= null) {
199 registerLoadingError(underlyingFacet
, child
, ProjectBundle
.message("error.message.0.cannot.be.placed.under.1",
200 type
.getPresentableName(), actualUnderlyingType
.getPresentableName()));
205 addFacet(type
, child
, underlyingFacet
, model
);
207 catch (InvalidDataException 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");
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
) {
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
;
267 FacetConfiguration configuration
= facet
.getConfiguration();
268 config
= FacetUtil
.saveFacetConfiguration(configuration
);
269 if (facet
instanceof JDOMExternalizable
) {
271 ((JDOMExternalizable
)facet
).writeExternal(config
);
274 catch (WriteExternalException e
) {
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());
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();
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
);
311 myInsideCommit
= true;
313 for (Facet facet
: model
.getAllFacets()) {
314 boolean isNew
= !toRemove
.remove(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
));
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()]));
357 myInsideCommit
= false;
361 for (Facet facet
: toAdd
) {
365 for (Facet facet
: toRemove
) {
366 Disposer
.dispose(facet
);
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() + "'");
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()) {
419 myModuleAdded
= true;
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
;
438 public Facet
[] getAllFacets() {
442 public void setAllFacets(final Facet
[] allFacets
) {
443 myAllFacets
= allFacets
;
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
) {