package org.eclipse.team.core.mapping.provider;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.mapping.IModelProviderDescriptor;
import org.eclipse.core.resources.mapping.ModelProvider;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.resources.mapping.ResourceMappingContext;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.team.core.mapping.ISynchronizationScope;
import org.eclipse.team.core.mapping.ISynchronizationScopeManager;
import org.eclipse.team.core.subscribers.SubscriberScopeManager;
import org.eclipse.team.internal.core.Policy;
import org.eclipse.team.internal.core.mapping.CompoundResourceTraversal;
import org.eclipse.team.internal.core.mapping.ResourceMappingScope;
import org.eclipse.team.internal.core.mapping.ScopeChangeEvent;
import org.eclipse.team.internal.core.mapping.ScopeManagerEventHandler;
public class SynchronizationScopeManager extends PlatformObject implements ISynchronizationScopeManager {
private static final int MAX_ITERATION = 10;
private final ResourceMappingContext context;
private final boolean consultModels;
private ISynchronizationScope scope;
private boolean initialized;
private ScopeManagerEventHandler handler;
private final String name;
public static ResourceMapping[] getMappingsFromProviders(ResourceTraversal[] traversals,
ResourceMappingContext context,
IProgressMonitor monitor) throws CoreException {
Set<ResourceMapping> result = new HashSet<>();
IModelProviderDescriptor[] descriptors = ModelProvider
.getModelProviderDescriptors();
for (IModelProviderDescriptor descriptor : descriptors) {
ResourceMapping[] mappings = getMappings(descriptor, traversals,
context, monitor);
result.addAll(Arrays.asList(mappings));
Policy.checkCanceled(monitor);
}
return result.toArray(new ResourceMapping[result.size()]);
}
private static ResourceMapping[] getMappings(IModelProviderDescriptor descriptor,
ResourceTraversal[] traversals,
ResourceMappingContext context, IProgressMonitor monitor)
throws CoreException {
ResourceTraversal[] matchingTraversals = descriptor.getMatchingTraversals(
traversals);
return descriptor.getModelProvider().getMappings(matchingTraversals,
context, monitor);
}
public SynchronizationScopeManager(String name, ResourceMapping[] inputMappings, ResourceMappingContext resourceMappingContext, boolean consultModels) {
this.name = name;
this.context = resourceMappingContext;
this.consultModels = consultModels;
scope = createScope(inputMappings);
}
@Override
public boolean isInitialized() {
return initialized;
}
public ISchedulingRule getSchedulingRule() {
Set<IProject> projects = new HashSet<>();
ResourceMapping[] mappings = scope.getInputMappings();
for (ResourceMapping mapping : mappings) {
Object modelObject = mapping.getModelObject();
if (modelObject instanceof IResource) {
IResource resource = (IResource) modelObject;
if (resource.getType() == IResource.ROOT)
return ResourcesPlugin.getWorkspace().getRoot();
projects.add(resource.getProject());
} else {
return ResourcesPlugin.getWorkspace().getRoot();
}
}
return MultiRule.combine(projects.toArray(new IProject[projects.size()]));
}
@Override
public void initialize(
IProgressMonitor monitor) throws CoreException {
ResourcesPlugin.getWorkspace().run((IWorkspaceRunnable) monitor1 -> internalPrepareContext(monitor1),
getSchedulingRule(), IResource.NONE, monitor);
}
@Override
public ResourceTraversal[] refresh(final ResourceMapping[] mappings, IProgressMonitor monitor) throws CoreException {
final ResourceTraversal[][] traversals = new ResourceTraversal[][] { new ResourceTraversal[0] };
IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.run((IWorkspaceRunnable) monitor1 -> traversals[0] = internalRefreshScope(mappings, true, monitor1),
getSchedulingRule(), IResource.NONE, monitor);
return traversals[0];
}
private void internalPrepareContext(IProgressMonitor monitor) throws CoreException {
if (initialized)
return;
monitor.beginTask(null, IProgressMonitor.UNKNOWN);
((ResourceMappingScope)scope).reset();
ResourceMapping[] targetMappings = scope.getInputMappings();
ResourceTraversal[] newTraversals;
boolean firstTime = true;
boolean hasAdditionalResources = false;
int count = 0;
do {
Policy.checkCanceled(monitor);
newTraversals = addMappingsToScope(targetMappings,
Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
if (newTraversals.length > 0 && consultModels) {
ResourceTraversal[] adjusted = adjustInputTraversals(newTraversals);
targetMappings = getMappingsFromProviders(adjusted,
context,
Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
if (firstTime) {
firstTime = false;
} else if (!hasAdditionalResources) {
hasAdditionalResources = newTraversals.length != 0;
}
}
} while (consultModels & newTraversals.length != 0 && count++ < MAX_ITERATION);
setHasAdditionalMappings(scope, consultModels && internalHasAdditionalMappings());
setHasAdditionalResources(consultModels && hasAdditionalResources);
monitor.done();
initialized = true;
fireMappingsChangedEvent(scope.getMappings(), scope.getTraversals());
}
private ResourceTraversal[] internalRefreshScope(ResourceMapping[] mappings, boolean checkForContraction, IProgressMonitor monitor) throws CoreException {
monitor.beginTask(null, 100 * mappings.length + 100);
ScopeChangeEvent change = new ScopeChangeEvent(scope);
CompoundResourceTraversal refreshTraversals = new CompoundResourceTraversal();
CompoundResourceTraversal removedTraversals = new CompoundResourceTraversal();
for (ResourceMapping mapping : mappings) {
ResourceTraversal[] previousTraversals = scope.getTraversals(mapping);
ResourceTraversal[] mappingTraversals = mapping.getTraversals(
context, Policy.subMonitorFor(monitor, 100));
refreshTraversals.addTraversals(mappingTraversals);
ResourceTraversal[] uncovered = getUncoveredTraversals(mappingTraversals);
if (checkForContraction && previousTraversals != null && previousTraversals.length > 0) {
ResourceTraversal[] removed = getUncoveredTraversals(mappingTraversals, previousTraversals);
removedTraversals.addTraversals(removed);
}
if (uncovered.length > 0) {
change.setExpanded(true);
ResourceTraversal[] result = performExpandScope(mapping, mappingTraversals, uncovered, monitor);
refreshTraversals.addTraversals(result);
}
}
if (checkForContraction && removedTraversals.getRoots().length > 0) {
((ResourceMappingScope)scope).reset();
internalRefreshScope(scope.getInputMappings(), false, monitor);
change.setContracted(true);
}
if (change.shouldFireChange())
fireMappingsChangedEvent(change.getChangedMappings(), change.getChangedTraversals(refreshTraversals));
monitor.done();
return refreshTraversals.asTraversals();
}
private ResourceTraversal[] getUncoveredTraversals(
ResourceTraversal[] newTraversals,
ResourceTraversal[] previousTraversals) {
CompoundResourceTraversal t = new CompoundResourceTraversal();
t.addTraversals(newTraversals);
return t.getUncoveredTraversals(previousTraversals);
}
private ResourceTraversal[] performExpandScope(
ResourceMapping mapping, ResourceTraversal[] mappingTraversals,
ResourceTraversal[] uncovered, IProgressMonitor monitor)
throws CoreException {
ResourceMapping ancestor = findAncestor(mapping);
if (ancestor == null) {
uncovered = addMappingToScope(mapping, mappingTraversals);
addResourcesToScope(uncovered, monitor);
return mappingTraversals;
} else {
ResourceTraversal[] ancestorTraversals = ancestor.getTraversals(
context, Policy.subMonitorFor(monitor, 100));
uncovered = addMappingToScope(ancestor, ancestorTraversals);
addResourcesToScope(uncovered, monitor);
return ancestorTraversals;
}
}
private ResourceMapping findAncestor(ResourceMapping mapping) {
ResourceMapping[] mappings = scope.getMappings(mapping.getModelProviderId());
for (ResourceMapping m : mappings) {
if (m.contains(mapping)) {
return m;
}
}
return null;
}
private ResourceTraversal[] getUncoveredTraversals(ResourceTraversal[] traversals) {
return ((ResourceMappingScope)scope).getCompoundTraversal().getUncoveredTraversals(traversals);
}
private void addResourcesToScope(ResourceTraversal[] newTraversals, IProgressMonitor monitor) throws CoreException {
if (!consultModels)
return;
ResourceMapping[] targetMappings;
int count = 0;
do {
ResourceTraversal[] adjusted = adjustInputTraversals(newTraversals);
targetMappings = getMappingsFromProviders(adjusted,
context, Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
newTraversals = addMappingsToScope(targetMappings,
Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
} while (newTraversals.length != 0 && count++ < MAX_ITERATION);
if (!scope.hasAdditionalMappings()) {
setHasAdditionalMappings(scope, internalHasAdditionalMappings());
}
if (!scope.hasAdditonalResources()) {
setHasAdditionalResources(true);
}
}
private void fireMappingsChangedEvent(ResourceMapping[] newMappings, ResourceTraversal[] newTraversals) {
((ResourceMappingScope)scope).fireTraversalsChangedEvent(newTraversals, newMappings);
}
protected final void setHasAdditionalMappings(
ISynchronizationScope scope, boolean hasAdditionalMappings) {
((ResourceMappingScope)scope).setHasAdditionalMappings(hasAdditionalMappings);
}
protected final void setHasAdditionalResources(boolean hasAdditionalResources) {
((ResourceMappingScope)scope).setHasAdditionalResources(hasAdditionalResources);
}
protected final ISynchronizationScope createScope(
ResourceMapping[] inputMappings) {
return new ResourceMappingScope(inputMappings, this);
}
protected ResourceTraversal[] adjustInputTraversals(ResourceTraversal[] traversals) {
return traversals;
}
private ResourceTraversal[] addMappingsToScope(
ResourceMapping[] targetMappings,
IProgressMonitor monitor) throws CoreException {
CompoundResourceTraversal result = new CompoundResourceTraversal();
ResourceMappingContext context = this.context;
for (ResourceMapping mapping : targetMappings) {
if (scope.getTraversals(mapping) == null) {
ResourceTraversal[] traversals = mapping.getTraversals(context,
Policy.subMonitorFor(monitor, 100));
ResourceTraversal[] newOnes = addMappingToScope(mapping, traversals);
result.addTraversals(newOnes);
}
Policy.checkCanceled(monitor);
}
return result.asTraversals();
}
protected final ResourceTraversal[] addMappingToScope(
ResourceMapping mapping, ResourceTraversal[] traversals) {
return ((ResourceMappingScope)scope).addMapping(mapping, traversals);
}
private boolean internalHasAdditionalMappings() {
ResourceMapping[] inputMappings = scope.getInputMappings();
ResourceMapping[] mappings = scope.getMappings();
if (inputMappings.length == mappings.length) {
Set<ResourceMapping> testSet = new HashSet<>();
Collections.addAll(testSet, mappings);
for (ResourceMapping mapping : inputMappings) {
if (!testSet.contains(mapping)) {
return true;
}
}
return false;
}
return true;
}
public ResourceMappingContext getContext() {
return context;
}
@Override
public ISynchronizationScope getScope() {
return scope;
}
@Override
public void dispose() {
if (handler != null)
handler.shutdown();
}
public void refresh(ResourceMapping[] mappings) {
getHandler().refresh(mappings);
}
private synchronized ScopeManagerEventHandler getHandler() {
if (handler == null)
handler = new ScopeManagerEventHandler(this);
return handler;
}
public String getName() {
return name;
}
}