blob: c962b4fd85a956feb78cd53d92623280673bdfc1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 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.internal.ccvs.ui.subscriber;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.SyncInfo;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSSyncInfo;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.ICVSRunnable;
import org.eclipse.team.internal.ccvs.core.client.PruneFolderVisitor;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
import org.eclipse.team.internal.ccvs.ui.Policy;
import org.eclipse.team.internal.ccvs.ui.operations.CVSBlockingRunnableContext;
import org.eclipse.team.internal.ccvs.ui.operations.CVSSubscriberNonblockingContext;
import org.eclipse.team.internal.ccvs.ui.operations.ICVSRunnableContext;
import org.eclipse.team.ui.sync.SubscriberAction;
import org.eclipse.team.ui.sync.SyncInfoSet;
public abstract class CVSSubscriberAction extends SubscriberAction {
protected boolean isOutOfSync(SyncInfo resource) {
if (resource == null) return false;
return (!(resource.getKind() == 0) || ! resource.getLocal().exists());
}
protected void makeInSync(SyncInfo[] folders) throws TeamException {
// If a node has a parent that is an incoming folder creation, we have to
// create that folder locally and set its sync info before we can get the
// node itself. We must do this for all incoming folder creations (recursively)
// in the case where there are multiple levels of incoming folder creations.
for (int i = 0; i < folders.length; i++) {
SyncInfo resource = folders[i];
makeInSync(resource);
}
}
protected void makeInSync(SyncInfo info) throws TeamException {
if (isOutOfSync(info)) {
SyncInfo parent = getParent(info);
if (parent != null) {
makeInSync(parent);
}
if (info == null) return;
if (info instanceof CVSSyncInfo) {
CVSSyncInfo cvsInfo= (CVSSyncInfo) info;
cvsInfo.makeInSync();
}
}
}
protected void makeOutgoing(SyncInfo[] folders, IProgressMonitor monitor) throws TeamException {
// If a node has a parent that is an incoming folder creation, we have to
// create that folder locally and set its sync info before we can get the
// node itself. We must do this for all incoming folder creations (recursively)
// in the case where there are multiple levels of incoming folder creations.
monitor.beginTask(null, 100 * folders.length);
for (int i = 0; i < folders.length; i++) {
SyncInfo info = folders[i];
makeOutgoing(info, Policy.subMonitorFor(monitor, 100));
}
monitor.done();
}
private void makeOutgoing(SyncInfo info, IProgressMonitor monitor) throws TeamException {
if (info == null) return;
if (info instanceof CVSSyncInfo) {
CVSSyncInfo cvsInfo= (CVSSyncInfo) info;
cvsInfo.makeOutgoing(monitor);
}
}
/**
* Handle the exception by showing an error dialog to the user.
* Sync actions seem to need to be sync-execed to work
* @param t
*/
protected void handle(Throwable t) {
CVSUIPlugin.openError(getShell(), getErrorTitle(), null, t, CVSUIPlugin.PERFORM_SYNC_EXEC | CVSUIPlugin.LOG_NONTEAM_EXCEPTIONS);
}
/**
* Return the error title that will appear in any error dialogs shown to the user
* @return
*/
protected String getErrorTitle() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
*/
public void run(IAction action) {
// TODO: Saving can change the sync state! How should this be handled?
// boolean result = saveIfNecessary();
// if (!result) return null;
SyncInfoSet syncSet = getFilteredSyncInfoSet(getFilteredSyncInfos());
if (syncSet == null || syncSet.isEmpty()) return;
try {
getCVSRunnableContext().run(getJobName(syncSet), getSchedulingRule(syncSet), true, getRunnable(syncSet));
} catch (InvocationTargetException e) {
handle(e);
} catch (InterruptedException e) {
// nothing to do;
}
}
/**
* Return an IRunnableWithProgress that will operate on the given sync set.
* This method is invoked by <code>run(IAction)</code> when the action is
* executed from a menu. The default implementation invokes the method
* <code>run(SyncInfoSet, IProgressMonitor)</code>.
* @param syncSet
* @return
*/
protected IRunnableWithProgress getRunnable(final SyncInfoSet syncSet) {
return new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
CVSWorkspaceRoot.getCVSFolderFor(ResourcesPlugin.getWorkspace().getRoot()).run(
new ICVSRunnable() {
public void run(IProgressMonitor monitor) throws CVSException {
try {
CVSSubscriberAction.this.run(syncSet, monitor);
} catch (TeamException e) {
throw CVSException.wrapException(e);
}
}
}, monitor);
} catch (TeamException e) {
throw new InvocationTargetException(e);
}
}
};
}
protected abstract void run(SyncInfoSet syncSet, IProgressMonitor monitor) throws TeamException;
/*
* Return the ICVSRunnableContext which will be used to run the operation.
*/
private ICVSRunnableContext getCVSRunnableContext() {
if (canRunAsJob() && areJobsEnabled()) {
return new CVSSubscriberNonblockingContext();
} else {
return new CVSBlockingRunnableContext(shell);
}
}
protected boolean areJobsEnabled() {
return CVSUIPlugin.getPlugin().getPreferenceStore().getBoolean(ICVSUIConstants.BACKGROUND_OPERATIONS);
}
/**
* Return the job name to be used if the action can run as a job.
*
* @param syncSet
* @return
*/
protected String getJobName(SyncInfoSet syncSet) {
return Policy.bind("CVSSubscriberAction.jobName", new Integer(syncSet.size()).toString()); //$NON-NLS-1$
}
/**
* Return a scheduling rule that includes all resources that will be operated
* on by the subscriber action. The default behavior is to include all projects
* effected by the operation. Subclasses may override.
*
* @param syncSet
* @return
*/
protected ISchedulingRule getSchedulingRule(SyncInfoSet syncSet) {
IResource[] resources = syncSet.getResources();
Set set = new HashSet();
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
set.add(resource.getProject());
}
IProject[] projects = (IProject[]) set.toArray(new IProject[set.size()]);
if (projects.length == 1) {
return projects[0];
} else {
return new MultiRule(projects);
}
}
protected boolean canRunAsJob() {
return true;
}
/**
* Filter the sync resource set using action specific criteria or input from the user.
*/
protected SyncInfoSet getFilteredSyncInfoSet(SyncInfo[] selectedResources) {
// If there are conflicts or outgoing changes in the syncSet, we need to warn the user.
return new SyncInfoSet(selectedResources);
}
protected void pruneEmptyParents(SyncInfo[] nodes) throws CVSException {
// TODO: A more explicit tie in to the pruning mechanism would be prefereable.
// i.e. I don't like referencing the option and visitor directly
if (!CVSProviderPlugin.getPlugin().getPruneEmptyDirectories()) return;
ICVSResource[] cvsResources = new ICVSResource[nodes.length];
for (int i = 0; i < cvsResources.length; i++) {
cvsResources[i] = CVSWorkspaceRoot.getCVSResourceFor(nodes[i].getLocal());
}
new PruneFolderVisitor().visit(
CVSWorkspaceRoot.getCVSFolderFor(ResourcesPlugin.getWorkspace().getRoot()),
cvsResources);
}
public CVSSyncInfo getCVSSyncInfo(SyncInfo info) {
if (info instanceof CVSSyncInfo) {
return (CVSSyncInfo)info;
}
return null;
}
protected SyncInfo getParent(SyncInfo info) throws TeamException {
return info.getSubscriber().getSyncInfo(info.getLocal().getParent(), new NullProgressMonitor());
}
protected IResource[] getIResourcesFrom(SyncInfo[] nodes) {
List resources = new ArrayList(nodes.length);
for (int i = 0; i < nodes.length; i++) {
resources.add(nodes[i].getLocal());
}
return (IResource[]) resources.toArray(new IResource[resources.size()]);
}
}