/*******************************************************************************
* Copyright (c) 2010, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
******************************************************************************/
package org.eclipse.buckminster.team;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.buckminster.core.actor.AbstractActor;
import org.eclipse.buckminster.core.actor.IActionContext;
import org.eclipse.buckminster.core.metadata.WorkspaceInfo;
import org.eclipse.buckminster.runtime.MonitorUtils;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
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.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.mapping.ISynchronizationScope;
import org.eclipse.team.core.mapping.provider.SynchronizationScopeManager;
/**
* A class capturing the common functionality of team actors (actors performing
* SCM related actions).
*
* @author michal.ruzicka@cloudsmith.com
*/
public abstract class AbstractTeamActor<C extends TeamPerformContext> extends AbstractActor {
public static IStatus createMultiStatus(String message, IStatus[] statuses) {
return new MultiStatus(BuckminsterTeam.PLUGIN_ID, IStatus.ERROR, statuses, message, null);
}
public static IStatus createStatus(String message) {
return createStatus(message, null);
}
public static IStatus createStatus(String message, Throwable t) {
return new Status(IStatus.ERROR, BuckminsterTeam.PLUGIN_ID, message, t);
}
public static IStatus createStatus(Throwable t) {
return createStatus(t.getMessage(), t);
}
protected static ResourceMapping getResourceMapping(Object o) {
if (o instanceof ResourceMapping) {
return (ResourceMapping) o;
}
if (o instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) o;
Object adapted = adaptable.getAdapter(ResourceMapping.class);
if (adapted instanceof ResourceMapping) {
return (ResourceMapping) adapted;
}
} else {
Object adapted = Platform.getAdapterManager().getAdapter(o, ResourceMapping.class);
if (adapted instanceof ResourceMapping) {
return (ResourceMapping) adapted;
}
}
return null;
}
/**
* Return a short description of this action.
*
* @param actionContext
* the context of this action
* @return the short description of this action
*/
public abstract String getActionName(IActionContext actionContext);
protected SynchronizationScopeManager createScopeManager(String scopeName, ResourceMapping[] resourceMappings) {
return new SynchronizationScopeManager(scopeName, resourceMappings, ResourceMappingContext.LOCAL_CONTEXT, true);
}
/**
* Create a team perform context from the given action context. This
* typically includes processing the actor properties and creating a team
* perform context specific to the actor implementing this method.
*
* @param actionContext
* the action context to use when building the team perform
* context
* @return the team perform context for the given <code>actionContext</code>
* @throws CoreException
*/
protected abstract C createTeamPerformContext(IActionContext actionContext) throws CoreException;
protected Map<RepositoryProvider, ProviderResources> getProviderResourcesMap(C teamPerformContext, ISynchronizationScope scope)
throws CoreException {
HashMap<RepositoryProvider, ProviderResources> result = new HashMap<RepositoryProvider, ProviderResources>();
for (ResourceMapping mapping : scope.getMappings()) {
ResourceTraversal[] traversals = scope.getTraversals(mapping);
for (IProject project : mapping.getProjects()) {
RepositoryProvider provider = RepositoryProvider.getProvider(project);
if (provider != null && teamPerformContext.getReaderTypeForRepositoryProvider(provider.getID()) != null) {
ProviderResources providerResources = result.get(provider);
if (providerResources == null) {
providerResources = new ProviderResources(provider);
result.put(provider, providerResources);
}
providerResources.add(traversals);
}
}
}
return result;
}
/**
* Return a short description of an activity performed by this actor on the
* given repository provider.
*
* @param provider
* the repository provider being operated on by this actor
* @return the short description of the activity performed by this actor on
* the given <code>provider</code>
*/
protected abstract String getTaskName(RepositoryProvider provider);
@Override
protected IStatus internalPerform(IActionContext actionContext, IProgressMonitor monitor) throws CoreException {
monitor.beginTask(getActionName(actionContext), 10000);
try {
C teamPerformContext = createTeamPerformContext(actionContext);
IProject[] projects = WorkspaceInfo.getProjectsInResolution(actionContext.getCSpec().getComponentIdentifier());
int workAmount = 10000 / (projects.length + 1);
monitor.worked(workAmount);
ArrayList<IStatus> statuses = null;
for (IProject project : projects) {
try {
internalTeamPerform(teamPerformContext, project, MonitorUtils.subMonitor(monitor, workAmount));
} catch (CoreException ce) {
teamPerformContext.collectStatus(ce.getStatus());
} catch (Throwable t) {
teamPerformContext.collectStatus(createStatus(t));
} finally {
if (teamPerformContext.hasErrors()) {
if (statuses == null)
statuses = new ArrayList<IStatus>();
statuses.add(teamPerformContext.collectedStatus(NLS.bind(Messages.problems_during_0, NLS.bind(Messages.processing_of_0,
project.getName()))));
}
}
}
if (statuses == null)
return Status.OK_STATUS;
return createMultiStatus(NLS.bind(Messages.problems_during_0, getActionName(actionContext)), statuses
.toArray(new IStatus[statuses.size()]));
} catch (CoreException ce) {
throw ce;
} catch (Throwable t) {
throw new CoreException(createStatus(t));
} finally {
monitor.done();
}
}
protected void internalTeamPerform(C teamPerformContext, IProject project, IProgressMonitor monitor) throws CoreException, InterruptedException {
ResourceMapping resourceMapping = getResourceMapping(project);
monitor.beginTask(NLS.bind(Messages.processing_project_0, project.getName()), 1100);
try {
if (shouldExclude(teamPerformContext, project))
return;
if (resourceMapping != null) {
SynchronizationScopeManager scopeManager = createScopeManager("Scope manager for " + project.getName(), //$NON-NLS-1$
new ResourceMapping[] { resourceMapping });
scopeManager.initialize(MonitorUtils.subMonitor(monitor, 100));
Map<RepositoryProvider, ProviderResources> providerResourcesMap = getProviderResourcesMap(teamPerformContext, scopeManager.getScope());
processProviderResourcesMap(teamPerformContext, providerResourcesMap, MonitorUtils.subMonitor(monitor, 1000));
}
} finally {
monitor.done();
}
}
/**
* Process any non-traversed (depth-zero) resources that were in the logical
* model that primed this action.
*
* @param teamPerformContext
* the team perform context of this action
* @param provider
* the repository provider associated with the project containing
* the folders
* @param nontraversedFolders
* the folders
* @param monitor
* a progress monitor
*/
protected void processNontraversedResources(C teamPerformContext, RepositoryProvider provider, IResource[] nontraversedFolders,
IProgressMonitor monitor) throws CoreException, InterruptedException {
// Default is to do nothing
}
/**
* Process resources from the given {@link ProviderResources}.
*
* @param teamPerformContext
* the team perform context of this action
* @param providerResources
* a set of resources to process along with a repository provider
* they are associated with
* @param monitor
* a progress monitor
* @throws CoreException
* @throws InterruptedException
*/
protected void processProviderResources(C teamPerformContext, ProviderResources providerResources, IProgressMonitor monitor)
throws CoreException, InterruptedException {
RepositoryProvider provider = providerResources.getProvider();
IResource[] deepResources = providerResources.getDeepResources();
IResource[] shallowResources = providerResources.getShallowResources();
IResource[] nontraversedFolders = providerResources.getNontraversedFolders();
int workAmount = (deepResources.length > 0 ? 100 : 0) + (shallowResources.length > 0 ? 100 : 0) + (nontraversedFolders.length > 0 ? 10 : 0);
monitor.beginTask(getTaskName(provider), workAmount);
try {
if (workAmount == 0)
return;
final ISchedulingRule rule = provider.getProject();
try {
Job.getJobManager().beginRule(rule, monitor);
if (deepResources.length > 0)
processResources(teamPerformContext, provider, deepResources, true /* recurse */, MonitorUtils.subMonitor(monitor, 100));
if (shallowResources.length > 0)
processResources(teamPerformContext, provider, shallowResources, false /* recurse */, MonitorUtils.subMonitor(monitor, 100));
if (nontraversedFolders.length > 0)
processNontraversedResources(teamPerformContext, provider, nontraversedFolders, MonitorUtils.subMonitor(monitor, 10));
} finally {
Job.getJobManager().endRule(rule);
}
} finally {
monitor.done();
}
}
protected void processProviderResourcesMap(C teamPerformContext, Map<RepositoryProvider, ProviderResources> providerResourcesMap,
IProgressMonitor monitor) throws CoreException, InterruptedException {
Set<Entry<RepositoryProvider, ProviderResources>> entrySet = providerResourcesMap.entrySet();
monitor.beginTask(null, entrySet.size() * 1000);
for (Entry<RepositoryProvider, ProviderResources> entry : entrySet) {
ProviderResources providerResources = entry.getValue();
monitor.setTaskName(getTaskName(providerResources.getProvider()));
processProviderResources(teamPerformContext, providerResources, MonitorUtils.subMonitor(monitor, 1000));
}
monitor.done();
}
/**
* Process the given resources.
*
* @param teamPerformContext
* the context to use when processing the resources
* @param provider
* a repository provider the given <code>resources</code> are
* associated with
* @param resources
* the resources to process
* @param recurse
* whether the processing should be deep or shallow
* @param monitor
* a progress monitor
* @throws CoreException
* @throws InterruptedException
*/
protected abstract void processResources(C teamPerformContext, RepositoryProvider provider, IResource[] resources, boolean recurse,
IProgressMonitor monitor) throws CoreException, InterruptedException;
/**
* Determine whether the given <code>resource</code> should be excluded from
* processing.
*
* @param teamPerformContext
* the context in which to determine whether the
* <code>resource</code> should be processed or not
* @param resource
* the resource to be examined
* @return <code>true</code> if the resource should be excluded from
* processing, <code>false</code> otherwise
*/
protected boolean shouldExclude(C teamPerformContext, IResource resource) {
return false;
}
}