org.eclipse.team.ui/src/org/eclipse/team/ui/operations/ResourceMappingMergeOperation.java
Parent Directory
|
Revision Log
Revision 1.37 -
(download)
(annotate)
Thu Jan 26 19:47:01 2006 UTC (3 years, 10 months ago) by mvalenta
Branch: MAIN
CVS Tags: I20060131, I200602131600, HEAD
Changes since 1.36: +0 -0 lines
FILE REMOVED
Thu Jan 26 19:47:01 2006 UTC (3 years, 10 months ago) by mvalenta
Branch: MAIN
CVS Tags: I20060131, I200602131600, HEAD
Changes since 1.36: +0 -0 lines
FILE REMOVED
Simplified some class names
/******************************************************************************* * Copyright (c) 2000, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.team.ui.operations; import java.lang.reflect.InvocationTargetException; import org.eclipse.compare.CompareConfiguration; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.mapping.*; import org.eclipse.core.runtime.*; import org.eclipse.jface.dialogs.*; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.swt.widgets.*; import org.eclipse.team.core.diff.*; import org.eclipse.team.core.mapping.IMergeContext; import org.eclipse.team.core.mapping.ISynchronizationContext; import org.eclipse.team.internal.ui.*; import org.eclipse.team.internal.ui.mapping.MergeOperation; import org.eclipse.team.ui.TeamUI; import org.eclipse.team.ui.mapping.ICompareAdapter; import org.eclipse.team.ui.synchronize.*; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; /** * The steps of an optimistic merge operation are: * <ol> * <li>Obtain the selection to be operated on. * <li>Determine the projection of the selection onto resources * using resource mappings and traversals. * <ul> * <li>this will require traversals using both the ancestor and remote * for three-way merges. * <li>for model providers with registered merger, mapping set need * not be expanded (this is tricky if one of the model providers doesn't * have a merge but all others do). * <li>if the model does not have a custom merger, ensure that additional * mappings are included (i.e. for many model elements to one resource case) * </ul> * <li>Create a MergeContext for the merge * <ul> * <li>Determine the synchronization state of all resources * covered by the input. * <li>Pre-fetch the required contents. * </ul> * <li>Obtain and invoke the merger for each provider * <ul> * <li>This will auto-merge as much as possible * <li>If everything was merged, cleanup and stop * <li>Otherwise, a set of un-merged resource mappings is returned * </ul> * <li>Delegate manual merge to the model provider * <ul> * <li>This hands off the context to the manual merge * <li>Once completed, the manual merge must clean up * </ul> * </ol> * * <p> * Handle multiple model providers where one extends all others by using * the top-most model provider. The assumption is that the model provider * will delegate to lower level model providers when appropriate. * <p> * Special case to support sub-file merges. * <ul> * <li>Restrict when sub-file merging is supported * <ul> * <li>Only one provider involved (i.e. consulting participants results * in participants that are from the model provider or below). * <li>The provider has a custom auto and manual merger. * </ul> * <li>Prompt to warn when sub-file merging is not possible. * <li>Need to display the additional elements that will be affected. * This could be done in a diff tree or some other view. It needs to * consider incoming changes including additions. * </ul> * <p> * Special case to handle conflicting model providers. * <ul> * <li>Prompt user to indicate the conflict * <li>Allow user to exclude one of the models? * <li>Allow use to choose order of evaluation? * <li>Support tabbed sync view * </ul> * * <p> * <strong>EXPERIMENTAL</strong>. This class or interface has been added as * part of a work in progress. There is a guarantee neither that this API will * work nor that it will remain the same. Please do not use this API without * consulting with the Platform/Team team. * </p> * * @since 3.2 */ public abstract class ResourceMappingMergeOperation extends ResourceMappingOperation { private static final int DONE_ID = IDialogConstants.CLIENT_ID + 1; private static final int REPLACE_ID = IDialogConstants.CLIENT_ID + 2; private IMergeContext context; /** * Create a merge operation * @param part the workbench part from which the merge was launched or <code>null</code> * @param selectedMappings the selected mappings */ protected ResourceMappingMergeOperation(IWorkbenchPart part, ResourceMapping[] selectedMappings) { super(part, selectedMappings); } /* (non-Javadoc) * @see org.eclipse.team.ui.mapping.ResourceMappingOperation#execute(org.eclipse.core.runtime.IProgressMonitor) */ protected void execute(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { monitor.beginTask(null, 100); context = buildMergeContext(Policy.subMonitorFor(monitor, 75)); if (!hasChangesOfInterest()) { promptForNoChanges(); context.dispose(); return; } if (!isPreviewRequested()) { IStatus status = MergeOperation.validateMerge(context, Policy.subMonitorFor(monitor, 5)); if (status.isOK()) { if (performMerge(Policy.subMonitorFor(monitor, 20))) // The merge was sucessful so we can just return return; } } // Either auto-merging was not attemped or it was not 100% sucessful // TODO If there is a problem between here and when the preview is shown, the context may be leaked showPreview(Policy.subMonitorFor(monitor, 25)); } catch (CoreException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } } /** * Return whether the context of this operation has changes that are * of interest to the operation. Sublcasses may override. * @return whether the context of this operation has changes that are * of interest to the operation */ protected boolean hasChangesOfInterest() { return !context.getDiffTree().isEmpty() && hasIncomingChanges(context.getDiffTree()); } private boolean hasIncomingChanges(IDiffTree tree) { return hasChangesMatching(tree, new FastDiffFilter() { public boolean select(IDiffNode node) { if (node instanceof IThreeWayDiff) { IThreeWayDiff twd = (IThreeWayDiff) node; int direction = twd.getDirection(); if (direction == IThreeWayDiff.INCOMING || direction == IThreeWayDiff.CONFLICTING) { return true; } } else { // Return true for any two-way change return true; } return false; } }); } /** * Method invoked when the context contains no changes so that the user * can be informed. */ protected void promptForNoChanges() { Display.getDefault().syncExec(new Runnable() { public void run() { MessageDialog.openInformation(getShell(), TeamUIMessages.ResourceMappingMergeOperation_0, TeamUIMessages.ResourceMappingMergeOperation_1); }; }); } /** * Preview the merge so the user can perform the merge manually. * @param monitor a progress monitor */ protected void showPreview(IProgressMonitor monitor) { calculateStates(context, Policy.subMonitorFor(monitor, 5)); Display.getDefault().asyncExec(new Runnable() { public void run() { ResourceMappingSynchronizeParticipant participant = createParticipant(); if (isPreviewInDialog()) { CompareConfiguration cc = new CompareConfiguration(); ISynchronizePageConfiguration pageConfiguration = participant.createPageConfiguration(); // Restrict preview page to only support incomign and conflict modes if (pageConfiguration.getComparisonType() == ISynchronizePageConfiguration.THREE_WAY) { pageConfiguration.setSupportedModes(ISynchronizePageConfiguration.INCOMING_MODE | ISynchronizePageConfiguration.CONFLICTING_MODE); pageConfiguration.setMode(ISynchronizePageConfiguration.INCOMING_MODE); } ParticipantPageSaveablePart input = new ParticipantPageSaveablePart(getShell(), cc, pageConfiguration, participant); ParticipantPageDialog dialog = new ParticipantPageDialog(getShell(), input, participant) { private Button doneButton; private Button replaceButton; protected boolean isOfferToRememberParticipant() { boolean isReplace = context.getMergeType() == ISynchronizationContext.TWO_WAY; if (isReplace) return false; return super.isOfferToRememberParticipant(); } protected void createButtonsForButtonBar(Composite parent) { boolean isReplace = context.getMergeType() == ISynchronizationContext.TWO_WAY; if (isReplace) { replaceButton = createButton(parent, REPLACE_ID, "&Replace", true); replaceButton.setEnabled(true); } doneButton = createButton(parent, DONE_ID, TeamUIMessages.ResourceMappingMergeOperation_2, !isReplace); doneButton.setEnabled(true); // Don't call super because we don't want the OK button to appear } protected void buttonPressed(int buttonId) { if (buttonId == DONE_ID) { super.buttonPressed(IDialogConstants.OK_ID); } else if (buttonId == REPLACE_ID) { try { // Do this inline so we don't have to manage disposing of the context PlatformUI.getWorkbench().getProgressService().run(true, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { performMerge(monitor); } catch (CoreException e) { throw new InvocationTargetException(e); } } }); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); IStatus status; if (t instanceof CoreException) { CoreException ce = (CoreException) t; status = ce.getStatus(); } else { status = new Status(IStatus.ERROR, TeamUIPlugin.ID, 0, TeamUIMessages.internal, t); TeamUIPlugin.log(status); } ErrorDialog.openError(getShell(), null, null, status); return; } catch (InterruptedException e) { // Operation was cancelled. Leave the dialog open return; } super.buttonPressed(IDialogConstants.OK_ID); } else { super.buttonPressed(buttonId); } } }; int result = dialog.open(); input.dispose(); if (TeamUI.getSynchronizeManager().get(participant.getId(), participant.getSecondaryId()) == null) participant.dispose(); } else { ISynchronizeManager mgr = TeamUI.getSynchronizeManager(); ISynchronizeView view = mgr.showSynchronizeViewInActivePage(); mgr.addSynchronizeParticipants(new ISynchronizeParticipant[] {participant}); view.display(participant); } } }); } /** * Return whether previews should occur in a dialog or in the synchronize view. * @return whether previews should occur in a dialog or in the synchronize view */ protected boolean isPreviewInDialog() { return true; } private void calculateStates(ISynchronizationContext context, IProgressMonitor monitor) { monitor.beginTask(null, IProgressMonitor.UNKNOWN); ModelProvider[] providers = getScope().getModelProviders(); for (int i = 0; i < providers.length; i++) { ModelProvider provider = providers[i]; calculateStates(context, provider, Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN)); } monitor.done(); } private void calculateStates(ISynchronizationContext context, ModelProvider provider, IProgressMonitor monitor) { Object o = provider.getAdapter(ICompareAdapter.class); if (o instanceof ICompareAdapter) { ICompareAdapter calculator = (ICompareAdapter) o; try { calculator.prepareContext(context, monitor); } catch (CoreException e) { TeamUIPlugin.log(e); } } monitor.done(); } /** * Perform the merge for the context of the operation. If the merge was not * succesful in it's entirety, there are still changes left to be merged. * Clients can decide how to handle this. * * @param monitor a prohress monitor * @return whether the merge was successful in it's entirety * @throws CoreException */ protected boolean performMerge(IProgressMonitor monitor) throws CoreException { boolean merged = new MergeOperation(getPart(), context).performMerge(monitor); if (merged) { context.dispose(); } return merged; } /** * Build and initialize a merge context for the input of this operation. * @param monitor a progress monitor * @return a merge context for merging the mappings of the input */ protected abstract IMergeContext buildMergeContext(IProgressMonitor monitor) throws CoreException; /* (non-Javadoc) * @see org.eclipse.team.ui.operations.ResourceMappingOperation#getContext() */ protected ISynchronizationContext getContext() { return context; } /* (non-Javadoc) * @see org.eclipse.team.ui.operations.ResourceMappingOperation#getPreviewRequestMessage() */ protected String getPreviewRequestMessage() { if (!isPreviewRequested()) { return TeamUIMessages.ResourceMappingMergeOperation_4; } return super.getPreviewRequestMessage(); } /** * Return whether the given diff tree contains any deltas that match the given filter. * @param tree the diff tree * @param filter the diff node filter * @return whether the given diff tree contains any deltas that match the given filter */ protected boolean hasChangesMatching(IDiffTree tree, final FastDiffFilter filter) { final CoreException found = new CoreException(Status.OK_STATUS); try { tree.accept(ResourcesPlugin.getWorkspace().getRoot().getFullPath(), new IDiffVisitor() { public boolean visit(IDiffNode delta) throws CoreException { if (filter.select(delta)) { throw found; } return false; } }, IResource.DEPTH_INFINITE); } catch (CoreException e) { if (e == found) return true; TeamUIPlugin.log(e); } return false; } /** * Create the synchronize pariticipant to be used by this operation * to preview changes. By default, a {@link ResourceMappingSynchronizeParticipant} * is created using he context ({@link #getContext()}) and job name ({@link #getJobName()}) * of this operation. Subclasses may override this method. * <p> * Once created, it is the responsibility of the participant to dispose of the * synchronization context when it is no longer needed. * @return a newly created synchronize pariticipant to be used by this operation */ protected ResourceMappingSynchronizeParticipant createParticipant() { return ResourceMappingSynchronizeParticipant.createParticipant(getContext(), getJobName()); } }
| help@eclipse.org | ViewVC Help |
| Powered by ViewVC 1.0.3 |
