| /******************************************************************************* |
| * 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 |
| } |
| } |
| } |
| |
| } |