KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > ui > mappings > CheckedInChangeSetCollector


1 /*******************************************************************************
2  * Copyright (c) 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.mappings;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.Set JavaDoc;
19
20 import org.eclipse.core.resources.IResource;
21 import org.eclipse.core.runtime.*;
22 import org.eclipse.core.runtime.jobs.Job;
23 import org.eclipse.team.core.TeamException;
24 import org.eclipse.team.core.diff.*;
25 import org.eclipse.team.core.diff.provider.DiffTree;
26 import org.eclipse.team.core.subscribers.Subscriber;
27 import org.eclipse.team.core.synchronize.SyncInfo;
28 import org.eclipse.team.core.synchronize.SyncInfoSet;
29 import org.eclipse.team.core.variants.IResourceVariant;
30 import org.eclipse.team.internal.ccvs.core.*;
31 import org.eclipse.team.internal.ccvs.core.mapping.CVSCheckedInChangeSet;
32 import org.eclipse.team.internal.ccvs.core.resources.RemoteResource;
33 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
34 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
35 import org.eclipse.team.internal.ccvs.ui.Policy;
36 import org.eclipse.team.internal.ccvs.ui.operations.RemoteLogOperation.LogEntryCache;
37 import org.eclipse.team.internal.ccvs.ui.subscriber.CVSChangeSetCollector;
38 import org.eclipse.team.internal.ccvs.ui.subscriber.LogEntryCacheUpdateHandler;
39 import org.eclipse.team.internal.ccvs.ui.subscriber.LogEntryCacheUpdateHandler.ILogsFetchedListener;
40 import org.eclipse.team.internal.core.mapping.SyncInfoToDiffConverter;
41 import org.eclipse.team.internal.core.subscribers.*;
42 import org.eclipse.team.internal.ui.Utils;
43 import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
44 import org.eclipse.team.ui.synchronize.SynchronizePageActionGroup;
45
46 public class CheckedInChangeSetCollector extends BatchingChangeSetManager implements ILogsFetchedListener {
47
48     /*
49      * Constant used to store the log entry handler in the configuration so it can
50      * be kept around over layout changes
51      */

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

54     /* *****************************************************************************
55      * Special sync info that has its kind already calculated.
56      */

57     private class CVSUpdatableSyncInfo extends CVSSyncInfo {
58         public int kind;
59         public CVSUpdatableSyncInfo(int kind, IResource local, IResourceVariant base, IResourceVariant remote, Subscriber s) {
60             super(local, base, remote, s);
61             this.kind = kind;
62         }
63
64         protected int calculateKind() throws TeamException {
65             return kind;
66         }
67     }
68     
69     IDiffChangeListener diffTreeListener = new IDiffChangeListener() {
70         public void propertyChanged(IDiffTree tree, int property, IPath[] paths) {
71             // Ignore
72
}
73         public void diffsChanged(IDiffChangeEvent event, IProgressMonitor monitor) {
74             if (event.getTree().isEmpty()) {
75                 ChangeSet changeSet = getChangeSet(event.getTree());
76                 if (changeSet != null) {
77                     remove(changeSet);
78                 }
79             } else {
80                 ChangeSet changeSet = getChangeSet(event.getTree());
81                 if (changeSet != null) {
82                     fireResourcesChangedEvent(changeSet, getAffectedPaths(event));
83                 }
84             }
85         }
86         private IPath[] getAffectedPaths(IDiffChangeEvent event) {
87             Set JavaDoc result = new HashSet JavaDoc();
88             IPath[] removed = event.getRemovals();
89             for (int i = 0; i < removed.length; i++) {
90                 IPath path = removed[i];
91                 result.add(path);
92             }
93             IDiff[] diffs = event.getAdditions();
94             for (int j = 0; j < diffs.length; j++) {
95                 IDiff diff = diffs[j];
96                 result.add(diff.getPath());
97             }
98             diffs = event.getChanges();
99             for (int j = 0; j < diffs.length; j++) {
100                 IDiff diff = diffs[j];
101                 result.add(diff.getPath());
102             }
103             return (IPath[]) result.toArray(new IPath[result.size()]);
104         }
105     };
106
107     private final ISynchronizePageConfiguration configuration;
108     private boolean disposed;
109     private LogEntryCache logEntryCache;
110     private final Subscriber subscriber;
111
112     private HashSet JavaDoc updatedSets;
113     
114     public CheckedInChangeSetCollector(ISynchronizePageConfiguration configuration, Subscriber subscriber) {
115         this.configuration = configuration;
116         this.subscriber = subscriber;
117     }
118     
119     /**
120      * Return the configuration for the page that is displaying the model created
121      * using this collector.
122      * @return the configuration for the page that is displaying the model created
123      * using this collector
124      */

