VCS: asynch load of committed changes when do "Browse changes"
[fedora-idea.git] / plugins / cvs / cvs-plugin / src / com / intellij / cvsSupport2 / changeBrowser / CvsCommittedChangesProvider.java
blob0faf93794af4a4b926bd3154c772df3785e95b93
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.cvsSupport2.changeBrowser;
18 import com.intellij.CvsBundle;
19 import com.intellij.cvsSupport2.CvsUtil;
20 import com.intellij.cvsSupport2.application.CvsEntriesManager;
21 import com.intellij.cvsSupport2.connections.CvsEnvironment;
22 import com.intellij.cvsSupport2.cvsExecution.CvsOperationExecutor;
23 import com.intellij.cvsSupport2.cvsExecution.CvsOperationExecutorCallback;
24 import com.intellij.cvsSupport2.cvshandlers.CommandCvsHandler;
25 import com.intellij.cvsSupport2.history.CvsRevisionNumber;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.cvsIntegration.CvsResult;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.progress.ProcessCanceledException;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.Comparing;
32 import com.intellij.openapi.util.Computable;
33 import com.intellij.openapi.vcs.*;
34 import com.intellij.openapi.vcs.changes.ChangesUtil;
35 import com.intellij.openapi.vcs.changes.committed.*;
36 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
37 import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
38 import com.intellij.openapi.vcs.versionBrowser.ChangesBrowserSettingsEditor;
39 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.util.AsynchConsumer;
42 import com.intellij.util.Consumer;
43 import org.jetbrains.annotations.NonNls;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46 import org.netbeans.lib.cvsclient.admin.Entry;
48 import java.io.DataInput;
49 import java.io.DataOutput;
50 import java.io.IOException;
51 import java.util.*;
53 /**
54 * @author yole
56 public class CvsCommittedChangesProvider implements CachingCommittedChangesProvider<CvsChangeList, ChangeBrowserSettings> {
57 private static final Logger LOG = Logger.getInstance("#com.intellij.cvsSupport2.changeBrowser.CvsCommittedChangesProvider");
59 private final Project myProject;
60 private final MyZipper myZipper;
62 @NonNls private static final String INVALID_OPTION_S = "invalid option -- S";
64 public CvsCommittedChangesProvider(Project project) {
65 myProject = project;
66 myZipper = new MyZipper();
69 public ChangeBrowserSettings createDefaultSettings() {
70 return new ChangeBrowserSettings();
73 public ChangesBrowserSettingsEditor<ChangeBrowserSettings> createFilterUI(final boolean showDateFilter) {
74 return new CvsVersionFilterComponent(showDateFilter);
77 public VcsCommittedListsZipper getZipper() {
78 return myZipper;
81 private class MyZipper extends VcsCommittedListsZipperAdapter {
82 private MyZipper() {
83 super(new GroupCreator() {
84 public Object createKey(final RepositoryLocation location) {
85 final CvsRepositoryLocation cvsLocation = (CvsRepositoryLocation) location;
86 return cvsLocation.getEnvironment().getRepository();
89 public RepositoryLocationGroup createGroup(final Object key, final Collection<RepositoryLocation> locations) {
90 final RepositoryLocationGroup group = new RepositoryLocationGroup((String) key);
91 for (RepositoryLocation location : locations) {
92 group.add(location);
94 return group;
96 });
99 @Override
100 public long getNumber(final CommittedChangeList list) {
101 return list.getCommitDate().getTime();
105 @Nullable
106 public CvsRepositoryLocation getLocationFor(final FilePath root) {
107 if (!CvsUtil.fileIsUnderCvs(root.getIOFile())) {
108 return null;
110 final VirtualFile rootDir = root.isDirectory() ? root.getVirtualFile() : root.getVirtualFileParent();
111 final String module = CvsUtil.getModuleName(root);
112 final CvsEnvironment connectionSettings = CvsEntriesManager.getInstance().getCvsConnectionSettingsFor(rootDir);
113 return new CvsRepositoryLocation(root.getVirtualFile(), connectionSettings, module);
116 public RepositoryLocation getLocationFor(final FilePath root, final String repositoryPath) {
117 return getLocationFor(root);
120 public ChangeListColumn[] getColumns() {
121 return new ChangeListColumn[] { ChangeListColumn.DATE, ChangeListColumn.NAME, ChangeListColumn.DESCRIPTION, BRANCH_COLUMN };
124 @Nullable
125 public VcsCommittedViewAuxiliary createActions(final DecoratorManager manager, final RepositoryLocation location) {
126 return null;
129 public int getUnlimitedCountValue() {
130 return 0;
133 public List<CvsChangeList> getCommittedChanges(ChangeBrowserSettings settings, RepositoryLocation location, final int maxCount) throws VcsException {
134 CvsRepositoryLocation cvsLocation = (CvsRepositoryLocation) location;
135 return loadCommittedChanges(settings, cvsLocation.getModuleName(), cvsLocation.getEnvironment(), cvsLocation.getRootFile());
138 public void loadCommittedChanges(ChangeBrowserSettings settings,
139 RepositoryLocation location,
140 int maxCount,
141 final AsynchConsumer<CommittedChangeList> consumer)
142 throws VcsException {
143 try {
144 CvsRepositoryLocation cvsLocation = (CvsRepositoryLocation) location;
145 final String module = cvsLocation.getModuleName();
146 final CvsEnvironment connectionSettings = cvsLocation.getEnvironment();
147 if (connectionSettings.isOffline()) {
148 return;
150 final CvsChangeListsBuilder builder = new CvsChangeListsBuilder(module, connectionSettings, myProject, cvsLocation.getRootFile());
151 Date dateTo = settings.getDateBeforeFilter();
152 Date dateFrom = settings.getDateAfterFilter();
153 if (dateFrom == null) {
154 final Calendar calendar = Calendar.getInstance();
155 calendar.set(1970, 2, 2);
156 dateFrom = calendar.getTime();
158 final ChangeBrowserSettings.Filter filter = settings.createFilter();
159 final Set<Date> controlSet = new HashSet<Date>();
160 final CvsResult executionResult = runRLogOperation(connectionSettings, module, dateFrom, dateTo, new Consumer<LogInformationWrapper>() {
161 public void consume(LogInformationWrapper wrapper) {
162 final RevisionWrapper revisionWrapper = builder.revisionWrapperFromLog(wrapper);
163 final CvsChangeList changeList = builder.addRevision(revisionWrapper);
164 if (controlSet.contains(changeList.getCommitDate())) return;
165 controlSet.add(changeList.getCommitDate());
166 if (filter.accepts(changeList)) {
167 consumer.consume(changeList);
172 if (executionResult.isCanceled()) {
173 throw new ProcessCanceledException();
175 else if (!executionResult.hasNoErrors()) {
176 throw executionResult.composeError();
179 finally {
180 consumer.finished();
184 private List<CvsChangeList> loadCommittedChanges(final ChangeBrowserSettings settings,
185 final String module,
186 final CvsEnvironment connectionSettings,
187 final VirtualFile rootFile) throws VcsException {
188 if (connectionSettings.isOffline()) {
189 return Collections.emptyList();
191 final CvsChangeListsBuilder builder = new CvsChangeListsBuilder(module, connectionSettings, myProject, rootFile);
192 Date dateTo = settings.getDateBeforeFilter();
193 Date dateFrom = settings.getDateAfterFilter();
194 if (dateFrom == null) {
195 final Calendar calendar = Calendar.getInstance();
196 calendar.set(1970, 2, 2);
197 dateFrom = calendar.getTime();
199 final List<LogInformationWrapper> log = new ArrayList<LogInformationWrapper>();
200 final CvsResult executionResult = runRLogOperation(connectionSettings, module, dateFrom, dateTo, log);
202 if (executionResult.isCanceled()) {
203 throw new ProcessCanceledException();
205 else if (!executionResult.hasNoErrors()) {
206 throw executionResult.composeError();
208 else {
209 builder.addLogs(log);
210 final List<CvsChangeList> versions = builder.getVersions();
211 settings.filterChanges(versions);
212 return versions;
216 private CvsResult runRLogOperation(final CvsEnvironment settings,
217 final String module,
218 final Date dateFrom,
219 final Date dateTo,
220 final Consumer<LogInformationWrapper> consumer) {
221 final CvsResult executionResult = runRLogOperationImpl(settings, module, dateFrom, dateTo, consumer);
223 for (VcsException error : executionResult.getErrors()) {
224 for (String message : error.getMessages()) {
225 if (message.indexOf(INVALID_OPTION_S) >= 0) {
226 LoadHistoryOperation.doesNotSuppressEmptyHeaders(settings);
227 // try only once
228 return runRLogOperationImpl(settings, module, dateFrom, dateTo, consumer);
232 return executionResult;
235 private CvsResult runRLogOperationImpl(final CvsEnvironment settings,
236 final String module,
237 final Date dateFrom,
238 final Date dateTo,
239 final Consumer<LogInformationWrapper> consumer) {
240 LoadHistoryOperation operation = new LoadHistoryOperation(settings, module, dateFrom, dateTo, null) {
241 @Override
242 protected void wrapperAdded(final LogInformationWrapper wrapper) {
243 consumer.consume(wrapper);
247 CvsOperationExecutor executor = new CvsOperationExecutor(myProject);
248 executor.performActionSync(new CommandCvsHandler(CvsBundle.message("browse.changes.load.history.progress.title"), operation),
249 CvsOperationExecutorCallback.EMPTY);
251 return executor.getResult();
254 private CvsResult runRLogOperation(final CvsEnvironment settings,
255 final String module,
256 final Date dateFrom,
257 final Date dateTo,
258 final List<LogInformationWrapper> log) {
259 LoadHistoryOperation operation = new LoadHistoryOperation(settings, module, dateFrom, dateTo, log);
261 CvsOperationExecutor executor = new CvsOperationExecutor(myProject);
262 executor.performActionSync(new CommandCvsHandler(CvsBundle.message("browse.changes.load.history.progress.title"), operation),
263 CvsOperationExecutorCallback.EMPTY);
265 final CvsResult executionResult = executor.getResult();
267 for (VcsException error : executionResult.getErrors()) {
268 for (String message : error.getMessages()) {
269 if (message.indexOf(INVALID_OPTION_S) >= 0) {
270 LoadHistoryOperation.doesNotSuppressEmptyHeaders(settings);
271 log.clear();
272 return runRLogOperation(settings, module, dateFrom, dateTo, log);
277 return executionResult;
280 public int getFormatVersion() {
281 return 2;
284 public void writeChangeList(final DataOutput stream, final CvsChangeList list) throws IOException {
285 list.writeToStream(stream);
288 public CvsChangeList readChangeList(final RepositoryLocation location, final DataInput stream) throws IOException {
289 CvsRepositoryLocation cvsLocation = (CvsRepositoryLocation) location;
290 return new CvsChangeList(myProject, cvsLocation.getEnvironment(), cvsLocation.getRootFile(), stream);
293 public boolean isMaxCountSupported() {
294 return false;
297 public Collection<FilePath> getIncomingFiles(final RepositoryLocation location) {
298 return null;
301 public boolean refreshCacheByNumber() {
302 return false;
305 public String getChangelistTitle() {
306 return null;
309 public boolean isChangeLocallyAvailable(final FilePath filePath, @Nullable VcsRevisionNumber localRevision, VcsRevisionNumber changeRevision,
310 final CvsChangeList changeList) {
311 if (localRevision instanceof CvsRevisionNumber && changeRevision instanceof CvsRevisionNumber) {
312 final CvsRevisionNumber cvsLocalRevision = (CvsRevisionNumber)localRevision;
313 final CvsRevisionNumber cvsChangeRevision = (CvsRevisionNumber)changeRevision;
314 final int[] localSubRevisions = cvsLocalRevision.getSubRevisions();
315 final int[] changeSubRevisions = cvsChangeRevision.getSubRevisions();
316 if (localSubRevisions != null && changeSubRevisions != null) {
317 if (localSubRevisions.length != changeSubRevisions.length) {
318 // local is trunk, change is branch / vice versa
319 return true;
321 for(int i=2; i<localSubRevisions.length; i += 2) {
322 if (localSubRevisions [i] != changeSubRevisions [i]) {
323 // local is one branch, change is a different branch
324 return true;
330 return isDifferentBranch(filePath, changeList) || (localRevision != null && localRevision.compareTo(changeRevision) >= 0);
333 private static boolean isDifferentBranch(final FilePath filePath, final CvsChangeList changeList) {
334 String localTag;
335 final CvsEntriesManager cvsEntriesManager = CvsEntriesManager.getInstance();
336 final VirtualFile parent = filePath.getVirtualFileParent();
337 if (parent != null) {
338 Entry entry = cvsEntriesManager.getEntryFor(parent, filePath.getName());
339 if (entry != null) {
340 localTag = entry.getStickyTag();
342 else {
343 localTag = getDirectoryTag(parent);
346 else {
347 final VirtualFile validParent = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
348 @Nullable
349 public VirtualFile compute() {
350 return ChangesUtil.findValidParent(filePath);
353 if (validParent == null) return false;
354 localTag = getDirectoryTag(validParent);
356 final String remoteTag = changeList.getBranch();
357 if (!Comparing.equal(localTag, remoteTag)) {
358 LOG.info(filePath + ": local tag " + localTag + ", remote tag " + remoteTag);
359 return true;
361 return false;
364 @Nullable
365 private static String getDirectoryTag(@NotNull final VirtualFile parent) {
366 final String dirTag = CvsEntriesManager.getInstance().getCvsInfoFor(parent).getStickyTag();
367 if (dirTag == null || !CvsUtil.isNonDateTag(dirTag)) return null;
368 return dirTag.substring(1);
371 public boolean refreshIncomingWithCommitted() {
372 return true;
375 private final ChangeListColumn<CvsChangeList> BRANCH_COLUMN = new ChangeListColumn<CvsChangeList>() {
376 public String getTitle() {
377 return CvsBundle.message("changelist.column.branch");
380 public Object getValue(final CvsChangeList changeList) {
381 final String branch = changeList.getBranch();
382 return branch == null ? "HEAD" : branch;