[duplicates] enable duplicates analysis in PyCharm/WebStorm/PhpStorm/RubyMine
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / incremental / fs / FilesDelta.java
1 /*
2  * Copyright 2000-2012 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 package org.jetbrains.jps.incremental.fs;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.util.io.FileUtil;
20 import com.intellij.util.io.IOUtil;
21 import gnu.trove.THashSet;
22 import org.jetbrains.annotations.NotNull;
23 import org.jetbrains.annotations.Nullable;
24 import org.jetbrains.jps.builders.BuildRootDescriptor;
25 import org.jetbrains.jps.builders.BuildRootIndex;
26 import org.jetbrains.jps.builders.BuildTarget;
27 import org.jetbrains.jps.incremental.Utils;
28
29 import java.io.DataInput;
30 import java.io.DataOutput;
31 import java.io.File;
32 import java.io.IOException;
33 import java.util.*;
34 import java.util.concurrent.locks.ReentrantLock;
35
36 /** */
37 public final class FilesDelta {
38   private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.fs.FilesDelta");
39   private final ReentrantLock myDataLock = new ReentrantLock();
40
41   private final Set<String> myDeletedPaths = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY);
42   private final Map<BuildRootDescriptor, Set<File>> myFilesToRecompile = new HashMap<>();
43
44   public void lockData(){
45     myDataLock.lock();
46   }
47
48   public void unlockData(){
49     myDataLock.unlock();
50   }
51
52   public FilesDelta() {
53   }
54
55   FilesDelta(Collection<FilesDelta> deltas) {
56     for (FilesDelta delta : deltas) {
57       addAll(delta);
58     }
59   }
60
61   private void addAll(FilesDelta other) {
62     other.lockData();
63     try {
64       myDeletedPaths.addAll(other.myDeletedPaths);
65       for (Map.Entry<BuildRootDescriptor, Set<File>> entry : other.myFilesToRecompile.entrySet()) {
66         _addToRecompiled(entry.getKey(), entry.getValue());
67       }
68     }
69     finally {
70       other.unlockData();
71     }
72   }
73
74   public void save(DataOutput out) throws IOException {
75     lockData();
76     try {
77       out.writeInt(myDeletedPaths.size());
78       for (String path : myDeletedPaths) {
79         IOUtil.writeString(path, out);
80       }
81       out.writeInt(myFilesToRecompile.size());
82       for (Map.Entry<BuildRootDescriptor, Set<File>> entry : myFilesToRecompile.entrySet()) {
83         IOUtil.writeString(entry.getKey().getRootId(), out);
84         final Set<File> files = entry.getValue();
85         out.writeInt(files.size());
86         for (File file : files) {
87           IOUtil.writeString(FileUtil.toSystemIndependentName(file.getPath()), out);
88         }
89       }
90     }
91     finally {
92       unlockData();
93     }
94   }
95
96   public void load(DataInput in, @NotNull BuildTarget<?> target, BuildRootIndex buildRootIndex) throws IOException {
97     lockData();
98     try {
99       myDeletedPaths.clear();
100       int deletedCount = in.readInt();
101       while (deletedCount-- > 0) {
102         myDeletedPaths.add(IOUtil.readString(in));
103       }
104       myFilesToRecompile.clear();
105       int recompileCount = in.readInt();
106       while (recompileCount-- > 0) {
107         String rootId = IOUtil.readString(in);
108         BuildRootDescriptor descriptor = target.findRootDescriptor(rootId, buildRootIndex);
109         Set<File> files;
110         if (descriptor != null) {
111           files = myFilesToRecompile.get(descriptor);
112           if (files == null) {
113             files = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
114             myFilesToRecompile.put(descriptor, files);
115           }
116         }
117         else {
118           LOG.debug("Cannot find root by " + rootId + ", delta will be skipped");
119           files = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
120         }
121         int filesCount = in.readInt();
122         while (filesCount-- > 0) {
123           final File file = new File(IOUtil.readString(in));
124           if (Utils.IS_TEST_MODE) {
125             LOG.info("Loaded " + file.getPath());
126           }
127           files.add(file);
128         }
129       }
130     }
131     finally {
132       unlockData();
133     }
134   }
135
136   public static void skip(DataInput in) throws IOException {
137     int deletedCount = in.readInt();
138     while (deletedCount-- > 0) {
139       IOUtil.readString(in);
140     }
141     int recompiledCount = in.readInt();
142     while (recompiledCount-- > 0) {
143       IOUtil.readString(in);
144       int filesCount = in.readInt();
145       while (filesCount-- > 0) {
146         IOUtil.readString(in);
147       }
148     }
149   }
150
151   public boolean hasChanges() {
152     lockData();
153     try {
154       if (!myDeletedPaths.isEmpty()) {
155         return true;
156       }
157       if(!myFilesToRecompile.isEmpty()) {
158         for (Set<File> files : myFilesToRecompile.values()) {
159           if (!files.isEmpty()) {
160             return true;
161           }
162         }
163       }
164       return false;
165     }
166     finally {
167       unlockData();
168     }
169   }
170
171
172   public boolean markRecompile(BuildRootDescriptor root, File file) {
173     lockData();
174     try {
175       final boolean added = _addToRecompiled(root, file);
176       if (added) {
177         if (!myDeletedPaths.isEmpty()) { // optimization
178           myDeletedPaths.remove(FileUtil.toCanonicalPath(file.getPath()));
179         }
180       }
181       return added;
182     }
183     finally {
184       unlockData();
185     }
186   }
187
188   public boolean markRecompileIfNotDeleted(BuildRootDescriptor root, File file) {
189     lockData();
190     try {
191       String path = null;
192       final boolean isMarkedDeleted = !myDeletedPaths.isEmpty() && myDeletedPaths.contains(path = FileUtil.toCanonicalPath(file.getPath()));
193       if (!isMarkedDeleted) {
194         if (!file.exists()) {
195           // incorrect paths data recovery, so that the next make should not contain non-existing sources in 'recompile' list
196           if (path == null) {
197             path = FileUtil.toCanonicalPath(file.getPath());
198           }
199           if (Utils.IS_TEST_MODE) {
200             LOG.info("Marking deleted: " + path);
201           }
202           myDeletedPaths.add(path);
203           return false;
204         }
205         _addToRecompiled(root, file);
206         return true;
207       }
208       return false;
209     }
210     finally {
211       unlockData();
212     }
213   }
214
215   private boolean _addToRecompiled(BuildRootDescriptor root, File file) {
216     if (Utils.IS_TEST_MODE) {
217       LOG.info("Marking dirty: " + file.getPath());
218     }
219     return _addToRecompiled(root, Collections.singleton(file));
220   }
221
222   private boolean _addToRecompiled(BuildRootDescriptor root, Collection<File> filesToAdd) {
223     Set<File> files = myFilesToRecompile.get(root);
224     if (files == null) {
225       files = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
226       myFilesToRecompile.put(root, files);
227     }
228     return files.addAll(filesToAdd);
229   }
230
231   public void addDeleted(File file) {
232     final String path = FileUtil.toCanonicalPath(file.getPath());
233     lockData();
234     try {
235       // ensure the file is not marked to recompilation anymore
236       for (Set<File> files : myFilesToRecompile.values()) {
237         files.remove(file);
238       }
239       myDeletedPaths.add(path);
240       if (Utils.IS_TEST_MODE) {
241         LOG.info("Marking deleted: " + path);
242       }
243     }
244     finally {
245       unlockData();
246     }
247   }
248
249   public void clearDeletedPaths() {
250     lockData();
251     try {
252       myDeletedPaths.clear();
253     }
254     finally {
255       unlockData();
256     }
257   }
258
259   public Set<String> getAndClearDeletedPaths() {
260     lockData();
261     try {
262       try {
263         final THashSet<String> _paths = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY);
264         _paths.addAll(myDeletedPaths);
265         return _paths;
266       }
267       finally {
268         myDeletedPaths.clear();
269       }
270     }
271     finally {
272       unlockData();
273     }
274   }
275
276   @NotNull
277   public Map<BuildRootDescriptor, Set<File>> getSourcesToRecompile() {
278     LOG.assertTrue(myDataLock.isHeldByCurrentThread(), "FilesDelta data must be locked by querying thread");
279     return myFilesToRecompile;
280   }
281
282   public boolean isMarkedRecompile(BuildRootDescriptor rd, File file) {
283     lockData();
284     try {
285       final Set<File> files = myFilesToRecompile.get(rd);
286       return files != null && files.contains(file);
287     }
288     finally {
289       unlockData();
290     }
291   }
292
293   @Nullable
294   public Set<File> clearRecompile(BuildRootDescriptor root) {
295     lockData();
296     try {
297       return myFilesToRecompile.remove(root);
298     }
299     finally {
300       unlockData();
301     }
302   }
303 }