125     public final ISynchronizePageConfiguration getConfiguration() {
126         return configuration;
127     }
128     
129     protected void handleSetAdded(ChangeSet set) {
130         ((DiffChangeSet)set).getDiffTree().addDiffChangeListener(diffTreeListener);
131         super.handleSetAdded(set);
132         if (updatedSets != null) {
133             updatedSets.add(set);
134             ((DiffTree)((DiffChangeSet)set).getDiffTree()).beginInput();
135         }
136     }
137     
138     protected void handleSetRemoved(ChangeSet set) {
139         ((DiffChangeSet)set).getDiffTree().removeDiffChangeListener(diffTreeListener);
140         super.handleSetRemoved(set);
141     }
142     
143     protected ChangeSet getChangeSet(IDiffTree tree) {
144         ChangeSet[] sets = getSets();
145         for (int i = 0; i < sets.length; i++) {
146             ChangeSet changeSet = sets[i];
147             if (((DiffChangeSet)changeSet).getDiffTree() == tree) {
148                 return changeSet;
149             }
150         }
151         return null;
152     }
153     
154     public void handleChange(IDiffChangeEvent event) {
155         List JavaDoc removals = new ArrayList JavaDoc();
156         List JavaDoc additions = new ArrayList JavaDoc();
157         removals.addAll(Arrays.asList(event.getRemovals()));
158         additions.addAll(Arrays.asList(event.getAdditions()));
159         IDiff[] changed = event.getChanges();
160         for (int i = 0; i < changed.length; i++) {
161             IDiff diff = changed[i];
162             additions.add(diff);
163             removals.add(diff.getPath());
164         }
165         if (!removals.isEmpty()) {
166             remove((IPath[]) removals.toArray(new IPath[removals.size()]));
167         }
168         if (!additions.isEmpty()) {
169             add((IDiff[]) additions.toArray(new IDiff[additions.size()]));
170         }
171     }
172     
173     protected void remove(IPath[] paths) {
174         ChangeSet[] sets = getSets();
175         for (int i = 0; i < sets.length; i++) {
176             DiffChangeSet set = (DiffChangeSet)sets[i];
177             set.remove(paths);
178         }
179     }
180     
181     public synchronized LogEntryCacheUpdateHandler getLogEntryHandler() {
182         LogEntryCacheUpdateHandler handler = (LogEntryCacheUpdateHandler)getConfiguration().getProperty(LOG_ENTRY_HANDLER);
183         if (handler == null) {
184             handler = initializeLogEntryHandler(getConfiguration());
185         }
186         handler.setListener(this);
187         return handler;
188     }
189     
190     /*
191      * Initialize the log entry handler and place it in the configuration
192      */

193     private LogEntryCacheUpdateHandler initializeLogEntryHandler(final ISynchronizePageConfiguration configuration) {
194         final LogEntryCacheUpdateHandler logEntryHandler = new LogEntryCacheUpdateHandler(configuration);
195         configuration.setProperty(LOG_ENTRY_HANDLER, logEntryHandler);
196         // Use an action group to get notified when the configuration is disposed
197
configuration.addActionContribution(new SynchronizePageActionGroup() {
198             public void dispose() {
199                 super.dispose();
200                 LogEntryCacheUpdateHandler handler = (LogEntryCacheUpdateHandler)configuration.getProperty(LOG_ENTRY_HANDLER);
201                 if (handler != null) {
202                     handler.shutdown();
203                     configuration.setProperty(LOG_ENTRY_HANDLER, null);
204                 }
205             }
206         });
207         return logEntryHandler;
208     }
209
210     protected void add(IDiff[] diffs) {
211         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
212         if (handler != null)
213             try {
214                 handler.fetch(getSyncInfos(diffs));
215             } catch (CVSException e) {
216                 CVSUIPlugin.log(e);
217             }
218     }
219     
220     private SyncInfo[] getSyncInfos(IDiff[] diffs) {
221         SyncInfoSet set = new SyncInfoSet();
222         for (int i = 0; i < diffs.length; i++) {
223             IDiff diff = diffs[i];
224             set.add(getConverter().asSyncInfo(diff, getSubscriber().getResourceComparator()));
225         }
226         return set.getSyncInfos();
227     }
228
229     public Subscriber getSubscriber() {
230         return subscriber;
231     }
232
233     /* (non-Javadoc)
234      * @see org.eclipse.team.ui.synchronize.views.HierarchicalModelProvider#dispose()
235      */

236     public void dispose() {
237         // No longer listen for log entry changes
238
// (The handler is disposed with the page)
239
disposed = true;
240         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
241         if (handler != null) handler.setListener(null);
242         getConfiguration().setProperty(CVSChangeSetCollector.CVS_CHECKED_IN_COLLECTOR, null);
243         logEntryCache = null;
244         super.dispose();
245     }
246     
247     /**
248      * Fetch the log histories for the remote changes and use this information
249      * to add each resource to an appropriate commit set.
250      */

