Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSChangeSetCollector.java')
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSChangeSetCollector.java456
1 files changed, 456 insertions, 0 deletions
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSChangeSetCollector.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSChangeSetCollector.java
new file mode 100644
index 000000000..c4133f283
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSChangeSetCollector.java
@@ -0,0 +1,456 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.subscriber;
+
+import com.ibm.icu.text.DateFormat;
+import java.util.Date;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamStatus;
+import org.eclipse.team.core.subscribers.Subscriber;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.synchronize.SyncInfoSet;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteResource;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+import org.eclipse.team.internal.ccvs.ui.*;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.operations.RemoteLogOperation.LogEntryCache;
+import org.eclipse.team.internal.core.subscribers.*;
+import org.eclipse.team.internal.ui.synchronize.SyncInfoSetChangeSetCollector;
+import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
+import org.eclipse.team.ui.synchronize.SynchronizePageActionGroup;
+
+/**
+ * Collector that fetches the log for incoming CVS change sets
+ */
+public class CVSChangeSetCollector extends SyncInfoSetChangeSetCollector implements LogEntryCacheUpdateHandler.ILogsFetchedListener {
+
+ /*
+ * Constant used to add the collector to the configuration of a page so
+ * it can be accessed by the CVS custom actions
+ */
+ public static final String CVS_CHECKED_IN_COLLECTOR = CVSUIPlugin.ID + ".CVSCheckedInCollector"; //$NON-NLS-1$
+
+ /*
+ * Constant used to store the log entry handler in the configuration so it can
+ * be kept around over layout changes
+ */
+ private static final String LOG_ENTRY_HANDLER = CVSUIPlugin.ID + ".LogEntryHandler"; //$NON-NLS-1$
+
+ private static final String DEFAULT_INCOMING_SET_NAME = CVSUIMessages.CVSChangeSetCollector_0;
+
+ boolean disposed = false;
+
+ private LogEntryCache logEntryCache;
+
+ /* *****************************************************************************
+ * Special sync info that has its kind already calculated.
+ */
+ public class CVSUpdatableSyncInfo extends CVSSyncInfo {
+ public int kind;
+ public CVSUpdatableSyncInfo(int kind, IResource local, IResourceVariant base, IResourceVariant remote, Subscriber s) {
+ super(local, base, remote, s);
+ this.kind = kind;
+ }
+
+ protected int calculateKind() throws TeamException {
+ return kind;
+ }
+ }
+
+ private class DefaultCheckedInChangeSet extends CheckedInChangeSet {
+
+ private Date date = new Date();
+
+ public DefaultCheckedInChangeSet(String name) {
+ setName(name);
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getAuthor()
+ */
+ public String getAuthor() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getDate()
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.subscribers.ChangeSet#getComment()
+ */
+ public String getComment() {
+ return ""; //$NON-NLS-1$
+ }
+
+ }
+
+ private class CVSCheckedInChangeSet extends CheckedInChangeSet {
+
+ private final ILogEntry entry;
+
+ public CVSCheckedInChangeSet(ILogEntry entry) {
+ this.entry = entry;
+ Date date = entry.getDate();
+ String comment = Util.flattenText(entry.getComment());
+ if (date == null) {
+ setName("["+entry.getAuthor()+ "] " + comment); //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ String dateString = DateFormat.getDateTimeInstance().format(date);
+ setName("["+entry.getAuthor()+ "] (" + dateString +") " + comment); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getAuthor()
+ */
+ public String getAuthor() {
+ return entry.getAuthor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.subscribers.CheckedInChangeSet#getDate()
+ */
+ public Date getDate() {
+ return entry.getDate();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.subscribers.ChangeSet#getComment()
+ */
+ public String getComment() {
+ return entry.getComment();
+ }
+ }
+
+ public CVSChangeSetCollector(ISynchronizePageConfiguration configuration) {
+ super(configuration);
+ configuration.setProperty(CVSChangeSetCollector.CVS_CHECKED_IN_COLLECTOR, this);
+ }
+
+ public synchronized LogEntryCacheUpdateHandler getLogEntryHandler() {
+ LogEntryCacheUpdateHandler handler = (LogEntryCacheUpdateHandler)getConfiguration().getProperty(LOG_ENTRY_HANDLER);
+ if (handler == null) {
+ handler = initializeLogEntryHandler(getConfiguration());
+ }
+ handler.setListener(this);
+ return handler;
+ }
+
+ /*
+ * Initialize the log entry handler and place it in the configuration
+ */
+ private LogEntryCacheUpdateHandler initializeLogEntryHandler(final ISynchronizePageConfiguration configuration) {
+ final LogEntryCacheUpdateHandler logEntryHandler = new LogEntryCacheUpdateHandler(configuration);
+ configuration.setProperty(LOG_ENTRY_HANDLER, logEntryHandler);
+ // Use an action group to get notified when the configuration is disposed
+ configuration.addActionContribution(new SynchronizePageActionGroup() {
+ public void dispose() {
+ super.dispose();
+ LogEntryCacheUpdateHandler handler = (LogEntryCacheUpdateHandler)configuration.getProperty(LOG_ENTRY_HANDLER);
+ if (handler != null) {
+ handler.shutdown();
+ configuration.setProperty(LOG_ENTRY_HANDLER, null);
+ }
+ }
+ });
+ // It is possible that the configuration has been disposed concurrently by another thread
+ // TODO
+ return logEntryHandler;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.subscribers.SyncInfoSetChangeSetCollector#add(org.eclipse.team.core.synchronize.SyncInfo[])
+ */
+ protected void add(SyncInfo[] infos) {
+ LogEntryCacheUpdateHandler handler = getLogEntryHandler();
+ if (handler != null)
+ try {
+ handler.fetch(infos);
+ } catch (CVSException e) {
+ getConfiguration().getSyncInfoSet().addError(new TeamStatus(IStatus.ERROR, CVSUIPlugin.ID, 0, e.getMessage(), e, null));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.SyncInfoSetChangeSetCollector#reset(org.eclipse.team.core.synchronize.SyncInfoSet)
+ */
+ public void reset(SyncInfoSet seedSet) {
+ // Notify that handler to stop any fetches in progress
+ LogEntryCacheUpdateHandler handler = getLogEntryHandler();
+ if (handler != null) {
+ handler.stopFetching();
+ }
+ super.reset(seedSet);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.views.HierarchicalModelProvider#dispose()
+ */
+ public void dispose() {
+ // No longer listen for log entry changes
+ // (The handler is disposed with the page)
+ disposed = true;
+ LogEntryCacheUpdateHandler handler = getLogEntryHandler();
+ if (handler != null) handler.setListener(null);
+ getConfiguration().setProperty(CVSChangeSetCollector.CVS_CHECKED_IN_COLLECTOR, null);
+ logEntryCache = null;
+ super.dispose();
+ }
+
+ /**
+ * Fetch the log histories for the remote changes and use this information
+ * to add each resource to an appropriate commit set.
+ */
+ private void handleRemoteChanges(final SyncInfo[] infos, final LogEntryCache logEntries, final IProgressMonitor monitor) {
+ performUpdate(new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) {
+ addLogEntries(infos, logEntries, monitor);
+ }
+ }, true /* preserver expansion */, monitor);
+ }
+
+ /*
+ * Add the following sync info elements to the viewer. It is assumed that these elements have associated
+ * log entries cached in the log operation.
+ */
+ private void addLogEntries(SyncInfo[] commentInfos, LogEntryCache logs, IProgressMonitor monitor) {
+ try {
+ monitor.beginTask(null, commentInfos.length * 10);
+ if (logs != null) {
+ for (int i = 0; i < commentInfos.length; i++) {
+ addSyncInfoToCommentNode(commentInfos[i], logs);
+ monitor.worked(10);
+ }
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /*
+ * Create a node for the given sync info object. The logs should contain the log for this info.
+ *
+ * @param info the info for which to create a node in the model
+ * @param log the cvs log for this node
+ */
+ private void addSyncInfoToCommentNode(SyncInfo info, LogEntryCache logs) {
+ LogEntryCacheUpdateHandler handler = getLogEntryHandler();
+ if (handler != null) {
+ ICVSRemoteResource remoteResource = handler.getRemoteResource(info);
+ if(handler.getSubscriber() instanceof CVSCompareSubscriber && remoteResource != null) {
+ addMultipleRevisions(info, logs, remoteResource);
+ } else {
+ addSingleRevision(info, logs, remoteResource);
+ }
+ }
+ }
+
+ /*
+ * Add a single log entry to the model.
+ *
+ * @param info
+ * @param logs
+ * @param remoteResource
+ */
+ private void addSingleRevision(SyncInfo info, LogEntryCache logs, ICVSRemoteResource remoteResource) {
+ ILogEntry logEntry = logs.getLogEntry(remoteResource);
+ if (remoteResource != null && !remoteResource.isFolder()) {
+ // For incoming deletions grab the comment for the latest on the same branch
+ // which is now in the attic.
+ try {
+ String remoteRevision = ((ICVSRemoteFile) remoteResource).getRevision();
+ if (isDeletedRemotely(info)) {
+ ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
+ for (int i = 0; i < logEntries.length; i++) {
+ ILogEntry entry = logEntries[i];
+ String revision = entry.getRevision();
+ if (entry.isDeletion() && ResourceSyncInfo.isLaterRevision(revision, remoteRevision)) {
+ logEntry = entry;
+ }
+ }
+ }
+ } catch (TeamException e) {
+ // continue and skip deletion checks
+ }
+ }
+ addRemoteChange(info, remoteResource, logEntry);
+ }
+
+ /*
+ * Add multiple log entries to the model.
+ *
+ * @param info
+ * @param logs
+ * @param remoteResource
+ */
+ private void addMultipleRevisions(SyncInfo info, LogEntryCache logs, ICVSRemoteResource remoteResource) {
+ ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
+ if(logEntries == null || logEntries.length == 0) {
+ // If for some reason we don't have a log entry, try the latest
+ // remote.
+ addRemoteChange(info, null, null);
+ } else {
+ for (int i = 0; i < logEntries.length; i++) {
+ ILogEntry entry = logEntries[i];
+ addRemoteChange(info, remoteResource, entry);
+ }
+ }
+ }
+
+ private boolean isDeletedRemotely(SyncInfo info) {
+ int kind = info.getKind();
+ if(kind == (SyncInfo.INCOMING | SyncInfo.DELETION)) return true;
+ if(SyncInfo.getDirection(kind) == SyncInfo.CONFLICTING && info.getRemote() == null) return true;
+ return false;
+ }
+
+ /*
+ * Add the remote change to an incoming commit set
+ */
+ private void addRemoteChange(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
+ if (disposed) return;
+ LogEntryCacheUpdateHandler handler = getLogEntryHandler();
+ if(handler != null && remoteResource != null && logEntry != null && handler.isRemoteChange(info)) {
+ if(requiresCustomSyncInfo(info, remoteResource, logEntry)) {
+ info = new CVSUpdatableSyncInfo(info.getKind(), info.getLocal(), info.getBase(), (RemoteResource)logEntry.getRemoteFile(), ((CVSSyncInfo)info).getSubscriber());
+ try {
+ info.init();
+ } catch (TeamException e) {
+ // this shouldn't happen, we've provided our own calculate kind
+ }
+ }
+ // Only add the info if the base and remote differ
+ IResourceVariant base = info.getBase();
+ IResourceVariant remote = info.getRemote();
+ if ((base == null && remote != null) || (remote == null && base != null) || (remote != null && base != null && !base.equals(remote))) {
+ synchronized(this) {
+ CheckedInChangeSet set = getChangeSetFor(logEntry);
+ if (set == null) {
+ set = createChangeSetFor(logEntry);
+ add(set);
+ }
+ set.add(info);
+ }
+ }
+ } else {
+ // The info was not retrieved for the remote change for some reason.
+ // Add the node to the root
+ addToDefaultSet(DEFAULT_INCOMING_SET_NAME, info);
+ }
+ }
+
+ private void addToDefaultSet(String name, SyncInfo info) {
+ CheckedInChangeSet set;
+ synchronized(this) {
+ set = getChangeSetFor(name);
+ if (set == null) {
+ set = createDefaultChangeSet(name);
+ add(set);
+ }
+ set.add(info);
+ }
+ }
+
+ private CheckedInChangeSet createDefaultChangeSet(String name) {
+ return new DefaultCheckedInChangeSet(name);
+ }
+
+ private CheckedInChangeSet createChangeSetFor(ILogEntry logEntry) {
+ return new CVSCheckedInChangeSet(logEntry);
+ }
+
+ private CheckedInChangeSet getChangeSetFor(ILogEntry logEntry) {
+ ChangeSet[] sets = getSets();
+ for (int i = 0; i < sets.length; i++) {
+ ChangeSet set = sets[i];
+ if (set instanceof CheckedInChangeSet &&
+ set.getComment().equals(logEntry.getComment()) &&
+ ((CheckedInChangeSet)set).getAuthor().equals(logEntry.getAuthor())) {
+ return (CheckedInChangeSet)set;
+ }
+ }
+ return null;
+ }
+
+ private CheckedInChangeSet getChangeSetFor(String name) {
+ ChangeSet[] sets = getSets();
+ for (int i = 0; i < sets.length; i++) {
+ ChangeSet set = sets[i];
+ if (set.getName().equals(name)) {
+ return (CheckedInChangeSet)set;
+ }
+ }
+ return null;
+ }
+
+ private boolean requiresCustomSyncInfo(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
+ // Only interested in non-deletions
+ if (logEntry.isDeletion() || !(info instanceof CVSSyncInfo)) return false;
+ // Only require a custom sync info if the remote of the sync info
+ // differs from the remote in the log entry
+ IResourceVariant remote = info.getRemote();
+ if (remote == null) return true;
+ return !remote.equals(remoteResource);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.SyncInfoSetChangeSetCollector#waitUntilDone(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void waitUntilDone(IProgressMonitor monitor) {
+ super.waitUntilDone(monitor);
+ monitor.worked(1);
+ // wait for the event handler to process changes.
+ LogEntryCacheUpdateHandler handler = getLogEntryHandler();
+ if (handler != null) {
+ while(handler.getEventHandlerJob().getState() != Job.NONE) {
+ monitor.worked(1);
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ }
+ Policy.checkCanceled(monitor);
+ }
+ }
+ monitor.worked(1);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.subscriber.LogEntryCacheUpdateHandler.ILogsFetchedListener#logEntriesFetched(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void logEntriesFetched(SyncInfoSet set, LogEntryCache logEntryCache, IProgressMonitor monitor) {
+ if (disposed) return;
+ // Hold on to the cache so we can use it while commit sets are visible
+ this.logEntryCache = logEntryCache;
+ handleRemoteChanges(set.getSyncInfos(), logEntryCache, monitor);
+ }
+
+ public ICVSRemoteFile getImmediatePredecessor(ICVSRemoteFile file) throws TeamException {
+ if (logEntryCache != null)
+ return logEntryCache.getImmediatePredecessor(file);
+ return null;
+ }
+
+ protected void initializeSets() {
+ // Nothing to do
+ }
+}

Back to the top