notnull
[idea/community.git] / platform / lang-impl / src / com / intellij / openapi / projectRoots / impl / ProjectRootContainerImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.intellij.openapi.projectRoots.impl;
18
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.projectRoots.ProjectRootListener;
22 import com.intellij.openapi.projectRoots.ex.ProjectRoot;
23 import com.intellij.openapi.projectRoots.ex.ProjectRootContainer;
24 import com.intellij.openapi.roots.OrderRootType;
25 import com.intellij.openapi.roots.PersistentOrderRootType;
26 import com.intellij.openapi.util.Comparing;
27 import com.intellij.openapi.util.InvalidDataException;
28 import com.intellij.openapi.util.JDOMExternalizable;
29 import com.intellij.openapi.util.WriteExternalException;
30 import com.intellij.openapi.vfs.JarFileSystem;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.openapi.vfs.VirtualFileManager;
33 import com.intellij.util.containers.ContainerUtil;
34 import com.intellij.util.containers.HashMap;
35 import org.jdom.Element;
36 import org.jetbrains.annotations.NotNull;
37
38 import java.util.List;
39 import java.util.Map;
40
41 /**
42  * @author mike
43  */
44 public class ProjectRootContainerImpl implements JDOMExternalizable, ProjectRootContainer {
45   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.ProjectRootContainerImpl");
46   private final Map<OrderRootType, CompositeProjectRoot> myRoots = new HashMap<OrderRootType, CompositeProjectRoot>();
47
48   private Map<OrderRootType, VirtualFile[]> myFiles = new HashMap<OrderRootType, VirtualFile[]>();
49
50   private boolean myInsideChange = false;
51   private final List<ProjectRootListener> myListeners = ContainerUtil.createEmptyCOWList();
52
53   private boolean myNoCopyJars = false;
54
55   public ProjectRootContainerImpl(boolean noCopyJars) {
56     myNoCopyJars = noCopyJars;
57
58     for(OrderRootType rootType: OrderRootType.getAllTypes()) {
59       myRoots.put(rootType, new CompositeProjectRoot());
60       myFiles.put(rootType, VirtualFile.EMPTY_ARRAY);
61     }
62   }
63
64   @NotNull
65   public VirtualFile[] getRootFiles(@NotNull OrderRootType type) {
66     return myFiles.get(type);
67   }
68
69   @NotNull
70   public ProjectRoot[] getRoots(@NotNull OrderRootType type) {
71     return myRoots.get(type).getProjectRoots();
72   }
73
74   public void startChange() {
75     LOG.assertTrue(!myInsideChange);
76
77     myInsideChange = true;
78   }
79
80   public void finishChange() {
81     LOG.assertTrue(myInsideChange);
82     HashMap<OrderRootType, VirtualFile[]> oldRoots = new HashMap<OrderRootType, VirtualFile[]>(myFiles);
83
84     for (OrderRootType orderRootType: OrderRootType.getAllTypes()) {
85       final VirtualFile[] roots = myRoots.get(orderRootType).getVirtualFiles();
86       final boolean same = Comparing.equal(roots, oldRoots.get(orderRootType));
87
88       myFiles.put(orderRootType, myRoots.get(orderRootType).getVirtualFiles());
89
90       if (!same) {
91         fireRootsChanged();
92       }
93     }
94
95     myInsideChange = false;
96   }
97
98   public void addProjectRootContainerListener(ProjectRootListener listener) {
99     myListeners.add(listener);
100   }
101
102   public void removeProjectRootContainerListener(ProjectRootListener listener) {
103     myListeners.remove(listener);
104   }
105
106   private void fireRootsChanged() {
107     /*
108     ApplicationManager.getApplication().runReadAction(new Runnable() {
109       public void run() {
110         LOG.info("roots changed: type='" + type + "'\n    oldRoots='" + Arrays.asList(oldRoots) + "'\n    newRoots='" + Arrays.asList(newRoots) + "' ");
111       }
112     });
113     */
114     for (final ProjectRootListener listener : myListeners) {
115       listener.rootsChanged();
116     }
117   }
118
119
120   public void removeRoot(@NotNull ProjectRoot root, @NotNull OrderRootType type) {
121     LOG.assertTrue(myInsideChange);
122     myRoots.get(type).remove(root);
123   }
124
125   @NotNull
126   public ProjectRoot addRoot(@NotNull VirtualFile virtualFile, @NotNull OrderRootType type) {
127     LOG.assertTrue(myInsideChange);
128     return myRoots.get(type).add(virtualFile);
129   }
130
131   public void addRoot(@NotNull ProjectRoot root, @NotNull OrderRootType type) {
132     LOG.assertTrue(myInsideChange);
133     myRoots.get(type).add(root);
134   }
135
136   public void removeAllRoots(@NotNull OrderRootType type ) {
137     LOG.assertTrue(myInsideChange);
138     myRoots.get(type).clear();
139   }
140
141   public void removeRoot(@NotNull VirtualFile root, @NotNull OrderRootType type) {
142     LOG.assertTrue(myInsideChange);
143     myRoots.get(type).remove(root);
144   }
145
146   public void removeAllRoots() {
147     LOG.assertTrue(myInsideChange);
148     for (CompositeProjectRoot myRoot : myRoots.values()) {
149       myRoot.clear();
150     }
151   }
152
153   public void update() {
154     LOG.assertTrue(myInsideChange);
155     for (CompositeProjectRoot myRoot : myRoots.values()) {
156       myRoot.update();
157     }
158   }
159
160   public void readExternal(Element element) throws InvalidDataException {
161     for (PersistentOrderRootType type : OrderRootType.getAllPersistentTypes()) {
162       read(element, type);
163     }
164
165     ApplicationManager.getApplication().runReadAction(new Runnable() {
166       public void run() {
167         myFiles = new HashMap<OrderRootType, VirtualFile[]>();
168         for(OrderRootType rootType: myRoots.keySet()) {
169           CompositeProjectRoot root = myRoots.get(rootType);
170           if (myNoCopyJars){
171             setNoCopyJars(root);
172           }
173           myFiles.put(rootType, root.getVirtualFiles());
174         }
175       }
176     });
177
178     for (OrderRootType type : OrderRootType.getAllTypes()) {
179       final VirtualFile[] newRoots = getRootFiles(type);
180       final VirtualFile[] oldRoots = VirtualFile.EMPTY_ARRAY;
181       if (!Comparing.equal(oldRoots, newRoots)) {
182         fireRootsChanged();
183       }
184     }
185   }
186
187   public void writeExternal(Element element) throws WriteExternalException {
188     List<PersistentOrderRootType> allTypes = OrderRootType.getSortedRootTypes();
189     for (PersistentOrderRootType type : allTypes) {
190       write(element, type);
191     }
192   }
193
194   private static void setNoCopyJars(ProjectRoot root){
195     if (root instanceof SimpleProjectRoot){
196       String url = ((SimpleProjectRoot)root).getUrl();
197       if (JarFileSystem.PROTOCOL.equals(VirtualFileManager.extractProtocol(url))){
198         String path = VirtualFileManager.extractPath(url);
199         JarFileSystem.getInstance().setNoCopyJarForPath(path);
200       }
201     }
202     else if (root instanceof CompositeProjectRoot){
203       ProjectRoot[] roots = ((CompositeProjectRoot)root).getProjectRoots();
204       for (ProjectRoot root1 : roots) {
205         setNoCopyJars(root1);
206       }
207     }
208   }
209
210   private void read(Element element, PersistentOrderRootType type) throws InvalidDataException {
211     Element child = element.getChild(type.getSdkRootName());
212     if (child == null){
213       myRoots.put(type, new CompositeProjectRoot());
214       return;
215     }
216
217     List children = child.getChildren();
218     LOG.assertTrue(children.size() == 1);
219     myRoots.put(type, (CompositeProjectRoot)ProjectRootUtil.read((Element)children.get(0)));
220   }
221
222   private void write(Element roots, PersistentOrderRootType type) throws WriteExternalException {
223     Element e = new Element(type.getSdkRootName());
224     roots.addContent(e);
225     final Element root = ProjectRootUtil.write(myRoots.get(type));
226     if (root != null) {
227       e.addContent(root);
228     }
229   }
230
231
232   @SuppressWarnings({"HardCodedStringLiteral"})
233   void readOldVersion(Element child) {
234     for (final Object o : child.getChildren("root")) {
235       Element root = (Element)o;
236       String url = root.getAttributeValue("file");
237       SimpleProjectRoot projectRoot = new SimpleProjectRoot(url);
238       String type = root.getChild("property").getAttributeValue("value");
239
240       for(PersistentOrderRootType rootType: OrderRootType.getAllPersistentTypes()) {
241         if (type.equals(rootType.getOldSdkRootName())) {
242           addRoot(projectRoot, rootType);
243           break;
244         }
245       }
246     }
247
248     myFiles = new HashMap<OrderRootType, VirtualFile[]>();
249     for(OrderRootType rootType: myRoots.keySet()) {
250       myFiles.put(rootType, myRoots.get(rootType).getVirtualFiles());
251     }
252     for (OrderRootType type : OrderRootType.getAllTypes()) {
253       final VirtualFile[] oldRoots = VirtualFile.EMPTY_ARRAY;
254       final VirtualFile[] newRoots = getRootFiles(type);
255       if (!Comparing.equal(oldRoots, newRoots)) {
256         fireRootsChanged();
257       }
258     }
259   }
260
261 }