Copyright (c) 2000, 2017 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.team.internal.core.subscribers; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.osgi.util.NLS; import org.eclipse.team.core.TeamException; import org.eclipse.team.core.diff.IDiff; import org.eclipse.team.core.mapping.provider.ResourceDiffTree; import org.eclipse.team.core.subscribers.Subscriber; import org.eclipse.team.internal.core.BackgroundEventHandler; import org.eclipse.team.internal.core.Messages; import org.eclipse.team.internal.core.Policy; import org.eclipse.team.internal.core.TeamPlugin; import org.osgi.service.prefs.Preferences;
This class manages the active change sets associated with a subscriber.
/** * This class manages the active change sets associated with a subscriber. */
public class SubscriberChangeSetManager extends ActiveChangeSetManager { private static final String PREF_CHANGE_SETS = "changeSets"; //$NON-NLS-1$ private static final int RESOURCE_REMOVAL = 1; private static final int RESOURCE_CHANGE = 2; private EventHandler handler; private ResourceCollector collector; /* * Background event handler for serializing and batching change set changes */ private class EventHandler extends BackgroundEventHandler { private List<Event> dispatchEvents = new ArrayList<>(); protected EventHandler(String jobName, String errorTitle) { super(jobName, errorTitle); } @Override protected void processEvent(Event event, IProgressMonitor monitor) throws CoreException { // Handle everything in the dispatch if (isShutdown()) throw new OperationCanceledException(); dispatchEvents.add(event); } @Override protected boolean doDispatchEvents(IProgressMonitor monitor) throws TeamException { if (dispatchEvents.isEmpty()) { return false; } if (isShutdown()) throw new OperationCanceledException(); ResourceDiffTree[] locked = null; try { locked = beginDispath(); for (Event event : dispatchEvents) { switch (event.getType()) { case RESOURCE_REMOVAL: handleRemove(event.getResource()); break; case RESOURCE_CHANGE: handleChange(event.getResource(), ((ResourceEvent)event).getDepth()); break; default: break; } if (isShutdown()) throw new OperationCanceledException(); } } catch (CoreException e) { throw TeamException.asTeamException(e); } finally { try { endDispatch(locked, monitor); } finally { dispatchEvents.clear(); } } return true; } /* * Begin input on all the sets and return the sync sets that were * locked. If this method throws an exception then the client * can assume that no sets were locked */ private ResourceDiffTree[] beginDispath() { ChangeSet[] sets = getSets(); List<ResourceDiffTree> lockedSets = new ArrayList<>(); try { for (ChangeSet s : sets) { ActiveChangeSet set = (ActiveChangeSet) s; ResourceDiffTree tree = set.internalGetDiffTree(); lockedSets.add(tree); tree.beginInput(); } return lockedSets.toArray(new ResourceDiffTree[lockedSets.size()]); } catch (RuntimeException e) { try { for (ResourceDiffTree tree : lockedSets) { try { tree.endInput(null); } catch (Throwable e1) { // Ignore so that original exception is not masked } } } catch (Throwable e1) { // Ignore so that original exception is not masked } throw e; } } private void endDispatch(ResourceDiffTree[] locked, IProgressMonitor monitor) { if (locked == null) { // The begin failed so there's nothing to unlock return; } monitor.beginTask(null, 100 * locked.length); for (ResourceDiffTree tree : locked) { try { tree.endInput(Policy.subMonitorFor(monitor, 100)); } catch (RuntimeException e) { // Don't worry about ending every set if an error occurs. // Instead, log the error and suggest a restart. TeamPlugin.log(IStatus.ERROR, Messages.SubscriberChangeSetCollector_0, e); throw e; } } monitor.done(); } @Override protected synchronized void queueEvent(Event event, boolean front) { // Override to allow access from enclosing class super.queueEvent(event, front); } /* * Handle the removal */ private void handleRemove(IResource resource) { ChangeSet[] sets = getSets(); for (ChangeSet set : sets) { // This will remove any descendants from the set and callback to // resourcesChanged which will batch changes if (!set.isEmpty()) { set.rootRemoved(resource, IResource.DEPTH_INFINITE); if (set.isEmpty()) { remove(set); } } } } /* * Handle the change */ private void handleChange(IResource resource, int depth) throws CoreException { IDiff diff = getDiff(resource); if (isModified(diff)) { ActiveChangeSet[] containingSets = getContainingSets(resource); if (containingSets.length == 0) { // Consider for inclusion in the default set // if the resource is not already a member of another set if (getDefaultSet() != null) { getDefaultSet().add(diff); } } else { for (ActiveChangeSet set : containingSets) { // Update the sync info in the set set.add(diff); } } } else { removeFromAllSets(resource); } if (depth != IResource.DEPTH_ZERO) { IResource[] members = getSubscriber().members(resource); for (IResource member : members) { handleChange(member, depth == IResource.DEPTH_ONE ? IResource.DEPTH_ZERO : IResource.DEPTH_INFINITE); } } } private void removeFromAllSets(IResource resource) { List<ChangeSet> toRemove = new ArrayList<>(); ChangeSet[] sets = getSets(); for (ChangeSet set : sets) { if (set.contains(resource)) { set.remove(resource); if (set.isEmpty()) { toRemove.add(set); } } } for (Object element : toRemove) { ActiveChangeSet set = (ActiveChangeSet) element; remove(set); } } private ActiveChangeSet[] getContainingSets(IResource resource) { Set<ActiveChangeSet> result = new HashSet<>(); ChangeSet[] sets = getSets(); for (ChangeSet set : sets) { if (set.contains(resource)) { result.add((ActiveChangeSet) set); } } return result.toArray(new ActiveChangeSet[result.size()]); } } private class ResourceCollector extends SubscriberResourceCollector { public ResourceCollector(Subscriber subscriber) { super(subscriber); } @Override protected void remove(IResource resource) { if (handler != null) handler.queueEvent(new BackgroundEventHandler.ResourceEvent(resource, RESOURCE_REMOVAL, IResource.DEPTH_INFINITE), false); } @Override protected void change(IResource resource, int depth) { if (handler != null) handler.queueEvent(new BackgroundEventHandler.ResourceEvent(resource, RESOURCE_CHANGE, depth), false); } @Override protected boolean hasMembers(IResource resource) { return SubscriberChangeSetManager.this.hasMembers(resource); } } public SubscriberChangeSetManager(Subscriber subscriber) { collector = new ResourceCollector(subscriber); handler = new EventHandler(NLS.bind(Messages.SubscriberChangeSetCollector_1, new String[] { subscriber.getName() }), NLS.bind(Messages.SubscriberChangeSetCollector_2, new String[] { subscriber.getName() })); // } @Override protected void initializeSets() { load(getPreferences()); } public boolean hasMembers(IResource resource) { ChangeSet[] sets = getSets(); for (ChangeSet s : sets) { ActiveChangeSet set = (ActiveChangeSet) s; if (set.getDiffTree().getChildren(resource.getFullPath()).length > 0) return true; } if (getDefaultSet() != null) return (getDefaultSet().getDiffTree().getChildren(resource.getFullPath()).length > 0); return false; }
Return the sync info for the given resource obtained from the subscriber.
Params:
  • resource – the resource
Throws:
Returns:the sync info for the resource
/** * Return the sync info for the given resource obtained * from the subscriber. * @param resource the resource * @return the sync info for the resource * @throws CoreException */
@Override public IDiff getDiff(IResource resource) throws CoreException { Subscriber subscriber = getSubscriber(); return subscriber.getDiff(resource); }
Return the subscriber associated with this collector.
Returns:the subscriber associated with this collector
/** * Return the subscriber associated with this collector. * @return the subscriber associated with this collector */
public Subscriber getSubscriber() { return collector.getSubscriber(); } @Override public void dispose() { handler.shutdown(); collector.dispose(); super.dispose(); save(getPreferences()); } private Preferences getPreferences() { return getParentPreferences().node(getSubscriberIdentifier()); } private static Preferences getParentPreferences() { return getTeamPreferences().node(PREF_CHANGE_SETS); } private static Preferences getTeamPreferences() { return InstanceScope.INSTANCE.getNode(TeamPlugin.getPlugin().getBundle().getSymbolicName()); }
Return the id that will uniquely identify the subscriber across restarts.
Returns:the id that will uniquely identify the subscriber across
/** * Return the id that will uniquely identify the subscriber across * restarts. * @return the id that will uniquely identify the subscriber across */
protected String getSubscriberIdentifier() { return getSubscriber().getName(); }
Wait until the collector is done processing any events. This method is for testing purposes only.
Params:
  • monitor –
/** * Wait until the collector is done processing any events. * This method is for testing purposes only. * @param monitor */
public void waitUntilDone(IProgressMonitor monitor) { monitor.worked(1); // wait for the event handler to process changes. while(handler.getEventHandlerJob().getState() != Job.NONE) { monitor.worked(1); try { Thread.sleep(10); } catch (InterruptedException e) { } Policy.checkCanceled(monitor); } monitor.worked(1); } @Override protected String getName() { return getSubscriber().getName(); } }