blob: f749754f468d9460cb482cc49dfa076c8f7e18fe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ui.synchronize.patch;
import java.io.BufferedReader;
import java.io.IOException;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.internal.ComparePreferencePage;
import org.eclipse.compare.internal.CompareUIPlugin;
import org.eclipse.compare.internal.core.patch.FilePatch2;
import org.eclipse.compare.internal.core.patch.PatchReader;
import org.eclipse.compare.internal.patch.FilePatch;
import org.eclipse.compare.internal.patch.PatchWizard;
import org.eclipse.compare.internal.patch.PatchWizardDialog;
import org.eclipse.compare.internal.patch.Utilities;
import org.eclipse.compare.patch.IFilePatch;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.internal.ui.IPreferenceIds;
import org.eclipse.team.internal.ui.TeamUIPlugin;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.ide.IDE;
/**
* An operation that provides an interface to the Apply Patch Wizard. Users specify
* the input in terms of an <code>IStorage</code> (note: input must be in unified diff
* format), an <code>IResource</code> target to apply the patch to and can provide <code>CompareConfiguration</code>
* elements to supply the label and images used on the preview page and hunk merge page. Finally, the
* user can also supply a title and image to override the default ones provided by the Apply Patch Wizard.
* Note that the Apply Patch Wizard does not require any particular set of inputs, and in the absence of
* any user supplied values, it will work in default mode.
* <p>
* This is copy of {@link org.eclipse.compare.patch.ApplyPatchOperation} which additionally honors
* the preference whether to show the patch in a wizard or in the Synchronize view.
* </p>
* <p>
* FIXME: This class will be removed with the fix for https://bugs.eclipse.org/309803
* </p>
*
* @since 3.3
*
*/
public class ApplyPatchOperation implements Runnable {
private IWorkbenchPart part;
/**
* Used for the Preview Patch page.
*/
private CompareConfiguration configuration;
/**
* The patch to use as an input into the Apply Patch wizard
*/
private IStorage patch;
/**
* Specific <code>IResource</code> target to patch.
*/
private IResource target;
/**
* An optional image for the patch wizard
*/
private ImageDescriptor patchWizardImage;
/**
* An optional title for the patchWizard
*/
private String patchWizardTitle;
private boolean saveAllEditors = true;
/**
* Return whether the given storage contains a patch.
* @param storage the storage
* @return whether the given storage contains a patch
* @throws CoreException if an error occurs reading the contents from the storage
*/
public static boolean isPatch(IStorage storage) throws CoreException {
return internalParsePatch(storage).length > 0;
}
/**
* Parse the given patch and return the set of file patches that it contains.
* @param storage the storage that contains the patch
* @return the set of file patches that the storage contains
* @throws CoreException if an error occurs reading the contents from the storage
*/
public static IFilePatch[] parsePatch(IStorage storage) throws CoreException {
return internalParsePatch(storage);
}
/**
* Creates a new ApplyPatchOperation with the supplied compare configuration, patch and target.
* The behaviour of the Apply Patch wizard is controlled by the number of parameters supplied:
* <ul>
* <li>If a patch is supplied, the initial input page is skipped. If a patch is not supplied the wizard
* will open on the input page.</li>
* <li>If the patch is a workspace patch, the target selection page is skipped and the preview page is
* displayed.</li>
* <li>If the patch is not a workspace patch and the target is specified, the target page is still
* shown with the target selected.</li>
* </ul>
*
* @param part an IWorkbenchPart or <code>null</code>
* @param patch an IStorage containing a patch in unified diff format or <code>null</code>
* @param target an IResource which the patch is to be applied to or <code>null</code>
* @param configuration a CompareConfiguration supplying the labels and images for the preview patch page
*/
public ApplyPatchOperation(IWorkbenchPart part, IStorage patch, IResource target, CompareConfiguration configuration) {
Assert.isNotNull(configuration);
this.part = part;
this.patch = patch;
this.target = target;
this.configuration = configuration;
}
/**
* Create an operation for the given part and resource. This method is a convenience
* method that calls {@link #ApplyPatchOperation(IWorkbenchPart, IStorage, IResource, CompareConfiguration)}
* with appropriate defaults for the other parameters.
* @param targetPart an IResource which the patch is to be applied to or <code>null</code>
* @param resource an IResource which the patch is to be applied to or <code>null</code>
* @see #ApplyPatchOperation(IWorkbenchPart, IStorage, IResource, CompareConfiguration)
*/
public ApplyPatchOperation(IWorkbenchPart targetPart, IResource resource) {
this(targetPart, null, resource, new CompareConfiguration());
}
/**
* Open the Apply Patch wizard using the values associated with this operation.
* This method must be called from the UI thread.
*/
public void openWizard() {
saveAllEditors();
if (saveAllEditors) {
PatchWizard wizard = createPatchWizard(patch, target, configuration);
if (patchWizardImage != null)
wizard.setDefaultPageImageDescriptor(patchWizardImage);
if (patchWizardTitle != null)
wizard.setWindowTitle(patchWizardTitle);
wizard.setNeedsProgressMonitor(true);
if (wizard instanceof ApplyPatchSynchronizationWizard
&& ((ApplyPatchSynchronizationWizard) wizard).isComplete())
wizard.performFinish();
else
new PatchWizardDialog(getShell(), wizard).open();
}
}
private PatchWizard createPatchWizard(IStorage patch, IResource target,
CompareConfiguration configuration) {
if (isApplyPatchInSynchronizeView())
return new ApplyPatchSynchronizationWizard(patch, target,
configuration);
return new PatchWizard(patch, target, configuration);
}
protected boolean isApplyPatchInSynchronizeView() {
return TeamUIPlugin.getPlugin().getPreferenceStore().getBoolean(
IPreferenceIds.APPLY_PATCH_IN_SYNCHRONIZE_VIEW);
}
/**
* Return the parent shell to be used when the wizard is opened.
* By default, the site of the part is used to get the shell.
* Subclasses may override.
* @return the parent shell to be used when the wizard is opened
*/
protected Shell getShell() {
if (part == null)
return CompareUIPlugin.getShell();
return part.getSite().getShell();
}
/**
* This method will save all dirty editors. It will prompt the user if the Compare preference to save
* dirty editors before viewing a patch is <code>false</code>. Clients can use this or provide their own
* implementation.
*/
protected void saveAllEditors(){
saveAllEditors = IDE.saveAllEditors(new IResource[]{ResourcesPlugin.getWorkspace().getRoot()}, !ComparePreferencePage.getSaveAllEditors());
}
/**
* Sets the title of the patch wizard. Needs to be set before {@link #openWizard()} is called.
* @param title a string to display in the title bar
*/
public void setPatchWizardTitle(String title){
this.patchWizardTitle = title;
}
/**
* Sets the image descriptor to use in the patch wizard. Needs to be set before {@link #openWizard()} is called.
* @param descriptor an image descriptor
*/
public void setPatchWizardImageDescriptor(ImageDescriptor descriptor){
this.patchWizardImage = descriptor;
}
@Override
public void run() {
openWizard();
}
private static IFilePatch[] internalParsePatch(IStorage storage)
throws CoreException {
BufferedReader reader = Utilities.createReader(storage);
try {
PatchReader patchReader = new PatchReader() {
@Override
protected FilePatch2 createFileDiff(IPath oldPath, long oldDate,
IPath newPath, long newDate) {
return new FilePatch(oldPath, oldDate, newPath,
newDate);
}
};
patchReader.parse(reader);
FilePatch2[] fileDiffs = patchReader.getAdjustedDiffs();
IFilePatch[] filePatch = new IFilePatch[fileDiffs.length];
for (int i = 0; i < fileDiffs.length; i++) {
filePatch[i] = (FilePatch) fileDiffs[i];
}
return filePatch;
} catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR,
CompareUIPlugin.PLUGIN_ID, 0, e.getMessage(), e));
} finally {
try {
reader.close();
} catch (IOException e) { // ignored
}
}
}
}