VCS: fix group by structure for Changes | Local
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / RemoteRevisionsNumbersCache.java
blob8984adc6890715b2a4ce28796842aecf638f690b
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.openapi.vcs.changes;
18 import com.intellij.lifecycle.AtomicSectionsAware;
19 import com.intellij.lifecycle.ControlledAlarmFactory;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.progress.ProgressIndicator;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.Computable;
24 import com.intellij.openapi.util.Getter;
25 import com.intellij.openapi.util.Pair;
26 import com.intellij.openapi.vcs.*;
27 import com.intellij.openapi.vcs.diff.DiffProvider;
28 import com.intellij.openapi.vcs.diff.ItemLatestState;
29 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
30 import com.intellij.openapi.vfs.LocalFileSystem;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.util.Consumer;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
36 import java.io.File;
37 import java.util.*;
39 /**
40 * for vcses where it is reasonable to ask revision of each item separately
42 public class RemoteRevisionsNumbersCache implements ChangesOnServerTracker {
43 public static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.RemoteRevisionsNumbersCache");
45 // every hour (time unit to check for server commits)
46 // default, actual in settings
47 private static final long ourRottenPeriod = 3600 * 1000;
48 private final Map<String, Pair<VcsRoot, VcsRevisionNumber>> myData;
49 private final Map<VcsRoot, LazyRefreshingSelfQueue<String>> myRefreshingQueues;
50 private final Map<String, VcsRevisionNumber> myLatestRevisionsMap;
51 private final ProjectLevelVcsManager myVcsManager;
52 private final LocalFileSystem myLfs;
53 private boolean mySomethingChanged;
55 private final Object myLock;
57 public static final VcsRevisionNumber NOT_LOADED = new VcsRevisionNumber() {
58 public String asString() {
59 return "NOT_LOADED";
62 public int compareTo(VcsRevisionNumber o) {
63 if (o == this) return 0;
64 return -1;
67 public static final VcsRevisionNumber UNKNOWN = new VcsRevisionNumber() {
68 public String asString() {
69 return "UNKNOWN";
72 public int compareTo(VcsRevisionNumber o) {
73 if (o == this) return 0;
74 return -1;
77 private final VcsConfiguration myVcsConfiguration;
79 RemoteRevisionsNumbersCache(final Project project) {
80 myLock = new Object();
81 myData = new HashMap<String, Pair<VcsRoot, VcsRevisionNumber>>();
82 myRefreshingQueues = Collections.synchronizedMap(new HashMap<VcsRoot, LazyRefreshingSelfQueue<String>>());
83 myLatestRevisionsMap = new HashMap<String, VcsRevisionNumber>();
84 myLfs = LocalFileSystem.getInstance();
85 myVcsManager = ProjectLevelVcsManager.getInstance(project);
86 myVcsConfiguration = VcsConfiguration.getInstance(project);
89 public boolean updateStep(final AtomicSectionsAware atomicSectionsAware) {
90 final List<LazyRefreshingSelfQueue<String>> list = new ArrayList<LazyRefreshingSelfQueue<String>>();
91 mySomethingChanged = false;
92 synchronized (myLock) {
93 final Set<VcsRoot> keys = myRefreshingQueues.keySet();
94 for (VcsRoot key : keys) {
95 final boolean backgroundOperationsAllowed = key.vcs.isVcsBackgroundOperationsAllowed(key.path);
96 LOG.debug("backgroundOperationsAllowed: " + backgroundOperationsAllowed + " for " + key.vcs.getName() + ", " + key.path.getPath());
97 if (backgroundOperationsAllowed) {
98 list.add(myRefreshingQueues.get(key));
102 LOG.debug("queues refresh started, queues: " + list.size());
103 final ProgressIndicator pi = ControlledAlarmFactory.createProgressIndicator(atomicSectionsAware);
104 for (LazyRefreshingSelfQueue<String> queue : list) {
105 atomicSectionsAware.checkShouldExit();
106 queue.updateStep(pi);
108 return mySomethingChanged;
111 public void directoryMappingChanged() {
112 synchronized (myLock) {
113 final HashSet<String> keys = new HashSet<String>(myData.keySet());
114 for (String key : keys) {
115 final Pair<VcsRoot, VcsRevisionNumber> value = myData.get(key);
116 final VcsRoot storedVcsRoot = value.getFirst();
117 final VirtualFile vf = myLfs.refreshAndFindFileByIoFile(new File(key));
118 final AbstractVcs newVcs = (vf == null) ? null : myVcsManager.getVcsFor(vf);
120 if (newVcs == null) {
121 myData.remove(key);
122 getQueue(storedVcsRoot).forceRemove(key);
123 } else {
124 final VirtualFile newRoot = myVcsManager.getVcsRootFor(vf);
125 final VcsRoot newVcsRoot = new VcsRoot(newVcs, newRoot);
126 if (! storedVcsRoot.equals(newVcsRoot)) {
127 switchVcs(storedVcsRoot, newVcsRoot, key);
134 private void switchVcs(final VcsRoot oldVcsRoot, final VcsRoot newVcsRoot, final String key) {
135 synchronized (myLock) {
136 final LazyRefreshingSelfQueue<String> oldQueue = getQueue(oldVcsRoot);
137 final LazyRefreshingSelfQueue<String> newQueue = getQueue(newVcsRoot);
138 myData.put(key, new Pair<VcsRoot, VcsRevisionNumber>(newVcsRoot, NOT_LOADED));
139 oldQueue.forceRemove(key);
140 newQueue.addRequest(key);
144 public void plus(final Pair<String, AbstractVcs> pair) {
145 // does not support
146 if (pair.getSecond().getDiffProvider() == null) return;
148 final String key = pair.getFirst();
149 final AbstractVcs newVcs = pair.getSecond();
151 final VirtualFile root = getRootForPath(key);
152 if (root == null) return;
154 final VcsRoot vcsRoot = new VcsRoot(newVcs, root);
156 synchronized (myLock) {
157 final Pair<VcsRoot, VcsRevisionNumber> value = myData.get(key);
158 if (value == null) {
159 final LazyRefreshingSelfQueue<String> queue = getQueue(vcsRoot);
160 myData.put(key, new Pair<VcsRoot, VcsRevisionNumber>(vcsRoot, NOT_LOADED));
161 queue.addRequest(key);
162 } else if (! value.getFirst().equals(vcsRoot)) {
163 switchVcs(value.getFirst(), vcsRoot, key);
168 public void invalidate(final Collection<String> paths) {
169 synchronized (myLock) {
170 for (String path : paths) {
171 final Pair<VcsRoot, VcsRevisionNumber> pair = myData.remove(path);
172 if (pair != null) {
173 // vcs [root] seems to not change
174 final VcsRoot vcsRoot = pair.getFirst();
175 final LazyRefreshingSelfQueue<String> queue = getQueue(vcsRoot);
176 queue.forceRemove(path);
177 queue.addRequest(path);
178 myData.put(path, new Pair<VcsRoot, VcsRevisionNumber>(vcsRoot, NOT_LOADED));
184 @Nullable
185 private VirtualFile getRootForPath(final String s) {
186 return myVcsManager.getVcsRootFor(new FilePathImpl(new File(s), false));
189 public void minus(Pair<String, AbstractVcs> pair) {
190 // does not support
191 if (pair.getSecond().getDiffProvider() == null) return;
192 final VirtualFile root = getRootForPath(pair.getFirst());
193 if (root == null) return;
195 final LazyRefreshingSelfQueue<String> queue;
196 final String key = pair.getFirst();
197 synchronized (myLock) {
198 queue = getQueue(new VcsRoot(pair.getSecond(), root));
199 myData.remove(key);
201 queue.forceRemove(key);
204 // +-
205 @NotNull
206 private LazyRefreshingSelfQueue<String> getQueue(final VcsRoot vcsRoot) {
207 synchronized (myLock) {
208 LazyRefreshingSelfQueue<String> queue = myRefreshingQueues.get(vcsRoot);
209 if (queue != null) return queue;
211 queue = new LazyRefreshingSelfQueue<String>(new Getter<Long>() {
212 public Long get() {
213 return myVcsConfiguration.CHANGED_ON_SERVER_INTERVAL > 0 ? myVcsConfiguration.CHANGED_ON_SERVER_INTERVAL * 60000 : ourRottenPeriod;
215 }, new MyShouldUpdateChecker(vcsRoot), new MyUpdater(vcsRoot));
216 myRefreshingQueues.put(vcsRoot, queue);
217 return queue;
221 private class MyUpdater implements Consumer<String> {
222 private final VcsRoot myVcsRoot;
224 public MyUpdater(final VcsRoot vcsRoot) {
225 myVcsRoot = vcsRoot;
228 public void consume(String s) {
229 LOG.debug("update for: " + s);
230 //todo check canceled - check VCS's ready for asynchronous queries
231 final VirtualFile vf = myLfs.refreshAndFindFileByIoFile(new File(s));
232 final ItemLatestState state;
233 final DiffProvider diffProvider = myVcsRoot.vcs.getDiffProvider();
234 if (vf == null) {
235 // doesnt matter if directory or not
236 state = diffProvider.getLastRevision(FilePathImpl.createForDeletedFile(new File(s), false));
237 } else {
238 state = diffProvider.getLastRevision(vf);
240 final VcsRevisionNumber newNumber = (state == null) || state.isDefaultHead() ? UNKNOWN : state.getNumber();
242 final Pair<VcsRoot, VcsRevisionNumber> oldPair;
243 synchronized (myLock) {
244 oldPair = myData.get(s);
245 myData.put(s, new Pair<VcsRoot, VcsRevisionNumber>(myVcsRoot, newNumber));
248 if ((oldPair == null) || (oldPair != null) && (oldPair.getSecond().compareTo(newNumber) != 0)) {
249 LOG.debug("refresh triggered by " + s);
250 mySomethingChanged = true;
255 private class MyShouldUpdateChecker implements Computable<Boolean> {
256 private final VcsRoot myVcsRoot;
258 public MyShouldUpdateChecker(final VcsRoot vcsRoot) {
259 myVcsRoot = vcsRoot;
262 public Boolean compute() {
263 final AbstractVcs vcs = myVcsRoot.vcs;
264 // won't be called in parallel for same vcs -> just synchronized map is ok
265 final String vcsName = vcs.getName();
266 LOG.debug("should update for: " + vcsName + " root: " + myVcsRoot.path.getPath());
267 final VcsRevisionNumber latestNew = vcs.getDiffProvider().getLatestCommittedRevision(myVcsRoot.path);
269 final VcsRevisionNumber latestKnown = myLatestRevisionsMap.get(vcsName);
270 // not known
271 if (latestNew == null) return true;
272 if ((latestKnown == null) || (latestNew.compareTo(latestKnown) != 0)) {
273 myLatestRevisionsMap.put(vcsName, latestNew);
274 return true;
276 return false;
280 private VcsRevisionNumber getNumber(final String path) {
281 synchronized (myLock) {
282 final Pair<VcsRoot, VcsRevisionNumber> pair = myData.get(path);
283 return pair == null ? NOT_LOADED : pair.getSecond();
287 public boolean isUpToDate(final Change change) {
288 if (change.getBeforeRevision() != null && change.getAfterRevision() != null && (! change.isMoved()) && (! change.isRenamed())) {
289 return getRevisionState(change.getBeforeRevision());
291 return getRevisionState(change.getBeforeRevision()) && getRevisionState(change.getAfterRevision());
294 private boolean getRevisionState(final ContentRevision revision) {
295 if (revision != null) {
296 final VcsRevisionNumber local = revision.getRevisionNumber();
297 final String path = revision.getFile().getIOFile().getAbsolutePath();
298 final VcsRevisionNumber remote = getNumber(path);
299 if ((NOT_LOADED == remote) || (UNKNOWN == remote)) {
300 return true;
302 return local.compareTo(remote) == 0;
304 return true;