KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > ui > subscriber > CVSChangeSetCollector


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.team.internal.ccvs.ui.subscriber;
12
13 import com.ibm.icu.text.DateFormat;
14 import java.util.Date JavaDoc;
15
16 import org.eclipse.core.resources.IResource;
17 import org.eclipse.core.resources.IWorkspaceRunnable;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.core.runtime.jobs.Job;
21 import org.eclipse.team.core.TeamException;
22 import org.eclipse.team.core.TeamStatus;
23 import org.eclipse.team.core.subscribers.Subscriber;
24 import org.eclipse.team.core.synchronize.SyncInfo;
25 import org.eclipse.team.core.synchronize.SyncInfoSet;
26 import org.eclipse.team.core.variants.IResourceVariant;
27 import org.eclipse.team.internal.ccvs.core.*;
28 import org.eclipse.team.internal.ccvs.core.resources.RemoteResource;
29 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
30 import org.eclipse.team.internal.ccvs.core.util.Util;
31 import org.eclipse.team.internal.ccvs.ui.*;
32 import org.eclipse.team.internal.ccvs.ui.Policy;
33 import org.eclipse.team.internal.ccvs.ui.operations.RemoteLogOperation.LogEntryCache;
34 import org.eclipse.team.internal.core.subscribers.*;
35 import org.eclipse.team.internal.ui.synchronize.SyncInfoSetChangeSetCollector;
36 import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
37 import org.eclipse.team.ui.synchronize.SynchronizePageActionGroup;
38
39 /**
40  * Collector that fetches the log for incoming CVS change sets
41  */

