diff options
Diffstat (limited to 'bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/SubscriberSyncInfoCollector.java')
-rw-r--r-- | bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/SubscriberSyncInfoCollector.java | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/SubscriberSyncInfoCollector.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/SubscriberSyncInfoCollector.java new file mode 100644 index 000000000..8597675c0 --- /dev/null +++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/SubscriberSyncInfoCollector.java @@ -0,0 +1,384 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.team.core.subscribers; + +import org.eclipse.core.resources.*; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.team.core.synchronize.*; +import org.eclipse.team.internal.core.Assert; +import org.eclipse.team.internal.core.subscribers.*; + +/** + * This collector maintains a {@link SyncInfoSet} for a particular team subscriber keeping + * it up-to-date with both incoming changes and outgoing changes as they occur for + * resources in the workspace. The collector can be configured to consider all the subscriber's + * roots or only a subset. + * <p> + * The advantage of this collector is that it processes both resource and team + * subscriber deltas in a background thread. + * </p> + * @since 3.0 + */ +public final class SubscriberSyncInfoCollector implements IResourceChangeListener, ISubscriberChangeListener { + + private SyncSetInputFromSubscriber subscriberInput; + private WorkingSetSyncSetInput workingSetInput; + private SyncSetInputFromSyncSet filteredInput; + private SubscriberEventHandler eventHandler; + private Subscriber subscriber; + private IResource[] roots; + + /** + * Create a collector on the subscriber that collects out-of-sync resources + * for all roots of the subscriber. The <code>start()</code> method must be called after creation + * to prime the collector's sync sets. + * @param subscriber the Subscriber + */ + public SubscriberSyncInfoCollector(Subscriber subscriber) { + this(subscriber, null /* use the subscriber roots */); + } + + /** + * Create a collector that collects out-of-sync resources that are children of + * the given roots. If the roots are <code>null</code>, then all out-of-sync resources + * from the subscriber are collected. An empty array of roots will cause no resources + * to be collected. The <code>start()</code> method must be called after creation + * to rpime the collector's sync sets. + * @param subscriber the Subscriber + * @param roots the roots of the out-of-sync resources to be collected + */ + public SubscriberSyncInfoCollector(Subscriber subscriber, IResource[] roots) { + this.roots = roots; + this.subscriber = subscriber; + Assert.isNotNull(subscriber); + this.eventHandler = new SubscriberEventHandler(subscriber); + this.subscriberInput = eventHandler.getSyncSetInput(); + ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); + subscriber.addListener(this); + + // TODO: optimize and don't use working set if no roots are passed in + workingSetInput = new WorkingSetSyncSetInput(subscriberInput.getSyncSet(), getEventHandler()); + filteredInput = new SyncSetInputFromSyncSet(workingSetInput.getSyncSet(), getEventHandler()); + filteredInput.setFilter(new SyncInfoFilter() { + public boolean select(SyncInfo info, IProgressMonitor monitor) { + return true; + } + }); + } + + public void setProgressGroup(IProgressMonitor monitor, int ticks) { + getEventHandler().setProgressGroupHint(monitor, ticks); + } + + /** + * Start the collector. + */ + public void start() { + eventHandler.start(); + } + + /** + * Return the set that provides access to the out-of-sync resources for the collector's + * subscriber that are descendants of the roots for the collector, + * are in the collector's working set and match the collectors filter. + * @see getSubscriberSyncInfoSet() + * @see getWorkingSetSyncInfoSet() + * @return a SyncInfoSet containing out-of-sync resources + */ + public SyncInfoTree getSyncInfoTree() { + return filteredInput.getSyncSet(); + } + + /** + * This causes the calling thread to wait any background collection of out-of-sync resources + * to stop before returning. + * @param monitor a progress monitor + */ + public void waitForCollector(IProgressMonitor monitor) { + monitor.worked(1); + // wait for the event handler to process changes. + while(eventHandler.getEventHandlerJob().getState() != Job.NONE) { + monitor.worked(1); + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } + } + monitor.worked(1); + } + + /** + * Clears this collector's sync info sets and causes them to be recreated from the + * associated <code>Subscriber</code>. The reset will occur in the background. If the + * caller wishes to wait for the reset to complete, they should call + * {@link waitForCollector(IProgressMonitor)}. + */ + public void reset() { + eventHandler.reset(getRoots()); + } + + /** + * Returns the <code>Subscriber</code> associated with this collector. + * + * @return the <code>Subscriber</code> associated with this collector. + */ + public Subscriber getSubscriber() { + return subscriber; + } + + /** + * Disposes of the background job associated with this collector and deregisters + * all it's listeners. This method must be called when the collector is no longer + * referenced and could be garbage collected. + */ + public void dispose() { + eventHandler.shutdown(); + subscriberInput.disconnect(); + workingSetInput.disconnect(); + if(filteredInput != null) { + filteredInput.disconnect(); + } + getSubscriber().removeListener(this); + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + } + + /** + * Process the resource delta and posts all necessary events to the background + * event handler. + * + * @param delta the resource delta to analyse + */ + private void processDelta(IResourceDelta delta) { + IResource resource = delta.getResource(); + int kind = delta.getKind(); + + if (resource.getType() == IResource.PROJECT) { + // Handle a deleted project + if (((kind & IResourceDelta.REMOVED) != 0)) { + eventHandler.remove(resource); + return; + } + // Handle a closed project + if ((delta.getFlags() & IResourceDelta.OPEN) != 0 && !((IProject) resource).isOpen()) { + eventHandler.remove(resource); + return; + } + // Only interested in projects mapped to the provider + if (!isAncestorOfRoot(resource)) { + // If the project has any entries in the sync set, remove them + if (getSyncInfoTree().hasMembers(resource)) { + eventHandler.remove(resource); + } + return; + } + } + + boolean visitChildren = false; + if (isDescendantOfRoot(resource)) { + visitChildren = true; + // If the resource has changed type, remove the old resource handle + // and add the new one + if ((delta.getFlags() & IResourceDelta.TYPE) != 0) { + eventHandler.remove(resource); + eventHandler.change(resource, IResource.DEPTH_INFINITE); + } + + // Check the flags for changes the SyncSet cares about. + // Notice we don't care about MARKERS currently. + int changeFlags = delta.getFlags(); + if ((changeFlags & (IResourceDelta.OPEN | IResourceDelta.CONTENT)) != 0) { + eventHandler.change(resource, IResource.DEPTH_ZERO); + } + + // Check the kind and deal with those we care about + if ((delta.getKind() & (IResourceDelta.REMOVED | IResourceDelta.ADDED)) != 0) { + eventHandler.change(resource, IResource.DEPTH_ZERO); + } + } + + // Handle changed children + if (visitChildren || isAncestorOfRoot(resource)) { + IResourceDelta[] affectedChildren = delta.getAffectedChildren(IResourceDelta.CHANGED | IResourceDelta.REMOVED | IResourceDelta.ADDED); + for (int i = 0; i < affectedChildren.length; i++) { + processDelta(affectedChildren[i]); + } + } + } + + private boolean isAncestorOfRoot(IResource parent) { + // Always traverse into projects in case a root was removed + if (parent.getType() == IResource.ROOT) return true; + IResource[] roots = getRoots(); + for (int i = 0; i < roots.length; i++) { + IResource resource = roots[i]; + if (parent.getFullPath().isPrefixOf(resource.getFullPath())) { + return true; + } + } + return false; + } + + private boolean isDescendantOfRoot(IResource resource) { + IResource[] roots = getRoots(); + for (int i = 0; i < roots.length; i++) { + IResource root = roots[i]; + if (root.getFullPath().isPrefixOf(resource.getFullPath())) { + return true; + } + } + return false; + } + + /** + * Return the roots that are being considered by this collector. + * By default, the collector is interested in the roots of its + * subscriber. However, the set can be reduced using {@link setRoots(IResource)). + * @return + */ + public IResource[] getRoots() { + if (roots == null) { + return getSubscriber().roots(); + } else { + return roots; + } + } + + /* + * Returns whether the collector is configured to collect for + * all roots of the subscriber or not + * @return <code>true</code> if the collector is considering all + * roots of the subscriber and <code>false</code> otherwise + */ + private boolean isAllRootsIncluded() { + return roots == null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) + */ + public void resourceChanged(IResourceChangeEvent event) { + processDelta(event.getDelta()); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.team.core.sync.ITeamResourceChangeListener#teamResourceChanged(org.eclipse.team.core.sync.TeamDelta[]) + */ + public void subscriberResourceChanged(ISubscriberChangeEvent[] deltas) { + for (int i = 0; i < deltas.length; i++) { + switch (deltas[i].getFlags()) { + case ISubscriberChangeEvent.SYNC_CHANGED : + if (isAllRootsIncluded() || isDescendantOfRoot(deltas[i].getResource())) { + eventHandler.change(deltas[i].getResource(), IResource.DEPTH_ZERO); + } + break; + case ISubscriberChangeEvent.ROOT_REMOVED : + eventHandler.remove(deltas[i].getResource()); + break; + case ISubscriberChangeEvent.ROOT_ADDED : + if (isAllRootsIncluded() || isDescendantOfRoot(deltas[i].getResource())) { + eventHandler.change(deltas[i].getResource(), IResource.DEPTH_INFINITE); + } + break; + } + } + } + + /** + * Set the roots that are to be considered by the collector. The provided + * resources should be either a subset of the roots of the collector's subscriber + * or children of those roots. Other resources can be provided but will be ignored. + * Setting the roots to <code>null</code> will cause the roots of the subscriber + * to be used + * @param roots The roots to be considered or <code>null</code>. + */ + public void setRoots(IResource[] roots) { + this.roots = roots; + } + + /** + * Return the event handler that performs the background processing for this collector. + * The event handler also serves the purpose of serializing the modifications and adjustments + * to the collector's sync sets in order to ensure that the state of the sets is kept + * consistent. + * @return Returns the eventHandler. + */ + protected SubscriberEventHandler getEventHandler() { + return eventHandler; + } + + /** + * Set the working set resources used to filter the output <code>SyncInfoSet</code>. + * @see getWorkingSetSyncInfoSet() + * @param resources the working set resources + */ + public void setWorkingSet(IResource[] resources) { + workingSetInput.setWorkingSet(resources); + workingSetInput.reset(); + } + + /** + * Get th working set resources used to filter the output sync info set. + * @return the working set resources + */ + public IResource[] getWorkingSet() { + return workingSetInput.getWorkingSet(); + } + + /** + * Set the filter for this collector. Only elements that match the filter will + * be in the out sync info set. + * @see getSyncInfoSet() + * @param filter the sync info filter + */ + public void setFilter(SyncInfoFilter filter) { + filteredInput.setFilter(filter); + filteredInput.reset(); + } + + /** + * Return the filter that is filtering the output of this collector. + * @return a sync info filter + */ + public SyncInfoFilter getFilter() { + if(filteredInput != null) { + return filteredInput.getFilter(); + } + return null; + } + + /** + * Return a <code>SyncInfoSet</code> that contains the out-of-sync elements + * from the subsciber sync info set filtered + * by the working set resources but not the collector's <code>SyncInfoFilter</code>. + * @see getSubscriberSyncInfoSet() + * @return a <code>SyncInfoSet</code> + */ + public SyncInfoSet getWorkingSetSyncInfoSet() { + return workingSetInput.getSyncSet(); + } + + /** + * Return the <code>SyncInfoSet</code> that contains all the all the out-of-sync resources for the + * subscriber that are descendants of the roots of this collector. The set will contain only those resources that are children of the roots + * of the collector unless the roots of the colletor has been set to <code>null</code> + * in which case all out-of-sync resources from the subscriber are collected. + * @return the subscriber sync info set + */ + public SyncInfoSet getSubscriberSyncInfoSet() { + return subscriberInput.getSyncSet(); + } + +} |