251     private void handleRemoteChanges(final SyncInfo[] infos, final LogEntryCache logEntries, final IProgressMonitor monitor) {
252         try {
253             beginSetUpdate();
254             addLogEntries(infos, logEntries, monitor);
255         } finally {
256             endSetUpdate(monitor);
257         }
258     }
259     
260     private void beginSetUpdate() {
261         updatedSets = new HashSet JavaDoc();
262     }
263
264     private void endSetUpdate(IProgressMonitor monitor) {
265         for (Iterator JavaDoc iter = updatedSets.iterator(); iter.hasNext();) {
266             DiffChangeSet set = (DiffChangeSet) iter.next();
267             try {
268                 ((DiffTree)set.getDiffTree()).endInput(monitor);
269             } catch (RuntimeException JavaDoc e) {
270                 CVSUIPlugin.log(IStatus.ERROR, "Internal error", e); //$NON-NLS-1$
271
}
272         }
273         updatedSets = null;
274     }
275
276     /*
277      * Add the following sync info elements to the viewer. It is assumed that these elements have associated
278      * log entries cached in the log operation.
279      */

280     private void addLogEntries(SyncInfo[] commentInfos, LogEntryCache logs, IProgressMonitor monitor) {
281         try {
282             monitor.beginTask(null, commentInfos.length * 10);
283             if (logs != null) {
284                 for (int i = 0; i < commentInfos.length; i++) {
285                     addSyncInfoToCommentNode(commentInfos[i], logs);
286                     monitor.worked(10);
287                 }
288             }
289         } finally {
290             monitor.done();
291         }
292     }
293     
294     /*
295      * Create a node for the given sync info object. The logs should contain the log for this info.
296      *
297      * @param info the info for which to create a node in the model
298      * @param log the cvs log for this node
299      */

300     private void addSyncInfoToCommentNode(SyncInfo info, LogEntryCache logs) {
301         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
302         if (handler != null) {
303             ICVSRemoteResource remoteResource = handler.getRemoteResource(info);
304             if(handler.getSubscriber() instanceof CVSCompareSubscriber && remoteResource != null) {
305                 addMultipleRevisions(info, logs, remoteResource);
306             } else {
307                 addSingleRevision(info, logs, remoteResource);
308             }
309         }
310     }
311     
312     /*
313      * Add a single log entry to the model.
314      *
315      * @param info
316      * @param logs
317      * @param remoteResource
318      */

319     private void addSingleRevision(SyncInfo info, LogEntryCache logs, ICVSRemoteResource remoteResource) {
320         ILogEntry logEntry = logs.getLogEntry(remoteResource);
321         if (remoteResource != null && !remoteResource.isFolder()) {
322             // For incoming deletions grab the comment for the latest on the same branch
323
// which is now in the attic.
324
try {
325                 String JavaDoc remoteRevision = ((ICVSRemoteFile) remoteResource).getRevision();
326                 if (isDeletedRemotely(info)) {
327                     ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
328                     for (int i = 0; i < logEntries.length; i++) {
329                         ILogEntry entry = logEntries[i];
330                         String JavaDoc revision = entry.getRevision();
331                         if (entry.isDeletion() && ResourceSyncInfo.isLaterRevision(revision, remoteRevision)) {
332                             logEntry = entry;
333                         }
334                     }
335                 }
336             } catch (TeamException e) {
337                 // continue and skip deletion checks
338
}
339         }
340         addRemoteChange(info, remoteResource, logEntry);
341     }
342     
343     /*
344      * Add multiple log entries to the model.
345      *
346      * @param info
347      * @param logs
348      * @param remoteResource
349      */

350     private void addMultipleRevisions(SyncInfo info, LogEntryCache logs, ICVSRemoteResource remoteResource) {
351         ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
352         if(logEntries == null || logEntries.length == 0) {
353             // If for some reason we don't have a log entry, try the latest
354
// remote.
355
addRemoteChange(info, null, null);
356         } else {
357             for (int i = 0; i < logEntries.length; i++) {
358                 ILogEntry entry = logEntries[i];
359                 addRemoteChange(info, remoteResource, entry);
360             }
361         }
362     }
363     
364     private boolean isDeletedRemotely(SyncInfo info) {
365         int kind = info.getKind();
366         if(kind == (SyncInfo.INCOMING | SyncInfo.DELETION)) return true;
367         if(SyncInfo.getDirection(kind) == SyncInfo.CONFLICTING && info.getRemote() == null) return true;
368         return false;
369     }
370     
371     /*
372      * Add the remote change to an incoming commit set
373      */

