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;
public class SubscriberChangeSetManager extends ActiveChangeSetManager {
private static final String PREF_CHANGE_SETS = "changeSets";
private static final int RESOURCE_REMOVAL = 1;
private static final int RESOURCE_CHANGE = 2;
private EventHandler handler;
private ResourceCollector collector;
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 {
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;
}
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) {
}
}
} catch (Throwable e1) {
}
throw e;
}
}
private void endDispatch(ResourceDiffTree[] locked, IProgressMonitor monitor) {
if (locked == null) {
return;
}
monitor.beginTask(null, 100 * locked.length);
for (ResourceDiffTree tree : locked) {
try {
tree.endInput(Policy.subMonitorFor(monitor, 100));
} catch (RuntimeException e) {
TeamPlugin.log(IStatus.ERROR, Messages.SubscriberChangeSetCollector_0, e);
throw e;
}
}
monitor.done();
}
@Override
protected synchronized void queueEvent(Event event, boolean front) {
super.queueEvent(event, front);
}
private void handleRemove(IResource resource) {
ChangeSet[] sets = getSets();
for (ChangeSet set : sets) {
if (!set.isEmpty()) {
set.rootRemoved(resource, IResource.DEPTH_INFINITE);
if (set.isEmpty()) {
remove(set);
}
}
}
}
private void handleChange(IResource resource, int depth) throws CoreException {
IDiff diff = getDiff(resource);
if (isModified(diff)) {
ActiveChangeSet[] containingSets = getContainingSets(resource);
if (containingSets.length == 0) {
if (getDefaultSet() != null) {
getDefaultSet().add(diff);
}
} else {
for (ActiveChangeSet set : containingSets) {
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;
}
@Override
public IDiff getDiff(IResource resource) throws CoreException {
Subscriber subscriber = getSubscriber();
return subscriber.getDiff(resource);
}
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());
}
protected String getSubscriberIdentifier() {
return getSubscriber().getName();
}
public void waitUntilDone(IProgressMonitor monitor) {
monitor.worked(1);
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();
}
}