ExcludedFileIndex -> FileIndexFacade; use it in PsiClassImplUtil
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / LocalChangeListImpl.java
1
2 package com.intellij.openapi.vcs.changes;
3
4 import com.intellij.lifecycle.PeriodicalTasksCloser;
5 import com.intellij.openapi.diagnostic.Logger;
6 import com.intellij.openapi.project.Project;
7 import com.intellij.openapi.roots.FileIndexFacade;
8 import com.intellij.openapi.util.Comparing;
9 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
10 import com.intellij.openapi.vfs.VirtualFile;
11 import com.intellij.util.containers.OpenTHashSet;
12 import org.jetbrains.annotations.NotNull;
13 import org.jetbrains.annotations.Nullable;
14
15 import java.util.*;
16
17 /**
18  * @author yole
19  */
20 public class LocalChangeListImpl extends LocalChangeList {
21   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangeList");
22
23   private final Project myProject;
24   private Collection<Change> myChanges = new HashSet<Change>();
25   private Collection<Change> myReadChangesCache = null;
26   private String myId;
27   @NotNull private String myName;
28   private String myComment = "";
29
30   private boolean myIsDefault = false;
31   private boolean myIsReadOnly = false;
32   private OpenTHashSet<Change> myChangesBeforeUpdate;
33
34   public static LocalChangeListImpl createEmptyChangeListImpl(Project project, String name) {
35     return new LocalChangeListImpl(project, name);
36   }
37
38   private LocalChangeListImpl(Project project, final String name) {
39     myProject = project;
40     myName = name;
41   }
42
43   private LocalChangeListImpl(LocalChangeListImpl origin) {
44     myId = origin.getId();
45     myName = origin.myName;
46     myProject = origin.myProject;
47   }
48
49   public synchronized Collection<Change> getChanges() {
50     createReadChangesCache();
51     return myReadChangesCache;
52   }
53
54   private void createReadChangesCache() {
55     if (myReadChangesCache == null) {
56       myReadChangesCache = Collections.unmodifiableCollection(new HashSet<Change>(myChanges));
57     }
58   }
59
60   @NotNull
61   @Override
62   public synchronized String getId() {
63     if (myId == null) {
64       myId = UUID.randomUUID().toString();
65     }
66
67     return myId;
68   }
69
70   @NotNull
71   public String getName() {
72     return myName;
73   }
74
75   public void setName(@NotNull final String name) {
76     if (! myName.equals(name)) {
77       myName = name;
78     }
79   }
80
81   public String getComment() {
82     return myComment;
83   }
84
85   // same as for setName()
86   public void setComment(final String comment) {
87     if (! Comparing.equal(comment, myComment)) {
88       myComment = comment != null ? comment : "";
89     }
90   }
91
92   void setNameImpl(@NotNull final String name) {
93     myName = name;
94   }
95
96   void setCommentImpl(final String comment) {
97     myComment = comment;
98   }
99
100   public boolean isDefault() {
101     return myIsDefault;
102   }
103
104   void setDefault(final boolean isDefault) {
105     myIsDefault = isDefault;
106   }
107
108   public boolean isReadOnly() {
109     return myIsReadOnly;
110   }
111
112   public void setReadOnly(final boolean isReadOnly) {
113     myIsReadOnly = isReadOnly;
114   }
115
116   synchronized void addChange(Change change) {
117     myReadChangesCache = null;
118     myChanges.add(change);
119   }
120
121   synchronized Change removeChange(Change change) {
122     for (Change localChange : myChanges) {
123       if (localChange.equals(change)) {
124         myChanges.remove(localChange);
125         myReadChangesCache = null;
126         return localChange;
127       }
128     }
129     return null;
130   }
131
132   synchronized Collection<Change> startProcessingChanges(final Project project, @Nullable final VcsDirtyScope scope) {
133     createReadChangesCache();
134     final Collection<Change> result = new ArrayList<Change>();
135     myChangesBeforeUpdate = new OpenTHashSet<Change>(myChanges);
136     final FileIndexFacade fileIndex = PeriodicalTasksCloser.getInstance().safeGetService(project, FileIndexFacade.class);
137     for (Change oldBoy : myChangesBeforeUpdate) {
138       final ContentRevision before = oldBoy.getBeforeRevision();
139       final ContentRevision after = oldBoy.getAfterRevision();
140       if (scope == null || before != null && scope.belongsTo(before.getFile()) || after != null && scope.belongsTo(after.getFile())
141         || isIgnoredChange(oldBoy, fileIndex)) {
142         result.add(oldBoy);
143         removeChange(oldBoy);
144       }
145     }
146     return result;
147   }
148
149   private static boolean isIgnoredChange(final Change change, final FileIndexFacade fileIndex) {
150     boolean beforeRevIgnored = change.getBeforeRevision() == null || isIgnoredRevision(change.getBeforeRevision(), fileIndex);
151     boolean afterRevIgnored = change.getAfterRevision() == null || isIgnoredRevision(change.getAfterRevision(), fileIndex);
152     return beforeRevIgnored && afterRevIgnored;
153   }
154
155   private static boolean isIgnoredRevision(final ContentRevision revision, final FileIndexFacade fileIndex) {
156     VirtualFile vFile = revision.getFile().getVirtualFile();
157     return vFile != null && fileIndex.isExcludedFile(vFile);
158   }
159
160   synchronized boolean processChange(Change change) {
161     LOG.debug("[process change] for '" + myName + "' isDefault: " + myIsDefault + " change: " +
162               ChangesUtil.getFilePath(change).getPath());
163     if (myIsDefault) {
164       LOG.debug("[process change] adding because default");
165       addChange(change);
166       return true;
167     }
168
169     for (Change oldChange : myChangesBeforeUpdate) {
170       if (Comparing.equal(oldChange, change)) {
171         LOG.debug("[process change] adding bacuae equal to old: " + ChangesUtil.getFilePath(oldChange).getPath());
172         addChange(change);
173         return true;
174       }
175     }
176     LOG.debug("[process change] not found");
177     return false;
178   }
179
180   synchronized boolean doneProcessingChanges(final List<Change> removedChanges, final List<Change> addedChanges) {
181     boolean changesDetected = (myChanges.size() != myChangesBeforeUpdate.size());
182
183     for (Change newChange : myChanges) {
184       Change oldChange = findOldChange(newChange);
185       if (oldChange == null) {
186         addedChanges.add(newChange);
187       }
188     }
189     changesDetected |= (! addedChanges.isEmpty());
190     final List<Change> removed = new ArrayList<Change>(myChangesBeforeUpdate);
191     // since there are SAME objects...
192     removed.removeAll(myChanges);
193     removedChanges.addAll(removed);
194     changesDetected = changesDetected || (! removedChanges.isEmpty());
195
196     myReadChangesCache = null;
197     return changesDetected;
198   }
199
200   @Nullable
201   private Change findOldChange(final Change newChange) {
202     Change oldChange = myChangesBeforeUpdate.get(newChange);
203     if (oldChange != null && sameBeforeRevision(oldChange, newChange) &&
204         newChange.getFileStatus().equals(oldChange.getFileStatus())) {
205       return oldChange;
206     }
207     return null;
208   }
209
210   private static boolean sameBeforeRevision(final Change change1, final Change change2) {
211     final ContentRevision b1 = change1.getBeforeRevision();
212     final ContentRevision b2 = change2.getBeforeRevision();
213     if (b1 != null && b2 != null) {
214       final VcsRevisionNumber rn1 = b1.getRevisionNumber();
215       final VcsRevisionNumber rn2 = b2.getRevisionNumber();
216       final boolean isBinary1 = (b1 instanceof BinaryContentRevision);
217       final boolean isBinary2 = (b2 instanceof BinaryContentRevision);
218       return rn1 != VcsRevisionNumber.NULL && rn2 != VcsRevisionNumber.NULL && rn1.compareTo(rn2) == 0 && isBinary1 == isBinary2;
219     }
220     return b1 == null && b2 == null;
221   }
222
223   public synchronized boolean equals(final Object o) {
224     if (this == o) return true;
225     if (o == null || getClass() != o.getClass()) return false;
226
227     final LocalChangeListImpl list = (LocalChangeListImpl)o;
228
229     if (myIsDefault != list.myIsDefault) return false;
230     if (!myName.equals(list.myName)) return false;
231     if (myIsReadOnly != list.myIsReadOnly) return false;
232
233     return true;
234   }
235
236   public int hashCode() {
237     return myName.hashCode();
238   }
239
240   @Override
241   public String toString() {
242     return myName.trim();
243   }
244
245   public synchronized LocalChangeList copy() {
246     final LocalChangeListImpl copy = new LocalChangeListImpl(this);
247     copy.myComment = myComment;
248     copy.myIsDefault = myIsDefault;
249     copy.myIsReadOnly = myIsReadOnly;
250
251     if (myChanges != null) {
252       copy.myChanges = new HashSet<Change>(myChanges);
253     }
254
255     if (myChangesBeforeUpdate != null) {
256       copy.myChangesBeforeUpdate = new OpenTHashSet<Change>((Collection<Change>)myChangesBeforeUpdate);
257     }
258
259     if (myReadChangesCache != null) {
260       copy.myReadChangesCache = new HashSet<Change>(myReadChangesCache);
261     }
262
263     return copy;
264   }
265
266   @Nullable
267   public ChangeListEditHandler getEditHandler() {
268     return null;
269   }
270
271   public void setId(String id) {
272     myId = id;
273   }
274 }