### 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 2 Dec 2008 22:32:47 -0000 @@ -22,6 +22,8 @@ // Do not instantiate } + public static String CreatePatchAction_pageDescription; + public static String CreatePatchActionTitle; public static String CompareContainer_0; public static String CompareDialog_commit_button; public static String CompareDialog_error_message; @@ -130,6 +132,58 @@ public static String CompareWithOtherResourceDialog_workspaceMainButton; public static String CompareWithOtherResourceDialog_workspaceRadioButton; + public static String GenerateLocalDiff_title; + public static String WorkspacePatchDialogTitle; + public static String WorkspacePatchDialogDescription; + public static String GenerateDiffFileWizard_9; + public static String Fi_le_name__9; + public static String GenerateDiffFileWizard_5; + public static String GenerateDiffFileWizard_FolderExists; + public static String GenerateDiffFileWizard_noChangesSelected; + public static String GenerateDiffFileWizard_0; + public static String GenerateDiffFileWizard_browseFilesystem; + public static String GenerateDiffFileWizard_2; + public static String GenerateDiffFileWizard_3; + public static String GenerateDiffFileWizard_4; + public static String GenerateDiffFileWizard_ProjectClosed; + public static String GenerateDiffFileWizard_SelectAll; + public static String GenerateDiffFileWizard_DeselectAll; + public static String Save_To_Clipboard_2; + public static String Save_In_File_System_3; + public static String Browse____4; + public static String Save_In_Workspace_7; + public static String CommitWizardCommitPage_1; + public static String CommitWizardCommitPage_5; + public static String Save_Patch_As_5; + public static String patch_txt_6; + public static String CommitWizard_4; + public static String Diff_output_format_12; + public static String Unified__format_required_by_Compare_With_Patch_feature__13; + public static String Context_14; + public static String Standard_15; + public static String GenerateDiffFileWizard_10; + public static String GenerateDiffFileWizard_6; + public static String GenerateDiffFileWizard_7; + public static String GenerateDiffFileWizard_8; + public static String CreatePatchAction_pageTitle; + public static String GenerateLocalDiff_pageTitle; + public static String GenerateLocalDiff_pageDescription; + public static String Advanced_options_19; + public static String Configure_the_options_used_for_the_local_diff_command_20; + public static String GenerateCVSDiff_error; + public static String GenerateDiffFileWizard_11; + public static String GenerateDiffFileWizard_12; + public static String GenerateLocalDiff_1; + public static String GenerateLocalDiff_2; + public static String GenerateLocalDiff_sourceChoose_left; + public static String GenerateLocalDiff_sourceChoose_right; + public static String GenerateLocalDiff_overwriteTitle; + public static String GenerateLocalDiff_overwriteMsg; + + public static String GenerateLocalDiffFile_CheckSourceFile_pageTitle; + public static String GenerateLocalDiffFile_CheckSourceFile_pageDescription; + + 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 2 Dec 2008 22:32:47 -0000 @@ -141,3 +141,57 @@ CompareWithOtherResourceDialog_externalFolderRadioButton=External folder CompareWithOtherResourceDialog_workspaceMainButton=Browse... CompareWithOtherResourceDialog_workspaceRadioButton=Workspace + +CreatePatchAction_pageTitle=Create Patch from local file +CommitWizardCommitPage_1=Hiding changes. The {0} changes exceeds the display threshold of {1}. It may take a long time to show this number of changes. +CommitWizardCommitPage_5=Show &Changes +GenerateLocalDiff_title=Create Patch +GenerateLocalDiff_pageTitle=Create a Patch Using the local Diff Command +GenerateLocalDiff_pageDescription=Specify the input and the location for the patch. +GenerateLocalDiff_overwriteTitle=Confirm Overwrite +GenerateLocalDiff_overwriteMsg=A file with that name already exists. Overwrite? +GenerateCVSDiff_error=Error running the CVS diff command +GenerateLocalDiff_1=Read-only file +GenerateLocalDiff_2=The specified file is read-only and cannot be overwritten. +GenerateLocalDiff_sourceChoose_left=Left: +GenerateLocalDiff_sourceChoose_right=Right: +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 +WorkspacePatchDialogTitle=Set a Patch Location +WorkspacePatchDialogDescription=Select a folder in the workspace and enter a name for the patch. +Fi_le_name__9=Fi&le name: +Diff_output_format_12=Diff Output Format +Unified__format_required_by_Compare_With_Patch_feature__13=&Unified (format required by the Apply Patch wizard) +Context_14=&Context +Standard_15=&Standard +Advanced_options_19=Advanced Options +Configure_the_options_used_for_the_local_diff_command_20=Configure the options used for the local diff command. +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_6=&Workspace (Multi-project Apply Patch wizard specific) +GenerateDiffFileWizard_7=&Project +GenerateDiffFileWizard_8=S&election +GenerateDiffFileWizard_9=Save Patch +GenerateDiffFileWizard_10=Patch Root +GenerateDiffFileWizard_11=Include Binary Files? +GenerateDiffFileWizard_12=The selected resources include files marked as binary. CVS does not handle the creation of patches for binary files. Should the files marked as binary be included? +GenerateDiffFileWizard_SelectAll=Select &All +GenerateDiffFileWizard_DeselectAll=&Deselect All +GenerateDiffFileWizard_browseFilesystem=Please select a location in the filesystem by browsing. +GenerateDiffFileWizard_noChangesSelected=No changes selected. +GenerateDiffFileWizard_FolderExists=The specified path points to an existing folder. +GenerateDiffFileWizard_ProjectClosed=The specified path points to a closed project. +CommitWizard_4=Collecting outgoing changes + +CreatePatchAction_pageDescription=Description +GenerateLocalDiffFile_CheckSourceFile_pageTitle=Choose source/goal file +GenerateLocalDiffFile_CheckSourceFile_pageDescription=Choose source file = left or goal = right + +CreatePatchActionTitle=Create Patch... 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 2 Dec 2008 22:32:47 -0000 @@ -49,4 +49,7 @@ public static final String PREF_VALUE_NEXT = "next"; //$NON-NLS-1$ public static final String COMMAND_IGNORE_WHITESPACE = PREFIX + "ignoreWhiteSpace"; //$NON-NLS-1$ + + public static 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.46 diff -u -r1.46 MergeSourceViewer.java --- compare/org/eclipse/compare/internal/MergeSourceViewer.java 14 Oct 2008 08:29:40 -0000 1.46 +++ compare/org/eclipse/compare/internal/MergeSourceViewer.java 2 Dec 2008 22:32:48 -0000 @@ -99,6 +99,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 { @@ -789,6 +790,7 @@ 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 2 Dec 2008 22:32:49 -0000 @@ -33,6 +33,10 @@ * This class should not have any UI dependencies. */ public class DocumentMerger { + + public static final char ANCESTOR_CONTRIBUTOR = 'A'; + public static final char RIGHT_CONTRIBUTOR = 'R'; + public static final char LEFT_CONTRIBUTOR = 'L'; private static final String DIFF_RANGE_CATEGORY = CompareUIPlugin.PLUGIN_ID + ".DIFF_RANGE_CATEGORY"; //$NON-NLS-1$ @@ -1344,4 +1348,8 @@ return null; } + public ArrayList getAllDiffs(){ + return this.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.237 diff -u -r1.237 TextMergeViewer.java --- compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java 27 Nov 2008 16:44:59 -0000 1.237 +++ compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java 2 Dec 2008 22:32:46 -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; @@ -2301,6 +2302,8 @@ contributeFindAction(part); contributeGotoLineAction(part); + + contributeCreatePatchAction(part); configureTextViewer(part); @@ -2332,6 +2335,11 @@ action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_GOTO); viewer.addAction(MergeSourceViewer.GOTO_LINE_ID, action); } + private void contributeCreatePatchAction(MergeSourceViewer viewer) { + IAction action = new CreatePatchAction(viewer, getCompareConfiguration().getContainer().getWorkbenchPart()); + action.setActionDefinitionId(MergeSourceViewer.CREATE_PATCH_ID); + viewer.addAction(MergeSourceViewer.CREATE_PATCH_ID, action); + } private void connectGlobalActions(final MergeSourceViewer part) { if (fHandlerService != null) { Index: compare/org/eclipse/compare/internal/GenerateLocalDiffFileWizard.java =================================================================== RCS file: compare/org/eclipse/compare/internal/GenerateLocalDiffFileWizard.java diff -N compare/org/eclipse/compare/internal/GenerateLocalDiffFileWizard.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compare/org/eclipse/compare/internal/GenerateLocalDiffFileWizard.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,1444 @@ +package org.eclipse.compare.internal; + +/******************************************************************************* + * Copyright (c) 2000, 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 + * Benjamin Muskalla (b.muskalla@gmx.net) - Bug 149672 [Patch] Create Patch wizard should remember previous settings + *******************************************************************************/ + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +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.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.Combo; +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.IWorkbenchPart; +import org.eclipse.ui.model.BaseWorkbenchContentProvider; +import org.eclipse.ui.model.WorkbenchLabelProvider; +import org.eclipse.ui.views.navigator.ResourceComparator; + +/** + * A wizard for creating a patch file for local files. + */ +public class GenerateLocalDiffFileWizard extends Wizard { + + //The initial size of this wizard. + private final static int INITIAL_WIDTH = 300; + private final static int INITIAL_HEIGHT = 350; + private IResource fLeft; + private IResource fRight; + + public static void run(IWorkbenchPart part, final IResource leftResource, final IResource rightResource, boolean unifiedSelectionEnabled) { + final String title = CompareMessages.GenerateLocalDiff_title; + final GenerateLocalDiffFileWizard wizard = new GenerateLocalDiffFileWizard(part,leftResource, rightResource, unifiedSelectionEnabled); + wizard.setWindowTitle(title); + WizardDialog dialog = new WizardDialog(part.getSite().getShell(), wizard); + dialog.setMinimumPageSize(INITIAL_WIDTH, INITIAL_HEIGHT); + dialog.open(); + } + + public static void run(IWorkbenchPart part, final IResource leftResource, final IResource rightResource) { + GenerateLocalDiffFileWizard.run(part,leftResource, rightResource, true); + } + + + /** + * Page to select a source and goal file. + */ + public class SourceChoosePage extends WizardPage { + public final static int FIRST_FILE= 0; + public final static int SECOND_FILE= 1; + private Combo lCombo; + private Combo rCombo; + private IResource firstFile; + private IResource secondFile; + + + protected SourceChoosePage(String pageName) { + super(pageName); + firstFile = fLeft; + secondFile = fRight; + } + + protected SourceChoosePage(String pageName, IResource defaultLeftResource) { + super(pageName); + firstFile = defaultLeftResource; + secondFile = (fRight!=firstFile?fRight:fLeft); + } + + private void addComboBox(Combo box){ + box.add(firstFile.getFullPath().toString(), FIRST_FILE); + box.add(secondFile.getFullPath().toString(), SECOND_FILE); + } + + public void createControl(Composite parent) { + // create the composite to hold the widgets + + Composite composite = new Composite(parent, SWT.NULL); + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns= 2; + gridLayout.marginHeight= 10; + gridLayout.marginWidth= 10; + composite.setLayout(gridLayout); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + + new Label(composite, SWT.CENTER).setText(CompareMessages.GenerateLocalDiff_sourceChoose_left); + lCombo = new Combo(composite,SWT.BORDER|SWT.READ_ONLY); + addComboBox(lCombo); + lCombo.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL|GridData.GRAB_HORIZONTAL|GridData.BEGINNING)); + + GridData rgData = new GridData(); + rgData.horizontalAlignment=GridData.BEGINNING; + + Label l = new Label(composite,SWT.NONE); + l.setText(CompareMessages.GenerateLocalDiff_sourceChoose_right); + l.setLayoutData(rgData); + rCombo = new Combo(composite, SWT.BORDER|SWT.READ_ONLY); + addComboBox(rCombo); + rCombo.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL|GridData.HORIZONTAL_ALIGN_FILL|GridData.BEGINNING)); + lCombo.addSelectionListener(new SelectionAdapter(){ + public void widgetSelected(SelectionEvent e){ + rCombo.select((lCombo.getSelectionIndex()+1)%2); + } + }); + rCombo.addSelectionListener(new SelectionAdapter(){ + public void widgetSelected(SelectionEvent e){ + lCombo.select((rCombo.getSelectionIndex()+1)%2); + } + }); + lCombo.select(FIRST_FILE);//default option + rCombo.select(SECOND_FILE); + + setControl(composite); + + } + public boolean canFlipToNextPage() { + if(firstFile==null || secondFile==null) + return false; + return true; + + } + + public IResource getLeftResource(){ + return lCombo.getSelectionIndex()==FIRST_FILE?firstFile:secondFile; + } + + public IResource getRightResource(){ + return rCombo.getSelectionIndex()==FIRST_FILE?firstFile:secondFile; + } + + } + + + + /** + * Page to select a patch file. Overriding validatePage was necessary to allow + * entering a file name that already exists. + */ + public class LocationPage extends WizardPage { + + /** + * The possible locations to save a patch. + */ + public final static int CLIPBOARD = 1; + public final static int FILESYSTEM = 2; + public final static int WORKSPACE = 3; + + /** + * GUI controls for clipboard (cp), filesystem (fs) and workspace (ws). + */ + 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; + + /** + * State information of this page, updated by the listeners. + */ + protected boolean canValidate; + 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 { + //Never show closed projects + 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); + //create title image + + 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; + } else { + 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; + } else { + 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; + this.canValidate=false; + } + + /** + * Allow the user to finish if a valid file has been entered. + */ + protected boolean validatePage() { + + if (!canValidate) + return false; + + 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; + } + + /** + * The following conditions must hold 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 the browse button) + */ + private boolean validateFilesystemLocation() { + 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; + } + + /** + * The following conditions must hold for the file system location to be valid: + * - a parent must be selected in the workspace tree view + * - the resource name must be valid + */ + private boolean validateWorkspaceLocation() { + //make sure that the field actually has a filename in it - making + //sure that the user has had a chance to browse the workspace first + if (wsPathText.getText().equals("")){ //$NON-NLS-1$ + 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. + */ + public String getWorkspaceLocation() { + + if (pageValid && selectedLocation == WORKSPACE) { + final String filename= wsPathText.getText().trim(); + return filename; + } + return null; + } + + /** + * Allow the user to chose to save the patch to the workspace or outside + * of the workspace. + */ + public void createControl(Composite parent) { + + final Composite composite= new Composite(parent, SWT.NULL); + composite.setLayout(new GridLayout()); + setControl(composite); + initializeDialogUnits(composite); + + //Create a location group + setupLocationControls(composite); + + initializeDefaultValues(); + + canValidate=true; + validatePage(); + + createSelectionButtons(composite); + + Dialog.applyDialogFont(parent); + + /** + * Ensure the page is in a valid state. + */ + /*if (!validatePage()) { + store.storeRadioSelection(CLIPBOARD); + initializeDefaultValues(); + validatePage(); + } + pageValid= true;*/ + validatePage(); + + updateEnablements(); + setupListeners(); + } + + + private void createSelectionButtons(Composite composite) { + final Composite buttonGroup = new Composite(composite,SWT.NONE); + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + layout.marginWidth = 0; + layout.marginHeight = 0; + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + buttonGroup.setLayout(layout); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END + | GridData.VERTICAL_ALIGN_CENTER); + buttonGroup.setLayoutData(data); + + } + + + /** + * Setup the controls for the location. + */ + 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); + + // change the cpRadio layout to be of the same height as other rows' layout + ((GridData)cpRadio.getLayoutData()).heightHint = minSize.y; + } + + /** + * Initialize the controls with the saved default values which are + * obtained from the DefaultValuesStore. + */ + private void initializeDefaultValues() { + + selectedLocation= store.getLocationSelection(); + + updateRadioButtons(); + + /** + * Text fields. + */ + // 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 - instead change selection + //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() { + /** + * Radio buttons + */ + cpRadio.setSelection(selectedLocation == CLIPBOARD); + fsRadio.setSelection(selectedLocation == FILESYSTEM); + wsRadio.setSelection(selectedLocation == WORKSPACE); + } + + /** + * Setup all the listeners for the controls. + */ + 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(); + } + }); + + } + + /** + * Enable and disable controls based on the selected radio button. + */ + public void updateEnablements() { + 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; + } + + } + + /** + * Page to select the options for creating the patch. + */ + private class OptionsPage extends WizardPage { + + /** + * The possible file format to save a patch. + */ + public final static int FORMAT_UNIFIED = 1; + public final static int FORMAT_CONTEXT = 2; + public final static int FORMAT_STANDARD = 3; + + /** + The possible root of the patch + */ + public final static int ROOT_WORKSPACE = 1; + public final static int ROOT_PROJECT = 2; + public final static int ROOT_SELECTION = 3; + + private Button unifiedDiffOption; + private Button unified_workspaceRelativeOption; //multi-patch format + private Button unified_projectRelativeOption; //full project path + private Button unified_selectionRelativeOption; //use path of whatever is selected + private Button contextDiffOption; + private Button regularDiffOption; + private final RadioButtonGroup diffTypeRadioGroup = new RadioButtonGroup(); + private final RadioButtonGroup unifiedRadioGroup = new RadioButtonGroup(); + + private boolean onlyUnifiedAllowed = true; + protected IPath patchRoot=ResourcesPlugin.getWorkspace().getRoot().getFullPath(); + + private final DefaultValuesStore store; + + /** + * Constructor for PatchFileCreationOptionsPage. + */ + protected OptionsPage(String pageName, String title, ImageDescriptor titleImage, DefaultValuesStore store) { + super(pageName, title, titleImage); + this.store = store; + } + + /* + * @see IDialogPage#createControl(Composite) + */ + 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); + + // set F1 help + + Group 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 + Group unifiedGroup = new Group(composite, SWT.None); + layout = new GridLayout(); + 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.setSelection(true); + + unified_projectRelativeOption = new Button(unifiedGroup, SWT.RADIO); + unified_projectRelativeOption.setText(CompareMessages.GenerateDiffFileWizard_7); + + unified_selectionRelativeOption = new Button(unifiedGroup, SWT.RADIO); + unified_selectionRelativeOption.setText(CompareMessages.GenerateDiffFileWizard_8); + + 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); + } + }); + +// calculatePatchRoot(); + updateEnablements(); + + // update selection + diffTypeRadioGroup.selectEnabledOnly(); + unifiedRadioGroup.selectEnabledOnly(); + } + + public int getFormatSelection() { + return diffTypeRadioGroup.getSelected(); + } + + public int getRootSelection() { + return unifiedRadioGroup.getSelected(); + } + + private void initializeDefaultValues() { + // Radio buttons for format + diffTypeRadioGroup.setSelection(store.getFormatSelection(), true); + + if (store.getFormatSelection() != FORMAT_UNIFIED) { + setEnableUnifiedGroup(false); + } + } + + + protected void updateEnablements() { + if (onlyUnifiedAllowed){ + diffTypeRadioGroup.setEnablement(false, new int[] { + FORMAT_CONTEXT, FORMAT_STANDARD }, FORMAT_UNIFIED); + unifiedRadioGroup.setEnablement(false, new int[] { + ROOT_PROJECT, ROOT_SELECTION }, ROOT_WORKSPACE); + } + + // temporary until we figure out best way to fix synchronize view + // selection + if (!unifiedSelectionEnabled) + unifiedRadioGroup.setEnablement(false, new int[] {ROOT_SELECTION}); + } + + 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. + */ + private final class DefaultValuesStore { + + private static final String PREF_LAST_SELECTION= "org.eclipse.team.internal.ccvs.ui.wizards.GenerateLocalDiffFileWizard.PatchFileSelectionPage.lastselection"; //$NON-NLS-1$ + private static final String PREF_LAST_FS_PATH= "org.eclipse.team.internal.ccvs.ui.wizards.GenerateLocalDiffFileWizard.PatchFileSelectionPage.filesystem.path"; //$NON-NLS-1$ + private static final String PREF_LAST_WS_PATH= "org.eclipse.team.internal.ccvs.ui.wizards.GenerateLocalDiffFileWizard.PatchFileSelectionPage.workspace.path"; //$NON-NLS-1$ + private static final String PREF_LAST_AO_FORMAT = "org.eclipse.team.internal.ccvs.ui.wizards.GenerateLocalDiffFileWizard.OptionsPage.diff.format"; //$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) { +// e.printStackTrace(); + } + + 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) { + } + + switch (value) { + case OptionsPage.FORMAT_UNIFIED: + case OptionsPage.FORMAT_CONTEXT: + case OptionsPage.FORMAT_STANDARD: + return value; + default: + return OptionsPage.FORMAT_UNIFIED; + } + } + + 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); + } + + } + + private SourceChoosePage myFirstPage; + private LocationPage locationPage; + private OptionsPage optionsPage; + + protected IResource[] resources; + private final DefaultValuesStore defaultValuesStore; + + //temporary until we figure out best way to fix synchronize view selection + protected boolean unifiedSelectionEnabled; + + public GenerateLocalDiffFileWizard(IWorkbenchPart part, final IResource leftResource, final IResource rightResource, boolean unifiedSelectionEnabled) { + super(); + this.fLeft = leftResource; + this.fRight = rightResource; + setWindowTitle(CompareMessages.GenerateLocalDiff_title); + defaultValuesStore= new DefaultValuesStore(); + this.unifiedSelectionEnabled=unifiedSelectionEnabled; + } + + public void addPages() { + String pageTitle = CompareMessages.CreatePatchAction_pageTitle; + myFirstPage = new SourceChoosePage(pageTitle); + myFirstPage.setTitle(CompareMessages.GenerateLocalDiffFile_CheckSourceFile_pageTitle); + myFirstPage.setDescription(CompareMessages.GenerateLocalDiffFile_CheckSourceFile_pageDescription); + addPage(myFirstPage); + + pageTitle = CompareMessages.GenerateLocalDiff_pageTitle; + String 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_local_diff_command_20; + optionsPage = new OptionsPage(pageTitle, pageTitle, CompareUIPlugin.getImageDescriptor(ICompareUIConstants.IMG_WIZBAN_DIFF), defaultValuesStore); + optionsPage.setDescription(pageDescription); + addPage(optionsPage); + } + + + /** + * Completes processing of the wizard. If this method returns + * true, the wizard will close; otherwise, it will stay active. + */ + public boolean performFinish() { + + final int location= locationPage.getSelectedLocation(); + + final File file= location != LocationPage.CLIPBOARD? locationPage.getFile() : null; + + if (!(file == null || validateFile(file))) { + return false; + } + + + /** + * Perform diff operation. + */ + IResource left = myFirstPage.getLeftResource(); + IResource right = myFirstPage.getRightResource(); + + try { + if (file != null) { + DiffGroup diffGroup = new DiffGroup(left, right); + diffGroup.generateDiff(file.getAbsolutePath(), optionsPage.getFormatSelection()); + } else { + DiffGroup diffGroup = new DiffGroup(left, right); + diffGroup.generateDiffToClipboard(getShell().getDisplay(), optionsPage.getFormatSelection()); + } + } catch (Exception e) { + e.printStackTrace(); + } + + /** + * 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, open with 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; + } + + + /** + * Save default selections of Options Page + */ + + defaultValuesStore.storeOutputFormat(optionsPage.getFormatSelection()); + + return true; + } + + 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; + } + + /** + * The class maintain proper selection of radio button within the group: + * + */ + /*private*/ class RadioButtonGroup { + + /** + * List of buttons in the group. Both radio groups contain 3 elements. + */ + private List buttons = new ArrayList(3); + + /** + * Index of the selected button. + */ + private int selected = 0; + + /** + * Add a button to the group. While adding a new button the method + * checks if there is only one button selected in the group. + * + * @param buttonCode + * A button's code (eg. ROOT_WORKSPACE). To get + * an index we need to subtract 1 from it. + * @param button + * A button to add. + */ + 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); + } + } + + /** + * Returns selected button's code. + * + * @return Selected button's code. + */ + public int getSelected() { + return selected + 1; + } + + /** + * Set selection to the given button. When + * selectEnabledOnly flag is true the returned value can + * differ from the parameter when a button we want to set selection to + * is disabled and there are other buttons which are enabled. + * + * @param buttonCode + * A button's code (eg. ROOT_WORKSPACE). To get + * an index we need to subtract 1 from it. + * @return Code of the button to which selection was finally set. + */ + 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(); + } + + /** + * Make sure that only an enabled radio button is selected. + * + * @return A code of the selected button. + */ + 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(); + } + + /** + * Enable or disable given buttons. + * + * @param enabled + * Indicates whether to enable or disable the buttons. + * @param buttonsToChange + * Buttons to enable/disable. + * @param defaultSelection + * The button to select if the currently selected button + * becomes disabled. + */ + 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(); + } + } + + /** + * Enable or disable given buttons with no default selection. The selection + * will be set to an enabled button using the selectEnabledOnly method. + * + * @param enabled Indicates whether to enable or disable the buttons. + * @param buttonsToChange Buttons to enable/disable. + */ + public void setEnablement(boolean enabled, int[] buttonsToChange) { + // -1 means that no default selection is given + setEnablement(enabled, buttonsToChange, -1); + } + + /** + * Deselect all buttons in the group. + */ + private void deselectAll() { + // clear all selections + for (Iterator iterator = buttons.iterator(); iterator.hasNext();) + ((Button) iterator.next()).setSelection(false); + } + + + } + +} Index: compare/org/eclipse/compare/internal/DiffGroup.java =================================================================== RCS file: compare/org/eclipse/compare/internal/DiffGroup.java diff -N compare/org/eclipse/compare/internal/DiffGroup.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compare/org/eclipse/compare/internal/DiffGroup.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; + + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Display; + +public class DiffGroup { + + 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; + + private ArrayList diffFiles; + private UnifiedDiffFormatter diffFormatter; + + + public DiffGroup(IResource leftRes, IResource rightRes) { + + + diffFiles = new ArrayList(); + + if (leftRes.getType()==IResource.FILE && rightRes.getType()==IResource.FILE){ + IFile file1 = (IFile)leftRes; + IFile file2 = (IFile)rightRes; + + DiffFile diffFile = new DiffFile(file1, file2); + diffFiles.add(diffFile); + } + + } + + public void generateDiffToClipboard(Display dis, int format) { + if (format == FORMAT_UNIFIED){ + + diffFormatter = new UnifiedDiffFormatter(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bos); + + diffFormatter.createDiff(diffFiles, ps); + ps.close(); + + TextTransfer plainTextTransfer = TextTransfer.getInstance(); + Clipboard clipboard = new Clipboard(dis); + clipboard.setContents( + new String[]{bos.toString()}, + new Transfer[]{plainTextTransfer}); + clipboard.dispose(); + try { + bos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + } + + + + public void generateDiff(String path, int format) { + if (format == FORMAT_UNIFIED){ + + diffFormatter = new UnifiedDiffFormatter(); + FileOutputStream fos; + + try { + fos = new FileOutputStream(path); + PrintStream ps = new PrintStream(fos); + diffFormatter.createDiff(diffFiles, ps); + ps.close(); + fos.close(); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + throw new UnsupportedOperationException(); + } + + } + + + + +} Index: compare/org/eclipse/compare/internal/DiffFile.java =================================================================== RCS file: compare/org/eclipse/compare/internal/DiffFile.java diff -N compare/org/eclipse/compare/internal/DiffFile.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compare/org/eclipse/compare/internal/DiffFile.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,233 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.contentmergeviewer.ITokenComparator; +import org.eclipse.compare.contentmergeviewer.TokenComparator; +import org.eclipse.compare.internal.merge.DocumentMerger; +import org.eclipse.compare.internal.merge.DocumentMerger.Diff; +import org.eclipse.compare.internal.merge.DocumentMerger.IDocumentMergerInput; +import org.eclipse.compare.rangedifferencer.RangeDifference; +import org.eclipse.core.resources.IFile; +//import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; + +public class DiffFile { + + private IFile leftFile; + private IFile rightFile; + private DocumentMerger docMerger; + private String leftName; + private String rightName; + + private ArrayList allDiffs; + + + public DiffFile(IFile leftFile, IFile rightFile) { + this.leftFile = leftFile ; + this.rightFile = rightFile; + this.leftName= this.leftFile.getFullPath().toString(); + this.rightName= this.rightFile.getFullPath().toString(); + this.allDiffs = new ArrayList(); + IDocument leftDoc = convertFileToDocument(leftFile); + IDocument rightDoc = convertFileToDocument(rightFile); + docMerger = new DocumentMerger(new LocalDiffMergerInput(leftDoc, rightDoc)); + this.doDiff(); + } + + public DiffFile(String leftName, String rightName, DocumentMerger docMerger){ + this.leftName= leftName; + this.rightName= rightName; + this.docMerger= docMerger; + this.doDiff(); + } + + public int getNumberOfParts() + { + return allDiffs.size(); + } + + public String getFilePathString(char side) { + if (side == 'L') + return leftName; + else + return rightName; + } + + //ArrayList of Strings containing lines of part + public ArrayList getPart(int nr, char side) //side = 'L' or 'R' + { + try{ + Diff diff = ((Diff)allDiffs.get(nr)); + + String s = this.extract(nr, side).replaceAll("\r", ""); //extract and convert "\r\n" to "\n" + ArrayList diffLines = new ArrayList(Arrays.asList(s.split("\n", -1))); + return diffLines; + } catch (IndexOutOfBoundsException e) { + CompareUIPlugin.log(e); + }catch (Exception e){ + CompareUIPlugin.log(e); + } + return null; + } + + public Date getDate(char side) { + Date date; + if (side == DiffGroup.LEFT_CONTRIBUTOR) + date = new Date(leftFile.getLocalTimeStamp()); + else + date = new Date(rightFile.getLocalTimeStamp()); + return date; + } + + public boolean isPartDifferent(int nr) { + Diff diff = ((Diff)allDiffs.get(nr)); + + if (diff.getKind() == RangeDifference.CHANGE) + return true; + else + return false; + } + + private String extract(int nr, char side) { + IDocument leftDoc = convertFileToDocument(leftFile); + IDocument rightDoc = convertFileToDocument(rightFile); + try { + Diff diff = ((Diff)allDiffs.get(nr)); + switch (side) { + case DiffGroup.LEFT_CONTRIBUTOR: + return leftDoc.get(diff.getPosition(DiffGroup.LEFT_CONTRIBUTOR).offset, + diff.getPosition(DiffGroup.LEFT_CONTRIBUTOR).length); + case DiffGroup.RIGHT_CONTRIBUTOR: + return rightDoc.get(diff.getPosition(DiffGroup.RIGHT_CONTRIBUTOR).offset, + diff.getPosition(DiffGroup.RIGHT_CONTRIBUTOR).length); + + } + } + catch (IndexOutOfBoundsException e){ + CompareUIPlugin.log(e); + } + catch (BadLocationException e) { + CompareUIPlugin.log(e); + } + return null; + } + + private void doDiff() { + try { + docMerger.doDiff(); + allDiffs = docMerger.getAllDiffs(); + + } catch (CoreException e) { + e.printStackTrace(); + } + } + + private IDocument convertFileToDocument(IFile file) { + StringBuffer str = new StringBuffer(); + int c; + try{ + InputStream inputStream = file.getContents(); + while ((c = inputStream.read())!=-1){ + str.append((char)c); + } + inputStream.close(); + return new Document(str.toString()); + }catch(Exception ex){ + CompareUIPlugin.log(ex); + } + + return null; + } + + + + class LocalDiffMergerInput implements IDocumentMergerInput { + + private IDocument leftDoc; + private IDocument rightDoc; + + public LocalDiffMergerInput(IDocument leftDoc, IDocument rightDoc) { + this.leftDoc = leftDoc ; + this.rightDoc= rightDoc; + } + + public ITokenComparator createTokenComparator(String line) { + return new TokenComparator(line); + } + public CompareConfiguration getCompareConfiguration() { + return new CompareConfiguration(); + } + public IDocument getDocument(char contributor) { + switch (contributor) { + case DocumentMerger.LEFT_CONTRIBUTOR: + return leftDoc; + case DocumentMerger.RIGHT_CONTRIBUTOR: + return rightDoc; + case DocumentMerger.ANCESTOR_CONTRIBUTOR: + return null; + } + return null; + } + public int getHunkStart() { + return 0; + } + public Position getRegion(char contributor) { + switch (contributor) { + case DocumentMerger.LEFT_CONTRIBUTOR: + return new Position(0, leftDoc.getLength()); + case DocumentMerger.RIGHT_CONTRIBUTOR: + return new Position(0, rightDoc.getLength()); + case DocumentMerger.ANCESTOR_CONTRIBUTOR: + return new Position(0, 0); + } + return null; + } + public boolean isHunkOnLeft() { +// ITypedElement left = ((ICompareInput)getInput()).getRight(); +// return left != null && Utilities.getAdapter(left, IHunk.class) != null; + return false; + } + public boolean isIgnoreAncestor() { +// return TextMergeViewer.this.isIgnoreAncestor(); + return true; + } + public boolean isPatchHunk() { +// return TextMergeViewer.this.isPatchHunk(); + return false; + } + + public boolean isShowPseudoConflicts() { +// return fShowPseudoConflicts; + return false; + } + public boolean isThreeWay() { +// return TextMergeViewer.this.isThreeWay(); + return false; + } + public boolean isPatchHunkOk() { +// return TextMergeViewer.this.isPatchHunkOk(); + return 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,54 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +package org.eclipse.compare.internal; + +import org.eclipse.compare.ResourceNode; +import org.eclipse.core.resources.IResource; +import org.eclipse.jface.action.Action; +import org.eclipse.ui.IWorkbenchPart; + + +public class CreatePatchAction extends Action { + + private IResource left; + private IResource right; + private IWorkbenchPart workbenchPart; + + public CreatePatchAction(MergeSourceViewer viewer, IWorkbenchPart workbenchPart) { + super(CompareMessages.CreatePatchActionTitle); + if(workbenchPart instanceof CompareEditor) { + + CompareEditor editor = (CompareEditor)workbenchPart; + + ResourceCompareInput input = (ResourceCompareInput)editor.getEditorInput(); + if(input.getCompareResult() instanceof ResourceCompareInput.MyDiffNode) { + ResourceCompareInput.MyDiffNode node = (ResourceCompareInput.MyDiffNode)input.getCompareResult(); + ResourceNode leftResNode = (ResourceNode)node.getLeft(); + ResourceNode rightResNode = (ResourceNode)node.getRight(); + + left = leftResNode.getResource(); + right = rightResNode.getResource(); + } + + } + + this.workbenchPart = workbenchPart; + } + + public void run() { + + GenerateLocalDiffFileWizard.run(workbenchPart, left, right); + } + + +} 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,181 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.io.PrintStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +public class UnifiedDiffFormatter { + 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$ + + private class Hunk { + private int oldStart; + private int oldEnd; + private int newStart; + private int newEnd; + ArrayList lines; + + public Hunk (int oldStart, int newStart) { + if (oldStart < 0) + oldStart = 0; + if (newStart < 0) + newStart = 0; + this.oldStart = oldStart; + this.newStart = newStart; + this.oldEnd = oldStart; + this.newEnd = newStart; + lines = new ArrayList(); + } + + public void addPartRangeToOld(ArrayList part, int start, int end, boolean lastPart) { + if (start < 0) + start = 0; + 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(OLD_LINE_PREFIX + part.get(lineNr)); + oldEnd++; + } + } + + public void addPartRangeToNew(ArrayList part, int start, int end, boolean lastPart) { + if (start < 0) + start = 0; + 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(NEW_LINE_PREFIX + part.get(lineNr)); + newEnd++; + } + } + + public void addPartRangeToBoth(ArrayList part, int start, int end, boolean lastPart) { + if (start < 0) + start = 0; + 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)); + oldEnd++; + newEnd++; + } + } + + private void printMarkerTo(PrintStream output) { + output.println("@@ -" + oldStart + "," + oldEnd + " +" + newStart + "," + newEnd + " @@"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + + public void printTo(PrintStream output) { + printMarkerTo(output); + for (int i = 0; i < lines.size(); i++) { + output.println(lines.get(i)); + } + } + } + + public void createDiff(ArrayList diffFiles, PrintStream output) { + for (int i=0;i 1 || df.isPartDifferent(0)) { + output.println(INDEX_MARKER + df.getFilePathString('L')); + output.println(DELIMITER); + + SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy hh:mm:ss"); //$NON-NLS-1$ + Date oldDate = df.getDate('L'); + Date newDate = df.getDate('R'); + output.println(OLD_FILE_PREFIX + df.getFilePathString('L') + '\t' + sdf.format(oldDate) + " -0000"); //$NON-NLS-1$ + output.println(NEW_FILE_PREFIX + df.getFilePathString('R') + '\t' + sdf.format(newDate) + " -0000"); //$NON-NLS-1$ + + boolean firstHunk = true; + + Hunk currentHunk = null; + + int currentLineNumberOld = 0; + int currentLineNumberNew = 0; + + for (int filePartNumber = 0; filePartNumber < df.getNumberOfParts(); filePartNumber++) { + + ArrayList oldPart = df.getPart(filePartNumber, 'L'); + ArrayList newPart = df.getPart(filePartNumber, 'R'); + + if (df.isPartDifferent(filePartNumber)) { + //this part has some changes + if (firstHunk) { + //hunk will start with changed block + currentHunk = new Hunk(0, 0); + firstHunk = false; + } + if (filePartNumber == (df.getNumberOfParts() - 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()-3, oldPart.size()-3); + firstHunk = false; + currentHunk.addPartRangeToBoth(oldPart, oldPart.size()-3, oldPart.size(), false); + } + else { + if (filePartNumber == (df.getNumberOfParts() - 1)) { + //if it is the last part + currentHunk.addPartRangeToBoth(oldPart, 0, 3, true); + } + else { + if (oldPart.size() < 6) { + //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, 3, false); + currentHunk.printTo(output); + currentHunk = new Hunk(currentLineNumberOld+oldPart.size()-3, currentLineNumberNew+oldPart.size()-3); + currentHunk.addPartRangeToBoth(oldPart, oldPart.size()-3, oldPart.size(), false); + } + } + } + } + currentLineNumberOld += oldPart.size(); + currentLineNumberNew += newPart.size(); + } + //print the last hunk + currentHunk.printTo(output); + } + } + } + } +}