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
;
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
) {
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() {
81 private class MyZipper
extends VcsCommittedListsZipperAdapter
{
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
) {
100 public long getNumber(final CommittedChangeList list
) {
101 return list
.getCommitDate().getTime();
106 public CvsRepositoryLocation
getLocationFor(final FilePath root
) {
107 if (!CvsUtil
.fileIsUnderCvs(root
.getIOFile())) {
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
};
125 public VcsCommittedViewAuxiliary
createActions(final DecoratorManager manager
, final RepositoryLocation location
) {
129 public int getUnlimitedCountValue() {
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
,
141 final AsynchConsumer
<CommittedChangeList
> consumer
)
142 throws VcsException
{
144 CvsRepositoryLocation cvsLocation
= (CvsRepositoryLocation
) location
;
145 final String module
= cvsLocation
.getModuleName();
146 final CvsEnvironment connectionSettings
= cvsLocation
.getEnvironment();
147 if (connectionSettings
.isOffline()) {
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();
184 private List
<CvsChangeList
> loadCommittedChanges(final ChangeBrowserSettings settings
,
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();
209 builder
.addLogs(log
);
210 final List
<CvsChangeList
> versions
= builder
.getVersions();
211 settings
.filterChanges(versions
);
216 private CvsResult
runRLogOperation(final CvsEnvironment settings
,
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
);
228 return runRLogOperationImpl(settings
, module
, dateFrom
, dateTo
, consumer
);
232 return executionResult
;
235 private CvsResult
runRLogOperationImpl(final CvsEnvironment settings
,
239 final Consumer
<LogInformationWrapper
> consumer
) {
240 LoadHistoryOperation operation
= new LoadHistoryOperation(settings
, module
, dateFrom
, dateTo
, null) {
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
,
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
);
272 return runRLogOperation(settings
, module
, dateFrom
, dateTo
, log
);
277 return executionResult
;
280 public int getFormatVersion() {
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() {
297 public Collection
<FilePath
> getIncomingFiles(final RepositoryLocation location
) {
301 public boolean refreshCacheByNumber() {
305 public String
getChangelistTitle() {
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
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
330 return isDifferentBranch(filePath
, changeList
) || (localRevision
!= null && localRevision
.compareTo(changeRevision
) >= 0);
333 private static boolean isDifferentBranch(final FilePath filePath
, final CvsChangeList changeList
) {
335 final CvsEntriesManager cvsEntriesManager
= CvsEntriesManager
.getInstance();
336 final VirtualFile parent
= filePath
.getVirtualFileParent();
337 if (parent
!= null) {
338 Entry entry
= cvsEntriesManager
.getEntryFor(parent
, filePath
.getName());
340 localTag
= entry
.getStickyTag();
343 localTag
= getDirectoryTag(parent
);
347 final VirtualFile validParent
= ApplicationManager
.getApplication().runReadAction(new Computable
<VirtualFile
>() {
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
);
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() {
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
;