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.
16 package com
.intellij
.packaging
.impl
.artifacts
;
18 import com
.intellij
.openapi
.application
.ApplicationManager
;
19 import com
.intellij
.openapi
.application
.Result
;
20 import com
.intellij
.openapi
.application
.WriteAction
;
21 import com
.intellij
.openapi
.components
.*;
22 import com
.intellij
.openapi
.diagnostic
.Logger
;
23 import com
.intellij
.openapi
.project
.Project
;
24 import com
.intellij
.openapi
.util
.ModificationTracker
;
25 import com
.intellij
.openapi
.util
.Pair
;
26 import com
.intellij
.openapi
.vfs
.*;
27 import com
.intellij
.packaging
.artifacts
.*;
28 import com
.intellij
.packaging
.elements
.*;
29 import com
.intellij
.util
.containers
.ContainerUtil
;
30 import com
.intellij
.util
.xmlb
.SkipDefaultValuesSerializationFilters
;
31 import com
.intellij
.util
.xmlb
.XmlSerializer
;
32 import gnu
.trove
.THashSet
;
33 import org
.jdom
.Element
;
34 import org
.jetbrains
.annotations
.NonNls
;
35 import org
.jetbrains
.annotations
.NotNull
;
36 import org
.jetbrains
.annotations
.Nullable
;
44 name
= ArtifactManagerImpl
.COMPONENT_NAME
,
46 @Storage(id
= "other", file
= "$PROJECT_FILE$"),
47 @Storage(id
= "dir", file
= "$PROJECT_CONFIG_DIR$/artifacts/", scheme
= StorageScheme
.DIRECTORY_BASED
, stateSplitter
= ArtifactManagerStateSplitter
.class)
50 public class ArtifactManagerImpl
extends ArtifactManager
implements ProjectComponent
, PersistentStateComponent
<ArtifactManagerState
> {
51 private static final Logger LOG
= Logger
.getInstance("#com.intellij.packaging.impl.artifacts.ArtifactManagerImpl");
52 @NonNls public static final String COMPONENT_NAME
= "ArtifactManager";
53 @NonNls public static final String PACKAGING_ELEMENT_NAME
= "element";
54 @NonNls public static final String TYPE_ID_ATTRIBUTE
= "id";
55 private final ArtifactManagerModel myModel
;
56 private final Project myProject
;
57 private final DefaultPackagingElementResolvingContext myResolvingContext
;
58 private boolean myInsideCommit
= false;
59 private boolean myLoaded
;
60 private long myModificationCount
;
61 private final ModificationTracker myModificationTracker
= new ModificationTracker() {
62 public long getModificationCount() {
63 return myModificationCount
;
66 private Map
<String
, LocalFileSystem
.WatchRequest
> myWatchedOutputs
= new HashMap
<String
, LocalFileSystem
.WatchRequest
>();
68 public ArtifactManagerImpl(Project project
) {
70 myModel
= new ArtifactManagerModel();
71 myResolvingContext
= new DefaultPackagingElementResolvingContext(myProject
);
72 ((ArtifactPointerManagerImpl
)ArtifactPointerManager
.getInstance(project
)).setArtifactManager(this);
76 public Artifact
[] getArtifacts() {
77 return myModel
.getArtifacts();
80 public Artifact
findArtifact(@NotNull String name
) {
81 return myModel
.findArtifact(name
);
85 public Artifact
getArtifactByOriginal(@NotNull Artifact artifact
) {
86 return myModel
.getArtifactByOriginal(artifact
);
90 public Artifact
getOriginalArtifact(@NotNull Artifact artifact
) {
91 return myModel
.getOriginalArtifact(artifact
);
94 public Collection
<?
extends Artifact
> getArtifactsByType(@NotNull ArtifactType type
) {
95 return myModel
.getArtifactsByType(type
);
98 public ArtifactManagerState
getState() {
99 final ArtifactManagerState state
= new ArtifactManagerState();
100 for (Artifact artifact
: getArtifacts()) {
101 final ArtifactState artifactState
= new ArtifactState();
102 artifactState
.setBuildOnMake(artifact
.isBuildOnMake());
103 artifactState
.setName(artifact
.getName());
104 artifactState
.setOutputPath(artifact
.getOutputPath());
105 artifactState
.setRootElement(serializePackagingElement(artifact
.getRootElement()));
106 artifactState
.setArtifactType(artifact
.getArtifactType().getId());
107 for (ArtifactPropertiesProvider provider
: artifact
.getPropertiesProviders()) {
108 final ArtifactPropertiesState propertiesState
= serializeProperties(provider
, artifact
.getProperties(provider
));
109 if (propertiesState
!= null) {
110 artifactState
.getPropertiesList().add(propertiesState
);
113 Collections
.sort(artifactState
.getPropertiesList(), new Comparator
<ArtifactPropertiesState
>() {
114 public int compare(ArtifactPropertiesState o1
, ArtifactPropertiesState o2
) {
115 return o1
.getId().compareTo(o2
.getId());
118 state
.getArtifacts().add(artifactState
);
124 private static <S
> ArtifactPropertiesState
serializeProperties(ArtifactPropertiesProvider provider
, ArtifactProperties
<S
> properties
) {
125 final ArtifactPropertiesState state
= new ArtifactPropertiesState();
126 state
.setId(provider
.getId());
127 final Element options
= new Element("options");
128 XmlSerializer
.serializeInto(properties
.getState(), options
, new SkipDefaultValuesSerializationFilters());
129 if (options
.getContent().isEmpty() && options
.getAttributes().isEmpty()) return null;
130 state
.setOptions(options
);
134 private static Element
serializePackagingElement(PackagingElement
<?
> packagingElement
) {
135 Element element
= new Element(PACKAGING_ELEMENT_NAME
);
136 element
.setAttribute(TYPE_ID_ATTRIBUTE
, packagingElement
.getType().getId());
137 final Object bean
= packagingElement
.getState();
139 XmlSerializer
.serializeInto(bean
, element
, new SkipDefaultValuesSerializationFilters());
141 if (packagingElement
instanceof CompositePackagingElement
) {
142 for (PackagingElement
<?
> child
: ((CompositePackagingElement
<?
>)packagingElement
).getChildren()) {
143 element
.addContent(serializePackagingElement(child
));
149 private <T
> PackagingElement
<T
> deserializeElement(Element element
) {
150 final String id
= element
.getAttributeValue(TYPE_ID_ATTRIBUTE
);
151 PackagingElementType
<?
> type
= PackagingElementFactory
.getInstance().findElementType(id
);
152 PackagingElement
<T
> packagingElement
= (PackagingElement
<T
>)type
.createEmpty(myProject
);
153 T state
= packagingElement
.getState();
155 XmlSerializer
.deserializeInto(state
, element
);
156 packagingElement
.loadState(state
);
158 final List children
= element
.getChildren(PACKAGING_ELEMENT_NAME
);
159 //noinspection unchecked
160 for (Element child
: (List
<?
extends Element
>)children
) {
161 ((CompositePackagingElement
<?
>)packagingElement
).addOrFindChild(deserializeElement(child
));
163 return packagingElement
;
166 public void loadState(ArtifactManagerState managerState
) {
167 final List
<ArtifactImpl
> artifacts
= new ArrayList
<ArtifactImpl
>();
168 for (ArtifactState state
: managerState
.getArtifacts()) {
169 final Element element
= state
.getRootElement();
170 ArtifactType type
= ArtifactType
.findById(state
.getArtifactType());
172 LOG
.info("Unknown artifact type: " + state
.getArtifactType());
176 final String artifactName
= state
.getName();
177 final CompositePackagingElement
<?
> rootElement
;
178 if (element
!= null) {
179 rootElement
= (CompositePackagingElement
<?
>)deserializeElement(element
);
182 rootElement
= type
.createRootElement(artifactName
);
185 final ArtifactImpl artifact
= new ArtifactImpl(artifactName
, type
, state
.isBuildOnMake(), rootElement
, state
.getOutputPath());
186 final List
<ArtifactPropertiesState
> propertiesList
= state
.getPropertiesList();
187 for (ArtifactPropertiesState propertiesState
: propertiesList
) {
188 final ArtifactPropertiesProvider provider
= ArtifactPropertiesProvider
.findById(propertiesState
.getId());
189 if (provider
!= null) {
190 deserializeProperties(artifact
.getProperties(provider
), propertiesState
);
193 artifacts
.add(artifact
);
197 final ArtifactModelImpl model
= new ArtifactModelImpl(this, artifacts
);
201 myModel
.setArtifactsList(artifacts
);
206 private static <S
> void deserializeProperties(ArtifactProperties
<S
> artifactProperties
, ArtifactPropertiesState propertiesState
) {
207 final Element options
= propertiesState
.getOptions();
208 if (artifactProperties
== null || options
== null) {
211 final S state
= artifactProperties
.getState();
213 XmlSerializer
.deserializeInto(state
, options
);
214 artifactProperties
.loadState(state
);
218 public void disposeComponent() {
219 LocalFileSystem
.getInstance().removeWatchedRoots(myWatchedOutputs
.values());
223 public String
getComponentName() {
224 return COMPONENT_NAME
;
227 public void initComponent() {
228 VirtualFileManager
.getInstance().addVirtualFileListener(new ArtifactVirtualFileListener(myProject
, this), myProject
);
229 updateWatchedRoots();
232 private void updateWatchedRoots() {
233 Set
<String
> pathsToRemove
= new HashSet
<String
>(myWatchedOutputs
.keySet());
234 Set
<String
> toAdd
= new HashSet
<String
>();
235 for (Artifact artifact
: getArtifacts()) {
236 final String path
= artifact
.getOutputPath();
237 if (path
!= null && path
.length() > 0) {
238 pathsToRemove
.remove(path
);
239 if (!myWatchedOutputs
.containsKey(path
)) {
245 List
<LocalFileSystem
.WatchRequest
> requestsToRemove
= new ArrayList
<LocalFileSystem
.WatchRequest
>();
246 for (String path
: pathsToRemove
) {
247 final LocalFileSystem
.WatchRequest request
= myWatchedOutputs
.remove(path
);
248 ContainerUtil
.addIfNotNull(request
, requestsToRemove
);
251 final LocalFileSystem fileSystem
= LocalFileSystem
.getInstance();
252 fileSystem
.removeWatchedRoots(requestsToRemove
);
253 final Set
<LocalFileSystem
.WatchRequest
> newRequests
= fileSystem
.addRootsToWatch(toAdd
, true);
254 for (LocalFileSystem
.WatchRequest request
: newRequests
) {
255 myWatchedOutputs
.put(request
.getRootPath(), request
);
259 public void projectOpened() {
262 public void projectClosed() {
266 public Artifact
[] getSortedArtifacts() {
267 return myModel
.getSortedArtifacts();
271 public ModifiableArtifactModel
createModifiableModel() {
272 return new ArtifactModelImpl(this, getArtifactsList());
276 public PackagingElementResolvingContext
getResolvingContext() {
277 return myResolvingContext
;
280 public List
<ArtifactImpl
> getArtifactsList() {
281 return myModel
.myArtifactsList
;
284 public void commit(ArtifactModelImpl artifactModel
) {
285 ApplicationManager
.getApplication().assertWriteAccessAllowed();
287 doCommit(artifactModel
);
290 private void doCommit(ArtifactModelImpl artifactModel
) {
291 LOG
.assertTrue(!myInsideCommit
, "Recursive commit");
292 myInsideCommit
= true;
295 final List
<ArtifactImpl
> allArtifacts
= artifactModel
.getOriginalArtifacts();
297 Set
<ArtifactImpl
> removed
= new THashSet
<ArtifactImpl
>(myModel
.myArtifactsList
);
298 List
<ArtifactImpl
> added
= new ArrayList
<ArtifactImpl
>();
299 List
<Pair
<ArtifactImpl
, String
>> changed
= new ArrayList
<Pair
<ArtifactImpl
, String
>>();
301 for (ArtifactImpl artifact
: allArtifacts
) {
302 final boolean isAdded
= !removed
.remove(artifact
);
303 final ArtifactImpl modifiableCopy
= artifactModel
.getModifiableCopy(artifact
);
307 else if (modifiableCopy
!= null && !modifiableCopy
.equals(artifact
)) {
308 final String oldName
= artifact
.getName();
309 artifact
.copyFrom(modifiableCopy
);
310 changed
.add(Pair
.create(artifact
, oldName
));
314 myModel
.setArtifactsList(allArtifacts
);
315 myModificationCount
++;
316 final ArtifactListener publisher
= myProject
.getMessageBus().syncPublisher(TOPIC
);
317 for (ArtifactImpl artifact
: removed
) {
318 publisher
.artifactRemoved(artifact
);
320 //it's important to send 'removed' events before 'added'. Otherwise when artifacts are reloaded from xml artifact pointers will be damaged
321 for (ArtifactImpl artifact
: added
) {
322 publisher
.artifactAdded(artifact
);
324 for (Pair
<ArtifactImpl
, String
> pair
: changed
) {
325 publisher
.artifactChanged(pair
.getFirst(), pair
.getSecond());
329 myInsideCommit
= false;
331 updateWatchedRoots();
334 public Project
getProject() {
339 public Artifact
addArtifact(@NotNull final String name
, @NotNull final ArtifactType type
, final CompositePackagingElement
<?
> root
) {
340 return new WriteAction
<Artifact
>() {
341 protected void run(final Result
<Artifact
> result
) {
342 final ModifiableArtifactModel model
= createModifiableModel();
343 final ModifiableArtifact artifact
= model
.addArtifact(name
, type
);
345 artifact
.setRootElement(root
);
348 result
.setResult(artifact
);
350 }.execute().getResultObject();
354 public void addElementsToDirectory(@NotNull Artifact artifact
, @NotNull String relativePath
, @NotNull PackagingElement
<?
> element
) {
355 addElementsToDirectory(artifact
, relativePath
, Collections
.singletonList(element
));
359 public void addElementsToDirectory(@NotNull Artifact artifact
, @NotNull String relativePath
,
360 @NotNull Collection
<?
extends PackagingElement
<?
>> elements
) {
361 final ModifiableArtifactModel model
= createModifiableModel();
362 final CompositePackagingElement
<?
> root
= model
.getOrCreateModifiableArtifact(artifact
).getRootElement();
363 PackagingElementFactory
.getInstance().getOrCreateDirectory(root
, relativePath
).addOrFindChildren(elements
);
365 protected void run(final Result result
) {
372 public ModificationTracker
getModificationTracker() {
373 return myModificationTracker
;
376 private static class ArtifactManagerModel
extends ArtifactModelBase
{
377 private List
<ArtifactImpl
> myArtifactsList
= new ArrayList
<ArtifactImpl
>();
378 private Artifact
[] mySortedArtifacts
;
380 public void setArtifactsList(List
<ArtifactImpl
> artifactsList
) {
381 myArtifactsList
= artifactsList
;
386 protected void artifactsChanged() {
387 super.artifactsChanged();
388 mySortedArtifacts
= null;
391 protected List
<?
extends Artifact
> getArtifactsList() {
392 return myArtifactsList
;
395 public Artifact
[] getSortedArtifacts() {
396 if (mySortedArtifacts
== null) {
397 mySortedArtifacts
= getArtifacts().clone();
398 Arrays
.sort(mySortedArtifacts
, ARTIFACT_COMPARATOR
);
400 return mySortedArtifacts
;