### Eclipse Workspace Patch 1.0 #P org.eclipse.compare Index: compare/org/eclipse/compare/internal/CompareMessages.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.java,v retrieving revision 1.20 diff -u -r1.20 CompareMessages.java --- compare/org/eclipse/compare/internal/CompareMessages.java 10 Oct 2008 14:55:31 -0000 1.20 +++ compare/org/eclipse/compare/internal/CompareMessages.java 8 Jan 2009 20:44:13 -0000 @@ -130,6 +130,51 @@ public static String CompareWithOtherResourceDialog_workspaceMainButton; public static String CompareWithOtherResourceDialog_workspaceRadioButton; + public static String CreatePatchActionTitle; + public static String WorkspacePatchDialogTitle; + public static String WorkspacePatchDialogDescription; + public static String Save_Patch_As_5; + public static String Save_To_Clipboard_2; + public static String Save_In_File_System_3; + public static String Browse____4; + public static String patch_txt_6; + public static String Save_In_Workspace_7; + public static String Fi_le_name__9; + public static String Context_14; + public static String Standard_15; + public static String Diff_output_format_12; + public static String Advanced_options_19; + public static String Configure_the_options_used_for_the_CVS_diff_command_20; + public static String Unified__format_required_by_Compare_With_Patch_feature__13; + public static String GenerateLocalDiff_title; + public static String GenerateLocalDiff_pageTitle; + public static String GenerateLocalDiff_pageDescription; + public static String GenerateLocalDiff_Specify_the_file_which_contributes_the_changes; + public static String GenerateLocalDiff_overwriteTitle; + public static String GenerateLocalDiff_overwriteMsg; + public static String GenerateLocalDiff_1; + public static String GenerateLocalDiff_2; + public static String GenerateDiffFileWizard_6; + public static String GenerateDiffFileWizard_7; + public static String GenerateDiffFileWizard_8; + public static String GenerateDiffFileWizard_9; + public static String GenerateDiffFileWizard_10; + public static String GenerateDiffFileWizard_0; + public static String GenerateDiffFileWizard_2; + public static String GenerateDiffFileWizard_3; + public static String GenerateDiffFileWizard_4; + public static String GenerateDiffFileWizard_5; + public static String GenerateDiffFileWizard_browseFilesystem; + public static String GenerateDiffFileWizard_FolderExists; + public static String GenerateDiffFileWizard_ProjectClosed; + public static String GenerateDiffFileWizard_13; + public static String GenerateDiffFileWizard_Left; + public static String GenerateDiffFileWizard_Right; + public static String GenerateLocalDiff_3; + public static String GenerateLocalDiff_4; + public static String GenerateLocalDiff_5; + public static String GenerateLocalDiff_6; + static { NLS.initializeMessages(BUNDLE_NAME, CompareMessages.class); } Index: compare/org/eclipse/compare/internal/CompareMessages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.properties,v retrieving revision 1.31 diff -u -r1.31 CompareMessages.properties --- compare/org/eclipse/compare/internal/CompareMessages.properties 10 Oct 2008 14:55:31 -0000 1.31 +++ compare/org/eclipse/compare/internal/CompareMessages.properties 8 Jan 2009 20:44:13 -0000 @@ -141,3 +141,92 @@ CompareWithOtherResourceDialog_externalFolderRadioButton=External folder CompareWithOtherResourceDialog_workspaceMainButton=Browse... CompareWithOtherResourceDialog_workspaceRadioButton=Workspace + +CreatePatchActionTitle=Save Diffs... +WorkspacePatchDialogTitle=Set a Patch Location +WorkspacePatchDialogDescription=Select a folder in the workspace and enter a name for the patch. +Save_To_Clipboard_2=&Clipboard +Save_In_File_System_3=Fil&e +Browse____4=Br&owse... +Save_Patch_As_5=Save Patch As +patch_txt_6=patch.txt +Save_In_Workspace_7=&Workspace +Fi_le_name__9=Fi&le name: +Context_14=&Context +Standard_15=&Standard +Diff_output_format_12=Diff Output Format +Advanced_options_19=Advanced Options +Configure_the_options_used_for_the_CVS_diff_command_20=Configure the options used for the command. +Unified__format_required_by_Compare_With_Patch_feature__13=&Unified (format required by the Apply Patch wizard) +GenerateLocalDiff_title=Save Diffs +GenerateLocalDiff_pageTitle=Save Diffs into a Patch +GenerateLocalDiff_pageDescription=Specify the input and the location for the patch. +GenerateLocalDiff_Specify_the_file_which_contributes_the_changes=Select which side of the compare editor is contributing the changes. +GenerateLocalDiff_overwriteTitle=Confirm Overwrite +GenerateLocalDiff_overwriteMsg=A output file with that name already exists. Overwrite? +GenerateLocalDiff_1=Read-only file +GenerateLocalDiff_2=The specified file is read-only and cannot be overwritten. +GenerateLocalDiff_3=File does not exist. +GenerateLocalDiff_4=Specified root patch file does not exist. +GenerateLocalDiff_5=File is not readable. +GenerateLocalDiff_6=Specified rot patch file is not readable. +GenerateDiffFileWizard_6=&Workspace (Multi-project Apply Patch wizard specific) +GenerateDiffFileWizard_7=&Project +GenerateDiffFileWizard_8=S&election +GenerateDiffFileWizard_9=Save Patch +GenerateDiffFileWizard_10=Patch Root +GenerateDiffFileWizard_0=Please enter a valid location. +GenerateDiffFileWizard_2=Please enter a file name. +GenerateDiffFileWizard_3=The specified directory does not exist. +GenerateDiffFileWizard_4=Please select a location in the workspace by browsing. +GenerateDiffFileWizard_5=Please enter a valid filename. +GenerateDiffFileWizard_browseFilesystem=Please select a location in the filesystem by browsing. +GenerateDiffFileWizard_FolderExists=The specified path points to an existing folder. +GenerateDiffFileWizard_ProjectClosed=The specified path points to a closed project. +GenerateDiffFileWizard_13=Use only &file path: +GenerateDiffFileWizard_Left=&Left: +GenerateDiffFileWizard_Right=&Right: +CreatePatchActionTitle=Save Diffs... +WorkspacePatchDialogTitle=Set a Patch Location +WorkspacePatchDialogDescription=Select a folder in the workspace and enter a name for the patch. +Save_To_Clipboard_2=&Clipboard +Save_In_File_System_3=Fil&e +Browse____4=Br&owse... +Save_Patch_As_5=Save Patch As +patch_txt_6=patch.txt +Save_In_Workspace_7=&Workspace +Fi_le_name__9=Fi&le name: +Context_14=&Context +Standard_15=&Standard +Diff_output_format_12=Diff Output Format +Advanced_options_19=Advanced Options +Configure_the_options_used_for_the_CVS_diff_command_20=Configure the options used for the command. +Unified__format_required_by_Compare_With_Patch_feature__13=&Unified (format required by the Apply Patch wizard) +GenerateLocalDiff_title=Save Diffs +GenerateLocalDiff_pageTitle=Save Diffs into a Patch +GenerateLocalDiff_pageDescription=Specify the input and the location for the patch. +GenerateLocalDiff_Specify_the_file_which_contributes_the_changes=Select which side of the compare editor is contributing the changes. +GenerateLocalDiff_overwriteTitle=Confirm Overwrite +GenerateLocalDiff_overwriteMsg=A output file with that name already exists. Overwrite? +GenerateLocalDiff_1=Read-only file +GenerateLocalDiff_2=The specified file is read-only and cannot be overwritten. +GenerateLocalDiff_3=File does not exist. +GenerateLocalDiff_4=Specified root patch file does not exist. +GenerateLocalDiff_5=File is not readable. +GenerateLocalDiff_6=Specified rot patch file is not readable. +GenerateDiffFileWizard_6=&Workspace (Multi-project Apply Patch wizard specific) +GenerateDiffFileWizard_7=&Project +GenerateDiffFileWizard_8=S&election +GenerateDiffFileWizard_9=Save Patch +GenerateDiffFileWizard_10=Patch Root +GenerateDiffFileWizard_0=Please enter a valid location. +GenerateDiffFileWizard_2=Please enter a file name. +GenerateDiffFileWizard_3=The specified directory does not exist. +GenerateDiffFileWizard_4=Please select a location in the workspace by browsing. +GenerateDiffFileWizard_5=Please enter a valid filename. +GenerateDiffFileWizard_browseFilesystem=Please select a location in the filesystem by browsing. +GenerateDiffFileWizard_FolderExists=The specified path points to an existing folder. +GenerateDiffFileWizard_ProjectClosed=The specified path points to a closed project. +GenerateDiffFileWizard_13=Use only &file path: +GenerateDiffFileWizard_Left=&Left: +GenerateDiffFileWizard_Right=&Right: \ No newline at end of file Index: compare/org/eclipse/compare/internal/ICompareUIConstants.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareUIConstants.java,v retrieving revision 1.13 diff -u -r1.13 ICompareUIConstants.java --- compare/org/eclipse/compare/internal/ICompareUIConstants.java 3 Mar 2008 13:30:41 -0000 1.13 +++ compare/org/eclipse/compare/internal/ICompareUIConstants.java 8 Jan 2009 20:44:13 -0000 @@ -49,4 +49,6 @@ public static final String PREF_VALUE_NEXT = "next"; //$NON-NLS-1$ public static final String COMMAND_IGNORE_WHITESPACE = PREFIX + "ignoreWhiteSpace"; //$NON-NLS-1$ + + public final String IMG_WIZBAN_DIFF = "wizban/createpatch_wizban.png"; //$NON-NLS-1$ } Index: compare/org/eclipse/compare/internal/MergeSourceViewer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java,v retrieving revision 1.47 diff -u -r1.47 MergeSourceViewer.java --- compare/org/eclipse/compare/internal/MergeSourceViewer.java 7 Jan 2009 10:14:11 -0000 1.47 +++ compare/org/eclipse/compare/internal/MergeSourceViewer.java 8 Jan 2009 20:44:14 -0000 @@ -105,6 +105,7 @@ public static final String SAVE_ID= "save"; //$NON-NLS-1$ public static final String FIND_ID= "find"; //$NON-NLS-1$ public static final String GOTO_LINE_ID= "gotoLine"; //$NON-NLS-1$ + public static final String CREATE_PATCH_ID= "createPatch"; //$NON-NLS-1$ class TextOperationAction extends MergeViewerAction { @@ -782,7 +783,7 @@ * Allows the viewer to add menus and/or tools to the context menu. */ public void menuAboutToShow(IMenuManager menu) { - + menu.add(new Separator("undo")); //$NON-NLS-1$ addMenu(menu, UNDO_ID); addMenu(menu, REDO_ID); @@ -799,6 +800,8 @@ addMenu(menu, SELECT_ALL_ID); menu.add(new Separator("edit")); //$NON-NLS-1$ + + addMenu(menu, CREATE_PATCH_ID); menu.add(new Separator("find")); //$NON-NLS-1$ addMenu(menu, FIND_ID); Index: compare/org/eclipse/compare/internal/merge/DocumentMerger.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java,v retrieving revision 1.7 diff -u -r1.7 DocumentMerger.java --- compare/org/eclipse/compare/internal/merge/DocumentMerger.java 28 Feb 2008 16:47:01 -0000 1.7 +++ compare/org/eclipse/compare/internal/merge/DocumentMerger.java 8 Jan 2009 20:44:15 -0000 @@ -1343,5 +1343,9 @@ } return null; } - + + public ArrayList getAllDiffs() { + return fAllDiffs; + } + } Index: compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java,v retrieving revision 1.239 diff -u -r1.239 TextMergeViewer.java --- compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java 7 Jan 2009 10:14:11 -0000 1.239 +++ compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java 8 Jan 2009 20:44:12 -0000 @@ -43,6 +43,7 @@ import org.eclipse.compare.internal.CompareMessages; import org.eclipse.compare.internal.ComparePreferencePage; import org.eclipse.compare.internal.CompareUIPlugin; +import org.eclipse.compare.internal.CreatePatchAction; import org.eclipse.compare.internal.DocumentManager; import org.eclipse.compare.internal.ICompareContextIds; import org.eclipse.compare.internal.ICompareUIConstants; @@ -1937,6 +1938,9 @@ service.activateContext("org.eclipse.ui.textEditorScope"); //$NON-NLS-1$ } } + + contributeCreatePatchAction(fLeft, false); + contributeCreatePatchAction(fRight, true); } private void hsynchViewport(final TextViewer tv1, final TextViewer tv2, final TextViewer tv3) { @@ -2430,6 +2434,12 @@ viewer.addAction(MergeSourceViewer.GOTO_LINE_ID, action); } + private void contributeCreatePatchAction(MergeSourceViewer viewer, + boolean rightToLeft) { + IAction action = new CreatePatchAction(this, rightToLeft); + viewer.addAction(MergeSourceViewer.CREATE_PATCH_ID, action); + } + private void connectGlobalActions(final MergeSourceViewer part) { if (fHandlerService != null) { if (part != null) Index: compare/org/eclipse/compare/internal/GenerateDiffFileWizard.java =================================================================== RCS file: compare/org/eclipse/compare/internal/GenerateDiffFileWizard.java diff -N compare/org/eclipse/compare/internal/GenerateDiffFileWizard.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compare/org/eclipse/compare/internal/GenerateDiffFileWizard.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,1561 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Krzysztof Poglodzinski (intuicje@gmail.com) - initial API and implementation + * Mariusz Tanski (mariusztanski@gmail.com) - initial API and implementation + * Kacper Zdanowicz (kacper.zdanowicz@gmail.com) - initial API and implementation + * IBM Corportation - initial API and implementation + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.compare.internal.merge.DocumentMerger; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.model.BaseWorkbenchContentProvider; +import org.eclipse.ui.model.WorkbenchLabelProvider; +import org.eclipse.ui.views.navigator.ResourceComparator; + +public class GenerateDiffFileWizard extends Wizard { + + protected final static int INITIAL_WIDTH = 300; + protected final static int INITIAL_HEIGHT = 350; + + public static void run(DocumentMerger merger, IDocument leftDoc, + IDocument rightDoc, String leftLabel, String rightLabel, + String leftPath, String rightPath, Shell shell, boolean rightToLeft) { + GenerateDiffFileWizard.run(merger, leftDoc, rightDoc, leftLabel, rightLabel, leftPath, rightPath, shell, rightToLeft, true); + } + + public static void run(DocumentMerger merger, IDocument leftDoc, + IDocument rightDoc, String leftLabel, String rightLabel, + String leftPath, String rightPath, Shell shell, boolean rightToLeft, boolean unifiedSelectionEnabled) { + final String title = CompareMessages.GenerateLocalDiff_title; + final GenerateDiffFileWizard wizard = new GenerateDiffFileWizard(merger, + leftDoc, rightDoc, leftLabel, rightLabel, leftPath, rightPath, + rightToLeft, unifiedSelectionEnabled); + wizard.setWindowTitle(title); + WizardDialog dialog = new WizardDialog(shell, wizard); + dialog.setMinimumPageSize(INITIAL_WIDTH, INITIAL_HEIGHT); + dialog.open(); + + } + + protected class DirectionSelectionPage extends WizardPage { + + public final static int LEFT_OPTION = 1; + public final static int RIGHT_OPTION = 2; + + private Button fromLeftOption; + private Button fromRightOption; + + private Label fromLeftLabel; + private Label fromRightLabel; + + private RadioButtonGroup fromRadioGroup = new RadioButtonGroup(); + + protected DirectionSelectionPage(String pageName, String title, + ImageDescriptor titleImage) { + super(pageName, title, titleImage); + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NULL); + GridLayout layout = new GridLayout(); + layout.marginLeft = 5; + layout.marginTop = 9; + layout.horizontalSpacing = 8; + layout.verticalSpacing = 8; + layout.numColumns = 2; + composite.setLayout(layout); + composite.setLayoutData(new GridData()); + setControl(composite); + + fromLeftLabel = new Label(composite, SWT.HORIZONTAL|SWT.SHADOW_OUT); + fromLeftLabel.setText(CompareMessages.GenerateDiffFileWizard_Left); + fromLeftLabel.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, false, 1, 1)); + + fromLeftOption = new Button(composite, SWT.RADIO); + fromLeftOption.setText(leftLabel); + fromLeftOption.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, false, 1, 1)); + + fromRightLabel = new Label(composite, SWT.HORIZONTAL|SWT.SHADOW_OUT); + fromRightLabel.setText(CompareMessages.GenerateDiffFileWizard_Right); + fromRightLabel.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, false, 1, 1)); + + fromRightOption = new Button(composite, SWT.RADIO); + fromRightOption.setText(rightLabel); + fromRightOption.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, false, 1, 1)); + + fromRadioGroup.add(LEFT_OPTION, fromLeftOption); + fromRadioGroup.add(RIGHT_OPTION, fromRightOption); + + Dialog.applyDialogFont(parent); + + // Add listeners + fromLeftOption.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + fromRadioGroup.setSelection(LEFT_OPTION, true); + targetFileEdited = false; + } + }); + + fromRightOption.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + fromRadioGroup.setSelection(RIGHT_OPTION, true); + targetFileEdited = false; + } + }); + + fromRadioGroup.setSelection(rightToLeft ? RIGHT_OPTION + : LEFT_OPTION, true); + } + + public boolean isRightToLeft() { + return fromRadioGroup.getSelected() == LEFT_OPTION; + } + + } + + /** + * Page to select a patch file. Overriding validatePage was necessary to + * allow entering a file name that already exists. + */ + protected class LocationPage extends WizardPage { + + public final static int CLIPBOARD = 1; + public final static int FILESYSTEM = 2; + public final static int WORKSPACE = 3; + + private Button cpRadio; + + private Button fsRadio; + protected Text fsPathText; + private Button fsBrowseButton; + private boolean fsBrowsed = false; + + private Button wsRadio; + protected Text wsPathText; + private Button wsBrowseButton; + private boolean wsBrowsed = false; + + protected boolean pageValid; + protected IContainer wsSelectedContainer; + protected IPath[] foldersToCreate; + protected int selectedLocation; + + /** + * The default values store used to initialize the selections. + */ + private final DefaultValuesStore store; + + class LocationPageContentProvider extends BaseWorkbenchContentProvider { + boolean showClosedProjects = false; + + public Object[] getChildren(Object element) { + if (element instanceof IWorkspace) { + // Check if closed projects should be shown + IProject[] allProjects = ((IWorkspace) element).getRoot() + .getProjects(); + if (showClosedProjects) + return allProjects; + + ArrayList accessibleProjects = new ArrayList(); + for (int i = 0; i < allProjects.length; i++) { + if (allProjects[i].isOpen()) { + accessibleProjects.add(allProjects[i]); + } + } + return accessibleProjects.toArray(); + } + return super.getChildren(element); + } + } + + class WorkspaceDialog extends TitleAreaDialog { + + protected TreeViewer wsTreeViewer; + protected Text wsFilenameText; + protected Image dlgTitleImage; + + private boolean modified = false; + + public WorkspaceDialog(Shell shell) { + super(shell); + } + + protected Control createContents(Composite parent) { + Control control = super.createContents(parent); + setTitle(CompareMessages.WorkspacePatchDialogTitle); + setMessage(CompareMessages.WorkspacePatchDialogDescription); + dlgTitleImage = CompareUIPlugin.getImageDescriptor( + ICompareUIConstants.IMG_WIZBAN_DIFF).createImage(); + setTitleImage(dlgTitleImage); + return control; + } + + protected Control createDialogArea(Composite parent) { + Composite parentComposite = (Composite) super + .createDialogArea(parent); + + // Create a composite with standard margins and spacing + Composite composite = new Composite(parentComposite, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + composite.setLayout(layout); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + composite.setFont(parentComposite.getFont()); + + getShell().setText(CompareMessages.GenerateDiffFileWizard_9); + + wsTreeViewer = new TreeViewer(composite, SWT.BORDER); + final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.widthHint = 550; + gd.heightHint = 250; + wsTreeViewer.getTree().setLayoutData(gd); + + wsTreeViewer + .setContentProvider(new LocationPageContentProvider()); + wsTreeViewer.setComparator(new ResourceComparator( + ResourceComparator.NAME)); + wsTreeViewer.setLabelProvider(new WorkbenchLabelProvider()); + wsTreeViewer.setInput(ResourcesPlugin.getWorkspace()); + + // Open to whatever is selected in the workspace field + IPath existingWorkspacePath = new Path(wsPathText.getText()); + if (existingWorkspacePath != null) { + // Ensure that this workspace path is valid + IResource selectedResource = ResourcesPlugin.getWorkspace() + .getRoot().findMember(existingWorkspacePath); + if (selectedResource != null) { + wsTreeViewer.expandToLevel(selectedResource, 0); + wsTreeViewer.setSelection(new StructuredSelection( + selectedResource)); + } + } + + final Composite group = new Composite(composite, SWT.NONE); + layout = new GridLayout(2, false); + layout.marginWidth = 0; + group.setLayout(layout); + group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, + false)); + + final Label label = new Label(group, SWT.NONE); + label.setLayoutData(new GridData()); + label.setText(CompareMessages.Fi_le_name__9); + + wsFilenameText = new Text(group, SWT.BORDER); + wsFilenameText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, + true, false)); + + setupListeners(); + + return parent; + } + + protected Button createButton(Composite parent, int id, + String label, boolean defaultButton) { + Button button = super.createButton(parent, id, label, + defaultButton); + if (id == IDialogConstants.OK_ID) { + button.setEnabled(false); + } + return button; + } + + private void validateDialog() { + String fileName = wsFilenameText.getText(); + + if (fileName.equals("")) { //$NON-NLS-1$ + if (modified) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_2); + getButton(IDialogConstants.OK_ID).setEnabled(false); + return; + } + setErrorMessage(null); + getButton(IDialogConstants.OK_ID).setEnabled(false); + return; + } + + // Make sure that the filename is valid + if (!(ResourcesPlugin.getWorkspace().validateName(fileName, + IResource.FILE)).isOK() + && modified) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_5); + getButton(IDialogConstants.OK_ID).setEnabled(false); + return; + } + + // Make sure that a container has been selected + if (getSelectedContainer() == null) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_0); + getButton(IDialogConstants.OK_ID).setEnabled(false); + return; + } + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IPath fullPath = wsSelectedContainer.getFullPath().append( + fileName); + if (workspace.getRoot().getFolder(fullPath).exists()) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_FolderExists); + getButton(IDialogConstants.OK_ID).setEnabled(false); + return; + + } + + setErrorMessage(null); + getButton(IDialogConstants.OK_ID).setEnabled(true); + } + + protected void okPressed() { + IFile file = wsSelectedContainer.getFile(new Path( + wsFilenameText.getText())); + if (file != null) + wsPathText.setText(file.getFullPath().toString()); + + validatePage(); + super.okPressed(); + } + + private IContainer getSelectedContainer() { + Object obj = ((IStructuredSelection) wsTreeViewer + .getSelection()).getFirstElement(); + if (obj instanceof IContainer) { + wsSelectedContainer = (IContainer) obj; + } else if (obj instanceof IFile) { + wsSelectedContainer = ((IFile) obj).getParent(); + } + return wsSelectedContainer; + } + + protected void cancelPressed() { + validatePage(); + super.cancelPressed(); + } + + public boolean close() { + if (dlgTitleImage != null) + dlgTitleImage.dispose(); + return super.close(); + } + + void setupListeners() { + wsTreeViewer + .addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged( + SelectionChangedEvent event) { + IStructuredSelection s = (IStructuredSelection) event + .getSelection(); + Object obj = s.getFirstElement(); + if (obj instanceof IContainer) + wsSelectedContainer = (IContainer) obj; + else if (obj instanceof IFile) { + IFile tempFile = (IFile) obj; + wsSelectedContainer = tempFile.getParent(); + wsFilenameText.setText(tempFile.getName()); + } + validateDialog(); + } + }); + + wsTreeViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + ISelection s = event.getSelection(); + if (s instanceof IStructuredSelection) { + Object item = ((IStructuredSelection) s) + .getFirstElement(); + if (wsTreeViewer.getExpandedState(item)) + wsTreeViewer.collapseToLevel(item, 1); + else + wsTreeViewer.expandToLevel(item, 1); + } + validateDialog(); + } + }); + + wsFilenameText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + modified = true; + validateDialog(); + } + }); + } + } + + LocationPage(String pageName, String title, ImageDescriptor image, + DefaultValuesStore store) { + super(pageName, title, image); + setPageComplete(false); + this.store = store; + } + + protected boolean validatePage() { + switch (selectedLocation) { + case WORKSPACE: + pageValid = validateWorkspaceLocation(); + break; + case FILESYSTEM: + pageValid = validateFilesystemLocation(); + break; + case CLIPBOARD: + pageValid = true; + break; + } + + // Avoid draw flicker by clearing error message if all is valid. + if (pageValid) { + setMessage(null); + setErrorMessage(null); + } + setPageComplete(pageValid); + return pageValid; + } + + private boolean validateFilesystemLocation() { + // Conditions for the file system location to be valid: + // - the path must be valid and non-empty + // - the path must be absolute + // - the specified file must be of type file + // - the parent must exist (new folders can be created via browse) + final String pathString = fsPathText.getText().trim(); + if (pathString.length() == 0 + || !new Path("").isValidPath(pathString)) { //$NON-NLS-1$ + if (fsBrowsed) + setErrorMessage(CompareMessages.GenerateDiffFileWizard_0); + else + setErrorMessage(CompareMessages.GenerateDiffFileWizard_browseFilesystem); + return false; + } + + final File file = new File(pathString); + if (!file.isAbsolute()) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_0); + return false; + } + + if (file.isDirectory()) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_2); + return false; + } + + if (pathString.endsWith("/") || pathString.endsWith("\\")) { //$NON-NLS-1$//$NON-NLS-2$ + setErrorMessage(CompareMessages.GenerateDiffFileWizard_3); + return false; + } + + final File parent = file.getParentFile(); + if (!(parent.exists() && parent.isDirectory())) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_3); + return false; + } + return true; + } + + private boolean validateWorkspaceLocation() { + // Conditions for the file system location to be valid: + // - a parent must be selected in the workspace tree view + // - the resource name must be valid + if (wsPathText.getText().equals("")) { //$NON-NLS-1$ + // Make sure that the field actually has a filename in it + // amd make sure that the user has had a chance to browse + if (selectedLocation == WORKSPACE && wsBrowsed) + setErrorMessage(CompareMessages.GenerateDiffFileWizard_5); + else + setErrorMessage(CompareMessages.GenerateDiffFileWizard_4); + return false; + } + + // Make sure that all the segments but the last one (i.e. project + + // all folders) exist - file doesn't have to exist. It may have + // happened that some folder refactoring has been done since this + // path was last saved. + // + // The path will always be in format project/{folders}*/file - this + // is controlled by the workspace location dialog and by + // validatePath method when path has been entered manually. + IPath pathToWorkspaceFile = new Path(wsPathText.getText()); + IStatus status = ResourcesPlugin.getWorkspace().validatePath( + wsPathText.getText(), IResource.FILE); + if (status.isOK()) { + // Trim file name from path + IPath containerPath = pathToWorkspaceFile.removeLastSegments(1); + IResource container = ResourcesPlugin.getWorkspace().getRoot() + .findMember(containerPath); + if (container == null) { + if (selectedLocation == WORKSPACE) + setErrorMessage(CompareMessages.GenerateDiffFileWizard_4); + return false; + } else if (!container.isAccessible()) { + if (selectedLocation == WORKSPACE) + setErrorMessage(CompareMessages.GenerateDiffFileWizard_ProjectClosed); + return false; + } else { + if (ResourcesPlugin.getWorkspace().getRoot().getFolder( + pathToWorkspaceFile).exists()) { + setErrorMessage(CompareMessages.GenerateDiffFileWizard_FolderExists); + return false; + } + } + } else { + setErrorMessage(status.getMessage()); + return false; + } + + return true; + } + + /** + * Answers a full path to a file system file or null if the + * user selected to save the patch in the clipboard. + */ + public File getFile() { + if (pageValid && selectedLocation == FILESYSTEM) { + return new File(fsPathText.getText().trim()); + } + if (pageValid && selectedLocation == WORKSPACE) { + final String filename = wsPathText.getText().trim(); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + final IFile file = root.getFile(new Path(filename)); + return file.getLocation().toFile(); + } + return null; + } + + /** + * Answers the workspace string entered in the dialog or + * null if the user selected to save the patch in the + * clipboard or file system. + * + * @return workspace location or null + */ + public String getWorkspaceLocation() { + if (pageValid && selectedLocation == WORKSPACE) { + final String filename = wsPathText.getText().trim(); + return filename; + } + return null; + } + + /** + * Get the selected workspace resource if the patch is to be saved in + * the workspace, or null otherwise. + * + * @return selected resource or null + */ + public IResource getResource() { + if (pageValid && selectedLocation == WORKSPACE) { + IPath pathToWorkspaceFile = new Path(wsPathText.getText() + .trim()); + // Trim file name from path + IPath containerPath = pathToWorkspaceFile.removeLastSegments(1); + return ResourcesPlugin.getWorkspace().getRoot().findMember( + containerPath); + } + return null; + } + + public void createControl(Composite parent) { + final Composite composite = new Composite(parent, SWT.NULL); + composite.setLayout(new GridLayout()); + setControl(composite); + initializeDialogUnits(composite); + + setupLocationControls(composite); + + initializeDefaultValues(); + + Dialog.applyDialogFont(parent); + + validatePage(); + + updateEnablements(); + setupListeners(); + } + + private void setupLocationControls(final Composite parent) { + final Composite composite = new Composite(parent, SWT.NULL); + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 3; + composite.setLayout(gridLayout); + composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Clipboard + GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalSpan = 3; + cpRadio = new Button(composite, SWT.RADIO); + cpRadio.setText(CompareMessages.Save_To_Clipboard_2); + cpRadio.setLayoutData(gd); + + // Filesystem + fsRadio = new Button(composite, SWT.RADIO); + fsRadio.setText(CompareMessages.Save_In_File_System_3); + + fsPathText = new Text(composite, SWT.BORDER); + gd = new GridData(GridData.FILL_HORIZONTAL); + fsPathText.setLayoutData(gd); + + fsBrowseButton = new Button(composite, SWT.PUSH); + fsBrowseButton.setText(CompareMessages.Browse____4); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); + Point minSize = fsBrowseButton.computeSize(SWT.DEFAULT, + SWT.DEFAULT, true); + data.widthHint = Math.max(widthHint, minSize.x); + fsBrowseButton.setLayoutData(data); + + // Workspace + wsRadio = new Button(composite, SWT.RADIO); + wsRadio.setText(CompareMessages.Save_In_Workspace_7); + + wsPathText = new Text(composite, SWT.BORDER); + gd = new GridData(GridData.FILL_HORIZONTAL); + wsPathText.setLayoutData(gd); + + wsBrowseButton = new Button(composite, SWT.PUSH); + wsBrowseButton.setText(CompareMessages.Browse____4); + data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); + minSize = fsBrowseButton + .computeSize(SWT.DEFAULT, SWT.DEFAULT, true); + data.widthHint = Math.max(widthHint, minSize.x); + wsBrowseButton.setLayoutData(data); + + ((GridData) cpRadio.getLayoutData()).heightHint = minSize.y; + } + + private void initializeDefaultValues() { + selectedLocation = store.getLocationSelection(); + + updateRadioButtons(); + + // We need to ensure that we have a valid workspace path - user + // could have altered workspace since last time this was saved + wsPathText.setText(store.getWorkspacePath()); + if (!validateWorkspaceLocation()) { + wsPathText.setText(""); //$NON-NLS-1$ + // Don't open wizard with an error - change to clipboard + if (selectedLocation == WORKSPACE) { + // Clear the error message caused by the workspace not + // having any workspace path entered + setErrorMessage(null); + selectedLocation = CLIPBOARD; + updateRadioButtons(); + } + } + // Do the same thing for the filesystem field + fsPathText.setText(store.getFilesystemPath()); + if (!validateFilesystemLocation()) { + fsPathText.setText(""); //$NON-NLS-1$ + if (selectedLocation == FILESYSTEM) { + setErrorMessage(null); + selectedLocation = CLIPBOARD; + updateRadioButtons(); + } + } + + } + + private void updateRadioButtons() { + cpRadio.setSelection(selectedLocation == CLIPBOARD); + fsRadio.setSelection(selectedLocation == FILESYSTEM); + wsRadio.setSelection(selectedLocation == WORKSPACE); + } + + private void setupListeners() { + cpRadio.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event event) { + selectedLocation = CLIPBOARD; + validatePage(); + updateEnablements(); + } + }); + fsRadio.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event event) { + selectedLocation = FILESYSTEM; + validatePage(); + updateEnablements(); + } + }); + + wsRadio.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event event) { + selectedLocation = WORKSPACE; + validatePage(); + updateEnablements(); + } + }); + + ModifyListener pathTextModifyListener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + validatePage(); + } + }; + fsPathText.addModifyListener(pathTextModifyListener); + wsPathText.addModifyListener(pathTextModifyListener); + + fsBrowseButton.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event event) { + final FileDialog dialog = new FileDialog(getShell(), + SWT.PRIMARY_MODAL | SWT.SAVE); + if (pageValid) { + final File file = new File(fsPathText.getText()); + dialog.setFilterPath(file.getParent()); + } + dialog.setText(CompareMessages.Save_Patch_As_5); + dialog.setFileName(CompareMessages.patch_txt_6); + final String path = dialog.open(); + fsBrowsed = true; + if (path != null) { + fsPathText.setText(new Path(path).toOSString()); + } + validatePage(); + } + }); + + wsBrowseButton.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event event) { + final WorkspaceDialog dialog = new WorkspaceDialog( + getShell()); + wsBrowsed = true; + dialog.open(); + validatePage(); + } + }); + + } + + public void updateEnablements() { + // Enable and disable controls based on the selected radio button. + fsBrowseButton.setEnabled(selectedLocation == FILESYSTEM); + fsPathText.setEnabled(selectedLocation == FILESYSTEM); + if (selectedLocation == FILESYSTEM) + fsBrowsed = false; + wsPathText.setEnabled(selectedLocation == WORKSPACE); + wsBrowseButton.setEnabled(selectedLocation == WORKSPACE); + if (selectedLocation == WORKSPACE) + wsBrowsed = false; + } + + public int getSelectedLocation() { + return selectedLocation; + } + + } + + public class OptionsPage extends WizardPage { + + public final static int FORMAT_UNIFIED = 1; + public final static int FORMAT_CONTEXT = 2; + public final static int FORMAT_STANDARD = 3; + + public final static int ROOT_WORKSPACE = 1; + public final static int ROOT_PROJECT = 2; + public final static int ROOT_SELECTION = 3; + public final static int ROOT_CUSTOM = 4; + + protected boolean initialized = false; + + protected Button unifiedDiffOption; + protected Button contextDiffOption; + protected Button regularDiffOption; + + protected Button unified_workspaceRelativeOption; + protected Button unified_projectRelativeOption; + protected Button unified_selectionRelativeOption; + + protected Group unifiedGroup; + protected Group diffTypeGroup; + + protected final RadioButtonGroup diffTypeRadioGroup = new RadioButtonGroup(); + protected final RadioButtonGroup unifiedRadioGroup = new RadioButtonGroup(); + + protected boolean patchHasCommonRoot=true; + protected IPath patchRoot=ResourcesPlugin.getWorkspace().getRoot().getFullPath(); + + public IPath getPatchRoot() { + return patchRoot; + } + + private final DefaultValuesStore store; + + public OptionsPage(String pageName, String title, + ImageDescriptor titleImage, DefaultValuesStore store) { + super(pageName, title, titleImage); + this.store = store; + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NULL); + GridLayout layout = new GridLayout(); + composite.setLayout(layout); + composite.setLayoutData(new GridData()); + setControl(composite); + + diffTypeGroup = new Group(composite, SWT.NONE); + layout = new GridLayout(); + layout.marginHeight = 0; + diffTypeGroup.setLayout(layout); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL + | GridData.GRAB_HORIZONTAL); + diffTypeGroup.setLayoutData(data); + diffTypeGroup.setText(CompareMessages.Diff_output_format_12); + + unifiedDiffOption = new Button(diffTypeGroup, SWT.RADIO); + unifiedDiffOption + .setText(CompareMessages.Unified__format_required_by_Compare_With_Patch_feature__13); + + contextDiffOption = new Button(diffTypeGroup, SWT.RADIO); + contextDiffOption.setText(CompareMessages.Context_14); + regularDiffOption = new Button(diffTypeGroup, SWT.RADIO); + regularDiffOption.setText(CompareMessages.Standard_15); + + diffTypeRadioGroup.add(FORMAT_UNIFIED, unifiedDiffOption); + diffTypeRadioGroup.add(FORMAT_CONTEXT, contextDiffOption); + diffTypeRadioGroup.add(FORMAT_STANDARD, regularDiffOption); + + // Unified Format Options + unifiedGroup = new Group(composite, SWT.None); + layout = new GridLayout(); + layout.numColumns = 2; + unifiedGroup.setLayout(layout); + data = new GridData(GridData.HORIZONTAL_ALIGN_FILL + | GridData.GRAB_HORIZONTAL); + unifiedGroup.setLayoutData(data); + unifiedGroup.setText(CompareMessages.GenerateDiffFileWizard_10); + + unified_workspaceRelativeOption = new Button(unifiedGroup, + SWT.RADIO); + unified_workspaceRelativeOption + .setText(CompareMessages.GenerateDiffFileWizard_6); + unified_workspaceRelativeOption.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, false, 2, 1)); + + unified_projectRelativeOption = new Button(unifiedGroup, SWT.RADIO); + unified_projectRelativeOption + .setText(CompareMessages.GenerateDiffFileWizard_7); + unified_projectRelativeOption.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, false, 2, 1)); + + unified_selectionRelativeOption = new Button(unifiedGroup, + SWT.RADIO); + unified_selectionRelativeOption + .setText(CompareMessages.GenerateDiffFileWizard_8); + unified_selectionRelativeOption.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, true, 2, 1)); + + unifiedRadioGroup.add(ROOT_WORKSPACE, + unified_workspaceRelativeOption); + unifiedRadioGroup.add(ROOT_PROJECT, unified_projectRelativeOption); + unifiedRadioGroup.add(ROOT_SELECTION, + unified_selectionRelativeOption); + + Dialog.applyDialogFont(parent); + + initializeDefaultValues(); + + // add listeners + unifiedDiffOption.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + setEnableUnifiedGroup(true); + updateEnablements(); + diffTypeRadioGroup.setSelection(FORMAT_UNIFIED, false); + } + }); + + contextDiffOption.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + setEnableUnifiedGroup(false); + updateEnablements(); + diffTypeRadioGroup.setSelection(FORMAT_CONTEXT, false); + } + }); + + regularDiffOption.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + setEnableUnifiedGroup(false); + updateEnablements(); + diffTypeRadioGroup.setSelection(FORMAT_STANDARD, false); + } + }); + + unified_workspaceRelativeOption + .addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + unifiedRadioGroup.setSelection(ROOT_WORKSPACE, + false); + } + }); + + unified_projectRelativeOption + .addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + unifiedRadioGroup.setSelection(ROOT_PROJECT, false); + } + }); + + unified_selectionRelativeOption + .addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + unifiedRadioGroup.setSelection(ROOT_SELECTION, + false); + } + }); + + updateEnablements(); + + performSpecificActions(); + + // update selection + diffTypeRadioGroup.selectEnabledOnly(); + unifiedRadioGroup.selectEnabledOnly(); + } + + protected void performSpecificActions() { + return; + } + + public int getFormatSelection() { + return diffTypeRadioGroup.getSelected(); + } + + public int getRootSelection() { + return unifiedRadioGroup.getSelected(); + } + + public String getPath() { + return unified_customRelativeText.getText(); + } + + public Group getUnifiedGroup() { + return unifiedGroup; + } + + public Group getDiffTypeGroup() { + return diffTypeGroup; + } + + public RadioButtonGroup getDiffTypeRadioGroup() { + return diffTypeRadioGroup; + } + + public RadioButtonGroup getUnifiedRadioGroup() { + return unifiedRadioGroup; + } + + + public Button getUnifiedDiffOption() { + return unifiedDiffOption; + } + + public Button getContextDiffOption() { + return contextDiffOption; + } + + public Button getRegularDiffOption() { + return regularDiffOption; + } + + public Button getUnified_workspaceRelativeOption() { + return unified_workspaceRelativeOption; + } + + public Button getUnified_projectRelativeOption() { + return unified_projectRelativeOption; + } + + public Button getUnified_selectionRelativeOption() { + return unified_selectionRelativeOption; + } + + private void initializeDefaultValues() { + // Radio buttons for format + diffTypeRadioGroup.setSelection(store.getFormatSelection(), true); + // Radio buttons for patch root + unifiedRadioGroup.setSelection(store.getRootSelection(), true); + } + + protected void updateEnablements() { + if(diffTypeRadioGroup.selected != FORMAT_UNIFIED) + setEnableUnifiedGroup(false); + } + + public void updateDiffTypeEnablements(int[] disabled, int enabled) { + diffTypeRadioGroup.setEnablement(false, disabled, enabled); + } + + public void updateUnifiedEnablements(int[] disabled, int enabled) { + unifiedRadioGroup.setEnablement(false, disabled, enabled); + } + + protected void setEnableUnifiedGroup(boolean enabled) { + unifiedRadioGroup.setEnablement(enabled, new int[] { + ROOT_WORKSPACE, ROOT_PROJECT, ROOT_SELECTION }); + + //temporary until we figure out best way to fix synchronize view selection + if (!unifiedSelectionEnabled) + unifiedRadioGroup.setEnablement(false, new int[] {ROOT_SELECTION}); + } + + } + + /** + * Class to retrieve and store the default selected values. + */ + protected final class DefaultValuesStore { + + private static final String PREF_LAST_SELECTION = "org.eclipse.compare.internal.GenerateDiffFileWizard.PatchFileSelectionPage.lastselection"; //$NON-NLS-1$ + private static final String PREF_LAST_FS_PATH = "org.eclipse.compare.internal.GenerateDiffFileWizard.PatchFileSelectionPage.filesystem.path"; //$NON-NLS-1$ + private static final String PREF_LAST_WS_PATH = "org.eclipse.compare.internal.GenerateDiffFileWizard.PatchFileSelectionPage.workspace.path"; //$NON-NLS-1$ + private static final String PREF_LAST_AO_FORMAT = "org.eclipse.compare.internal.GenerateDiffFileWizard.OptionsPage.diff.format"; //$NON-NLS-1$ + private static final String PREF_LAST_AO_ROOT = "org.eclipse.compare.internal.GenerateDiffFileWizard.OptionsPage.patch.root"; //$NON-NLS-1$ + + private final IDialogSettings dialogSettings; + + public DefaultValuesStore() { + dialogSettings = CompareUIPlugin.getDefault().getDialogSettings(); + } + + public int getLocationSelection() { + int value = LocationPage.CLIPBOARD; + try { + value = dialogSettings.getInt(PREF_LAST_SELECTION); + } catch (NumberFormatException e) { + // Ignore + } + + switch (value) { + case LocationPage.FILESYSTEM: + case LocationPage.WORKSPACE: + case LocationPage.CLIPBOARD: + return value; + default: + return LocationPage.CLIPBOARD; + } + } + + public String getFilesystemPath() { + final String path = dialogSettings.get(PREF_LAST_FS_PATH); + return path != null ? path : ""; //$NON-NLS-1$ + } + + public String getWorkspacePath() { + final String path = dialogSettings.get(PREF_LAST_WS_PATH); + return path != null ? path : ""; //$NON-NLS-1$ + } + + public int getFormatSelection() { + int value = OptionsPage.FORMAT_UNIFIED; + try { + value = dialogSettings.getInt(PREF_LAST_AO_FORMAT); + } catch (NumberFormatException e) { + // Ignore + } + + switch (value) { + case OptionsPage.FORMAT_UNIFIED: + case OptionsPage.FORMAT_CONTEXT: + case OptionsPage.FORMAT_STANDARD: + return value; + default: + return OptionsPage.FORMAT_UNIFIED; + } + } + + public int getRootSelection() { + int value = OptionsPage.ROOT_WORKSPACE; + try { + value = dialogSettings.getInt(PREF_LAST_AO_ROOT); + } catch (NumberFormatException e) { + // Ignore + } + + switch (value) { + case OptionsPage.ROOT_WORKSPACE: + case OptionsPage.ROOT_PROJECT: + case OptionsPage.ROOT_SELECTION: + return value; + default: + return OptionsPage.ROOT_WORKSPACE; + } + } + + public void storeLocationSelection(int defaultSelection) { + dialogSettings.put(PREF_LAST_SELECTION, defaultSelection); + } + + public void storeFilesystemPath(String path) { + dialogSettings.put(PREF_LAST_FS_PATH, path); + } + + public void storeWorkspacePath(String path) { + dialogSettings.put(PREF_LAST_WS_PATH, path); + } + + public void storeOutputFormat(int selection) { + dialogSettings.put(PREF_LAST_AO_FORMAT, selection); + } + + public void storePatchRoot(int selection) { + dialogSettings.put(PREF_LAST_AO_ROOT, selection); + } + } + + protected DirectionSelectionPage directionSelectionPage; + protected LocationPage locationPage; + protected OptionsPage optionsPage; + + protected final DefaultValuesStore defaultValuesStore; + + //temporary until we figure out best way to fix synchronize view selection + protected boolean unifiedSelectionEnabled; + + private DocumentMerger merger; + private IDocument leftDoc; + private IDocument rightDoc; + private String leftLabel; + private String rightLabel; + private String leftPath; + private String rightPath; + private boolean rightToLeft; + + private boolean targetFileEdited = false; + + private Text unified_customRelativeText; + private Button unified_customRelativeOption; + + public GenerateDiffFileWizard() { + super(); + setWindowTitle(CompareMessages.GenerateLocalDiff_title); + initializeDefaultPageImageDescriptor(); + defaultValuesStore = new DefaultValuesStore(); + } + + public GenerateDiffFileWizard(DocumentMerger merger, IDocument leftDoc, + IDocument rightDoc, String leftLabel, String rightLabel, + String leftPath, String rightPath, boolean rightToLeft, boolean unifiedSelectionEnabled) { + this(); + this.merger = merger; + this.leftDoc = leftDoc; + this.rightDoc = rightDoc; + this.leftLabel = leftLabel; + this.rightLabel = rightLabel; + this.leftPath = leftPath; + this.rightPath = rightPath; + this.rightToLeft = rightToLeft; + this.unifiedSelectionEnabled = unifiedSelectionEnabled; + } + + public void addPages() { + String pageTitle = CompareMessages.GenerateLocalDiff_pageTitle; + String pageDescription = CompareMessages.GenerateLocalDiff_Specify_the_file_which_contributes_the_changes; + directionSelectionPage = new DirectionSelectionPage( + pageTitle, + pageTitle, + CompareUIPlugin + .getImageDescriptor(ICompareUIConstants.IMG_WIZBAN_DIFF)); + directionSelectionPage.setDescription(pageDescription); + addPage(directionSelectionPage); + + pageTitle = CompareMessages.GenerateLocalDiff_pageTitle; + pageDescription = CompareMessages.GenerateLocalDiff_pageDescription; + locationPage = new LocationPage(pageTitle, pageTitle, CompareUIPlugin + .getImageDescriptor(ICompareUIConstants.IMG_WIZBAN_DIFF), + defaultValuesStore); + locationPage.setDescription(pageDescription); + addPage(locationPage); + + pageTitle = CompareMessages.Advanced_options_19; + pageDescription = CompareMessages.Configure_the_options_used_for_the_CVS_diff_command_20; + optionsPage = new OptionsPage(pageTitle, pageTitle, CompareUIPlugin + .getImageDescriptor(ICompareUIConstants.IMG_WIZBAN_DIFF), + defaultValuesStore) { + + public void setVisible(boolean visible) { + super.setVisible(visible); + if (!initialized && visible) { + File toFile = null; + if (directionSelectionPage.isRightToLeft()) { + unified_customRelativeText.setText(leftLabel); + } else { + unified_customRelativeText.setText(rightLabel); + } + targetFileEdited = true; + } + } + + protected void performSpecificActions() { + createCustomRelativeControl(); + } + + protected void updateEnablements() { + diffTypeRadioGroup.setEnablement(false, new int[] { FORMAT_CONTEXT, + FORMAT_STANDARD }, FORMAT_UNIFIED); + unifiedRadioGroup.setEnablement(false, new int[] { + ROOT_PROJECT, ROOT_SELECTION, ROOT_WORKSPACE } ); + } + + protected void setEnableUnifiedGroup(boolean enabled) { + unifiedRadioGroup.setEnablement(false, new int[] { + ROOT_WORKSPACE, ROOT_PROJECT, ROOT_SELECTION }); + + //temporary until we figure out best way to fix synchronize view selection + if (!unifiedSelectionEnabled) + unifiedRadioGroup.setEnablement(false, new int[] {ROOT_SELECTION}); + } + }; + optionsPage.setDescription(pageDescription); + addPage(optionsPage); + } + + private void createCustomRelativeControl() { + Group unifiedGroup = optionsPage.getUnifiedGroup(); + final RadioButtonGroup unifiedRadioGroup = optionsPage.getUnifiedRadioGroup(); + unified_customRelativeOption = new Button(unifiedGroup, SWT.RADIO); + unified_customRelativeOption.setText(CompareMessages.GenerateDiffFileWizard_13); + unified_customRelativeOption.setSelection(true); + unified_customRelativeOption.setLayoutData(new GridData( + SWT.BEGINNING, SWT.CENTER, false, false, 1, 1)); + + unified_customRelativeText = new Text(unifiedGroup, SWT.BORDER); + unified_customRelativeText.setLayoutData(new GridData( + SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + optionsPage.getUnifiedRadioGroup().add(OptionsPage.ROOT_CUSTOM, unified_customRelativeOption); + unified_customRelativeOption.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + unifiedRadioGroup.setSelection(OptionsPage.ROOT_CUSTOM, false); + } + }); + optionsPage.updateDiffTypeEnablements(new int[] {OptionsPage.FORMAT_CONTEXT, OptionsPage.FORMAT_STANDARD}, OptionsPage.FORMAT_UNIFIED); + optionsPage.updateUnifiedEnablements(new int[] {OptionsPage.ROOT_PROJECT, OptionsPage.ROOT_WORKSPACE, OptionsPage.ROOT_SELECTION}, OptionsPage.ROOT_CUSTOM); + } + + /** + * Declares the wizard banner iamge descriptor + */ + protected void initializeDefaultPageImageDescriptor() { + final String iconPath = "icons/full/"; //$NON-NLS-1$ + try { + final URL installURL = CompareUIPlugin.getDefault().getBundle() + .getEntry("/"); //$NON-NLS-1$ + final URL url = new URL(installURL, iconPath + + "wizards/newconnect_wiz.gif"); //$NON-NLS-1$ + ImageDescriptor desc = ImageDescriptor.createFromURL(url); + setDefaultPageImageDescriptor(desc); + } catch (MalformedURLException e) { + // Should not happen. Ignore. + } + } + + /* + * (Non-javadoc) Method declared on IWizard. + */ + public boolean needsProgressMonitor() { + return true; + } + + public boolean performFinish() { + final int location = locationPage.getSelectedLocation(); + final File file = location != LocationPage.CLIPBOARD ? locationPage + .getFile() : null; + + if (!(file == null || validateFile(file))) { + return false; + } + + //Validation of patch root + if(optionsPage.getRootSelection() == OptionsPage.ROOT_CUSTOM) { + String path = optionsPage.getPath(); + IFile file2; + try { + file2 = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path)); + } catch(IllegalArgumentException e) { + final String title = CompareMessages.GenerateLocalDiff_3; + final String msg = CompareMessages.GenerateLocalDiff_4; + final MessageDialog dialog = new MessageDialog(getShell(), title, + null, msg, MessageDialog.ERROR, + new String[] { IDialogConstants.OK_LABEL }, 0); + dialog.open(); + return false; + } + if(!validateFile2(file2)) { + return false; + } + } + + // Create the patch + generateDiffFile(file); + + // Refresh workspace if necessary and save default selection. + switch (location) { + case LocationPage.WORKSPACE: + final String workspaceResource = locationPage + .getWorkspaceLocation(); + if (workspaceResource != null) { + defaultValuesStore + .storeLocationSelection(LocationPage.WORKSPACE); + defaultValuesStore.storeWorkspacePath(workspaceResource); + } else { + // Problem with workspace location, choose clipboard next time + defaultValuesStore + .storeLocationSelection(LocationPage.CLIPBOARD); + } + break; + case LocationPage.FILESYSTEM: + defaultValuesStore.storeFilesystemPath(file.getPath()); + defaultValuesStore.storeLocationSelection(LocationPage.FILESYSTEM); + break; + case LocationPage.CLIPBOARD: + defaultValuesStore.storeLocationSelection(LocationPage.CLIPBOARD); + break; + default: + return false; + } + + defaultValuesStore.storeOutputFormat(optionsPage.getFormatSelection()); + defaultValuesStore.storePatchRoot(optionsPage.getRootSelection()); + + return true; + } + + private void generateDiffFile(File file) { + String toPath = null; + if (targetFileEdited) { + toPath = optionsPage.getPath(); + } else { + File toFile = null; + if (directionSelectionPage.isRightToLeft()) { + toFile = new File(leftPath); + } else { + toFile = new File(rightPath); + } + toPath = toFile.getPath(); + } + String oldPath, newPath; + if(directionSelectionPage.isRightToLeft()){ + oldPath =this.rightPath; + newPath =this.leftPath; + } else { + oldPath =this.leftLabel; + newPath =this.rightPath; + } + + UnifiedDiffFormatter formatter = new UnifiedDiffFormatter(merger, + leftDoc, rightDoc, oldPath, newPath, directionSelectionPage + .isRightToLeft()); + try { + if (file == null) { + formatter.generateDiff(getShell().getDisplay()); + } else { + formatter.generateDiff(file); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public boolean validateFile(File file) { + if (file == null) + return false; + + // Consider file valid if it doesn't exist for now. + if (!file.exists()) + return true; + + // The file exists. + if (!file.canWrite()) { + final String title = CompareMessages.GenerateLocalDiff_1; + final String msg = CompareMessages.GenerateLocalDiff_2; + final MessageDialog dialog = new MessageDialog(getShell(), title, + null, msg, MessageDialog.ERROR, + new String[] { IDialogConstants.OK_LABEL }, 0); + dialog.open(); + return false; + } + + final String title = CompareMessages.GenerateLocalDiff_overwriteTitle; + final String msg = CompareMessages.GenerateLocalDiff_overwriteMsg; + final MessageDialog dialog = new MessageDialog(getShell(), title, null, + msg, MessageDialog.QUESTION, new String[] { + IDialogConstants.YES_LABEL, + IDialogConstants.CANCEL_LABEL }, 0); + dialog.open(); + if (dialog.getReturnCode() != 0) { + return false; + } + return true; + } + + public boolean validateFile2(IFile file) { + if (file == null) + return false; + + // Consider file invalid if it doesn't exist. + if (!file.exists()) { + final String title = CompareMessages.GenerateLocalDiff_3; + final String msg = CompareMessages.GenerateLocalDiff_4; + final MessageDialog dialog = new MessageDialog(getShell(), title, + null, msg, MessageDialog.ERROR, + new String[] { IDialogConstants.OK_LABEL }, 0); + dialog.open(); + return false; + } + + return true; + } + + /** + * The class maintain proper selection of radio button within the group: + * + */ + protected class RadioButtonGroup { + + private List buttons = new ArrayList(3); + + private int selected = 0; + + public void add(int buttonCode, Button button) { + if (button != null && (button.getStyle() & SWT.RADIO) != 0) { + if (button.getSelection() && !buttons.isEmpty()) { + deselectAll(); + selected = buttonCode - 1; + } + buttons.add(buttonCode - 1, button); + } + } + + public int getSelected() { + return selected + 1; + } + + public int setSelection(int buttonCode, boolean selectEnabledOnly) { + deselectAll(); + + ((Button) buttons.get(buttonCode - 1)).setSelection(true); + selected = buttonCode - 1; + if (selectEnabledOnly) + selected = selectEnabledOnly() - 1; + return getSelected(); + } + + public int selectEnabledOnly() { + deselectAll(); + Button selectedButton = (Button) buttons.get(selected); + if (!selectedButton.isEnabled()) { + // If the button is disabled, set selection to an enabled one + for (Iterator iterator = buttons.iterator(); iterator.hasNext();) { + Button b = (Button) iterator.next(); + if (b.isEnabled()) { + b.setSelection(true); + selected = buttons.indexOf(b); + return selected + 1; + } + } + // If none found, reset the initial selection + selectedButton.setSelection(true); + } else { + // Because selection has been cleared, set it again + selectedButton.setSelection(true); + } + // Return selected button's code so the value can be stored + return getSelected(); + } + + public void setEnablement(boolean enabled, int[] buttonsToChange, + int defaultSelection) { + // Enable (or disable) given buttons + for (int i = 0; i < buttonsToChange.length; i++) { + ((Button) this.buttons.get(buttonsToChange[i] - 1)) + .setEnabled(enabled); + } + // Check whether the selected button is enabled + if (!((Button) this.buttons.get(selected)).isEnabled()) { + if (defaultSelection != -1) + // Set the default selection and check if it's enabled + setSelection(defaultSelection, true); + else + // No default selection is given, select any enabled button + selectEnabledOnly(); + } + } + + public void setEnablement(boolean enabled, int[] buttonsToChange) { + // Value -1 means that no default selection is given + setEnablement(enabled, buttonsToChange, -1); + } + + private void deselectAll() { + for (Iterator iterator = buttons.iterator(); iterator.hasNext();) + ((Button) iterator.next()).setSelection(false); + } + } + +} Index: compare/org/eclipse/compare/internal/CreatePatchAction.java =================================================================== RCS file: compare/org/eclipse/compare/internal/CreatePatchAction.java diff -N compare/org/eclipse/compare/internal/CreatePatchAction.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compare/org/eclipse/compare/internal/CreatePatchAction.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2008 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.compare.internal; + +import java.lang.reflect.Field; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.contentmergeviewer.ContentMergeViewer; +import org.eclipse.compare.contentmergeviewer.TextMergeViewer; +import org.eclipse.compare.internal.merge.DocumentMerger; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.text.Assert; +import org.eclipse.jface.text.IDocument; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.part.FileEditorInput; + +public class CreatePatchAction extends Action { + + private TextMergeViewer viewer; + private boolean rightToLeft; + + public CreatePatchAction(TextMergeViewer viewer, boolean rightToLeft) { + super(CompareMessages.CreatePatchActionTitle); + this.viewer = viewer; + this.rightToLeft = rightToLeft; + } + + public void run() { + GenerateDiffFileWizard.run(getMerger(), getDocument(true), + getDocument(false), getLabel(true), getLabel(false), + getLabel(true), getLabel(false), viewer.getControl().getShell(), + rightToLeft); + getPath(true); + } + + private String getPath(boolean left) { + try { + Field ciField = null; + try { + if (left) { + ciField = TextMergeViewer.class + .getDeclaredField("fLeftContributor"); //$NON-NLS-1$ + } else { + ciField = TextMergeViewer.class + .getDeclaredField("fRightContributor"); //$NON-NLS-1$ + } + } catch (SecurityException ex){ + CompareUIPlugin.log(ex); + } catch (NoSuchFieldException ex){ + CompareUIPlugin.log(ex); + } + Assert.isNotNull(ciField); + ciField.setAccessible(true); + try { + Object ciObj = ciField.get(viewer); + Class clazz = ciObj.getClass(); + try { + Field field = clazz.getDeclaredField("fDocumentKey"); //$NON-NLS-1$ + field.setAccessible(true); + IEditorInput editorInput = (IEditorInput) field.get(ciObj); + if (editorInput instanceof FileEditorInput) { + FileEditorInput fei = (FileEditorInput) editorInput; + String path = fei.getFile().getProjectRelativePath().toString(); + return path; + } + } catch (SecurityException ex){ + CompareUIPlugin.log(ex); + } catch (NoSuchFieldException ex){ + CompareUIPlugin.log(ex); + } + } catch (IllegalArgumentException ex){ + CompareUIPlugin.log(ex); + } catch (IllegalAccessException ex){ + CompareUIPlugin.log(ex); + } catch (NullPointerException ex){ + CompareUIPlugin.log(ex); + } catch (ExceptionInInitializerError ex){ + CompareUIPlugin.log(ex); + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; //$NON-NLS-1$ + } + + private String getLabel(boolean left) { + try { + Field field = ContentMergeViewer.class + .getDeclaredField("fCompareConfiguration"); //$NON-NLS-1$ + field.setAccessible(true); + CompareConfiguration cc = (CompareConfiguration) field.get(viewer); + if (left) { + field = CompareConfiguration.class + .getDeclaredField("fLeftLabel"); //$NON-NLS-1$ + } else { + field = CompareConfiguration.class + .getDeclaredField("fRightLabel"); //$NON-NLS-1$ + } + field.setAccessible(true); + return (String) field.get(cc); + } catch (SecurityException ex){ + CompareUIPlugin.log(ex); + } catch (NoSuchFieldException ex){ + CompareUIPlugin.log(ex); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public DocumentMerger getMerger() { + try { + Field field = TextMergeViewer.class.getDeclaredField("fMerger"); //$NON-NLS-1$ + field.setAccessible(true); + return (DocumentMerger) field.get(viewer); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private IDocument getDocument(boolean left) { + try { + Field field = null; + if (left) { + field = TextMergeViewer.class.getDeclaredField("fLeft"); //$NON-NLS-1$ + } else { + field = TextMergeViewer.class.getDeclaredField("fRight"); //$NON-NLS-1$ + } + field.setAccessible(true); + MergeSourceViewer msv = (MergeSourceViewer) field.get(viewer); + field = MergeSourceViewer.class + .getDeclaredField("fRememberedDocument"); //$NON-NLS-1$ + field.setAccessible(true); + return (IDocument) field.get(msv); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} Index: compare/org/eclipse/compare/internal/UnifiedDiffFormatter.java =================================================================== RCS file: compare/org/eclipse/compare/internal/UnifiedDiffFormatter.java diff -N compare/org/eclipse/compare/internal/UnifiedDiffFormatter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compare/org/eclipse/compare/internal/UnifiedDiffFormatter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,390 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Krzysztof Poglodzinski (intuicje@gmail.com) - initial API and implementation + * Mariusz Tanski (mariusztanski@gmail.com) - initial API and implementation + * Kacper Zdanowicz (kacper.zdanowicz@gmail.com) - initial API and implementation + * IBM Corportation - initial API and implementation + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import org.eclipse.compare.internal.merge.DocumentMerger; +import org.eclipse.compare.internal.merge.DocumentMerger.Diff; +import org.eclipse.compare.rangedifferencer.RangeDifference; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Display; + +import com.ibm.icu.text.DateFormat; + +public class UnifiedDiffFormatter { + + public static final char RIGHT_CONTRIBUTOR = 'R'; + public static final char LEFT_CONTRIBUTOR = 'L'; + + public final static int FORMAT_UNIFIED = 1; + public final static int FORMAT_CONTEXT = 2; + public final static int FORMAT_STANDARD = 3; + + public final static int NUMBER_OF_CONTEXT_LINES = 3; + + public static final String INDEX_MARKER = "Index: "; //$NON-NLS-1$ + public static final String DELIMITER = "==================================================================="; //$NON-NLS-1$ + public static final String OLD_FILE_PREFIX = "--- "; //$NON-NLS-1$ + public static final String NEW_FILE_PREFIX = "+++ "; //$NON-NLS-1$ + public static final String OLD_LINE_PREFIX = "-"; //$NON-NLS-1$ + public static final String NEW_LINE_PREFIX = "+"; //$NON-NLS-1$ + public static final String CONTEXT_LINE_PREFIX = " "; //$NON-NLS-1$ + public static final String RANGE_INFORMATION_PREFIX = "@@ -"; //$NON-NLS-1$ + public static final String RANGE_INFORMATION_AFFIX = " @@"; //$NON-NLS-1$ + + private DocumentMerger merger; + private IDocument leftDoc; + private IDocument rightDoc; + private String oldPath; + private String newPath; + private boolean rightToLeft; + + public UnifiedDiffFormatter(DocumentMerger merger, IDocument leftDoc, + IDocument rightDoc, String oldPath, String newPath, boolean rightToLeft) { + this.merger = merger; + this.leftDoc = leftDoc; + this.rightDoc = rightDoc; + this.oldPath =oldPath; + this.newPath =newPath; + System.out.println(newPath); + this.rightToLeft = rightToLeft; + } + + public void generateDiff(Display dis) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bos); + + createDiff(ps); + ps.close(); + + TextTransfer plainTextTransfer = TextTransfer.getInstance(); + Clipboard clipboard = new Clipboard(dis); + clipboard.setContents(new String[] { bos.toString() }, + new Transfer[] { plainTextTransfer }); + clipboard.dispose(); + + bos.close(); + } + + public void generateDiff(File file) throws IOException { + FileOutputStream fos = null; + PrintStream ps = null; + try { + fos = new FileOutputStream(file); + try { + ps = new PrintStream(fos); + createDiff(ps); + if (ps.checkError()) { + throw new IOException("Error while writing patch file: " //$NON-NLS-1$ + + file); + } + } finally { + if (ps != null) { + ps.close(); + } + } + } finally { + if (fos != null) { + fos.close(); + } + } + } + + /** + * Creates a diff which isn't in strict Unix format. + * GNU diff command has default line format different than Eclipse has. + * This issue is the subject of bug 259636. + * + * @param output output to which diff will be written + * + */ + public void createDiff(PrintStream output) { + createDiff(output, false); + } + + public void createDiff(PrintStream output, boolean strictUnixFormat) { + ArrayList allDiffs = merger.getAllDiffs(); + // If the first block isn't the only one, or first block is different + if (allDiffs.size() > 1 || isPartDifferent(allDiffs, 0)) { + output.println(INDEX_MARKER + oldPath); + output.println(DELIMITER); + Date oldDate = Calendar.getInstance(Locale.US).getTime(); + Date newDate = Calendar.getInstance(Locale.US).getTime(); + String oldDateFormat = DateFormat.getDateTimeInstance().format(oldDate); + String newDateFormat = DateFormat.getDateTimeInstance().format(newDate); + output.println(OLD_FILE_PREFIX + oldPath + '\t' + + oldDateFormat + " -0000"); //$NON-NLS-1$ + output.println(NEW_FILE_PREFIX + newPath + '\t' + + newDateFormat + " -0000"); //$NON-NLS-1$ + + boolean firstHunk = true; + Hunk currentHunk = null; + + int currentLineNumberOld = 0; + int currentLineNumberNew = 0; + + for (int partNumber = 0; partNumber < allDiffs.size(); partNumber++) { + + ArrayList oldPart = getPart(partNumber, LEFT_CONTRIBUTOR); + ArrayList newPart = getPart(partNumber, RIGHT_CONTRIBUTOR); + + if (isPartDifferent(allDiffs, partNumber)) { + // This part has some changes + if (firstHunk) { + // Hunk will start with changed block + currentHunk = new Hunk(0, 0, strictUnixFormat); + firstHunk = false; + } + if (partNumber == (allDiffs.size() - 1)) { + // If it is the last part + currentHunk.addPartRangeToOld(oldPart, 0, oldPart + .size(), true); + currentHunk.addPartRangeToNew(newPart, 0, newPart + .size(), true); + } else { + currentHunk.addPartRangeToOld(oldPart, 0, oldPart + .size(), false); + currentHunk.addPartRangeToNew(newPart, 0, newPart + .size(), false); + } + } else { + if (firstHunk) { + // Hunk will start with context + currentHunk = new Hunk((oldPart.size() - 1) - NUMBER_OF_CONTEXT_LINES, + (oldPart.size() - 1) - NUMBER_OF_CONTEXT_LINES, strictUnixFormat); + firstHunk = false; + currentHunk.addPartRangeToBoth(oldPart, + (oldPart.size() - 1) - NUMBER_OF_CONTEXT_LINES, oldPart.size(), false); + } else { + if (partNumber == (allDiffs.size() - 1)) { + // If it is the last part + currentHunk.addPartRangeToBoth(oldPart, 0, NUMBER_OF_CONTEXT_LINES, true); + } else { + if (oldPart.size() - 1 < 2*NUMBER_OF_CONTEXT_LINES) { + // Context too short to start new hunk + currentHunk.addPartRangeToBoth(oldPart, 0, + oldPart.size(), false); + } else { + // Context long enough to start new hunk + currentHunk.addPartRangeToBoth(oldPart, 0, NUMBER_OF_CONTEXT_LINES, + false); + currentHunk.printTo(output); + currentHunk = new Hunk(currentLineNumberOld + + (oldPart.size() - 1) - NUMBER_OF_CONTEXT_LINES, + currentLineNumberNew + (oldPart.size() - 1) + - NUMBER_OF_CONTEXT_LINES, strictUnixFormat); + currentHunk.addPartRangeToBoth(oldPart, + (oldPart.size() - 1) - NUMBER_OF_CONTEXT_LINES, + oldPart.size(), false); + } + } + } + } + currentLineNumberOld += oldPart.size(); + currentLineNumberNew += newPart.size(); + } + // Print the last hunk + currentHunk.printTo(output); + } + } + + private ArrayList getPart(int nr, char side) { + try { + String s = extract(nr, side).replaceAll("\r\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + s.replaceAll("\r", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + ArrayList diffLines = new ArrayList(Arrays + .asList(s.split("\n", -1))); //$NON-NLS-1$ + return diffLines; + } catch (BadLocationException e) { + CompareUIPlugin.log(e); + } + return null; + } + + private String extract(int nr, char side) throws BadLocationException { + Diff diff = ((Diff) merger.getAllDiffs().get(nr)); + if (side == LEFT_CONTRIBUTOR && !rightToLeft + || side == RIGHT_CONTRIBUTOR && rightToLeft) { + return leftDoc.get(diff.getPosition(LEFT_CONTRIBUTOR).offset, diff + .getPosition(LEFT_CONTRIBUTOR).length); + } + return rightDoc.get(diff.getPosition(RIGHT_CONTRIBUTOR).offset, diff + .getPosition(RIGHT_CONTRIBUTOR).length); + } + + public boolean isPartDifferent(ArrayList allDiffs, int nr) { + Diff diff = ((Diff) allDiffs.get(nr)); + if (diff.getKind() == RangeDifference.CHANGE) { + return true; + } + return false; + } + + private class Hunk { + private int oldStart; + private int oldLength; + private int newStart; + private int newLength; + private boolean strictUnixFormat; + private boolean printNoNewlineMarker; + ArrayList lines; + + public Hunk(int oldStart, int newStart, boolean strictUnixFormat) { + if (oldStart < 0) + oldStart = 0; + if (newStart < 0) + newStart = 0; + this.oldStart = oldStart; + this.newStart = newStart; + this.oldLength = 0; + this.newLength = 0; + this.strictUnixFormat = strictUnixFormat; + printNoNewlineMarker = false; + lines = new ArrayList(); + } + + public void addPartRangeToOld(ArrayList part, int start, int end, + boolean lastPart) { + if (start < 0) + start = 0; + if (strictUnixFormat) { + if ((lastPart) && part.size() != 1) + end = Math.min(end, part.size()); + else + end = Math.min(end, part.size() - 1); + for (int lineNr = start; lineNr < end; lineNr++) { + lines.add(OLD_LINE_PREFIX + part.get(lineNr)); + oldLength++; + } + } + else { + end = Math.min(end, part.size() - 1); + for (int lineNr = start; lineNr < end; lineNr++) { + lines.add(OLD_LINE_PREFIX + part.get(lineNr)); + oldLength++; + } + if (!part.get(part.size() - 1).toString().equals("")) //$NON-NLS-1$ + { + //part doesn't end with newline character + //don't cut the last line, because it isn't empty + lines.add(OLD_LINE_PREFIX + part.get(part.size() - 1)); + oldLength++; + printNoNewlineMarker = true; + } + } + } + + public void addPartRangeToNew(ArrayList part, int start, int end, + boolean lastPart) { + if (start < 0) + start = 0; + if (strictUnixFormat) { + if ((lastPart) && part.size() != 1) + end = Math.min(end, part.size()); + else + end = Math.min(end, part.size() - 1); + for (int lineNr = start; lineNr < end; lineNr++) { + lines.add(NEW_LINE_PREFIX + part.get(lineNr)); + newLength++; + } + } + else { + end = Math.min(end, part.size() - 1); + for (int lineNr = start; lineNr < end; lineNr++) { + lines.add(NEW_LINE_PREFIX + part.get(lineNr)); + newLength++; + } + if (!part.get(part.size() - 1).toString().equals("")) { //$NON-NLS-1$ + //part doesn't end with newline character + //don't cut the last line, because it isn't empty + lines.add(NEW_LINE_PREFIX + part.get(part.size() - 1)); + newLength++; + printNoNewlineMarker = true; + } + } + } + + public void addPartRangeToBoth(ArrayList part, int start, int end, + boolean lastPart) { + if (start < 0) + start = 0; + if (strictUnixFormat) { + if (lastPart) + end = Math.min(end, part.size()); + else + end = Math.min(end, part.size() - 1); + for (int lineNr = start; lineNr < end; lineNr++) { + lines.add(CONTEXT_LINE_PREFIX + part.get(lineNr)); + oldLength++; + newLength++; + } + } + else { + end = Math.min(end, part.size() - 1); + for (int lineNr = start; lineNr < end; lineNr++) { + lines.add(CONTEXT_LINE_PREFIX + part.get(lineNr)); + oldLength++; + newLength++; + } + if (!part.get(part.size() - 1).toString().equals("")) { //$NON-NLS-1$ + //part doesn't end with newline character + //don't cut the last line, because it isn't empty + lines.add(CONTEXT_LINE_PREFIX + part.get(part.size() - 1)); + oldLength++; + newLength++; + printNoNewlineMarker = true; + } + } + } + + private void printMarkerTo(PrintStream output) { + if (oldLength == 0) + { + //all lines are new lines + oldStart = -1; + } + if (newLength == 0) { + //all lines are old lines + newStart = -1; + } + output.println(RANGE_INFORMATION_PREFIX + (oldStart+1) + "," + oldLength + " +" + (newStart+1) //$NON-NLS-1$ //$NON-NLS-2$ + + "," + newLength + RANGE_INFORMATION_AFFIX); //$NON-NLS-1$ + } + + public void printTo(PrintStream output) { + printMarkerTo(output); + for (int i = 0; i < lines.size(); i++) { + output.println(lines.get(i)); + } + if (printNoNewlineMarker) { + output.println("\\ No newline at end of file"); //$NON-NLS-1$ + } + } + } + +}