42 public class CVSChangeSetCollector extends SyncInfoSetChangeSetCollector implements LogEntryCacheUpdateHandler.ILogsFetchedListener {
43
44     /*
45      * Constant used to add the collector to the configuration of a page so
46      * it can be accessed by the CVS custom actions
47      */

48     public static final String JavaDoc CVS_CHECKED_IN_COLLECTOR = CVSUIPlugin.ID + ".CVSCheckedInCollector"; //$NON-NLS-1$
49

50     /*
51      * Constant used to store the log entry handler in the configuration so it can
52      * be kept around over layout changes
53      */

54     private static final String JavaDoc LOG_ENTRY_HANDLER = CVSUIPlugin.ID + ".LogEntryHandler"; //$NON-NLS-1$
55

56     private static final String JavaDoc DEFAULT_INCOMING_SET_NAME = CVSUIMessages.CVSChangeSetCollector_0;
57     
58     boolean disposed = false;
59
60     private LogEntryCache logEntryCache;
61     
62     /* *****************************************************************************
63      * Special sync info that has its kind already calculated.
64      */

65     public class CVSUpdatableSyncInfo extends CVSSyncInfo {
66         public int kind;
67         public CVSUpdatableSyncInfo(int kind, IResource local, IResourceVariant base, IResourceVariant remote, Subscriber s) {
68             super(local, base, remote, s);
69             this.kind = kind;
70         }
71
72         protected int calculateKind() throws TeamException {
73             return kind;
74         }
75     }
76     
77     private class DefaultCheckedInChangeSet extends CheckedInChangeSet {
78
79         private Date JavaDoc date = new Date JavaDoc();
80         
81         public DefaultCheckedInChangeSet(String JavaDoc name) {
82             setName(name);
83         }
84         /* (non-Javadoc)
85          * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getAuthor()
86          */

87         public String JavaDoc getAuthor() {
88             return ""; //$NON-NLS-1$
89
}
90
91         /* (non-Javadoc)
92          * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getDate()
93          */

94         public Date JavaDoc getDate() {
95             return date;
96         }
97
98         /* (non-Javadoc)
99          * @see org.eclipse.team.core.subscribers.ChangeSet#getComment()
100          */

101         public String JavaDoc getComment() {
102             return ""; //$NON-NLS-1$
103
}
104         
105     }
106     
107     private class CVSCheckedInChangeSet extends CheckedInChangeSet {
108
109         private final ILogEntry entry;
110
111         public CVSCheckedInChangeSet(ILogEntry entry) {
112             this.entry = entry;
113             Date JavaDoc date = entry.getDate();
114             String JavaDoc comment = Util.flattenText(entry.getComment());
115             if (date == null) {
116                 setName("["+entry.getAuthor()+ "] " + comment); //$NON-NLS-1$ //$NON-NLS-2$
117
} else {
118                 String JavaDoc dateString = DateFormat.getDateTimeInstance().format(date);
119                 setName("["+entry.getAuthor()+ "] (" + dateString +") " + comment); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
120
}
121         }
122         
123         /* (non-Javadoc)
124          * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getAuthor()
125          */

126         public String JavaDoc getAuthor() {
127             return entry.getAuthor();
128         }
129
130         /* (non-Javadoc)
131          * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getDate()
132          */

133         public Date JavaDoc getDate() {
134             return entry.getDate();
135         }
136
137         /* (non-Javadoc)
138          * @see org.eclipse.team.core.subscribers.ChangeSet#getComment()
139          */

140         public String JavaDoc getComment() {
141             return entry.getComment();
142         }
143     }
144     
145     public CVSChangeSetCollector(ISynchronizePageConfiguration configuration) {
146         super(configuration);
147         configuration.setProperty(CVSChangeSetCollector.CVS_CHECKED_IN_COLLECTOR, this);
148     }
149
150     public synchronized LogEntryCacheUpdateHandler getLogEntryHandler() {
151         LogEntryCacheUpdateHandler handler = (LogEntryCacheUpdateHandler)getConfiguration().getProperty(LOG_ENTRY_HANDLER);
152         if (handler == null) {
153             handler = initializeLogEntryHandler(getConfiguration());
154         }
155         handler.setListener(this);
156         return handler;
157     }
158     
159     /*
160      * Initialize the log entry handler and place it in the configuration
161      */

162     private LogEntryCacheUpdateHandler initializeLogEntryHandler(final ISynchronizePageConfiguration configuration) {
163         final LogEntryCacheUpdateHandler logEntryHandler = new LogEntryCacheUpdateHandler(configuration);
164         configuration.setProperty(LOG_ENTRY_HANDLER, logEntryHandler);
165         // Use an action group to get notified when the configuration is disposed
166
configuration.addActionContribution(new SynchronizePageActionGroup() {
167             public void dispose() {
168                 super.dispose();
169                 LogEntryCacheUpdateHandler handler = (LogEntryCacheUpdateHandler)configuration.getProperty(LOG_ENTRY_HANDLER);
170                 if (handler != null) {
171                     handler.shutdown();
172                     configuration.setProperty(LOG_ENTRY_HANDLER, null);
173                 }
174             }
175         });
176         // It is possible that the configuration has been disposed concurrently by another thread
177
// TODO
178
return logEntryHandler;
179     }
180
181     /* (non-Javadoc)
182      * @see org.eclipse.team.core.subscribers.SyncInfoSetChangeSetCollector#add(org.eclipse.team.core.synchronize.SyncInfo[])
183      */

184     protected void add(SyncInfo[] infos) {
185         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
186         if (handler != null)
187             try {
188                 handler.fetch(infos);
189             } catch (CVSException e) {
190                 getConfiguration().getSyncInfoSet().addError(new TeamStatus(IStatus.ERROR, CVSUIPlugin.ID, 0, e.getMessage(), e, null));
191             }
192     }
193
194     /* (non-Javadoc)
195      * @see org.eclipse.team.ui.synchronize.SyncInfoSetChangeSetCollector#reset(org.eclipse.team.core.synchronize.SyncInfoSet)
196      */

197     public void reset(SyncInfoSet seedSet) {
198         // Notify that handler to stop any fetches in progress
199
LogEntryCacheUpdateHandler handler = getLogEntryHandler();
200         if (handler != null) {
201             handler.stopFetching();
202         }
203         super.reset(seedSet);
204     }
205     
206     /* (non-Javadoc)
207      * @see org.eclipse.team.ui.synchronize.views.HierarchicalModelProvider#dispose()
208      */

209     public void dispose() {
210         // No longer listen for log entry changes
211
// (The handler is disposed with the page)
212
disposed = true;
213         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
214         if (handler != null) handler.setListener(null);
215         getConfiguration().setProperty(CVSChangeSetCollector.CVS_CHECKED_IN_COLLECTOR, null);
216         logEntryCache = null;
217         super.dispose();
218     }
219     
220     /**
221      * Fetch the log histories for the remote changes and use this information
222      * to add each resource to an appropriate commit set.
223      */

224     private void handleRemoteChanges(final SyncInfo[] infos, final LogEntryCache logEntries, final IProgressMonitor monitor) {
225         performUpdate(new IWorkspaceRunnable() {
226             public void run(IProgressMonitor monitor) {
227                 addLogEntries(infos, logEntries, monitor);
228             }
229         }, true /* preserver expansion */, monitor);
230     }
231     
232     /*
233      * Add the following sync info elements to the viewer. It is assumed that these elements have associated
234      * log entries cached in the log operation.
235      */

236     private void addLogEntries(SyncInfo[] commentInfos, LogEntryCache logs, IProgressMonitor monitor) {
237         try {
238             monitor.beginTask(null, commentInfos.length * 10);
239             if (logs != null) {
240                 for (int i = 0; i < commentInfos.length; i++) {
241                     addSyncInfoToCommentNode(commentInfos[i], logs);
242                     monitor.worked(10);
243                 }
244             }
245         } finally {
246             monitor.done();
247         }
248     }
249     
250     /*
251      * Create a node for the given sync info object. The logs should contain the log for this info.
252      *
253      * @param info the info for which to create a node in the model
254      * @param log the cvs log for this node
255      */

256     private void addSyncInfoToCommentNode(SyncInfo info, LogEntryCache logs) {
257         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
258         if (handler != null) {
259             ICVSRemoteResource remoteResource = handler.getRemoteResource(info);
260             if(handler.getSubscriber() instanceof CVSCompareSubscriber && remoteResource != null) {
261                 addMultipleRevisions(info, logs, remoteResource);
262             } else {
263                 addSingleRevision(info, logs, remoteResource);
264             }
265         }
266     }
267     
268     /*
269      * Add a single log entry to the model.
270      *
271      * @param info
272      * @param logs
273      * @param remoteResource
274      */

275     private void addSingleRevision(SyncInfo info, LogEntryCache logs, ICVSRemoteResource remoteResource) {
276         ILogEntry logEntry = logs.getLogEntry(remoteResource);
277         if (remoteResource != null && !remoteResource.isFolder()) {
278             // For incoming deletions grab the comment for the latest on the same branch
279
// which is now in the attic.
280
try {
281                 String JavaDoc remoteRevision = ((ICVSRemoteFile) remoteResource).getRevision();
282                 if (isDeletedRemotely(info)) {
283                     ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
284                     for (int i = 0; i < logEntries.length; i++) {
285                         ILogEntry entry = logEntries[i];
286                         String JavaDoc revision = entry.getRevision();
287                         if (entry.isDeletion() && ResourceSyncInfo.isLaterRevision(revision, remoteRevision)) {
288                             logEntry = entry;
289                         }
290                     }
291                 }
292             } catch (TeamException e) {
293                 // continue and skip deletion checks
294
}
295         }
296         addRemoteChange(info, remoteResource, logEntry);
297     }
298     
299     /*
300      * Add multiple log entries to the model.
301      *
302      * @param info
303      * @param logs
304      * @param remoteResource
305      */

306     private void addMultipleRevisions(SyncInfo info, LogEntryCache logs, ICVSRemoteResource remoteResource) {
307         ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
308         if(logEntries == null || logEntries.length == 0) {
309             // If for some reason we don't have a log entry, try the latest
310
// remote.
311
addRemoteChange(info, null, null);
312         } else {
313             for (int i = 0; i < logEntries.length; i++) {
314                 ILogEntry entry = logEntries[i];
315                 addRemoteChange(info, remoteResource, entry);
316             }
317         }
318     }
319     
320     private boolean isDeletedRemotely(SyncInfo info) {
321         int kind = info.getKind();
322         if(kind == (SyncInfo.INCOMING | SyncInfo.DELETION)) return true;
323         if(SyncInfo.getDirection(kind) == SyncInfo.CONFLICTING && info.getRemote() == null) return true;
324         return false;
325     }
326     
327     /*
328      * Add the remote change to an incoming commit set
329      */

330     private void addRemoteChange(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
331         if (disposed) return;
332         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
333         if(handler != null && remoteResource != null && logEntry != null && handler.isRemoteChange(info)) {
334             if(requiresCustomSyncInfo(info, remoteResource, logEntry)) {
335                 info = new CVSUpdatableSyncInfo(info.getKind(), info.getLocal(), info.getBase(), (RemoteResource)logEntry.getRemoteFile(), ((CVSSyncInfo)info).getSubscriber());
336                 try {
337                     info.init();
338                 } catch (TeamException e) {
339                     // this shouldn't happen, we've provided our own calculate kind
340
}
341             }
342             // Only add the info if the base and remote differ
343
IResourceVariant base = info.getBase();
344             IResourceVariant remote = info.getRemote();
345             if ((base == null && remote != null) || (remote == null && base != null) || (remote != null && base != null && !base.equals(remote))) {
346                 synchronized(this) {
347                     CheckedInChangeSet set = getChangeSetFor(logEntry);
348                     if (set == null) {
349                         set = createChangeSetFor(logEntry);
350                         add(set);
351                     }
352                     set.add(info);
353                 }
354             }
355         } else {
356             // The info was not retrieved for the remote change for some reason.
357
// Add the node to the root
358
addToDefaultSet(DEFAULT_INCOMING_SET_NAME, info);
359         }
360     }
361     
362     private void addToDefaultSet(String JavaDoc name, SyncInfo info) {
363         CheckedInChangeSet set;
364         synchronized(this) {
365             set = getChangeSetFor(name);
366             if (set == null) {
367                 set = createDefaultChangeSet(name);
368                 add(set);
369             }
370             set.add(info);
371         }
372     }
373     
374     private CheckedInChangeSet createDefaultChangeSet(String JavaDoc name) {
375         return new DefaultCheckedInChangeSet(name);
376     }
377
378     private CheckedInChangeSet createChangeSetFor(ILogEntry logEntry) {
379         return new CVSCheckedInChangeSet(logEntry);
380     }
381
382     private CheckedInChangeSet getChangeSetFor(ILogEntry logEntry) {
383         ChangeSet[] sets = getSets();
384         for (int i = 0; i < sets.length; i++) {
385             ChangeSet set = sets[i];
386             if (set instanceof CheckedInChangeSet &&
387                     set.getComment().equals(logEntry.getComment()) &&
388                     ((CheckedInChangeSet)set).getAuthor().equals(logEntry.getAuthor())) {
389                 return (CheckedInChangeSet)set;
390             }
391         }
392         return null;
393     }
394
395     private CheckedInChangeSet getChangeSetFor(String JavaDoc name) {
396         ChangeSet[] sets = getSets();
397         for (int i = 0; i < sets.length; i++) {
398             ChangeSet set = sets[i];
399             if (set.getName().equals(name)) {
400                 return (CheckedInChangeSet)set;
401             }
402         }
403         return null;
404     }
405     
406     private boolean requiresCustomSyncInfo(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
407         // Only interested in non-deletions
408
if (logEntry.isDeletion() || !(info instanceof CVSSyncInfo)) return false;
409         // Only require a custom sync info if the remote of the sync info
410
// differs from the remote in the log entry
411
IResourceVariant remote = info.getRemote();
412         if (remote == null) return true;
413         return !remote.equals(remoteResource);
414     }
415     
416     /* (non-Javadoc)
417      * @see org.eclipse.team.ui.synchronize.SyncInfoSetChangeSetCollector#waitUntilDone(org.eclipse.core.runtime.IProgressMonitor)
418      */

419     public void waitUntilDone(IProgressMonitor monitor) {
420         super.waitUntilDone(monitor);
421         monitor.worked(1);
422         // wait for the event handler to process changes.
423
LogEntryCacheUpdateHandler handler = getLogEntryHandler();
424         if (handler != null) {
425             while(handler.getEventHandlerJob().getState() != Job.NONE) {
426                 monitor.worked(1);
427                 try {
428                     Thread.sleep(10);
429                 } catch (InterruptedException JavaDoc e) {
430                 }
431                 Policy.checkCanceled(monitor);
432             }
433         }
434         monitor.worked(1);
435     }
436
437     /* (non-Javadoc)
438      * @see org.eclipse.team.internal.ccvs.ui.subscriber.LogEntryCacheUpdateHandler.ILogsFetchedListener#logEntriesFetched(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor)
439      */

440     public void logEntriesFetched(SyncInfoSet set, LogEntryCache logEntryCache, IProgressMonitor monitor) {
441         if (disposed) return;
442         // Hold on to the cache so we can use it while commit sets are visible
443
this.logEntryCache = logEntryCache;
444         handleRemoteChanges(set.getSyncInfos(), logEntryCache, monitor);
445     }
446
447     public ICVSRemoteFile getImmediatePredecessor(ICVSRemoteFile file) throws TeamException {
448         if (logEntryCache != null)
449             return logEntryCache.getImmediatePredecessor(file);
450         return null;
451     }
452
453     protected void initializeSets() {
454         // Nothing to do
455
}
456 }
457
Popular Tags