IDEADEV-40824: Artifact: project elements move/rename does not update artifact definition
[fedora-idea.git] / java / compiler / impl / src / com / intellij / packaging / impl / artifacts / ArtifactManagerImpl.java
blobbb89c9db80db45cad4dbd69e446b90ebeabc1385
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.
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;
38 import java.util.*;
40 /**
41 * @author nik
43 @State(
44 name = ArtifactManagerImpl.COMPONENT_NAME,
45 storages = {
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 = new ArtifactManagerModel();
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, VirtualFileManager virtualFileManager) {
69 myProject = project;
70 myResolvingContext = new DefaultPackagingElementResolvingContext(myProject);
71 virtualFileManager.addVirtualFileListener(new ArtifactVirtualFileListener(myProject, this), myProject);
74 @NotNull
75 public Artifact[] getArtifacts() {
76 return myModel.getArtifacts();
79 public Artifact findArtifact(@NotNull String name) {
80 return myModel.findArtifact(name);
83 @NotNull
84 public Artifact getArtifactByOriginal(@NotNull Artifact artifact) {
85 return myModel.getArtifactByOriginal(artifact);
88 public Collection<? extends Artifact> getArtifactsByType(@NotNull ArtifactType type) {
89 return myModel.getArtifactsByType(type);
92 public ArtifactManagerState getState() {
93 final ArtifactManagerState state = new ArtifactManagerState();
94 for (Artifact artifact : getArtifacts()) {
95 final ArtifactState artifactState = new ArtifactState();
96 artifactState.setBuildOnMake(artifact.isBuildOnMake());
97 artifactState.setName(artifact.getName());
98 artifactState.setOutputPath(artifact.getOutputPath());
99 artifactState.setRootElement(serializePackagingElement(artifact.getRootElement()));
100 artifactState.setArtifactType(artifact.getArtifactType().getId());
101 for (ArtifactPropertiesProvider provider : artifact.getPropertiesProviders()) {
102 final ArtifactPropertiesState propertiesState = serializeProperties(provider, artifact.getProperties(provider));
103 if (propertiesState != null) {
104 artifactState.getPropertiesList().add(propertiesState);
107 state.getArtifacts().add(artifactState);
109 return state;
112 @Nullable
113 private static <S> ArtifactPropertiesState serializeProperties(ArtifactPropertiesProvider provider, ArtifactProperties<S> properties) {
114 final ArtifactPropertiesState state = new ArtifactPropertiesState();
115 state.setId(provider.getId());
116 final Element options = new Element("options");
117 XmlSerializer.serializeInto(properties.getState(), options, new SkipDefaultValuesSerializationFilters());
118 if (options.getContent().isEmpty() && options.getAttributes().isEmpty()) return null;
119 state.setOptions(options);
120 return state;
123 private static Element serializePackagingElement(PackagingElement<?> packagingElement) {
124 Element element = new Element(PACKAGING_ELEMENT_NAME);
125 element.setAttribute(TYPE_ID_ATTRIBUTE, packagingElement.getType().getId());
126 final Object bean = packagingElement.getState();
127 if (bean != null) {
128 XmlSerializer.serializeInto(bean, element, new SkipDefaultValuesSerializationFilters());
130 if (packagingElement instanceof CompositePackagingElement) {
131 for (PackagingElement<?> child : ((CompositePackagingElement<?>)packagingElement).getChildren()) {
132 element.addContent(serializePackagingElement(child));
135 return element;
138 private <T> PackagingElement<T> deserializeElement(Element element) {
139 final String id = element.getAttributeValue(TYPE_ID_ATTRIBUTE);
140 PackagingElementType<?> type = PackagingElementFactory.getInstance().findElementType(id);
141 PackagingElement<T> packagingElement = (PackagingElement<T>)type.createEmpty(myProject);
142 T state = packagingElement.getState();
143 if (state != null) {
144 XmlSerializer.deserializeInto(state, element);
145 packagingElement.loadState(state);
147 final List children = element.getChildren(PACKAGING_ELEMENT_NAME);
148 //noinspection unchecked
149 for (Element child : (List<? extends Element>)children) {
150 ((CompositePackagingElement<?>)packagingElement).addOrFindChild(deserializeElement(child));
152 return packagingElement;
155 public void loadState(ArtifactManagerState managerState) {
156 final List<ArtifactImpl> artifacts = new ArrayList<ArtifactImpl>();
157 for (ArtifactState state : managerState.getArtifacts()) {
158 final Element element = state.getRootElement();
159 ArtifactType type = ArtifactType.findById(state.getArtifactType());
160 if (type == null) {
161 LOG.info("Unknown artifact type: " + state.getArtifactType());
162 continue;
165 final String artifactName = state.getName();
166 final CompositePackagingElement<?> rootElement;
167 if (element != null) {
168 rootElement = (CompositePackagingElement<?>)deserializeElement(element);
170 else {
171 rootElement = type.createRootElement(artifactName);
174 final ArtifactImpl artifact = new ArtifactImpl(artifactName, type, state.isBuildOnMake(), rootElement, state.getOutputPath());
175 final List<ArtifactPropertiesState> propertiesList = state.getPropertiesList();
176 for (ArtifactPropertiesState propertiesState : propertiesList) {
177 final ArtifactPropertiesProvider provider = ArtifactPropertiesProvider.findById(propertiesState.getId());
178 if (provider != null) {
179 deserializeProperties(artifact.getProperties(provider), propertiesState);
182 artifacts.add(artifact);
185 if (myLoaded) {
186 final ArtifactModelImpl model = new ArtifactModelImpl(this);
187 model.addArtifacts(artifacts);
188 doCommit(model);
190 else {
191 myModel.setArtifactsList(artifacts);
192 myLoaded = true;
196 private static <S> void deserializeProperties(ArtifactProperties<S> artifactProperties, ArtifactPropertiesState propertiesState) {
197 final Element options = propertiesState.getOptions();
198 if (artifactProperties == null || options == null) {
199 return;
201 final S state = artifactProperties.getState();
202 if (state != null) {
203 XmlSerializer.deserializeInto(state, options);
204 artifactProperties.loadState(state);
208 public void disposeComponent() {
209 LocalFileSystem.getInstance().removeWatchedRoots(myWatchedOutputs.values());
212 @NotNull
213 public String getComponentName() {
214 return COMPONENT_NAME;
217 public void initComponent() {
218 updateWatchedRoots();
221 private void updateWatchedRoots() {
222 Set<String> pathsToRemove = new HashSet<String>(myWatchedOutputs.keySet());
223 Set<String> toAdd = new HashSet<String>();
224 for (Artifact artifact : getArtifacts()) {
225 final String path = artifact.getOutputPath();
226 if (path != null && path.length() > 0) {
227 pathsToRemove.remove(path);
228 if (!myWatchedOutputs.containsKey(path)) {
229 toAdd.add(path);
234 List<LocalFileSystem.WatchRequest> requestsToRemove = new ArrayList<LocalFileSystem.WatchRequest>();
235 for (String path : pathsToRemove) {
236 final LocalFileSystem.WatchRequest request = myWatchedOutputs.remove(path);
237 ContainerUtil.addIfNotNull(request, requestsToRemove);
240 final LocalFileSystem fileSystem = LocalFileSystem.getInstance();
241 fileSystem.removeWatchedRoots(requestsToRemove);
242 final Set<LocalFileSystem.WatchRequest> newRequests = fileSystem.addRootsToWatch(toAdd, true);
243 for (LocalFileSystem.WatchRequest request : newRequests) {
244 myWatchedOutputs.put(request.getRootPath(), request);
248 public void projectOpened() {
251 public void projectClosed() {
254 @Override
255 public Artifact[] getSortedArtifacts() {
256 return myModel.getSortedArtifacts();
259 @Override
260 public ModifiableArtifactModel createModifiableModel() {
261 ((ArtifactPointerManagerImpl)ArtifactPointerManager.getInstance(myProject)).updateAllPointers();
262 final ArtifactModelImpl model = new ArtifactModelImpl(this);
263 model.addArtifacts(getArtifactsList());
264 return model;
267 @Override
268 public PackagingElementResolvingContext getResolvingContext() {
269 return myResolvingContext;
272 public List<ArtifactImpl> getArtifactsList() {
273 return myModel.myArtifactsList;
276 public void commit(ArtifactModelImpl artifactModel) {
277 ApplicationManager.getApplication().assertWriteAccessAllowed();
279 doCommit(artifactModel);
282 private void doCommit(ArtifactModelImpl artifactModel) {
283 LOG.assertTrue(!myInsideCommit, "Recursive commit");
284 myInsideCommit = true;
285 try {
287 final List<ArtifactImpl> allArtifacts = artifactModel.getOriginalArtifacts();
289 Set<ArtifactImpl> removed = new THashSet<ArtifactImpl>(myModel.myArtifactsList);
290 List<ArtifactImpl> added = new ArrayList<ArtifactImpl>();
291 List<Pair<ArtifactImpl, String>> changed = new ArrayList<Pair<ArtifactImpl, String>>();
293 for (ArtifactImpl artifact : allArtifacts) {
294 final boolean isAdded = !removed.remove(artifact);
295 final ArtifactImpl modifiableCopy = artifactModel.getModifiableCopy(artifact);
296 if (isAdded) {
297 added.add(artifact);
299 else if (modifiableCopy != null && !modifiableCopy.equals(artifact)) {
300 final String oldName = artifact.getName();
301 artifact.copyFrom(modifiableCopy);
302 changed.add(Pair.create(artifact, oldName));
306 myModel.setArtifactsList(allArtifacts);
307 myModificationCount++;
308 final ArtifactListener publisher = myProject.getMessageBus().syncPublisher(TOPIC);
309 for (ArtifactImpl artifact : added) {
310 publisher.artifactAdded(artifact);
312 for (ArtifactImpl artifact : removed) {
313 publisher.artifactRemoved(artifact);
315 for (Pair<ArtifactImpl, String> pair : changed) {
316 publisher.artifactChanged(pair.getFirst(), pair.getSecond());
319 finally {
320 myInsideCommit = false;
324 public Project getProject() {
325 return myProject;
328 @NotNull
329 public Artifact addArtifact(@NotNull final String name, @NotNull final ArtifactType type, final CompositePackagingElement<?> root) {
330 return new WriteAction<Artifact>() {
331 protected void run(final Result<Artifact> result) {
332 final ModifiableArtifactModel model = createModifiableModel();
333 final ModifiableArtifact artifact = model.addArtifact(name, type);
334 if (root != null) {
335 artifact.setRootElement(root);
337 model.commit();
338 result.setResult(artifact);
340 }.execute().getResultObject();
343 @Override
344 public void addElementsToDirectory(@NotNull Artifact artifact, @NotNull String relativePath, @NotNull PackagingElement<?> element) {
345 addElementsToDirectory(artifact, relativePath, Collections.singletonList(element));
348 @Override
349 public void addElementsToDirectory(@NotNull Artifact artifact, @NotNull String relativePath,
350 @NotNull Collection<? extends PackagingElement<?>> elements) {
351 final ModifiableArtifactModel model = createModifiableModel();
352 final CompositePackagingElement<?> root = model.getOrCreateModifiableArtifact(artifact).getRootElement();
353 PackagingElementFactory.getInstance().getOrCreateDirectory(root, relativePath).addOrFindChildren(elements);
354 new WriteAction() {
355 protected void run(final Result result) {
356 model.commit();
358 }.execute();
361 @Override
362 public ModificationTracker getModificationTracker() {
363 return myModificationTracker;
366 private static class ArtifactManagerModel extends ArtifactModelBase {
367 private List<ArtifactImpl> myArtifactsList = new ArrayList<ArtifactImpl>();
368 private Artifact[] mySortedArtifacts;
370 public void setArtifactsList(List<ArtifactImpl> artifactsList) {
371 myArtifactsList = artifactsList;
372 artifactsChanged();
375 @Override
376 protected void artifactsChanged() {
377 super.artifactsChanged();
378 mySortedArtifacts = null;
381 protected List<? extends Artifact> getArtifactsList() {
382 return myArtifactsList;
385 public Artifact[] getSortedArtifacts() {
386 if (mySortedArtifacts == null) {
387 mySortedArtifacts = getArtifacts().clone();
388 Arrays.sort(mySortedArtifacts, ARTIFACT_COMPARATOR);
390 return mySortedArtifacts;