artifacts pointers fixed (IDEA-27203 & IDEA-27189)
[fedora-idea.git] / java / compiler / impl / src / com / intellij / packaging / impl / artifacts / ArtifactManagerImpl.java
blob342c17a815e4cc40685dde9d4fafbda2353433dd
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;
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) {
69 myProject = project;
70 myModel = new ArtifactManagerModel();
71 myResolvingContext = new DefaultPackagingElementResolvingContext(myProject);
72 ((ArtifactPointerManagerImpl)ArtifactPointerManager.getInstance(project)).setArtifactManager(this);
75 @NotNull
76 public Artifact[] getArtifacts() {
77 return myModel.getArtifacts();
80 public Artifact findArtifact(@NotNull String name) {
81 return myModel.findArtifact(name);
84 @NotNull
85 public Artifact getArtifactByOriginal(@NotNull Artifact artifact) {
86 return myModel.getArtifactByOriginal(artifact);
89 @NotNull
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);
120 return state;
123 @Nullable
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);
131 return state;
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();
138 if (bean != null) {
139 XmlSerializer.serializeInto(bean, element, new SkipDefaultValuesSerializationFilters());
141 if (packagingElement instanceof CompositePackagingElement) {
142 for (PackagingElement<?> child : ((CompositePackagingElement<?>)packagingElement).getChildren()) {
143 element.addContent(serializePackagingElement(child));
146 return element;
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();
154 if (state != null) {
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());
171 if (type == null) {
172 LOG.info("Unknown artifact type: " + state.getArtifactType());
173 continue;
176 final String artifactName = state.getName();
177 final CompositePackagingElement<?> rootElement;
178 if (element != null) {
179 rootElement = (CompositePackagingElement<?>)deserializeElement(element);
181 else {
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);
196 if (myLoaded) {
197 final ArtifactModelImpl model = new ArtifactModelImpl(this, artifacts);
198 doCommit(model);
200 else {
201 myModel.setArtifactsList(artifacts);
202 myLoaded = true;
206 private static <S> void deserializeProperties(ArtifactProperties<S> artifactProperties, ArtifactPropertiesState propertiesState) {
207 final Element options = propertiesState.getOptions();
208 if (artifactProperties == null || options == null) {
209 return;
211 final S state = artifactProperties.getState();
212 if (state != null) {
213 XmlSerializer.deserializeInto(state, options);
214 artifactProperties.loadState(state);
218 public void disposeComponent() {
219 LocalFileSystem.getInstance().removeWatchedRoots(myWatchedOutputs.values());
222 @NotNull
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)) {
240 toAdd.add(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() {
265 @Override
266 public Artifact[] getSortedArtifacts() {
267 return myModel.getSortedArtifacts();
270 @Override
271 public ModifiableArtifactModel createModifiableModel() {
272 return new ArtifactModelImpl(this, getArtifactsList());
275 @Override
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;
293 try {
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);
304 if (isAdded) {
305 added.add(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());
328 finally {
329 myInsideCommit = false;
331 updateWatchedRoots();
334 public Project getProject() {
335 return myProject;
338 @NotNull
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);
344 if (root != null) {
345 artifact.setRootElement(root);
347 model.commit();
348 result.setResult(artifact);
350 }.execute().getResultObject();
353 @Override
354 public void addElementsToDirectory(@NotNull Artifact artifact, @NotNull String relativePath, @NotNull PackagingElement<?> element) {
355 addElementsToDirectory(artifact, relativePath, Collections.singletonList(element));
358 @Override
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);
364 new WriteAction() {
365 protected void run(final Result result) {
366 model.commit();
368 }.execute();
371 @Override
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;
382 artifactsChanged();
385 @Override
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;