374     private void addRemoteChange(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
375         if (disposed) return;
376         LogEntryCacheUpdateHandler handler = getLogEntryHandler();
377         if(handler != null && remoteResource != null && logEntry != null && handler.isRemoteChange(info)) {
378             if(requiresCustomSyncInfo(info, remoteResource, logEntry)) {
379                 info = new CVSUpdatableSyncInfo(info.getKind(), info.getLocal(), info.getBase(), (RemoteResource)logEntry.getRemoteFile(), getSubscriber());
380                 try {
381                     info.init();
382                 } catch (TeamException e) {
383                     // this shouldn't happen, we've provided our own calculate kind
384
}
385             }
386             IDiff diff = getConverter().getDeltaFor(info);
387             // Only add the info if the base and remote differ
388
IResourceVariant base = info.getBase();
389             IResourceVariant remote = info.getRemote();
390             if ((base == null && remote != null) || (remote == null && base != null) || (remote != null && base != null && !base.equals(remote))) {
391                 synchronized(this) {
392                     CVSCheckedInChangeSet set = getChangeSetFor(logEntry);
393                     if (set == null) {
394                         set = createChangeSetFor(logEntry);
395                         add(set);
396                     }
397                     set.add(diff);
398                 }
399             }
400         } else {
401             // The info was not retrieved for the remote change for some reason.
402
// Add the node to the root
403
//addToDefaultSet(DEFAULT_INCOMING_SET_NAME, info);
404
}
405     }
406
407     private SyncInfoToDiffConverter getConverter() {
408         SyncInfoToDiffConverter converter = (SyncInfoToDiffConverter)Utils.getAdapter(subscriber, SyncInfoToDiffConverter.class);
409         if (converter == null)
410             converter = SyncInfoToDiffConverter.getDefault();
411         return converter;
412     }
413
414     private CVSCheckedInChangeSet createChangeSetFor(ILogEntry logEntry) {
415         return new CVSCheckedInChangeSet(logEntry);
416     }
417
418     private CVSCheckedInChangeSet getChangeSetFor(ILogEntry logEntry) {
419         ChangeSet[] sets = getSets();
420         for (int i = 0; i < sets.length; i++) {
421             ChangeSet set = sets[i];
422             if (set instanceof CVSCheckedInChangeSet &&
423                     set.getComment().equals(logEntry.getComment()) &&
424                     ((CVSCheckedInChangeSet)set).getAuthor().equals(logEntry.getAuthor())) {
425                 return (CVSCheckedInChangeSet)set;
426             }
427         }
428         return null;
429     }
430     
431     private boolean requiresCustomSyncInfo(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
432         // Only interested in non-deletions
433
if (logEntry.isDeletion()) return false;
434         // Only require a custom sync info if the remote of the sync info
435
// differs from the remote in the log entry
436
IResourceVariant remote = info.getRemote();
437         if (remote == null) return true;
438         return !remote.equals(remoteResource);
439     }
440     
441     /* (non-Javadoc)
442      * @see org.eclipse.team.ui.synchronize.SyncInfoSetChangeSetCollector#waitUntilDone(org.eclipse.core.runtime.IProgressMonitor)
443      */

444     public void waitUntilDone(IProgressMonitor monitor) {
445         monitor.worked(1);
446         // wait for the event handler to process changes.
447
LogEntryCacheUpdateHandler handler = getLogEntryHandler();
448         if (handler != null) {
449             while(handler.getEventHandlerJob().getState() != Job.NONE) {
450                 monitor.worked(1);
451                 try {
452                     Thread.sleep(10);
453                 } catch (InterruptedException JavaDoc e) {
454                 }
455                 Policy.checkCanceled(monitor);
456             }
457         }
458         monitor.worked(1);
459     }
460
461     /* (non-Javadoc)
462      * @see org.eclipse.team.internal.ccvs.ui.subscriber.LogEntryCacheUpdateHandler.ILogsFetchedListener#logEntriesFetched(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor)
463      */

464     public void logEntriesFetched(SyncInfoSet set, LogEntryCache logEntryCache, IProgressMonitor monitor) {
465         if (disposed) return;
466         // Hold on to the cache so we can use it while commit sets are visible
467
this.logEntryCache = logEntryCache;
468         try {
469             beginInput();
470             handleRemoteChanges(set.getSyncInfos(), logEntryCache, monitor);
471         } finally {
472             endInput(monitor);
473         }
474     }
475
476     public ICVSRemoteFile getImmediatePredecessor(ICVSRemoteFile file) throws TeamException {
477         if (logEntryCache != null)
478             return logEntryCache.getImmediatePredecessor(file);
479         return null;
480     }
481 }
482
Popular Tags