Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.compare/compare/org/eclipse/compare/internal')
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java38
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AdapterFactory.java43
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.java144
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.properties45
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java486
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BaseCompareAction.java43
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java140
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java29
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerResources.properties23
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java98
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java128
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java82
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java64
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContainer.java112
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContentViewerSwitchingPane.java285
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java286
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java771
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java123
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorInputNavigator.java104
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorSelectionProvider.java245
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java397
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareHandlerService.java153
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.java135
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.properties143
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareOutlinePage.java157
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferenceInitializer.java27
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java489
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareStructureViewerSwitchingPane.java256
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java1421
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.java20
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.properties38
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceAction.java41
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceDialog.java838
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceHandler.java52
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ContentChangeNotifier.java91
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImageDescriptor.java168
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java202
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocumentManager.java65
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/EditionAction.java237
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ExceptionHandler.java133
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareContextIds.java53
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareUIConstants.java53
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IFlushable2.java28
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java27
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ISavingSaveable.java23
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java41
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java139
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java144
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java30
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties32
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ListContentProvider.java49
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java1039
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerAction.java44
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java201
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NavigationEndDialog.java48
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NullViewer.java35
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OutlineViewerCreator.java73
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OverlayPreferenceStore.java452
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java20
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties40
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithPreviousEditionAction.java21
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResizableDialog.java162
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java553
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowWhitespaceAction.java165
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/SimpleTextViewer.java66
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StreamMergerDescriptor.java47
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StructureCreatorDescriptor.java57
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TabFolderLayout.java53
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextEditorPropertyAction.java91
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java31
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java32
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java915
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java79
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerSwitchingCancelled.java19
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkQueue.java57
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Worker.java134
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkerJob.java60
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java1409
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/LineComparator.java63
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.java31
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.properties15
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/TextStreamMerger.java97
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DecoratorOverlayIcon.java123
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DiffViewerComparator.java56
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/FilePatch.java47
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkDiffNode.java108
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkTypedElement.java113
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java990
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/LineReader.java253
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchCompareEditorInput.java433
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchDiffNode.java68
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchErrorDialog.java19
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileDiffNode.java125
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileTypedElement.java128
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.java111
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.properties117
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchProjectDiffNode.java87
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchTargetPage.java231
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java236
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizardDialog.java38
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Patcher.java763
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java747
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/RetargetPatchElementDialog.java224
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/UnmatchedHunkTypedElement.java115
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Utilities.java106
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspaceFileDiffResult.java68
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java382
107 files changed, 20768 insertions, 0 deletions
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java
new file mode 100644
index 000000000..f76927a20
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.Viewer;
+
+
+public abstract class AbstractViewer extends Viewer {
+
+ public void setInput(Object input) {
+ // empty default implementation
+ }
+
+ public Object getInput() {
+ return null;
+ }
+
+ public ISelection getSelection() {
+ return null;
+ }
+
+ public void setSelection(ISelection s, boolean reveal) {
+ // empty default implementation
+ }
+
+ public void refresh() {
+ // empty default implementation
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AdapterFactory.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AdapterFactory.java
new file mode 100644
index 000000000..e101563b6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AdapterFactory.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 org.eclipse.compare.CompareEditorInput;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.ui.IContributorResourceAdapter;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+
+public class AdapterFactory implements IAdapterFactory {
+
+ public Object getAdapter(final Object adaptableObject, Class adapterType) {
+ if (IContributorResourceAdapter.class.equals(adapterType)
+ && adaptableObject instanceof CompareEditorInput) {
+ return new IContributorResourceAdapter() {
+ public IResource getAdaptedResource(IAdaptable adaptable) {
+ Object ei = ((CompareEditorInput) adaptableObject)
+ .getAdapter(IEditorInput.class);
+ if (ei instanceof IFileEditorInput) {
+ return ((IFileEditorInput) ei).getFile();
+ }
+ return null;
+ }
+ };
+ }
+ return null;
+ }
+
+ public Class[] getAdapterList() {
+ return new Class[] { IContributorResourceAdapter.class };
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.java
new file mode 100644
index 000000000..1fdd5c1f6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.util.ResourceBundle;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+
+public class AddFromHistoryAction extends BaseCompareAction {
+
+ private static final String BUNDLE_NAME= "org.eclipse.compare.internal.AddFromHistoryAction"; //$NON-NLS-1$
+
+ public AddFromHistoryAction() {
+ // empty default implementation
+ }
+
+ protected boolean isEnabled(ISelection selection) {
+ return Utilities.getResources(selection).length == 1;
+ }
+
+ protected void run(ISelection selection) {
+
+ ResourceBundle bundle= ResourceBundle.getBundle(BUNDLE_NAME);
+ String title= Utilities.getString(bundle, "title"); //$NON-NLS-1$
+
+ Shell parentShell= CompareUIPlugin.getShell();
+ AddFromHistoryDialog dialog= null;
+
+ Object[] s= Utilities.getResources(selection);
+
+ for (int i= 0; i < s.length; i++) {
+ Object o= s[i];
+ if (o instanceof IContainer) {
+ IContainer container= (IContainer) o;
+
+ ProgressMonitorDialog pmdialog= new ProgressMonitorDialog(parentShell);
+ IProgressMonitor pm= pmdialog.getProgressMonitor();
+ IFile[] states= null;
+ try {
+ states= container.findDeletedMembersWithHistory(IResource.DEPTH_INFINITE, pm);
+ } catch (CoreException ex) {
+ pm.done();
+ }
+
+ // There could be a recently deleted file at the same path as
+ // the container. If such a file is the only one to restore, we
+ // should not suggest to restore it, so set states to null.
+ if (states.length == 1 && states[0].getFullPath().equals(container.getFullPath()))
+ states = null;
+
+ if (states == null || states.length <= 0) {
+ String msg= Utilities.getString(bundle, "noLocalHistoryError"); //$NON-NLS-1$
+ MessageDialog.openInformation(parentShell, title, msg);
+ return;
+ }
+
+ if (dialog == null) {
+ dialog= new AddFromHistoryDialog(parentShell, bundle);
+ dialog.setHelpContextId(ICompareContextIds.ADD_FROM_HISTORY_DIALOG);
+ }
+
+ if (dialog.select(container, states)) {
+ AddFromHistoryDialog.HistoryInput[] selected = dialog
+ .getSelected();
+ if (selected != null && selected.length > 0) {
+ try {
+ updateWorkspace(bundle, parentShell, selected);
+ } catch (InterruptedException x) {
+ // Do nothing. Operation has been canceled by user.
+ } catch (InvocationTargetException x) {
+ String reason = x.getTargetException().getMessage();
+ MessageDialog.openError(parentShell, title,
+ Utilities.getFormattedString(bundle,
+ "replaceError", reason)); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void createContainers(IResource resource) throws CoreException {
+ IContainer container= resource.getParent();
+ if (container instanceof IFolder) {
+ IFolder parent= (IFolder) container;
+ if (parent != null && !parent.exists()) {
+ createContainers(parent);
+ parent.create(false, true, null);
+ }
+ }
+ }
+
+ private void updateWorkspace(final ResourceBundle bundle, Shell shell,
+ final AddFromHistoryDialog.HistoryInput[] selected)
+ throws InvocationTargetException, InterruptedException {
+
+ WorkspaceModifyOperation operation= new WorkspaceModifyOperation() {
+ public void execute(IProgressMonitor pm) throws InvocationTargetException {
+ try {
+ String taskName= Utilities.getString(bundle, "taskName"); //$NON-NLS-1$
+ pm.beginTask(taskName, selected.length);
+
+ for (int i= 0; i < selected.length; i++) {
+ IFile file= selected[i].fFile;
+ IFileState fileState= selected[i].fFileState;
+ createContainers(file);
+
+ SubProgressMonitor subMonitor= new SubProgressMonitor(pm, 1);
+ try {
+ file.create(fileState.getContents(), false, subMonitor);
+ } finally {
+ subMonitor.done();
+ }
+ }
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ pm.done();
+ }
+ }
+ };
+
+ ProgressMonitorDialog pmdialog= new ProgressMonitorDialog(shell);
+ pmdialog.run(false, true, operation);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.properties
new file mode 100644
index 000000000..0a97df87f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryAction.properties
@@ -0,0 +1,45 @@
+###############################################################################
+# Copyright (c) 2000, 2010 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
+###############################################################################
+
+# @(#)AddFromHistoryAction.properties
+#
+# Resources for AddFromHistoryAction.java
+
+title= Restore from Local History
+
+memberPaneTitle= {0} - Available Files in Local History:
+
+treeTitleFormat= Local History of ''{0}''
+dateIcon= obj16/day_obj.gif
+timeIcon= obj16/resource_obj.gif
+
+memberDescription= Check files to restore from local history:
+editionDescription= Select an edition of a file:
+
+treeFormat= {0}
+workspaceTreeFormat= {0} (Workspace File)
+parseErrorFormat= {0} (Parse Error)
+
+editionLabel= Local History ({0})
+workspaceEditionLabel= Workspace File
+
+targetLabel= {0}
+
+todayFormat= Today ({0})
+yesterdayFormat= Yesterday ({0})
+dayFormat= {0}
+
+buttonLabel= &Restore
+
+noLocalHistoryError= No deleted resources in local history for selected container.
+replaceError=Cannot replace resource (reason: {0}).
+
+taskName=Restoring
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java
new file mode 100644
index 000000000..0bb24bc9e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java
@@ -0,0 +1,486 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.io.*;
+import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.text.MessageFormat;
+import java.util.ArrayList;
+import com.ibm.icu.util.Calendar;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+import org.eclipse.compare.*;
+
+
+public class AddFromHistoryDialog extends ResizableDialog {
+
+ static class HistoryInput implements ITypedElement, IEncodedStreamContentAccessor, IModificationDate {
+ IFile fFile;
+ IFileState fFileState;
+
+ HistoryInput(IFile file, IFileState fileState) {
+ fFile= file;
+ fFileState= fileState;
+ }
+ public InputStream getContents() throws CoreException {
+ return new BufferedInputStream(fFileState.getContents());
+ }
+ public String getCharset() {
+ String charset= null;
+ try {
+ charset= fFileState.getCharset();
+ } catch (CoreException e) {
+ // fall through
+ }
+ if (charset == null)
+ charset= Utilities.getCharset(fFile);
+ return charset;
+ }
+ public String getName() {
+ return fFile.getName();
+ }
+ public String getType() {
+ return fFile.getFileExtension();
+ }
+ public Image getImage() {
+ return CompareUI.getImage(fFile);
+ }
+ public long getModificationDate() {
+ return fFileState.getModificationTime();
+ }
+ }
+
+ static class FileHistory {
+ private IFile fFile;
+ private IFileState[] fStates;
+ private int fSelected;
+
+ FileHistory(IFile file) {
+ fFile= file;
+ }
+
+ IFile getFile() {
+ return fFile;
+ }
+
+ IFileState[] getStates() {
+ if (fStates == null) {
+ try {
+ fStates= fFile.getHistory(new NullProgressMonitor());
+ } catch (CoreException ex) {
+ // NeedWork
+ }
+ }
+ return fStates;
+ }
+
+ IFileState getSelectedState() {
+ return getStates()[fSelected];
+ }
+
+ void setSelected(IFileState state) {
+ for (int i= 0; i < fStates.length; i++) {
+ if (fStates[i] == state) {
+ fSelected= i;
+ return;
+ }
+ }
+ }
+
+ HistoryInput getHistoryInput() {
+ return new HistoryInput(fFile, getSelectedState());
+ }
+
+ boolean isSelected(int index) {
+ return index == fSelected;
+ }
+ }
+
+ private CompareConfiguration fCompareConfiguration;
+ private ArrayList fArrayList= new ArrayList();
+ private FileHistory fCurrentFileHistory;
+
+ // SWT controls
+ private CompareViewerSwitchingPane fContentPane;
+ private Button fCommitButton;
+ private Table fMemberTable;
+ private CompareViewerPane fMemberPane;
+ private Tree fEditionTree;
+ private CompareViewerPane fEditionPane;
+ private Image fDateImage;
+ private Image fTimeImage;
+
+
+ public AddFromHistoryDialog(Shell parent, ResourceBundle bundle) {
+ super(parent, bundle);
+
+ String iconName= Utilities.getString(fBundle, "dateIcon", "obj16/day_obj.gif"); //$NON-NLS-2$ //$NON-NLS-1$
+ ImageDescriptor id= CompareUIPlugin.getImageDescriptor(iconName);
+ if (id != null)
+ fDateImage= id.createImage();
+ iconName= Utilities.getString(fBundle, "timeIcon", "obj16/resource_obj.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ id= CompareUIPlugin.getImageDescriptor(iconName);
+ if (id != null)
+ fTimeImage= id.createImage();
+ }
+
+ public boolean select(IContainer root, IFile[] inputFiles) {
+
+ create(); // create widgets
+
+ String format= Utilities.getString(fBundle, "memberPaneTitle"); //$NON-NLS-1$
+ String title= MessageFormat.format(format, new Object[] { root.getName() });
+ fMemberPane.setImage(CompareUI.getImage(root));
+ fMemberPane.setText(title);
+
+ // sort input files
+ final int count= inputFiles.length;
+ final IFile[] files= new IFile[count];
+ for (int i= 0; i < count; i++)
+ files[i]= inputFiles[i];
+ if (count > 1)
+ internalSort(files, 0, count-1);
+
+
+ String prefix= root.getFullPath().toString();
+
+ if (fMemberTable != null && !fMemberTable.isDisposed()) {
+ for (int i = 0; i < files.length; i++) {
+ IFile file = files[i];
+ String path = file.getFullPath().toString();
+
+ // ignore a recently deleted file at the same path as the
+ // container
+ if (path.equals(prefix))
+ continue;
+
+ if (path.startsWith(prefix))
+ path = path.substring(prefix.length() + 1);
+ TableItem ti = new TableItem(fMemberTable, SWT.NONE);
+ ti.setImage(CompareUI.getImage(file));
+ ti.setText(path);
+ ti.setData(new FileHistory(file));
+ }
+ }
+
+ open();
+
+ return (getReturnCode() == OK) && (fArrayList.size() > 0);
+ }
+
+ HistoryInput[] getSelected() {
+ HistoryInput[] selected= new HistoryInput[fArrayList.size()];
+ Iterator iter= fArrayList.iterator();
+ for (int i= 0; iter.hasNext(); i++) {
+ FileHistory h= (FileHistory) iter.next();
+ selected[i]= h.getHistoryInput();
+ }
+ return selected;
+ }
+
+ protected synchronized Control createDialogArea(Composite parent2) {
+
+ Composite parent= (Composite) super.createDialogArea(parent2);
+
+ getShell().setText(Utilities.getString(fBundle, "title")); //$NON-NLS-1$
+
+ org.eclipse.compare.Splitter vsplitter= new org.eclipse.compare.Splitter(parent, SWT.VERTICAL);
+ vsplitter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL
+ | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
+
+ vsplitter.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fDateImage != null)
+ fDateImage.dispose();
+ if (fTimeImage != null)
+ fTimeImage.dispose();
+ }
+ }
+ );
+
+ // we need two panes: the left for the elements, the right one for the editions
+ Splitter hsplitter= new Splitter(vsplitter, SWT.HORIZONTAL);
+
+ Composite c= new Composite(hsplitter, SWT.NONE);
+ GridLayout layout= new GridLayout();
+ layout.marginWidth= 0;
+ layout.marginHeight= 2;
+ layout.verticalSpacing= 2;
+ layout.numColumns= 1;
+ c.setLayout(layout);
+ Label l1= new Label(c, SWT.NONE);
+ l1.setText(Utilities.getString(fBundle, "memberDescription")); //$NON-NLS-1$
+ fMemberPane= new CompareViewerPane(c, SWT.BORDER | SWT.FLAT);
+ GridData gd= new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
+ fMemberPane.setLayoutData(gd);
+
+ fMemberTable= new Table(fMemberPane, SWT.CHECK | SWT.H_SCROLL | SWT.V_SCROLL);
+ fMemberTable.addSelectionListener(
+ new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if (e.detail == SWT.CHECK) {
+ if (e.item instanceof TableItem) {
+ TableItem ti= (TableItem) e.item;
+ if (ti.getChecked())
+ fArrayList.add(ti.getData());
+ else
+ fArrayList.remove(ti.getData());
+
+ if (fCommitButton != null)
+ fCommitButton.setEnabled(fArrayList.size() > 0);
+ }
+ } else {
+ handleMemberSelect(e.item);
+ }
+ }
+ }
+ );
+
+ fMemberPane.setContent(fMemberTable);
+
+ c= new Composite(hsplitter, SWT.NONE);
+ layout= new GridLayout();
+ layout.marginWidth= 0;
+ layout.marginHeight= 2;
+ layout.verticalSpacing= 2;
+ layout.numColumns= 1;
+ c.setLayout(layout);
+ Label l2= new Label(c, SWT.NONE);
+ l2.setText(Utilities.getString(fBundle, "editionDescription")); //$NON-NLS-1$
+ fEditionPane= new CompareViewerPane(c, SWT.BORDER | SWT.FLAT);
+ gd= new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
+ fEditionPane.setLayoutData(gd);
+
+ fEditionTree= new Tree(fEditionPane, SWT.H_SCROLL | SWT.V_SCROLL);
+ fEditionTree.addSelectionListener(
+ new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ feedContent(e.item);
+ }
+ }
+ );
+ fEditionPane.setContent(fEditionTree);
+
+ applyDialogFont(parent); // to avoid applying font to compare viewer
+ fContentPane= new CompareViewerSwitchingPane(vsplitter, SWT.BORDER | SWT.FLAT) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return CompareUI.findContentViewer(oldViewer, input, this, fCompareConfiguration);
+ }
+ };
+ vsplitter.setWeights(new int[] { 30, 70 });
+
+ return parent;
+ }
+
+ /*
+ * Feeds selection from member viewer to edition viewer.
+ */
+ private void handleMemberSelect(Widget w) {
+ Object data= null;
+ if (w != null)
+ data= w.getData();
+ if (data instanceof FileHistory) {
+
+ FileHistory h= (FileHistory) data;
+ fCurrentFileHistory= h;
+
+ IFile file= h.getFile();
+ IFileState[] states= h.getStates();
+
+ fEditionPane.setImage(CompareUI.getImage(file));
+ String pattern= Utilities.getString(fBundle, "treeTitleFormat"); //$NON-NLS-1$
+ String title= MessageFormat.format(pattern, new Object[] { file.getName() });
+ fEditionPane.setText(title);
+
+ if (fEditionTree != null) {
+ fEditionTree.setRedraw(false);
+ fEditionTree.removeAll();
+ for (int i= 0; i < states.length; i++) {
+ addEdition(new HistoryInput(file, states[i]), h.isSelected(i));
+ }
+ fEditionTree.setRedraw(true);
+ }
+ } else
+ fCurrentFileHistory= null;
+ }
+
+ /*
+ * Adds the given Pair to the edition tree.
+ * It takes care of creating tree nodes for different dates.
+ */
+ private void addEdition(HistoryInput input, boolean isSelected) {
+ if (fEditionTree == null || fEditionTree.isDisposed())
+ return;
+
+ IFileState state= input.fFileState;
+
+ // find last day
+ TreeItem[] days= fEditionTree.getItems();
+ TreeItem lastDay= null;
+ if (days.length > 0)
+ lastDay= days[days.length-1];
+
+ long ldate= state.getModificationTime();
+ long day= dayNumber(ldate);
+ Date date= new Date(ldate);
+ if (lastDay == null || day != dayNumber(((Date)lastDay.getData()).getTime())) {
+ lastDay= new TreeItem(fEditionTree, SWT.NONE);
+ lastDay.setImage(fDateImage);
+ String df= DateFormat.getDateInstance().format(date);
+ long today= dayNumber(System.currentTimeMillis());
+
+ String formatKey;
+ if (day == today)
+ formatKey= "todayFormat"; //$NON-NLS-1$
+ else if (day == today-1)
+ formatKey= "yesterdayFormat"; //$NON-NLS-1$
+ else
+ formatKey= "dayFormat"; //$NON-NLS-1$
+ String pattern= Utilities.getString(fBundle, formatKey);
+ if (pattern != null)
+ df= MessageFormat.format(pattern, new String[] { df });
+ lastDay.setText(df);
+ lastDay.setData(date);
+ }
+ TreeItem ti= new TreeItem(lastDay, SWT.NONE);
+ ti.setImage(fTimeImage);
+ ti.setText(DateFormat.getTimeInstance().format(date));
+ ti.setData(input);
+
+ if (isSelected) {
+ lastDay.setExpanded(true);
+ fEditionTree.setSelection(new TreeItem[] { ti });
+ feedContent(ti);
+ }
+ }
+
+ /*
+ * Returns the number of s since Jan 1st, 1970.
+ * The given date is converted to GMT and daylight saving is taken into account too.
+ */
+ private long dayNumber(long date) {
+ int ONE_DAY_MS= 24*60*60 * 1000; // one day in milli seconds
+
+ Calendar calendar= Calendar.getInstance();
+ long localTimeOffset= calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
+
+ return (date + localTimeOffset) / ONE_DAY_MS;
+ }
+
+ /*
+ * Feeds the tree viewer's selection to the contentviewer
+ */
+ private void feedContent(Widget w) {
+ if (fContentPane != null && !fContentPane.isDisposed()) {
+ Object o= w.getData();
+ if (o instanceof HistoryInput) {
+ HistoryInput selected= (HistoryInput) o;
+ fContentPane.setInput(selected);
+ fContentPane.setText(getEditionLabel(selected));
+ fContentPane.setImage(fTimeImage);
+
+ if (fCurrentFileHistory != null)
+ fCurrentFileHistory.setSelected(selected.fFileState);
+ } else {
+ fContentPane.setInput(null);
+ }
+ }
+ }
+
+ protected String getEditionLabel(HistoryInput input) {
+ String format= Utilities.getString(fBundle, "historyEditionLabel", null); //$NON-NLS-1$
+ if (format == null)
+ format= Utilities.getString(fBundle, "editionLabel"); //$NON-NLS-1$
+ if (format == null)
+ format= "x{0}"; //$NON-NLS-1$
+
+ long modDate= input.getModificationDate();
+ String date= DateFormat.getDateTimeInstance().format(new Date(modDate));
+
+ return MessageFormat.format(format, new Object[] { date });
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Dialog.
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+ String buttonLabel= Utilities.getString(fBundle, "buttonLabel", IDialogConstants.OK_LABEL); //$NON-NLS-1$
+ // a 'Cancel' and a 'Add' button
+ fCommitButton= createButton(parent, IDialogConstants.OK_ID, buttonLabel, true);
+ fCommitButton.setEnabled(false);
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+ }
+
+ /*
+ * Returns true if the pathname of f1 comes after f2
+ */
+ private static boolean greaterThan(IFile f1, IFile f2) {
+ String[] ss1= f1.getFullPath().segments();
+ String[] ss2= f2.getFullPath().segments();
+ int l1= ss1.length;
+ int l2= ss2.length;
+ int n= Math.max(l1, l2);
+
+ for (int i= 0; i < n; i++) {
+ String s1= i < l1 ? ss1[i] : ""; //$NON-NLS-1$
+ String s2= i < l2 ? ss2[i] : ""; //$NON-NLS-1$
+ int rc= s1.compareToIgnoreCase(s2);
+ if (rc != 0)
+ return rc < 0;
+ }
+ return false;
+ }
+
+ private static void internalSort(IFile[] keys, int left, int right) {
+
+ int original_left= left;
+ int original_right= right;
+
+ IFile mid= keys[(left + right) / 2];
+ do {
+ while (greaterThan(keys[left], mid))
+ left++;
+
+ while (greaterThan(mid, keys[right]))
+ right--;
+
+ if (left <= right) {
+ IFile tmp= keys[left];
+ keys[left]= keys[right];
+ keys[right]= tmp;
+ left++;
+ right--;
+ }
+ } while (left <= right);
+
+ if (original_left < right)
+ internalSort(keys, original_left, right);
+
+ if (left < original_right)
+ internalSort(keys, left, original_right);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BaseCompareAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BaseCompareAction.java
new file mode 100644
index 000000000..0998e18d4
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BaseCompareAction.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IActionDelegate;
+
+
+public abstract class BaseCompareAction implements IActionDelegate {
+
+ private ISelection fSelection;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+ */
+ final public void run(IAction action) {
+ run(fSelection);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+ */
+ final public void selectionChanged(IAction action, ISelection selection) {
+ fSelection= selection;
+ if (action != null)
+ action.setEnabled(isEnabled(fSelection));
+ }
+
+ protected boolean isEnabled(ISelection selection) {
+ return false;
+ }
+
+ abstract protected void run(ISelection selection);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java
new file mode 100644
index 000000000..c6d9d2e91
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.io.IOException;
+import java.io.InputStream;
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.IStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.PlatformUI;
+
+import com.ibm.icu.text.MessageFormat;
+
+/**
+ * A simple compare viewer for binary files.
+ * Shows the position of the first non-matching byte.
+ */
+public class BinaryCompareViewer extends AbstractViewer {
+
+ private static final String BUNDLE_NAME = "org.eclipse.compare.internal.BinaryCompareViewerResources"; //$NON-NLS-1$
+
+ private static final int EOF = -1;
+ private ICompareInput fInput;
+ private ResourceBundle fBundle;
+ private boolean fLeftIsLocal;
+
+ private Composite fComposite;
+ private Label fMessage;
+
+ public BinaryCompareViewer(Composite parent, final CompareConfiguration cc) {
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, ICompareContextIds.BINARY_COMPARE_VIEW);
+
+ fBundle= ResourceBundle.getBundle(BUNDLE_NAME);
+
+ fComposite= new Composite(parent, SWT.NONE);
+ RowLayout rowLayout = new RowLayout();
+ rowLayout.type = SWT.VERTICAL;
+ fComposite.setLayout(rowLayout);
+
+ fMessage= new Label(fComposite, SWT.WRAP);
+ fComposite.setData(CompareUI.COMPARE_VIEWER_TITLE, Utilities.getString(fBundle, "title")); //$NON-NLS-1$
+
+ fLeftIsLocal= Utilities.getBoolean(cc, "LEFT_IS_LOCAL", false); //$NON-NLS-1$
+
+ if (cc != null && cc.getContainer() instanceof CompareEditorInput) {
+ Label compareAsTextLabel = new Label(fComposite, SWT.WRAP);
+ compareAsTextLabel
+ .setText(Utilities.getString(fBundle, "compareAsText")); //$NON-NLS-1$
+ }
+ }
+
+ public Control getControl() {
+ return fComposite;
+ }
+
+ public void setInput(Object input) {
+ if (fComposite != null && input instanceof ICompareInput) {
+ fInput= (ICompareInput) input;
+
+ InputStream left= null;
+ InputStream right= null;
+
+ String message= null;
+ try {
+ left= getStream(fInput.getLeft());
+ right= getStream(fInput.getRight());
+
+ if (left != null && right != null) {
+ int pos= 0;
+ while (true) {
+ int l= left.read();
+ int r= right.read();
+ if (l != r) {
+ String format= Utilities.getString(fBundle, "diffMessageFormat"); //$NON-NLS-1$
+ message= MessageFormat.format(format, new String[] { Integer.toString(pos) } );
+ break;
+ }
+ if (l == EOF)
+ break;
+ pos++;
+ }
+ } else if (left == null && right == null) {
+ message= Utilities.getString(fBundle, "deleteConflictMessage"); //$NON-NLS-1$
+ } else if (left == null) {
+ if (fLeftIsLocal)
+ message= Utilities.getString(fBundle, "deletedMessage"); //$NON-NLS-1$
+ else
+ message= Utilities.getString(fBundle, "addedMessage"); //$NON-NLS-1$
+ } else if (right == null) {
+ if (fLeftIsLocal)
+ message= Utilities.getString(fBundle, "addedMessage"); //$NON-NLS-1$
+ else
+ message= Utilities.getString(fBundle, "deletedMessage"); //$NON-NLS-1$
+ }
+ } catch (CoreException ex) {
+ message = Utilities.getString(fBundle, "errorMessage"); //$NON-NLS-1$
+ CompareUIPlugin.log(ex);
+ } catch (IOException ex) {
+ message = Utilities.getString(fBundle, "errorMessage"); //$NON-NLS-1$
+ CompareUIPlugin.log(ex);
+ } finally {
+ Utilities.close(left);
+ Utilities.close(right);
+ }
+ if (message != null)
+ fMessage.setText(message);
+ fComposite.layout();
+ }
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ private InputStream getStream(ITypedElement input) throws CoreException {
+ if (input instanceof IStreamContentAccessor)
+ return ((IStreamContentAccessor)input).getContents();
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java
new file mode 100644
index 000000000..e66bd88b6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.widgets.Composite;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+
+/**
+ * A factory object for the <code>BinaryCompareViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class BinaryCompareViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new BinaryCompareViewer(parent, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerResources.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerResources.properties
new file mode 100644
index 000000000..225cb447b
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerResources.properties
@@ -0,0 +1,23 @@
+###############################################################################
+# Copyright (c) 2000, 2009 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
+###############################################################################
+
+# @(#)BinaryCompareViewerResources.properties
+#
+# Resource strings for BinaryCompareViewer.java
+
+title= Binary Compare
+
+diffMessageFormat= First bytes differ at position {0}
+deleteConflictMessage= Delete Conflict
+addedMessage= Added Resource
+deletedMessage= Removed Resource
+errorMessage= Internal Error
+compareAsText= Switch to Text Compare using the drop down menu above
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java
new file mode 100644
index 000000000..af8af9041
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.events.*;
+
+/**
+ * A Canvas which reduces flicker by drawing in an off screen buffer.
+ */
+public abstract class BufferedCanvas extends Canvas {
+
+ //private static final boolean USE_DOUBLE_BUFFER= !"carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
+ private static final boolean USE_DOUBLE_BUFFER= true;
+
+ /** The drawable for double buffering */
+ Image fBuffer;
+
+ public BufferedCanvas(Composite parent, int flags) {
+ super(parent, flags | SWT.NO_BACKGROUND);
+
+ addPaintListener(
+ new PaintListener() {
+ public void paintControl(PaintEvent event) {
+ doubleBufferPaint(event.gc);
+ }
+ }
+ );
+
+ addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fBuffer != null) {
+ fBuffer.dispose();
+ fBuffer= null;
+ }
+ }
+ }
+ );
+ }
+
+ public void repaint() {
+ if (!isDisposed()) {
+ GC gc= new GC(this);
+ doubleBufferPaint(gc);
+ gc.dispose();
+ }
+ }
+
+ /*
+ * Double buffer drawing.
+ */
+ void doubleBufferPaint(GC dest) {
+
+ if (!USE_DOUBLE_BUFFER) {
+ doPaint(dest);
+ return;
+ }
+
+ Point size= getSize();
+
+ if (size.x <= 1 || size.y <= 1) // we test for <= 1 because on X11 controls have initial size 1,1
+ return;
+
+ if (fBuffer != null) {
+ Rectangle r= fBuffer.getBounds();
+ if (r.width != size.x || r.height != size.y) {
+ fBuffer.dispose();
+ fBuffer= null;
+ }
+ }
+ if (fBuffer == null)
+ fBuffer= new Image(getDisplay(), size.x, size.y);
+
+ GC gc= new GC(fBuffer);
+ try {
+ gc.setBackground(getBackground());
+ gc.fillRectangle(0, 0, size.x, size.y);
+ doPaint(gc);
+ } finally {
+ gc.dispose();
+ }
+
+ dest.drawImage(fBuffer, 0, 0);
+ }
+
+ abstract public void doPaint(GC gc);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
new file mode 100644
index 000000000..ccf566c21
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.io.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.IStructureComparator;
+
+/**
+ * A buffer for a workspace resource.
+ */
+public class BufferedResourceNode extends ResourceNode {
+
+ private boolean fDirty= false;
+ private IFile fDeleteFile;
+
+ /**
+ * Creates a <code>ResourceNode</code> for the given resource.
+ *
+ * @param resource the resource
+ */
+ public BufferedResourceNode(IResource resource) {
+ super(resource);
+ }
+
+ /*
+ * Returns <code>true</code> if buffer contains uncommitted changes.
+ */
+ public boolean isDirty() {
+ return fDirty;
+ }
+
+ protected IStructureComparator createChild(IResource child) {
+ return new BufferedResourceNode(child);
+ }
+
+ public void setContent(byte[] contents) {
+ fDirty= true;
+ super.setContent(contents);
+ }
+
+ /*
+ * Commits buffered contents to resource.
+ */
+ public void commit(IProgressMonitor pm) throws CoreException {
+ if (fDirty) {
+
+ if (fDeleteFile != null) {
+ fDeleteFile.delete(true, true, pm);
+ return;
+ }
+
+ IResource resource= getResource();
+ if (resource instanceof IFile) {
+
+ byte[] bytes= getContent();
+ ByteArrayInputStream is= new ByteArrayInputStream(bytes);
+ try {
+ IFile file= (IFile) resource;
+ if (file.exists())
+ file.setContents(is, false, true, pm);
+ else
+ file.create(is, false, pm);
+ fDirty= false;
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch(IOException ex) {
+ // Silently ignored
+ }
+ }
+ }
+ }
+ }
+
+ public ITypedElement replace(ITypedElement child, ITypedElement other) {
+
+ if (child == null) { // add resource
+ // create a node without a resource behind it!
+ IResource resource= getResource();
+ if (resource instanceof IFolder) {
+ IFolder folder= (IFolder) resource;
+ IFile file= folder.getFile(other.getName());
+ child= new BufferedResourceNode(file);
+ }
+ }
+
+ if (other == null) { // delete resource
+ IResource resource= getResource();
+ if (resource instanceof IFolder) {
+ IFolder folder= (IFolder) resource;
+ IFile file= folder.getFile(child.getName());
+ if (file != null && file.exists()) {
+ fDeleteFile= file;
+ fDirty= true;
+ }
+ }
+ return null;
+ }
+
+ if (other instanceof IStreamContentAccessor && child instanceof IEditableContent) {
+ IEditableContent dst= (IEditableContent) child;
+
+ try {
+ InputStream is= ((IStreamContentAccessor)other).getContents();
+ byte[] bytes= Utilities.readBytes(is);
+ if (bytes != null)
+ dst.setContent(bytes);
+ } catch (CoreException ex) {
+ // NeedWork
+ }
+ }
+ return child;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java
new file mode 100644
index 000000000..85fd9b48e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.util.ResourceBundle;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * Toggles a boolean property of an <code>CompareConfiguration</code>.
+ */
+public class ChangePropertyAction extends Action implements IPropertyChangeListener, DisposeListener {
+
+ private CompareConfiguration fCompareConfiguration;
+ private String fPropertyKey;
+ private ResourceBundle fBundle;
+ private String fPrefix;
+
+ public static ChangePropertyAction createIgnoreWhiteSpaceAction(ResourceBundle bundle, CompareConfiguration compareConfiguration) {
+ return new ChangePropertyAction(bundle, compareConfiguration, "action.IgnoreWhiteSpace.", CompareConfiguration.IGNORE_WHITESPACE); //$NON-NLS-1$
+ }
+ public static ChangePropertyAction createShowPseudoConflictsAction(ResourceBundle bundle, CompareConfiguration compareConfiguration) {
+ return new ChangePropertyAction(bundle, compareConfiguration, "action.ShowPseudoConflicts.", CompareConfiguration.SHOW_PSEUDO_CONFLICTS); //$NON-NLS-1$
+ }
+
+ public ChangePropertyAction(ResourceBundle bundle, CompareConfiguration cc, String rkey, String pkey) {
+ fPropertyKey= pkey;
+ fBundle= bundle;
+ fPrefix= rkey;
+ Utilities.initAction(this, fBundle, fPrefix);
+ setCompareConfiguration(cc);
+ }
+
+ public void run() {
+ boolean b= !Utilities.getBoolean(fCompareConfiguration, fPropertyKey, false);
+ setChecked(b);
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.setProperty(fPropertyKey, new Boolean(b));
+ }
+
+ public void setChecked(boolean state) {
+ super.setChecked(state);
+ Utilities.initToggleAction(this, fBundle, fPrefix, state);
+ }
+
+ public void setCompareConfiguration(CompareConfiguration cc) {
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.removePropertyChangeListener(this);
+ fCompareConfiguration= cc;
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.addPropertyChangeListener(this);
+ setChecked(Utilities.getBoolean(fCompareConfiguration, fPropertyKey, false));
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(fPropertyKey)) {
+ setChecked(Utilities.getBoolean(fCompareConfiguration, fPropertyKey, false));
+ }
+ }
+
+ public void dispose(){
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.removePropertyChangeListener(this);
+ }
+
+ public void widgetDisposed(DisposeEvent e) {
+ dispose();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java
new file mode 100644
index 000000000..0ff9e5444
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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
+ * Matt McCutchen (hashproduct+eclipse@gmail.com) - Bug 35390 Three-way compare cannot select (mis-selects) )ancestor resource
+ * Aleksandra Wozniak (aleksandra.k.wozniak@gmail.com) - Bug 239959
+ *******************************************************************************/
+package org.eclipse.compare.internal;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+
+
+/*
+ * The "Compare with each other" action
+ */
+public class CompareAction extends BaseCompareAction implements IObjectActionDelegate {
+
+ protected ResourceCompareInput fInput;
+ protected IWorkbenchPage fWorkbenchPage;
+ protected boolean showSelectAncestorDialog = true;
+
+ public void run(ISelection selection) {
+ if (fInput != null) {
+ // Pass the shell so setSelection can prompt the user for which
+ // resource should be the ancestor
+ boolean ok = fInput.setSelection(selection, fWorkbenchPage
+ .getWorkbenchWindow().getShell(), showSelectAncestorDialog);
+ if (!ok) return;
+ fInput.initializeCompareConfiguration();
+ CompareUI.openCompareEditorOnPage(fInput, fWorkbenchPage);
+ fInput= null; // don't reuse this input!
+ }
+ }
+
+ protected boolean isEnabled(ISelection selection) {
+ if (fInput == null) {
+ CompareConfiguration cc= new CompareConfiguration();
+ // buffered merge mode: don't ask for confirmation
+ // when switching between modified resources
+ cc.setProperty(CompareEditor.CONFIRM_SAVE_PROPERTY, new Boolean(false));
+
+ // uncomment following line to have separate outline view
+ //cc.setProperty(CompareConfiguration.USE_OUTLINE_VIEW, new Boolean(true));
+
+ fInput= new ResourceCompareInput(cc);
+ }
+ return fInput.isEnabled(selection);
+ }
+
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ fWorkbenchPage= targetPart.getSite().getPage();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContainer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContainer.java
new file mode 100644
index 000000000..2a106c004
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContainer.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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.InvocationTargetException;
+
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.compare.ICompareNavigator;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.ui.*;
+import org.eclipse.ui.services.IServiceLocator;
+
+public class CompareContainer implements ICompareContainer {
+
+ private WorkerJob worker;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#setStatusMessage(java.lang.String)
+ */
+ public void setStatusMessage(String message) {
+ // Do nothing, by default
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#addCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ public void addCompareInputChangeListener(ICompareInput input,
+ ICompareInputChangeListener listener) {
+ input.addCompareInputChangeListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ public void removeCompareInputChangeListener(ICompareInput input,
+ ICompareInputChangeListener listener) {
+ input.removeCompareInputChangeListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#registerContextMenu(org.eclipse.jface.action.MenuManager, org.eclipse.jface.viewers.ISelectionProvider)
+ */
+ public void registerContextMenu(MenuManager menu,
+ ISelectionProvider selectionProvider) {
+ // Nothing to do
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#getServiceLocator()
+ */
+ public IServiceLocator getServiceLocator() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#getActionBars()
+ */
+ public IActionBars getActionBars() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.operation.IRunnableContext#run(boolean, boolean, org.eclipse.jface.operation.IRunnableWithProgress)
+ */
+ public void run(boolean fork, boolean cancelable,
+ IRunnableWithProgress runnable)
+ throws InvocationTargetException, InterruptedException {
+ PlatformUI.getWorkbench().getProgressService().run(fork, cancelable, runnable);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#getNavigator()
+ */
+ public ICompareNavigator getNavigator() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#runAsynchronously(org.eclipse.jface.operation.IRunnableWithProgress)
+ */
+ public synchronized void runAsynchronously(IRunnableWithProgress runnable) {
+ if (worker == null)
+ worker = createWorkerJob();
+ worker.add(runnable);
+ }
+
+ protected WorkerJob createWorkerJob() {
+ WorkerJob workerJob = new WorkerJob(getWorkerJobName());
+ return workerJob;
+ }
+
+ protected String getWorkerJobName() {
+ return CompareMessages.CompareContainer_0;
+ }
+
+ public IWorkbenchPart getWorkbenchPart() {
+ return null;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContentViewerSwitchingPane.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContentViewerSwitchingPane.java
new file mode 100644
index 000000000..d92ad78ba
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareContentViewerSwitchingPane.java
@@ -0,0 +1,285 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareViewerSwitchingPane;
+import org.eclipse.compare.Splitter;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MenuAdapter;
+import org.eclipse.swt.events.MenuEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.ui.PlatformUI;
+
+public class CompareContentViewerSwitchingPane extends
+ CompareViewerSwitchingPane {
+
+ private static final String OPTIMIZED_INFO_IMAGE_NAME = "obj16/message_info.gif"; //$NON-NLS-1$
+ public static final String OPTIMIZED_ALGORITHM_USED = "OPTIMIZED_ALGORITHM_USED"; //$NON-NLS-1$
+
+ private CompareEditorInput fCompareEditorInput;
+
+ private ViewerDescriptor fSelectedViewerDescriptor;
+
+ private ToolBar toolBar;
+ private CLabel clOptimized;
+
+ private boolean menuShowing;
+
+ public CompareContentViewerSwitchingPane(Splitter parent, int style,
+ CompareEditorInput cei) {
+ super(parent, style);
+ fCompareEditorInput = cei;
+ }
+
+ private CompareConfiguration getCompareConfiguration() {
+ return fCompareEditorInput.getCompareConfiguration();
+ }
+
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ if (fSelectedViewerDescriptor != null) {
+ ViewerDescriptor[] array = CompareUIPlugin.getDefault().findContentViewerDescriptor(
+ oldViewer, input, getCompareConfiguration());
+ List list = array != null ? Arrays.asList(array)
+ : Collections.EMPTY_LIST;
+ if (list.contains(fSelectedViewerDescriptor)) {
+ // use selected viewer only when appropriate for the new input
+ fCompareEditorInput
+ .setContentViewerDescriptor(fSelectedViewerDescriptor);
+ Viewer viewer = fCompareEditorInput.findContentViewer(
+ oldViewer, (ICompareInput) input, this);
+ return viewer;
+ }
+ // fallback to default otherwise
+ fSelectedViewerDescriptor = null;
+ }
+ if (input instanceof ICompareInput) {
+ fCompareEditorInput.setContentViewerDescriptor(null);
+ Viewer viewer = fCompareEditorInput.findContentViewer(oldViewer,
+ (ICompareInput) input, this);
+ fCompareEditorInput.setContentViewerDescriptor(fSelectedViewerDescriptor);
+ return viewer;
+ }
+ return null;
+ }
+
+ protected Control createTopLeft(Composite p) {
+ final Composite composite = new Composite(p, SWT.NONE) {
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ return super.computeSize(wHint, Math.max(24, hHint), changed);
+ }
+ };
+
+ RowLayout layout = new RowLayout();
+ layout.marginTop = 0;
+ composite.setLayout(layout);
+
+ CLabel cl = new CLabel(composite, SWT.NONE);
+ cl.setText(null);
+
+ toolBar = new ToolBar(composite, SWT.FLAT);
+ toolBar.setVisible(false); // hide by default
+ final ToolItem toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
+ toolItem.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(
+ /* IWorkbenchGraphicConstants */"IMG_LCL_VIEW_MENU")); //$NON-NLS-1$
+ toolItem
+ .setToolTipText(CompareMessages.CompareContentViewerSwitchingPane_switchButtonTooltip);
+ toolItem.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ showMenu();
+ }
+ });
+ toolBar.addMouseListener(new MouseAdapter() {
+ public void mouseDown(MouseEvent e) {
+ showMenu();
+ }
+ });
+
+ clOptimized = new CLabel(composite, SWT.NONE);
+ clOptimized
+ .setText(CompareMessages.CompareContentViewerSwitchingPane_optimized);
+ clOptimized
+ .setToolTipText(CompareMessages.CompareContentViewerSwitchingPane_optimizedTooltip);
+ clOptimized.setImage(CompareUIPlugin.getImageDescriptor(
+ OPTIMIZED_INFO_IMAGE_NAME).createImage());
+ clOptimized.setVisible(false); // hide by default
+ clOptimized.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ Image img = clOptimized.getImage();
+ if ((img != null) && (!img.isDisposed())) {
+ img.dispose();
+ }
+ }
+ });
+
+ return composite;
+ }
+
+ protected boolean inputChanged(Object input) {
+ return getInput() != input
+ || fCompareEditorInput.getContentViewerDescriptor() != fSelectedViewerDescriptor;
+ }
+
+ public void setInput(Object input) {
+ super.setInput(input);
+ if (getViewer() == null || !Utilities.okToUse(getViewer().getControl()))
+ return;
+ ViewerDescriptor[] vd = CompareUIPlugin.getDefault()
+ .findContentViewerDescriptor(getViewer(), getInput(),
+ getCompareConfiguration());
+ toolBar.setVisible(vd != null && vd.length > 1);
+ CompareConfiguration cc = getCompareConfiguration();
+ Boolean isOptimized = (Boolean) cc.getProperty(OPTIMIZED_ALGORITHM_USED);
+ clOptimized.setVisible(isOptimized != null && isOptimized.booleanValue());
+ }
+
+ private void showMenu() {
+ if (menuShowing)
+ return;
+ menuShowing= true;
+
+ ViewerDescriptor[] vd = CompareUIPlugin.getDefault()
+ .findContentViewerDescriptor(getViewer(), getInput(),
+ getCompareConfiguration());
+
+ // 1. create
+ final Menu menu = new Menu(getShell(), SWT.POP_UP);
+
+ // add default
+ String label = CompareMessages.CompareContentViewerSwitchingPane_defaultViewer;
+ MenuItem defaultItem = new MenuItem(menu, SWT.RADIO);
+ defaultItem.setText(label);
+ defaultItem.addSelectionListener(createSelectionListener(null));
+ defaultItem.setSelection(fSelectedViewerDescriptor == null);
+
+ new MenuItem(menu, SWT.SEPARATOR);
+
+ // add others
+ for (int i = 0; i < vd.length; i++) {
+ final ViewerDescriptor vdi = vd[i];
+ label = vdi.getLabel();
+ if (label == null || label.equals("")) { //$NON-NLS-1$
+ String l = CompareUIPlugin.getDefault().findContentTypeNameOrType((ICompareInput) getInput(), vdi, getCompareConfiguration());
+ if (l == null)
+ // couldn't figure out the label, skip the viewer
+ continue;
+ label = NLS.bind(CompareMessages.CompareContentViewerSwitchingPane_discoveredLabel, new Object[] {l});
+ }
+ MenuItem item = new MenuItem(menu, SWT.RADIO);
+ item.setText(label);
+ item.addSelectionListener(createSelectionListener(vdi));
+ item.setSelection(vdi == fSelectedViewerDescriptor);
+ }
+
+ // 2. show
+ Rectangle bounds = toolBar.getItem(0).getBounds();
+ Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
+ topLeft = toolBar.toDisplay(topLeft);
+ menu.setLocation(topLeft.x, topLeft.y);
+ menu.setVisible(true);
+
+ // 3. dispose on close
+ menu.addMenuListener(new MenuAdapter() {
+ public void menuHidden(MenuEvent e) {
+ menuShowing= false;
+ e.display.asyncExec(new Runnable() {
+ public void run() {
+ menu.dispose();
+ }
+ });
+ }
+ });
+ }
+
+ private SelectionListener createSelectionListener(final ViewerDescriptor vd) {
+ return new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ MenuItem mi = (MenuItem) e.widget;
+ if (mi.getSelection()) {
+ Viewer oldViewer = getViewer();
+ fSelectedViewerDescriptor = vd;
+ CompareContentViewerSwitchingPane.this.setInput(oldViewer
+ .getInput());
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ // nothing to do
+ }
+ };
+ }
+
+ public void setText(String label) {
+ Composite c = (Composite) getTopLeft();
+ Control[] children = c.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (children[i] instanceof CLabel) {
+ CLabel cl = (CLabel) children[i];
+ if (cl != null && !cl.isDisposed()) {
+ cl.setText(label);
+ c.layout();
+ }
+ return;
+ }
+ }
+ }
+
+ public void setImage(Image image) {
+ Composite c = (Composite) getTopLeft();
+ Control[] children = c.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (children[i] instanceof CLabel) {
+ CLabel cl = (CLabel) children[i];
+ if (cl != null && !cl.isDisposed())
+ cl.setImage(image);
+ return;
+ }
+ }
+ }
+
+ public void addMouseListener(MouseListener listener) {
+ Composite c = (Composite) getTopLeft();
+ Control[] children = c.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (children[i] instanceof CLabel) {
+ CLabel cl = (CLabel) children[i];
+ cl.addMouseListener(listener);
+ }
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java
new file mode 100644
index 000000000..7a7fcd0ac
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java
@@ -0,0 +1,286 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.InvocationTargetException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * This is a dialog that can host a {@link CompareEditorInput}.
+ * <p>
+ * This class can be used as is or can be subclassed.
+ *
+ * @since 3.3
+ */
+public class CompareDialog extends TrayDialog implements IPropertyChangeListener {
+
+ private final CompareEditorInput fCompareEditorInput;
+ private Button fCommitButton;
+ private Label statusLabel;
+ boolean hasSettings = true;
+ private final DialogCompareContainer fContainer = new DialogCompareContainer();
+
+ private class DialogCompareContainer extends CompareContainer {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.operation.IRunnableContext#run(boolean, boolean, org.eclipse.jface.operation.IRunnableWithProgress)
+ */
+ public void run(boolean fork, boolean cancelable,
+ IRunnableWithProgress runnable) throws InvocationTargetException,
+ InterruptedException {
+ ProgressMonitorDialog dialog = new ProgressMonitorDialog(getShell());
+ dialog.run(fork, cancelable, runnable);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#setStatusMessage(java.lang.String)
+ */
+ public void setStatusMessage(String message) {
+ if (statusLabel != null && !statusLabel.isDisposed()) {
+ if (message == null) {
+ statusLabel.setText(""); //$NON-NLS-1$
+ } else {
+ statusLabel.setText(message);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a dialog to host the given input.
+ * @param shell a shell
+ * @param input the dialog input
+ */
+ public CompareDialog(Shell shell, CompareEditorInput input) {
+ super(shell);
+ setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
+ Assert.isNotNull(input);
+ fCompareEditorInput= input;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.internal.ResizableDialog#close()
+ */
+ public boolean close() {
+ if (super.close()) {
+ if (fCompareEditorInput != null)
+ fCompareEditorInput.removePropertyChangeListener(this);
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+ fCommitButton= createButton(parent, IDialogConstants.OK_ID, getOKButtonLabel(), true);
+ fCommitButton.setEnabled(isOKEnabled());
+ if (isCancelable()) {
+ createButton(parent, IDialogConstants.CANCEL_ID, getCancelButtonLabel(), false);
+ }
+ }
+
+ private String getCancelButtonLabel() {
+ return fCompareEditorInput.getCancelButtonLabel();
+ }
+
+ private boolean isCancelable() {
+ return isInputEditable() || isElementSelectionDialog();
+ }
+
+ private String getOKButtonLabel() {
+ return fCompareEditorInput.getOKButtonLabel();
+ }
+
+ private boolean isElementSelectionDialog() {
+ return fCompareEditorInput.isEditionSelectionDialog();
+ }
+
+ /**
+ * Return whether the compare editor input of this dialog is editable.
+ * By default, the input is editable if the compare configuration
+ * indicates that either the left or right sides are editable.
+ * Subclasses may override.
+ * @return whether the compare editor input of this dialog is editable
+ * @see CompareConfiguration#isLeftEditable()
+ * @see CompareConfiguration#isRightEditable()
+ */
+ protected boolean isInputEditable() {
+ return fCompareEditorInput.getCompareConfiguration().isLeftEditable()
+ || fCompareEditorInput.getCompareConfiguration().isRightEditable();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(CompareEditorInput.DIRTY_STATE)
+ || event.getProperty().equals(CompareEditorInput.PROP_SELECTED_EDITION)) {
+ if (fCommitButton != null && fCompareEditorInput != null)
+ fCommitButton.setEnabled(isOKEnabled());
+ } else if (event.getProperty().equals(CompareEditorInput.PROP_TITLE)) {
+ getShell().setText(fCompareEditorInput.getTitle());
+ } else if (event.getProperty().equals(CompareEditorInput.PROP_TITLE_IMAGE)) {
+ getShell().setImage(fCompareEditorInput.getTitleImage());
+ }
+ }
+
+ private boolean isOKEnabled() {
+ if (isInputEditable())
+ return fCompareEditorInput.isDirty();
+ if (isElementSelectionDialog())
+ return getSelectedElement() != null;
+ return true;
+ }
+
+ private Object getSelectedElement() {
+ return fCompareEditorInput.getSelectedEdition();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ protected Control createDialogArea(Composite parent2) {
+
+ Composite parent= (Composite) super.createDialogArea(parent2);
+
+ Control c= fCompareEditorInput.createContents(parent);
+ c.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ statusLabel = new Label(parent, SWT.NONE);
+ statusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ Shell shell= c.getShell();
+ shell.setText(fCompareEditorInput.getTitle());
+ shell.setImage(fCompareEditorInput.getTitleImage());
+ applyDialogFont(parent);
+ return parent;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.window.Window#open()
+ */
+ public int open() {
+ // Before opening, set the container of the input and listen
+ // for changes to the input
+ fCompareEditorInput.addPropertyChangeListener(this);
+ fCompareEditorInput.setContainer(fContainer);
+ return super.open();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
+ */
+ protected void buttonPressed(int buttonId) {
+ if (buttonId == OK) {
+ if (!fCompareEditorInput.okPressed())
+ return;
+ } else if (buttonId == CANCEL) {
+ fCompareEditorInput.cancelPressed();
+ }
+ super.buttonPressed(buttonId);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#getDialogBoundsSettings()
+ */
+ protected IDialogSettings getDialogBoundsSettings() {
+ IDialogSettings compareSettings = CompareUIPlugin.getDefault().getDialogSettings();
+ String sectionName = this.getClass().getName();
+ IDialogSettings dialogSettings = compareSettings.getSection(sectionName);
+ if (dialogSettings == null) {
+ hasSettings = false;
+ dialogSettings = compareSettings.addNewSection(sectionName);
+ }
+ return dialogSettings;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.internal.ResizableDialog#configureShell(org.eclipse.swt.widgets.Shell)
+ */
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ if (getHelpContextId() != null)
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell, getHelpContextId());
+ }
+
+ /**
+ * Return the help content id for this dialog or <code>null</code>.
+ * By default, a generic help content id is returned. Subclasses may
+ * override.
+ * @return the help content id for this dialog or <code>null</code>
+ */
+ public String getHelpContextId() {
+ return ICompareContextIds.COMPARE_DIALOG;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#getInitialSize()
+ */
+ protected Point getInitialSize() {
+ Point initialSize = super.getInitialSize();
+ if (hasSettings) {
+ return initialSize;
+ }
+ return getDefaultSize();
+ }
+
+ /**
+ * If we don't have settings we need to come up with a reasonable default
+ * since we can't depend on the compare editor input layout returning a
+ * good default size.
+ * @return the default size of the dialog
+ */
+ protected Point getDefaultSize() {
+ int width= 0;
+ int height= 0;
+ Shell shell= getParentShell();
+ if (shell != null) {
+ Point parentSize= shell.getSize();
+ width= parentSize.x-100;
+ height= parentSize.y-100;
+ }
+ if (width < 700)
+ width= 700;
+ if (height < 500)
+ height= 500;
+ return new Point(width, height);
+ }
+
+ /**
+ * Return the compare editor input for this dialog.
+ * @return the compare editor input for this dialog
+ */
+ protected final CompareEditorInput getInput() {
+ return fCompareEditorInput;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java
new file mode 100644
index 000000000..f3fbc5261
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java
@@ -0,0 +1,771 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.IPropertyChangeNotifier;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IFindReplaceTarget;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IReusableEditor;
+import org.eclipse.ui.ISaveablesLifecycleListener;
+import org.eclipse.ui.ISaveablesSource;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartConstants;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.Saveable;
+import org.eclipse.ui.SaveablesLifecycleEvent;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.contexts.IContextService;
+import org.eclipse.ui.part.EditorPart;
+import org.eclipse.ui.part.IShowInSource;
+import org.eclipse.ui.part.PageBook;
+import org.eclipse.ui.services.IServiceLocator;
+import org.eclipse.ui.texteditor.ITextEditorExtension3;
+import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+
+/**
+ * A CompareEditor takes a ICompareEditorInput as input.
+ * Most functionality is delegated to the ICompareEditorInput.
+ */
+public class CompareEditor extends EditorPart implements IReusableEditor, ISaveablesSource, IPropertyChangeListener, ISaveablesLifecycleListener {
+
+ public final static String CONFIRM_SAVE_PROPERTY= "org.eclipse.compare.internal.CONFIRM_SAVE_PROPERTY"; //$NON-NLS-1$
+
+ private static final int UNINITIALIZED = 0;
+ private static final int INITIALIZING = 1;
+ private static final int NO_DIFF = 2;
+ private static final int CANCELED = 3;
+ private static final int INITIALIZED = 4;
+ private static final int ERROR = 5;
+ private static final int STILL_INITIALIZING = 6;
+ private static final int CREATING_CONTROL = 7;
+ private static final int DONE = 8;
+
+ private IActionBars fActionBars;
+
+ private PageBook fPageBook;
+
+ /** the SWT control from the compare editor input*/
+ private Control fControl;
+ /** the outline page */
+ private CompareOutlinePage fOutlinePage;
+
+ private CompareSaveable fSaveable;
+
+ private Control initializingPage;
+ private Control emptyPage;
+
+ private int state = UNINITIALIZED;
+ private HashSet knownSaveables;
+
+ private final EditorCompareContainer fContainer = new EditorCompareContainer();
+
+ private class EditorCompareContainer extends CompareContainer {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#registerContextMenu(org.eclipse.jface.action.MenuManager, org.eclipse.jface.viewers.ISelectionProvider)
+ */
+ public void registerContextMenu(MenuManager menu, ISelectionProvider provider) {
+ if (getSite() instanceof IEditorSite) {
+ IEditorSite es = (IEditorSite) getSite();
+ es.registerContextMenu(menu, provider, true);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#setStatusMessage(java.lang.String)
+ */
+ public void setStatusMessage(String message) {
+ if (fActionBars != null) {
+ IStatusLineManager slm= fActionBars.getStatusLineManager();
+ if (slm != null) {
+ slm.setMessage(message);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareContainer#getServiceLocator()
+ */
+ public IServiceLocator getServiceLocator() {
+ return getSite();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.internal.CompareContainer#createWorkerJob()
+ */
+ protected WorkerJob createWorkerJob() {
+ WorkerJob workerJob = new WorkerJob(getWorkerJobName()) {
+ public boolean belongsTo(Object family) {
+ if (family == CompareEditor.this)
+ return true;
+ return super.belongsTo(family);
+ }
+ };
+ return workerJob;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.internal.CompareContainer#getWorkerJobName()
+ */
+ protected String getWorkerJobName() {
+ return NLS.bind(CompareMessages.CompareEditor_2, getTitle());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.internal.CompareContainer#getWorkbenchPart()
+ */
+ public IWorkbenchPart getWorkbenchPart() {
+ return CompareEditor.this;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.internal.CompareContainer#getActionBars()
+ */
+ public IActionBars getActionBars() {
+ return CompareEditor.this.getActionBars();
+ }
+ }
+
+ /**
+ * No-argument constructor required for extension points.
+ */
+ public CompareEditor() {
+ // empty default implementation
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IAdaptable
+ */
+ public Object getAdapter(Class key) {
+
+ if (key.equals(IContentOutlinePage.class)) {
+ Object object= getCompareConfiguration().getProperty(CompareConfiguration.USE_OUTLINE_VIEW);
+ if (object instanceof Boolean && ((Boolean)object).booleanValue()) {
+ if (fOutlinePage != null) {
+ if (fOutlinePage.getControl() != null && fOutlinePage.getControl().isDisposed()) {
+ fOutlinePage = null;
+ } else {
+ return fOutlinePage;
+ }
+ }
+ fOutlinePage= new CompareOutlinePage(this);
+ return fOutlinePage;
+ }
+ }
+
+ if (key == IShowInSource.class
+ || key == OutlineViewerCreator.class
+ || key == IFindReplaceTarget.class) {
+ Object input = getEditorInput();
+ if (input != null) {
+ return Utilities.getAdapter(input, key);
+ }
+ }
+
+ if (key == IEditorInput.class) {
+ return getEditorInput().getAdapter(IEditorInput.class);
+ }
+
+ if (key == ITextEditorExtension3.class) {
+ return getEditorInput().getAdapter(ITextEditorExtension3.class);
+ }
+
+ return super.getAdapter(key);
+ }
+
+ /*
+ * Helper method used by ComapreEditorConfiguration to get at the compare configuration of the editor
+ */
+ /* package */ CompareConfiguration getCompareConfiguration() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ return ((CompareEditorInput)input).getCompareConfiguration();
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
+ */
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+
+ if (!(input instanceof CompareEditorInput))
+ throw new PartInitException(Utilities.getString("CompareEditor.invalidInput")); //$NON-NLS-1$
+
+ setSite(site);
+ setInput(input);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#setInput(org.eclipse.ui.IEditorInput)
+ */
+ public void setInput(IEditorInput input) {
+ if (!(input instanceof CompareEditorInput)) {
+ IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, Utilities.getString("CompareEditor.invalidInput"), null); //$NON-NLS-1$
+ String title= Utilities.getString("CompareEditor.error.setinput.title"); //$NON-NLS-1$
+ String msg= Utilities.getString("CompareEditor.error.setinput.message"); //$NON-NLS-1$
+ ErrorDialog.openError(getSite().getShell(), title, msg, s);
+ return;
+ }
+ doSetInput(input);
+ // Need to refresh the contributor (see #67888)
+ refreshActionBarsContributor();
+ }
+
+ public void refreshActionBarsContributor() {
+ IEditorSite editorSite= getEditorSite();
+ if (editorSite != null) {
+ IEditorActionBarContributor actionBarContributor= editorSite.getActionBarContributor();
+ if (actionBarContributor != null) {
+ actionBarContributor.setActiveEditor(null);
+ actionBarContributor.setActiveEditor(this);
+ }
+ }
+ }
+
+ private void doSetInput(IEditorInput input) {
+ IEditorInput oldInput= getEditorInput();
+ disconnectFromInput(oldInput);
+ Point oldSize = null;
+ boolean hadPreviousInput = oldInput != null;
+ if (hadPreviousInput) {
+ // Cancel any jobs associated with the old input
+ Job.getJobManager().cancel(this);
+ if (fControl != null && !fControl.isDisposed()) {
+ oldSize= fControl.getSize();
+ if (emptyPage == null)
+ emptyPage = new Composite(fPageBook, SWT.NONE);
+ fPageBook.showPage(emptyPage);
+ fControl.dispose();
+ fControl = null;
+ }
+ }
+
+ super.setInput(input);
+
+ if (fOutlinePage != null)
+ fOutlinePage.reset();
+
+ final CompareEditorInput cei= (CompareEditorInput) input;
+ cei.setContainer(fContainer);
+ setTitleImage(cei.getTitleImage());
+ setPartName(cei.getTitle());
+ setTitleToolTip(cei.getToolTipText());
+
+ if (input instanceof IPropertyChangeNotifier)
+ ((IPropertyChangeNotifier)input).addPropertyChangeListener(this);
+
+ setState(cei.getCompareResult() == null ? INITIALIZING : INITIALIZED);
+ if (fPageBook != null)
+ createCompareControl();
+ if (fControl != null && oldSize != null)
+ fControl.setSize(oldSize);
+
+ boolean hasResult = cei.getCompareResult() != null;
+ if (!hasResult) {
+ initializeInBackground(cei, hadPreviousInput);
+ }
+
+ firePropertyChange(IWorkbenchPartConstants.PROP_INPUT);
+
+ // We only need to notify of new Saveables if we are changing inputs
+ if (hadPreviousInput && hasResult) {
+ registerSaveable();
+ }
+ }
+
+ private void registerSaveable() {
+ ISaveablesLifecycleListener lifecycleListener= (ISaveablesLifecycleListener) getSite().getService(ISaveablesLifecycleListener.class);
+ lifecycleListener.handleLifecycleEvent(
+ new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_OPEN, internalGetSaveables(true), false));
+ }
+
+ private void disconnectFromInput(IEditorInput oldInput) {
+ if (oldInput != null) {
+
+ if (oldInput instanceof IPropertyChangeNotifier)
+ ((IPropertyChangeNotifier)oldInput).removePropertyChangeListener(this);
+
+ // Let the workbench know that the old input's saveables are no longer needed
+ if (knownSaveables != null && !knownSaveables.isEmpty()) {
+ ISaveablesLifecycleListener lifecycleListener= (ISaveablesLifecycleListener) getSite().getService(ISaveablesLifecycleListener.class);
+ lifecycleListener.handleLifecycleEvent(
+ new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_CLOSE, (Saveable[]) knownSaveables.toArray(new Saveable[knownSaveables.size()]), false));
+ knownSaveables.clear();
+ }
+ }
+ }
+
+ protected void initializeInBackground(final CompareEditorInput cei, final boolean hadPreviousInput) {
+ // Need to cancel any running jobs associated with the oldInput
+ Job job = new Job(NLS.bind(CompareMessages.CompareEditor_0, cei.getTitle())) {
+ protected IStatus run(final IProgressMonitor monitor) {
+ final int[] newState = new int[] { ERROR };
+ try {
+ IStatus status = CompareUIPlugin.getDefault().prepareInput(cei, monitor);
+ if (status.isOK()) {
+ // We need to update the saveables list
+ newState[0] = INITIALIZED;
+ return Status.OK_STATUS;
+ }
+ if (status.getCode() == CompareUIPlugin.NO_DIFFERENCE) {
+ newState[0] = NO_DIFF;
+ return Status.OK_STATUS;
+ }
+ newState[0] = ERROR;
+ return status;
+ } catch (OperationCanceledException e) {
+ newState[0] = CANCELED;
+ return Status.CANCEL_STATUS;
+ } finally {
+ if (monitor.isCanceled())
+ newState[0] = CANCELED;
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ if (fPageBook.isDisposed())
+ return;
+ // we need to register the saveable if we had a previous input or if
+ // there are knownSaveables (which means that the workbench called
+ // getSaveables and got an empty list
+ if (hadPreviousInput || (knownSaveables != null && !isAllSaveablesKnown())) {
+ registerSaveable();
+ }
+ setState(newState[0]);
+ createCompareControl();
+ }
+ });
+ monitor.done();
+ }
+ }
+ public boolean belongsTo(Object family) {
+ if (family == CompareEditor.this || family == cei)
+ return true;
+ return cei.belongsTo(family);
+ }
+ };
+ job.setUser(true);
+ Utilities.schedule(job, getSite());
+ }
+
+ /*
+ * Helper method used to find an action bars using the Utilities#findActionsBars(Control)
+ */
+ public IActionBars getActionBars() {
+ return fActionBars;
+ }
+
+ /*
+ * Set the action bars so the Utilities class can access it.
+ */
+ /* package */ void setActionBars(IActionBars actionBars) {
+ fActionBars= actionBars;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createPartControl(Composite parent) {
+ parent.setData(this);
+ fPageBook = new PageBook(parent, SWT.NONE);
+ createCompareControl();
+ IContextService service = (IContextService)getSite().getService(IContextService.class);
+ if (service != null) {
+ service.activateContext("org.eclipse.compare.compareEditorScope"); //$NON-NLS-1$
+ service.activateContext("org.eclipse.ui.textEditorScope"); //$NON-NLS-1$
+ }
+ }
+
+ private void createCompareControl() {
+ if (fPageBook.isDisposed())
+ return;
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput) {
+ CompareEditorInput ci = (CompareEditorInput) input;
+ if (ci.getCompareResult() == null) {
+ if (getState() == INITIALIZING) {
+ getSite().setSelectionProvider(new CompareEditorSelectionProvider());
+ setPageLater();
+ } else if (getState() == STILL_INITIALIZING) {
+ if (initializingPage == null) {
+ initializingPage = getInitializingMessagePane(fPageBook);
+ }
+ fPageBook.showPage(initializingPage);
+ } else if (getState() == CANCELED) {
+ // Close the editor when we are canceled
+ closeEditor();
+ } else if (getState() == NO_DIFF) {
+ // Prompt and close the editor as well
+ setState(DONE);
+ closeEditor();
+ CompareUIPlugin.getDefault().handleNoDifference();
+ } else if (getState() == ERROR) {
+ // If an error occurred, close the editor
+ // (the message would be displayed by the progress view)
+ closeEditor();
+ }
+ } else if (fControl == null && getState() != CREATING_CONTROL) {
+ if (getState() == CANCELED) {
+ // Close the editor when we are canceled, even when compare
+ // result has been already prepared
+ closeEditor();
+ return;
+ }
+ // Set the state in case this method gets called again
+ setState(CREATING_CONTROL);
+ if (getSite().getSelectionProvider() == null)
+ getSite().setSelectionProvider(new CompareEditorSelectionProvider());
+ try {
+ fControl = ci.createContents(fPageBook);
+ } catch (SWTException e) {
+ // closed while creating
+ if (e.code == SWT.ERROR_WIDGET_DISPOSED) {
+ setState(CANCELED);
+ return;
+ }
+ }
+ fPageBook.showPage(fControl);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(fControl, ICompareContextIds.COMPARE_EDITOR);
+ if (isActive()) {
+ setFocus();
+ }
+ setState(INITIALIZED);
+ }
+ }
+ }
+
+ private boolean isActive() {
+ return getSite().getPage().getActivePart() == this;
+ }
+
+ private void setPageLater() {
+ Display.getCurrent().timerExec(1000, new Runnable() {
+ public void run() {
+ synchronized(CompareEditor.this) {
+ if (getState() == INITIALIZING) {
+ setState(STILL_INITIALIZING);
+ createCompareControl();
+ }
+ }
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#dispose()
+ */
+ public void dispose() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof IPropertyChangeNotifier)
+ ((IPropertyChangeNotifier)input).removePropertyChangeListener(this);
+ super.dispose();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
+ */
+ public void setFocus() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ if (!((CompareEditorInput)input).setFocus2())
+ fPageBook.setFocus();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
+ */
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * Always throws an AssertionFailedException.
+ * @see org.eclipse.ui.part.EditorPart#doSaveAs()
+ */
+ public void doSaveAs() {
+ Assert.isTrue(false); // Save As not supported for CompareEditor
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void doSave(IProgressMonitor progressMonitor) {
+
+ final IEditorInput input= getEditorInput();
+
+ WorkspaceModifyOperation operation= new WorkspaceModifyOperation() {
+ public void execute(IProgressMonitor pm) throws CoreException {
+ if (input instanceof CompareEditorInput)
+ ((CompareEditorInput)input).saveChanges(pm);
+ }
+ };
+
+ Shell shell= getSite().getShell();
+
+ try {
+
+ operation.run(progressMonitor);
+
+ firePropertyChange(PROP_DIRTY);
+
+ } catch (InterruptedException x) {
+ // NeedWork
+ } catch (OperationCanceledException x) {
+ // NeedWork
+ } catch (InvocationTargetException x) {
+ String title= Utilities.getString("CompareEditor.saveError.title"); //$NON-NLS-1$
+ String reason= x.getTargetException().getMessage();
+ MessageDialog.openError(shell, title, Utilities.getFormattedString("CompareEditor.cantSaveError", reason)); //$NON-NLS-1$
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#isDirty()
+ */
+ public boolean isDirty() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ return ((CompareEditorInput)input).isDirty();
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(CompareEditorInput.DIRTY_STATE)) {
+ Object old_value= event.getOldValue();
+ Object new_value= event.getNewValue();
+ if (old_value == null || new_value == null || !old_value.equals(new_value))
+ firePropertyChange(PROP_DIRTY);
+ } else if (event.getProperty().equals(CompareEditorInput.PROP_TITLE)) {
+ setPartName(((CompareEditorInput)getEditorInput()).getTitle());
+ setTitleToolTip(((CompareEditorInput)getEditorInput()).getToolTipText());
+ } else if (event.getProperty().equals(CompareEditorInput.PROP_TITLE_IMAGE)) {
+ setTitleImage(((CompareEditorInput)getEditorInput()).getTitleImage());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.ISaveablesSource#getModels()
+ */
+ public Saveable[] getSaveables() {
+ return internalGetSaveables(knownSaveables == null);
+ }
+
+ private Saveable[] internalGetSaveables(boolean init) {
+ IEditorInput input= getEditorInput();
+ Saveable[] sourceSaveables = getSaveables(input);
+ if (init || knownSaveables == null) {
+ recordSaveables(sourceSaveables);
+ } else {
+ for (int i = 0; i < sourceSaveables.length; i++) {
+ Saveable saveable = sourceSaveables[i];
+ if (!knownSaveables.contains(saveable)) {
+ CompareUIPlugin.logErrorMessage(NLS.bind("Saveable {0} was not added using a saveables lifecycle event.", saveable.getName())); //$NON-NLS-1$
+ knownSaveables.add(saveable);
+ }
+ }
+ if (sourceSaveables.length != knownSaveables.size()) {
+ CompareUIPlugin.logErrorMessage("Saveables were removed without an appropriate event"); //$NON-NLS-1$
+ knownSaveables.clear();
+ recordSaveables(sourceSaveables);
+ }
+ }
+ return sourceSaveables;
+ }
+
+ private boolean isAllSaveablesKnown() {
+ IEditorInput input= getEditorInput();
+ Saveable[] sourceSaveables = getSaveables(input);
+ if (knownSaveables == null) {
+ return sourceSaveables.length == 0;
+ }
+ if (sourceSaveables.length != knownSaveables.size()) {
+ return false;
+ }
+ for (int i = 0; i < sourceSaveables.length; i++) {
+ Saveable saveable = sourceSaveables[i];
+ if (!knownSaveables.contains(saveable)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void recordSaveables(Saveable[] sourceSaveables) {
+ if (knownSaveables == null)
+ knownSaveables = new HashSet();
+ for (int i = 0; i < sourceSaveables.length; i++) {
+ Saveable saveable = sourceSaveables[i];
+ knownSaveables.add(saveable);
+ }
+ }
+
+ private Saveable[] getSaveables(IEditorInput input) {
+ if (input instanceof ISaveablesSource) {
+ ISaveablesSource source= (ISaveablesSource) input;
+ return source.getSaveables();
+ }
+ return new Saveable[] { getSaveable() };
+ }
+
+ private Saveable getSaveable() {
+ if (fSaveable == null) {
+ fSaveable= new CompareSaveable();
+ }
+ return fSaveable;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.ISaveablesSource#getActiveModels()
+ */
+ public Saveable[] getActiveSaveables() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof ISaveablesSource) {
+ ISaveablesSource source= (ISaveablesSource) input;
+ return source.getActiveSaveables();
+ }
+ return new Saveable[] { getSaveable() };
+ }
+
+ private class CompareSaveable extends Saveable {
+
+ public String getName() {
+ return CompareEditor.this.getPartName();
+ }
+
+ public String getToolTipText() {
+ return CompareEditor.this.getTitleToolTip();
+ }
+
+ public ImageDescriptor getImageDescriptor() {
+ return ImageDescriptor.createFromImage(CompareEditor.this.getTitleImage());
+ }
+
+ public void doSave(IProgressMonitor monitor) throws CoreException {
+ CompareEditor.this.doSave(monitor);
+ }
+
+ public boolean isDirty() {
+ return CompareEditor.this.isDirty();
+ }
+
+ public boolean equals(Object object) {
+ return object == this;
+ }
+
+ public int hashCode() {
+ return CompareEditor.this.hashCode();
+ }
+ }
+
+ private Composite getInitializingMessagePane(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setBackground(getBackgroundColor(parent));
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 3;
+ composite.setLayout(layout);
+
+ createDescriptionLabel(composite, CompareMessages.CompareEditor_1);
+ return composite;
+ }
+
+ private Color getBackgroundColor(Composite parent) {
+ return parent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+ }
+
+ private Label createDescriptionLabel(Composite parent, String text) {
+ Label description = new Label(parent, SWT.WRAP);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.horizontalSpan = 2;
+ description.setLayoutData(data);
+ description.setText(text);
+ description.setBackground(getBackgroundColor(parent));
+ return description;
+ }
+
+ private void closeEditor() {
+ getSite().getPage().closeEditor(CompareEditor.this, false);
+ }
+
+ private synchronized void setState(int state) {
+ this.state = state;
+ }
+
+ private int getState() {
+ return state;
+ }
+
+ public void handleLifecycleEvent(SaveablesLifecycleEvent event) {
+ ISaveablesLifecycleListener lifecycleListener= (ISaveablesLifecycleListener) getSite().getService(ISaveablesLifecycleListener.class);
+ if (event.getEventType() == SaveablesLifecycleEvent.POST_CLOSE) {
+ // We may get a post close for a saveable that is not known to the workbench.
+ // Only pass on the event for known saveables
+ if (knownSaveables == null || knownSaveables.isEmpty())
+ return;
+ java.util.List result = new ArrayList();
+ Saveable[] all = event.getSaveables();
+ for (int i = 0; i < all.length; i++) {
+ Saveable saveable = all[i];
+ if (knownSaveables.contains(saveable))
+ result.add(saveable);
+ knownSaveables.remove(saveable);
+ }
+ if (result.isEmpty())
+ return;
+ event = new SaveablesLifecycleEvent(this,
+ SaveablesLifecycleEvent.POST_CLOSE,
+ (Saveable[]) result.toArray(new Saveable[result.size()]),
+ false);
+ } else if (event.getEventType() == SaveablesLifecycleEvent.POST_OPEN) {
+ recordSaveables(event.getSaveables());
+ }
+ lifecycleListener.handleLifecycleEvent(event);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java
new file mode 100644
index 000000000..fc427fe23
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.util.ResourceBundle;
+
+import org.eclipse.jface.action.*;
+
+import org.eclipse.ui.*;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.help.IWorkbenchHelpSystem;
+import org.eclipse.ui.part.EditorActionBarContributor;
+import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
+
+import org.eclipse.compare.*;
+
+
+public class CompareEditorContributor extends EditorActionBarContributor {
+
+ private IEditorPart fActiveEditorPart= null;
+
+ private ChangePropertyAction fIgnoreWhitespace;
+ private NavigationAction fNext;
+ private NavigationAction fPrevious;
+
+ private NavigationAction fToolbarNext;
+ private NavigationAction fToolbarPrevious;
+
+ public CompareEditorContributor() {
+ ResourceBundle bundle= CompareUI.getResourceBundle();
+
+ IWorkbenchHelpSystem helpSystem= PlatformUI.getWorkbench().getHelpSystem();
+
+ fIgnoreWhitespace= ChangePropertyAction.createIgnoreWhiteSpaceAction(bundle, null);
+ helpSystem.setHelp(fIgnoreWhitespace, ICompareContextIds.IGNORE_WHITESPACE_ACTION);
+
+ fNext= new NavigationAction(bundle, true);
+ helpSystem.setHelp(fNext, ICompareContextIds.GLOBAL_NEXT_DIFF_ACTION);
+
+ fPrevious= new NavigationAction(bundle, false);
+ helpSystem.setHelp(fPrevious, ICompareContextIds.GLOBAL_PREVIOUS_DIFF_ACTION);
+
+ fToolbarNext= new NavigationAction(bundle, true);
+ helpSystem.setHelp(fToolbarNext, ICompareContextIds.NEXT_DIFF_ACTION);
+
+ fToolbarPrevious= new NavigationAction(bundle, false);
+ helpSystem.setHelp(fToolbarPrevious, ICompareContextIds.PREVIOUS_DIFF_ACTION);
+ }
+
+ /*
+ * @see EditorActionBarContributor#contributeToToolBar(IToolBarManager)
+ */
+ public void contributeToToolBar(IToolBarManager tbm) {
+ tbm.add(new Separator());
+ tbm.add(fIgnoreWhitespace);
+ tbm.add(fToolbarNext);
+ tbm.add(fToolbarPrevious);
+ }
+
+ /*
+ * @see EditorActionBarContributor#contributeToMenu(IMenuManager)
+ */
+ public void contributeToMenu(IMenuManager menuManager) {
+ // empty implementation
+ }
+
+ public void setActiveEditor(IEditorPart targetEditor) {
+
+ if (fActiveEditorPart == targetEditor)
+ return;
+
+ fActiveEditorPart= targetEditor;
+
+ if (fActiveEditorPart != null) {
+ IEditorInput input= fActiveEditorPart.getEditorInput();
+ if (input instanceof CompareEditorInput) {
+ CompareEditorInput compareInput= (CompareEditorInput) input;
+ fNext.setCompareEditorInput(compareInput);
+ fPrevious.setCompareEditorInput(compareInput);
+ // Begin fix http://bugs.eclipse.org/bugs/show_bug.cgi?id=20105
+ fToolbarNext.setCompareEditorInput(compareInput);
+ fToolbarPrevious.setCompareEditorInput(compareInput);
+ // End fix http://bugs.eclipse.org/bugs/show_bug.cgi?id=20105
+ }
+ }
+
+ if (targetEditor instanceof CompareEditor) {
+ IActionBars actionBars= getActionBars();
+
+ CompareEditor editor= (CompareEditor) targetEditor;
+ editor.setActionBars(actionBars);
+
+ actionBars.setGlobalActionHandler(ActionFactory.NEXT.getId(), fNext);
+ actionBars.setGlobalActionHandler(ActionFactory.PREVIOUS.getId(), fPrevious);
+
+ actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_NEXT_ANNOTATION, fNext);
+ actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_PREVIOUS_ANNOTATION, fPrevious);
+
+ CompareConfiguration cc= editor.getCompareConfiguration();
+ fIgnoreWhitespace.setCompareConfiguration(cc);
+ } else {
+ IActionBars actionBars= getActionBars();
+ actionBars.setGlobalActionHandler(ActionFactory.NEXT.getId(), null);
+ actionBars.setGlobalActionHandler(ActionFactory.PREVIOUS.getId(), null);
+ actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_NEXT_ANNOTATION, null);
+ actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_PREVIOUS_ANNOTATION, null);
+ }
+ }
+
+ public void dispose() {
+ setActiveEditor(null);
+ super.dispose();
+ fIgnoreWhitespace.dispose();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorInputNavigator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorInputNavigator.java
new file mode 100644
index 000000000..9fc2ccf8b
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorInputNavigator.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.compare.*;
+
+/**
+ * Supports cross-pane navigation through the differences contained in a {@link CompareEditorInput}
+ * or a similar type of compare container.
+ * @see INavigatable
+ */
+public class CompareEditorInputNavigator extends CompareNavigator {
+
+ // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ private boolean fNextFirstTime= true;
+ private Object[] fPanes;
+
+ /**
+ * Create a navigator for navigating the given panes
+ * @param panes the panes to navigate.
+ */
+ public CompareEditorInputNavigator(Object[] panes) {
+ fPanes= panes;
+ }
+
+ /**
+ * Return the set of panes that this navigator is navigating.
+ * The {@link INavigatable} is obtain from each pane using the
+ * adaptable mechanism.
+ * @return the set of panes that this navigator is navigating
+ */
+ public Object[] getPanes() {
+ return fPanes;
+ }
+
+ protected INavigatable[] getNavigatables() {
+ List result = new ArrayList();
+ Object[] panes = getPanes();
+ for (int i = 0; i < panes.length; i++) {
+ Object pane = panes[i];
+ INavigatable navigator= getNavigator(pane);
+ if (navigator != null)
+ result.add(navigator);
+ }
+ return (INavigatable[]) result.toArray(new INavigatable[result.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ICompareNavigator#selectChange(boolean)
+ */
+ public boolean selectChange(boolean next) {
+ // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ if (next && fNextFirstTime && mustOpen()) {
+ fNextFirstTime= false;
+ if (openElement())
+ return false;
+ }
+ return super.selectChange(next);
+ }
+
+ /*
+ * Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ */
+ private boolean mustOpen() {
+ Object[] panes = getPanes();
+ if (panes == null || panes.length == 0)
+ return false;
+ for (int i= 1; i < panes.length; i++) {
+ Object pane= panes[i];
+ INavigatable nav = getNavigator(pane);
+ if (nav != null && nav.getInput() != null)
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ */
+ private boolean openElement() {
+ Object[] panes = getPanes();
+ if (panes == null || panes.length == 0)
+ return false;
+ INavigatable nav = getNavigator(panes[0]);
+ if (nav != null) {
+ if (!nav.openSelectedChange())
+ // selected change not opened, open first instead
+ nav.selectChange(INavigatable.FIRST_CHANGE);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorSelectionProvider.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorSelectionProvider.java
new file mode 100644
index 000000000..771688bcd
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorSelectionProvider.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.widgets.Widget;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.text.TextViewer;
+
+
+/**
+ * A selection provider for view parts with more that one viewer. Tracks the
+ * focus of the viewers to provide the correct selection.
+ *
+ * This is a modified version of
+ * org.eclipse.jdt.internal.ui.viewsupport.SelectionProviderMediator
+ */
+public class CompareEditorSelectionProvider implements IPostSelectionProvider {
+
+ private class InternalListener implements ISelectionChangedListener, FocusListener {
+ /*
+ * @see ISelectionChangedListener#selectionChanged
+ */
+ public void selectionChanged(SelectionChangedEvent event) {
+ doSelectionChanged(event);
+ }
+
+ /*
+ * @see FocusListener#focusGained
+ */
+ public void focusGained(FocusEvent e) {
+ // expecting a StyledText widget here
+ doFocusChanged(e.widget);
+ }
+
+ /*
+ * @see FocusListener#focusLost
+ */
+ public void focusLost(FocusEvent e) {
+ // do not reset due to focus behavior on GTK
+ //fViewerInFocus= null;
+ }
+ }
+
+ private class InternalPostSelectionListener implements ISelectionChangedListener {
+ public void selectionChanged(SelectionChangedEvent event) {
+ doPostSelectionChanged(event);
+ }
+
+ }
+
+ private TextViewer[] fViewers;
+
+ private TextViewer fViewerInFocus;
+ private ListenerList fSelectionChangedListeners;
+ private ListenerList fPostSelectionChangedListeners;
+
+ public CompareEditorSelectionProvider() {
+ fSelectionChangedListeners = new ListenerList();
+ fPostSelectionChangedListeners = new ListenerList();
+ // nothing more to do here, Compare Editor is initializing
+ }
+
+ /**
+ * @param viewers All viewers that can provide a selection
+ * @param viewerInFocus the viewer currently in focus or <code>null</code>
+ */
+ public void setViewers(TextViewer[] viewers, TextViewer viewerInFocus) {
+ Assert.isNotNull(viewers);
+ fViewers= viewers;
+ InternalListener listener= new InternalListener();
+ fViewerInFocus= viewerInFocus;
+
+ for (int i= 0; i < fViewers.length; i++) {
+ TextViewer viewer= fViewers[i];
+ viewer.addSelectionChangedListener(listener);
+ viewer.addPostSelectionChangedListener(new InternalPostSelectionListener());
+ StyledText textWidget = viewer.getTextWidget();
+ textWidget.addFocusListener(listener);
+ }
+ }
+
+ private void doFocusChanged(Widget control) {
+ for (int i= 0; i < fViewers.length; i++) {
+ if (fViewers[i].getTextWidget() == control) {
+ propagateFocusChanged(fViewers[i]);
+ return;
+ }
+ }
+ }
+
+ final void doPostSelectionChanged(SelectionChangedEvent event) {
+ ISelectionProvider provider= event.getSelectionProvider();
+ if (provider == fViewerInFocus) {
+ firePostSelectionChanged();
+ }
+ }
+
+ final void doSelectionChanged(SelectionChangedEvent event) {
+ ISelectionProvider provider= event.getSelectionProvider();
+ if (provider == fViewerInFocus) {
+ fireSelectionChanged();
+ }
+ }
+
+ final void propagateFocusChanged(TextViewer viewer) {
+ if (viewer != fViewerInFocus) { // OK to compare by identity
+ fViewerInFocus= viewer;
+ fireSelectionChanged();
+ firePostSelectionChanged();
+ }
+ }
+
+ private void fireSelectionChanged() {
+ if (fSelectionChangedListeners != null) {
+ SelectionChangedEvent event= new SelectionChangedEvent(this, getSelection());
+
+ Object[] listeners= fSelectionChangedListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++) {
+ ISelectionChangedListener listener= (ISelectionChangedListener) listeners[i];
+ listener.selectionChanged(event);
+ }
+ }
+ }
+
+ private void firePostSelectionChanged() {
+ if (fPostSelectionChangedListeners != null) {
+ SelectionChangedEvent event= new SelectionChangedEvent(this, getSelection());
+
+ Object[] listeners= fPostSelectionChangedListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++) {
+ ISelectionChangedListener listener= (ISelectionChangedListener) listeners[i];
+ listener.selectionChanged(event);
+ }
+ }
+ }
+
+ /*
+ * @see ISelectionProvider#addSelectionChangedListener
+ */
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ fSelectionChangedListeners.add(listener);
+ }
+
+ /*
+ * @see ISelectionProvider#removeSelectionChangedListener
+ */
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ fSelectionChangedListeners.remove(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
+ fPostSelectionChangedListeners.add(listener);
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
+ fPostSelectionChangedListeners.remove(listener);
+ }
+
+ /*
+ * @see ISelectionProvider#getSelection
+ */
+ public ISelection getSelection() {
+ if (fViewerInFocus != null) {
+ return fViewerInFocus.getSelection();
+ }
+ return TextSelection.emptySelection();
+ }
+
+ /*
+ * @see ISelectionProvider#setSelection
+ */
+ public void setSelection(ISelection selection) {
+ setSelection(selection, true);
+ }
+
+ public void setSelection(ISelection selection, boolean reveal) {
+ if (fViewerInFocus != null) {
+ if (reveal && !isSelectionInsideVisibleRegion(fViewerInFocus, selection))
+ resetVisibleRegion();
+ fViewerInFocus.setSelection(selection, reveal);
+ }
+ }
+
+ /**
+ * Resets the visible region for all text viewers of this selection provider.
+ *
+ * @since 3.6
+ */
+ private void resetVisibleRegion() {
+ if (fViewers == null)
+ return;
+
+ for (int i= 0; i < fViewers.length; i++)
+ fViewers[i].setVisibleRegion(0, fViewers[i].getDocument().getLength());
+ }
+
+ /**
+ * Tells whether the given selection is inside the text viewer's visible region.
+ *
+ * @param textViewer the text viewer
+ * @param selection the selection
+ * @return <code>true</code> if the selection is inside the text viewer's visible region
+ * @since 3.6
+ */
+ private static boolean isSelectionInsideVisibleRegion(TextViewer textViewer, ISelection selection) {
+ if (!(selection instanceof ITextSelection))
+ return false;
+
+ ITextSelection textSelection= (ITextSelection)selection;
+ IRegion visibleRegion= textViewer.getVisibleRegion();
+
+ return textSelection.getOffset() >= visibleRegion.getOffset() && textSelection.getOffset() + textSelection.getLength() <= visibleRegion.getOffset() + visibleRegion.getLength();
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java
new file mode 100644
index 000000000..3a17994be
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 com.ibm.icu.text.MessageFormat;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.IStatus;
+
+
+public class CompareFilter {
+ private static final char[][] NO_CHAR_CHAR= new char[0][];
+
+ private char[][] fExtraResourceFileFilters;
+ private String[] fExtraResourceFolderFilters;
+
+
+ public CompareFilter() {
+ // nothing to do
+ }
+
+ /*
+ * Returns true if path matches filter, that is if path should be filtered.
+ */
+ public boolean filter(String path0, boolean folder, boolean isArchive) {
+ if (!folder && fExtraResourceFileFilters != null) {
+ char[] name= path0.toCharArray();
+ for (int i= 0, l= fExtraResourceFileFilters.length; i < l; i++)
+ if (match(fExtraResourceFileFilters[i], name, true))
+ return true;
+ }
+ if (folder && fExtraResourceFolderFilters != null) {
+ for (int i= 0, l= fExtraResourceFolderFilters.length; i < l; i++)
+ if (fExtraResourceFolderFilters[i].equals(path0))
+ return true;
+ }
+ return false;
+ }
+
+ public static String validateResourceFilters(String text) {
+ IWorkspace workspace= ResourcesPlugin.getWorkspace();
+ String[] filters= getTokens(text, ","); //$NON-NLS-1$
+ for (int i= 0; i < filters.length; i++) {
+ String fileName= filters[i].replace('*', 'x');
+ int resourceType= IResource.FILE;
+ int lastCharacter= fileName.length() - 1;
+ if (lastCharacter >= 0 && fileName.charAt(lastCharacter) == '/') {
+ fileName= fileName.substring(0, lastCharacter);
+ resourceType= IResource.FOLDER;
+ }
+ IStatus status= workspace.validateName(fileName, resourceType);
+ if (status.matches(IStatus.ERROR)) {
+ String format= Utilities.getString("ComparePreferencePage.filter.invalidsegment.error"); //$NON-NLS-1$
+ return MessageFormat.format(format, new String[] { status.getMessage() } );
+ }
+ }
+ return null;
+ }
+
+ public void setFilters(String filterSequence) {
+ char[][] filters= filterSequence != null && filterSequence.length() > 0
+ ? splitAndTrimOn(',', filterSequence.toCharArray())
+ : null;
+ if (filters == null) {
+ fExtraResourceFileFilters= null;
+ fExtraResourceFolderFilters= null;
+ } else {
+ int fileCount= 0, folderCount= 0;
+ for (int i= 0, l= filters.length; i < l; i++) {
+ char[] f= filters[i];
+ if (f.length == 0)
+ continue;
+ if (f[f.length - 1] == '/')
+ folderCount++;
+ else
+ fileCount++;
+ }
+ fExtraResourceFileFilters= new char[fileCount][];
+ fExtraResourceFolderFilters= new String[folderCount];
+ for (int i= 0, l= filters.length; i < l; i++) {
+ char[] f= filters[i];
+ if (f.length == 0)
+ continue;
+ if (f[f.length - 1] == '/')
+ fExtraResourceFolderFilters[--folderCount]= new String(subarray(f, 0, f.length - 1));
+ else
+ fExtraResourceFileFilters[--fileCount]= f;
+ }
+ }
+ }
+
+ /////////
+
+ private static String[] getTokens(String text, String separator) {
+ StringTokenizer tok= new StringTokenizer(text, separator);
+ int nTokens= tok.countTokens();
+ String[] res= new String[nTokens];
+ for (int i= 0; i < res.length; i++)
+ res[i]= tok.nextToken().trim();
+ return res;
+ }
+
+ /**
+ * Answers true if the pattern matches the given name, false otherwise.
+ * This char[] pattern matching accepts wild-cards '*' and '?'.
+ *
+ * When not case sensitive, the pattern is assumed to already be
+ * lowercased, the name will be lowercased character per character as
+ * comparing. If name is null, the answer is false. If pattern is null, the
+ * answer is true if name is not null. <br><br>For example:
+ * <ol>
+ * <li>
+ *
+ * <pre>
+ * pattern = { '?', 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => true
+ * </pre>
+ *
+ *
+ * </li>
+ * <li>
+ *
+ * <pre>
+ * pattern = { '?', 'b', '?' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
+ * </pre>
+ *
+ *
+ * </li>
+ * <li>
+ *
+ * <pre>
+ * pattern = { 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
+ * </pre>
+ *
+ *
+ * </li>
+ * </ol>
+ *
+ * @param pattern
+ * the given pattern
+ * @param name
+ * the given name
+ * @param isCaseSensitive
+ * flag to know whether or not the matching should be case
+ * sensitive
+ * @return true if the pattern matches the given name, false otherwise
+ */
+ private boolean match(char[] pattern, char[] name, boolean isCaseSensitive) {
+ if (name == null)
+ return false; // null name cannot match
+ if (pattern == null)
+ return true; // null pattern is equivalent to '*'
+ return match(pattern, 0, pattern.length, name, 0, name.length, isCaseSensitive);
+ }
+
+ /**
+ * Answers true if the a sub-pattern matches the subpart of the given name,
+ * false otherwise. char[] pattern matching, accepting wild-cards '*' and
+ * '?'. Can match only subset of name/pattern. end positions are
+ * non-inclusive. The subpattern is defined by the patternStart and
+ * pattternEnd positions. When not case sensitive, the pattern is assumed
+ * to already be lowercased, the name will be lowercased character per
+ * character as comparing. <br><br>For example:
+ * <ol>
+ * <li>
+ *
+ * <pre>
+ * pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 3 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 4 isCaseSensitive = true result => true
+ * </pre>
+ *
+ *
+ * </li>
+ * <li>
+ *
+ * <pre>
+ * pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 2 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 2 isCaseSensitive = true result => false
+ * </pre>
+ *
+ *
+ * </li>
+ * </ol>
+ *
+ * @param pattern
+ * the given pattern
+ * @param patternStart
+ * the given pattern start
+ * @param patternEnd
+ * the given pattern end
+ * @param name
+ * the given name
+ * @param nameStart
+ * the given name start
+ * @param nameEnd
+ * the given name end
+ * @param isCaseSensitive
+ * flag to know if the matching should be case sensitive
+ * @return true if the a sub-pattern matches the subpart of the given name,
+ * false otherwise
+ */
+ private boolean match(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd,
+ boolean isCaseSensitive) {
+ if (name == null)
+ return false; // null name cannot match
+ if (pattern == null)
+ return true; // null pattern is equivalent to '*'
+ int iPattern= patternStart;
+ int iName= nameStart;
+ if (patternEnd < 0)
+ patternEnd= pattern.length;
+ if (nameEnd < 0)
+ nameEnd= name.length;
+ /* check first segment */
+ char patternChar= 0;
+ while ((iPattern < patternEnd) && (patternChar= pattern[iPattern]) != '*') {
+ if (iName == nameEnd)
+ return false;
+ if (patternChar != (isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) && patternChar != '?') {
+ return false;
+ }
+ iName++;
+ iPattern++;
+ }
+ /* check sequence of star+segment */
+ int segmentStart;
+ if (patternChar == '*') {
+ segmentStart= ++iPattern; // skip star
+ } else {
+ segmentStart= 0; // force iName check
+ }
+ int prefixStart= iName;
+ checkSegment : while (iName < nameEnd) {
+ if (iPattern == patternEnd) {
+ iPattern= segmentStart; // mismatch - restart current segment
+ iName= ++prefixStart;
+ continue checkSegment;
+ }
+ /* segment is ending */
+ if ((patternChar= pattern[iPattern]) == '*') {
+ segmentStart= ++iPattern; // skip start
+ if (segmentStart == patternEnd) {
+ return true;
+ }
+ prefixStart= iName;
+ continue checkSegment;
+ }
+ /* check current name character */
+ if ((isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) != patternChar && patternChar != '?') {
+ iPattern= segmentStart; // mismatch - restart current segment
+ iName= ++prefixStart;
+ continue checkSegment;
+ }
+ iName++;
+ iPattern++;
+ }
+ return (segmentStart == patternEnd) || (iName == nameEnd && iPattern == patternEnd)
+ || (iPattern == patternEnd - 1 && pattern[iPattern] == '*');
+ }
+
+ /**
+ * Return a new array which is the split of the given array using the given
+ * divider and triming each subarray to remove whitespaces equals to ' '.
+ * <br><br>For example:
+ * <ol>
+ * <li>
+ *
+ * <pre>
+ * divider = 'b' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
+ * </pre>
+ *
+ *
+ * </li>
+ * <li>
+ *
+ * <pre>
+ * divider = 'c' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
+ * </pre>
+ *
+ *
+ * </li>
+ * <li>
+ *
+ * <pre>
+ * divider = 'b' array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
+ * </pre>
+ *
+ *
+ * </li>
+ * <li>
+ *
+ * <pre>
+ * divider = 'c' array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
+ * </pre>
+ *
+ *
+ * </li>
+ * </ol>
+ *
+ * @param divider
+ * the given divider
+ * @param array
+ * the given array
+ * @return a new array which is the split of the given array using the
+ * given divider and triming each subarray to remove whitespaces
+ * equals to ' '
+ */
+ private char[][] splitAndTrimOn(char divider, char[] array) {
+ int length= array == null ? 0 : array.length;
+ if (length == 0)
+ return NO_CHAR_CHAR;
+ int wordCount= 1;
+ for (int i= 0; i < length; i++)
+ if (array[i] == divider)
+ wordCount++;
+ char[][] split= new char[wordCount][];
+ int last= 0, currentWord= 0;
+ for (int i= 0; i < length; i++) {
+ if (array[i] == divider) {
+ int start= last, end= i - 1;
+ while (start < i && array[start] == ' ')
+ start++;
+ while (end > start && array[end] == ' ')
+ end--;
+ split[currentWord]= new char[end - start + 1];
+ System.arraycopy(array, start, split[currentWord++], 0, end - start + 1);
+ last= i + 1;
+ }
+ }
+ int start= last, end= length - 1;
+ while (start < length && array[start] == ' ')
+ start++;
+ while (end > start && array[end] == ' ')
+ end--;
+ split[currentWord]= new char[end - start + 1];
+ System.arraycopy(array, start, split[currentWord++], 0, end - start + 1);
+ return split;
+ }
+
+ /**
+ * Answers a new array which is a copy of the given array starting at the
+ * given start and ending at the given end. The given start is inclusive
+ * and the given end is exclusive. Answers null if start is greater than
+ * end, if start is lower than 0 or if end is greater than the length of
+ * the given array. If end equals -1, it is converted to the array length.
+ * <br><br>For example:
+ * <ol>
+ * <li>
+ *
+ * <pre>
+ * array = { 'a' , 'b' } start = 0 end = 1 result => { 'a' }
+ * </pre>
+ *
+ *
+ * </li>
+ * <li>
+ *
+ * <pre>
+ * array = { 'a', 'b' } start = 0 end = -1 result => { 'a' , 'b' }
+ * </pre>
+ *
+ *
+ * </li>
+ * </ol>
+ *
+ * @param array
+ * the given array
+ * @param start
+ * the given starting index
+ * @param end
+ * the given ending index
+ * @return a new array which is a copy of the given array starting at the
+ * given start and ending at the given end
+ * @exception NullPointerException
+ * if the given array is null
+ */
+ private char[] subarray(char[] array, int start, int end) {
+ if (end == -1)
+ end= array.length;
+ if (start > end)
+ return null;
+ if (start < 0)
+ return null;
+ if (end > array.length)
+ return null;
+ char[] result= new char[end - start];
+ System.arraycopy(array, start, result, 0, end - start);
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareHandlerService.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareHandlerService.java
new file mode 100644
index 000000000..809e79d21
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareHandlerService.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.core.expressions.Expression;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.commands.ActionHandler;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.*;
+import org.eclipse.ui.handlers.IHandlerActivation;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.services.IServiceLocator;
+
+public class CompareHandlerService {
+
+ private final List fActivations = new ArrayList();
+ private final Expression fExpression;
+ private ICompareContainer fContainer;
+ private boolean fDisposed;
+ private List fPaneActivations = new ArrayList();
+ private IHandlerService fHandlerService;
+
+ public static CompareHandlerService createFor(ICompareContainer container, Shell shell) {
+ IServiceLocator serviceLocator = container.getServiceLocator();
+ if (serviceLocator != null) {
+ IHandlerService service = (IHandlerService)serviceLocator.getService(IHandlerService.class);
+ if (service != null)
+ return new CompareHandlerService(container, null);
+ }
+ if (container.getWorkbenchPart() == null && shell != null) {
+ // We're in a dialog so we can use an active shell expression
+ IHandlerService service = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class);
+ if (service != null) {
+ Expression e = new ActiveShellExpression(shell);
+ return new CompareHandlerService(container, e);
+ }
+ }
+ return new CompareHandlerService(null, null);
+ }
+
+ private CompareHandlerService(ICompareContainer container,
+ Expression expression) {
+ fContainer = container;
+ fExpression = expression;
+ initialize();
+ }
+
+ public void registerAction(IAction action, String commandId) {
+ IHandlerService handlerService = getHandlerService();
+ if (handlerService == null)
+ return;
+ action.setActionDefinitionId(commandId);
+ IHandlerActivation activation;
+ if (fExpression == null) {
+ activation = handlerService.activateHandler(commandId, new ActionHandler(action));
+ } else {
+ activation = handlerService.activateHandler(commandId, new ActionHandler(action), fExpression);
+ }
+ if (activation != null) {
+ fActivations .add(activation);
+ }
+ }
+
+ private IHandlerService getHandlerService() {
+ if (fDisposed)
+ return null;
+ return fHandlerService;
+ }
+
+ private void initialize() {
+ if (fHandlerService == null) {
+ IServiceLocator serviceLocator = fContainer.getServiceLocator();
+ if (serviceLocator != null) {
+ IHandlerService service = (IHandlerService)serviceLocator.getService(IHandlerService.class);
+ if (service != null)
+ fHandlerService = service;
+ }
+ if (fHandlerService == null && fContainer.getWorkbenchPart() == null && fExpression != null) {
+ // We're in a dialog so we can use an active shell expression
+ IHandlerService service = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class);
+ if (service != null) {
+ fHandlerService = service;
+ }
+ }
+ }
+ }
+
+ public void setGlobalActionHandler(String actionId, IAction actionHandler) {
+ IActionBars bars = getActionBars();
+ if (bars != null) {
+ bars.setGlobalActionHandler(actionId, actionHandler);
+ return;
+ } else if (fExpression != null && actionHandler != null && actionHandler.getActionDefinitionId() != null) {
+ IHandlerService service = getHandlerService();
+ if (service != null) {
+ IHandlerActivation activation = service.activateHandler(actionHandler.getActionDefinitionId(), new ActionHandler(actionHandler), fExpression);
+ fPaneActivations.add(activation);
+ return;
+ }
+ }
+ // Remove the action definition id since we won't get key bindings
+ if (actionHandler != null)
+ actionHandler.setActionDefinitionId(null);
+ }
+
+ private void updateActionBars() {
+ IActionBars bars = getActionBars();
+ if (bars != null)
+ bars.updateActionBars();
+ }
+
+ private void clearPaneActionHandlers() {
+ if (!fPaneActivations.isEmpty()) {
+ IHandlerService service = getHandlerService();
+ if (service != null) {
+ service.deactivateHandlers(fPaneActivations);
+ fPaneActivations.clear();
+ }
+ }
+ }
+
+ private IActionBars getActionBars() {
+ return fContainer.getActionBars();
+ }
+
+ public void dispose() {
+ clearPaneActionHandlers();
+ IHandlerService service = getHandlerService();
+ if (service == null)
+ return;
+ service.deactivateHandlers(fActivations);
+ fActivations.clear();
+ fDisposed = true;
+ }
+
+ public void updatePaneActionHandlers(Runnable runnable) {
+ clearPaneActionHandlers();
+ runnable.run();
+ updateActionBars();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.java
new file mode 100644
index 000000000..6be02f6ff
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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
+ * Matt McCutchen (hashproduct+eclipse@gmail.com) - Bug 35390 Three-way compare cannot select (mis-selects) )ancestor resource
+ * Aleksandra Wozniak (aleksandra.k.wozniak@gmail.com) - Bug 239959, Bug 73923
+ *******************************************************************************/
+package org.eclipse.compare.internal;
+
+import org.eclipse.osgi.util.NLS;
+
+public final class CompareMessages extends NLS {
+
+ private static final String BUNDLE_NAME = "org.eclipse.compare.internal.CompareMessages";//$NON-NLS-1$
+
+ private CompareMessages() {
+ // Do not instantiate
+ }
+
+ public static String CompareContainer_0;
+ public static String CompareDialog_commit_button;
+ public static String CompareDialog_error_message;
+ public static String CompareDialog_error_title;
+ public static String CompareEditor_0;
+ public static String CompareEditor_1;
+ public static String CompareEditor_2;
+ public static String DocumentMerger_0;
+ public static String DocumentMerger_1;
+ public static String DocumentMerger_2;
+ public static String DocumentMerger_3;
+ public static String CompareEditorInput_0;
+ public static String ComparePlugin_internal_error;
+ public static String ComparePreferencePage_0;
+ public static String ComparePreferencePage_1;
+ public static String ComparePreferencePage_2;
+ public static String ComparePreferencePage_3;
+ public static String ComparePreferencePage_4;
+ public static String CompareUIPlugin_0;
+ public static String CompareUIPlugin_1;
+ public static String ContentMergeViewer_resource_changed_description;
+ public static String ContentMergeViewer_resource_changed_title;
+ public static String ExceptionDialog_seeErrorLogMessage;
+ public static String CompareViewerSwitchingPane_Titleformat;
+ public static String NavigationEndDialog_0;
+ public static String NavigationEndDialog_1;
+ public static String ShowWhitespaceAction_0;
+ public static String StructureDiffViewer_0;
+ public static String StructureDiffViewer_1;
+ public static String StructureDiffViewer_2;
+ public static String StructureDiffViewer_3;
+ public static String StructureDiffViewer_NoStructuralDifferences;
+ public static String StructureDiffViewer_StructureError;
+ public static String TextMergeViewer_0;
+ public static String TextMergeViewer_1;
+ public static String TextMergeViewer_10;
+ public static String TextMergeViewer_11;
+ public static String TextMergeViewer_12;
+ public static String TextMergeViewer_13;
+ public static String TextMergeViewer_14;
+ public static String TextMergeViewer_15;
+ public static String TextMergeViewer_16;
+ public static String TextMergeViewer_17;
+ public static String TextMergeViewer_2;
+ public static String TextMergeViewer_3;
+ public static String TextMergeViewer_4;
+ public static String TextMergeViewer_5;
+ public static String TextMergeViewer_6;
+ public static String TextMergeViewer_7;
+ public static String TextMergeViewer_8;
+ public static String TextMergeViewer_9;
+ public static String TextMergeViewer_accessible_ancestor;
+ public static String TextMergeViewer_accessible_left;
+ public static String TextMergeViewer_accessible_right;
+ public static String TextMergeViewer_cursorPosition_format;
+ public static String TextMergeViewer_beforeLine_format;
+ public static String TextMergeViewer_range_format;
+ public static String TextMergeViewer_changeType_addition;
+ public static String TextMergeViewer_changeType_deletion;
+ public static String TextMergeViewer_changeType_change;
+ public static String TextMergeViewer_direction_outgoing;
+ public static String TextMergeViewer_direction_incoming;
+ public static String TextMergeViewer_direction_conflicting;
+ public static String TextMergeViewer_diffType_format;
+ public static String TextMergeViewer_diffDescription_noDiff_format;
+ public static String TextMergeViewer_diffDescription_diff_format;
+ public static String TextMergeViewer_statusLine_format;
+ public static String TextMergeViewer_atEnd_title;
+ public static String TextMergeViewer_atEnd_message;
+ public static String TextMergeViewer_atBeginning_title;
+ public static String TextMergeViewer_atBeginning_message;
+ public static String CompareNavigator_atEnd_title;
+ public static String CompareNavigator_atEnd_message;
+ public static String CompareNavigator_atBeginning_title;
+ public static String CompareNavigator_atBeginning_message;
+ public static String WorkerJob_0;
+ public static String SelectAncestorDialog_title;
+ public static String SelectAncestorDialog_message;
+ public static String SelectAncestorDialog_option;
+ public static String CompareWithOtherResourceDialog_ancestor;
+ public static String CompareWithOtherResourceDialog_rightPanel;
+ public static String CompareWithOtherResourceDialog_leftPanel;
+ public static String CompareWithOtherResourceDialog_dialogTitle;
+ public static String CompareWithOtherResourceDialog_dialogMessage;
+ public static String CompareWithOtherResourceDialog_error_not_comparable;
+ public static String CompareWithOtherResourceDialog_error_empty;
+ public static String CompareWithOtherResourceDialog_clear;
+ public static String CompareWithOtherResourceDialog_info;
+ public static String CompareWithOtherResourceDialog_externalFile_errorTitle;
+ public static String CompareWithOtherResourceDialog_externalFile_errorMessage;
+ public static String CompareWithOtherResourceDialog_externalFileMainButton;
+ public static String CompareWithOtherResourceDialog_externalFileRadioButton;
+ public static String CompareWithOtherResourceDialog_externalFolderMainButton;
+ public static String CompareWithOtherResourceDialog_externalFolderRadioButton;
+ public static String CompareWithOtherResourceDialog_workspaceMainButton;
+ public static String CompareWithOtherResourceDialog_workspaceRadioButton;
+ public static String CompareContentViewerSwitchingPane_defaultViewer;
+ public static String CompareContentViewerSwitchingPane_switchButtonTooltip;
+ public static String CompareContentViewerSwitchingPane_discoveredLabel;
+ public static String CompareContentViewerSwitchingPane_optimized;
+ public static String CompareContentViewerSwitchingPane_optimizedTooltip;
+ public static String CompareStructureViewerSwitchingPane_defaultViewer;
+ public static String CompareStructureViewerSwitchingPane_switchButtonTooltip;
+ public static String CompareStructureViewerSwitchingPane_discoveredLabel;
+
+ public static String ReaderCreator_fileIsNotAccessible;
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, CompareMessages.class);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.properties
new file mode 100644
index 000000000..451ac3bb6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareMessages.properties
@@ -0,0 +1,143 @@
+###############################################################################
+# Copyright (c) 2000, 2010 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
+# Matt McCutchen (hashproduct+eclipse@gmail.com) - Bug 35390 Three-way compare cannot select (mis-selects) )ancestor resource
+# Aleksandra Wozniak (aleksandra.k.wozniak@gmail.com) - Bug 239959, Bug 73923
+###############################################################################
+
+ComparePlugin_internal_error= Internal Error
+ExceptionDialog_seeErrorLogMessage= See error log for details.
+
+#
+# Title format for CompareViewerSwitchingPane
+#
+CompareViewerSwitchingPane_Titleformat= {0} ({1})
+
+#
+# Title message for StructureDiffViewer if no structural differences could be found
+#
+StructureDiffViewer_NoStructuralDifferences= No Structural Differences
+StructureDiffViewer_StructureError= Cannot Compare Structures
+StructureDiffViewer_0=Generating Structure Differences
+StructureDiffViewer_1=Computing Structure Differences
+StructureDiffViewer_2=Generating Structure Differences
+StructureDiffViewer_3=Refresh Canceled
+
+#
+# TextMergeViewer
+#
+TextMergeViewer_cursorPosition_format= {0} : {1}
+
+TextMergeViewer_beforeLine_format= before line {0}
+TextMergeViewer_range_format= {0} : {1}
+
+TextMergeViewer_changeType_addition= addition
+TextMergeViewer_changeType_deletion= deletion
+TextMergeViewer_changeType_change= change
+
+TextMergeViewer_direction_outgoing= outgoing
+TextMergeViewer_direction_incoming= incoming
+TextMergeViewer_direction_conflicting= conflicting
+
+TextMergeViewer_diffType_format= {0} {1}
+TextMergeViewer_accessible_left=Left: {0}
+
+TextMergeViewer_diffDescription_noDiff_format= no diff
+TextMergeViewer_diffDescription_diff_format= {0} #{1} (Left: {2}, Right: {3})
+TextMergeViewer_statusLine_format= Left: {0}, Right: {1}, {2}
+
+TextMergeViewer_atEnd_title= Go to Next Difference
+TextMergeViewer_0=End Reached
+TextMergeViewer_1=You have reached the end of the currently displayed element. What would you like to do?
+TextMergeViewer_2=&Go to the beginning of this element
+TextMergeViewer_3=&Display the next element
+TextMergeViewer_4=Beginning Reached
+TextMergeViewer_5=You have reached the beginning of the currently displayed element. What would you like to do?
+TextMergeViewer_6=&Go to the end of this element
+TextMergeViewer_7=&Display the previous element
+TextMergeViewer_8=End Reached
+TextMergeViewer_9=You have reached the last difference. Would you like go to the beginning of this element?
+TextMergeViewer_10=Beginning Reached
+TextMergeViewer_11=You have reached the first difference. Would you like to go to the end of this element?
+TextMergeViewer_12=Element is Read Only
+TextMergeViewer_13=The element being edited is read-only
+TextMergeViewer_14=Element is Read Only
+TextMergeViewer_15=The element being edited is read-only
+TextMergeViewer_16=Show &Line Numbers
+TextMergeViewer_17=Do &nothing
+TextMergeViewer_atEnd_message= End of document reached. Continue from beginning?
+
+TextMergeViewer_atBeginning_title= Go to Previous Difference
+TextMergeViewer_accessible_right=Right: {0}
+TextMergeViewer_atBeginning_message= Beginning of document reached. Continue from end?
+TextMergeViewer_accessible_ancestor=Ancestor: {0}
+
+CompareNavigator_atEnd_title= End Reached
+CompareDialog_commit_button=C&ommit
+CompareDialog_error_title=Error Saving Changes
+ComparePreferencePage_0=When the end/beginning is reached while navigating an element
+ComparePreferencePage_1=Pro&mpt
+ComparePreferencePage_2=G&o to the beginning/end of the element
+ComparePreferencePage_3=Disp&lay the next/previous element
+ComparePreferencePage_4=Do &nothing
+CompareDialog_error_message=The changes were not saved: {0}
+CompareNavigator_atEnd_message= You have reached the last difference.
+
+CompareNavigator_atBeginning_title= Beginning Reached
+CompareNavigator_atBeginning_message= You have reached the first difference.
+ContentMergeViewer_resource_changed_title=Resources Changed
+ContentMergeViewer_resource_changed_description=The resources being compared have changed outside the compare editor. Do you want to save your changes? Any unsaved changes will be discarded.
+NavigationEndDialog_0=Remember decision
+NavigationEndDialog_1=Navigation Options
+CompareUIPlugin_0=Opening Compare Editor
+CompareUIPlugin_1=Opening Compare Dialog
+CompareContainer_0=Updating Comparison State
+CompareEditor_0=Initializing Compare Editor for {0}
+CompareEditor_1=Initializing...
+CompareEditor_2=Update comparison {0}
+DocumentMerger_0=Computing Differences...
+DocumentMerger_1=Too many differences found
+DocumentMerger_2=Finding Differences...
+DocumentMerger_3=Too many differences found
+CompareEditorInput_0=&Select
+WorkerJob_0=Multiple errors occurred while processing compare editor events
+
+SelectAncestorDialog_title=Select Common Ancestor
+SelectAncestorDialog_message=Which resource would you like to use as the common ancestor in the three-way compare?
+SelectAncestorDialog_option=''{0}''
+ShowWhitespaceAction_0=Show &Whitespace Characters
+
+CompareWithOtherResourceDialog_ancestor=Ancestor
+CompareWithOtherResourceDialog_rightPanel=Right
+CompareWithOtherResourceDialog_leftPanel=Left
+CompareWithOtherResourceDialog_dialogTitle=Compare with Other Resource
+CompareWithOtherResourceDialog_dialogMessage=Select resources to compare
+CompareWithOtherResourceDialog_error_not_comparable=Selected resources are not comparable.
+CompareWithOtherResourceDialog_error_empty=Both left and right panel must contain a valid path.
+CompareWithOtherResourceDialog_clear=&Clear
+CompareWithOtherResourceDialog_info=Drag files from a view or between dialog's fields.
+CompareWithOtherResourceDialog_externalFile_errorTitle=Compare With Other Resource Error
+CompareWithOtherResourceDialog_externalFile_errorMessage=Cannot create a link to an external resource.
+CompareWithOtherResourceDialog_externalFileMainButton=Browse...
+CompareWithOtherResourceDialog_externalFileRadioButton=&External file
+CompareWithOtherResourceDialog_externalFolderMainButton=Browse...
+CompareWithOtherResourceDialog_externalFolderRadioButton=External folder
+CompareWithOtherResourceDialog_workspaceMainButton=&Browse...
+CompareWithOtherResourceDialog_workspaceRadioButton=&Workspace
+
+CompareContentViewerSwitchingPane_defaultViewer=Default Compare
+CompareContentViewerSwitchingPane_switchButtonTooltip=Switch Compare Viewer
+CompareContentViewerSwitchingPane_discoveredLabel={0} Compare
+CompareContentViewerSwitchingPane_optimized=Differences shown might not be optimal
+CompareContentViewerSwitchingPane_optimizedTooltip=To avoid long computation time a faster comparison algorithm has been used. As a result, the differences highlighted in the viewer may be larger than necessary.
+CompareStructureViewerSwitchingPane_defaultViewer=Default Structure Compare
+CompareStructureViewerSwitchingPane_switchButtonTooltip=Switch Structure Compare Viewer
+CompareStructureViewerSwitchingPane_discoveredLabel={0} Structure Compare
+
+ReaderCreator_fileIsNotAccessible=Cannot create a reader because the file is inaccessible.
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareOutlinePage.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareOutlinePage.java
new file mode 100644
index 000000000..0c7b8c27b
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareOutlinePage.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.part.IPageSite;
+import org.eclipse.ui.part.Page;
+import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+
+/**
+ */
+public class CompareOutlinePage extends Page implements IContentOutlinePage, IPropertyChangeListener {
+
+ private CompareEditor fCompareEditor;
+ private Control fControl;
+ private CompareViewerSwitchingPane fStructurePane;
+ private OutlineViewerCreator fCreator;
+
+ CompareOutlinePage(CompareEditor editor) {
+ fCompareEditor= editor;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent) {
+ final Splitter h= new Splitter(parent, SWT.HORIZONTAL);
+ fStructurePane= new CompareViewerSwitchingPane(h, SWT.BORDER | SWT.FLAT, true) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ if (input instanceof ICompareInput)
+ return findStructureViewer(oldViewer, (ICompareInput)input, this);
+ return null;
+ }
+ };
+ h.setVisible(fStructurePane, true);
+ fControl = h;
+ IPageSite site = getSite();
+ site.setSelectionProvider(fStructurePane);
+ h.layout();
+ reset();
+ }
+
+ private Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent) {
+ OutlineViewerCreator creator = getCreator();
+ if (creator != null)
+ return creator.findStructureViewer(oldViewer, input, parent, getCompareConfiguration());
+ return null;
+ }
+
+ private CompareConfiguration getCompareConfiguration() {
+ return fCompareEditor.getCompareConfiguration();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.IPage#getControl()
+ */
+ public Control getControl() {
+ return fControl;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.IPage#setFocus()
+ */
+ public void setFocus() {
+ if (fStructurePane != null)
+ fStructurePane.setFocus();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ if (fStructurePane != null)
+ fStructurePane.addSelectionChangedListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
+ */
+ public ISelection getSelection() {
+ if (fStructurePane != null)
+ return fStructurePane.getSelection();
+ return StructuredSelection.EMPTY;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ if (fStructurePane != null)
+ fStructurePane.removeSelectionChangedListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
+ */
+ public void setSelection(ISelection selection) {
+ if (fStructurePane != null)
+ fStructurePane.setSelection(selection);
+ }
+
+ private void setInput(Object input) {
+ if (fStructurePane != null) {
+ fStructurePane.setInput(input);
+ ((Splitter)fControl).layout();
+ }
+ }
+
+ public OutlineViewerCreator getCreator() {
+ if (fCreator == null) {
+ fCreator = (OutlineViewerCreator)Utilities.getAdapter(fCompareEditor, OutlineViewerCreator.class);
+ if (fCreator != null)
+ fCreator.addPropertyChangeListener(this);
+ }
+ return fCreator;
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(OutlineViewerCreator.PROP_INPUT)) {
+ fStructurePane.setInput(event.getNewValue());
+ ((Splitter)fControl).layout();
+ }
+ }
+
+ public void dispose() {
+ super.dispose();
+ if (fCreator != null)
+ fCreator.removePropertyChangeListener(this);
+ fCreator = null;
+ }
+
+ public void reset() {
+ if (fCreator != null)
+ fCreator.removePropertyChangeListener(this);
+ fCreator = null;
+ OutlineViewerCreator creator = getCreator();
+ if (creator != null)
+ setInput(creator.getInput());
+ else
+ setInput(null);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferenceInitializer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferenceInitializer.java
new file mode 100644
index 000000000..23162e562
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferenceInitializer.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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 org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+public class ComparePreferenceInitializer extends AbstractPreferenceInitializer {
+
+ public ComparePreferenceInitializer() {
+ // Nothing to do
+ }
+
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = CompareUIPlugin.getDefault().getPreferenceStore();
+ ComparePreferencePage.initDefaults(store);
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java
new file mode 100644
index 000000000..f4fe72b73
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java
@@ -0,0 +1,489 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+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.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+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.Label;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Text;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PreferenceLinkArea;
+import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.IEncodedStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+import org.eclipse.compare.internal.core.ComparePlugin;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+
+
+public class ComparePreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+
+ class FakeInput implements ITypedElement, IEncodedStreamContentAccessor {
+ static final String UTF_16= "UTF-16"; //$NON-NLS-1$
+ String fContent;
+
+ FakeInput(String name) {
+ fContent= loadPreviewContentFromFile(name);
+ }
+ public Image getImage() {
+ return null;
+ }
+ public String getName() {
+ return "no name"; //$NON-NLS-1$
+ }
+ public String getType() {
+ return "no type"; //$NON-NLS-1$
+ }
+ public InputStream getContents() {
+ return new ByteArrayInputStream(Utilities.getBytes(fContent, UTF_16));
+ }
+ public String getCharset() {
+ return UTF_16;
+ }
+ }
+
+ private static final String PREFIX= CompareUIPlugin.PLUGIN_ID + "."; //$NON-NLS-1$
+ public static final String OPEN_STRUCTURE_COMPARE= PREFIX + "OpenStructureCompare"; //$NON-NLS-1$
+ public static final String USE_OUTLINE_VIEW= PREFIX + "UseOutlineView"; //$NON-NLS-1$
+ public static final String SYNCHRONIZE_SCROLLING= PREFIX + "SynchronizeScrolling"; //$NON-NLS-1$
+ public static final String SHOW_PSEUDO_CONFLICTS= PREFIX + "ShowPseudoConflicts"; //$NON-NLS-1$
+ public static final String INITIALLY_SHOW_ANCESTOR_PANE= PREFIX + "InitiallyShowAncestorPane"; //$NON-NLS-1$
+ public static final String PREF_SAVE_ALL_EDITORS= PREFIX + "SaveAllEditors"; //$NON-NLS-1$
+ public static final String IGNORE_WHITESPACE= PREFIX + "IgnoreWhitespace"; //$NON-NLS-1$
+
+ //public static final String USE_SPLINES= PREFIX + "UseSplines"; //$NON-NLS-1$
+ public static final String USE_SINGLE_LINE= PREFIX + "UseSingleLine"; //$NON-NLS-1$
+ public static final String HIGHLIGHT_TOKEN_CHANGES= PREFIX + "HighlightTokenChanges"; //$NON-NLS-1$
+ //public static final String USE_RESOLVE_UI= PREFIX + "UseResolveUI"; //$NON-NLS-1$
+ public static final String CAPPING_DISABLED= PREFIX + "CappingDisable"; //$NON-NLS-1$
+ public static final String PATH_FILTER= PREFIX + "PathFilter"; //$NON-NLS-1$
+ public static final String ADDED_LINES_REGEX= PREFIX + "AddedLinesRegex"; //$NON-NLS-1$
+ public static final String REMOVED_LINES_REGEX= PREFIX + "RemovedLinesRegex"; //$NON-NLS-1$
+
+
+ private TextMergeViewer fPreviewViewer;
+ private IPropertyChangeListener fPreferenceChangeListener;
+ private CompareConfiguration fCompareConfiguration;
+ private OverlayPreferenceStore fOverlayStore;
+ private Map fCheckBoxes= new HashMap();
+ private Text fFilters;
+ private Text addedLinesRegex;
+ private Text removedLinesRegex;
+ private SelectionListener fCheckBoxListener;
+
+
+ public final OverlayPreferenceStore.OverlayKey[] fKeys= new OverlayPreferenceStore.OverlayKey[] {
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, OPEN_STRUCTURE_COMPARE),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, USE_OUTLINE_VIEW),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, SYNCHRONIZE_SCROLLING),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, SHOW_PSEUDO_CONFLICTS),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, INITIALLY_SHOW_ANCESTOR_PANE),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, IGNORE_WHITESPACE),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PREF_SAVE_ALL_EDITORS),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ADDED_LINES_REGEX),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, REMOVED_LINES_REGEX),
+ //new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, USE_SPLINES),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, USE_SINGLE_LINE),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, HIGHLIGHT_TOKEN_CHANGES),
+ //new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, USE_RESOLVE_UI),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, CAPPING_DISABLED),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, PATH_FILTER),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ICompareUIConstants.PREF_NAVIGATION_END_ACTION),
+ new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL),
+ };
+ private RadioGroupFieldEditor editor;
+
+
+ public static void initDefaults(IPreferenceStore store) {
+ store.setDefault(OPEN_STRUCTURE_COMPARE, true);
+ store.setDefault(USE_OUTLINE_VIEW, false);
+ store.setDefault(SYNCHRONIZE_SCROLLING, true);
+ store.setDefault(SHOW_PSEUDO_CONFLICTS, false);
+ store.setDefault(INITIALLY_SHOW_ANCESTOR_PANE, false);
+ store.setDefault(IGNORE_WHITESPACE, false);
+ store.setDefault(PREF_SAVE_ALL_EDITORS, false);
+ store.setDefault(ADDED_LINES_REGEX, ""); //$NON-NLS-1$
+ store.setDefault(REMOVED_LINES_REGEX, ""); //$NON-NLS-1$
+ //store.setDefault(USE_SPLINES, false);
+ store.setDefault(USE_SINGLE_LINE, true);
+ store.setDefault(HIGHLIGHT_TOKEN_CHANGES, true);
+ //store.setDefault(USE_RESOLVE_UI, false);
+ store.setDefault(CAPPING_DISABLED, false);
+ store.setDefault(PATH_FILTER, ""); //$NON-NLS-1$
+ store.setDefault(ICompareUIConstants.PREF_NAVIGATION_END_ACTION, ICompareUIConstants.PREF_VALUE_PROMPT);
+ store.setDefault(ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL, ICompareUIConstants.PREF_VALUE_LOOP);
+ }
+
+ public ComparePreferencePage() {
+
+ //setDescription(Utilities.getString("ComparePreferencePage.description")); //$NON-NLS-1$
+
+ setPreferenceStore(CompareUIPlugin.getDefault().getPreferenceStore());
+
+ fOverlayStore= new OverlayPreferenceStore(getPreferenceStore(), fKeys);
+ fPreferenceChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ String key= event.getProperty();
+ if (key.equals(INITIALLY_SHOW_ANCESTOR_PANE)) {
+ boolean b= fOverlayStore.getBoolean(INITIALLY_SHOW_ANCESTOR_PANE);
+ if (fCompareConfiguration != null) {
+ fCompareConfiguration.setProperty(INITIALLY_SHOW_ANCESTOR_PANE, new Boolean(b));
+ }
+ }
+ }
+ };
+ fOverlayStore.addPropertyChangeListener(fPreferenceChangeListener);
+ }
+
+ /*
+ * @see IWorkbenchPreferencePage#init()
+ */
+ public void init(IWorkbench workbench) {
+ // empty
+ }
+
+ /*
+ * @see PreferencePage#performOk()
+ */
+ public boolean performOk() {
+ fOverlayStore.setValue(ADDED_LINES_REGEX, addedLinesRegex.getText());
+ fOverlayStore.setValue(REMOVED_LINES_REGEX, removedLinesRegex.getText());
+
+ editor.store();
+ fOverlayStore.propagate();
+
+ ComparePlugin.getDefault().setCappingDisabled(
+ getPreferenceStore().getBoolean(
+ ComparePreferencePage.CAPPING_DISABLED));
+ return true;
+ }
+
+ /*
+ * @see PreferencePage#performDefaults()
+ */
+ protected void performDefaults() {
+
+ fOverlayStore.loadDefaults();
+ initializeFields();
+
+ super.performDefaults();
+ }
+
+ /*
+ * @see DialogPage#dispose()
+ */
+ public void dispose() {
+
+ if (fOverlayStore != null) {
+ if (fPreferenceChangeListener != null) {
+ fOverlayStore.removePropertyChangeListener(fPreferenceChangeListener);
+ fPreferenceChangeListener= null;
+ }
+ fOverlayStore.stop();
+ fOverlayStore= null;
+ }
+
+ super.dispose();
+ }
+
+ static public boolean getSaveAllEditors() {
+ IPreferenceStore store= CompareUIPlugin.getDefault().getPreferenceStore();
+ return store.getBoolean(PREF_SAVE_ALL_EDITORS);
+ }
+
+ static public void setSaveAllEditors(boolean value) {
+ IPreferenceStore store= CompareUIPlugin.getDefault().getPreferenceStore();
+ store.setValue(PREF_SAVE_ALL_EDITORS, value);
+ }
+
+ /*
+ * @see PreferencePage#createContents(Composite)
+ */
+ protected Control createContents(Composite parent) {
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, ICompareContextIds.COMPARE_PREFERENCE_PAGE);
+
+ fOverlayStore.load();
+ fOverlayStore.start();
+
+ TabFolder folder= new TabFolder(parent, SWT.NONE);
+ folder.setLayout(new TabFolderLayout());
+ folder.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ TabItem item= new TabItem(folder, SWT.NONE);
+ item.setText(Utilities.getString("ComparePreferencePage.generalTab.label")); //$NON-NLS-1$
+ //item.setImage(JavaPluginImages.get(JavaPluginImages.IMG_OBJS_CFILE));
+ item.setControl(createGeneralPage(folder));
+
+ item= new TabItem(folder, SWT.NONE);
+ item.setText(Utilities.getString("ComparePreferencePage.textCompareTab.label")); //$NON-NLS-1$
+ //item.setImage(JavaPluginImages.get(JavaPluginImages.IMG_OBJS_CFILE));
+ item.setControl(createTextComparePage(folder));
+
+ initializeFields();
+ Dialog.applyDialogFont(folder);
+ return folder;
+ }
+
+ private Control createGeneralPage(Composite parent) {
+ Composite composite= new Composite(parent, SWT.NULL);
+ GridLayout layout= new GridLayout();
+ layout.numColumns= 1;
+ composite.setLayout(layout);
+
+ addCheckBox(composite, "ComparePreferencePage.structureCompare.label", OPEN_STRUCTURE_COMPARE, 0); //$NON-NLS-1$
+ addCheckBox(composite, "ComparePreferencePage.structureOutline.label", USE_OUTLINE_VIEW, 0); //$NON-NLS-1$
+ addCheckBox(composite, "ComparePreferencePage.ignoreWhitespace.label", IGNORE_WHITESPACE, 0); //$NON-NLS-1$
+
+ // a spacer
+ new Label(composite, SWT.NONE);
+
+ addCheckBox(composite, "ComparePreferencePage.saveBeforePatching.label", PREF_SAVE_ALL_EDITORS, 0); //$NON-NLS-1$
+
+ // a spacer
+ new Label(composite, SWT.NONE);
+
+ Label l= new Label(composite, SWT.WRAP);
+ l.setText(Utilities.getString("ComparePreferencePage.regex.description")); //$NON-NLS-1$
+
+ Composite c2= new Composite(composite, SWT.NONE);
+ c2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ layout= new GridLayout(2, false);
+ layout.marginWidth= 0;
+ c2.setLayout(layout);
+
+ l= new Label(c2, SWT.NONE);
+ l.setText(Utilities.getString("ComparePreferencePage.regexAdded.label")); //$NON-NLS-1$
+ addedLinesRegex = new Text(c2, SWT.BORDER);
+ addedLinesRegex.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ addedLinesRegex.setText(fOverlayStore.getString(ADDED_LINES_REGEX));
+
+ l= new Label(c2, SWT.NONE);
+ l.setText(Utilities.getString("ComparePreferencePage.regexRemoved.label")); //$NON-NLS-1$
+ removedLinesRegex = new Text(c2, SWT.BORDER);
+ removedLinesRegex.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ removedLinesRegex.setText(fOverlayStore.getString(REMOVED_LINES_REGEX));
+
+ // a spacer
+ new Label(composite, SWT.NONE);
+
+ l= new Label(composite, SWT.WRAP);
+ l.setText(Utilities.getString("ComparePreferencePage.filter.description")); //$NON-NLS-1$
+
+ Composite c3= new Composite(composite, SWT.NONE);
+ c3.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ layout= new GridLayout(2, false);
+ layout.marginWidth= 0;
+ c3.setLayout(layout);
+
+ l= new Label(c3, SWT.NONE);
+ l.setText(Utilities.getString("ComparePreferencePage.filter.label")); //$NON-NLS-1$
+
+ fFilters= new Text(c3, SWT.BORDER);
+ fFilters.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ fFilters.setText(fOverlayStore.getString(PATH_FILTER));
+ fFilters.addModifyListener(
+ new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ String filters= fFilters.getText();
+ String message= CompareFilter.validateResourceFilters(filters);
+ setValid(message == null);
+ setMessage(null);
+ setErrorMessage(message);
+ fOverlayStore.setValue(PATH_FILTER, filters);
+ }
+ }
+ );
+
+ return composite;
+ }
+
+ private Control createTextComparePage(Composite parent) {
+
+ Composite composite= new Composite(parent, SWT.NULL);
+ GridLayout layout= new GridLayout();
+ layout.numColumns= 1;
+ composite.setLayout(layout);
+
+ addCheckBox(composite, "ComparePreferencePage.synchronizeScrolling.label", SYNCHRONIZE_SCROLLING, 0); //$NON-NLS-1$
+ addCheckBox(composite, "ComparePreferencePage.initiallyShowAncestorPane.label", INITIALLY_SHOW_ANCESTOR_PANE, 0); //$NON-NLS-1$
+ addCheckBox(composite, "ComparePreferencePage.showPseudoConflicts.label", SHOW_PSEUDO_CONFLICTS, 0); //$NON-NLS-1$
+
+ //addCheckBox(composite, "ComparePreferencePage.useSplines.label", USE_SPLINES, 0); //$NON-NLS-1$
+ addCheckBox(composite, "ComparePreferencePage.useSingleLine.label", USE_SINGLE_LINE, 0); //$NON-NLS-1$
+ addCheckBox(composite, "ComparePreferencePage.highlightTokenChanges.label", HIGHLIGHT_TOKEN_CHANGES, 0); //$NON-NLS-1$
+ //addCheckBox(composite, "ComparePreferencePage.useResolveUI.label", USE_RESOLVE_UI, 0); //$NON-NLS-1$
+ addCheckBox(composite, "ComparePreferencePage.disableCapping.label", CAPPING_DISABLED, 0); //$NON-NLS-1$
+
+ Composite radioGroup = new Composite(composite, SWT.NULL);
+ radioGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ editor = new RadioGroupFieldEditor(ICompareUIConstants.PREF_NAVIGATION_END_ACTION, CompareMessages.ComparePreferencePage_0, 1,
+ new String[][] {
+ new String[] { CompareMessages.ComparePreferencePage_1, ICompareUIConstants.PREF_VALUE_PROMPT },
+ new String[] { CompareMessages.ComparePreferencePage_2, ICompareUIConstants.PREF_VALUE_LOOP },
+ new String[] { CompareMessages.ComparePreferencePage_3, ICompareUIConstants.PREF_VALUE_NEXT },
+ new String[] { CompareMessages.ComparePreferencePage_4, ICompareUIConstants.PREF_VALUE_DO_NOTHING}
+ },
+ radioGroup, true);
+ editor.setPreferenceStore(fOverlayStore);
+ editor.fillIntoGrid(radioGroup, 1);
+
+ // a spacer
+ Label separator= new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setVisible(false);
+
+ Label previewLabel= new Label(composite, SWT.NULL);
+ previewLabel.setText(Utilities.getString("ComparePreferencePage.preview.label")); //$NON-NLS-1$
+
+ Control previewer= createPreviewer(composite);
+ GridData gd= new GridData(GridData.FILL_BOTH);
+ gd.widthHint= convertWidthInCharsToPixels(60);
+ gd.heightHint= convertHeightInCharsToPixels(13);
+ previewer.setLayoutData(gd);
+
+ PreferenceLinkArea area = new PreferenceLinkArea(composite, SWT.NONE,
+ "org.eclipse.ui.preferencePages.ColorsAndFonts", Utilities.getString("ComparePreferencePage.colorAndFontLink"), //$NON-NLS-1$ //$NON-NLS-2$
+ (IWorkbenchPreferenceContainer)getContainer(), "selectCategory:org.eclipse.compare.contentmergeviewer.TextMergeViewer"); //$NON-NLS-1$
+
+ GridData data= new GridData(SWT.FILL, SWT.CENTER, false, false);
+ area.getControl().setLayoutData(data);
+
+ return composite;
+ }
+
+ private Control createPreviewer(Composite parent) {
+
+ fCompareConfiguration= new CompareConfiguration(fOverlayStore);
+ fCompareConfiguration.setAncestorLabel(Utilities.getString("ComparePreferencePage.ancestor.label")); //$NON-NLS-1$
+
+ fCompareConfiguration.setLeftLabel(Utilities.getString("ComparePreferencePage.left.label")); //$NON-NLS-1$
+ fCompareConfiguration.setLeftEditable(false);
+
+ fCompareConfiguration.setRightLabel(Utilities.getString("ComparePreferencePage.right.label")); //$NON-NLS-1$
+ fCompareConfiguration.setRightEditable(false);
+
+ fPreviewViewer= new TextMergeViewer(parent, SWT.BORDER, fCompareConfiguration);
+
+ fPreviewViewer.setInput(
+ new DiffNode(Differencer.CONFLICTING,
+ new FakeInput("ComparePreferencePage.previewAncestor"), //$NON-NLS-1$
+ new FakeInput("ComparePreferencePage.previewLeft"), //$NON-NLS-1$
+ new FakeInput("ComparePreferencePage.previewRight") //$NON-NLS-1$
+ )
+ );
+
+ Control c= fPreviewViewer.getControl();
+ c.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.dispose();
+ }
+ });
+
+ return c;
+ }
+
+ private void initializeFields() {
+
+ Iterator e= fCheckBoxes.keySet().iterator();
+ while (e.hasNext()) {
+ Button b= (Button) e.next();
+ String key= (String) fCheckBoxes.get(b);
+ b.setSelection(fOverlayStore.getBoolean(key));
+ }
+
+ if (fFilters != null)
+ fFilters.setText(fOverlayStore.getString(PATH_FILTER));
+ if (addedLinesRegex != null)
+ addedLinesRegex.setText(fOverlayStore.getString(ADDED_LINES_REGEX));
+ if (removedLinesRegex != null)
+ removedLinesRegex.setText(fOverlayStore.getString(REMOVED_LINES_REGEX));
+
+ editor.load();
+ }
+
+ // overlay stuff
+
+ private Button addCheckBox(Composite parent, String labelKey, String key, int indentation) {
+
+ String label= Utilities.getString(labelKey);
+
+ Button checkBox= new Button(parent, SWT.CHECK);
+ checkBox.setText(label);
+
+ GridData gd= new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalIndent= indentation;
+ gd.horizontalSpan= 2;
+ checkBox.setLayoutData(gd);
+
+ if (fCheckBoxListener == null) {
+ fCheckBoxListener= new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ Button button= (Button) e.widget;
+ fOverlayStore.setValue((String) fCheckBoxes.get(button), button.getSelection());
+ }
+ };
+ }
+ checkBox.addSelectionListener(fCheckBoxListener);
+
+ fCheckBoxes.put(checkBox, key);
+
+ return checkBox;
+ }
+
+ private String loadPreviewContentFromFile(String key) {
+
+ String preview= Utilities.getString(key);
+ String separator= System.getProperty("line.separator"); //$NON-NLS-1$
+ StringBuffer buffer= new StringBuffer();
+ for (int i= 0; i < preview.length(); i++) {
+ char c= preview.charAt(i);
+ if (c == '\n')
+ buffer.append(separator);
+ else
+ buffer.append(c);
+ }
+ return buffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareStructureViewerSwitchingPane.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareStructureViewerSwitchingPane.java
new file mode 100644
index 000000000..04bfdf3c5
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareStructureViewerSwitchingPane.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareViewerSwitchingPane;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.MenuAdapter;
+import org.eclipse.swt.events.MenuEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.ui.PlatformUI;
+
+public class CompareStructureViewerSwitchingPane extends
+ CompareViewerSwitchingPane {
+
+ private CompareEditorInput fCompareEditorInput;
+
+ private ViewerDescriptor fSelectedViewerDescriptor;
+
+ private ToolBar toolBar;
+
+ public CompareStructureViewerSwitchingPane(Composite parent, int style,
+ boolean visibility, CompareEditorInput cei) {
+ super(parent, style, visibility);
+ fCompareEditorInput = cei;
+ }
+
+ private CompareConfiguration getCompareConfiguration() {
+ return fCompareEditorInput.getCompareConfiguration();
+ }
+
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ if (input instanceof ICompareInput) {
+ if (fSelectedViewerDescriptor != null) {
+ ViewerDescriptor[] array = CompareUIPlugin.getDefault().findStructureViewerDescriptor(
+ oldViewer, (ICompareInput)input, getCompareConfiguration());
+ List list = array != null ? Arrays.asList(array)
+ : Collections.EMPTY_LIST;
+ if (list.contains(fSelectedViewerDescriptor)) {
+ // use selected viewer only when appropriate for the new input
+ fCompareEditorInput
+ .setStructureViewerDescriptor(fSelectedViewerDescriptor);
+ Viewer viewer = fCompareEditorInput.findStructureViewer(
+ oldViewer, (ICompareInput) input, this);
+ return viewer;
+ }
+ // fallback to default otherwise
+ fSelectedViewerDescriptor = null;
+ }
+
+ fCompareEditorInput.setStructureViewerDescriptor(null);
+ Viewer viewer = fCompareEditorInput.findStructureViewer(oldViewer,
+ (ICompareInput) input, this);
+ fCompareEditorInput.setStructureViewerDescriptor(fSelectedViewerDescriptor);
+ return viewer;
+ }
+ return null;
+ }
+
+ protected Control createTopLeft(Composite p) {
+ final Composite composite = new Composite(p, SWT.NONE) {
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ return super.computeSize(wHint, Math.max(24, hHint), changed);
+ }
+ };
+
+ RowLayout layout = new RowLayout();
+ layout.marginTop = 0;
+ composite.setLayout(layout);
+
+ CLabel cl = new CLabel(composite, SWT.NONE);
+ cl.setText(null);
+
+ toolBar = new ToolBar(composite, SWT.FLAT);
+ toolBar.setVisible(false); // hide by default
+ final ToolItem toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
+ toolItem.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(
+ /* IWorkbenchGraphicConstants */"IMG_LCL_VIEW_MENU")); //$NON-NLS-1$
+ toolItem
+ .setToolTipText(CompareMessages.CompareStructureViewerSwitchingPane_switchButtonTooltip);
+ toolItem.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ showMenu();
+ }
+ });
+ toolBar.addMouseListener(new MouseAdapter() {
+ public void mouseDown(MouseEvent e) {
+ showMenu();
+ }
+ });
+ return composite;
+ }
+
+ protected boolean inputChanged(Object input) {
+ return getInput() != input
+ || fCompareEditorInput.getStructureViewerDescriptor() != fSelectedViewerDescriptor;
+ }
+
+ public void setInput(Object input) {
+ super.setInput(input);
+ if (getViewer() == null || !Utilities.okToUse(getViewer().getControl()))
+ return;
+ ViewerDescriptor[] vd = null;
+ if (getInput() instanceof ICompareInput) {
+ vd = CompareUIPlugin.getDefault().findStructureViewerDescriptor(
+ getViewer(), (ICompareInput) getInput(),
+ getCompareConfiguration());
+ }
+ toolBar.setVisible(vd != null && vd.length > 1);
+ }
+
+ private void showMenu() {
+ if (!(getInput() instanceof ICompareInput))
+ return;
+
+ ViewerDescriptor[] vd = CompareUIPlugin.getDefault()
+ .findStructureViewerDescriptor(getViewer(),
+ (ICompareInput) getInput(), getCompareConfiguration());
+
+ // 1. create
+ final Menu menu = new Menu(getShell(), SWT.POP_UP);
+
+ // add default
+ String label = CompareMessages.CompareStructureViewerSwitchingPane_defaultViewer;
+ MenuItem defaultItem = new MenuItem(menu, SWT.RADIO);
+ defaultItem.setText(label);
+ defaultItem.addSelectionListener(createSelectionListener(null));
+ defaultItem.setSelection(fSelectedViewerDescriptor == null);
+
+ new MenuItem(menu, SWT.SEPARATOR);
+
+ // add others
+ for (int i = 0; i < vd.length; i++) {
+ final ViewerDescriptor vdi = vd[i];
+ label = vdi.getLabel();
+ if (label == null || label.equals("")) { //$NON-NLS-1$
+ String l = CompareUIPlugin.getDefault().findStructureTypeNameOrType((ICompareInput) getInput(), vdi, getCompareConfiguration());
+ if (l == null)
+ // couldn't figure out the label, skip the viewer
+ continue;
+ label = NLS.bind(CompareMessages.CompareStructureViewerSwitchingPane_discoveredLabel, new Object[] {l});
+ }
+ MenuItem item = new MenuItem(menu, SWT.RADIO);
+ item.setText(label);
+ item.addSelectionListener(createSelectionListener(vdi));
+ item.setSelection(vdi == fSelectedViewerDescriptor);
+ }
+
+ // 2. show
+ Rectangle bounds = toolBar.getItem(0).getBounds();
+ Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
+ topLeft = toolBar.toDisplay(topLeft);
+ menu.setLocation(topLeft.x, topLeft.y);
+ menu.setVisible(true);
+
+ // 3. dispose on close
+ menu.addMenuListener(new MenuAdapter() {
+ public void menuHidden(MenuEvent e) {
+ e.display.asyncExec(new Runnable() {
+ public void run() {
+ menu.dispose();
+ }
+ });
+ }
+ });
+ }
+
+ private SelectionListener createSelectionListener(final ViewerDescriptor vd) {
+ return new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ MenuItem mi = (MenuItem) e.widget;
+ if (mi.getSelection()) {
+ Viewer oldViewer = getViewer();
+ fSelectedViewerDescriptor = vd;
+ CompareStructureViewerSwitchingPane.this.setInput(oldViewer
+ .getInput());
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ // nothing to do
+ }
+ };
+ }
+
+ public void setText(String label) {
+ Composite c = (Composite) getTopLeft();
+ Control[] children = c.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (children[i] instanceof CLabel) {
+ CLabel cl = (CLabel) children[i];
+ if (cl != null && !cl.isDisposed()) {
+ cl.setText(label);
+ c.layout();
+ }
+ return;
+ }
+ }
+ }
+
+ public void setImage(Image image) {
+ Composite c = (Composite) getTopLeft();
+ Control[] children = c.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (children[i] instanceof CLabel) {
+ CLabel cl = (CLabel) children[i];
+ if (cl != null && !cl.isDisposed())
+ cl.setImage(image);
+ return;
+ }
+ }
+ }
+
+ public void addMouseListener(MouseListener listener) {
+ Composite c = (Composite) getTopLeft();
+ Control[] children = c.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (children[i] instanceof CLabel) {
+ CLabel cl = (CLabel) children[i];
+ cl.addMouseListener(listener);
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java
new file mode 100644
index 000000000..15815f375
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java
@@ -0,0 +1,1421 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.IStreamContentAccessor;
+import org.eclipse.compare.IStreamMerger;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.internal.core.ComparePlugin;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.IStructureCreator;
+import org.eclipse.compare.structuremergeviewer.StructureDiffViewer;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorRegistry;
+import org.eclipse.ui.IReusableEditor;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The Compare UI plug-in defines the entry point to initiate a configurable
+ * compare operation on arbitrary resources. The result of the compare
+ * is opened into a compare editor where the details can be browsed and
+ * edited in dynamically selected structure and content viewers.
+ * <p>
+ * The Compare UI provides a registry for content and structure compare viewers,
+ * which is initialized from extensions contributed to extension points
+ * declared by this plug-in.
+ * <p>
+ * This class is the plug-in runtime class for the
+ * <code>"org.eclipse.compare"</code> plug-in.
+ * </p>
+ */
+public final class CompareUIPlugin extends AbstractUIPlugin {
+
+ static class CompareRegistry {
+
+ private final static String ID_ATTRIBUTE= "id"; //$NON-NLS-1$
+ private final static String EXTENSIONS_ATTRIBUTE= "extensions"; //$NON-NLS-1$
+ private final static String CONTENT_TYPE_ID_ATTRIBUTE= "contentTypeId"; //$NON-NLS-1$
+
+ private HashMap fIdMap; // maps ids to data
+ private HashMap fExtensionMap; // multimap: maps extensions to list of data
+ private HashMap fContentTypeBindings; // multimap: maps content type bindings to list of data
+
+
+ void register(IConfigurationElement element, Object data) {
+ String id= element.getAttribute(ID_ATTRIBUTE);
+ if (id != null) {
+ if (fIdMap == null)
+ fIdMap= new HashMap();
+ fIdMap.put(id, data);
+ }
+
+ String types= element.getAttribute(EXTENSIONS_ATTRIBUTE);
+ if (types != null) {
+ if (fExtensionMap == null)
+ fExtensionMap= new HashMap();
+ StringTokenizer tokenizer= new StringTokenizer(types, ","); //$NON-NLS-1$
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ List l = (List) fExtensionMap.get(normalizeCase(extension));
+ if (l == null)
+ fExtensionMap.put(normalizeCase(extension), l = new ArrayList());
+ l.add(data);
+ }
+ }
+ }
+
+ void createBinding(IConfigurationElement element, String idAttributeName) {
+ String type= element.getAttribute(CONTENT_TYPE_ID_ATTRIBUTE);
+ String id= element.getAttribute(idAttributeName);
+ if (id == null)
+ logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.targetIdAttributeMissing", idAttributeName)); //$NON-NLS-1$
+ if (type != null && id != null && fIdMap != null) {
+ Object o= fIdMap.get(id);
+ if (o != null) {
+ IContentType ct= fgContentTypeManager.getContentType(type);
+ if (ct != null) {
+ if (fContentTypeBindings == null)
+ fContentTypeBindings= new HashMap();
+ List l = (List) fContentTypeBindings.get(ct);
+ if (l == null)
+ fContentTypeBindings.put(ct, l = new ArrayList());
+ l.add(o);
+ } else {
+ logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.contentTypeNotFound", type)); //$NON-NLS-1$
+ }
+ } else {
+ logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.targetNotFound", id)); //$NON-NLS-1$
+ }
+ }
+ }
+
+ Object search(IContentType type) {
+ List list = searchAll(type);
+ return list != null ? list.get(0) : null;
+ }
+
+ List searchAll(IContentType type) {
+ if (fContentTypeBindings != null) {
+ for (; type != null; type= type.getBaseType()) {
+ List data= (List) fContentTypeBindings.get(type);
+ if (data != null)
+ return data;
+ }
+ }
+ return null;
+ }
+
+ Object search(String extension) {
+ List list = searchAll(extension);
+ return list != null ? list.get(0) : null;
+ }
+
+ List searchAll(String extension) {
+ if (fExtensionMap != null)
+ return (List) fExtensionMap.get(normalizeCase(extension));
+ return null;
+ }
+ }
+
+ /** Status code describing an internal error */
+ public static final int INTERNAL_ERROR= 1;
+
+ private static boolean NORMALIZE_CASE= true;
+
+ public static final String PLUGIN_ID= "org.eclipse.compare"; //$NON-NLS-1$
+
+ private static final String BINARY_TYPE= "binary"; //$NON-NLS-1$
+
+ private static final String STREAM_MERGER_EXTENSION_POINT= "streamMergers"; //$NON-NLS-1$
+ private static final String STREAM_MERGER= "streamMerger"; //$NON-NLS-1$
+ private static final String STREAM_MERGER_ID_ATTRIBUTE= "streamMergerId"; //$NON-NLS-1$
+ private static final String STRUCTURE_CREATOR_EXTENSION_POINT= "structureCreators"; //$NON-NLS-1$
+ private static final String STRUCTURE_CREATOR= "structureCreator"; //$NON-NLS-1$
+ private static final String STRUCTURE_CREATOR_ID_ATTRIBUTE= "structureCreatorId"; //$NON-NLS-1$
+
+ private static final String VIEWER_TAG= "viewer"; //$NON-NLS-1$
+ private static final String STRUCTURE_MERGE_VIEWER_EXTENSION_POINT= "structureMergeViewers"; //$NON-NLS-1$
+ private static final String STRUCTURE_MERGE_VIEWER_ID_ATTRIBUTE= "structureMergeViewerId"; //$NON-NLS-1$
+ private static final String CONTENT_MERGE_VIEWER_EXTENSION_POINT= "contentMergeViewers"; //$NON-NLS-1$
+ private static final String CONTENT_MERGE_VIEWER_ID_ATTRIBUTE= "contentMergeViewerId"; //$NON-NLS-1$
+ private static final String CONTENT_VIEWER_EXTENSION_POINT= "contentViewers"; //$NON-NLS-1$
+ private static final String CONTENT_VIEWER_ID_ATTRIBUTE= "contentViewerId"; //$NON-NLS-1$
+
+ private static final String CONTENT_TYPE_BINDING= "contentTypeBinding"; //$NON-NLS-1$
+
+
+ private static final String COMPARE_EDITOR= PLUGIN_ID + ".CompareEditor"; //$NON-NLS-1$
+
+ private static final String STRUCTUREVIEWER_ALIASES_PREFERENCE_NAME= "StructureViewerAliases"; //$NON-NLS-1$
+
+ // content type
+ private static final IContentTypeManager fgContentTypeManager= Platform.getContentTypeManager();
+
+ public static final int NO_DIFFERENCE = 10000;
+
+ /**
+ * The plugin singleton.
+ */
+ private static CompareUIPlugin fgComparePlugin;
+
+ /** Maps type to icons */
+ private static Map fgImages= new Hashtable(10);
+ /** Maps type to ImageDescriptors */
+ private static Map fgImageDescriptors= new Hashtable(10);
+ /** Maps ImageDescriptors to Images */
+ private static Map fgImages2= new Hashtable(10);
+
+ private static List fgDisposeOnShutdownImages= new ArrayList();
+
+ private ResourceBundle fResourceBundle;
+
+ private boolean fRegistriesInitialized;
+ private CompareRegistry fStreamMergers= new CompareRegistry();
+ private CompareRegistry fStructureCreators= new CompareRegistry();
+ private CompareRegistry fStructureMergeViewers= new CompareRegistry();
+ private CompareRegistry fContentViewers= new CompareRegistry();
+ private CompareRegistry fContentMergeViewers= new CompareRegistry();
+
+ private Map fStructureViewerAliases;
+ private CompareFilter fFilter;
+ private IPropertyChangeListener fPropertyChangeListener;
+
+ /**
+ * Creates the <code>CompareUIPlugin</code> object and registers all
+ * structure creators, content merge viewers, and structure merge viewers
+ * contributed to this plug-in's extension points.
+ * <p>
+ * Note that instances of plug-in runtime classes are automatically created
+ * by the platform in the course of plug-in activation.
+ */
+ public CompareUIPlugin() {
+ super();
+ Assert.isTrue(fgComparePlugin == null);
+ fgComparePlugin= this;
+ }
+
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+
+ ComparePlugin.getDefault().setCappingDisabled(
+ getPreferenceStore().getBoolean(
+ ComparePreferencePage.CAPPING_DISABLED));
+ }
+
+ public void stop(BundleContext context) throws Exception {
+
+ IPreferenceStore ps= getPreferenceStore();
+ rememberAliases(ps);
+ if (fPropertyChangeListener != null) {
+ ps.removePropertyChangeListener(fPropertyChangeListener);
+ fPropertyChangeListener= null;
+ }
+
+ super.stop(context);
+
+ if (fgDisposeOnShutdownImages != null) {
+ Iterator i= fgDisposeOnShutdownImages.iterator();
+ while (i.hasNext()) {
+ Image img= (Image) i.next();
+ if (!img.isDisposed())
+ img.dispose();
+ }
+ fgImages= null;
+ }
+ }
+
+ /**
+ * Returns the singleton instance of this plug-in runtime class.
+ *
+ * @return the compare plug-in instance
+ */
+ public static CompareUIPlugin getDefault() {
+ return fgComparePlugin;
+ }
+
+ /**
+ * Returns this plug-in's resource bundle.
+ *
+ * @return the plugin's resource bundle
+ */
+ public ResourceBundle getResourceBundle() {
+ if (fResourceBundle == null)
+ fResourceBundle= Platform.getResourceBundle(getBundle());
+ return fResourceBundle;
+ }
+
+ /**
+ * Returns this plug-in's unique identifier.
+ *
+ * @return the plugin's unique identifier
+ */
+ public static String getPluginId() {
+ return getDefault().getBundle().getSymbolicName();
+ }
+
+ private void initializeRegistries() {
+ if (!fRegistriesInitialized) {
+ registerExtensions();
+ fRegistriesInitialized= true;
+ }
+ }
+
+ /**
+ * Registers all stream mergers, structure creators, content merge viewers, and structure merge viewers
+ * that are found in the XML plugin files.
+ */
+ private void registerExtensions() {
+ IExtensionRegistry registry= Platform.getExtensionRegistry();
+
+ // collect all IStreamMergers
+ IConfigurationElement[] elements= registry.getConfigurationElementsFor(PLUGIN_ID, STREAM_MERGER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ if (STREAM_MERGER.equals(element.getName()))
+ fStreamMergers.register(element, new StreamMergerDescriptor(element));
+ }
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ if (CONTENT_TYPE_BINDING.equals(element.getName()))
+ fStreamMergers.createBinding(element, STREAM_MERGER_ID_ATTRIBUTE);
+ }
+
+ // collect all IStructureCreators
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_CREATOR_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ String name= element.getName();
+ if (!CONTENT_TYPE_BINDING.equals(name)) {
+ if (!STRUCTURE_CREATOR.equals(name))
+ logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, STRUCTURE_CREATOR)); //$NON-NLS-1$
+ fStructureCreators.register(element, new StructureCreatorDescriptor(element));
+ }
+ }
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ if (CONTENT_TYPE_BINDING.equals(element.getName()))
+ fStructureCreators.createBinding(element, STRUCTURE_CREATOR_ID_ATTRIBUTE);
+ }
+
+ // collect all viewers which define the structure merge viewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_MERGE_VIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ String name= element.getName();
+ if (!CONTENT_TYPE_BINDING.equals(name)) {
+ if (!VIEWER_TAG.equals(name))
+ logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, VIEWER_TAG)); //$NON-NLS-1$
+ fStructureMergeViewers.register(element, new ViewerDescriptor(element));
+ }
+ }
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ if (CONTENT_TYPE_BINDING.equals(element.getName()))
+ fStructureMergeViewers.createBinding(element, STRUCTURE_MERGE_VIEWER_ID_ATTRIBUTE);
+ }
+
+ // collect all viewers which define the content merge viewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_MERGE_VIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ String name= element.getName();
+ if (!CONTENT_TYPE_BINDING.equals(name)) {
+ if (!VIEWER_TAG.equals(name))
+ logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, VIEWER_TAG)); //$NON-NLS-1$
+ fContentMergeViewers.register(element, new ViewerDescriptor(element));
+ }
+ }
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ if (CONTENT_TYPE_BINDING.equals(element.getName()))
+ fContentMergeViewers.createBinding(element, CONTENT_MERGE_VIEWER_ID_ATTRIBUTE);
+ }
+
+ // collect all viewers which define the content viewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_VIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ String name= element.getName();
+ if (!CONTENT_TYPE_BINDING.equals(name)) {
+ if (!VIEWER_TAG.equals(name))
+ logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, VIEWER_TAG)); //$NON-NLS-1$
+ fContentViewers.register(element, new ViewerDescriptor(element));
+ }
+ }
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+ if (CONTENT_TYPE_BINDING.equals(element.getName()))
+ fContentViewers.createBinding(element, CONTENT_VIEWER_ID_ATTRIBUTE);
+ }
+ }
+
+ public static IWorkbench getActiveWorkbench() {
+ CompareUIPlugin plugin= getDefault();
+ if (plugin == null)
+ return null;
+ return plugin.getWorkbench();
+ }
+
+ public static IWorkbenchWindow getActiveWorkbenchWindow() {
+ IWorkbench workbench= getActiveWorkbench();
+ if (workbench == null)
+ return null;
+ return workbench.getActiveWorkbenchWindow();
+ }
+
+ /**
+ * Returns the active workbench page or <code>null</code> if
+ * no active workbench page can be determined.
+ *
+ * @return the active workbench page or <code>null</code> if
+ * no active workbench page can be determined
+ */
+ private static IWorkbenchPage getActivePage() {
+ IWorkbenchWindow window= getActiveWorkbenchWindow();
+ if (window == null)
+ return null;
+ return window.getActivePage();
+ }
+
+ /**
+ * Returns the SWT Shell of the active workbench window or <code>null</code> if
+ * no workbench window is active.
+ *
+ * @return the SWT Shell of the active workbench window, or <code>null</code> if
+ * no workbench window is active
+ */
+ public static Shell getShell() {
+ IWorkbenchWindow window= getActiveWorkbenchWindow();
+ if (window == null)
+ return null;
+ return window.getShell();
+ }
+
+ /**
+ * Registers the given image for being disposed when this plug-in is shutdown.
+ *
+ * @param image the image to register for disposal
+ */
+ public static void disposeOnShutdown(Image image) {
+ if (image != null)
+ fgDisposeOnShutdownImages.add(image);
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a compare
+ * editor on the result.
+ *
+ * @param input
+ * the input on which to open the compare editor
+ * @param page
+ * the workbench page on which to create a new compare editor
+ * @param editor
+ * if not null the input is opened in this editor
+ * @param activate
+ * if <code>true</code> the editor will be activated
+ * @see IWorkbenchPage#openEditor(org.eclipse.ui.IEditorInput, String,
+ * boolean)
+ * @see CompareEditorInput
+ */
+ public void openCompareEditor(final CompareEditorInput input,
+ final IWorkbenchPage page, final IReusableEditor editor,
+ final boolean activate) {
+ CompareConfiguration configuration = input.getCompareConfiguration();
+ if (configuration != null) {
+ IPreferenceStore ps= configuration.getPreferenceStore();
+ if (ps != null)
+ configuration.setProperty(
+ CompareConfiguration.USE_OUTLINE_VIEW,
+ Boolean.valueOf(ps.getBoolean(ComparePreferencePage.USE_OUTLINE_VIEW)));
+ }
+ if (input.canRunAsJob()) {
+ openEditorInBackground(input, page, editor, activate);
+ } else {
+ if (compareResultOK(input, null)) {
+ internalOpenEditor(input, page, editor, activate);
+ }
+ }
+ }
+
+ private void openEditorInBackground(final CompareEditorInput input,
+ final IWorkbenchPage page, final IReusableEditor editor,
+ final boolean activate) {
+ internalOpenEditor(input, page, editor, activate);
+ }
+
+ private void internalOpenEditor(final CompareEditorInput input,
+ final IWorkbenchPage wp, final IReusableEditor editor,
+ final boolean activate) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ if (editor != null && !editor.getSite().getShell().isDisposed()) { // reuse the given editor
+ editor.setInput(input);
+ return;
+ }
+
+ IWorkbenchPage page = wp;
+ if (page == null)
+ page= getActivePage();
+ if (page != null) {
+ // open new CompareEditor on page
+ try {
+ page.openEditor(input, COMPARE_EDITOR, activate);
+ } catch (PartInitException e) {
+ MessageDialog.openError(getShell(), Utilities.getString("CompareUIPlugin.openEditorError"), e.getMessage()); //$NON-NLS-1$
+ }
+ } else {
+ MessageDialog.openError(getShell(),
+ Utilities.getString("CompareUIPlugin.openEditorError"), //$NON-NLS-1$
+ Utilities.getString("CompareUIPlugin.noActiveWorkbenchPage")); //$NON-NLS-1$
+ }
+ }
+ };
+ syncExec(runnable);
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * compare dialog on the result.
+ *
+ * @param input the input on which to open the compare editor
+ * @see CompareEditorInput
+ */
+ public void openCompareDialog(final CompareEditorInput input) {
+ // We don't ever open dialogs in the background
+ if (compareResultOK(input, null)) {
+ internalOpenDialog(input);
+ }
+ }
+
+ public IStatus prepareInput(CompareEditorInput input, IProgressMonitor monitor) {
+ try {
+ input.run(monitor);
+ String message= input.getMessage();
+ if (message != null) {
+ return new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, message, null);
+ }
+ if (input.getCompareResult() == null) {
+ return new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, NO_DIFFERENCE, Utilities.getString("CompareUIPlugin.noDifferences"), null); //$NON-NLS-1$
+ }
+ return Status.OK_STATUS;
+ } catch (InterruptedException e) {
+ throw new OperationCanceledException();
+ } catch (InvocationTargetException e) {
+ return new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, Utilities.getString("CompareUIPlugin.compareFailed"), e.getTargetException()); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * @return <code>true</code> if compare result is OK to show, <code>false</code> otherwise
+ */
+ public boolean compareResultOK(CompareEditorInput input, IRunnableContext context) {
+ final Shell shell= getShell();
+ try {
+
+ // run operation in separate thread and make it cancelable
+ if (context == null)
+ context = PlatformUI.getWorkbench().getProgressService();
+ context.run(true, true, input);
+
+ String message= input.getMessage();
+ if (message != null) {
+ MessageDialog.openError(shell, Utilities.getString("CompareUIPlugin.compareFailed"), message); //$NON-NLS-1$
+ return false;
+ }
+
+ if (input.getCompareResult() == null) {
+ MessageDialog.openInformation(shell, Utilities.getString("CompareUIPlugin.dialogTitle"), Utilities.getString("CompareUIPlugin.noDifferences")); //$NON-NLS-2$ //$NON-NLS-1$
+ return false;
+ }
+
+ return true;
+
+ } catch (InterruptedException x) {
+ // canceled by user
+ } catch (InvocationTargetException x) {
+ MessageDialog.openError(shell, Utilities.getString("CompareUIPlugin.compareFailed"), x.getTargetException().getMessage()); //$NON-NLS-1$
+ }
+ return false;
+ }
+
+ /*
+ * Registers an image for the given type.
+ */
+ private static void registerImage(String type, Image image, boolean dispose) {
+ fgImages.put(normalizeCase(type), image);
+ if (image != null && dispose) {
+ fgDisposeOnShutdownImages.add(image);
+ }
+ }
+
+ /**
+ * Registers an image descriptor for the given type.
+ *
+ * @param type the type
+ * @param descriptor the image descriptor
+ */
+ public static void registerImageDescriptor(String type, ImageDescriptor descriptor) {
+ fgImageDescriptors.put(normalizeCase(type), descriptor);
+ }
+
+ public static ImageDescriptor getImageDescriptor(String relativePath) {
+ if (fgComparePlugin == null)
+ return null;
+ IPath path= Utilities.getIconPath(null).append(relativePath);
+ URL url= FileLocator.find(fgComparePlugin.getBundle(), path, null);
+ if (url == null)
+ return null;
+ return ImageDescriptor.createFromURL(url);
+ }
+
+ /**
+ * Returns a shared image for the given type, or a generic image if none
+ * has been registered for the given type.
+ * <p>
+ * Note: Images returned from this method will be automatically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param type the type
+ * @return the image
+ */
+ public static Image getImage(String type) {
+
+ type= normalizeCase(type);
+
+ boolean dispose= false;
+ Image image= null;
+ if (type != null)
+ image= (Image) fgImages.get(type);
+ if (image == null) {
+ ImageDescriptor id= (ImageDescriptor) fgImageDescriptors.get(type);
+ if (id != null) {
+ image= id.createImage();
+ dispose= true;
+ }
+
+ if (image == null) {
+ if (fgComparePlugin != null) {
+ if (ITypedElement.FOLDER_TYPE.equals(type)) {
+ image= getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
+ //image= SharedImages.getImage(ISharedImages.IMG_OBJ_FOLDER);
+ } else {
+ image= createWorkbenchImage(type);
+ dispose= true;
+ }
+ } else {
+ id= (ImageDescriptor) fgImageDescriptors.get(normalizeCase("file")); //$NON-NLS-1$
+ image= id.createImage();
+ dispose= true;
+ }
+ }
+ if (image != null)
+ registerImage(type, image, dispose);
+ }
+ return image;
+ }
+
+ /**
+ * Returns a shared image for the given adaptable.
+ * This convenience method queries the given adaptable
+ * for its <code>IWorkbenchAdapter.getImageDescriptor</code>, which it
+ * uses to create an image if it does not already have one.
+ * <p>
+ * Note: Images returned from this method will be automatically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param adaptable the adaptable for which to find an image
+ * @return an image
+ */
+ public static Image getImage(IAdaptable adaptable) {
+ if (adaptable != null) {
+ Object o= adaptable.getAdapter(IWorkbenchAdapter.class);
+ if (o instanceof IWorkbenchAdapter) {
+ ImageDescriptor id= ((IWorkbenchAdapter) o).getImageDescriptor(adaptable);
+ if (id != null) {
+ Image image= (Image)fgImages2.get(id);
+ if (image == null) {
+ image= id.createImage();
+ try {
+ fgImages2.put(id, image);
+ } catch (NullPointerException ex) {
+ // NeedWork
+ }
+ fgDisposeOnShutdownImages.add(image);
+
+ }
+ return image;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Image createWorkbenchImage(String type) {
+ IEditorRegistry er= getDefault().getWorkbench().getEditorRegistry();
+ ImageDescriptor id= er.getImageDescriptor("foo." + type); //$NON-NLS-1$
+ return id.createImage();
+ }
+
+ /**
+ * Returns an structure creator descriptor for the given type.
+ *
+ * @param type the type for which to find a descriptor
+ * @return a descriptor for the given type, or <code>null</code> if no
+ * descriptor has been registered
+ */
+ public StructureCreatorDescriptor getStructureCreator(String type) {
+ initializeRegistries();
+ return (StructureCreatorDescriptor) fStructureCreators.search(type);
+ }
+
+ /**
+ * Returns a stream merger for the given type.
+ *
+ * @param type the type for which to find a stream merger
+ * @return a stream merger for the given type, or <code>null</code> if no
+ * stream merger has been registered
+ */
+ public IStreamMerger createStreamMerger(String type) {
+ initializeRegistries();
+ StreamMergerDescriptor descriptor= (StreamMergerDescriptor) fStreamMergers.search(type);
+ if (descriptor != null)
+ return descriptor.createStreamMerger();
+ return null;
+ }
+
+ /**
+ * Returns a stream merger for the given content type.
+ *
+ * @param type the type for which to find a stream merger
+ * @return a stream merger for the given type, or <code>null</code> if no
+ * stream merger has been registered
+ */
+ public IStreamMerger createStreamMerger(IContentType type) {
+ initializeRegistries();
+ StreamMergerDescriptor descriptor= (StreamMergerDescriptor) fStreamMergers.search(type);
+ if (descriptor != null)
+ return descriptor.createStreamMerger();
+ return null;
+ }
+
+ public ViewerDescriptor[] findStructureViewerDescriptor(Viewer oldViewer,
+ ICompareInput input, CompareConfiguration configuration) {
+ if (input == null)
+ return null;
+ // we don't show the structure of additions or deletions
+ if (input == null || input.getLeft() == null || input.getRight() == null)
+ return null;
+
+ Set result = new LinkedHashSet();
+
+ // content type search
+ IContentType ctype= getCommonType(input);
+ if (ctype != null) {
+ initializeRegistries();
+ List list = fStructureMergeViewers.searchAll(ctype);
+ if (list != null)
+ result.addAll(list);
+ }
+
+ // old style search
+ String[] types= getTypes(input);
+ String type= null;
+ if (isHomogenous(types)) {
+ type= normalizeCase(types[0]);
+ initializeRegistries();
+ List list = fStructureMergeViewers.searchAll(type);
+ if (list != null)
+ result.addAll(list);
+ String alias= getStructureViewerAlias(type);
+ if (alias != null) {
+ list = fStructureMergeViewers.searchAll(alias);
+ if (list != null)
+ result.addAll(list);
+ }
+ }
+
+ return result.size() > 0 ? (ViewerDescriptor[]) result
+ .toArray(new ViewerDescriptor[0]) : null;
+ }
+
+ /**
+ * Returns a structure compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input, the old viewer
+ * is returned. Otherwise, the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a structure compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a structure viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent,
+ CompareConfiguration configuration) {
+ ViewerDescriptor[] descriptors = findStructureViewerDescriptor(oldViewer, input, configuration);
+ if (descriptors == null || descriptors.length == 0) {
+ // we didn't found any viewer so far.
+ // now we try to find a structure creator for the generic StructureDiffViewer
+ IContentType ctype= getCommonType(input);
+
+ String[] types= getTypes(input);
+ String type= null;
+ if (isHomogenous(types)) {
+ type= normalizeCase(types[0]);
+ }
+
+ StructureCreatorDescriptor scc= null;
+ initializeRegistries();
+ Object desc= fStructureCreators.search(ctype); // search for content type
+ if (desc instanceof StructureCreatorDescriptor)
+ scc= (StructureCreatorDescriptor) desc;
+ if (scc == null && type != null)
+ scc= getStructureCreator(type); // search for old-style type scheme
+ if (scc != null) {
+ IStructureCreator sc= scc.createStructureCreator();
+ if (sc != null) {
+ StructureDiffViewer sdv= new StructureDiffViewer(parent, configuration);
+ sdv.setStructureCreator(sc);
+ return sdv;
+ }
+ }
+ return null;
+ }
+ return getViewer(descriptors[0], oldViewer, parent, configuration);
+ }
+
+ public ViewerDescriptor[] findContentViewerDescriptor(Viewer oldViewer, Object in, CompareConfiguration cc) {
+ Set result = new LinkedHashSet();
+ if (in instanceof IStreamContentAccessor) {
+ String type= ITypedElement.TEXT_TYPE;
+
+ if (in instanceof ITypedElement) {
+ ITypedElement tin= (ITypedElement) in;
+
+ IContentType ct= getContentType(tin);
+ if (ct != null) {
+ initializeRegistries();
+ List list = fContentViewers.searchAll(ct);
+ if (list != null)
+ result.addAll(list);
+ }
+
+ String ty= tin.getType();
+ if (ty != null)
+ type= ty;
+ }
+
+ initializeRegistries();
+ List list = fContentViewers.searchAll(type);
+ if (list != null)
+ result.addAll(list);
+ // fallback
+ result.add(fContentViewers.search(Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT)));
+ return (ViewerDescriptor[]) result.toArray(new ViewerDescriptor[0]);
+ }
+
+ if (!(in instanceof ICompareInput))
+ return null;
+
+ ICompareInput input= (ICompareInput) in;
+
+ IContentType ctype = getCommonType(input);
+ if (ctype != null) {
+ initializeRegistries();
+ List list = fContentMergeViewers.searchAll(ctype);
+ if (list != null)
+ result.addAll(list);
+ }
+
+ String[] types= getTypes(input);
+ String type= null;
+ if (isHomogenous(types))
+ type= types[0];
+
+ if (ITypedElement.FOLDER_TYPE.equals(type))
+ return null;
+
+ if (type == null) {
+ int n= 0;
+ for (int i= 0; i < types.length; i++)
+ if (!ITypedElement.UNKNOWN_TYPE.equals(types[i])) {
+ n++;
+ if (type == null)
+ type= types[i]; // remember the first known type
+ }
+ if (n > 1) // don't use the type if there were more than one
+ type= null;
+ }
+
+ if (type != null) {
+ initializeRegistries();
+ List list = fContentMergeViewers.searchAll(type);
+ if (list != null)
+ result.addAll(list);
+ }
+
+ // fallback
+ String leftType= guessType(input.getLeft());
+ String rightType= guessType(input.getRight());
+
+ if (leftType != null || rightType != null) {
+ boolean right_text = rightType != null
+ && ITypedElement.TEXT_TYPE.equals(rightType);
+ boolean left_text = leftType != null
+ && ITypedElement.TEXT_TYPE.equals(leftType);
+ initializeRegistries();
+ if ((rightType != null && !right_text)
+ || (leftType != null && !left_text)) {
+ List list = fContentMergeViewers.searchAll(BINARY_TYPE);
+ if (list != null)
+ result.addAll(list);
+ }
+ List list = fContentMergeViewers.searchAll(ITypedElement.TEXT_TYPE);
+ if (list != null)
+ result.addAll(list);
+
+ return (ViewerDescriptor[]) result.toArray(new ViewerDescriptor[0]);
+ }
+ return result.size() > 0 ? (ViewerDescriptor[])result.toArray(new ViewerDescriptor[0]) : null;
+ }
+
+ /**
+ * Returns a content compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input the old viewer
+ * is returned. Otherwise the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a content compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param in the input object for which to find a content viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param cc a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public Viewer findContentViewer(Viewer oldViewer, Object in,
+ Composite parent, CompareConfiguration cc) {
+ ViewerDescriptor[] descriptors = findContentViewerDescriptor(oldViewer, in, cc);
+ return getViewer(descriptors != null ? descriptors[0] : null, oldViewer, parent, cc);
+ }
+
+ private static Viewer getViewer(Object descriptor, Viewer oldViewer, Composite parent, CompareConfiguration cc) {
+ if (descriptor instanceof IViewerDescriptor)
+ return ((IViewerDescriptor)descriptor).createViewer(oldViewer, parent, cc);
+ return null;
+ }
+
+ private static String[] getTypes(ICompareInput input) {
+ ITypedElement ancestor= input.getAncestor();
+ ITypedElement left= input.getLeft();
+ ITypedElement right= input.getRight();
+
+ ArrayList tmp= new ArrayList();
+ if (ancestor != null) {
+ String type= ancestor.getType();
+ if (type != null)
+ tmp.add(normalizeCase(type));
+ }
+ if (left != null) {
+ String type= left.getType();
+ if (type != null)
+ tmp.add(normalizeCase(type));
+ }
+ if (right != null) {
+ String type= right.getType();
+ if (type != null)
+ tmp.add(normalizeCase(type));
+ }
+ return (String[]) tmp.toArray(new String[tmp.size()]);
+ }
+
+ private static IContentType getContentType(ITypedElement element) {
+ if (element == null)
+ return null;
+ String name= element.getName();
+ IContentType ct= null;
+ if (element instanceof IStreamContentAccessor) {
+ IStreamContentAccessor isa= (IStreamContentAccessor) element;
+ try {
+ InputStream is= isa.getContents();
+ if (is != null) {
+ InputStream bis= new BufferedInputStream(is);
+ try {
+ ct= fgContentTypeManager.findContentTypeFor(is, name);
+ } catch (IOException e) {
+ // silently ignored
+ } finally {
+ try {
+ bis.close();
+ } catch (IOException e2) {
+ // silently ignored
+ }
+ }
+ }
+ } catch (CoreException e1) {
+ // silently ignored
+ }
+ }
+ if (ct == null)
+ ct= fgContentTypeManager.findContentTypeFor(name);
+ return ct;
+ }
+
+ /*
+ * Returns true if the given types are homogeneous.
+ */
+ private static boolean isHomogenous(String[] types) {
+ switch (types.length) {
+ case 1:
+ return true;
+ case 2:
+ return types[0].equals(types[1]);
+ case 3:
+ return types[0].equals(types[1]) && types[1].equals(types[2]);
+ }
+ return false;
+ }
+
+ /*
+ * Returns the most specific content type that is common to the given inputs or null.
+ */
+ private static IContentType getCommonType(ICompareInput input) {
+
+ ITypedElement ancestor= input.getAncestor();
+ ITypedElement left= input.getLeft();
+ ITypedElement right= input.getRight();
+
+ int n= 0;
+ IContentType[] types= new IContentType[3];
+ IContentType type= null;
+
+ if (ancestor != null) {
+ type= getContentType(ancestor);
+ if (type != null)
+ types[n++]= type;
+ }
+ type= getContentType(left);
+ if (type != null)
+ types[n++]= type;
+ else
+ return null;
+ type= getContentType(right);
+ if (type != null)
+ types[n++]= type;
+ else
+ return null;
+
+ IContentType result= null;
+ IContentType[] s0, s1, s2;
+ switch (n) {
+ case 0:
+ return null;
+ case 1:
+ return types[0];
+ case 2:
+ if (types[0].equals(types[1]))
+ return types[0];
+ s0= toFullPath(types[0]);
+ s1= toFullPath(types[1]);
+ for (int i= 0; i < Math.min(s0.length, s1.length); i++) {
+ if (!s0[i].equals(s1[i]))
+ break;
+ result= s0[i];
+ }
+ return result;
+ case 3:
+ if (types[0].equals(types[1]) && types[1].equals(types[2]))
+ return types[0];
+ s0= toFullPath(types[0]);
+ s1= toFullPath(types[1]);
+ s2= toFullPath(types[2]);
+ for (int i= 0; i < Math.min(Math.min(s0.length, s1.length), s2.length); i++) {
+ if (!s0[i].equals(s1[i]) || !s1[i].equals(s2[i]))
+ break;
+ result= s0[i];
+ }
+ return result;
+ }
+ return null;
+ }
+
+ private static IContentType[] toFullPath(IContentType ct) {
+ List l= new ArrayList();
+ for (; ct != null; ct= ct.getBaseType())
+ l.add(0, ct);
+ return (IContentType[]) l.toArray(new IContentType[l.size()]);
+ }
+
+ /*
+ * Guesses the file type of the given input.
+ * Returns ITypedElement.TEXT_TYPE if none of the first 10 lines is longer than 1000 bytes.
+ * Returns ITypedElement.UNKNOWN_TYPE otherwise.
+ * Returns <code>null</code> if the input isn't an <code>IStreamContentAccessor</code>.
+ */
+ private static String guessType(ITypedElement input) {
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ InputStream is= null;
+ try {
+ is= sca.getContents();
+ if (is == null)
+ return null;
+ int lineLength= 0;
+ int lines= 0;
+ while (lines < 10) {
+ int c= is.read();
+ if (c == -1) // EOF
+ break;
+ if (c == '\n' || c == '\r') { // reset line length
+ lineLength= 0;
+ lines++;
+ } else
+ lineLength++;
+ if (lineLength > 1000)
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+ return ITypedElement.TEXT_TYPE;
+ } catch (CoreException ex) {
+ // be silent and return UNKNOWN_TYPE
+ } catch (IOException ex) {
+ // be silent and return UNKNOWN_TYPE
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ex) {
+ // silently ignored
+ }
+ }
+ }
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+ return null;
+ }
+
+ private static String normalizeCase(String s) {
+ if (NORMALIZE_CASE && s != null)
+ return s.toUpperCase();
+ return s;
+ }
+
+ //---- alias management
+
+ private String getStructureViewerAlias(String type) {
+ return (String) getStructureViewerAliases().get(type);
+ }
+
+ public void addStructureViewerAlias(String type, String alias) {
+ getStructureViewerAliases().put(normalizeCase(alias), normalizeCase(type));
+ }
+
+ private Map getStructureViewerAliases() {
+ if (fStructureViewerAliases == null) {
+ fStructureViewerAliases= new Hashtable(10);
+ String aliases= getPreferenceStore().getString(STRUCTUREVIEWER_ALIASES_PREFERENCE_NAME);
+ if (aliases != null && aliases.length() > 0) {
+ StringTokenizer st= new StringTokenizer(aliases, " "); //$NON-NLS-1$
+ while (st.hasMoreTokens()) {
+ String pair= st.nextToken();
+ int pos= pair.indexOf('.');
+ if (pos > 0) {
+ String key= pair.substring(0, pos);
+ String alias= pair.substring(pos+1);
+ fStructureViewerAliases.put(key, alias);
+ }
+ }
+ }
+ }
+ return fStructureViewerAliases;
+ }
+
+ public void removeAllStructureViewerAliases(String type) {
+ if (fStructureViewerAliases == null)
+ return;
+ String t= normalizeCase(type);
+ Set entrySet= fStructureViewerAliases.entrySet();
+ for (Iterator iter= entrySet.iterator(); iter.hasNext(); ) {
+ Map.Entry entry= (Map.Entry)iter.next();
+ if (entry.getValue().equals(t))
+ iter.remove();
+ }
+ }
+
+ /*
+ * Converts the aliases into a single string before they are stored
+ * in the preference store.
+ * The format is:
+ * <key> '.' <alias> ' ' <key> '.' <alias> ...
+ */
+ private void rememberAliases(IPreferenceStore ps) {
+ if (fStructureViewerAliases == null)
+ return;
+ StringBuffer buffer= new StringBuffer();
+ Iterator iter= fStructureViewerAliases.keySet().iterator();
+ while (iter.hasNext()) {
+ String key= (String) iter.next();
+ String alias= (String) fStructureViewerAliases.get(key);
+ buffer.append(key);
+ buffer.append('.');
+ buffer.append(alias);
+ buffer.append(' ');
+ }
+ ps.setValue(STRUCTUREVIEWER_ALIASES_PREFERENCE_NAME, buffer.toString());
+ }
+
+ //---- filters
+
+ public boolean filter(String name, boolean isFolder, boolean isArchive) {
+ if (fFilter == null) {
+ fFilter= new CompareFilter();
+ final IPreferenceStore ps= getPreferenceStore();
+ fFilter.setFilters(ps.getString(ComparePreferencePage.PATH_FILTER));
+ fPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (ComparePreferencePage.PATH_FILTER.equals(event.getProperty()))
+ fFilter.setFilters(ps.getString(ComparePreferencePage.PATH_FILTER));
+ }
+ };
+ ps.addPropertyChangeListener(fPropertyChangeListener);
+ }
+ return fFilter.filter(name, isFolder, isArchive);
+ }
+
+ private void internalOpenDialog(final CompareEditorInput input) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ CompareDialog dialog= new CompareDialog(getShell(), input);
+ dialog.open();
+ }
+ };
+ syncExec(runnable);
+ }
+
+ private void syncExec(Runnable runnable) {
+ if (Display.getCurrent() == null) {
+ Display.getDefault().syncExec(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ //---- more utilities
+
+ protected void handleNoDifference() {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ MessageDialog.openInformation(getShell(), Utilities.getString("CompareUIPlugin.dialogTitle"), Utilities.getString("CompareUIPlugin.noDifferences")); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ };
+ syncExec(runnable);
+ }
+
+ /**
+ * Returns an array of all editors that have an unsaved content. If the identical content is
+ * presented in more than one editor, only one of those editor parts is part of the result.
+ *
+ * @return an array of all dirty editor parts.
+ */
+ public static IEditorPart[] getDirtyEditors() {
+ Set inputs= new HashSet();
+ List result= new ArrayList(0);
+ IWorkbench workbench= getDefault().getWorkbench();
+ IWorkbenchWindow[] windows= workbench.getWorkbenchWindows();
+ for (int i= 0; i < windows.length; i++) {
+ IWorkbenchPage[] pages= windows[i].getPages();
+ for (int x= 0; x < pages.length; x++) {
+ IEditorPart[] editors= pages[x].getDirtyEditors();
+ for (int z= 0; z < editors.length; z++) {
+ IEditorPart ep= editors[z];
+ IEditorInput input= ep.getEditorInput();
+ if (!inputs.contains(input)) {
+ inputs.add(input);
+ result.add(ep);
+ }
+ }
+ }
+ }
+ return (IEditorPart[])result.toArray(new IEditorPart[result.size()]);
+ }
+
+ public static void logErrorMessage(String message) {
+ if (message == null)
+ message= ""; //$NON-NLS-1$
+ log(new Status(IStatus.ERROR, getPluginId(), INTERNAL_ERROR, message, null));
+ }
+
+ public static void log(Throwable e) {
+ log(new Status(IStatus.ERROR, getPluginId(), INTERNAL_ERROR, CompareMessages.ComparePlugin_internal_error, e));
+ }
+
+ public static void log(IStatus status) {
+ getDefault().getLog().log(status);
+ }
+
+ String findContentTypeNameOrType(ICompareInput input, ViewerDescriptor vd, CompareConfiguration cc) {
+ IContentType ctype= getCommonType(input);
+ if (ctype != null) {
+ initializeRegistries();
+ List list = fContentMergeViewers.searchAll(ctype);
+ if (list != null)
+ if (list.contains(vd))
+ return ctype.getName();
+ }
+
+ String[] types= getTypes(input);
+ String type= null;
+ if (isHomogenous(types))
+ type= types[0];
+
+ if (ITypedElement.FOLDER_TYPE.equals(type))
+ return null;
+
+ if (type == null) {
+ int n= 0;
+ for (int i= 0; i < types.length; i++)
+ if (!ITypedElement.UNKNOWN_TYPE.equals(types[i])) {
+ n++;
+ if (type == null)
+ type= types[i]; // remember the first known type
+ }
+ if (n > 1) // don't use the type if there were more than one
+ type= null;
+ }
+
+ if (type != null) {
+ initializeRegistries();
+ List list = fContentMergeViewers.searchAll(type);
+ if (list != null)
+ if (list.contains(vd))
+ return type;
+ }
+
+ // fallback
+ String leftType= guessType(input.getLeft());
+ String rightType= guessType(input.getRight());
+
+ if (leftType != null || rightType != null) {
+ boolean right_text = rightType != null
+ && ITypedElement.TEXT_TYPE.equals(rightType);
+ boolean left_text = leftType != null
+ && ITypedElement.TEXT_TYPE.equals(leftType);
+ initializeRegistries();
+ if ((rightType != null && !right_text)
+ || (leftType != null && !left_text)) {
+ List list = fContentMergeViewers.searchAll(BINARY_TYPE);
+ if (list != null)
+ if (list.contains(vd))
+ return type;
+ }
+ List list = fContentMergeViewers.searchAll(ITypedElement.TEXT_TYPE);
+ if (list != null)
+ if (list.contains(vd))
+ return type;
+ }
+ return null;
+ }
+
+ String findStructureTypeNameOrType(ICompareInput input, ViewerDescriptor vd, CompareConfiguration cc) {
+ if (input == null)
+ return null;
+ // we don't show the structure of additions or deletions
+ if (input == null || input.getLeft() == null || input.getRight() == null)
+ return null;
+
+ // content type search
+ IContentType ctype= getCommonType(input);
+ if (ctype != null) {
+ initializeRegistries();
+ List list = fStructureMergeViewers.searchAll(ctype);
+ if (list != null)
+ if (list.contains(vd))
+ return ctype.getName();
+ }
+
+ // old style search
+ String[] types= getTypes(input);
+ String type= null;
+ if (isHomogenous(types)) {
+ type= normalizeCase(types[0]);
+ initializeRegistries();
+ List list = fStructureMergeViewers.searchAll(type);
+ if (list != null)
+ if (list.contains(vd))
+ return type;
+ String alias= getStructureViewerAlias(type);
+ if (alias != null) {
+ list = fStructureMergeViewers.searchAll(alias);
+ if (list != null)
+ if (list.contains(vd))
+ return alias;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.java
new file mode 100644
index 000000000..771aef90e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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;
+
+public class CompareWithEditionAction extends EditionAction {
+
+ public CompareWithEditionAction() {
+ super(false, "org.eclipse.compare.internal.CompareWithEditionAction"); //$NON-NLS-1$
+ this.fHelpContextId= ICompareContextIds.COMPARE_WITH_EDITION_DIALOG;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.properties
new file mode 100644
index 000000000..ad6aec79f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithEditionAction.properties
@@ -0,0 +1,38 @@
+###############################################################################
+# Copyright (c) 2000, 2006 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
+###############################################################################
+
+# @(#)CompareWithEditionAction.properties
+#
+# Resources for CompareWithEditionAction.java
+
+title= Compare with Local History
+
+treeTitleFormat= Local History of ''{0}''
+dateIcon= obj16/day_obj.gif
+timeIcon= obj16/resource_obj.gif
+
+treeFormat= {0}
+workspaceTreeFormat= {0} (Workspace File)
+parseErrorFormat= {0} (Parse Error)
+
+editionLabel= Local History ({0})
+workspaceEditionLabel= Workspace File
+
+targetLabel= Editor Buffer
+workspaceTargetLabel= Workspace File
+
+todayFormat= Today ({0})
+yesterdayFormat= Yesterday ({0})
+dayFormat= {0}
+
+closeButton.label=Close
+
+noLocalHistoryError= No local history available for selected resource.
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceAction.java
new file mode 100644
index 000000000..e6899c1bf
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceAction.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Aleksandra Wozniak 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:
+ * Aleksandra Wozniak (aleksandra.k.wozniak@gmail.com) - initial implementation
+ * IBM Corporation - maintenance
+ *******************************************************************************/
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+/**
+ * The "Compare with other resource" action.
+ *
+ * @deprecated Temporarily replaced by CompareWithOtherResourceHandler. See bug
+ * 264498.
+ */
+public class CompareWithOtherResourceAction extends CompareAction {
+
+ public void run(ISelection selection) {
+ // Show CompareWithOtherResourceDialog which return resources to compare
+ // and ancestor if specified. Don't need to display the other dialog
+ showSelectAncestorDialog = false;
+ super.run(selection);
+ }
+
+ protected boolean isEnabled(ISelection selection) {
+ int selectionSize = 0;
+ if (selection instanceof IStructuredSelection) {
+ selectionSize = ((IStructuredSelection) selection).toArray().length;
+ }
+ // enable for a single selection
+ return super.isEnabled(selection) || selectionSize == 1;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceDialog.java
new file mode 100644
index 000000000..b5debc26f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceDialog.java
@@ -0,0 +1,838 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Aleksandra Wozniak 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:
+ * Aleksandra Wozniak (aleksandra.k.wozniak@gmail.com) - initial implementation
+ * IBM Corporation - Bug 73923 (major refactoring and adjustments)
+ * IBM Corporation - Bug 241649 - [Dialogs] Resizing of the "compare with other" dialog
+ *******************************************************************************/
+package org.eclipse.compare.internal;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+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.DirectoryDialog;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.events.ExpansionAdapter;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.part.ResourceTransfer;
+
+/**
+ * This is a dialog that can invoke the compare editor on chosen files.
+ */
+public class CompareWithOtherResourceDialog extends TitleAreaDialog {
+
+ private int MIN_WIDTH = 320;
+ private int MIN_HEIGHT_WITH_ANCESTOR = 320;
+ private int MIN_HEIGHT_WITHOUT_ANCESTOR = 238;
+
+ private class FileTextDragListener implements DragSourceListener {
+
+ private ContentTypeElement element;
+
+ public FileTextDragListener(ContentTypeElement element) {
+ this.element = element;
+ }
+
+ public void dragFinished(DragSourceEvent event) {
+ element.setText(""); //$NON-NLS-1$
+ }
+
+ public void dragSetData(DragSourceEvent event) {
+ event.data = element.getText();
+ }
+
+ public void dragStart(DragSourceEvent event) {
+ if (element.getText() == null)
+ event.doit = false;
+ }
+ }
+
+ private class FileTextDropListener implements DropTargetListener {
+
+ private ContentTypeElement element;
+ private ResourceTransfer resourceTransfer;
+ private TextTransfer textTransfer;
+
+ public FileTextDropListener(ContentTypeElement element) {
+ this.element = element;
+ resourceTransfer = ResourceTransfer.getInstance();
+ textTransfer = TextTransfer.getInstance();
+ }
+
+ public void dragEnter(DropTargetEvent event) {
+
+ if (event.detail == DND.DROP_DEFAULT) {
+ if ((event.operations & DND.DROP_COPY) != 0)
+ event.detail = DND.DROP_COPY;
+ else
+ event.detail = DND.DROP_NONE;
+ }
+
+ for (int i = 0; i < event.dataTypes.length; i++) {
+ if (resourceTransfer.isSupportedType(event.dataTypes[i])
+ || textTransfer.isSupportedType(event.dataTypes[i])) {
+ event.currentDataType = event.dataTypes[i];
+ if (event.detail != DND.DROP_COPY)
+ event.detail = DND.DROP_NONE;
+ break;
+ }
+ }
+ }
+
+ public void dragLeave(DropTargetEvent event) {
+ // intentionally empty
+ }
+
+ public void dragOperationChanged(DropTargetEvent event) {
+
+ if (event.detail == DND.DROP_DEFAULT) {
+ if ((event.operations & DND.DROP_COPY) != 0)
+ event.detail = DND.DROP_COPY;
+ else
+ event.detail = DND.DROP_NONE;
+ } else if (resourceTransfer.isSupportedType(event.currentDataType)) {
+ if (event.detail != DND.DROP_COPY)
+ event.detail = DND.DROP_NONE;
+ }
+ }
+
+ public void dragOver(DropTargetEvent event) {
+ // intentionally empty
+ }
+
+ public void drop(DropTargetEvent event) {
+
+ if (textTransfer.isSupportedType(event.currentDataType)) {
+ String txt = (String) event.data;
+ IResource r = ResourcesPlugin.getWorkspace().getRoot().findMember(txt);
+ if (r != null)
+ element.setResource(r);
+ } else if (resourceTransfer.isSupportedType(event.currentDataType)) {
+ IResource[] files = (IResource[]) event.data;
+ if (files.length > 0)
+ element.setResource(files[0]);
+ }
+
+ }
+
+ public void dropAccept(DropTargetEvent event) {
+ // intentionally empty
+ }
+
+ }
+
+ private abstract class ContentTypeElement {
+
+ private Button radioButton;
+ protected Button mainButton;
+ protected Text text;
+ private String type;
+ protected InternalSection section;
+ private IResource resource;
+
+ public ContentTypeElement(Composite parent, String type, InternalSection section) {
+ this.type = type;
+ this.section = section;
+ createContents(parent);
+ }
+
+ private void createContents(Composite parent) {
+ createRadioButton(parent);
+ createText(parent);
+ createMainButton(parent);
+ }
+
+ private void createRadioButton(Composite parent) {
+ radioButton = new Button(parent, SWT.RADIO);
+ radioButton.setText(type);
+ }
+
+ protected void createText(Composite parent) {
+ text = new Text(parent, SWT.BORDER);
+ text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ text.setEditable(false);
+ }
+
+ protected void createMainButton(Composite parent) {
+ mainButton = new Button(parent, SWT.PUSH);
+ mainButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ }
+
+ protected Button getRadioButton() {
+ return radioButton;
+ }
+
+ protected String getText() {
+ return text.getText();
+ }
+
+ protected void setText(String string) {
+ text.setText(string);
+ }
+
+ protected void setEnabled(boolean enabled) {
+ radioButton.setSelection(enabled);
+ mainButton.setEnabled(enabled);
+ text.setEnabled(enabled);
+ }
+
+ protected void setResource(IResource resource) {
+ this.resource = resource;
+ section.setResource(resource);
+ }
+
+ public IResource getResource() {
+ return resource;
+ }
+
+ void clearResource() {
+ resource = null;
+ text.setText(""); //$NON-NLS-1$
+ }
+
+ }
+
+ private class WorkspaceContent extends ContentTypeElement {
+
+ public WorkspaceContent(Composite parent, InternalSection section) {
+ super(parent, CompareMessages.CompareWithOtherResourceDialog_workspaceRadioButton, section);
+ }
+
+ protected void createMainButton(Composite parent) {
+ super.createMainButton(parent);
+ mainButton.setText(CompareMessages.CompareWithOtherResourceDialog_workspaceMainButton);
+ // temporarily hide this button. For more information about supporting for browsing workspace see bug 243744.
+ mainButton.setVisible(false);
+ }
+
+ protected void createText(Composite parent) {
+
+ super.createText(parent);
+ text.setEditable(true);
+
+ text.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ section.setResource(text.getText());
+ updateErrorInfo();
+ }
+ });
+
+ text.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ public void widgetSelected(SelectionEvent e) {
+ section.setResource(text.getText());
+ updateErrorInfo();
+ }
+ });
+
+ initDrag();
+ initDrop();
+ }
+
+ protected void setResource(IResource resource) {
+ super.setResource(resource);
+ text.setText(resource.getFullPath().toOSString());
+ }
+
+ protected void initDrag() {
+ DragSource source = new DragSource(text, DND.DROP_MOVE
+ | DND.DROP_COPY | DND.DROP_DEFAULT);
+ Transfer[] types = new Transfer[] { TextTransfer.getInstance(),
+ ResourceTransfer.getInstance() };
+ source.setTransfer(types);
+ source.addDragListener(new FileTextDragListener(this));
+ }
+
+ protected void initDrop() {
+ DropTarget target = new DropTarget(text, DND.DROP_MOVE
+ | DND.DROP_COPY | DND.DROP_DEFAULT);
+ Transfer[] types = new Transfer[] { TextTransfer.getInstance(),
+ ResourceTransfer.getInstance() };
+ target.setTransfer(types);
+ target.addDropListener(new FileTextDropListener(this));
+ }
+
+ }
+
+ private class ExternalFileContent extends ContentTypeElement {
+
+ public ExternalFileContent(Composite parent, InternalSection section) {
+ super(parent, CompareMessages.CompareWithOtherResourceDialog_externalFileRadioButton, section);
+ }
+
+ protected void createMainButton(Composite parent) {
+ super.createMainButton(parent);
+ mainButton.setText(CompareMessages.CompareWithOtherResourceDialog_externalFileMainButton);
+ mainButton.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ public void widgetSelected(SelectionEvent e) {
+ IResource r = tmpProject.getExternalFile();
+ if (r == null)
+ return;
+ setResource(r);
+ }
+ });
+ }
+
+ protected void setResource(IResource resource) {
+ super.setResource(resource);
+ text.setText(resource.getLocation().toOSString());
+ }
+
+ }
+
+ private class ExternalFolderContent extends ContentTypeElement {
+
+ public ExternalFolderContent(Composite parent, InternalSection section) {
+ super(parent, CompareMessages.CompareWithOtherResourceDialog_externalFolderRadioButton, section);
+ }
+
+ protected void createMainButton(Composite parent) {
+ super.createMainButton(parent);
+ mainButton.setText(CompareMessages.CompareWithOtherResourceDialog_externalFolderMainButton);
+ mainButton.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ public void widgetSelected(SelectionEvent e) {
+ IResource r = tmpProject.getExternalFolder();
+ if (r == null)
+ return;
+ setResource(r);
+ }
+ });
+ }
+
+ protected void setResource(IResource resource) {
+ super.setResource(resource);
+ text.setText(resource.getLocation().toOSString());
+ }
+
+ }
+
+ private abstract class InternalSection {
+
+ // there is no "enum" support in Java 1.4. Sigh...
+ public static final int WORKSPACE = 0;
+ public static final int EXTERNAL_FILE = 1;
+ public static final int EXTERNAL_FOLDER = 2;
+
+ protected Group group;
+ private IResource resource;
+
+ ExternalFileContent externalFileContent;
+ ExternalFolderContent externalFolderContent;
+ WorkspaceContent workspaceContent;
+
+ private InternalSection() {
+ // not to instantiate
+ }
+
+ protected void createContents(Composite parent) {
+
+ group = new Group(parent, SWT.NONE);
+ group.setLayout(new GridLayout(3, false));
+ group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ workspaceContent = new WorkspaceContent(group, this);
+ externalFileContent = new ExternalFileContent(group, this);
+ externalFolderContent = new ExternalFolderContent(group, this);
+
+ addListenersToRadioButtons();
+ }
+
+ private void addListenersToRadioButtons() {
+ final ContentTypeElement[] elements = new ContentTypeElement[] { workspaceContent,
+ externalFileContent, externalFolderContent };
+ for (int i = 0; i < elements.length; i++) {
+ elements[i].getRadioButton().addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ for (int j = 0; j < elements.length; j++) {
+ if (event.widget != elements[j].getRadioButton())
+ elements[j].setEnabled(false);
+ else {
+ elements[j].setEnabled(true);
+ setResource(elements[j].getResource());
+ }
+ }
+ }
+ });
+ }
+ }
+
+ protected IResource getResource() {
+ return resource;
+ }
+
+ protected void setResource(IResource resource) {
+ this.resource = resource;
+ updateErrorInfo();
+ }
+
+ protected void setResource(String s) {
+ IResource tmp = ResourcesPlugin.getWorkspace().getRoot()
+ .findMember(s);
+ if (tmp instanceof IWorkspaceRoot)
+ resource = null;
+ else
+ resource = tmp;
+ updateErrorInfo();
+ }
+
+ protected void clearResource() {
+ resource = null;
+ workspaceContent.clearResource();
+ externalFileContent.clearResource();
+ externalFolderContent.clearResource();
+ updateErrorInfo();
+ }
+
+ protected void setContentType(int type) {
+ switch(type) {
+ case WORKSPACE:
+ workspaceContent.setEnabled(true);
+ externalFileContent.setEnabled(false);
+ externalFolderContent.setEnabled(false);
+ break;
+ case EXTERNAL_FILE:
+ workspaceContent.setEnabled(false);
+ externalFileContent.setEnabled(true);
+ externalFolderContent.setEnabled(false);
+ break;
+ case EXTERNAL_FOLDER:
+ workspaceContent.setEnabled(false);
+ externalFileContent.setEnabled(false);
+ externalFolderContent.setEnabled(true);
+ }
+ }
+ }
+
+ private class InternalGroup extends InternalSection {
+
+ public InternalGroup(Composite parent) {
+ createContents(parent);
+ }
+
+ public void setText(String text) {
+ group.setText(text);
+ }
+
+ public void setLayoutData(GridData layoutData) {
+ group.setLayoutData(layoutData);
+ }
+ }
+
+ private class InternalExpandable extends InternalSection {
+
+ private ExpandableComposite expandable;
+ private Button clearButton;
+
+ public InternalExpandable(Composite parent) {
+ createContents(parent);
+ }
+
+ protected void createContents(Composite parent) {
+ final Composite p = parent;
+ expandable = new ExpandableComposite(parent, SWT.NONE,
+ ExpandableComposite.TREE_NODE | ExpandableComposite.TWISTIE);
+ super.createContents(expandable);
+ createClearButton(group);
+ expandable.setClient(group);
+ expandable.addExpansionListener(new ExpansionAdapter() {
+ public void expansionStateChanged(ExpansionEvent e) {
+ p.layout();
+ adjustSize(e.getState());
+ }
+ });
+ }
+
+ private void createClearButton(Composite parent) {
+ clearButton = new Button(parent, SWT.PUSH);
+ clearButton.setText(CompareMessages.CompareWithOtherResourceDialog_clear);
+ clearButton.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ public void widgetSelected(SelectionEvent e) {
+ clearResource();
+ }
+ });
+ }
+
+ public void setText(String text) {
+ expandable.setText(text);
+ group.setText(text);
+ }
+
+ public void setLayoutData(GridData layoutData) {
+ expandable.setLayoutData(layoutData);
+ }
+ }
+
+ private class ExternalResourcesProject {
+
+ // Implementation based on org.eclipse.jdt.internal.core.ExternalFoldersManager
+
+ private int counter = 0;
+
+ private static final String TMP_PROJECT_NAME = ".org.eclipse.compare.tmp"; //$NON-NLS-1$
+
+ private final static String TMP_PROJECT_FILE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" //$NON-NLS-1$
+ + "<projectDescription>\n" //$NON-NLS-1$
+ + "\t<name>" + TMP_PROJECT_NAME + "\t</name>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + "\t<comment></comment>\n" //$NON-NLS-1$
+ + "\t<projects>\n" //$NON-NLS-1$
+ + "\t</projects>\n" //$NON-NLS-1$
+ + "\t<buildSpec>\n" //$NON-NLS-1$
+ + "\t</buildSpec>\n" //$NON-NLS-1$
+ + "\t<natures>\n" + "\t</natures>\n" //$NON-NLS-1$//$NON-NLS-2$
+ + "</projectDescription>"; //$NON-NLS-1$
+
+ private final static String TMP_FOLDER_NAME = "tmpFolder"; //$NON-NLS-1$
+
+ private ExternalResourcesProject() {
+ // nothing to do here
+ }
+
+ private IProject createTmpProject() throws CoreException {
+ IProject project = getTmpProject();
+ if (!project.isAccessible()) {
+ try {
+ IPath stateLocation = CompareUI.getPlugin().getStateLocation();
+ if (!project.exists()) {
+ IProjectDescription desc = project.getWorkspace()
+ .newProjectDescription(project.getName());
+ desc.setLocation(stateLocation.append(TMP_PROJECT_NAME));
+ project.create(desc, null);
+ }
+ try {
+ project.open(null);
+ } catch (CoreException e) { // in case .project file or folder has been deleted
+ IPath projectPath = stateLocation.append(TMP_PROJECT_NAME);
+ projectPath.toFile().mkdirs();
+ FileOutputStream output = new FileOutputStream(
+ projectPath.append(".project").toOSString()); //$NON-NLS-1$
+ try {
+ output.write(TMP_PROJECT_FILE.getBytes());
+ } finally {
+ output.close();
+ }
+ project.open(null);
+ }
+ getTmpFolder(project);
+ } catch (IOException ioe) {
+ return project;
+ } catch (CoreException ce) {
+ throw new CoreException(ce.getStatus());
+ }
+ }
+ project.setHidden(true);
+ return project;
+ }
+
+ private IFolder getTmpFolder(IProject project) throws CoreException {
+ IFolder folder = project.getFolder(TMP_FOLDER_NAME);
+ if (!folder.exists())
+ folder.create(IResource.NONE, true, null);
+ return folder;
+ }
+
+ private IFile getExternalFile() {
+ FileDialog dialog = new FileDialog(getShell());
+ String path = dialog.open();
+ if (path != null)
+ return (IFile) linkResource(new Path(path));
+ return null;
+ }
+
+ private IFolder getExternalFolder() {
+ DirectoryDialog dialog = new DirectoryDialog(getShell());
+ String path = dialog.open();
+ if (path != null)
+ return (IFolder) linkResource(new Path(path));
+ return null;
+ }
+
+ private IResource linkResource(IPath path) {
+ IResource r = null;
+ String resourceName = path.lastSegment();
+ try {
+ IProject project = createTmpProject();
+ if (!project.isOpen())
+ project.open(null);
+ if (path.toFile().isFile()) {
+ r = getTmpFolder(project).getFile(resourceName);
+ if (r.exists()) { // add a number to file's name when there already is a file with that name in a folder
+ String extension = path.getFileExtension();
+ String fileName = path.removeFileExtension().lastSegment();
+ r = getTmpFolder(project).getFile(getName(fileName, extension));
+ }
+ ((IFile)r).createLink(path, IResource.REPLACE, null);
+ } else { // isDirectory
+ r = getTmpFolder(project).getFolder(resourceName);
+ if (r.exists()) {
+ r = getTmpFolder(project).getFolder(getName(resourceName, null));
+ }
+ ((IFolder)r).createLink(path, IResource.REPLACE, null);
+ }
+ } catch (CoreException e) {
+ CompareUIPlugin.log(e);
+ MessageDialog.openError(getShell(),
+ CompareMessages.CompareWithOtherResourceDialog_externalFile_errorTitle,
+ CompareMessages.CompareWithOtherResourceDialog_externalFile_errorMessage);
+ }
+ return r;
+ }
+
+ /**
+ * This method is used to prevent duplicating names of linked resources.
+ * It adds a suffix based on the <code>counter</code> value.
+ *
+ * @param name
+ * @param extension optional
+ * @return
+ */
+ private String getName(String name, String extension) {
+ if (counter != 0) {
+ name = name + "-" + counter; //$NON-NLS-1$
+ }
+ // at most 3 resources at the same time with the same name:
+ // left, right, ancestor
+ counter = (counter + 1) % 3;
+ if (extension != null) {
+ name += "." + extension; //$NON-NLS-1$
+ }
+ // don't change the name if counter equals 0
+ return name;
+ }
+
+ private IProject getTmpProject() {
+ return ResourcesPlugin.getWorkspace().getRoot().getProject(
+ TMP_PROJECT_NAME);
+ }
+ }
+
+ private Button okButton;
+ private InternalGroup rightPanel, leftPanel;
+ private InternalExpandable ancestorPanel;
+ private ISelection selection;
+ private ExternalResourcesProject tmpProject = new ExternalResourcesProject();
+
+ /**
+ * Creates the dialog.
+ *
+ * @param shell
+ * a shell
+ * @param selection
+ * if the selection is not null, it will be set as initial files
+ * for comparison
+ * @since 3.4
+ */
+ protected CompareWithOtherResourceDialog(Shell shell, ISelection selection) {
+ super(shell);
+ setShellStyle(SWT.MODELESS | SWT.RESIZE | SWT.MAX);
+ this.selection = selection;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ protected Control createDialogArea(Composite parent) {
+
+ Composite mainPanel = new Composite(parent, SWT.NULL);
+ mainPanel.setLayout(new GridLayout(1, true));
+ mainPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ ancestorPanel = new InternalExpandable(mainPanel);
+ ancestorPanel.setText(CompareMessages.CompareWithOtherResourceDialog_ancestor);
+ GridData ancestorGD = new GridData(SWT.FILL, SWT.FILL, true, false);
+ ancestorPanel.setLayoutData(ancestorGD);
+
+ leftPanel = new InternalGroup(mainPanel);
+ leftPanel.setText(CompareMessages.CompareWithOtherResourceDialog_leftPanel);
+ leftPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ rightPanel = new InternalGroup(mainPanel);
+ rightPanel.setText(CompareMessages.CompareWithOtherResourceDialog_rightPanel);
+ rightPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ setSelection(selection);
+ getShell().setText(CompareMessages.CompareWithOtherResourceDialog_dialogTitle);
+ setTitle(CompareMessages.CompareWithOtherResourceDialog_dialogMessage);
+ adjustSize(ancestorPanel.expandable.isExpanded());
+
+ return mainPanel;
+ }
+
+ private void adjustSize(boolean expanded) {
+ int minWidth = convertHorizontalDLUsToPixels(MIN_WIDTH);
+ int minHeight = convertVerticalDLUsToPixels(expanded ? MIN_HEIGHT_WITH_ANCESTOR
+ : MIN_HEIGHT_WITHOUT_ANCESTOR);
+ getShell().setMinimumSize(minWidth, minHeight);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse
+ * .swt.widgets.Composite)
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+ super.createButtonsForButtonBar(parent);
+ okButton = getButton(IDialogConstants.OK_ID);
+ updateErrorInfo();
+ setMessage(CompareMessages.CompareWithOtherResourceDialog_info);
+ }
+
+ private void setSelection(ISelection selection) {
+ IResource[] selectedResources = Utilities.getResources(selection);
+ switch (selectedResources.length) {
+ case 1:
+ leftPanel.workspaceContent.setResource(selectedResources[0]);
+ break;
+ case 2:
+ leftPanel.workspaceContent.setResource(selectedResources[0]);
+ rightPanel.workspaceContent.setResource(selectedResources[1]);
+ break;
+ case 3:
+ ancestorPanel.workspaceContent.setResource(selectedResources[0]);
+ ancestorPanel.expandable.setExpanded(true);
+ leftPanel.workspaceContent.setResource(selectedResources[1]);
+ rightPanel.workspaceContent.setResource(selectedResources[2]);
+ break;
+ }
+ setInitialContentTypes();
+ }
+
+ private void setInitialContentTypes() {
+ ancestorPanel.setContentType(InternalSection.WORKSPACE);
+ leftPanel.setContentType(InternalSection.WORKSPACE);
+ rightPanel.setContentType(InternalSection.WORKSPACE);
+ }
+
+ private boolean isComparePossible() {
+ IResource[] resources;
+ if (ancestorPanel.getResource() == null)
+ resources = new IResource[] { leftPanel.getResource(),
+ rightPanel.getResource() };
+ else
+ resources = new IResource[] { ancestorPanel.getResource(),
+ leftPanel.getResource(), rightPanel.getResource() };
+
+ ResourceCompareInput r = new ResourceCompareInput(
+ new CompareConfiguration());
+ return r.isEnabled(new StructuredSelection(resources));
+ }
+
+ private void updateErrorInfo() {
+ if (okButton != null) {
+ if (leftPanel.getResource() == null
+ || rightPanel.getResource() == null) {
+ setMessage(CompareMessages.CompareWithOtherResourceDialog_error_empty,
+ IMessageProvider.ERROR);
+ okButton.setEnabled(false);
+ } else if (!isComparePossible()) {
+ setMessage(
+ CompareMessages.CompareWithOtherResourceDialog_error_not_comparable,
+ IMessageProvider.ERROR);
+ okButton.setEnabled(false);
+ } else {
+ setMessage(CompareMessages.CompareWithOtherResourceDialog_info);
+ okButton.setEnabled(true);
+ }
+ }
+ }
+
+ /**
+ * Returns table with selected resources. If any resource wasn't chosen in
+ * the ancestor panel, table has only two elements -- resources chosen in
+ * left and right panel. In the other case table contains all three
+ * resources.
+ *
+ * @return table with selected resources
+ */
+ public IResource[] getResult() {
+ IResource[] resources;
+ IResource rightResource = rightPanel.getResource();
+ IResource leftResource = leftPanel.getResource();
+ IResource ancestorResource = ancestorPanel.getResource();
+ if (ancestorResource == null)
+ resources = new IResource[] { leftResource, rightResource };
+ else
+ resources = new IResource[] { ancestorResource, leftResource,
+ rightResource };
+ return resources;
+ }
+
+ /*
+ * @see org.eclipse.jface.dialogs.Dialog#getDialogBoundsSettings()
+ */
+ protected IDialogSettings getDialogBoundsSettings() {
+ String sectionName = getClass().getName() + "_dialogBounds"; //$NON-NLS-1$
+ IDialogSettings settings = CompareUIPlugin.getDefault()
+ .getDialogSettings();
+ IDialogSettings section = settings.getSection(sectionName);
+ if (section == null)
+ section = settings.addNewSection(sectionName);
+ return section;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceHandler.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceHandler.java
new file mode 100644
index 000000000..43a55ee83
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareWithOtherResourceHandler.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * This is a temporary replacement for CompareWithOtherResourceAction which was
+ * available from "Compare With > Other Resource...". See bug 264498.
+ */
+public class CompareWithOtherResourceHandler extends AbstractHandler {
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+ IWorkbenchPage workbenchPage = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
+
+ // CompareAction#isEnabled(ISelection)
+ CompareConfiguration cc = new CompareConfiguration();
+ cc.setProperty(CompareEditor.CONFIRM_SAVE_PROPERTY, new Boolean(false));
+ ResourceCompareInput input = new ResourceCompareInput(cc);
+
+ int selectionSize = 0;
+ if (selection instanceof IStructuredSelection) {
+ selectionSize = ((IStructuredSelection) selection).toArray().length;
+ }
+ if (input.isEnabled(selection) || selectionSize == 1) {
+
+ // CompareAction#run(ISelection)
+ if (!input.setSelection(selection, workbenchPage.getWorkbenchWindow().getShell(), false))
+ return null;
+ input.initializeCompareConfiguration();
+ CompareUI.openCompareEditorOnPage(input, workbenchPage);
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ContentChangeNotifier.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ContentChangeNotifier.java
new file mode 100644
index 000000000..9aee9c259
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ContentChangeNotifier.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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 org.eclipse.compare.IContentChangeListener;
+import org.eclipse.compare.IContentChangeNotifier;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * A helper class for managing content change notification.
+ */
+public class ContentChangeNotifier implements IContentChangeNotifier {
+
+ private ListenerList fListenerList;
+ private final IContentChangeNotifier element;
+
+ public ContentChangeNotifier(IContentChangeNotifier element) {
+ this.element = element;
+ }
+
+ /* (non-Javadoc)
+ * see IContentChangeNotifier.addChangeListener
+ */
+ public void addContentChangeListener(IContentChangeListener listener) {
+ if (fListenerList == null)
+ fListenerList= new ListenerList();
+ fListenerList.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * see IContentChangeNotifier.removeChangeListener
+ */
+ public void removeContentChangeListener(IContentChangeListener listener) {
+ if (fListenerList != null) {
+ fListenerList.remove(listener);
+ if (fListenerList.isEmpty())
+ fListenerList= null;
+ }
+ }
+
+ /**
+ * Notifies all registered <code>IContentChangeListener</code>s of a content change.
+ */
+ public void fireContentChanged() {
+ if (isEmpty()) {
+ return;
+ }
+ // Legacy listeners may expect to be notified in the UI thread.
+ Runnable runnable = new Runnable() {
+ public void run() {
+ Object[] listeners= fListenerList.getListeners();
+ for (int i= 0; i < listeners.length; i++) {
+ final IContentChangeListener contentChangeListener = (IContentChangeListener)listeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ public void run() throws Exception {
+ contentChangeListener.contentChanged(element);
+ }
+ public void handleException(Throwable exception) {
+ // Logged by safe runner
+ }
+ });
+ }
+ }
+ };
+ if (Display.getCurrent() == null) {
+ Display.getDefault().syncExec(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ /**
+ * Return whether this notifier is empty (i.e. has no listeners).
+ * @return whether this notifier is empty
+ */
+ public boolean isEmpty() {
+ return fListenerList == null || fListenerList.isEmpty();
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImageDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImageDescriptor.java
new file mode 100644
index 000000000..a0ac41e04
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImageDescriptor.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 org.eclipse.swt.graphics.*;
+
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * Combines an image with an overlay.
+ */
+public class DiffImageDescriptor extends CompositeImageDescriptor {
+
+ static final int HEIGHT= 16;
+
+ private final ImageData fBaseImageData;
+ private final ImageDescriptor fOverlayImage;
+ private final int fWidth;
+ private final boolean fLeft;
+ private final int hashCode;
+
+ public DiffImageDescriptor(Image base, ImageDescriptor overlay, int w, boolean onLeft) {
+ ImageData data = null;
+ if (base != null) {
+ data = base.getImageData();
+ if (data == null)
+ data = DEFAULT_IMAGE_DATA;
+ }
+ fBaseImageData = data;
+ fOverlayImage= overlay;
+ fWidth= w;
+ fLeft= onLeft;
+ hashCode = calculateHashCode();
+ }
+
+ private int calculateHashCode() {
+ int h1 = 0;
+ int h2 = 0;
+ if (fBaseImageData != null) {
+ h1 = calculateHash(fBaseImageData);
+ }
+ if (fOverlayImage != null) {
+ h2 = fOverlayImage.hashCode();
+ }
+ return h1 + h2 + fWidth;
+ }
+
+ private int calculateHash(ImageData baseImageData) {
+ byte[] data = baseImageData.data;
+ int hash = baseImageData.width + baseImageData.height;
+ for (int i = 0; i < data.length; i++) {
+ byte b = data[i];
+ hash >>>= 1;
+ hash ^= b;
+ }
+ return hash;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.resource.CompositeImageDescriptor#getSize()
+ */
+ protected Point getSize() {
+ return new Point(fWidth, HEIGHT);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.resource.CompositeImageDescriptor#drawCompositeImage(int, int)
+ */
+ protected void drawCompositeImage(int width, int height) {
+ if (fLeft) {
+ if (fBaseImageData != null) {
+ drawImage(fBaseImageData, fWidth - fBaseImageData.width, 0);
+ }
+
+ if (fOverlayImage != null) {
+ ImageData overlay= fOverlayImage.getImageData();
+ if (overlay == null)
+ overlay= DEFAULT_IMAGE_DATA;
+ drawImage(overlay, 0, (HEIGHT - overlay.height) / 2);
+ }
+ } else {
+ if (fBaseImageData != null) {
+ drawImage(fBaseImageData, 0, 0);
+ }
+
+ if (fOverlayImage != null) {
+ ImageData overlay= fOverlayImage.getImageData();
+ if (overlay == null)
+ overlay= DEFAULT_IMAGE_DATA;
+ drawImage(overlay, fWidth - overlay.width, (HEIGHT - overlay.height) / 2);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return hashCode;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj instanceof DiffImageDescriptor) {
+ DiffImageDescriptor other = (DiffImageDescriptor) obj;
+ return (other.hashCode == hashCode
+ && isEqual(other.fOverlayImage, fOverlayImage)
+ && other.fWidth == fWidth
+ && other.fLeft == fLeft
+ && isEqual(other.fBaseImageData, fBaseImageData));
+ }
+ return false;
+ }
+
+ private boolean isEqual(ImageData i1, ImageData i2) {
+ if (isEqual((Object) i1, (Object) i2)) {
+ return true;
+ }
+ if (i1 == null || i2 == null)
+ return false;
+ return (i1.width == i2.width && i1.height == i2.height
+ && i1.depth == i2.depth && i1.scanlinePad == i2.scanlinePad
+ && i1.bytesPerLine == i2.bytesPerLine
+ /* && i1.palette == i2.palette */
+ && i1.transparentPixel == i2.transparentPixel
+ && i1.maskPad == i2.maskPad
+ && i1.alpha == i2.alpha
+ && i1.type == i2.type && i1.x == i2.x && i1.y == i2.y
+ && i1.disposalMethod == i2.disposalMethod && i1.delayTime == i2.delayTime
+ && equals(i1.data,i2.data) && equals(i1.maskData, i2.maskData)
+ && equals(i1.alphaData, i2.alphaData));
+ }
+
+ private boolean equals(byte[] data, byte[] data2) {
+ if (isEqual(data, data2))
+ return true;
+ if (data == null || data2 == null)
+ return false;
+ if (data.length != data2.length)
+ return false;
+ for (int i = 0; i < data2.length; i++) {
+ if (data[i] != data2[i])
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isEqual(Object o1, Object o2) {
+ if (o1 == o2)
+ return true;
+ if (o1 == null || o2 == null)
+ return false;
+ return o1.equals(o2);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java
new file mode 100644
index 000000000..8686bbbb3
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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 org.eclipse.jface.text.*;
+import org.eclipse.compare.contentmergeviewer.ITokenComparator;
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+
+/**
+ * Implements the <code>IRangeComparator</code> interface for lines in a document.
+ * A <code>DocLineComparator</code> is used as the input for the <code>RangeDifferencer</code>
+ * engine to perform a line oriented compare on documents.
+ * <p>
+ * A <code>DocLineComparator</code> doesn't know anything about line separators because
+ * its notion of lines is solely defined in the underlying <code>IDocument</code>.
+ */
+public class DocLineComparator implements ITokenComparator {
+
+ private IDocument fDocument;
+ private int fLineOffset;
+ private int fLineCount;
+ private int fLength;
+ private boolean fIgnoreWhiteSpace;
+
+ /**
+ * Creates a <code>DocLineComparator</code> for the given document range.
+ * ignoreWhiteSpace controls whether comparing lines (in method
+ * <code>rangesEqual<code>) should ignore whitespace.
+ *
+ * @param document the document from which the lines are taken
+ * @param region if non-<code>null</code> only lines within this range are taken
+ * @param ignoreWhiteSpace if <code>true</code> white space is ignored when comparing lines
+ */
+ public DocLineComparator(IDocument document, IRegion region,
+ boolean ignoreWhiteSpace) {
+ fDocument = document;
+ fIgnoreWhiteSpace = ignoreWhiteSpace;
+
+ fLineOffset = 0;
+ if (region != null) {
+ fLength = region.getLength();
+ int start = region.getOffset();
+ try {
+ fLineOffset = fDocument.getLineOfOffset(start);
+ } catch (BadLocationException ex) {
+ // silently ignored
+ }
+
+ if (fLength == 0) {
+ // optimization, empty documents have one line
+ fLineCount = 1;
+ } else {
+ int endLine = fDocument.getNumberOfLines();
+ try {
+ endLine = fDocument.getLineOfOffset(start + fLength);
+ } catch (BadLocationException ex) {
+ // silently ignored
+ }
+ fLineCount = endLine - fLineOffset + 1;
+ }
+ } else {
+ fLength = document.getLength();
+ fLineCount = fDocument.getNumberOfLines();
+ }
+ }
+
+ /**
+ * Returns the number of lines in the document.
+ *
+ * @return number of lines
+ */
+ public int getRangeCount() {
+ return fLineCount;
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenStart
+ */
+ public int getTokenStart(int line) {
+ try {
+ IRegion r= fDocument.getLineInformation(fLineOffset + line);
+ return r.getOffset();
+ } catch (BadLocationException ex) {
+ return fDocument.getLength();
+ }
+ }
+
+ /* (non Javadoc)
+ * Returns the length of the given line.
+ * see ITokenComparator.getTokenLength
+ */
+ public int getTokenLength(int line) {
+ return getTokenStart(line+1) - getTokenStart(line);
+ }
+
+ /**
+ * Returns <code>true</code> if a line given by the first index
+ * matches a line specified by the other <code>IRangeComparator</code> and index.
+ *
+ * @param thisIndex the number of the line within this range comparator
+ * @param otherComparator the range comparator to compare this with
+ * @param otherIndex the number of the line within the other comparator
+ * @return <code>true</code> if the lines are equal
+ */
+ public boolean rangesEqual(int thisIndex, IRangeComparator otherComparator, int otherIndex) {
+
+ if (otherComparator != null && otherComparator.getClass() == getClass()) {
+ DocLineComparator other= (DocLineComparator) otherComparator;
+
+ if (fIgnoreWhiteSpace) {
+ String s1= extract(thisIndex);
+ String s2= other.extract(otherIndex);
+ //return s1.trim().equals(s2.trim());
+ return compare(s1, s2);
+ }
+
+ int tlen= getTokenLength(thisIndex);
+ int olen= other.getTokenLength(otherIndex);
+ if (tlen == olen) {
+ String s1= extract(thisIndex);
+ String s2= other.extract(otherIndex);
+ return s1.equals(s2);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Aborts the comparison if the number of tokens is too large.
+ *
+ * @param length a number on which to base the decision whether to return
+ * <code>true</code> or <code>false</code>
+ * @param maxLength another number on which to base the decision whether to return
+ * <code>true</code> or <code>false</code>
+ * @param other the other <code>IRangeComparator</code> to compare with
+ * @return <code>true</code> to avoid a too lengthy range comparison
+ */
+ public boolean skipRangeComparison(int length, int maxLength, IRangeComparator other) {
+ return false;
+ }
+
+ //---- private methods
+
+ /**
+ * Extract a single line from the underlying document without the line separator.
+ *
+ * @param line the number of the line to extract
+ * @return the contents of the line as a String
+ */
+ private String extract(int line) {
+ if (line < fLineCount) {
+ try {
+ IRegion r= fDocument.getLineInformation(fLineOffset + line);
+ return fDocument.get(r.getOffset(), r.getLength());
+ } catch(BadLocationException e) {
+ // silently ignored
+ }
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ private boolean compare(String s1, String s2) {
+ int l1= s1.length();
+ int l2= s2.length();
+ int c1= 0, c2= 0;
+ int i1= 0, i2= 0;
+
+ while (c1 != -1) {
+
+ c1= -1;
+ while (i1 < l1) {
+ char c= s1.charAt(i1++);
+ if (! Character.isWhitespace(c)) {
+ c1= c;
+ break;
+ }
+ }
+
+ c2= -1;
+ while (i2 < l2) {
+ char c= s2.charAt(i2++);
+ if (! Character.isWhitespace(c)) {
+ c2= c;
+ break;
+ }
+ }
+
+ if (c1 != c2)
+ return false;
+ }
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocumentManager.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocumentManager.java
new file mode 100644
index 000000000..7f2dd3e9e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocumentManager.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.util.ArrayList;
+
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * No API yet.
+ */
+public class DocumentManager {
+
+ private static final boolean DEBUG= false;
+
+ private static ArrayList fgKeys= new ArrayList();
+ private static ArrayList fgValues= new ArrayList();
+
+ public static IDocument get(Object o) {
+
+ for (int i= 0; i < fgKeys.size(); i++) {
+ if (fgKeys.get(i) == o)
+ return (IDocument) fgValues.get(i);
+ }
+ return null;
+ }
+
+ public static void put(Object o, IDocument document) {
+ if (DEBUG) System.out.println("DocumentManager.put: " + document); //$NON-NLS-1$
+ for (int i= 0; i < fgKeys.size(); i++) {
+ if (fgKeys.get(i) == o) {
+ fgValues.set(i, document);
+ return;
+ }
+ }
+ fgKeys.add(o);
+ fgValues.add(document);
+ }
+
+ public static void remove(IDocument document) {
+ if (document != null) {
+ if (DEBUG) System.out.println("DocumentManager.remove: " + document); //$NON-NLS-1$
+ for (int i= 0; i < fgValues.size(); i++) {
+ if (fgValues.get(i) == document) {
+ fgKeys.remove(i);
+ fgValues.remove(i);
+ return;
+ }
+ }
+ if (DEBUG) System.out.println("DocumentManager.remove: not found"); //$NON-NLS-1$
+ }
+ }
+
+ public static void dump() {
+ if (DEBUG) System.out.println("DocumentManager: managed docs:" + fgValues.size()); //$NON-NLS-1$
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/EditionAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/EditionAction.java
new file mode 100644
index 000000000..f0adb2cb8
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/EditionAction.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ResourceBundle;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.BadLocationException;
+
+import org.eclipse.ui.*;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.IStreamContentAccessor;
+
+
+public class EditionAction extends BaseCompareAction {
+
+ /**
+ * Implements the IStreamContentAccessor and ITypedElement protocols
+ * for a Document.
+ */
+ class DocumentBufferNode implements ITypedElement, IEncodedStreamContentAccessor {
+ private static final String UTF_16= "UTF-16"; //$NON-NLS-1$
+ private IDocument fDocument;
+ private IFile fFile;
+
+ DocumentBufferNode(IDocument document, IFile file) {
+ fDocument= document;
+ fFile= file;
+ }
+
+ public String getName() {
+ return fFile.getName();
+ }
+
+ public String getType() {
+ return fFile.getFileExtension();
+ }
+
+ public Image getImage() {
+ return null;
+ }
+
+ public InputStream getContents() {
+ return new ByteArrayInputStream(Utilities.getBytes(fDocument.get(), UTF_16));
+ }
+
+ public String getCharset() {
+ return UTF_16;
+ }
+ }
+
+ private String fBundleName;
+ private boolean fReplaceMode;
+ protected boolean fPrevious= false;
+ protected String fHelpContextId;
+
+ EditionAction(boolean replaceMode, String bundleName) {
+ fReplaceMode= replaceMode;
+ fBundleName= bundleName;
+ }
+
+ protected boolean isEnabled(ISelection selection) {
+ return Utilities.getFiles(selection).length == 1; // we don't support multiple selection for now
+ }
+
+ protected void run(ISelection selection) {
+ IFile[] files= Utilities.getFiles(selection);
+ for (int i= 0; i < files.length; i++)
+ doFromHistory(files[i]);
+ }
+
+ private void doFromHistory(final IFile file) {
+
+ ResourceBundle bundle= ResourceBundle.getBundle(fBundleName);
+ String title= Utilities.getString(bundle, "title"); //$NON-NLS-1$
+
+ Shell parentShell= CompareUIPlugin.getShell();
+
+ IFileState states[]= null;
+ try {
+ states= file.getHistory(null);
+ } catch (CoreException ex) {
+ MessageDialog.openError(parentShell, title, ex.getMessage());
+ return;
+ }
+
+ if (states == null || states.length <= 0) {
+ String msg= Utilities.getString(bundle, "noLocalHistoryError"); //$NON-NLS-1$
+ MessageDialog.openInformation(parentShell, title, msg);
+ return;
+ }
+
+ ITypedElement base= new ResourceNode(file);
+
+ IDocument document= getDocument(file);
+ ITypedElement target= base;
+ if (document != null)
+ target= new DocumentBufferNode(document, file);
+
+ ITypedElement[] editions= new ITypedElement[states.length+1];
+ editions[0]= base;
+ for (int i= 0; i < states.length; i++)
+ editions[i+1]= new HistoryItem(base, states[i]);
+
+ EditionSelectionDialog d= new EditionSelectionDialog(parentShell, bundle);
+ d.setEditionTitleArgument(file.getName());
+ d.setEditionTitleImage(CompareUIPlugin.getImage(file));
+ //d.setHideIdenticalEntries(false);
+ if (fHelpContextId != null)
+ d.setHelpContextId(fHelpContextId);
+
+ if (fReplaceMode) {
+
+ ITypedElement ti= null;
+ if (fPrevious)
+ ti= d.selectPreviousEdition(target, editions, null);
+ else
+ ti= d.selectEdition(target, editions, null);
+
+ if (ti instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sa= (IStreamContentAccessor)ti;
+
+ if (Utilities.validateResource(file, parentShell, title)) {
+ try {
+
+ if (document != null)
+ updateDocument(document, sa);
+ else
+ updateWorkspace(bundle, parentShell, sa, file);
+
+ } catch (InterruptedException x) {
+ // Do nothing. Operation has been canceled by user.
+
+ } catch (InvocationTargetException x) {
+ String reason= x.getTargetException().getMessage();
+ MessageDialog.openError(parentShell, title, Utilities.getFormattedString(bundle, "replaceError", reason)); //$NON-NLS-1$
+ }
+ }
+ }
+ } else {
+ d.setCompareMode(true);
+
+ d.selectEdition(target, editions, null);
+ }
+ }
+
+ private void updateWorkspace(final ResourceBundle bundle, Shell shell,
+ final IStreamContentAccessor sa, final IFile file)
+ throws InvocationTargetException, InterruptedException {
+ WorkspaceModifyOperation operation= new WorkspaceModifyOperation() {
+ public void execute(IProgressMonitor pm) throws InvocationTargetException {
+ try {
+ String taskName= Utilities.getString(bundle, "taskName"); //$NON-NLS-1$
+ pm.beginTask(taskName, IProgressMonitor.UNKNOWN);
+ file.setContents(sa.getContents(), false, true, pm);
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ pm.done();
+ }
+ }
+ };
+
+ ProgressMonitorDialog pmdialog= new ProgressMonitorDialog(shell);
+ pmdialog.run(false, true, operation);
+ }
+
+ private void updateDocument(IDocument document, IStreamContentAccessor sa) throws InvocationTargetException {
+ try {
+ String text= Utilities.readString(sa);
+ document.replace(0, document.getLength(), text);
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ } catch (BadLocationException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ private IDocument getDocument(IFile file) {
+ IWorkbench wb= PlatformUI.getWorkbench();
+ if (wb == null)
+ return null;
+ IWorkbenchWindow[] ws= wb.getWorkbenchWindows();
+ if (ws == null)
+ return null;
+
+ FileEditorInput test= new FileEditorInput(file);
+
+ for (int i= 0; i < ws.length; i++) {
+ IWorkbenchWindow w= ws[i];
+ IWorkbenchPage[] wps= w.getPages();
+ if (wps != null) {
+ for (int j= 0; j < wps.length; j++) {
+ IWorkbenchPage wp= wps[j];
+ IEditorPart ep= wp.findEditor(test);
+ if (ep instanceof ITextEditor) {
+ ITextEditor te= (ITextEditor) ep;
+ IDocumentProvider dp= te.getDocumentProvider();
+ if (dp != null) {
+ IDocument doc= dp.getDocument(ep);
+ if (doc != null)
+ return doc;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ExceptionHandler.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ExceptionHandler.java
new file mode 100644
index 000000000..723d8cfbb
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ExceptionHandler.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+
+/**
+ * The default exception handler shows an error dialog when one of its handle methods
+ * is called. If the passed exception is a <code>CoreException</code> an error dialog
+ * pops up showing the exception's status information. For a <code>InvocationTargetException</code>
+ * a normal message dialog pops up showing the exception's message. Additionally the exception
+ * is written to the platform log.
+ */
+public class ExceptionHandler {
+
+ private static ExceptionHandler fgInstance= new ExceptionHandler();
+
+ /*
+ * Logs the given exception using the platform's logging mechanism. The exception is
+ * logged as an error with the error code <code>JavaStatusConstants.INTERNAL_ERROR</code>.
+ */
+ public static void log(Throwable t, String message) {
+ CompareUIPlugin.log(new Status(IStatus.ERROR, CompareUIPlugin.getPluginId(),
+ CompareUIPlugin.INTERNAL_ERROR, message, t));
+ }
+
+ /**
+ * Handles the given <code>CoreException</code>. The workbench shell is used as a parent
+ * for the dialog window.
+ *
+ * @param e the <code>CoreException</code> to be handled
+ * @param title the dialog window's window title
+ * @param message message to be displayed by the dialog window
+ */
+ public static void handle(CoreException e, String title, String message) {
+ handle(e, CompareUIPlugin.getShell(), title, message);
+ }
+
+ /**
+ * Handles the given <code>CoreException</code>.
+ *
+ * @param e the <code>CoreException</code> to be handled
+ * @param parent the dialog window's parent shell
+ * @param title the dialog window's window title
+ * @param message message to be displayed by the dialog window
+ */
+ public static void handle(CoreException e, Shell parent, String title, String message) {
+ fgInstance.perform(e, parent, title, message);
+ }
+
+ /**
+ * Handles the given <code>InvocationTargetException</code>. The workbench shell is used
+ * as a parent for the dialog window.
+ *
+ * @param e the <code>InvocationTargetException</code> to be handled
+ * @param title the dialog window's window title
+ * @param message message to be displayed by the dialog window
+ */
+ public static void handle(InvocationTargetException e, String title, String message) {
+ handle(e, CompareUIPlugin.getShell(), title, message);
+ }
+
+ /**
+ * Handles the given <code>InvocationTargetException</code>.
+ *
+ * @param e the <code>InvocationTargetException</code> to be handled
+ * @param parent the dialog window's parent shell
+ * @param title the dialog window's window title
+ * @param message message to be displayed by the dialog window
+ */
+ public static void handle(InvocationTargetException e, Shell parent, String title, String message) {
+ fgInstance.perform(e, parent, title, message);
+ }
+
+ //---- Hooks for subclasses to control exception handling ------------------------------------
+
+ protected void perform(CoreException e, Shell shell, String title, String message) {
+ CompareUIPlugin.log(e);
+ IStatus status= e.getStatus();
+ if (status != null) {
+ ErrorDialog.openError(shell, title, message, status);
+ } else {
+ displayMessageDialog(e, e.getMessage(), shell, title, message);
+ }
+ }
+
+ protected void perform(InvocationTargetException e, Shell shell, String title, String message) {
+ Throwable target= e.getTargetException();
+ if (target instanceof CoreException) {
+ perform((CoreException)target, shell, title, message);
+ } else {
+ CompareUIPlugin.log(e);
+ if (e.getMessage() != null && e.getMessage().length() > 0) {
+ displayMessageDialog(e, e.getMessage(), shell, title, message);
+ } else {
+ displayMessageDialog(e, target.getMessage(), shell, title, message);
+ }
+ }
+ }
+
+ //---- Helper methods -----------------------------------------------------------------------
+
+ private void displayMessageDialog(Throwable t, String exceptionMessage, Shell shell, String title, String message) {
+ StringWriter msg= new StringWriter();
+ if (message != null) {
+ msg.write(message);
+ msg.write("\n\n"); //$NON-NLS-1$
+ }
+ if (exceptionMessage == null || exceptionMessage.length() == 0)
+ msg.write(CompareMessages.ExceptionDialog_seeErrorLogMessage);
+ else
+ msg.write(exceptionMessage);
+ MessageDialog.openError(shell, title, msg.toString());
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareContextIds.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareContextIds.java
new file mode 100644
index 000000000..e2b36a5bb
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareContextIds.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.compare.CompareUI;
+
+/**
+ * Help context ids for the Compare UI.
+ * <p>
+ * This interface contains constants only; it is not intended to be implemented
+ * or extended.
+ * </p>
+ *
+ */
+public interface ICompareContextIds {
+
+ public static final String PREFIX= CompareUI.PLUGIN_ID + '.';
+
+ // Dialogs
+ public static final String EDITION_DIALOG= PREFIX + "edition_dialog_context"; //$NON-NLS-1$
+
+ public static final String COMPARE_EDITOR= PREFIX + "compare_editor_context"; //$NON-NLS-1$
+ public static final String PATCH_INPUT_WIZARD_PAGE= PREFIX + "patch_input_wizard_page_context"; //$NON-NLS-1$
+ public static final String PATCH_PREVIEW_WIZARD_PAGE= PREFIX + "patch_preview_wizard_page_context"; //$NON-NLS-1$
+ public static final String ADD_FROM_HISTORY_DIALOG= PREFIX + "add_from_history_dialog_context"; //$NON-NLS-1$
+ public static final String COMPARE_DIALOG= PREFIX + "compare_dialog_context"; //$NON-NLS-1$
+ public static final String COMPARE_WITH_EDITION_DIALOG= PREFIX + "compare_with_edition_dialog_context"; //$NON-NLS-1$
+ public static final String REPLACE_WITH_EDITION_DIALOG= PREFIX + "replace_with_edition_dialog_context"; //$NON-NLS-1$
+
+ // Viewer
+ public static final String TEXT_MERGE_VIEW= PREFIX + "text_merge_view_context"; //$NON-NLS-1$
+ public static final String IMAGE_COMPARE_VIEW= PREFIX + "image_compare_view_context"; //$NON-NLS-1$
+ public static final String BINARY_COMPARE_VIEW= PREFIX + "binary_compare_view_context"; //$NON-NLS-1$
+ public static final String DIFF_VIEW= PREFIX + "diff_view_context"; //$NON-NLS-1$
+
+ // Actions
+ public static final String GLOBAL_NEXT_DIFF_ACTION= PREFIX + "global_next_diff_action_context"; //$NON-NLS-1$
+ public static final String GLOBAL_PREVIOUS_DIFF_ACTION= PREFIX + "global_previous_diff_action_context"; //$NON-NLS-1$
+ public static final String NEXT_DIFF_ACTION= PREFIX + "next_diff_action_context"; //$NON-NLS-1$
+ public static final String PREVIOUS_DIFF_ACTION= PREFIX + "previous_diff_action_context"; //$NON-NLS-1$
+ public static final String IGNORE_WHITESPACE_ACTION= PREFIX + "ignore_whitespace_action_context"; //$NON-NLS-1$
+
+ // Preference page
+ public static final String COMPARE_PREFERENCE_PAGE= PREFIX + "compare_preference_page_context"; //$NON-NLS-1$
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareUIConstants.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareUIConstants.java
new file mode 100644
index 000000000..e36def063
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ICompareUIConstants.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 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;
+
+
+public interface ICompareUIConstants {
+ public final String PREFIX = CompareUIPlugin.getPluginId() + "."; //$NON-NLS-1$
+
+ public static final String DTOOL_NEXT= "dlcl16/next_nav.gif"; //$NON-NLS-1$
+ public static final String ETOOL_NEXT= "elcl16/next_nav.gif"; //$NON-NLS-1$
+ public static final String CTOOL_NEXT= ETOOL_NEXT;
+
+ public static final String DTOOL_PREV= "dlcl16/prev_nav.gif"; //$NON-NLS-1$
+ public static final String ETOOL_PREV= "elcl16/prev_nav.gif"; //$NON-NLS-1$
+ public static final String CTOOL_PREV= ETOOL_PREV;
+
+ public static final String HUNK_OBJ = "obj16/hunk_obj.gif"; //$NON-NLS-1$
+
+ public static final String ERROR_OVERLAY= "ovr16/error_ov.gif"; //$NON-NLS-1$
+ public static final String IS_MERGED_OVERLAY= "ovr16/merged_ov.gif"; //$NON-NLS-1$
+ public static final String REMOVED_OVERLAY= "ovr16/removed_ov.gif"; //$NON-NLS-1$
+ public static final String WARNING_OVERLAY= "ovr16/warning_ov.gif"; //$NON-NLS-1$
+
+ public static final String RETARGET_PROJECT= "eview16/compare_view.gif"; //$NON-NLS-1$
+
+ public static final String IGNORE_WHITESPACE_ENABLED= "etool16/ignorews_edit.gif"; //$NON-NLS-1$
+ public static final String IGNORE_WHITESPACE_DISABLED= "dtool16/ignorews_edit.gif"; //$NON-NLS-1$
+
+ public static final String PROP_ANCESTOR_VISIBLE = PREFIX + "AncestorVisible"; //$NON-NLS-1$
+ public static final String PROP_IGNORE_ANCESTOR = PREFIX + "IgnoreAncestor"; //$NON-NLS-1$
+ public static final String PROP_TITLE = PREFIX + "Title"; //$NON-NLS-1$
+ public static final String PROP_TITLE_IMAGE = PREFIX + "TitleImage"; //$NON-NLS-1$
+ public static final String PROP_SELECTED_EDITION = PREFIX + "SelectedEdition"; //$NON-NLS-1$
+
+ public static final int COMPARE_IMAGE_WIDTH= 22;
+
+ public static final String PREF_NAVIGATION_END_ACTION= PREFIX + "NavigationEndAction"; //$NON-NLS-1$
+ public static final String PREF_NAVIGATION_END_ACTION_LOCAL= PREFIX + "NavigationEndActionLocal"; //$NON-NLS-1$
+ public static final String PREF_VALUE_PROMPT = "prompt"; //$NON-NLS-1$
+ public static final String PREF_VALUE_LOOP = "loop"; //$NON-NLS-1$
+ public static final String PREF_VALUE_NEXT = "next"; //$NON-NLS-1$
+ public static final String PREF_VALUE_DO_NOTHING = "doNothing"; //$NON-NLS-1$
+
+ public static final String COMMAND_IGNORE_WHITESPACE = PREFIX + "ignoreWhiteSpace"; //$NON-NLS-1$
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IFlushable2.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IFlushable2.java
new file mode 100644
index 000000000..47a5b735a
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IFlushable2.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 org.eclipse.compare.contentmergeviewer.IFlushable;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Interface which provides the ability to flush the contents from the specified
+ * side of the viewer.
+ *
+ * @see IFlushable
+ *
+ * @since 3.7
+ */
+public interface IFlushable2 {
+ void flushLeft(IProgressMonitor monitor);
+
+ void flushRight(IProgressMonitor monitor);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java
new file mode 100644
index 000000000..a790eaae6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2006 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 org.eclipse.jface.text.IDocument;
+
+/**
+ * An interface that provides access to the internals of a merge viewer for the purposes of testing.
+ * NOTE: This interface is not to be used for any other purpose.
+ */
+public interface IMergeViewerTestAdapter {
+
+ /**
+ * Return the document for the given leg
+ * @param leg the leg (or side)
+ * @return the document for that leg of the comparison
+ */
+ public IDocument getDocument(char leg);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ISavingSaveable.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ISavingSaveable.java
new file mode 100644
index 000000000..287a78c33
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ISavingSaveable.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 org.eclipse.ui.Saveable;
+
+/**
+ * Interface defines API for checking if an object, preferably an instance of
+ * {@link Saveable}, is being saved.
+ *
+ * @since 3.7
+ */
+public interface ISavingSaveable {
+ public boolean isSaving();
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java
new file mode 100644
index 000000000..69cbbbc77
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * A factory object for creating a <code>Viewer</code>s from a descriptor.
+ * <p>
+ * It is used when registering a viewer for a specific type
+ * in <code>CompareUIPlugin.registerContentViewerDescriptor</code> and
+ * in <code>CompareUIPlugin.registerStructureViewerDescriptor</code>.
+ *
+ * @see org.eclipse.compare.structuremergeviewer.IStructureCreator
+ * @see CompareUIPlugin
+ */
+public interface IViewerDescriptor {
+
+ /**
+ * Creates a new viewer from this descriptor under the given STW parent control.
+ * If the current viewer has the same type as a new viewer
+ * the implementation of this method is free to return the current viewer instead.
+ *
+ * @param currentViewer the current viewer which is going to be replaced with a new viewer.
+ * @param parent the SWT parent control under which the new viewer has to be created.
+ * @param config a compare configuration the new viewer might be interested in.
+ * @return a new viewer or the current viewer.
+ */
+ Viewer createViewer(Viewer currentViewer, Composite parent, CompareConfiguration config);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java
new file mode 100644
index 000000000..53d4165ac
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A <code>Canvas</code> showing a single centered SWT <code>Image</code>.
+ * If the <code>Image</code> is larger than the <code>Canvas<code>,
+ * <code>Scrollbars</code> will appear.
+ */
+class ImageCanvas extends Canvas {
+
+ private Image fImage;
+
+ /*
+ * Create a new ImageCanvas with the given SWT stylebits.
+ * (SWT.H_SCROLL and SWT.V_SCROLL are automtically added).
+ */
+ public ImageCanvas(Composite parent, int style) {
+ super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL);
+
+ ScrollBar sb= getHorizontalBar();
+ sb.setIncrement(20);
+ sb.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ repaint();
+ }
+ });
+
+ sb= getVerticalBar();
+ sb.setIncrement(20);
+ sb.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ repaint();
+ }
+ });
+
+ addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event e) {
+ updateScrollbars();
+ }
+ });
+
+ addListener(SWT.Paint, new Listener() {
+ public void handleEvent(Event event) {
+ paint(event.gc);
+ }
+ });
+ }
+
+ /*
+ * Set the SWT Image to use as the ImageCanvas contents.
+ */
+ public void setImage(Image img) {
+ fImage= img;
+
+ if (!isDisposed()) {
+ getHorizontalBar().setSelection(0);
+ getVerticalBar().setSelection(0);
+ updateScrollbars();
+ getParent().layout();
+ redraw();
+ }
+ }
+
+ public void repaint() {
+ if (!isDisposed()) {
+ GC gc= new GC(this);
+ paint(gc);
+ gc.dispose();
+ }
+ }
+
+ void paint(GC gc) {
+ if (fImage != null) {
+ Rectangle bounds= fImage.getBounds();
+ Rectangle clientArea= getClientArea();
+
+ int x;
+ if (bounds.width < clientArea.width)
+ x= (clientArea.width - bounds.width) / 2;
+ else
+ x= -getHorizontalBar().getSelection();
+
+ int y;
+ if (bounds.height < clientArea.height)
+ y= (clientArea.height - bounds.height) / 2;
+ else
+ y= -getVerticalBar().getSelection();
+
+ gc.drawImage(fImage, x, y);
+ }
+ }
+
+ /**
+ * @private
+ */
+ void updateScrollbars() {
+ Rectangle bounds= fImage != null ? fImage.getBounds() : new Rectangle(0, 0, 0, 0);
+ Point size= getSize();
+ Rectangle clientArea= getClientArea();
+
+ ScrollBar horizontal= getHorizontalBar();
+ if (bounds.width <= clientArea.width) {
+ horizontal.setVisible(false);
+ horizontal.setSelection(0);
+ } else {
+ horizontal.setPageIncrement(clientArea.width - horizontal.getIncrement());
+ int max= bounds.width + (size.x - clientArea.width);
+ horizontal.setMaximum(max);
+ horizontal.setThumb(size.x > max ? max : size.x);
+ horizontal.setVisible(true);
+ }
+
+ ScrollBar vertical= getVerticalBar();
+ if (bounds.height <= clientArea.height) {
+ vertical.setVisible(false);
+ vertical.setSelection(0);
+ } else {
+ vertical.setPageIncrement(clientArea.height - vertical.getIncrement());
+ int max= bounds.height + (size.y - clientArea.height);
+ vertical.setMaximum(max);
+ vertical.setThumb(size.y > max ? max : size.y);
+ vertical.setVisible(true);
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java
new file mode 100644
index 000000000..c9cb53956
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.io.InputStream;
+import java.io.IOException;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+
+/**
+ */
+public class ImageMergeViewer extends ContentMergeViewer {
+
+ private static final String BUNDLE_NAME= "org.eclipse.compare.internal.ImageMergeViewerResources"; //$NON-NLS-1$
+
+ private Object fLeftImage;
+ private Object fRightImage;
+
+ private ImageCanvas fAncestor;
+ private ImageCanvas fLeft;
+ private ImageCanvas fRight;
+
+
+ public ImageMergeViewer(Composite parent, int styles, CompareConfiguration mp) {
+ super(styles, ResourceBundle.getBundle(BUNDLE_NAME), mp);
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, ICompareContextIds.IMAGE_COMPARE_VIEW);
+
+ buildControl(parent);
+ String title= Utilities.getString(getResourceBundle(), "title"); //$NON-NLS-1$
+ getControl().setData(CompareUI.COMPARE_VIEWER_TITLE, title);
+ }
+
+ protected void updateContent(Object ancestor, Object left, Object right) {
+
+ setInput(fAncestor, ancestor);
+
+ fLeftImage= left;
+ setInput(fLeft, left);
+
+ fRightImage= right;
+ setInput(fRight, right);
+ }
+
+ /*
+ * We can't modify the contents of either side we just return null.
+ */
+ protected byte[] getContents(boolean left) {
+ return null;
+ }
+
+ public void createControls(Composite composite) {
+ fAncestor= new ImageCanvas(composite, SWT.NO_FOCUS);
+ fLeft= new ImageCanvas(composite, SWT.NO_FOCUS);
+ fRight= new ImageCanvas(composite, SWT.NO_FOCUS);
+ }
+
+ private static void setInput(ImageCanvas canvas, Object input) {
+ if (canvas != null) {
+
+ InputStream stream= null;
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ if (sca != null) {
+ try {
+ stream= sca.getContents();
+ } catch (CoreException ex) {
+ // NeedWork
+ }
+ }
+ }
+
+ Image image= null;
+ Display display= canvas.getDisplay();
+ if (stream != null) {
+ try {
+ image= new Image(display, stream);
+ } catch (SWTException ex) {
+ // silently ignored
+ }
+ }
+
+ canvas.setImage(image);
+ if (image != null) {
+ canvas.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ } else {
+ canvas.setBackground(null);
+ }
+
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException ex) {
+ // silently ignored
+ }
+ }
+ }
+ }
+
+ protected void handleResizeAncestor(int x, int y, int width, int height) {
+ if (width > 0) {
+ fAncestor.setVisible(true);
+ fAncestor.setBounds(x, y, width, height);
+ } else {
+ fAncestor.setVisible(false);
+ }
+ }
+
+ protected void handleResizeLeftRight(int x, int y, int width1, int centerWidth, int width2, int height) {
+ fLeft.setBounds(x, y, width1, height);
+ fRight.setBounds(x+width1+centerWidth, y, width2, height);
+ }
+
+ protected void copy(boolean leftToRight) {
+ if (leftToRight) {
+ fRightImage= fLeftImage;
+ setInput(fRight, fRightImage);
+ setRightDirty(true);
+ } else {
+ fLeftImage= fRightImage;
+ setInput(fLeft, fLeftImage);
+ setLeftDirty(true);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java
new file mode 100644
index 000000000..d73a961c1
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.compare.*;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A factory object for the <code>ImageMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class ImageMergeViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new ImageMergeViewer(parent, SWT.NULL, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties
new file mode 100644
index 000000000..7388f9c7a
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties
@@ -0,0 +1,32 @@
+###############################################################################
+# Copyright (c) 2000, 2006 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
+###############################################################################
+
+# @(#)ImageMergeViewerResources.properties
+#
+# Resource strings for ImageMergeViewer.java
+
+title= Image Compare
+
+#####################################################
+# Actions
+#####################################################
+
+action.CopyLeftToRight.label= Copy Left to Right
+action.CopyLeftToRight.tooltip= Copy Image from Left to Right
+action.CopyLeftToRight.image= elcl16/copy_r_co.gif
+
+action.CopyRightToLeft.label= Copy Right to Left
+action.CopyRightToLeft.tooltip= Copy Image from Right to Left
+action.CopyRightToLeft.image= elcl16/copy_l_co.gif
+
+action.EnableAncestor.label= Enable Ancestor Pane
+action.EnableAncestor.tooltip= Control Visibility of Ancestor Pane
+action.EnableAncestor.image= elcl16/ancestorpane_co.gif
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ListContentProvider.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ListContentProvider.java
new file mode 100644
index 000000000..86a0d6d45
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ListContentProvider.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.util.List;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A specialized content provider to show a list of editor parts.
+ */
+public class ListContentProvider implements IStructuredContentProvider {
+ List fContents;
+
+ public ListContentProvider() {
+ // nothing to do
+ }
+
+ public Object[] getElements(Object input) {
+ if (fContents != null && fContents == input)
+ return fContents.toArray();
+ return new Object[0];
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ if (newInput instanceof List)
+ fContents= (List)newInput;
+ else
+ fContents= null;
+ // we use a fixed set.
+ }
+
+ public void dispose() {
+ // empty default implementation
+ }
+
+ public boolean isDeleted(Object o) {
+ return fContents != null && !fContents.contains(o);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java
new file mode 100644
index 000000000..ec99bd716
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java
@@ -0,0 +1,1039 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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
+ * Max Weninger (max.weninger@windriver.com) - Bug 131895 [Edit] Undo in compare
+ * Max Weninger (max.weninger@windriver.com) - Bug 72936 [Viewers] Show line numbers in comparision
+ *******************************************************************************/
+package org.eclipse.compare.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.IUndoManager;
+import org.eclipse.jface.text.IUndoManagerExtension;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISharedTextColors;
+import org.eclipse.jface.text.source.LineNumberRulerColumn;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.source.SourceViewerConfiguration;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.menus.CommandContributionItem;
+import org.eclipse.ui.menus.CommandContributionItemParameter;
+import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
+import org.eclipse.ui.texteditor.ChangeEncodingAction;
+import org.eclipse.ui.texteditor.FindReplaceAction;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.IElementStateListener;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Wraps a JFace SourceViewer and add some convenience methods.
+ */
+public class MergeSourceViewer implements ISelectionChangedListener,
+ ITextListener, IMenuListener, IOperationHistoryListener, IAdaptable {
+
+ public static final String UNDO_ID= "undo"; //$NON-NLS-1$
+ public static final String REDO_ID= "redo"; //$NON-NLS-1$
+ public static final String CUT_ID= "cut"; //$NON-NLS-1$
+ public static final String COPY_ID= "copy"; //$NON-NLS-1$
+ public static final String PASTE_ID= "paste"; //$NON-NLS-1$
+ public static final String DELETE_ID= "delete"; //$NON-NLS-1$
+ public static final String SELECT_ALL_ID= "selectAll"; //$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 CHANGE_ENCODING_ID= "changeEncoding"; //$NON-NLS-1$
+
+ class TextOperationAction extends MergeViewerAction {
+
+ private int fOperationCode;
+
+ TextOperationAction(int operationCode, boolean mutable, boolean selection, boolean content) {
+ this(operationCode, null, mutable, selection, content);
+ }
+
+ public TextOperationAction(int operationCode, String actionDefinitionId, boolean mutable, boolean selection, boolean content) {
+ super(mutable, selection, content);
+ if (actionDefinitionId != null)
+ setActionDefinitionId(actionDefinitionId);
+ fOperationCode= operationCode;
+ update();
+ }
+
+ public void run() {
+ if (isEnabled())
+ getSourceViewer().doOperation(fOperationCode);
+ }
+
+ public boolean isEnabled() {
+ return fOperationCode != -1 && getSourceViewer().canDoOperation(fOperationCode);
+ }
+
+ public void update() {
+ this.setEnabled(isEnabled());
+ }
+ }
+
+ /**
+ * TODO: The only purpose of this class is to provide "Go to Line" action in
+ * TextMergeViewer. The adapter should be removed as soon as we implement
+ * embedded TextEditor in a similar way JDT has it done for Java compare.
+ */
+ class TextEditorAdapter implements ITextEditor {
+
+ public void close(boolean save) {
+ // defining interface method
+ }
+
+ public void doRevertToSaved() {
+ // defining interface method
+ }
+
+ public IAction getAction(String actionId) {
+ // defining interface method
+ return null;
+ }
+
+ public IDocumentProvider getDocumentProvider() {
+ return new IDocumentProvider(){
+
+ public void aboutToChange(Object element) {
+ // defining interface method
+ }
+
+ public void addElementStateListener(
+ IElementStateListener listener) {
+ // defining interface method
+ }
+
+ public boolean canSaveDocument(Object element) {
+ // defining interface method
+ return false;
+ }
+
+ public void changed(Object element) {
+ // defining interface method
+ }
+
+ public void connect(Object element) throws CoreException {
+ // defining interface method
+ }
+
+ public void disconnect(Object element) {
+ // defining interface method
+ }
+
+ public IAnnotationModel getAnnotationModel(Object element) {
+ // defining interface method
+ return null;
+ }
+
+ public IDocument getDocument(Object element) {
+ return MergeSourceViewer.this.getSourceViewer().getDocument();
+ }
+
+ public long getModificationStamp(Object element) {
+ // defining interface method
+ return 0;
+ }
+
+ public long getSynchronizationStamp(Object element) {
+ // defining interface method
+ return 0;
+ }
+
+ public boolean isDeleted(Object element) {
+ // defining interface method
+ return false;
+ }
+
+ public boolean mustSaveDocument(Object element) {
+ // defining interface method
+ return false;
+ }
+
+ public void removeElementStateListener(
+ IElementStateListener listener) {
+ // defining interface method
+ }
+
+ public void resetDocument(Object element) throws CoreException {
+ // defining interface method
+ }
+
+ public void saveDocument(IProgressMonitor monitor,
+ Object element, IDocument document, boolean overwrite)
+ throws CoreException {
+ // defining interface method
+ }};
+ }
+
+ public IRegion getHighlightRange() {
+ // defining interface method
+ return null;
+ }
+
+ public ISelectionProvider getSelectionProvider() {
+ return MergeSourceViewer.this.getSourceViewer().getSelectionProvider();
+ }
+
+ public boolean isEditable() {
+ // defining interface method
+ return false;
+ }
+
+ public void removeActionActivationCode(String actionId) {
+ // defining interface method
+ }
+
+ public void resetHighlightRange() {
+ // defining interface method
+ }
+
+ public void selectAndReveal(int start, int length) {
+ selectAndReveal(start, length, start, length);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.AbstractTextEditor#selectAndReveal(int, int, int, int)
+ */
+ private void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) {
+
+ ISelection selection = getSelectionProvider().getSelection();
+ if (selection instanceof ITextSelection) {
+ ITextSelection textSelection = (ITextSelection) selection;
+ if (textSelection.getOffset() != 0 || textSelection.getLength() != 0)
+ markInNavigationHistory();
+ }
+
+ StyledText widget= MergeSourceViewer.this.getSourceViewer().getTextWidget();
+ widget.setRedraw(false);
+ {
+ adjustHighlightRange(revealStart, revealLength);
+ MergeSourceViewer.this.getSourceViewer().revealRange(revealStart, revealLength);
+
+ MergeSourceViewer.this.getSourceViewer().setSelectedRange(selectionStart, selectionLength);
+
+ markInNavigationHistory();
+ }
+ widget.setRedraw(true);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.AbstractTextEditor#markInNavigationHistory()
+ */
+ private void markInNavigationHistory() {
+ getSite().getPage().getNavigationHistory().markLocation(this);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.AbstractTextEditor#adjustHighlightRange(int, int)
+ */
+ private void adjustHighlightRange(int offset, int length) {
+
+ if (MergeSourceViewer.this instanceof ITextViewerExtension5) {
+ ITextViewerExtension5 extension= (ITextViewerExtension5) MergeSourceViewer.this;
+ extension.exposeModelRange(new Region(offset, length));
+ } else if (!isVisible(MergeSourceViewer.this.getSourceViewer(), offset, length)) {
+ MergeSourceViewer.this.getSourceViewer().resetVisibleRegion();
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.AbstractTextEditor#isVisible(ISourceViewer, int, int)
+ */
+ private /*static*/ final boolean isVisible(ITextViewer viewer, int offset, int length) {
+ if (viewer instanceof ITextViewerExtension5) {
+ ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
+ IRegion overlap= extension.modelRange2WidgetRange(new Region(offset, length));
+ return overlap != null;
+ }
+ return viewer.overlapsWithVisibleRegion(offset, length);
+ }
+
+ public void setAction(String actionID, IAction action) {
+ // defining interface method
+ }
+
+ public void setActionActivationCode(String actionId,
+ char activationCharacter, int activationKeyCode,
+ int activationStateMask) {
+ // defining interface method
+ }
+
+ public void setHighlightRange(int offset, int length, boolean moveCursor) {
+ // defining interface method
+ }
+
+ public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
+ // defining interface method
+ }
+
+ public boolean showsHighlightRangeOnly() {
+ // defining interface method
+ return false;
+ }
+
+ public IEditorInput getEditorInput() {
+ if (MergeSourceViewer.this.fContainer.getWorkbenchPart() instanceof IEditorPart)
+ return ((IEditorPart) MergeSourceViewer.this.fContainer.getWorkbenchPart()).getEditorInput();
+ return null;
+ }
+
+ public IEditorSite getEditorSite() {
+ // defining interface method
+ return null;
+ }
+
+ public void init(IEditorSite site, IEditorInput input)
+ throws PartInitException {
+ // defining interface method
+ }
+
+ public void addPropertyListener(IPropertyListener listener) {
+ // defining interface method
+ }
+
+ public void createPartControl(Composite parent) {
+ // defining interface method
+ }
+
+ public void dispose() {
+ // defining interface method
+ }
+
+ public IWorkbenchPartSite getSite() {
+ return MergeSourceViewer.this.fContainer.getWorkbenchPart().getSite();
+ }
+
+ public String getTitle() {
+ // defining interface method
+ return null;
+ }
+
+ public Image getTitleImage() {
+ // defining interface method
+ return null;
+ }
+
+ public String getTitleToolTip() {
+ // defining interface method
+ return null;
+ }
+
+ public void removePropertyListener(IPropertyListener listener) {
+ // defining interface method
+ }
+
+ public void setFocus() {
+ // defining interface method
+ }
+
+ public Object getAdapter(Class adapter) {
+ // defining interface method
+ return null;
+ }
+
+ public void doSave(IProgressMonitor monitor) {
+ // defining interface method
+ }
+
+ public void doSaveAs() {
+ // defining interface method
+ }
+
+ public boolean isDirty() {
+ // defining interface method
+ return false;
+ }
+
+ public boolean isSaveAsAllowed() {
+ // defining interface method
+ return false;
+ }
+
+ public boolean isSaveOnCloseNeeded() {
+ // defining interface method
+ return false;
+ }
+ }
+
+ private ResourceBundle fResourceBundle;
+ private ICompareContainer fContainer;
+ private SourceViewer fSourceViewer;
+ private Position fRegion;
+ private boolean fEnabled= true;
+ private HashMap fActions= new HashMap();
+ private IDocument fRememberedDocument;
+
+ private boolean fAddSaveAction= true;
+ private boolean isConfigured = false;
+
+ // line number ruler support
+ private IPropertyChangeListener fPreferenceChangeListener;
+ private boolean fShowLineNumber=false;
+ private LineNumberRulerColumn fLineNumberColumn;
+ private List textActions = new ArrayList();
+ private CommandContributionItem fSaveContributionItem;
+
+ public MergeSourceViewer(SourceViewer sourceViewer, ResourceBundle bundle, ICompareContainer container) {
+ Assert.isNotNull(sourceViewer);
+ fSourceViewer= sourceViewer;
+ fResourceBundle= bundle;
+ fContainer = container;
+
+ MenuManager menu= new MenuManager();
+ menu.setRemoveAllWhenShown(true);
+ menu.addMenuListener(this);
+ StyledText te= getSourceViewer().getTextWidget();
+ te.setMenu(menu.createContextMenu(te));
+ fContainer.registerContextMenu(menu, getSourceViewer());
+
+ // for listening to editor show/hide line number preference value
+ fPreferenceChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ MergeSourceViewer.this.handlePropertyChangeEvent(event);
+ }
+ };
+ EditorsUI.getPreferenceStore().addPropertyChangeListener(fPreferenceChangeListener);
+ fShowLineNumber= EditorsUI.getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER);
+ if(fShowLineNumber){
+ updateLineNumberRuler();
+ }
+
+ IOperationHistory history = getHistory();
+ if (history != null)
+ history.addOperationHistoryListener(this);
+
+ // don't add save when in a dialog, IWorkbenchPart is null in dialog containers
+ fAddSaveAction = fContainer.getWorkbenchPart() != null;
+ }
+
+ public void rememberDocument(IDocument doc) {
+// if (doc != null && fRememberedDocument != null) {
+// System.err.println("MergeSourceViewer.rememberDocument: fRememberedDocument != null: shouldn't happen"); //$NON-NLS-1$
+// }
+ fRememberedDocument= doc;
+ }
+
+ public IDocument getRememberedDocument() {
+ return fRememberedDocument;
+ }
+
+ public void hideSaveAction() {
+ fAddSaveAction= false;
+ }
+
+ public void setFont(Font font) {
+ StyledText te= getSourceViewer().getTextWidget();
+ if (te != null)
+ te.setFont(font);
+ if (fLineNumberColumn != null) {
+ fLineNumberColumn.setFont(font);
+ layoutViewer();
+ }
+ }
+
+ public void setBackgroundColor(Color color) {
+ StyledText te= getSourceViewer().getTextWidget();
+ if (te != null)
+ te.setBackground(color);
+ if (fLineNumberColumn != null)
+ fLineNumberColumn.setBackground(color);
+ }
+
+ public void setForegroundColor(Color color) {
+ StyledText te= getSourceViewer().getTextWidget();
+ if (te != null)
+ te.setForeground(color);
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (enabled != fEnabled) {
+ fEnabled= enabled;
+ StyledText c= getSourceViewer().getTextWidget();
+ if (c != null) {
+ c.setEnabled(enabled);
+ Display d= c.getDisplay();
+ c.setBackground(enabled ? d.getSystemColor(SWT.COLOR_LIST_BACKGROUND) : null);
+ }
+ }
+ }
+
+ public boolean getEnabled() {
+ return fEnabled;
+ }
+
+ public void setRegion(Position region) {
+ fRegion= region;
+ }
+
+ public Position getRegion() {
+ return fRegion;
+ }
+
+ public boolean isControlOkToUse() {
+ StyledText t= getSourceViewer().getTextWidget();
+ return t != null && !t.isDisposed();
+ }
+
+ public void setSelection(Position position) {
+ if (position != null)
+ getSourceViewer().setSelectedRange(position.getOffset(), position.getLength());
+ }
+
+ public void setLineBackground(Position position, Color c) {
+ StyledText t= getSourceViewer().getTextWidget();
+ if (t != null && !t.isDisposed()) {
+ Point region= new Point(0, 0);
+ getLineRange(position, region);
+
+ region.x-= getDocumentRegionOffset();
+
+ try {
+ t.setLineBackground(region.x, region.y, c);
+ } catch (IllegalArgumentException ex) {
+ // silently ignored
+ }
+ }
+ }
+
+ public void resetLineBackground() {
+ StyledText t= getSourceViewer().getTextWidget();
+ if (t != null && !t.isDisposed()) {
+ int lines= getLineCount();
+ t.setLineBackground(0, lines, null);
+ }
+ }
+
+ /*
+ * Returns number of lines in document region.
+ */
+ public int getLineCount() {
+ IRegion region= getSourceViewer().getVisibleRegion();
+
+ int length= region.getLength();
+ if (length == 0)
+ return 0;
+
+ IDocument doc= getSourceViewer().getDocument();
+ int startLine= 0;
+ int endLine= 0;
+
+ int start= region.getOffset();
+ try {
+ startLine= doc.getLineOfOffset(start);
+ } catch(BadLocationException ex) {
+ // silently ignored
+ }
+ try {
+ endLine= doc.getLineOfOffset(start+length);
+ } catch(BadLocationException ex) {
+ // silently ignored
+ }
+
+ return endLine-startLine+1;
+ }
+
+ public int getViewportLines() {
+ StyledText te= getSourceViewer().getTextWidget();
+ Rectangle clArea= te.getClientArea();
+ if (!clArea.isEmpty())
+ return clArea.height / te.getLineHeight();
+ return 0;
+ }
+
+ public int getViewportHeight() {
+ StyledText te= getSourceViewer().getTextWidget();
+ Rectangle clArea= te.getClientArea();
+ if (!clArea.isEmpty())
+ return clArea.height;
+ return 0;
+ }
+
+ /*
+ * Returns lines
+ */
+ public int getDocumentRegionOffset() {
+ int start= getSourceViewer().getVisibleRegion().getOffset();
+ IDocument doc= getSourceViewer().getDocument();
+ if (doc != null) {
+ try {
+ return doc.getLineOfOffset(start);
+ } catch(BadLocationException ex) {
+ // silently ignored
+ }
+ }
+ return 0;
+ }
+
+ public int getVerticalScrollOffset() {
+ StyledText st= getSourceViewer().getTextWidget();
+ int lineHeight= st.getLineHeight();
+ return getSourceViewer().getTopInset() - ((getDocumentRegionOffset()*lineHeight) + st.getTopPixel());
+ }
+
+ /*
+ * Returns the start line and the number of lines which correspond to the given position.
+ * Starting line number is 0 based.
+ */
+ public Point getLineRange(Position p, Point region) {
+
+ IDocument doc= getSourceViewer().getDocument();
+
+ if (p == null || doc == null) {
+ region.x= 0;
+ region.y= 0;
+ return region;
+ }
+
+ int start= p.getOffset();
+ int length= p.getLength();
+
+ int startLine= 0;
+ try {
+ startLine= doc.getLineOfOffset(start);
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+
+ int lineCount= 0;
+
+ if (length == 0) {
+// // if range length is 0 and if range starts a new line
+// try {
+// if (start == doc.getLineStartOffset(startLine)) {
+// lines--;
+// }
+// } catch (BadLocationException e) {
+// lines--;
+// }
+
+ } else {
+ int endLine= 0;
+ try {
+ endLine= doc.getLineOfOffset(start + length - 1); // why -1?
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+ lineCount= endLine-startLine+1;
+ }
+
+ region.x= startLine;
+ region.y= lineCount;
+ return region;
+ }
+
+ /*
+ * Scroll TextPart to the given line.
+ */
+ public void vscroll(int line) {
+
+ int srcViewSize= getLineCount();
+ int srcExtentSize= getViewportLines();
+
+ if (srcViewSize > srcExtentSize) {
+
+ if (line < 0)
+ line= 0;
+
+ int cp= getSourceViewer().getTopIndex();
+ if (cp != line)
+ getSourceViewer().setTopIndex(line + getDocumentRegionOffset());
+ }
+ }
+
+ public void addAction(String actionId, MergeViewerAction action) {
+ fActions.put(actionId, action);
+ }
+
+ public IAction getAction(String actionId) {
+ IAction action= (IAction) fActions.get(actionId);
+ if (action == null) {
+ action= createAction(actionId);
+ if (action == null)
+ return null;
+ if (action instanceof MergeViewerAction) {
+ MergeViewerAction mva = (MergeViewerAction) action;
+ if (mva.isContentDependent())
+ getSourceViewer().addTextListener(this);
+ if (mva.isSelectionDependent())
+ getSourceViewer().addSelectionChangedListener(this);
+
+ Utilities.initAction(action, fResourceBundle, "action." + actionId + "."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ addAction(actionId, action);
+
+ }
+ if (action instanceof MergeViewerAction) {
+ MergeViewerAction mva = (MergeViewerAction) action;
+ if (mva.isEditableDependent() && !getSourceViewer().isEditable())
+ return null;
+ }
+ return action;
+ }
+
+ protected IAction createAction(String actionId) {
+ if (UNDO_ID.equals(actionId))
+ return new TextOperationAction(ITextOperationTarget.UNDO, IWorkbenchCommandConstants.EDIT_UNDO, true, false, true);
+ if (REDO_ID.equals(actionId))
+ return new TextOperationAction(ITextOperationTarget.REDO, IWorkbenchCommandConstants.EDIT_REDO, true, false, true);
+ if (CUT_ID.equals(actionId))
+ return new TextOperationAction(ITextOperationTarget.CUT, IWorkbenchCommandConstants.EDIT_CUT, true, true, false);
+ if (COPY_ID.equals(actionId))
+ return new TextOperationAction(ITextOperationTarget.COPY, IWorkbenchCommandConstants.EDIT_COPY, false, true, false);
+ if (PASTE_ID.equals(actionId))
+ return new TextOperationAction(ITextOperationTarget.PASTE, IWorkbenchCommandConstants.EDIT_PASTE, true, false, false);
+ if (DELETE_ID.equals(actionId))
+ return new TextOperationAction(ITextOperationTarget.DELETE, IWorkbenchCommandConstants.EDIT_DELETE, true, false, false);
+ if (SELECT_ALL_ID.equals(actionId))
+ return new TextOperationAction(ITextOperationTarget.SELECT_ALL, IWorkbenchCommandConstants.EDIT_SELECT_ALL, false, false, false);
+ return null;
+ }
+
+ public void selectionChanged(SelectionChangedEvent event) {
+ Iterator e= fActions.values().iterator();
+ while (e.hasNext()) {
+ Object next = e.next();
+ if (next instanceof MergeViewerAction) {
+ MergeViewerAction action = (MergeViewerAction) next;
+ if (action.isSelectionDependent())
+ action.update();
+ }
+ }
+ }
+
+ public void textChanged(TextEvent event) {
+ updateContentDependantActions();
+ }
+
+ void updateContentDependantActions() {
+ Iterator e= fActions.values().iterator();
+ while (e.hasNext()) {
+ Object next = e.next();
+ if (next instanceof MergeViewerAction) {
+ MergeViewerAction action = (MergeViewerAction) next;
+ if (action.isContentDependent())
+ action.update();
+ }
+ }
+ }
+
+ /*
+ * 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);
+ menu.add(new GroupMarker("save")); //$NON-NLS-1$
+ if (fAddSaveAction)
+ addSave(menu);
+ menu.add(new Separator("file")); //$NON-NLS-1$
+
+ menu.add(new Separator("ccp")); //$NON-NLS-1$
+ addMenu(menu, CUT_ID);
+ addMenu(menu, COPY_ID);
+ addMenu(menu, PASTE_ID);
+ addMenu(menu, DELETE_ID);
+ addMenu(menu, SELECT_ALL_ID);
+
+ menu.add(new Separator("edit")); //$NON-NLS-1$
+ addMenu(menu, CHANGE_ENCODING_ID);
+ menu.add(new Separator("find")); //$NON-NLS-1$
+ addMenu(menu, FIND_ID);
+
+ menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+
+ menu.add(new Separator("text")); //$NON-NLS-1$
+ for (Iterator iterator = textActions.iterator(); iterator.hasNext();) {
+ IAction action = (IAction) iterator.next();
+ menu.add(action);
+ }
+
+ menu.add(new Separator("rest")); //$NON-NLS-1$
+
+ // update all actions
+ // to get undo redo right
+ updateActions();
+ }
+
+ private void addMenu(IMenuManager menu, String actionId) {
+ IAction action= getAction(actionId);
+ if (action != null)
+ menu.add(action);
+ }
+
+ private void addSave(IMenuManager menu) {
+ ICommandService commandService = (ICommandService) fContainer.getWorkbenchPart().getSite().getService(ICommandService.class);
+ final Command command= commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE);
+
+ final IHandler handler = command.getHandler();
+ if (handler != null) {
+ if (fSaveContributionItem == null) {
+ fSaveContributionItem = new CommandContributionItem(
+ new CommandContributionItemParameter(fContainer
+ .getWorkbenchPart().getSite(), null, command
+ .getId(), CommandContributionItem.STYLE_PUSH));
+ }
+ // save is an editable dependent action, ie add only when edit
+ // is possible
+ if (handler.isHandled() && getSourceViewer().isEditable())
+ menu.add(fSaveContributionItem);
+ }
+ }
+
+ /**
+ * The viewer is no longer part of the UI, it's a wrapper only. The disposal
+ * doesn't take place while releasing the editor pane's children. The method
+ * have to be called it manually. The wrapped viewer is disposed as a
+ * regular viewer, while disposing the UI.
+ */
+ public void dispose() {
+ getSourceViewer().removeTextListener(this);
+ getSourceViewer().removeSelectionChangedListener(this);
+ EditorsUI.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener);
+
+ IOperationHistory history = getHistory();
+ if (history != null)
+ history.removeOperationHistoryListener(this);
+ }
+
+ /**
+ * update all actions independent of their type
+ *
+ */
+ public void updateActions() {
+ Iterator e= fActions.values().iterator();
+ while (e.hasNext()) {
+ Object next = e.next();
+ if (next instanceof MergeViewerAction) {
+ MergeViewerAction action = (MergeViewerAction) next;
+ action.update();
+ } else if (next instanceof FindReplaceAction) {
+ FindReplaceAction action = (FindReplaceAction) next;
+ action.update();
+ } else if (next instanceof ChangeEncodingAction) {
+ ChangeEncodingAction action = (ChangeEncodingAction) next;
+ action.update();
+ }
+ }
+ }
+
+ public void configure(SourceViewerConfiguration configuration) {
+ if (isConfigured )
+ getSourceViewer().unconfigure();
+ isConfigured = true;
+ getSourceViewer().configure(configuration);
+ }
+
+ /**
+ * specific implementation to support a vertical ruler
+ * @param x
+ * @param y
+ * @param width
+ * @param height
+ */
+ public void setBounds (int x, int y, int width, int height) {
+ if(getSourceViewer().getControl() instanceof Composite){
+ ((Composite)getSourceViewer().getControl()).setBounds(x, y, width, height);
+ } else {
+ getSourceViewer().getTextWidget().setBounds(x, y, width, height);
+ }
+ }
+
+ /**
+ * handle show/hide line numbers from editor preferences
+ * @param event
+ */
+ protected void handlePropertyChangeEvent(PropertyChangeEvent event) {
+
+ String key= event.getProperty();
+
+ if(key.equals(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER)){
+ boolean b= EditorsUI.getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER);
+ if (b != fShowLineNumber){
+ toggleLineNumberRuler();
+ }
+ } else if (key.equals(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR)) {
+ updateLineNumberColumnPresentation(true);
+ }
+ }
+
+ /**
+ * Hides or shows line number ruler column based of preference setting
+ */
+ private void updateLineNumberRuler() {
+ if (!fShowLineNumber) {
+ if (fLineNumberColumn != null) {
+ getSourceViewer().removeVerticalRulerColumn(fLineNumberColumn);
+ }
+ } else {
+ if (fLineNumberColumn == null) {
+ fLineNumberColumn = new LineNumberRulerColumn();
+ updateLineNumberColumnPresentation(false);
+ }
+ getSourceViewer().addVerticalRulerColumn(fLineNumberColumn);
+ }
+ }
+
+ private void updateLineNumberColumnPresentation(boolean refresh) {
+ if (fLineNumberColumn == null)
+ return;
+ RGB rgb= getColorFromStore(EditorsUI.getPreferenceStore(), AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR);
+ if (rgb == null)
+ rgb= new RGB(0, 0, 0);
+ ISharedTextColors sharedColors= getSharedColors();
+ fLineNumberColumn.setForeground(sharedColors.getColor(rgb));
+ if (refresh) {
+ fLineNumberColumn.redraw();
+ }
+ }
+
+ private void layoutViewer() {
+ Control parent= getSourceViewer().getControl();
+ if (parent instanceof Composite && !parent.isDisposed())
+ ((Composite) parent).layout(true);
+ }
+
+ private ISharedTextColors getSharedColors() {
+ return EditorsUI.getSharedTextColors();
+ }
+
+ private RGB getColorFromStore(IPreferenceStore store, String key) {
+ RGB rgb= null;
+ if (store.contains(key)) {
+ if (store.isDefault(key))
+ rgb= PreferenceConverter.getDefaultColor(store, key);
+ else
+ rgb= PreferenceConverter.getColor(store, key);
+ }
+ return rgb;
+ }
+
+ /**
+ * Toggles line number ruler column.
+ */
+ private void toggleLineNumberRuler()
+ {
+ fShowLineNumber=!fShowLineNumber;
+
+ updateLineNumberRuler();
+ }
+
+ public void addTextAction(IAction textEditorPropertyAction) {
+ textActions.add(textEditorPropertyAction);
+ }
+
+ public void addAction(String id, IAction action) {
+ fActions.put(id, action);
+ }
+
+ private IOperationHistory getHistory() {
+ if (PlatformUI.getWorkbench() == null) {
+ return null;
+ }
+ return PlatformUI.getWorkbench().getOperationSupport()
+ .getOperationHistory();
+ }
+
+ public void historyNotification(OperationHistoryEvent event) {
+ // This method updates the enablement of all content operations
+ // when the undo history changes. It could be localized to UNDO and REDO.
+ IUndoContext context = getUndoContext();
+ if (context != null && event.getOperation().hasContext(context)) {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ updateContentDependantActions();
+ }
+ });
+ }
+ }
+
+ private IUndoContext getUndoContext() {
+ IUndoManager undoManager = getSourceViewer().getUndoManager();
+ if (undoManager instanceof IUndoManagerExtension)
+ return ((IUndoManagerExtension)undoManager).getUndoContext();
+ return null;
+ }
+
+ /**
+ * @return the wrapped viewer
+ */
+ public SourceViewer getSourceViewer() {
+ return fSourceViewer;
+ }
+
+ public Object getAdapter(Class adapter) {
+ if (adapter == ITextEditor.class) {
+ return new TextEditorAdapter();
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerAction.java
new file mode 100644
index 000000000..b1b579c8e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerAction.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.ui.texteditor.IUpdate;
+import org.eclipse.jface.action.Action;
+
+
+public abstract class MergeViewerAction extends Action implements IUpdate {
+
+ private boolean fMutable;
+ private boolean fSelection;
+ private boolean fContent;
+
+ public MergeViewerAction(boolean mutable, boolean selection, boolean content) {
+ fMutable= mutable;
+ fSelection= selection;
+ fContent= content;
+ }
+
+ public boolean isSelectionDependent() {
+ return fSelection;
+ }
+
+ public boolean isContentDependent() {
+ return fContent;
+ }
+
+ public boolean isEditableDependent() {
+ return fMutable;
+ }
+
+ public void update() {
+ // empty default implementation
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java
new file mode 100644
index 000000000..a46445a28
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.compare.*;
+import org.eclipse.compare.contentmergeviewer.IMergeViewerContentProvider;
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Adapts any <code>ContentMergeViewer</code> to work on an <code>ICompareInput</code>
+ * e.g. a <code>DiffNode</code>.
+ */
+public class MergeViewerContentProvider implements IMergeViewerContentProvider {
+
+ public static final char ANCESTOR_CONTRIBUTOR = 'A';
+ public static final char RIGHT_CONTRIBUTOR = 'R';
+ public static final char LEFT_CONTRIBUTOR = 'L';
+
+ private CompareConfiguration fCompareConfiguration;
+ private String fAncestorError;
+ private String fLeftError;
+ private String fRightError;
+
+ public MergeViewerContentProvider(CompareConfiguration cc) {
+ fCompareConfiguration= cc;
+ }
+
+ private boolean hasError() {
+ return fAncestorError != null || fLeftError != null || fRightError != null;
+ }
+
+ public void dispose() {
+ // empty default implementation
+ }
+
+ public void inputChanged(Viewer v, Object o1, Object o2) {
+ // we are not interested since we have no state
+ }
+
+ //---- ancestor
+
+ public void setAncestorError(String errorMessage) {
+ fAncestorError= errorMessage;
+ }
+
+ public String getAncestorLabel(Object element) {
+ if (fAncestorError != null)
+ return fAncestorError;
+ return fCompareConfiguration.getAncestorLabel(element);
+ }
+
+ public Image getAncestorImage(Object element) {
+ if (fAncestorError != null)
+ return null;
+ return fCompareConfiguration.getAncestorImage(element);
+ }
+
+ public Object getAncestorContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getAncestor();
+ return null;
+ }
+
+ public boolean showAncestor(Object element) {
+ if (element instanceof ICompareInput)
+ return true; // fix for #45239: Show ancestor for incoming and outgoing changes
+ //return (((ICompareInput)element).getKind() & Differencer.DIRECTION_MASK) == Differencer.CONFLICTING;
+ return false;
+ }
+
+ //---- left
+
+ public void setLeftError(String errorMessage) {
+ fLeftError= errorMessage;
+ }
+
+ public String getLeftLabel(Object element) {
+ if (fLeftError != null)
+ return fLeftError;
+ return fCompareConfiguration.getLeftLabel(element);
+ }
+
+ public Image getLeftImage(Object element) {
+ if (fLeftError != null)
+ return null;
+ return fCompareConfiguration.getLeftImage(element);
+ }
+
+ public Object getLeftContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getLeft();
+ return null;
+ }
+
+ public boolean isLeftEditable(Object element) {
+ if (hasError())
+ return false;
+ if (element instanceof ICompareInput) {
+ Object left= ((ICompareInput) element).getLeft();
+ if (left == null && element instanceof IDiffElement) {
+ IDiffElement parent= ((IDiffElement)element).getParent();
+ if (parent instanceof ICompareInput)
+ left= ((ICompareInput) parent).getLeft();
+ }
+ if (left instanceof IEditableContent)
+ return ((IEditableContent)left).isEditable();
+ }
+ return false;
+ }
+
+ public void saveLeftContent(Object element, byte[] bytes) {
+ if (element instanceof ICompareInput) {
+ ICompareInput node= (ICompareInput) element;
+ if (bytes != null) {
+ ITypedElement left= node.getLeft();
+ // #9869: problem if left is null (because no resource exists yet) nothing is done!
+ if (left == null) {
+ node.copy(false);
+ left= node.getLeft();
+ }
+ if (left instanceof IEditableContent)
+ ((IEditableContent)left).setContent(bytes);
+ if (node instanceof ResourceCompareInput.MyDiffNode)
+ ((ResourceCompareInput.MyDiffNode)node).fireChange();
+ } else {
+ node.copy(false);
+ }
+ }
+ }
+
+ //---- right
+
+ public void setRightError(String errorMessage) {
+ fRightError= errorMessage;
+ }
+
+ public String getRightLabel(Object element) {
+ if (fRightError != null)
+ return fRightError;
+ return fCompareConfiguration.getRightLabel(element);
+ }
+
+ public Image getRightImage(Object element) {
+ if (fRightError != null)
+ return null;
+ return fCompareConfiguration.getRightImage(element);
+ }
+
+ public Object getRightContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getRight();
+ return null;
+ }
+
+ public boolean isRightEditable(Object element) {
+ if (hasError())
+ return false;
+ if (element instanceof ICompareInput) {
+ Object right= ((ICompareInput) element).getRight();
+ if (right == null && element instanceof IDiffElement) {
+ IDiffContainer parent= ((IDiffElement)element).getParent();
+ if (parent instanceof ICompareInput)
+ right= ((ICompareInput) parent).getRight();
+ }
+ if (right instanceof IEditableContent)
+ return ((IEditableContent)right).isEditable();
+ }
+ return false;
+ }
+
+ public void saveRightContent(Object element, byte[] bytes) {
+ if (element instanceof ICompareInput) {
+ ICompareInput node= (ICompareInput) element;
+ if (bytes != null) {
+ ITypedElement right= node.getRight();
+ // #9869: problem if right is null (because no resource exists yet) nothing is done!
+ if (right == null) {
+ node.copy(true);
+ right= node.getRight();
+ }
+ if (right instanceof IEditableContent)
+ ((IEditableContent)right).setContent(bytes);
+ if (node instanceof ResourceCompareInput.MyDiffNode)
+ ((ResourceCompareInput.MyDiffNode)node).fireChange();
+ } else {
+ node.copy(true);
+ }
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NavigationEndDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NavigationEndDialog.java
new file mode 100644
index 000000000..3ca58fe91
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NavigationEndDialog.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2006 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 org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.*;
+
+public class NavigationEndDialog extends MessageDialogWithToggle {
+
+ private final String[][] labelsAndValues;
+ private RadioGroupFieldEditor editor;
+
+ public NavigationEndDialog(Shell parentShell, String dialogTitle,
+ Image dialogTitleImage, String dialogMessage, String[][] labelsAndValues) {
+ super(parentShell, dialogTitle, dialogTitleImage, dialogMessage,
+ QUESTION, new String[] { IDialogConstants.OK_LABEL , IDialogConstants.CANCEL_LABEL}, 0,
+ CompareMessages.NavigationEndDialog_0, false);
+ this.labelsAndValues = labelsAndValues;
+ }
+
+ protected Control createCustomArea(Composite parent) {
+ editor = new RadioGroupFieldEditor(ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL, CompareMessages.NavigationEndDialog_1, 1,
+ labelsAndValues,
+ parent, true);
+ editor.setPreferenceStore(CompareUIPlugin.getDefault().getPreferenceStore());
+ editor.fillIntoGrid(parent, 1);
+ editor.load();
+ return parent;
+ }
+
+ protected void buttonPressed(int buttonId) {
+ if (buttonId == IDialogConstants.OK_ID) {
+ editor.store();
+ }
+ super.buttonPressed(buttonId);
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NullViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NullViewer.java
new file mode 100644
index 000000000..f9fce2b90
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/NullViewer.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.*;
+
+import org.eclipse.compare.CompareViewerPane;
+
+/**
+ * Used whenever the input is null or no viewer can be found.
+ */
+public class NullViewer extends AbstractViewer {
+
+ private Control fDummy;
+
+ public NullViewer(Composite parent) {
+
+ fDummy= new Tree(parent, SWT.NULL);
+
+ CompareViewerPane.clearToolBar(parent);
+ }
+
+ public Control getControl() {
+ return fDummy;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OutlineViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OutlineViewerCreator.java
new file mode 100644
index 000000000..ee303531c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OutlineViewerCreator.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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 org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Class which allows content merge viewer to provide a structure viewer that can be used in the outline
+ * view.
+ */
+public abstract class OutlineViewerCreator {
+
+ /**
+ * Property constant that identifies the input of the outline view.
+ */
+ public static final String PROP_INPUT = "org.eclipse.compare.OutlineInput"; //$NON-NLS-1$
+
+ private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+
+ /**
+ * Method called by the editor to create a structure viewer for the current content merge viewer.
+ * @param oldViewer the current viewer that is being used to show the structure
+ * @param input the input
+ * @param parent the parent composite
+ * @param configuration the compare configuration
+ * @return a viewer to be placed in the outline viewer or <code>null</code>
+ */
+ public abstract Viewer findStructureViewer(Viewer oldViewer, ICompareInput input,
+ Composite parent, CompareConfiguration configuration);
+
+ public abstract boolean hasViewerFor(Object input);
+
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ public void fireInputChange(Object oldInput, Object newInput) {
+ Object[] list = listeners.getListeners();
+ final PropertyChangeEvent event = new PropertyChangeEvent(this, PROP_INPUT, oldInput, newInput);
+ for (int i = 0; i < list.length; i++) {
+ final IPropertyChangeListener listener = (IPropertyChangeListener)list[i];
+ SafeRunner.run(new ISafeRunnable() {
+ public void run() throws Exception {
+ listener.propertyChange(event);
+ }
+ public void handleException(Throwable exception) {
+ // Logged by SafeRunner
+ }
+ });
+ }
+ }
+
+ public abstract Object getInput();
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OverlayPreferenceStore.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OverlayPreferenceStore.java
new file mode 100644
index 000000000..de7ba253a
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/OverlayPreferenceStore.java
@@ -0,0 +1,452 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+/**
+ * An overlaying preference store.
+ */
+public class OverlayPreferenceStore implements IPreferenceStore {
+
+
+ public static final class TypeDescriptor {
+ private TypeDescriptor() {
+ // nothing to do
+ }
+ }
+
+ public static final TypeDescriptor BOOLEAN= new TypeDescriptor();
+ public static final TypeDescriptor DOUBLE= new TypeDescriptor();
+ public static final TypeDescriptor FLOAT= new TypeDescriptor();
+ public static final TypeDescriptor INT= new TypeDescriptor();
+ public static final TypeDescriptor LONG= new TypeDescriptor();
+ public static final TypeDescriptor STRING= new TypeDescriptor();
+
+ public static class OverlayKey {
+
+ TypeDescriptor fDescriptor;
+ String fKey;
+
+ public OverlayKey(TypeDescriptor descriptor, String key) {
+ fDescriptor= descriptor;
+ fKey= key;
+ }
+ }
+
+ private class PropertyListener implements IPropertyChangeListener {
+
+ /*
+ * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ OverlayKey key= findOverlayKey(event.getProperty());
+ if (key != null)
+ propagateProperty(fParent, key, fStore);
+ }
+ }
+
+
+ private IPreferenceStore fParent;
+ private IPreferenceStore fStore;
+ private OverlayKey[] fOverlayKeys;
+
+ private PropertyListener fPropertyListener;
+
+
+ public OverlayPreferenceStore(IPreferenceStore parent, OverlayKey[] overlayKeys) {
+ fParent= parent;
+ fOverlayKeys= overlayKeys;
+ fStore= new PreferenceStore();
+ }
+
+ private OverlayKey findOverlayKey(String key) {
+ for (int i= 0; i < fOverlayKeys.length; i++) {
+ if (fOverlayKeys[i].fKey.equals(key))
+ return fOverlayKeys[i];
+ }
+ return null;
+ }
+
+ private boolean covers(String key) {
+ return (findOverlayKey(key) != null);
+ }
+
+ private void propagateProperty(IPreferenceStore orgin, OverlayKey key, IPreferenceStore target) {
+
+ if (orgin.isDefault(key.fKey)) {
+ if (!target.isDefault(key.fKey))
+ target.setToDefault(key.fKey);
+ return;
+ }
+
+ TypeDescriptor d= key.fDescriptor;
+ if (BOOLEAN == d) {
+
+ boolean originValue= orgin.getBoolean(key.fKey);
+ boolean targetValue= target.getBoolean(key.fKey);
+ if (targetValue != originValue)
+ target.setValue(key.fKey, originValue);
+
+ } else if (DOUBLE == d) {
+
+ double originValue= orgin.getDouble(key.fKey);
+ double targetValue= target.getDouble(key.fKey);
+ if (targetValue != originValue)
+ target.setValue(key.fKey, originValue);
+
+ } else if (FLOAT == d) {
+
+ float originValue= orgin.getFloat(key.fKey);
+ float targetValue= target.getFloat(key.fKey);
+ if (targetValue != originValue)
+ target.setValue(key.fKey, originValue);
+
+ } else if (INT == d) {
+
+ int originValue= orgin.getInt(key.fKey);
+ int targetValue= target.getInt(key.fKey);
+ if (targetValue != originValue)
+ target.setValue(key.fKey, originValue);
+
+ } else if (LONG == d) {
+
+ long originValue= orgin.getLong(key.fKey);
+ long targetValue= target.getLong(key.fKey);
+ if (targetValue != originValue)
+ target.setValue(key.fKey, originValue);
+
+ } else if (STRING == d) {
+
+ String originValue= orgin.getString(key.fKey);
+ String targetValue= target.getString(key.fKey);
+ if (targetValue != null && originValue != null && !targetValue.equals(originValue))
+ target.setValue(key.fKey, originValue);
+
+ }
+ }
+
+ public void propagate() {
+ for (int i= 0; i < fOverlayKeys.length; i++)
+ propagateProperty(fStore, fOverlayKeys[i], fParent);
+ }
+
+ private void loadProperty(IPreferenceStore orgin, OverlayKey key, IPreferenceStore target, boolean forceInitialization) {
+ TypeDescriptor d= key.fDescriptor;
+ if (BOOLEAN == d) {
+
+ if (forceInitialization)
+ target.setValue(key.fKey, true);
+ target.setValue(key.fKey, orgin.getBoolean(key.fKey));
+ target.setDefault(key.fKey, orgin.getDefaultBoolean(key.fKey));
+
+ } else if (DOUBLE == d) {
+
+ if (forceInitialization)
+ target.setValue(key.fKey, 1.0D);
+ target.setValue(key.fKey, orgin.getDouble(key.fKey));
+ target.setDefault(key.fKey, orgin.getDefaultDouble(key.fKey));
+
+ } else if (FLOAT == d) {
+
+ if (forceInitialization)
+ target.setValue(key.fKey, 1.0F);
+ target.setValue(key.fKey, orgin.getFloat(key.fKey));
+ target.setDefault(key.fKey, orgin.getDefaultFloat(key.fKey));
+
+ } else if (INT == d) {
+
+ if (forceInitialization)
+ target.setValue(key.fKey, 1);
+ target.setValue(key.fKey, orgin.getInt(key.fKey));
+ target.setDefault(key.fKey, orgin.getDefaultInt(key.fKey));
+
+ } else if (LONG == d) {
+
+ if (forceInitialization)
+ target.setValue(key.fKey, 1L);
+ target.setValue(key.fKey, orgin.getLong(key.fKey));
+ target.setDefault(key.fKey, orgin.getDefaultLong(key.fKey));
+
+ } else if (STRING == d) {
+
+ if (forceInitialization)
+ target.setValue(key.fKey, "1"); //$NON-NLS-1$
+ target.setValue(key.fKey, orgin.getString(key.fKey));
+ target.setDefault(key.fKey, orgin.getDefaultString(key.fKey));
+
+ }
+ }
+
+ public void load() {
+ for (int i= 0; i < fOverlayKeys.length; i++)
+ loadProperty(fParent, fOverlayKeys[i], fStore, true);
+ }
+
+ public void loadDefaults() {
+ for (int i= 0; i < fOverlayKeys.length; i++)
+ setToDefault(fOverlayKeys[i].fKey);
+ }
+
+ public void start() {
+ if (fPropertyListener == null) {
+ fPropertyListener= new PropertyListener();
+ fParent.addPropertyChangeListener(fPropertyListener);
+ }
+ }
+
+ public void stop() {
+ if (fPropertyListener != null) {
+ fParent.removePropertyChangeListener(fPropertyListener);
+ fPropertyListener= null;
+ }
+ }
+
+ /*
+ * @see IPreferenceStore#addPropertyChangeListener(IPropertyChangeListener)
+ */
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ fStore.addPropertyChangeListener(listener);
+ }
+
+ /*
+ * @see IPreferenceStore#removePropertyChangeListener(IPropertyChangeListener)
+ */
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ fStore.removePropertyChangeListener(listener);
+ }
+
+ /*
+ * @see IPreferenceStore#firePropertyChangeEvent(String, Object, Object)
+ */
+ public void firePropertyChangeEvent(String name, Object oldValue, Object newValue) {
+ fStore.firePropertyChangeEvent(name, oldValue, newValue);
+ }
+
+ /*
+ * @see IPreferenceStore#contains(String)
+ */
+ public boolean contains(String name) {
+ return fStore.contains(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getBoolean(String)
+ */
+ public boolean getBoolean(String name) {
+ return fStore.getBoolean(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getDefaultBoolean(String)
+ */
+ public boolean getDefaultBoolean(String name) {
+ return fStore.getDefaultBoolean(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getDefaultDouble(String)
+ */
+ public double getDefaultDouble(String name) {
+ return fStore.getDefaultDouble(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getDefaultFloat(String)
+ */
+ public float getDefaultFloat(String name) {
+ return fStore.getDefaultFloat(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getDefaultInt(String)
+ */
+ public int getDefaultInt(String name) {
+ return fStore.getDefaultInt(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getDefaultLong(String)
+ */
+ public long getDefaultLong(String name) {
+ return fStore.getDefaultLong(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getDefaultString(String)
+ */
+ public String getDefaultString(String name) {
+ return fStore.getDefaultString(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getDouble(String)
+ */
+ public double getDouble(String name) {
+ return fStore.getDouble(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getFloat(String)
+ */
+ public float getFloat(String name) {
+ return fStore.getFloat(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getInt(String)
+ */
+ public int getInt(String name) {
+ return fStore.getInt(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getLong(String)
+ */
+ public long getLong(String name) {
+ return fStore.getLong(name);
+ }
+
+ /*
+ * @see IPreferenceStore#getString(String)
+ */
+ public String getString(String name) {
+ return fStore.getString(name);
+ }
+
+ /*
+ * @see IPreferenceStore#isDefault(String)
+ */
+ public boolean isDefault(String name) {
+ return fStore.isDefault(name);
+ }
+
+ /*
+ * @see IPreferenceStore#needsSaving()
+ */
+ public boolean needsSaving() {
+ return fStore.needsSaving();
+ }
+
+ /*
+ * @see IPreferenceStore#putValue(String, String)
+ */
+ public void putValue(String name, String value) {
+ if (covers(name))
+ fStore.putValue(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setDefault(String, double)
+ */
+ public void setDefault(String name, double value) {
+ if (covers(name))
+ fStore.setDefault(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setDefault(String, float)
+ */
+ public void setDefault(String name, float value) {
+ if (covers(name))
+ fStore.setDefault(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setDefault(String, int)
+ */
+ public void setDefault(String name, int value) {
+ if (covers(name))
+ fStore.setDefault(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setDefault(String, long)
+ */
+ public void setDefault(String name, long value) {
+ if (covers(name))
+ fStore.setDefault(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setDefault(String, String)
+ */
+ public void setDefault(String name, String value) {
+ if (covers(name))
+ fStore.setDefault(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setDefault(String, boolean)
+ */
+ public void setDefault(String name, boolean value) {
+ if (covers(name))
+ fStore.setDefault(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setToDefault(String)
+ */
+ public void setToDefault(String name) {
+ fStore.setToDefault(name);
+ }
+
+ /*
+ * @see IPreferenceStore#setValue(String, double)
+ */
+ public void setValue(String name, double value) {
+ if (covers(name))
+ fStore.setValue(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setValue(String, float)
+ */
+ public void setValue(String name, float value) {
+ if (covers(name))
+ fStore.setValue(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setValue(String, int)
+ */
+ public void setValue(String name, int value) {
+ if (covers(name))
+ fStore.setValue(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setValue(String, long)
+ */
+ public void setValue(String name, long value) {
+ if (covers(name))
+ fStore.setValue(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setValue(String, String)
+ */
+ public void setValue(String name, String value) {
+ if (covers(name))
+ fStore.setValue(name, value);
+ }
+
+ /*
+ * @see IPreferenceStore#setValue(String, boolean)
+ */
+ public void setValue(String name, boolean value) {
+ if (covers(name))
+ fStore.setValue(name, value);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java
new file mode 100644
index 000000000..4d5eca397
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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;
+
+
+public class ReplaceWithEditionAction extends EditionAction {
+
+ public ReplaceWithEditionAction() {
+ super(true, "org.eclipse.compare.internal.ReplaceWithEditionAction"); //$NON-NLS-1$
+ fHelpContextId= ICompareContextIds.REPLACE_WITH_EDITION_DIALOG;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties
new file mode 100644
index 000000000..94d650eac
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties
@@ -0,0 +1,40 @@
+###############################################################################
+# Copyright (c) 2000, 2006 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
+###############################################################################
+
+# @(#)ReplaceWithEditionAction.properties
+#
+# Resources for ReplaceWithEditionAction.java
+
+title= Replace from Local History
+
+treeTitleFormat= Local History of ''{0}''
+dateIcon= obj16/day_obj.gif
+timeIcon= obj16/resource_obj.gif
+
+treeFormat= {0}
+workspaceTreeFormat= {0} (Workspace File)
+parseErrorFormat= {0} (Parse Error)
+
+editionLabel= Local History ({0})
+workspaceEditionLabel= Workspace File
+
+targetLabel= {0}
+
+todayFormat= Today ({0})
+yesterdayFormat= Yesterday ({0})
+dayFormat= {0}
+
+buttonLabel= Replace
+
+noLocalHistoryError= No local history available for selected resource.
+replaceError=Cannot replace resource (reason: {0}).
+
+taskName=Replacing
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithPreviousEditionAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithPreviousEditionAction.java
new file mode 100644
index 000000000..ecd4e87be
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithPreviousEditionAction.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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;
+
+
+public class ReplaceWithPreviousEditionAction extends EditionAction {
+
+ public ReplaceWithPreviousEditionAction() {
+ super(true,
+ "org.eclipse.compare.internal.ReplaceWithEditionAction"); //$NON-NLS-1$
+ fPrevious= true;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResizableDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResizableDialog.java
new file mode 100644
index 000000000..d18bf6c7c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResizableDialog.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.DialogSettings;
+
+
+/**
+ * Base class for resizable Dialogs with persistent window bounds.
+ */
+public abstract class ResizableDialog extends Dialog {
+
+ // dialog store id constants
+ private final static String DIALOG_BOUNDS_KEY= "ResizableDialogBounds"; //$NON-NLS-1$
+ private static final String X= "x"; //$NON-NLS-1$
+ private static final String Y= "y"; //$NON-NLS-1$
+ private static final String WIDTH= "width"; //$NON-NLS-1$
+ private static final String HEIGHT= "height"; //$NON-NLS-1$
+
+ protected ResourceBundle fBundle;
+ private Rectangle fNewBounds;
+ private IDialogSettings fSettings;
+ private String fContextId;
+
+
+ public ResizableDialog(Shell parent, ResourceBundle bundle) {
+ super(parent);
+ setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
+
+ fBundle= bundle;
+
+ fSettings= CompareUIPlugin.getDefault().getDialogSettings();
+ }
+
+ public void setHelpContextId(String contextId) {
+ fContextId= contextId;
+ }
+
+ /*
+ * @see org.eclipse.jface.window.Window#configureShell(Shell)
+ */
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ if (fContextId != null)
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell, fContextId);
+ }
+
+ protected Point getInitialSize() {
+
+ int width= 0;
+ int height= 0;
+
+ final Shell s= getShell();
+ if (s != null) {
+ s.addControlListener(
+ new ControlListener() {
+ public void controlMoved(ControlEvent arg0) {
+ fNewBounds= s.getBounds();
+ }
+ public void controlResized(ControlEvent arg0) {
+ fNewBounds= s.getBounds();
+ }
+ }
+ );
+ }
+
+ IDialogSettings bounds= fSettings.getSection(DIALOG_BOUNDS_KEY);
+ if (bounds == null) {
+ if (fBundle != null) {
+ width= Utilities.getInteger(fBundle, WIDTH, 0);
+ height= Utilities.getInteger(fBundle, HEIGHT, 0);
+ Shell shell= getParentShell();
+ if (shell != null) {
+ Point parentSize= shell.getSize();
+ if (width <= 0)
+ width= parentSize.x-300;
+ if (height <= 0)
+ height= parentSize.y-200;
+ }
+ } else {
+ Shell shell= getParentShell();
+ if (shell != null) {
+ Point parentSize= shell.getSize();
+ width= parentSize.x-100;
+ height= parentSize.y-100;
+ }
+ }
+ if (width < 700)
+ width= 700;
+ if (height < 500)
+ height= 500;
+ } else {
+ try {
+ width= bounds.getInt(WIDTH);
+ } catch (NumberFormatException e) {
+ width= 700;
+ }
+ try {
+ height= bounds.getInt(HEIGHT);
+ } catch (NumberFormatException e) {
+ height= 500;
+ }
+ }
+
+ return new Point(width, height);
+ }
+
+ protected Point getInitialLocation(Point initialSize) {
+ Point loc= super.getInitialLocation(initialSize);
+
+ IDialogSettings bounds= fSettings.getSection(DIALOG_BOUNDS_KEY);
+ if (bounds != null) {
+ try {
+ loc.x= bounds.getInt(X);
+ } catch (NumberFormatException e) {
+ // silently ignored
+ }
+ try {
+ loc.y= bounds.getInt(Y);
+ } catch (NumberFormatException e) {
+ // silently ignored
+ }
+ }
+ return loc;
+ }
+
+ public boolean close() {
+ boolean closed= super.close();
+ if (closed && fNewBounds != null)
+ saveBounds(fNewBounds);
+ return closed;
+ }
+
+ private void saveBounds(Rectangle bounds) {
+ IDialogSettings dialogBounds= fSettings.getSection(DIALOG_BOUNDS_KEY);
+ if (dialogBounds == null) {
+ dialogBounds= new DialogSettings(DIALOG_BOUNDS_KEY);
+ fSettings.addSection(dialogBounds);
+ }
+ dialogBounds.put(X, bounds.x);
+ dialogBounds.put(Y, bounds.y);
+ dialogBounds.put(WIDTH, bounds.width);
+ dialogBounds.put(HEIGHT, bounds.height);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
new file mode 100644
index 000000000..fe72e6b24
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
@@ -0,0 +1,553 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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
+ * Matt McCutchen (hashproduct+eclipse@gmail.com) - Bug 35390 Three-way compare cannot select (mis-selects) )ancestor resource
+ * Aleksandra Wozniak (aleksandra.k.wozniak@gmail.com) - Bug 239959
+ *******************************************************************************/
+package org.eclipse.compare.internal;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.ZipFileStructureCreator;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.DiffTreeViewer;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.IDiffContainer;
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.compare.structuremergeviewer.IStructureComparator;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+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.Shell;
+
+import com.ibm.icu.text.MessageFormat;
+
+
+/**
+ * A two-way or three-way compare for arbitrary IResources.
+ */
+class ResourceCompareInput extends CompareEditorInput {
+
+ private static final boolean NORMALIZE_CASE= true;
+
+ private boolean fThreeWay= false;
+ private Object fRoot;
+ private IStructureComparator fAncestor;
+ private IStructureComparator fLeft;
+ private IStructureComparator fRight;
+ private IResource fAncestorResource;
+ private IResource fLeftResource;
+ private IResource fRightResource;
+ private DiffTreeViewer fDiffViewer;
+ private IAction fOpenAction;
+
+ class MyDiffNode extends DiffNode {
+
+ private boolean fDirty= false;
+ private ITypedElement fLastId;
+ private String fLastName;
+
+
+ public MyDiffNode(IDiffContainer parent, int description, ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ super(parent, description, ancestor, left, right);
+ }
+ public void fireChange() {
+ super.fireChange();
+ setDirty(true);
+ fDirty= true;
+ if (fDiffViewer != null)
+ fDiffViewer.refresh(this);
+ }
+ void clearDirty() {
+ fDirty= false;
+ }
+ public String getName() {
+ if (fLastName == null)
+ fLastName= super.getName();
+ if (fDirty)
+ return '<' + fLastName + '>';
+ return fLastName;
+ }
+
+ public ITypedElement getId() {
+ ITypedElement id= super.getId();
+ if (id == null)
+ return fLastId;
+ fLastId= id;
+ return id;
+ }
+ }
+
+ static class FilteredBufferedResourceNode extends BufferedResourceNode {
+ FilteredBufferedResourceNode(IResource resource) {
+ super(resource);
+ }
+ protected IStructureComparator createChild(IResource child) {
+ String name= child.getName();
+ if (CompareUIPlugin.getDefault().filter(name, child instanceof IContainer, false))
+ return null;
+ return new FilteredBufferedResourceNode(child);
+ }
+ }
+
+ /*
+ * Creates an compare editor input for the given selection.
+ */
+ ResourceCompareInput(CompareConfiguration config) {
+ super(config);
+ }
+
+ public Viewer createDiffViewer(Composite parent) {
+ fDiffViewer= new DiffTreeViewer(parent, getCompareConfiguration()) {
+ protected void fillContextMenu(IMenuManager manager) {
+
+ if (fOpenAction == null) {
+ fOpenAction= new Action() {
+ public void run() {
+ handleOpen(null);
+ }
+ };
+ Utilities.initAction(fOpenAction, getBundle(), "action.CompareContents."); //$NON-NLS-1$
+ }
+
+ boolean enable= false;
+ ISelection selection= getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss= (IStructuredSelection)selection;
+ if (ss.size() == 1) {
+ Object element= ss.getFirstElement();
+ if (element instanceof MyDiffNode) {
+ ITypedElement te= ((MyDiffNode) element).getId();
+ if (te != null)
+ enable= !ITypedElement.FOLDER_TYPE.equals(te.getType());
+ } else
+ enable= true;
+ }
+ }
+ fOpenAction.setEnabled(enable);
+
+ manager.add(fOpenAction);
+
+ super.fillContextMenu(manager);
+ }
+ };
+ return fDiffViewer;
+ }
+
+ class SelectAncestorDialog extends MessageDialog {
+ private IResource[] theResources;
+ IResource ancestorResource;
+ IResource leftResource;
+ IResource rightResource;
+
+ private Button[] buttons;
+
+ public SelectAncestorDialog(Shell parentShell, IResource[] theResources) {
+ super(parentShell, CompareMessages.SelectAncestorDialog_title,
+ null, CompareMessages.SelectAncestorDialog_message,
+ MessageDialog.QUESTION,
+ new String[] { IDialogConstants.OK_LABEL,
+ IDialogConstants.CANCEL_LABEL }, 0);
+ this.theResources = theResources;
+ }
+
+ protected Control createCustomArea(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout());
+ buttons = new Button[3];
+ for (int i = 0; i < 3; i++) {
+ buttons[i] = new Button(composite, SWT.RADIO);
+ buttons[i].addSelectionListener(selectionListener);
+ buttons[i].setText(NLS.bind(CompareMessages.SelectAncestorDialog_option,
+ theResources[i].getFullPath().toPortableString()));
+ buttons[i].setFont(parent.getFont());
+ // set initial state
+ buttons[i].setSelection(i == 0);
+ }
+ pickAncestor(0);
+ return composite;
+ }
+
+ private void pickAncestor(int i) {
+ ancestorResource = theResources[i];
+ leftResource = theResources[i == 0 ? 1 : 0];
+ rightResource = theResources[i == 2 ? 1 : 2];
+ }
+
+ private SelectionListener selectionListener = new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ Button selectedButton = (Button) e.widget;
+ if (!selectedButton.getSelection())
+ return;
+ for (int i = 0; i < 3; i++)
+ if (selectedButton == buttons[i])
+ pickAncestor(i);
+ }
+ };
+ }
+ // If the compare is three-way, this method asks the user which resource
+ // to use as the ancestor. Depending on the value of
+ // showSelectAncestorDialog flag it uses different dialogs to get the
+ // feedback from the user. Returns false if the user cancels the prompt,
+ // true otherwise.
+ boolean setSelection(ISelection s, Shell shell, boolean showSelectAncestorDialog) {
+
+ if (!showSelectAncestorDialog)
+ return showCompareWithOtherResourceDialog(shell, s);
+
+ IResource[] selection= Utilities.getResources(s);
+
+ fThreeWay= selection.length == 3;
+
+ if (fThreeWay) {
+ SelectAncestorDialog dialog =
+ new SelectAncestorDialog(shell, selection);
+ int code = dialog.open();
+ if (code != Window.OK)
+ return false;
+
+ fAncestorResource= dialog.ancestorResource;
+ fAncestor= getStructure(fAncestorResource);
+ fLeftResource= dialog.leftResource;
+ fRightResource= dialog.rightResource;
+ } else {
+ fAncestorResource= null;
+ fAncestor= null;
+ fLeftResource= selection[0];
+ fRightResource= selection[1];
+ }
+ fLeft= getStructure(fLeftResource);
+ fRight= getStructure(fRightResource);
+ return true;
+ }
+
+ private boolean showCompareWithOtherResourceDialog(Shell shell, ISelection s) {
+ CompareWithOtherResourceDialog dialog = new CompareWithOtherResourceDialog(shell, s);
+ if (dialog.open() != IDialogConstants.OK_ID)
+ return false;
+ IResource[] selection = dialog.getResult();
+ if (!checkSelection(selection))
+ return false;
+
+ fThreeWay = selection.length == 3;
+ if (fThreeWay) {
+ fAncestorResource = selection[0];
+ fAncestor = getStructure(fAncestorResource);
+ fLeftResource = selection[1];
+ fRightResource = selection[2];
+ } else {
+ fAncestorResource = null;
+ fAncestor = null;
+ fLeftResource = selection[0];
+ fRightResource = selection[1];
+ }
+ fLeft= getStructure(fLeftResource);
+ fRight= getStructure(fRightResource);
+ return true;
+ }
+
+ private boolean checkSelection(IResource[] resources) {
+ for (int i = 0; i < resources.length; i++)
+ if (resources[i] == null)
+ return false;
+ return true;
+ }
+
+ /*
+ * Returns true if compare can be executed for the given selection.
+ */
+ public boolean isEnabled(ISelection s) {
+
+ IResource[] selection= Utilities.getResources(s);
+ if (selection.length < 2 || selection.length > 3)
+ return false;
+
+ boolean threeWay= selection.length == 3;
+
+ if (threeWay)
+ // It only makes sense if they're all mutually comparable.
+ // If not, the user should compare two of them.
+ return comparable(selection[0], selection[1])
+ && comparable(selection[0], selection[2])
+ && comparable(selection[1], selection[2]);
+
+ return comparable(selection[0], selection[1]);
+ }
+
+ /**
+ * Initializes the images in the compare configuration.
+ */
+ void initializeCompareConfiguration() {
+ CompareConfiguration cc= getCompareConfiguration();
+ if (fLeftResource != null) {
+ cc.setLeftLabel(buildLabel(fLeftResource));
+ cc.setLeftImage(CompareUIPlugin.getImage(fLeftResource));
+ }
+ if (fRightResource != null) {
+ cc.setRightLabel(buildLabel(fRightResource));
+ cc.setRightImage(CompareUIPlugin.getImage(fRightResource));
+ }
+ if (fThreeWay && fAncestorResource != null) {
+ cc.setAncestorLabel(buildLabel(fAncestorResource));
+ cc.setAncestorImage(CompareUIPlugin.getImage(fAncestorResource));
+ }
+ }
+
+ /*
+ * Returns true if both resources are either structured or unstructured.
+ */
+ private boolean comparable(IResource c1, IResource c2) {
+ return hasStructure(c1) == hasStructure(c2);
+ }
+
+ /*
+ * Returns true if the given argument has a structure.
+ */
+ private boolean hasStructure(IResource input) {
+
+ if (input instanceof IContainer)
+ return true;
+
+ if (input instanceof IFile) {
+ IFile file= (IFile) input;
+ String type= file.getFileExtension();
+ if (type != null) {
+ type= normalizeCase(type);
+ return "JAR".equals(type) || "ZIP".equals(type); //$NON-NLS-2$ //$NON-NLS-1$
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * Creates a <code>IStructureComparator</code> for the given input.
+ * Returns <code>null</code> if no <code>IStructureComparator</code>
+ * can be found for the <code>IResource</code>.
+ */
+ private IStructureComparator getStructure(IResource input) {
+
+ if (input instanceof IContainer)
+ return new FilteredBufferedResourceNode(input);
+
+ if (input instanceof IFile) {
+ IStructureComparator rn= new FilteredBufferedResourceNode(input);
+ IFile file= (IFile) input;
+ String type= normalizeCase(file.getFileExtension());
+ if ("JAR".equals(type) || "ZIP".equals(type)) //$NON-NLS-2$ //$NON-NLS-1$
+ return new ZipFileStructureCreator().getStructure(rn);
+ return rn;
+ }
+ return null;
+ }
+
+ /*
+ * Performs a two-way or three-way diff on the current selection.
+ */
+ public Object prepareInput(IProgressMonitor pm) throws InvocationTargetException {
+
+ try {
+ // fix for PR 1GFMLFB: ITPUI:WIN2000 - files that are out of sync with the file system appear as empty
+ fLeftResource.refreshLocal(IResource.DEPTH_INFINITE, pm);
+ fRightResource.refreshLocal(IResource.DEPTH_INFINITE, pm);
+ if (fThreeWay && fAncestorResource != null)
+ fAncestorResource.refreshLocal(IResource.DEPTH_INFINITE, pm);
+ // end fix
+
+ pm.beginTask(Utilities.getString("ResourceCompare.taskName"), IProgressMonitor.UNKNOWN); //$NON-NLS-1$
+
+ String leftLabel= fLeftResource.getName();
+ String rightLabel= fRightResource.getName();
+
+ String title;
+ if (fThreeWay) {
+ String format= Utilities.getString("ResourceCompare.threeWay.title"); //$NON-NLS-1$
+ String ancestorLabel= fAncestorResource.getName();
+ title= MessageFormat.format(format, new String[] {ancestorLabel, leftLabel, rightLabel});
+ } else {
+ String format= Utilities.getString("ResourceCompare.twoWay.title"); //$NON-NLS-1$
+ title= MessageFormat.format(format, new String[] {leftLabel, rightLabel});
+ }
+ setTitle(title);
+
+ Differencer d= new Differencer() {
+ protected Object visit(Object parent, int description, Object ancestor, Object left, Object right) {
+ return new MyDiffNode((IDiffContainer) parent, description, (ITypedElement)ancestor, (ITypedElement)left, (ITypedElement)right);
+ }
+ };
+
+ fRoot= d.findDifferences(fThreeWay, pm, null, fAncestor, fLeft, fRight);
+ return fRoot;
+
+ } catch (CoreException ex) {
+ throw new InvocationTargetException(ex);
+ } finally {
+ pm.done();
+ }
+ }
+
+ public String getToolTipText() {
+ if (fLeftResource != null && fRightResource != null) {
+ String leftLabel= fLeftResource.getFullPath().makeRelative().toString();
+ String rightLabel= fRightResource.getFullPath().makeRelative().toString();
+ if (fThreeWay) {
+ String format= Utilities.getString("ResourceCompare.threeWay.tooltip"); //$NON-NLS-1$
+ String ancestorLabel= fAncestorResource.getFullPath().makeRelative().toString();
+ return MessageFormat.format(format, new String[] {ancestorLabel, leftLabel, rightLabel});
+ }
+ String format= Utilities.getString("ResourceCompare.twoWay.tooltip"); //$NON-NLS-1$
+ return MessageFormat.format(format, new String[] {leftLabel, rightLabel});
+ }
+ // fall back
+ return super.getToolTipText();
+ }
+
+ private String buildLabel(IResource r) {
+ // for a linked resource in a hidden project use its local file system location
+ if (r.isLinked() && r.getProject().isHidden())
+ return r.getLocation().toString();
+ String n= r.getFullPath().toString();
+ if (n.charAt(0) == IPath.SEPARATOR)
+ return n.substring(1);
+ return n;
+ }
+
+ public void saveChanges(IProgressMonitor pm) throws CoreException {
+ super.saveChanges(pm);
+ if (fRoot instanceof DiffNode) {
+ try {
+ commit(pm, (DiffNode) fRoot);
+ } finally {
+ if (fDiffViewer != null)
+ fDiffViewer.refresh();
+ setDirty(false);
+ }
+ }
+ }
+
+ /*
+ * Recursively walks the diff tree and commits all changes.
+ */
+ private static void commit(IProgressMonitor pm, DiffNode node) throws CoreException {
+
+ if (node instanceof MyDiffNode)
+ ((MyDiffNode)node).clearDirty();
+
+ ITypedElement left= node.getLeft();
+ if (left instanceof BufferedResourceNode)
+ ((BufferedResourceNode) left).commit(pm);
+
+ ITypedElement right= node.getRight();
+ if (right instanceof BufferedResourceNode)
+ ((BufferedResourceNode) right).commit(pm);
+
+ IDiffElement[] children= node.getChildren();
+ if (children != null) {
+ for (int i= 0; i < children.length; i++) {
+ IDiffElement element= children[i];
+ if (element instanceof DiffNode)
+ commit(pm, (DiffNode) element);
+ }
+ }
+ }
+
+ /* (non Javadoc)
+ * see IAdaptable.getAdapter
+ */
+ public Object getAdapter(Class adapter) {
+ if (IFile.class.equals(adapter)) {
+ IProgressMonitor pm= new NullProgressMonitor();
+ // flush changes in any dirty viewer
+ flushViewers(pm);
+ IFile[] files= (IFile[]) getAdapter(IFile[].class);
+ if (files != null && files.length > 0)
+ return files[0]; // can only return one: limitation on IDE.saveAllEditors; see #64617
+ return null;
+ }
+ if (IFile[].class.equals(adapter)) {
+ HashSet collector= new HashSet();
+ collectDirtyResources(fRoot, collector);
+ return collector.toArray(new IFile[collector.size()]);
+ }
+ return super.getAdapter(adapter);
+ }
+
+ private void collectDirtyResources(Object o, Set collector) {
+ if (o instanceof DiffNode) {
+ DiffNode node= (DiffNode) o;
+
+ ITypedElement left= node.getLeft();
+ if (left instanceof BufferedResourceNode) {
+ BufferedResourceNode bn= (BufferedResourceNode) left;
+ if (bn.isDirty()) {
+ IResource resource= bn.getResource();
+ if (resource instanceof IFile)
+ collector.add(resource);
+ }
+ }
+
+ ITypedElement right= node.getRight();
+ if (right instanceof BufferedResourceNode) {
+ BufferedResourceNode bn= (BufferedResourceNode) right;
+ if (bn.isDirty()) {
+ IResource resource= bn.getResource();
+ if (resource instanceof IFile)
+ collector.add(resource);
+ }
+ }
+
+ IDiffElement[] children= node.getChildren();
+ if (children != null) {
+ for (int i= 0; i < children.length; i++) {
+ IDiffElement element= children[i];
+ if (element instanceof DiffNode)
+ collectDirtyResources(element, collector);
+ }
+ }
+ }
+ }
+
+ private static String normalizeCase(String s) {
+ if (NORMALIZE_CASE && s != null)
+ return s.toUpperCase();
+ return s;
+ }
+
+ public boolean canRunAsJob() {
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowWhitespaceAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowWhitespaceAction.java
new file mode 100644
index 000000000..2b2e14771
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowWhitespaceAction.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.WhitespaceCharacterPainter;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.texteditor.AbstractTextEditor;
+
+public class ShowWhitespaceAction extends TextEditorPropertyAction {
+
+ private Map fPainters;
+ private boolean isWhitespaceShowing;
+ private boolean[] fNeedsPainters;
+ /** @since 3.7 */
+ private boolean fShowLeadingSpaces;
+ /** @since 3.7 */
+ private boolean fShowEnclosedSpaces;
+ /** @since 3.7 */
+ private boolean fShowTrailingSpaces;
+ /** @since 3.7 */
+ private boolean fShowLeadingIdeographicSpaces;
+ /** @since 3.7 */
+ private boolean fShowEnclosedIdeographicSpaces;
+ /** @since 3.7 */
+ private boolean fShowTrailingIdeographicSpace;
+ /** @since 3.7 */
+ private boolean fShowLeadingTabs;
+ /** @since 3.7 */
+ private boolean fShowEnclosedTabs;
+ /** @since 3.7 */
+ private boolean fShowTrailingTabs;
+ /** @since 3.7 */
+ private boolean fShowCarriageReturn;
+ /** @since 3.7 */
+ private boolean fShowLineFeed;
+ /** @since 3.7 */
+ private IPreferenceStore fStore = EditorsUI.getPreferenceStore();
+ /** @since 3.7 */
+ private int fAlpha;
+
+ public ShowWhitespaceAction(MergeSourceViewer[] viewers, boolean[] needsPainters) {
+ super(CompareMessages.ShowWhitespaceAction_0, viewers, AbstractTextEditor.PREFERENCE_SHOW_WHITESPACE_CHARACTERS);
+ fNeedsPainters = needsPainters;
+ synchronizeWithPreference();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.compare.internal.TextEditorPropertyAction#synchronizeWithPreference()
+ */
+ protected void synchronizeWithPreference() {
+ boolean checked = false;
+ if (fStore != null) {
+ checked = fStore.getBoolean(getPreferenceKey());
+ fShowLeadingSpaces = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_LEADING_SPACES);
+ fShowEnclosedSpaces = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_ENCLOSED_SPACES);
+ fShowTrailingSpaces = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_TRAILING_SPACES);
+ fShowLeadingIdeographicSpaces = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES);
+ fShowEnclosedIdeographicSpaces = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES);
+ fShowTrailingIdeographicSpace = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES);
+ fShowLeadingTabs = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_LEADING_TABS);
+ fShowEnclosedTabs = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_ENCLOSED_TABS);
+ fShowTrailingTabs = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_TRAILING_TABS);
+ fShowCarriageReturn = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_CARRIAGE_RETURN);
+ fShowLineFeed = fStore.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_LINE_FEED);
+ fAlpha = fStore.getInt(AbstractTextEditor.PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE);
+ }
+ if (checked != isChecked()) {
+ if (toggleState(checked))
+ setChecked(checked);
+ } else if (fNeedsPainters != null && checked) {
+ hideWhitespace();
+ showWhitespace();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.compare.internal.TextEditorPropertyAction#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ String property = event.getProperty();
+ if (property.equals(getPreferenceKey()) || AbstractTextEditor.PREFERENCE_SHOW_LEADING_SPACES.equals(property) || AbstractTextEditor.PREFERENCE_SHOW_ENCLOSED_SPACES.equals(property)
+ || AbstractTextEditor.PREFERENCE_SHOW_TRAILING_SPACES.equals(property) || AbstractTextEditor.PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES.equals(property)
+ || AbstractTextEditor.PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES.equals(property) || AbstractTextEditor.PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES.equals(property)
+ || AbstractTextEditor.PREFERENCE_SHOW_LEADING_TABS.equals(property) || AbstractTextEditor.PREFERENCE_SHOW_ENCLOSED_TABS.equals(property)
+ || AbstractTextEditor.PREFERENCE_SHOW_TRAILING_TABS.equals(property) || AbstractTextEditor.PREFERENCE_SHOW_CARRIAGE_RETURN.equals(property)
+ || AbstractTextEditor.PREFERENCE_SHOW_LINE_FEED.equals(property) || AbstractTextEditor.PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE.equals(property)) {
+ synchronizeWithPreference();
+ }
+ }
+
+ protected boolean toggleState(boolean checked) {
+ if (fNeedsPainters == null)
+ return false; // Not initialized yet
+ if (checked) {
+ showWhitespace();
+ } else {
+ hideWhitespace();
+ }
+ return true;
+ }
+
+ private synchronized Map getPainters() {
+ if (fPainters == null)
+ fPainters = new HashMap();
+ return fPainters;
+ }
+
+ private void showWhitespace() {
+ if (isWhitespaceShowing)
+ return;
+ try {
+ Map painters = getPainters();
+ MergeSourceViewer[] viewers = getViewers();
+ for (int i = 0; i < viewers.length; i++) {
+ if (fNeedsPainters[i]) {
+ MergeSourceViewer viewer = viewers[i];
+ SourceViewer sourceViewer = viewer.getSourceViewer();
+ WhitespaceCharacterPainter painter;
+ if (fStore != null) {
+ painter = new WhitespaceCharacterPainter(sourceViewer, fShowLeadingSpaces, fShowEnclosedSpaces, fShowTrailingSpaces, fShowLeadingIdeographicSpaces,
+ fShowEnclosedIdeographicSpaces, fShowTrailingIdeographicSpace, fShowLeadingTabs, fShowEnclosedTabs, fShowTrailingTabs, fShowCarriageReturn, fShowLineFeed, fAlpha);
+ } else {
+ painter = new WhitespaceCharacterPainter(sourceViewer);
+ }
+ sourceViewer.addPainter(painter);
+ painters.put(viewer, painter);
+ }
+ }
+ } finally {
+ isWhitespaceShowing = true;
+ }
+ }
+
+ private void hideWhitespace() {
+ Map painters = getPainters();
+ for (Iterator iterator = painters.keySet().iterator(); iterator.hasNext();) {
+ MergeSourceViewer viewer = (MergeSourceViewer) iterator.next();
+ WhitespaceCharacterPainter painter = (WhitespaceCharacterPainter)painters.get(viewer);
+ if (painter != null) {
+ viewer.getSourceViewer().removePainter(painter);
+ painter.deactivate(true);
+ }
+ }
+ painters.clear();
+ isWhitespaceShowing = false;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/SimpleTextViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/SimpleTextViewer.java
new file mode 100644
index 000000000..a3d1bac5d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/SimpleTextViewer.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.Document;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+
+
+public class SimpleTextViewer extends AbstractViewer {
+
+ private SourceViewer fSourceViewer;
+ private ICompareInput fInput;
+
+
+ SimpleTextViewer(Composite parent) {
+ fSourceViewer= new SourceViewer(parent, null, SWT.H_SCROLL | SWT.V_SCROLL);
+ fSourceViewer.setEditable(false);
+ }
+
+ public Control getControl() {
+ return fSourceViewer.getTextWidget();
+ }
+
+ public void setInput(Object input) {
+ if (input instanceof IStreamContentAccessor) {
+ fSourceViewer.setDocument(new Document(getString(input)));
+ } else if (input instanceof ICompareInput) {
+ fInput= (ICompareInput) input;
+ ITypedElement left= fInput.getLeft();
+ fSourceViewer.setDocument(new Document(getString(left)));
+ }
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ private String getString(Object input) {
+
+ if (input instanceof IStreamContentAccessor) {
+ try {
+ return Utilities.readString((IStreamContentAccessor) input);
+ } catch (CoreException ex) {
+ // NeedWork
+ CompareUIPlugin.log(ex);
+ }
+ }
+ return ""; //$NON-NLS-1$
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StreamMergerDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StreamMergerDescriptor.java
new file mode 100644
index 000000000..5fc385ed2
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StreamMergerDescriptor.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 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 org.eclipse.compare.IStreamMerger;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+
+/**
+ * A factory proxy for creating a StructureCreator.
+ */
+class StreamMergerDescriptor {
+
+ private final static String CLASS_ATTRIBUTE= "class"; //$NON-NLS-1$
+
+ private IConfigurationElement fElement;
+
+ /*
+ * Creates a new sorter node with the given configuration element.
+ */
+ public StreamMergerDescriptor(IConfigurationElement element) {
+ fElement= element;
+ }
+
+ /*
+ * Creates a new stream merger from this node.
+ */
+ public IStreamMerger createStreamMerger() {
+ try {
+ return (IStreamMerger)fElement.createExecutableExtension(CLASS_ATTRIBUTE);
+ } catch (CoreException ex) {
+ //ExceptionHandler.handle(ex, SearchMessages.getString("Search.Error.createSorter.title"), SearchMessages.getString("Search.Error.createSorter.message")); //$NON-NLS-2$ //$NON-NLS-1$
+ return null;
+ } catch (ClassCastException ex) {
+ //ExceptionHandler.displayMessageDialog(ex, SearchMessages.getString("Search.Error.createSorter.title"), SearchMessages.getString("Search.Error.createSorter.message")); //$NON-NLS-2$ //$NON-NLS-1$
+ return null;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StructureCreatorDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StructureCreatorDescriptor.java
new file mode 100644
index 000000000..7f9011c55
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/StructureCreatorDescriptor.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 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 org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+
+import org.eclipse.compare.structuremergeviewer.IStructureCreator;
+
+/**
+ * A factory proxy for creating a StructureCreator.
+ */
+public class StructureCreatorDescriptor {
+
+ private final static String CLASS_ATTRIBUTE= "class"; //$NON-NLS-1$
+ private final static String EXTENSIONS_ATTRIBUTE= "extensions"; //$NON-NLS-1$
+
+ private IConfigurationElement fElement;
+
+ /*
+ * Creates a new sorter node with the given configuration element.
+ */
+ public StructureCreatorDescriptor(IConfigurationElement element) {
+ fElement= element;
+ }
+
+ /*
+ * Creates a new sorter from this node.
+ */
+ public IStructureCreator createStructureCreator() {
+ try {
+ return (IStructureCreator)fElement.createExecutableExtension(CLASS_ATTRIBUTE);
+ } catch (CoreException ex) {
+ CompareUIPlugin.log(ex.getStatus());
+ //ExceptionHandler.handle(ex, SearchMessages.getString("Search.Error.createSorter.title"), SearchMessages.getString("Search.Error.createSorter.message")); //$NON-NLS-2$ //$NON-NLS-1$
+ return null;
+ } catch (ClassCastException ex) {
+ //ExceptionHandler.displayMessageDialog(ex, SearchMessages.getString("Search.Error.createSorter.title"), SearchMessages.getString("Search.Error.createSorter.message")); //$NON-NLS-2$ //$NON-NLS-1$
+ return null;
+ }
+ }
+
+ /*
+ * Returns the structure creator's extensions.
+ */
+ public String getExtension() {
+ return fElement.getAttribute(EXTENSIONS_ATTRIBUTE);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TabFolderLayout.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TabFolderLayout.java
new file mode 100644
index 000000000..fc8e61c1e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TabFolderLayout.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+
+public class TabFolderLayout extends Layout {
+
+ protected Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache) {
+ if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT)
+ return new Point(wHint, hHint);
+
+ Control [] children = composite.getChildren ();
+ int count = children.length;
+ int maxWidth = 0, maxHeight = 0;
+ for (int i=0; i<count; i++) {
+ Control child = children [i];
+ Point pt = child.computeSize (SWT.DEFAULT, SWT.DEFAULT, flushCache);
+ maxWidth = Math.max (maxWidth, pt.x);
+ maxHeight = Math.max (maxHeight, pt.y);
+ }
+
+ if (wHint != SWT.DEFAULT)
+ maxWidth= wHint;
+ if (hHint != SWT.DEFAULT)
+ maxHeight= hHint;
+
+ return new Point(maxWidth, maxHeight);
+
+ }
+
+ protected void layout (Composite composite, boolean flushCache) {
+ Rectangle rect= composite.getClientArea();
+
+ Control[] children = composite.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ children[i].setBounds(rect);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextEditorPropertyAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextEditorPropertyAction.java
new file mode 100644
index 000000000..f7a49847f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextEditorPropertyAction.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.ui.editors.text.EditorsUI;
+
+public class TextEditorPropertyAction extends Action implements IPropertyChangeListener {
+
+ private final MergeSourceViewer[] viewers;
+ private final String preferenceKey;
+ private IPreferenceStore store;
+
+ public TextEditorPropertyAction(String label, MergeSourceViewer[] viewers, String preferenceKey) {
+ super(label, IAction.AS_CHECK_BOX);
+ this.viewers = viewers;
+ this.preferenceKey = preferenceKey;
+ this.store = EditorsUI.getPreferenceStore();
+ if (store != null)
+ store.addPropertyChangeListener(this);
+ synchronizeWithPreference();
+ addActionToViewers();
+ }
+
+ private void addActionToViewers() {
+ for (int i = 0; i < viewers.length; i++) {
+ MergeSourceViewer viewer = viewers[i];
+ viewer.addTextAction(this);
+ }
+ }
+
+ public MergeSourceViewer[] getViewers() {
+ return viewers;
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(getPreferenceKey())) {
+ synchronizeWithPreference();
+ }
+ }
+
+ protected void synchronizeWithPreference() {
+ boolean checked = false;
+ if (store != null) {
+ checked = store.getBoolean(getPreferenceKey());
+ }
+ if (checked != isChecked()) {
+ if (toggleState(checked))
+ setChecked(checked);
+ }
+ }
+
+ public String getPreferenceKey() {
+ return preferenceKey;
+ }
+
+ public void run() {
+ toggleState(isChecked());
+ if (store != null)
+ store.setValue(getPreferenceKey(), isChecked());
+ }
+
+ public void dispose() {
+ if (store != null)
+ store.removePropertyChangeListener(this);
+ }
+
+ /**
+ * @param checked
+ * new state
+ * @return <code>true</code> if state has been changed, toggle has been
+ * successful
+ */
+ protected boolean toggleState(boolean checked) {
+ // No-op by default
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java
new file mode 100644
index 000000000..7c92beb49
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+
+/**
+ * A factory object for the <code>TextMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class TextMergeViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new TextMergeViewer(parent, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java
new file mode 100644
index 000000000..e6dbe26d3
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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 org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.IViewerCreator;
+
+
+/**
+ * A factory object for the <code>TextMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class TextViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new SimpleTextViewer(parent);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
new file mode 100644
index 000000000..fe48c7219
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
@@ -0,0 +1,915 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.IEncodedStreamContentAccessor;
+import org.eclipse.compare.ISharedDocumentAdapter;
+import org.eclipse.compare.IStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.SharedDocumentAdapter;
+import org.eclipse.compare.contentmergeviewer.IDocumentRange;
+import org.eclipse.compare.internal.core.patch.HunkResult;
+import org.eclipse.compare.internal.patch.PatchMessages;
+import org.eclipse.compare.patch.IHunk;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.core.resources.IEncodedStorage;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.resources.mapping.ResourceMappingContext;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.IWorkbenchSite;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+
+import com.ibm.icu.text.MessageFormat;
+
+/**
+ * Convenience and utility methods.
+ */
+public class Utilities {
+
+ private static final IPath ICONS_PATH= new Path("$nl$/icons/full/"); //$NON-NLS-1$
+
+ public static IWorkbenchPartSite findSite(Control c) {
+ while (c != null && !c.isDisposed()) {
+ Object data= c.getData();
+ if (data instanceof IWorkbenchPart)
+ return ((IWorkbenchPart)data).getSite();
+ c= c.getParent();
+ }
+ return null;
+ }
+
+ public static IActionBars findActionBars(Control c) {
+ while (c != null && !c.isDisposed()) {
+ Object data= c.getData();
+ if (data instanceof CompareEditor)
+ return ((CompareEditor)data).getActionBars();
+
+ // PR 1GDVZV7: ITPVCM:WIN98 - CTRL + C does not work in Java source compare
+ if (data instanceof IViewPart)
+ return ((IViewPart)data).getViewSite().getActionBars();
+ // end PR 1GDVZV7
+
+ c= c.getParent();
+ }
+ return null;
+ }
+
+ public static void setEnableComposite(Composite composite, boolean enable) {
+ Control[] children= composite.getChildren();
+ for (int i= 0; i < children.length; i++)
+ children[i].setEnabled(enable);
+ }
+
+ public static boolean getBoolean(CompareConfiguration cc, String key, boolean dflt) {
+ if (cc != null) {
+ Object value= cc.getProperty(key);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ }
+ return dflt;
+ }
+
+ public static void firePropertyChange(ListenerList listenerList, Object source, String property, Object old, Object newValue) {
+ PropertyChangeEvent event= new PropertyChangeEvent(source, property, old, newValue);
+ firePropertyChange(listenerList, event);
+ }
+
+ public static void firePropertyChange(final ListenerList listenerList, final PropertyChangeEvent event) {
+ if (listenerList == null || listenerList.isEmpty())
+ return;
+ // Legacy listeners may expect to get notified in the UI thread
+ Runnable runnable = new Runnable() {
+ public void run() {
+ Object[] listeners= listenerList.getListeners();
+ for (int i= 0; i < listeners.length; i++) {
+ final IPropertyChangeListener listener= (IPropertyChangeListener) listeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ public void run() throws Exception {
+ listener.propertyChange(event);
+ }
+ public void handleException(Throwable exception) {
+ // Logged by SafeRunner
+ }
+ });
+ }
+ }
+ };
+ if (Display.getCurrent() == null) {
+ Display.getDefault().syncExec(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ public static boolean okToUse(Widget widget) {
+ return widget != null && !widget.isDisposed();
+ }
+
+ private static ArrayList internalGetResources(ISelection selection, Class type) {
+ ArrayList tmp= new ArrayList();
+ if (selection instanceof IStructuredSelection) {
+ Object[] s= ((IStructuredSelection)selection).toArray();
+
+ for (int i= 0; i < s.length; i++) {
+ IResource resource= null;
+ Object o= s[i];
+ if (type.isInstance(o)) {
+ resource= (IResource) o;
+
+ } else if (o instanceof ResourceMapping) {
+ try {
+ ResourceTraversal[] travs= ((ResourceMapping)o).getTraversals(ResourceMappingContext.LOCAL_CONTEXT, null);
+ if (travs != null) {
+ for (int k= 0; k < travs.length; k++) {
+ IResource[] resources= travs[k].getResources();
+ for (int j= 0; j < resources.length; j++) {
+ if (type.isInstance(resources[j]) && resources[j].isAccessible())
+ tmp.add(resources[j]);
+ }
+ }
+ }
+ } catch (CoreException ex) {
+ CompareUIPlugin.log(ex);
+ }
+ } else if (o instanceof IAdaptable) {
+ IAdaptable a= (IAdaptable) o;
+ Object adapter= a.getAdapter(IResource.class);
+ if (type.isInstance(adapter))
+ resource= (IResource) adapter;
+ }
+
+ if (resource != null && resource.isAccessible())
+ tmp.add(resource);
+ }
+ }
+ return tmp;
+ }
+
+
+ /*
+ * Convenience method: extract all accessible <code>IResources</code> from given selection.
+ * Never returns null.
+ */
+ public static IResource[] getResources(ISelection selection) {
+ ArrayList tmp= internalGetResources(selection, IResource.class);
+ return (IResource[]) tmp.toArray(new IResource[tmp.size()]);
+ }
+
+ /*
+ * Convenience method: extract all accessible <code>IFiles</code> from given selection.
+ * Never returns null.
+ */
+ public static IFile[] getFiles(ISelection selection) {
+ ArrayList tmp= internalGetResources(selection, IFile.class);
+ return (IFile[]) tmp.toArray(new IFile[tmp.size()]);
+ }
+
+ public static byte[] readBytes(InputStream in) {
+ ByteArrayOutputStream bos= new ByteArrayOutputStream();
+ try {
+ while (true) {
+ int c= in.read();
+ if (c == -1)
+ break;
+ bos.write(c);
+ }
+
+ } catch (IOException ex) {
+ return null;
+
+ } finally {
+ Utilities.close(in);
+ try {
+ bos.close();
+ } catch (IOException x) {
+ // silently ignored
+ }
+ }
+
+ return bos.toByteArray();
+ }
+
+ public static IPath getIconPath(Display display) {
+ return ICONS_PATH;
+ }
+
+ /*
+ * Initialize the given Action from a ResourceBundle.
+ */
+ public static void initAction(IAction a, ResourceBundle bundle, String prefix) {
+
+ String labelKey= "label"; //$NON-NLS-1$
+ String tooltipKey= "tooltip"; //$NON-NLS-1$
+ String imageKey= "image"; //$NON-NLS-1$
+ String descriptionKey= "description"; //$NON-NLS-1$
+
+ if (prefix != null && prefix.length() > 0) {
+ labelKey= prefix + labelKey;
+ tooltipKey= prefix + tooltipKey;
+ imageKey= prefix + imageKey;
+ descriptionKey= prefix + descriptionKey;
+ }
+
+ a.setText(getString(bundle, labelKey, labelKey));
+ a.setToolTipText(getString(bundle, tooltipKey, null));
+ a.setDescription(getString(bundle, descriptionKey, null));
+
+ String relPath= getString(bundle, imageKey, null);
+ if (relPath != null && relPath.trim().length() > 0) {
+
+ String dPath;
+ String ePath;
+
+ if (relPath.indexOf("/") >= 0) { //$NON-NLS-1$
+ String path= relPath.substring(1);
+ dPath= 'd' + path;
+ ePath= 'e' + path;
+ } else {
+ dPath= "dlcl16/" + relPath; //$NON-NLS-1$
+ ePath= "elcl16/" + relPath; //$NON-NLS-1$
+ }
+
+ ImageDescriptor id= CompareUIPlugin.getImageDescriptor(dPath); // we set the disabled image first (see PR 1GDDE87)
+ if (id != null)
+ a.setDisabledImageDescriptor(id);
+ id= CompareUIPlugin.getImageDescriptor(ePath);
+ if (id != null) {
+ a.setImageDescriptor(id);
+ a.setHoverImageDescriptor(id);
+ }
+ }
+ }
+
+ public static void initToggleAction(IAction a, ResourceBundle bundle, String prefix, boolean checked) {
+
+ String tooltip= null;
+ if (checked)
+ tooltip= getString(bundle, prefix + "tooltip.checked", null); //$NON-NLS-1$
+ else
+ tooltip= getString(bundle, prefix + "tooltip.unchecked", null); //$NON-NLS-1$
+ if (tooltip == null)
+ tooltip= getString(bundle, prefix + "tooltip", null); //$NON-NLS-1$
+
+ if (tooltip != null)
+ a.setToolTipText(tooltip);
+
+ String description= null;
+ if (checked)
+ description= getString(bundle, prefix + "description.checked", null); //$NON-NLS-1$
+ else
+ description= getString(bundle, prefix + "description.unchecked", null); //$NON-NLS-1$
+ if (description == null)
+ description= getString(bundle, prefix + "description", null); //$NON-NLS-1$
+
+ if (description != null)
+ a.setDescription(description);
+
+ }
+
+ public static String getString(ResourceBundle bundle, String key, String dfltValue) {
+
+ if (bundle != null) {
+ try {
+ return bundle.getString(key);
+ } catch (MissingResourceException x) {
+ // fall through
+ }
+ }
+ return dfltValue;
+ }
+
+ public static String getFormattedString(ResourceBundle bundle, String key, String arg) {
+
+ if (bundle != null) {
+ try {
+ return MessageFormat.format(bundle.getString(key), new String[] { arg });
+ } catch (MissingResourceException x) {
+ CompareUIPlugin.log(x);
+ }
+ }
+ return "!" + key + "!"; //$NON-NLS-2$ //$NON-NLS-1$
+ }
+
+ public static String getString(String key) {
+ try {
+ return CompareUI.getResourceBundle().getString(key);
+ } catch (MissingResourceException e) {
+ return "!" + key + "!"; //$NON-NLS-2$ //$NON-NLS-1$
+ }
+ }
+
+ public static String getFormattedString(String key, String arg) {
+ try {
+ return MessageFormat.format(CompareUI.getResourceBundle().getString(key), new String[] { arg });
+ } catch (MissingResourceException e) {
+ return "!" + key + "!"; //$NON-NLS-2$ //$NON-NLS-1$
+ }
+ }
+
+ public static String getFormattedString(String key, String arg0, String arg1) {
+ try {
+ return MessageFormat.format(CompareUI.getResourceBundle().getString(key), new String[] { arg0, arg1 });
+ } catch (MissingResourceException e) {
+ return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$
+ }
+ }
+
+ public static String getString(ResourceBundle bundle, String key) {
+ return getString(bundle, key, key);
+ }
+
+ public static int getInteger(ResourceBundle bundle, String key, int dfltValue) {
+
+ if (bundle != null) {
+ try {
+ String s= bundle.getString(key);
+ if (s != null)
+ return Integer.parseInt(s);
+ } catch (NumberFormatException x) {
+ CompareUIPlugin.log(x);
+ } catch (MissingResourceException x) {
+ // silently ignore Exception
+ }
+ }
+ return dfltValue;
+ }
+
+ /**
+ * Answers <code>true</code> if the given selection contains resources that don't
+ * have overlapping paths and <code>false</code> otherwise.
+ */
+ /*
+ public static boolean isSelectionNonOverlapping() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ // allow operation for non-overlapping resource selections
+ if(resources.length>0) {
+ List validPaths = new ArrayList(2);
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+
+ // only allow cvs resources to be selected
+ if(RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId()) == null) {
+ return false;
+ }
+
+ // check if this resource overlaps other selections
+ IPath resourceFullPath = resource.getFullPath();
+ if(!validPaths.isEmpty()) {
+ for (Iterator it = validPaths.iterator(); it.hasNext();) {
+ IPath path = (IPath) it.next();
+ if(path.isPrefixOf(resourceFullPath) ||
+ resourceFullPath.isPrefixOf(path)) {
+ return false;
+ }
+ }
+ }
+ validPaths.add(resourceFullPath);
+
+ // ensure that resources are managed
+ ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
+ if(cvsResource.isFolder()) {
+ if( ! ((ICVSFolder)cvsResource).isCVSFolder()) return false;
+ } else {
+ if( ! cvsResource.isManaged()) return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ */
+
+ /* validate edit utilities */
+
+ /**
+ * Status constant indicating that an validateEdit call has changed the
+ * content of a file on disk.
+ */
+ private static final int VALIDATE_EDIT_PROBLEM= 10004;
+
+ /**
+ * Constant used to indicate that tests are being run.
+ */
+ public static boolean RUNNING_TESTS = false;
+
+ /**
+ * Constant used while testing the indicate that changes should be flushed
+ * when the compare input changes and a viewer is dirty.
+ */
+ public static boolean TESTING_FLUSH_ON_COMPARE_INPUT_CHANGE = false;
+
+ /*
+ * Makes the given resources committable. Committable means that all
+ * resources are writeable and that the content of the resources hasn't
+ * changed by calling <code>validateEdit</code> for a given file on
+ * <tt>IWorkspace</tt>.
+ *
+ * @param resources the resources to be checked
+ * @param shell the Shell passed to <code>validateEdit</code> as a context
+ * @return returns <code>true</code> if all resources are committable, <code>false</code> otherwise
+ *
+ * @see org.eclipse.core.resources.IWorkspace#validateEdit(org.eclipse.core.resources.IFile[], java.lang.Object)
+ */
+ public static boolean validateResource(IResource resource, Shell shell, String title) {
+ return validateResources(new IResource[] { resource }, shell, title);
+ }
+
+ /*
+ * Makes the given resources committable. Committable means that all
+ * resources are writeable and that the content of the resources hasn't
+ * changed by calling <code>validateEdit</code> for a given file on
+ * <tt>IWorkspace</tt>.
+ *
+ * @param resources the resources to be checked
+ * @param shell the Shell passed to <code>validateEdit</code> as a context
+ * @return returns <code>true</code> if all resources are committable, <code>false</code> otherwise
+ *
+ * @see org.eclipse.core.resources.IWorkspace#validateEdit(org.eclipse.core.resources.IFile[], java.lang.Object)
+ */
+ public static boolean validateResources(List resources, Shell shell, String title) {
+ IResource r[]= (IResource[]) resources.toArray(new IResource[resources.size()]);
+ return validateResources(r, shell, title);
+ }
+
+ /*
+ * Makes the given resources committable. Committable means that all
+ * resources are writeable and that the content of the resources hasn't
+ * changed by calling <code>validateEdit</code> for a given file on
+ * <tt>IWorkspace</tt>.
+ *
+ * @param resources the resources to be checked
+ * @param shell the Shell passed to <code>validateEdit</code> as a context
+ * @return returns <code>true</code> if all resources are committable, <code>false</code> otherwise
+ *
+ * @see org.eclipse.core.resources.IWorkspace#validateEdit(org.eclipse.core.resources.IFile[], java.lang.Object)
+ */
+ public static boolean validateResources(IResource[] resources, Shell shell, String title) {
+
+ // get all readonly files
+ List readOnlyFiles= getReadonlyFiles(resources);
+ if (readOnlyFiles.size() == 0)
+ return true;
+
+ // get timestamps of readonly files before validateEdit
+ Map oldTimeStamps= createModificationStampMap(readOnlyFiles);
+
+ IFile[] files= (IFile[]) readOnlyFiles.toArray(new IFile[readOnlyFiles.size()]);
+ IStatus status= ResourcesPlugin.getWorkspace().validateEdit(files, shell);
+ if (! status.isOK()) {
+ String message= getString("ValidateEdit.error.unable_to_perform"); //$NON-NLS-1$
+ displayError(shell, title, status, message);
+ return false;
+ }
+
+ IStatus modified= null;
+ Map newTimeStamps= createModificationStampMap(readOnlyFiles);
+ for (Iterator iter= oldTimeStamps.keySet().iterator(); iter.hasNext();) {
+ IFile file= (IFile) iter.next();
+ if (file.isReadOnly()) {
+ IStatus entry= new Status(IStatus.ERROR,
+ CompareUIPlugin.getPluginId(),
+ VALIDATE_EDIT_PROBLEM,
+ getFormattedString("ValidateEdit.error.stillReadonly", file.getFullPath().toString()), //$NON-NLS-1$
+ null);
+ modified= addStatus(modified, entry);
+ } else if (! oldTimeStamps.get(file).equals(newTimeStamps.get(file))) {
+ IStatus entry= new Status(IStatus.ERROR,
+ CompareUIPlugin.getPluginId(),
+ VALIDATE_EDIT_PROBLEM,
+ getFormattedString("ValidateEdit.error.fileModified", file.getFullPath().toString()), //$NON-NLS-1$
+ null);
+ modified= addStatus(modified, entry);
+ }
+ }
+ if (modified != null) {
+ String message= getString("ValidateEdit.error.unable_to_perform"); //$NON-NLS-1$
+ displayError(shell, title, modified, message);
+ return false;
+ }
+ return true;
+ }
+
+ private static void displayError(final Shell shell, final String title, final IStatus status, final String message) {
+ if (Display.getCurrent() != null)
+ ErrorDialog.openError(shell, title, message, status);
+ else {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ ErrorDialog.openError(shell, title, message, status);
+ }
+ });
+ }
+ }
+
+ private static List getReadonlyFiles(IResource[] resources) {
+ List readOnlyFiles= new ArrayList();
+ for (int i= 0; i < resources.length; i++) {
+ IResource resource= resources[i];
+ ResourceAttributes resourceAttributes= resource.getResourceAttributes();
+ if (resource.getType() == IResource.FILE && resourceAttributes != null && resourceAttributes.isReadOnly())
+ readOnlyFiles.add(resource);
+ }
+ return readOnlyFiles;
+ }
+
+ private static Map createModificationStampMap(List files) {
+ Map map= new HashMap();
+ for (Iterator iter= files.iterator(); iter.hasNext(); ) {
+ IFile file= (IFile)iter.next();
+ map.put(file, new Long(file.getModificationStamp()));
+ }
+ return map;
+ }
+
+ private static IStatus addStatus(IStatus status, IStatus entry) {
+
+ if (status == null)
+ return entry;
+
+ if (status.isMultiStatus()) {
+ ((MultiStatus)status).add(entry);
+ return status;
+ }
+
+ MultiStatus result= new MultiStatus(CompareUIPlugin.getPluginId(),
+ VALIDATE_EDIT_PROBLEM,
+ getString("ValidateEdit.error.unable_to_perform"), null); //$NON-NLS-1$
+ result.add(status);
+ result.add(entry);
+ return result;
+ }
+
+ // encoding
+
+ public static String readString(IStreamContentAccessor sca, String encoding) throws CoreException {
+ String s = null;
+ try {
+ try {
+ s= Utilities.readString(sca.getContents(), encoding);
+ } catch (UnsupportedEncodingException e) {
+ if (!encoding.equals(ResourcesPlugin.getEncoding())) {
+ s = Utilities.readString(sca.getContents(), ResourcesPlugin.getEncoding());
+ }
+ }
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, e.getMessage(), e));
+ }
+ return s;
+ }
+
+ /*
+ * Returns null if an error occurred.
+ */
+ public static String readString(InputStream is, String encoding) throws IOException {
+ return readString(is, encoding, -1, null);
+ }
+
+ public static String readString(InputStream is, String encoding, int length, IProgressMonitor monitor) throws IOException {
+ SubMonitor progress = SubMonitor.convert(monitor);
+ progress.setWorkRemaining(length);
+ if (is == null)
+ return null;
+ BufferedReader reader= null;
+ try {
+ StringBuffer buffer= new StringBuffer();
+ char[] part= new char[2048];
+ int read= 0;
+ reader= new BufferedReader(new InputStreamReader(is, encoding));
+ while ((read= reader.read(part)) != -1) {
+ buffer.append(part, 0, read);
+ progress.worked(2048);
+ if (progress.isCanceled())
+ throw new OperationCanceledException();
+ }
+
+ return buffer.toString();
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException ex) {
+ // silently ignored
+ }
+ }
+ }
+ }
+
+ public static String getCharset(Object resource) {
+ if (resource instanceof IEncodedStorage) {
+ try {
+ return ((IEncodedStorage)resource).getCharset();
+ } catch (CoreException ex) {
+ CompareUIPlugin.log(ex);
+ }
+ }
+ return ResourcesPlugin.getEncoding();
+ }
+
+ public static byte[] getBytes(String s, String encoding) {
+ byte[] bytes= null;
+ if (s != null) {
+ try {
+ bytes= s.getBytes(encoding);
+ } catch (UnsupportedEncodingException e) {
+ bytes= s.getBytes();
+ }
+ }
+ return bytes;
+ }
+
+ public static String readString(IStreamContentAccessor sa) throws CoreException {
+ String encoding= null;
+ if (sa instanceof IEncodedStreamContentAccessor)
+ encoding= ((IEncodedStreamContentAccessor)sa).getCharset();
+ if (encoding == null)
+ encoding= ResourcesPlugin.getEncoding();
+ return Utilities.readString(sa, encoding);
+ }
+
+ public static void close(InputStream is) {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ex) {
+ // silently ignored
+ }
+ }
+ }
+
+ public static IResource getFirstResource(ISelection selection) {
+ IResource[] resources = getResources(selection);
+ if (resources.length > 0)
+ return resources[0];
+ return null;
+ }
+
+ public static Object getAdapter(Object element, Class adapterType, boolean load) {
+ if (adapterType.isInstance(element))
+ return element;
+ if (element instanceof IAdaptable) {
+ Object adapted = ((IAdaptable) element).getAdapter(adapterType);
+ if (adapterType.isInstance(adapted))
+ return adapted;
+ }
+ if (load) {
+ Object adapted = Platform.getAdapterManager().loadAdapter(element, adapterType.getName());
+ if (adapterType.isInstance(adapted))
+ return adapted;
+ } else {
+ Object adapted = Platform.getAdapterManager().getAdapter(element, adapterType);
+ if (adapterType.isInstance(adapted))
+ return adapted;
+ }
+ return null;
+ }
+
+ public static Object getAdapter(Object element, Class adapterType) {
+ return getAdapter(element, adapterType, false);
+ }
+
+ public static ITypedElement getLeg(char type, Object input) {
+ if (input instanceof ICompareInput) {
+ switch (type) {
+ case MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR:
+ return ((ICompareInput)input).getAncestor();
+ case MergeViewerContentProvider.LEFT_CONTRIBUTOR:
+ return ((ICompareInput)input).getLeft();
+ case MergeViewerContentProvider.RIGHT_CONTRIBUTOR:
+ return ((ICompareInput)input).getRight();
+ }
+ }
+ return null;
+ }
+
+ public static IDocument getDocument(char type, Object element, boolean isUsingDefaultContentProvider, boolean canHaveSharedDocument) {
+ ITypedElement te= getLeg(type, element);
+ if (te == null)
+ return null;
+ if (te instanceof IDocument)
+ return (IDocument) te;
+ if (te instanceof IDocumentRange)
+ return ((IDocumentRange) te).getDocument();
+
+ if (isUsingDefaultContentProvider && canHaveSharedDocument) {
+ ISharedDocumentAdapter sda = (ISharedDocumentAdapter)Utilities.getAdapter(te, ISharedDocumentAdapter.class, true);
+ if (sda != null) {
+ IEditorInput input= sda.getDocumentKey(te);
+ if (input != null) {
+ IDocumentProvider provider = SharedDocumentAdapter.getDocumentProvider(input);
+ if (provider != null)
+ return provider.getDocument(input);
+ }
+ }
+ }
+
+ if (te instanceof IStreamContentAccessor)
+ return DocumentManager.get(te);
+
+ return null;
+ }
+
+ /**
+ * Return whether either the left or right sides of the given input
+ * represents a hunk. A hunk is a portion of a file.
+ * @param input the compare input
+ * @return whether the left or right side of the input represents a hunk
+ */
+ public static boolean isHunk(Object input) {
+ if (input != null && input instanceof DiffNode){
+ ITypedElement right = ((DiffNode) input).getRight();
+ if (right != null) {
+ Object element = Utilities.getAdapter(right, IHunk.class);
+ if (element instanceof IHunk)
+ return true;
+ }
+ ITypedElement left = ((DiffNode) input).getLeft();
+ if (left != null) {
+ Object element = Utilities.getAdapter(left, IHunk.class);
+ if (element instanceof IHunk)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isHunkOk(Object input) {
+ if (input != null && input instanceof DiffNode){
+ ITypedElement right = ((DiffNode) input).getRight();
+ if (right != null) {
+ Object element = Utilities.getAdapter(right, HunkResult.class);
+ if (element instanceof HunkResult) {
+ return ((HunkResult)element).isOK();
+ }
+ }
+ ITypedElement left = ((DiffNode) input).getLeft();
+ if (left != null) {
+ Object element = Utilities.getAdapter(left, HunkResult.class);
+ if (element instanceof HunkResult)
+ return ((HunkResult)element).isOK();
+ }
+ }
+ return false;
+ }
+
+ public static void schedule(Job job, IWorkbenchSite site) {
+ if (site != null) {
+ IWorkbenchSiteProgressService siteProgress = (IWorkbenchSiteProgressService) site.getAdapter(IWorkbenchSiteProgressService.class);
+ if (siteProgress != null) {
+ siteProgress.schedule(job, 0, true /* use half-busy cursor */);
+ return;
+ }
+ }
+ job.schedule();
+ }
+
+ public static void runInUIThread(final Runnable runnable) {
+ if (Display.getCurrent() != null) {
+ BusyIndicator.showWhile(Display.getCurrent(), runnable);
+ } else {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ BusyIndicator.showWhile(Display.getCurrent(), runnable);
+ }
+ });
+ }
+ }
+
+ /**
+ * @param connection a connection for which the timeout is set
+ * @param timeout an int that specifies the connect timeout value in milliseconds
+ * @return whether the timeout has been successfully set
+ */
+ public static boolean setReadTimeout(URLConnection connection, int timeout) {
+ Method[] methods = connection.getClass().getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].getName().equals("setReadTimeout")) //$NON-NLS-1$
+ try {
+ methods[i].invoke(connection, new Object[] {new Integer(timeout)});
+ return true;
+ } catch (IllegalArgumentException e) { // ignore
+ } catch (IllegalAccessException e) { // ignore
+ } catch (InvocationTargetException e) { // ignore
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Load content of file under <code>url</code> displaying progress on given
+ * context.
+ *
+ * @param url
+ * @param context
+ * @return the content of file under given URL, or <code>null</code> if URL
+ * could not be loaded
+ * @throws InvocationTargetException
+ * thrown on errors while URL loading
+ * @throws OperationCanceledException
+ * @throws InterruptedException
+ */
+ public static String getURLContents(final URL url, IRunnableContext context)
+ throws InvocationTargetException, OperationCanceledException,
+ InterruptedException {
+ final String[] result = new String[1];
+ context.run(true, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ SubMonitor progress = SubMonitor.convert(monitor,
+ PatchMessages.InputPatchPage_URLConnecting, 100);
+ try {
+ URLConnection connection = url.openConnection();
+ progress.worked(10);
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ setReadTimeout(connection, 60 * 1000);
+ progress.setTaskName(PatchMessages.InputPatchPage_URLFetchingContent);
+ String enc = connection.getContentEncoding();
+ if (enc == null)
+ enc = ResourcesPlugin.getEncoding();
+ result[0] = Utilities.readString(
+ connection.getInputStream(), enc,
+ connection.getContentLength(),
+ progress.newChild(90));
+ } catch (SocketTimeoutException e) {
+ throw new InvocationTargetException(e);
+ } catch (IOException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+ });
+ return result[0];
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java
new file mode 100644
index 000000000..e5d35bcf9
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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 org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.IViewerCreator;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Creates <code>Viewer</code>s from an <code>IConfigurationElement</code>.
+ */
+public class ViewerDescriptor implements IViewerDescriptor {
+
+ private final static String CLASS_ATTRIBUTE= "class"; //$NON-NLS-1$
+ private final static String EXTENSIONS_ATTRIBUTE= "extensions"; //$NON-NLS-1$
+ private final static String LABEL_ATTRIBUTE = "label"; //$NON-NLS-1$
+
+ private IConfigurationElement fConfiguration;
+ private IViewerCreator fViewerCreator;
+ private Class fViewerClass;
+
+ public ViewerDescriptor(IConfigurationElement config) {
+ fConfiguration= config;
+ }
+
+ public Viewer createViewer(Viewer currentViewer, Composite parent, CompareConfiguration mp) {
+
+ if (currentViewer != null && currentViewer.getClass() == fViewerClass) {
+ //System.out.println("reused viewer: " + currentViewer.getClass().getName());
+ return currentViewer;
+ }
+
+ if (fViewerCreator == null) {
+ try {
+ fViewerCreator= (IViewerCreator) fConfiguration.createExecutableExtension(CLASS_ATTRIBUTE);
+ } catch (CoreException e) {
+ CompareUIPlugin.log(e);
+ }
+ }
+
+ if (fViewerCreator != null) {
+ // If we are going to return a new viewer, we want to preemptively deregister
+ // any handlers to avoid the logging of conflict warnings
+ if (currentViewer != null) {
+ CompareHandlerService[] compareHandlerService = (CompareHandlerService[]) Utilities.getAdapter(currentViewer, CompareHandlerService[].class);
+ if (compareHandlerService != null) {
+ for (int i = 0; i < compareHandlerService.length; i++) {
+ compareHandlerService[i].dispose();
+ }
+ }
+ }
+ Viewer viewer= fViewerCreator.createViewer(parent, mp);
+ if (viewer != null)
+ fViewerClass= viewer.getClass();
+ return viewer;
+ }
+
+ return null;
+ }
+
+ public String getExtension() {
+ return fConfiguration.getAttribute(EXTENSIONS_ATTRIBUTE);
+ }
+
+ String getLabel() {
+ return fConfiguration.getAttribute(LABEL_ATTRIBUTE);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerSwitchingCancelled.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerSwitchingCancelled.java
new file mode 100644
index 000000000..6bd7a541f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerSwitchingCancelled.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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;
+
+
+public class ViewerSwitchingCancelled extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkQueue.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkQueue.java
new file mode 100644
index 000000000..14ebbc46a
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkQueue.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2006 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.util.*;
+
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
+/**
+ * A work queue maintains a list of tasks that need to be run.
+ * If the same task is added multiple times, the last occurrence of
+ * the task will be run(i.e. the task will be removed from it's
+ * previous location and aded to the end of the queue.
+ */
+public class WorkQueue {
+
+ private List runnables = new ArrayList();
+
+ public boolean add(IRunnableWithProgress runnable) {
+ if (runnables.contains(runnable))
+ runnables.remove(runnable);
+ return runnables.add(runnable);
+ }
+
+ public void clear() {
+ runnables.clear();
+ }
+
+ public boolean contains(IRunnableWithProgress runnable) {
+ return runnables.contains(runnable);
+ }
+
+ public boolean isEmpty() {
+ return runnables.isEmpty();
+ }
+
+ public boolean remove(IRunnableWithProgress runnable) {
+ return runnables.remove(runnable);
+ }
+
+ public int size() {
+ return runnables.size();
+ }
+ public IRunnableWithProgress remove() {
+ return (IRunnableWithProgress)runnables.remove(0);
+ }
+
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Worker.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Worker.java
new file mode 100644
index 000000000..69455f981
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Worker.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
+/**
+ * A worker performs a set of tasks in order and accumulates any errors
+ * that may have occurred. If the same task is queued multiple times,
+ * the last occurrence will be run. If a task is queued while it is
+ * running, the running task will be canceled and the task added
+ * to the end of the work queue.
+ */
+public class Worker implements IRunnableWithProgress {
+
+ private final WorkQueue work = new WorkQueue();
+ private boolean isWorking;
+ private final List errors = new ArrayList();
+ private WorkProgressMonitor currentMonitor;
+ private IRunnableWithProgress currentTask;
+ private final String taskName;
+
+ /**
+ * Progress monitor that supports local cancelation of a task.
+ */
+ private static class WorkProgressMonitor extends ProgressMonitorWrapper {
+ private boolean localCancel;
+ protected WorkProgressMonitor(IProgressMonitor monitor) {
+ super(monitor);
+ }
+ public void cancelTask() {
+ localCancel = true;
+ }
+ public boolean isCanceled() {
+ return localCancel || super.isCanceled();
+ }
+ }
+
+ public Worker(String taskName) {
+ this.taskName = taskName;
+ }
+
+ public void run(IProgressMonitor monitor) {
+ errors.clear();
+ SubMonitor pm = SubMonitor.convert(monitor, getTaskName(), 100);
+ try {
+ isWorking = true;
+ while (!work.isEmpty()) {
+ try {
+ performNextTask(pm);
+ checkCancelled(pm);
+ } catch (OperationCanceledException e) {
+ // Only cancel all the work if the outer monitor is canceled
+ checkCancelled(pm);
+ } catch (InterruptedException e) {
+ // Only cancel all the work if the outer monitor is canceled
+ checkCancelled(pm);
+ } catch (InvocationTargetException e) {
+ handleError(e.getTargetException());
+ }
+ pm.setWorkRemaining(100);
+ }
+ } catch (OperationCanceledException e) {
+ // The user chose to cancel
+ work.clear();
+ } finally {
+ isWorking = false;
+ if (monitor!= null)
+ monitor.done();
+ currentMonitor = null;
+ currentTask = null;
+ }
+ }
+
+ private WorkProgressMonitor subMonitorFor(SubMonitor pm, int ticks) {
+ return new WorkProgressMonitor(pm.newChild(ticks));
+ }
+
+ private void handleError(Throwable targetException) {
+ errors.add(targetException);
+ }
+
+ public Throwable[] getErrors() {
+ return (Throwable[]) errors.toArray(new Throwable[errors.size()]);
+ }
+
+ private void checkCancelled(SubMonitor pm) {
+ if (pm.isCanceled())
+ throw new OperationCanceledException();
+ }
+
+ protected String getTaskName() {
+ return taskName;
+ }
+
+ private void performNextTask(SubMonitor pm) throws InvocationTargetException, InterruptedException {
+ synchronized (this) {
+ if (work.isEmpty())
+ return;
+ currentTask = work.remove();
+ }
+ currentMonitor = subMonitorFor(pm, 10);
+ currentTask.run(currentMonitor);
+ }
+
+ public synchronized void add(IRunnableWithProgress r) {
+ if (currentTask != null && currentTask.equals(r)) {
+ currentMonitor.cancelTask();
+ }
+ work.add(r);
+ }
+
+ public boolean isWorking() {
+ return isWorking;
+ }
+
+ public boolean hasWork() {
+ return isWorking() || !work.isEmpty();
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkerJob.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkerJob.java
new file mode 100644
index 000000000..7dd38c9d4
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/WorkerJob.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2006 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.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
+public class WorkerJob extends Job {
+
+ private final Worker worker;
+
+ public WorkerJob(String name) {
+ super(name);
+ worker = new Worker(name);
+ }
+
+ protected IStatus run(IProgressMonitor monitor) {
+ worker.run(monitor);
+ // reschedule to ensure we don't miss a task
+ IStatus result = getResult(worker);
+ schedule();
+ return result;
+ }
+
+ private IStatus getResult(Worker w) {
+ Throwable[] errors = w.getErrors();
+ if (errors.length == 0)
+ return Status.OK_STATUS;
+ if (errors.length == 1)
+ return new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, errors[0].getMessage(), errors[0]);
+ List statii = new ArrayList();
+ for (int i = 0; i < errors.length; i++) {
+ Throwable throwable = errors[i];
+ statii.add(new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, errors[0].getMessage(), throwable));
+ }
+ return new MultiStatus(CompareUIPlugin.PLUGIN_ID, 0, (IStatus[]) statii.toArray(new IStatus[statii.size()]), CompareMessages.WorkerJob_0, null);
+ }
+
+ public boolean shouldRun() {
+ return worker.hasWork();
+ }
+
+ public void add(IRunnableWithProgress runnable) {
+ worker.add(runnable);
+ schedule();
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java
new file mode 100644
index 000000000..463d8d159
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java
@@ -0,0 +1,1409 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.merge;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.IProgressService;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.contentmergeviewer.ITokenComparator;
+import org.eclipse.compare.internal.CompareContentViewerSwitchingPane;
+import org.eclipse.compare.internal.CompareMessages;
+import org.eclipse.compare.internal.ComparePreferencePage;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.DocLineComparator;
+import org.eclipse.compare.internal.MergeViewerContentProvider;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.internal.core.LCS;
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+import org.eclipse.compare.rangedifferencer.RangeDifference;
+import org.eclipse.compare.rangedifferencer.RangeDifferencer;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+
+/**
+ * A document merger manages the differences between two documents
+ * for either a 2-way or 3-way comparison.
+ * <p>
+ * This class should not have any UI dependencies.
+ */
+public class DocumentMerger {
+
+ private static final String DIFF_RANGE_CATEGORY = CompareUIPlugin.PLUGIN_ID + ".DIFF_RANGE_CATEGORY"; //$NON-NLS-1$
+
+ /** Selects between smartTokenDiff and mergingTokenDiff */
+ private static final boolean USE_MERGING_TOKEN_DIFF= false;
+
+ /** if true copying conflicts from one side to other concatenates both sides */
+ private static final boolean APPEND_CONFLICT= true;
+
+ /** All diffs for calculating scrolling position (includes line ranges without changes) */
+ private ArrayList fAllDiffs;
+ /** Subset of above: just real differences. */
+ private ArrayList fChangeDiffs;
+
+ private final boolean fLeftIsLocal;
+
+ private IDocumentMergerInput fInput;
+
+ /**
+ * Interface that defines that input to the document merge process
+ */
+ public interface IDocumentMergerInput {
+
+ IDocument getDocument(char contributor);
+
+ Position getRegion(char contributor);
+
+ boolean isIgnoreAncestor();
+
+ boolean isThreeWay();
+
+ CompareConfiguration getCompareConfiguration();
+
+ ITokenComparator createTokenComparator(String s);
+
+ boolean isHunkOnLeft();
+
+ int getHunkStart();
+
+ boolean isPatchHunk();
+
+ boolean isShowPseudoConflicts();
+
+ boolean isPatchHunkOk();
+ }
+
+ public class Diff {
+ /** character range in ancestor document */
+ Position fAncestorPos;
+ /** character range in left document */
+ Position fLeftPos;
+ /** character range in right document */
+ Position fRightPos;
+ /** if this is a TokenDiff fParent points to the enclosing LineDiff */
+ Diff fParent;
+ /** if Diff has been resolved */
+ boolean fResolved;
+ int fDirection;
+ boolean fIsToken= false;
+ /** child token diffs */
+ ArrayList fDiffs;
+ boolean fIsWhitespace= false;
+
+ /*
+ * Create Diff from two ranges and an optional parent diff.
+ */
+ Diff(Diff parent, int dir, IDocument ancestorDoc, Position aRange, int ancestorStart, int ancestorEnd,
+ IDocument leftDoc, Position lRange, int leftStart, int leftEnd,
+ IDocument rightDoc, Position rRange, int rightStart, int rightEnd) {
+ fParent= parent != null ? parent : this;
+ fDirection= dir;
+
+ fLeftPos= createPosition(leftDoc, lRange, leftStart, leftEnd);
+ fRightPos= createPosition(rightDoc, rRange, rightStart, rightEnd);
+ if (ancestorDoc != null)
+ fAncestorPos= createPosition(ancestorDoc, aRange, ancestorStart, ancestorEnd);
+ }
+
+ public Position getPosition(char type) {
+ switch (type) {
+ case MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR:
+ return fAncestorPos;
+ case MergeViewerContentProvider.LEFT_CONTRIBUTOR:
+ return fLeftPos;
+ case MergeViewerContentProvider.RIGHT_CONTRIBUTOR:
+ return fRightPos;
+ }
+ return null;
+ }
+
+ boolean isInRange(char type, int pos) {
+ Position p= getPosition(type);
+ return (pos >= p.offset) && (pos < (p.offset+p.length));
+ }
+
+ public String changeType() {
+ boolean leftEmpty= fLeftPos.length == 0;
+ boolean rightEmpty= fRightPos.length == 0;
+
+ if (fDirection == RangeDifference.LEFT) {
+ if (!leftEmpty && rightEmpty)
+ return CompareMessages.TextMergeViewer_changeType_addition;
+ if (leftEmpty && !rightEmpty)
+ return CompareMessages.TextMergeViewer_changeType_deletion;
+ } else {
+ if (leftEmpty && !rightEmpty)
+ return CompareMessages.TextMergeViewer_changeType_addition;
+ if (!leftEmpty && rightEmpty)
+ return CompareMessages.TextMergeViewer_changeType_deletion;
+ }
+ return CompareMessages.TextMergeViewer_changeType_change;
+ }
+
+ public Image getImage() {
+ int code= Differencer.CHANGE;
+ switch (fDirection) {
+ case RangeDifference.RIGHT:
+ code+= Differencer.LEFT;
+ break;
+ case RangeDifference.LEFT:
+ code+= Differencer.RIGHT;
+ break;
+ case RangeDifference.ANCESTOR:
+ case RangeDifference.CONFLICT:
+ code+= Differencer.CONFLICTING;
+ break;
+ }
+ if (code != 0)
+ return getCompareConfiguration().getImage(code);
+ return null;
+ }
+
+ Position createPosition(IDocument doc, Position range, int start, int end) {
+ try {
+ int l= end-start;
+ if (range != null) {
+ int dl= range.length;
+ if (l > dl)
+ l= dl;
+ } else {
+ int dl= doc.getLength();
+ if (start+l > dl)
+ l= dl-start;
+ }
+
+ Position p= null;
+ try {
+ p= new Position(start, l);
+ } catch (RuntimeException ex) {
+ p= new Position(0, 0);
+ }
+
+ try {
+ doc.addPosition(DIFF_RANGE_CATEGORY, p);
+ } catch (BadPositionCategoryException ex) {
+ // silently ignored
+ }
+ return p;
+ } catch (BadLocationException ee) {
+ // silently ignored
+ }
+ return null;
+ }
+
+ void add(Diff d) {
+ if (fDiffs == null)
+ fDiffs= new ArrayList();
+ fDiffs.add(d);
+ }
+
+ public boolean isDeleted() {
+ if (fAncestorPos != null && fAncestorPos.isDeleted())
+ return true;
+ return fLeftPos.isDeleted() || fRightPos.isDeleted();
+ }
+
+ void setResolved(boolean r) {
+ fResolved= r;
+ if (r)
+ fDiffs= null;
+ }
+
+ public boolean isResolved() {
+ if (!fResolved && fDiffs != null) {
+ Iterator e= fDiffs.iterator();
+ while (e.hasNext()) {
+ Diff d= (Diff) e.next();
+ if (!d.isResolved())
+ return false;
+ }
+ return true;
+ }
+ return fResolved;
+ }
+
+// private boolean isIncoming() {
+// switch (fDirection) {
+// case RangeDifference.RIGHT:
+// if (fLeftIsLocal)
+// return true;
+// break;
+// case RangeDifference.LEFT:
+// if (!fLeftIsLocal)
+// return true;
+// break;
+// }
+// return false;
+// }
+
+ public boolean isIncomingOrConflicting() {
+ switch (fDirection) {
+ case RangeDifference.RIGHT:
+ if (fLeftIsLocal)
+ return true;
+ break;
+ case RangeDifference.LEFT:
+ if (!fLeftIsLocal)
+ return true;
+ break;
+ case RangeDifference.CONFLICT:
+ return true;
+ }
+ return false;
+ }
+
+// private boolean isUnresolvedIncoming() {
+// if (fResolved)
+// return false;
+// return isIncoming();
+// }
+
+ public boolean isUnresolvedIncomingOrConflicting() {
+ if (fResolved)
+ return false;
+ return isIncomingOrConflicting();
+ }
+
+ Position getPosition(int contributor) {
+ if (contributor == MergeViewerContentProvider.LEFT_CONTRIBUTOR)
+ return fLeftPos;
+ if (contributor == MergeViewerContentProvider.RIGHT_CONTRIBUTOR)
+ return fRightPos;
+ if (contributor == MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR)
+ return fAncestorPos;
+ return null;
+ }
+
+ /*
+ * Returns true if given character range overlaps with this Diff.
+ */
+ public boolean overlaps(int contributor, int start, int end, int docLength) {
+ Position h= getPosition(contributor);
+ if (h != null) {
+ int ds= h.getOffset();
+ int de= ds + h.getLength();
+ if ((start < de) && (end >= ds))
+ return true;
+ if ((start == docLength) && (start <= de) && (end >= ds))
+ return true;
+ }
+ return false;
+ }
+
+ public int getMaxDiffHeight() {
+ Point region= new Point(0, 0);
+ int h= getLineRange(getDocument(MergeViewerContentProvider.LEFT_CONTRIBUTOR), fLeftPos, region).y;
+ if (isThreeWay())
+ h= Math.max(h, getLineRange(getDocument(MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR), fAncestorPos, region).y);
+ return Math.max(h, getLineRange(getDocument(MergeViewerContentProvider.RIGHT_CONTRIBUTOR), fRightPos, region).y);
+ }
+
+ public int getAncestorHeight() {
+ Point region= new Point(0, 0);
+ return getLineRange(getDocument(MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR), fAncestorPos, region).y;
+ }
+
+ public int getLeftHeight() {
+ Point region= new Point(0, 0);
+ return getLineRange(getDocument(MergeViewerContentProvider.LEFT_CONTRIBUTOR), fLeftPos, region).y;
+ }
+
+ public int getRightHeight() {
+ Point region= new Point(0, 0);
+ return getLineRange(getDocument(MergeViewerContentProvider.RIGHT_CONTRIBUTOR), fRightPos, region).y;
+ }
+
+ public Diff[] getChangeDiffs(int contributor, IRegion region) {
+ if (fDiffs != null && intersectsRegion(contributor, region)) {
+ List result = new ArrayList();
+ for (Iterator iterator = fDiffs.iterator(); iterator.hasNext();) {
+ Diff diff = (Diff) iterator.next();
+ if (diff.intersectsRegion(contributor, region)) {
+ result.add(diff);
+ }
+ }
+ return (Diff[]) result.toArray(new Diff[result.size()]);
+ }
+ return new Diff[0];
+ }
+
+ private boolean intersectsRegion(int contributor, IRegion region) {
+ Position p = getPosition(contributor);
+ if (p != null)
+ return p.overlapsWith(region.getOffset(), region.getLength());
+ return false;
+ }
+
+ public boolean hasChildren() {
+ return fDiffs != null && !fDiffs.isEmpty();
+ }
+
+ public int getKind() {
+ return fDirection;
+ }
+
+ public boolean isToken() {
+ return fIsToken;
+ }
+
+ public Diff getParent() {
+ return fParent;
+ }
+
+ public Iterator childIterator() {
+ if (fDiffs == null)
+ return new ArrayList().iterator();
+ return fDiffs.iterator();
+ }
+ }
+
+ public DocumentMerger(IDocumentMergerInput input) {
+ this.fInput = input;
+ fLeftIsLocal= Utilities.getBoolean(getCompareConfiguration(), "LEFT_IS_LOCAL", false); //$NON-NLS-1$
+ }
+
+ /**
+ * Perform a two level 2- or 3-way diff.
+ * The first level is based on line comparison, the second level on token comparison.
+ * @throws CoreException
+ */
+ public void doDiff() throws CoreException {
+
+ fChangeDiffs= new ArrayList();
+ IDocument lDoc = getDocument(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
+ IDocument rDoc = getDocument(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
+
+ if (lDoc == null || rDoc == null)
+ return;
+
+ Position lRegion= getRegion(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
+ Position rRegion= getRegion(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
+
+ IDocument aDoc = null;
+ Position aRegion= null;
+ if (isThreeWay() && !isIgnoreAncestor()) {
+ aDoc= getDocument(MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR);
+ aRegion= getRegion(MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR);
+ }
+
+ resetPositions(lDoc);
+ resetPositions(rDoc);
+ resetPositions(aDoc);
+
+ boolean ignoreWhiteSpace= isIgnoreWhitespace();
+
+ DocLineComparator sright= new DocLineComparator(rDoc, toRegion(rRegion), ignoreWhiteSpace);
+ DocLineComparator sleft= new DocLineComparator(lDoc, toRegion(lRegion), ignoreWhiteSpace);
+ DocLineComparator sancestor= null;
+ if (aDoc != null) {
+ sancestor= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
+ /*if (isPatchHunk()) {
+ if (isHunkOnLeft()) {
+ sright= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
+ } else {
+ sleft= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
+ }
+ }*/
+ }
+
+ final Object[] result= new Object[1];
+ final DocLineComparator sa= sancestor, sl= sleft, sr= sright;
+ IRunnableWithProgress runnable= new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException {
+ monitor.beginTask(CompareMessages.DocumentMerger_0, maxWork(sa, sl, sr));
+ try {
+ result[0]= RangeDifferencer.findRanges(monitor, sa, sl, sr);
+ } catch (OutOfMemoryError ex) {
+ System.gc();
+ throw new InvocationTargetException(ex);
+ }
+ if (monitor.isCanceled()) { // canceled
+ throw new InterruptedException();
+ }
+ monitor.done();
+ }
+ };
+
+ RangeDifference[] e= null;
+ try {
+ getCompareConfiguration().getContainer().run(true, true, runnable);
+ e= (RangeDifference[]) result[0];
+ } catch (InvocationTargetException ex) {
+ // we create a NOCHANGE range for the whole document
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, aRegion, 0, aDoc != null ? aDoc.getLength() : 0,
+ lDoc, lRegion, 0, lDoc.getLength(),
+ rDoc, rRegion, 0, rDoc.getLength());
+
+ fAllDiffs = new ArrayList();
+ fAllDiffs.add(diff);
+ throw new CoreException(new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, CompareMessages.DocumentMerger_1, ex.getTargetException()));
+ } catch (InterruptedException ex) {
+ // we create a NOCHANGE range for the whole document
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, aRegion, 0, aDoc != null ? aDoc.getLength() : 0,
+ lDoc, lRegion, 0, lDoc.getLength(),
+ rDoc, rRegion, 0, rDoc.getLength());
+
+ fAllDiffs = new ArrayList();
+ fAllDiffs.add(diff);
+ return;
+ }
+
+ if (isCapped(sa, sl, sr))
+ fInput.getCompareConfiguration().setProperty(
+ CompareContentViewerSwitchingPane.OPTIMIZED_ALGORITHM_USED,
+ new Boolean(true));
+ else
+ fInput.getCompareConfiguration().setProperty(
+ CompareContentViewerSwitchingPane.OPTIMIZED_ALGORITHM_USED,
+ new Boolean(false));
+
+ ArrayList newAllDiffs = new ArrayList();
+ for (int i= 0; i < e.length; i++) {
+ RangeDifference es= e[i];
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (sancestor != null) {
+ ancestorStart= sancestor.getTokenStart(es.ancestorStart());
+ ancestorEnd= getTokenEnd2(sancestor, es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart= sleft.getTokenStart(es.leftStart());
+ int leftEnd= getTokenEnd2(sleft, es.leftStart(), es.leftLength());
+
+ int rightStart= sright.getTokenStart(es.rightStart());
+ int rightEnd= getTokenEnd2(sright, es.rightStart(), es.rightLength());
+
+ /*if (isPatchHunk()) {
+ if (isHunkOnLeft()) {
+ rightStart = rightEnd = getHunkStart();
+ } else {
+ leftStart = leftEnd = getHunkStart();
+ }
+ }*/
+
+ Diff diff= new Diff(null, es.kind(),
+ aDoc, aRegion, ancestorStart, ancestorEnd,
+ lDoc, lRegion, leftStart, leftEnd,
+ rDoc, rRegion, rightStart, rightEnd);
+
+ newAllDiffs.add(diff); // remember all range diffs for scrolling
+
+ if (isPatchHunk()) {
+ if (useChange(diff)) {
+ recordChangeDiff(diff);
+ }
+ } else {
+ if (ignoreWhiteSpace || useChange(es.kind())) {
+
+ // Extract the string for each contributor.
+ String a= null;
+ if (sancestor != null)
+ a= extract2(aDoc, sancestor, es.ancestorStart(), es.ancestorLength());
+ String s= extract2(lDoc, sleft, es.leftStart(), es.leftLength());
+ String d= extract2(rDoc, sright, es.rightStart(), es.rightLength());
+
+ // Indicate whether all contributors are whitespace
+ if (ignoreWhiteSpace
+ && (a == null || a.trim().length() == 0)
+ && s.trim().length() == 0
+ && d.trim().length() == 0) {
+ diff.fIsWhitespace= true;
+ }
+
+ // If the diff is of interest, record it and generate the token diffs
+ if (useChange(diff)) {
+ recordChangeDiff(diff);
+ if (s.length() > 0 && d.length() > 0) {
+ if (a == null && sancestor != null)
+ a= extract2(aDoc, sancestor, es.ancestorStart(), es.ancestorLength());
+ if (USE_MERGING_TOKEN_DIFF)
+ mergingTokenDiff(diff, aDoc, a, rDoc, d, lDoc, s);
+ else
+ simpleTokenDiff(diff, aDoc, a, rDoc, d, lDoc, s);
+ }
+ }
+ }
+ }
+ }
+ fAllDiffs = newAllDiffs;
+ }
+
+ private boolean isCapped(DocLineComparator ancestor,
+ DocLineComparator left, DocLineComparator right) {
+ if (isCappingDisabled())
+ return false;
+ int aLength = ancestor == null? 0 : ancestor.getRangeCount();
+ int lLength = left.getRangeCount();
+ int rLength = right.getRangeCount();
+ if ((double) aLength * (double) lLength > LCS.TOO_LONG
+ || (double) aLength * (double) rLength > LCS.TOO_LONG
+ || (double) lLength * (double) rLength > LCS.TOO_LONG)
+ return true;
+ return false;
+ }
+
+ public Diff findDiff(char type, int pos) throws CoreException {
+
+ IDocument aDoc= null;
+ IDocument lDoc= getDocument(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
+ IDocument rDoc= getDocument(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
+ if (lDoc == null || rDoc == null)
+ return null;
+
+ Position aRegion= null;
+ Position lRegion= null;
+ Position rRegion= null;
+
+ boolean threeWay= isThreeWay();
+
+ if (threeWay && !isIgnoreAncestor())
+ aDoc= getDocument(MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR);
+
+ boolean ignoreWhiteSpace= isIgnoreWhitespace();
+
+ DocLineComparator sright= new DocLineComparator(rDoc, toRegion(rRegion), ignoreWhiteSpace);
+ DocLineComparator sleft= new DocLineComparator(lDoc, toRegion(lRegion), ignoreWhiteSpace);
+ DocLineComparator sancestor= null;
+ if (aDoc != null)
+ sancestor= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
+
+ final Object[] result= new Object[1];
+ final DocLineComparator sa= sancestor, sl= sleft, sr= sright;
+ IRunnableWithProgress runnable= new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException {
+ monitor.beginTask(CompareMessages.DocumentMerger_2, maxWork(sa, sl, sr));
+ try {
+ result[0]= RangeDifferencer.findRanges(monitor, sa, sl, sr);
+ } catch (OutOfMemoryError ex) {
+ System.gc();
+ throw new InvocationTargetException(ex);
+ }
+ if (monitor.isCanceled()) { // canceled
+ throw new InterruptedException();
+ }
+ monitor.done();
+ }
+ };
+ IProgressService progressService= PlatformUI.getWorkbench().getProgressService();
+
+ RangeDifference[] e= null;
+ try {
+ progressService.run(true, true, runnable);
+ e= (RangeDifference[]) result[0];
+ } catch (InvocationTargetException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, CompareMessages.DocumentMerger_3, ex.getTargetException()));
+ } catch (InterruptedException ex) {
+ //
+ }
+
+ if (e != null) {
+ for (int i= 0; i < e.length; i++) {
+ RangeDifference es= e[i];
+
+ int kind= es.kind();
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (sancestor != null) {
+ ancestorStart= sancestor.getTokenStart(es.ancestorStart());
+ ancestorEnd= getTokenEnd2(sancestor, es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart= sleft.getTokenStart(es.leftStart());
+ int leftEnd= getTokenEnd2(sleft, es.leftStart(), es.leftLength());
+
+ int rightStart= sright.getTokenStart(es.rightStart());
+ int rightEnd= getTokenEnd2(sright, es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(null, kind,
+ aDoc, aRegion, ancestorStart, ancestorEnd,
+ lDoc, lRegion, leftStart, leftEnd,
+ rDoc, rRegion, rightStart, rightEnd);
+
+ if (diff.isInRange(type, pos))
+ return diff;
+ }
+ }
+
+ return null;
+ }
+
+ private void recordChangeDiff(Diff diff) {
+ fChangeDiffs.add(diff); // here we remember only the real diffs
+ }
+
+ /*private boolean isHunkOnLeft() {
+ return fInput.isHunkOnLeft();
+ }
+
+ private int getHunkStart() {
+ return fInput.getHunkStart();
+ }*/
+
+ private boolean isPatchHunk() {
+ return fInput.isPatchHunk();
+ }
+
+ private boolean isIgnoreWhitespace() {
+ return Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false);
+ }
+
+ private boolean isCappingDisabled() {
+ return CompareUIPlugin.getDefault().getPreferenceStore().getBoolean(ComparePreferencePage.CAPPING_DISABLED);
+ }
+
+ private IDocument getDocument(char contributor) {
+ return fInput.getDocument(contributor);
+ }
+
+ private Position getRegion(char contributor) {
+ return fInput.getRegion(contributor);
+ }
+
+ public boolean isIgnoreAncestor() {
+ return fInput.isIgnoreAncestor();
+ }
+
+ public boolean isThreeWay() {
+ return fInput.isThreeWay();
+ }
+
+ /**
+ * Return the compare configuration associated with this merger.
+ * @return the compare configuration associated with this merger
+ */
+ public CompareConfiguration getCompareConfiguration() {
+ return fInput.getCompareConfiguration();
+ }
+
+ /*
+ * Returns true if kind of change should be shown.
+ */
+ public boolean useChange(Diff diff) {
+ if (diff.fIsWhitespace)
+ return false;
+ int kind = diff.getKind();
+ return useChange(kind);
+ }
+
+ private boolean useChange(int kind) {
+ if (kind == RangeDifference.NOCHANGE)
+ return false;
+ if (fInput.getCompareConfiguration().isChangeIgnored(kind))
+ return false;
+ if (kind == RangeDifference.ANCESTOR)
+ return fInput.isShowPseudoConflicts();
+ return true;
+ }
+
+ private int getTokenEnd(ITokenComparator tc, int start, int count) {
+ if (count <= 0)
+ return tc.getTokenStart(start);
+ int index= start + count - 1;
+ return tc.getTokenStart(index) + tc.getTokenLength(index);
+ }
+
+ private static int getTokenEnd2(ITokenComparator tc, int start, int length) {
+ return tc.getTokenStart(start + length);
+ }
+
+ /**
+ * Returns the content of lines in the specified range as a String.
+ * This includes the line separators.
+ *
+ * @param doc the document from which to extract the characters
+ * @param start index of first line
+ * @param length number of lines
+ * @return the contents of the specified line range as a String
+ */
+ private String extract2(IDocument doc, ITokenComparator tc, int start, int length) {
+ int count= tc.getRangeCount();
+ if (length > 0 && count > 0) {
+
+//
+// int startPos= tc.getTokenStart(start);
+// int endPos= startPos;
+//
+// if (length > 1)
+// endPos= tc.getTokenStart(start + (length-1));
+// endPos+= tc.getTokenLength(start + (length-1));
+//
+
+ int startPos= tc.getTokenStart(start);
+ int endPos;
+
+ if (length == 1) {
+ endPos= startPos + tc.getTokenLength(start);
+ } else {
+ endPos= tc.getTokenStart(start + length);
+ }
+
+ try {
+ return doc.get(startPos, endPos - startPos);
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ private static IRegion toRegion(Position position) {
+ if (position != null)
+ return new Region(position.getOffset(), position.getLength());
+ return null;
+ }
+
+ /*
+ * Performs a "smart" token based 3-way diff on the character range specified by the given baseDiff.
+ * It is "smart" because it tries to minimize the number of token diffs by merging them.
+ */
+ private void mergingTokenDiff(Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+ ITokenComparator sa= null;
+ int ancestorStart= 0;
+ if (ancestorDoc != null) {
+ sa= createTokenComparator(a);
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] r= RangeDifferencer.findRanges(sa, sy, sm);
+ for (int i= 0; i < r.length; i++) {
+ RangeDifference es= r[i];
+ // determine range of diffs in one line
+ int start= i;
+ int leftLine= -1;
+ int rightLine= -1;
+ try {
+ leftLine= leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart()));
+ rightLine= rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart()));
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+ i++;
+ for (; i < r.length; i++) {
+ es= r[i];
+ try {
+ if (leftLine != leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart())))
+ break;
+ if (rightLine != rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart())))
+ break;
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+ }
+ int end= i;
+
+ // find first diff from left
+ RangeDifference first= null;
+ for (int ii= start; ii < end; ii++) {
+ es= r[ii];
+ if (useChange(es.kind())) {
+ first= es;
+ break;
+ }
+ }
+
+ // find first diff from mine
+ RangeDifference last= null;
+ for (int ii= end-1; ii >= start; ii--) {
+ es= r[ii];
+ if (useChange(es.kind())) {
+ last= es;
+ break;
+ }
+ }
+
+ if (first != null && last != null) {
+
+ int ancestorStart2= 0;
+ int ancestorEnd2= 0;
+ if (ancestorDoc != null) {
+ ancestorStart2= ancestorStart+sa.getTokenStart(first.ancestorStart());
+ ancestorEnd2= ancestorStart+getTokenEnd(sa, last.ancestorStart(), last.ancestorLength());
+ }
+
+ int leftStart2= leftStart+sy.getTokenStart(first.leftStart());
+ int leftEnd2= leftStart+getTokenEnd(sy, last.leftStart(), last.leftLength());
+
+ int rightStart2= rightStart+sm.getTokenStart(first.rightStart());
+ int rightEnd2= rightStart+getTokenEnd(sm, last.rightStart(), last.rightLength());
+ Diff diff= new Diff(baseDiff, first.kind(),
+ ancestorDoc, null, ancestorStart2, ancestorEnd2,
+ leftDoc, null, leftStart2, leftEnd2,
+ rightDoc, null, rightStart2, rightEnd2);
+ diff.fIsToken= true;
+ baseDiff.add(diff);
+ }
+ }
+ }
+
+ /*
+ * Performs a token based 3-way diff on the character range specified by the given baseDiff.
+ */
+ private void simpleTokenDiff(final Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+
+ int ancestorStart= 0;
+ ITokenComparator sa= null;
+ if (ancestorDoc != null) {
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ sa= createTokenComparator(a);
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] e= RangeDifferencer.findRanges(sa, sy, sm);
+ for (int i= 0; i < e.length; i++) {
+ RangeDifference es= e[i];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE) {
+
+ int ancestorStart2= ancestorStart;
+ int ancestorEnd2= ancestorStart;
+ if (ancestorDoc != null) {
+ ancestorStart2 += sa.getTokenStart(es.ancestorStart());
+ ancestorEnd2 += getTokenEnd(sa, es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart2= leftStart + sy.getTokenStart(es.leftStart());
+ int leftEnd2= leftStart + getTokenEnd(sy, es.leftStart(), es.leftLength());
+
+ int rightStart2= rightStart + sm.getTokenStart(es.rightStart());
+ int rightEnd2= rightStart + getTokenEnd(sm, es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(baseDiff, kind,
+ ancestorDoc, null, ancestorStart2, ancestorEnd2,
+ leftDoc, null, leftStart2, leftEnd2,
+ rightDoc, null, rightStart2, rightEnd2);
+
+ // ensure that token diff is smaller than basediff
+ int leftS= baseDiff.fLeftPos.offset;
+ int leftE= baseDiff.fLeftPos.offset+baseDiff.fLeftPos.length;
+ int rightS= baseDiff.fRightPos.offset;
+ int rightE= baseDiff.fRightPos.offset+baseDiff.fRightPos.length;
+ if (leftS != leftStart2 || leftE != leftEnd2 ||
+ rightS != rightStart2 || rightE != rightEnd2) {
+ diff.fIsToken= true;
+ // add to base Diff
+ baseDiff.add(diff);
+ }
+ }
+ }
+ }
+
+ private ITokenComparator createTokenComparator(String s) {
+ return fInput.createTokenComparator(s);
+ }
+
+ private static int maxWork(IRangeComparator a, IRangeComparator l, IRangeComparator r) {
+ int ln= l.getRangeCount();
+ int rn= r.getRangeCount();
+ if (a != null) {
+ int an= a.getRangeCount();
+ return (2 * Math.max(an, ln)) + (2 * Math.max(an, rn));
+ }
+ return 2 * Math.max(ln, rn);
+ }
+
+ private void resetPositions(IDocument doc) {
+ if (doc == null)
+ return;
+ try {
+ doc.removePositionCategory(DIFF_RANGE_CATEGORY);
+ } catch (BadPositionCategoryException e) {
+ // Ignore
+ }
+ doc.addPositionCategory(DIFF_RANGE_CATEGORY);
+ }
+
+ /*
+ * Returns the start line and the number of lines which correspond to the given position.
+ * Starting line number is 0 based.
+ */
+ protected Point getLineRange(IDocument doc, Position p, Point region) {
+
+ if (p == null || doc == null) {
+ region.x= 0;
+ region.y= 0;
+ return region;
+ }
+
+ int start= p.getOffset();
+ int length= p.getLength();
+
+ int startLine= 0;
+ try {
+ startLine= doc.getLineOfOffset(start);
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+
+ int lineCount= 0;
+
+ if (length == 0) {
+// // if range length is 0 and if range starts a new line
+// try {
+// if (start == doc.getLineStartOffset(startLine)) {
+// lines--;
+// }
+// } catch (BadLocationException e) {
+// lines--;
+// }
+
+ } else {
+ int endLine= 0;
+ try {
+ endLine= doc.getLineOfOffset(start + length - 1); // why -1?
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+ lineCount= endLine-startLine+1;
+ }
+
+ region.x= startLine;
+ region.y= lineCount;
+ return region;
+ }
+
+ public Diff findDiff(Position p, boolean left) {
+ for (Iterator iterator = fAllDiffs.iterator(); iterator.hasNext();) {
+ Diff diff = (Diff) iterator.next();
+ Position diffPos;
+ if (left) {
+ diffPos = diff.fLeftPos;
+ } else {
+ diffPos = diff.fRightPos;
+ }
+ // If the element falls within a diff, highlight that diff
+ if (diffPos.offset + diffPos.length >= p.offset && diff.fDirection != RangeDifference.NOCHANGE)
+ return diff;
+ // Otherwise, highlight the first diff after the elements position
+ if (diffPos.offset >= p.offset)
+ return diff;
+ }
+ return null;
+ }
+
+ public void reset() {
+ fChangeDiffs= null;
+ fAllDiffs= null;
+ }
+
+ /**
+ * Returns the virtual position for the given view position.
+ * @param contributor
+ * @param vpos
+ * @return the virtual position for the given view position
+ */
+ public int realToVirtualPosition(char contributor, int vpos) {
+
+ if (fAllDiffs == null)
+ return vpos;
+
+ int viewPos= 0; // real view position
+ int virtualPos= 0; // virtual position
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(contributor);
+ getLineRange(getDocument(contributor),pos, region);
+ int realHeight= region.y;
+ int virtualHeight= diff.getMaxDiffHeight();
+ if (vpos <= viewPos + realHeight) { // OK, found!
+ vpos-= viewPos; // make relative to this slot
+ // now scale position within this slot to virtual slot
+ if (realHeight <= 0)
+ vpos= 0;
+ else
+ vpos= (vpos*virtualHeight)/realHeight;
+ return virtualPos+vpos;
+ }
+ viewPos+= realHeight;
+ virtualPos+= virtualHeight;
+ }
+ return virtualPos;
+ }
+
+ /**
+ * maps given virtual position into a real view position of this view.
+ * @param contributor
+ * @param v
+ * @return the real view position
+ */
+ public int virtualToRealPosition(char contributor, int v) {
+
+ if (fAllDiffs == null)
+ return v;
+
+ int virtualPos= 0;
+ int viewPos= 0;
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(contributor);
+ int viewHeight= getLineRange(getDocument(contributor), pos, region).y;
+ int virtualHeight= diff.getMaxDiffHeight();
+ if (v < (virtualPos + virtualHeight)) {
+ v-= virtualPos; // make relative to this slot
+ if (viewHeight <= 0) {
+ v= 0;
+ } else {
+ v= (int) (v * ((double)viewHeight/virtualHeight));
+ }
+ return viewPos+v;
+ }
+ virtualPos+= virtualHeight;
+ viewPos+= viewHeight;
+ }
+ return viewPos;
+ }
+
+ /*
+ * Calculates virtual height (in lines) of views by adding the maximum of corresponding diffs.
+ */
+ public int getVirtualHeight() {
+ int h= 1;
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ h+= diff.getMaxDiffHeight();
+ }
+ }
+ return h;
+ }
+
+ /*
+ * Calculates height (in lines) of right view by adding the height of the right diffs.
+ */
+ public int getRightHeight() {
+ int h= 1;
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ h+= diff.getRightHeight();
+ }
+ }
+ return h;
+ }
+
+ public int findInsertionPoint(Diff diff, char type) {
+ if (diff != null) {
+ switch (type) {
+ case MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR:
+ if (diff.fAncestorPos != null)
+ return diff.fAncestorPos.offset;
+ break;
+ case MergeViewerContentProvider.LEFT_CONTRIBUTOR:
+ if (diff.fLeftPos != null)
+ return diff.fLeftPos.offset;
+ break;
+ case MergeViewerContentProvider.RIGHT_CONTRIBUTOR:
+ if (diff.fRightPos != null)
+ return diff.fRightPos.offset;
+ break;
+ }
+ }
+ return 0;
+ }
+
+ public Diff[] getChangeDiffs(char contributor, IRegion region) {
+ if (fChangeDiffs == null)
+ return new Diff[0];
+ List intersectingDiffs = new ArrayList();
+ for (Iterator iterator = fChangeDiffs.iterator(); iterator.hasNext();) {
+ Diff diff = (Diff) iterator.next();
+ Diff[] changeDiffs = diff.getChangeDiffs(contributor, region);
+ for (int i = 0; i < changeDiffs.length; i++) {
+ Diff changeDiff = changeDiffs[i];
+ intersectingDiffs.add(changeDiff);
+ }
+ }
+ return (Diff[]) intersectingDiffs.toArray(new Diff[intersectingDiffs.size()]);
+ }
+
+ public Diff findDiff(int viewportHeight, boolean synchronizedScrolling, Point size, int my) {
+ int virtualHeight= synchronizedScrolling ? getVirtualHeight() : getRightHeight();
+ if (virtualHeight < viewportHeight)
+ return null;
+
+ int yy, hh;
+ int y= 0;
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ int h= synchronizedScrolling ? diff.getMaxDiffHeight()
+ : diff.getRightHeight();
+ if (useChange(diff.getKind()) && !diff.fIsWhitespace) {
+
+ yy= (y*size.y)/virtualHeight;
+ hh= (h*size.y)/virtualHeight;
+ if (hh < 3)
+ hh= 3;
+
+ if (my >= yy && my < yy+hh)
+ return diff;
+ }
+ y+= h;
+ }
+ }
+ return null;
+ }
+
+ public boolean hasChanges() {
+ return fChangeDiffs != null && !fChangeDiffs.isEmpty();
+ }
+
+ public Iterator changesIterator() {
+ if (fChangeDiffs == null)
+ return new ArrayList().iterator();
+ return fChangeDiffs.iterator();
+ }
+
+ public Iterator rangesIterator() {
+ if (fAllDiffs == null)
+ return new ArrayList().iterator();
+ return fAllDiffs.iterator();
+ }
+
+ public boolean isFirstChildDiff(char contributor, int startOffset,
+ Diff diff) {
+ if (!diff.hasChildren())
+ return false;
+ Diff d = (Diff)diff.fDiffs.get(0);
+ Position p= d.getPosition(contributor);
+ return (p.getOffset() >= startOffset);
+ }
+
+ public Diff getWrappedDiff(Diff diff, boolean down) {
+ if (fChangeDiffs != null && fChangeDiffs.size() > 0) {
+ if (down)
+ return (Diff) fChangeDiffs.get(0);
+ return (Diff) fChangeDiffs.get(fChangeDiffs.size()-1);
+ }
+ return null;
+ }
+
+ /*
+ * Copy the contents of the given diff from one side to the other but
+ * doesn't reveal anything.
+ * Returns true if copy was successful.
+ */
+ public boolean copy(Diff diff, boolean leftToRight) {
+
+ if (diff != null && !diff.isResolved()) {
+ Position fromPos= null;
+ Position toPos= null;
+ IDocument fromDoc= null;
+ IDocument toDoc= null;
+
+ if (leftToRight) {
+ fromPos= diff.getPosition(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
+ toPos= diff.getPosition(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
+ fromDoc= getDocument(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
+ toDoc= getDocument(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
+ } else {
+ fromPos= diff.getPosition(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
+ toPos= diff.getPosition(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
+ fromDoc= getDocument(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
+ toDoc= getDocument(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
+ }
+
+ if (fromDoc != null) {
+
+ int fromStart= fromPos.getOffset();
+ int fromLen= fromPos.getLength();
+
+ int toStart= toPos.getOffset();
+ int toLen= toPos.getLength();
+
+ try {
+ String s= null;
+
+ switch (diff.getKind()) {
+ case RangeDifference.RIGHT:
+ case RangeDifference.LEFT:
+ s= fromDoc.get(fromStart, fromLen);
+ break;
+ case RangeDifference.ANCESTOR:
+ break;
+ case RangeDifference.CONFLICT:
+ if (APPEND_CONFLICT) {
+ s= toDoc.get(toStart, toLen);
+ String ls = TextUtilities.getDefaultLineDelimiter(toDoc);
+ if (!s.endsWith(ls))
+ s += ls;
+ s+= fromDoc.get(fromStart, fromLen);
+ } else
+ s= fromDoc.get(fromStart, fromLen);
+ break;
+ }
+ if (s != null) {
+ toDoc.replace(toStart, toLen, s);
+ toPos.setOffset(toStart);
+ toPos.setLength(s.length());
+ }
+
+ } catch (BadLocationException e) {
+ // silently ignored
+ }
+ }
+
+ diff.setResolved(true);
+ return true;
+ }
+ return false;
+ }
+
+ public int changesCount() {
+ if (fChangeDiffs == null)
+ return 0;
+ return fChangeDiffs.size();
+ }
+
+ public Diff findDiff(char contributor, int rangeStart, int rangeEnd) {
+ if (hasChanges()) {
+ for (Iterator iterator = changesIterator(); iterator.hasNext();) {
+ Diff diff = (Diff) iterator.next();
+ if (diff.isDeleted() || diff.getKind() == RangeDifference.NOCHANGE)
+ continue;
+ if (diff.overlaps(contributor, rangeStart, rangeEnd, getDocument(contributor).getLength()))
+ return diff;
+ }
+ }
+ return null;
+ }
+
+ public Diff findDiff(char contributor, Position range) {
+ int start= range.getOffset();
+ int end= start + range.getLength();
+ return findDiff(contributor, start, end);
+ }
+
+ public Diff findNext(char contributor, int start, int end, boolean deep) {
+ return findNext(contributor, fChangeDiffs, start, end, deep);
+ }
+
+ private Diff findNext(char contributor, List v, int start, int end, boolean deep) {
+ if (v == null)
+ return null;
+ for (int i= 0; i < v.size(); i++) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(contributor);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ if (end < startOffset) // <=
+ return diff;
+ if (deep && diff.hasChildren()) {
+ Diff d= null;
+ int endOffset= startOffset + p.getLength();
+ if (start == startOffset && (end == endOffset || end == endOffset-1)) {
+ d= findNext(contributor, diff.fDiffs, start-1, start-1, deep);
+ } else if (end < endOffset) {
+ d= findNext(contributor, diff.fDiffs, start, end, deep);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ public Diff findPrev(char contributor, int start, int end, boolean deep) {
+ return findPrev(contributor, fChangeDiffs, start, end, deep);
+ }
+
+ private Diff findPrev(char contributor, List v, int start, int end, boolean deep) {
+ if (v == null)
+ return null;
+ for (int i= v.size()-1; i >= 0; i--) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(contributor);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ int endOffset= startOffset + p.getLength();
+ if (start > endOffset) {
+ if (deep && diff.hasChildren()) {
+ // If we are going deep, find the last change in the diff
+ return findPrev(contributor, diff.fDiffs, end, end, deep);
+ }
+ return diff;
+ }
+ if (deep && diff.hasChildren()) {
+ Diff d= null;
+ if (start == startOffset && end == endOffset) {
+ // A whole diff is selected so we'll fall through
+ // and go the the last change in the previous diff
+ } else if (start >= startOffset) {
+ // If we are at or before the first diff, select the
+ // entire diff so next and previous are symmetrical
+ if (isFirstChildDiff(contributor, startOffset, diff)) {
+ return diff;
+ }
+ d= findPrev(contributor, diff.fDiffs, start, end, deep);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/LineComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/LineComparator.java
new file mode 100644
index 000000000..4b4891637
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/LineComparator.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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.merge;
+
+import java.io.*;
+import java.util.ArrayList;
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+
+/**
+ * This implementation of IRangeComparator breaks an input stream into lines.
+ */
+class LineComparator implements IRangeComparator {
+
+ private String[] fLines;
+
+ public LineComparator(InputStream is, String encoding) throws IOException {
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(is, encoding));
+ String line;
+ ArrayList ar = new ArrayList();
+ while ((line = br.readLine()) != null) {
+ ar.add(line);
+ }
+ // It is the responsibility of the caller to close the stream
+ fLines = (String[]) ar.toArray(new String[ar.size()]);
+ }
+
+ String getLine(int ix) {
+ return fLines[ix];
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.rangedifferencer.IRangeComparator#getRangeCount()
+ */
+ public int getRangeCount() {
+ return fLines.length;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.rangedifferencer.IRangeComparator#rangesEqual(int, org.eclipse.compare.rangedifferencer.IRangeComparator, int)
+ */
+ public boolean rangesEqual(int thisIndex, IRangeComparator other,
+ int otherIndex) {
+ String s1 = fLines[thisIndex];
+ String s2 = ((LineComparator) other).fLines[otherIndex];
+ return s1.equals(s2);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.rangedifferencer.IRangeComparator#skipRangeComparison(int, int, org.eclipse.compare.rangedifferencer.IRangeComparator)
+ */
+ public boolean skipRangeComparison(int length, int maxLength, IRangeComparator other) {
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.java
new file mode 100644
index 000000000..f3fa54dea
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.compare.internal.merge;
+
+import org.eclipse.osgi.util.NLS;
+
+public final class MergeMessages extends NLS {
+
+ private static final String BUNDLE_NAME = "org.eclipse.compare.internal.merge.MergeMessages";//$NON-NLS-1$
+
+ private MergeMessages() {
+ // Do not instantiate
+ }
+
+ public static String TextAutoMerge_inputEncodingError;
+ public static String TextAutoMerge_outputEncodingError;
+ public static String TextAutoMerge_outputIOError;
+ public static String TextAutoMerge_conflict;
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, MergeMessages.class);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.properties
new file mode 100644
index 000000000..a548ea8e6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/MergeMessages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2004, 2006 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
+###############################################################################
+
+TextAutoMerge_inputEncodingError= Unsupported encoding for input streams
+TextAutoMerge_outputEncodingError= Unsupported encoding for output streams
+TextAutoMerge_outputIOError= IO error on writing
+TextAutoMerge_conflict= Conflict: cannot auto merge
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/TextStreamMerger.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/TextStreamMerger.java
new file mode 100644
index 000000000..2aaecd12b
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/TextStreamMerger.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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.merge;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.rangedifferencer.RangeDifference;
+import org.eclipse.compare.rangedifferencer.RangeDifferencer;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * A simple merger for streams containing text lines.
+ */
+public class TextStreamMerger implements IStreamMerger {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.compare.internal.merge.IAutoMerger#automerge(java.io.OutputStream,
+ * org.eclipse.core.resources.IEncodedStorage,
+ * org.eclipse.core.resources.IEncodedStorage,
+ * org.eclipse.core.resources.IEncodedStorage,
+ * org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus merge(OutputStream output, String outputEncoding, InputStream ancestor, String ancestorEncoding, InputStream target, String targetEncoding, InputStream other, String otherEncoding, IProgressMonitor monitor) {
+
+ LineComparator a, t, o;
+
+ try {
+ a= new LineComparator(ancestor, ancestorEncoding);
+ t= new LineComparator(target, targetEncoding);
+ o= new LineComparator(other, otherEncoding);
+ } catch (UnsupportedEncodingException e) {
+ return new Status(IStatus.ERROR, CompareUI.PLUGIN_ID, 1, MergeMessages.TextAutoMerge_inputEncodingError, e);
+ } catch (IOException e) {
+ return new Status(IStatus.ERROR, CompareUI.PLUGIN_ID, 1, e.getMessage(), e);
+ }
+
+ try {
+ String lineSeparator= System.getProperty("line.separator"); //$NON-NLS-1$
+ if (lineSeparator == null)
+ lineSeparator= "\n"; //$NON-NLS-1$
+
+ RangeDifference[] diffs= RangeDifferencer.findRanges(monitor, a, t, o);
+
+ for (int i= 0; i < diffs.length; i++) {
+ RangeDifference rd= diffs[i];
+ switch (rd.kind()) {
+ case RangeDifference.ANCESTOR: // pseudo conflict
+ case RangeDifference.NOCHANGE:
+ case RangeDifference.RIGHT:
+ for (int j= rd.rightStart(); j < rd.rightEnd(); j++) {
+ String s= o.getLine(j);
+ output.write(s.getBytes(outputEncoding));
+ output.write(lineSeparator.getBytes(outputEncoding));
+ }
+ break;
+
+ case RangeDifference.LEFT:
+ for (int j= rd.leftStart(); j < rd.leftEnd(); j++) {
+ String s= t.getLine(j);
+ output.write(s.getBytes(outputEncoding));
+ output.write(lineSeparator.getBytes(outputEncoding));
+ }
+ break;
+
+ case RangeDifference.CONFLICT:
+ return new Status(IStatus.ERROR, CompareUI.PLUGIN_ID, CONFLICT, MergeMessages.TextAutoMerge_conflict, null);
+
+ default:
+ break;
+ }
+ }
+
+ } catch (UnsupportedEncodingException e) {
+ return new Status(IStatus.ERROR, CompareUI.PLUGIN_ID, 1, MergeMessages.TextAutoMerge_outputEncodingError, e);
+ } catch (IOException e) {
+ return new Status(IStatus.ERROR, CompareUI.PLUGIN_ID, 1, MergeMessages.TextAutoMerge_outputIOError, e);
+ }
+
+ return Status.OK_STATUS;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DecoratorOverlayIcon.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DecoratorOverlayIcon.java
new file mode 100644
index 000000000..1a8e3d44c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DecoratorOverlayIcon.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.patch;
+
+import java.util.Arrays;
+
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.IDecoration;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * An DecoratorOverlayIcon consists of a main icon and several adornments.
+ * Copied until bug 164394 is resolved.
+ */
+class DecoratorOverlayIcon extends CompositeImageDescriptor {
+ // the base image
+ private Image base;
+
+ // the overlay images
+ private ImageDescriptor[] overlays;
+
+ // the size
+ private Point size;
+
+
+ /**
+ * OverlayIcon constructor.
+ *
+ * @param baseImage the base image
+ * @param overlaysArray the overlay images
+ * @param sizeValue the size
+ */
+ public DecoratorOverlayIcon(Image baseImage,
+ ImageDescriptor[] overlaysArray, Point sizeValue) {
+ this.base = baseImage;
+ this.overlays = overlaysArray;
+ this.size = sizeValue;
+ }
+
+ /**
+ * Draw the overlays for the receiver.
+ * @param overlaysArray the overlay images
+ */
+ protected void drawOverlays(ImageDescriptor[] overlaysArray) {
+
+ for (int i = 0; i < overlays.length; i++) {
+ ImageDescriptor overlay = overlaysArray[i];
+ if (overlay == null) {
+ continue;
+ }
+ ImageData overlayData = overlay.getImageData();
+ //Use the missing descriptor if it is not there.
+ if (overlayData == null) {
+ overlayData = ImageDescriptor.getMissingImageDescriptor()
+ .getImageData();
+ }
+ switch (i) {
+ case IDecoration.TOP_LEFT:
+ drawImage(overlayData, 0, 0);
+ break;
+ case IDecoration.TOP_RIGHT:
+ drawImage(overlayData, size.x - overlayData.width, 0);
+ break;
+ case IDecoration.BOTTOM_LEFT:
+ drawImage(overlayData, 0, size.y - overlayData.height);
+ break;
+ case IDecoration.BOTTOM_RIGHT:
+ drawImage(overlayData, size.x - overlayData.width, size.y
+ - overlayData.height);
+ break;
+ }
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof DecoratorOverlayIcon)) {
+ return false;
+ }
+ DecoratorOverlayIcon other = (DecoratorOverlayIcon) o;
+ return base.equals(other.base)
+ && Arrays.equals(overlays, other.overlays);
+ }
+
+ public int hashCode() {
+ int code = base.hashCode();
+ for (int i = 0; i < overlays.length; i++) {
+ if (overlays[i] != null) {
+ code ^= overlays[i].hashCode();
+ }
+ }
+ return code;
+ }
+
+ protected void drawCompositeImage(int width, int height) {
+ ImageDescriptor underlay = overlays[IDecoration.UNDERLAY];
+ if (underlay != null) {
+ drawImage(underlay.getImageData(), 0, 0);
+ }
+ drawImage(base.getImageData(), 0, 0);
+ drawOverlays(overlays);
+ }
+
+ protected Point getSize() {
+ return size;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.resource.CompositeImageDescriptor#getTransparentPixel()
+ */
+ protected int getTransparentPixel() {
+ return base.getImageData().transparentPixel;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DiffViewerComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DiffViewerComparator.java
new file mode 100644
index 000000000..5a0134edd
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/DiffViewerComparator.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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.patch;
+
+import java.util.Comparator;
+import java.util.regex.Pattern;
+
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.DocumentRangeNode;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.viewers.ViewerSorter;
+
+public class DiffViewerComparator extends ViewerSorter {
+
+ public boolean isSorterProperty(Object element, Object property) {
+ return false;
+ }
+
+ public int category(Object node) {
+ if (node instanceof DiffNode) {
+ Object o= ((DiffNode) node).getId();
+ if (o instanceof DocumentRangeNode)
+ return ((DocumentRangeNode) o).getTypeCode();
+ }
+ return 0;
+ }
+
+ protected Comparator getComparator() {
+ return new Comparator() {
+ public int compare(Object arg0, Object arg1) {
+ String label0 = arg0 == null ? "" : arg0.toString(); //$NON-NLS-1$
+ String label1 = arg1 == null ? "" : arg1.toString(); //$NON-NLS-1$
+
+ // see org.eclipse.compare.internal.patch.Hunk.getDescription()
+ String pattern = "\\d+,\\d+ -> \\d+,\\d+.*"; //$NON-NLS-1$
+
+ if (Pattern.matches(pattern, label0)
+ && Pattern.matches(pattern, label1)) {
+ int oldStart0 = Integer.parseInt(label0.split(",")[0]); //$NON-NLS-1$
+ int oldStart1 = Integer.parseInt(label1.split(",")[0]); //$NON-NLS-1$
+
+ return oldStart0 - oldStart1;
+ }
+ return Policy.getComparator().compare(arg0, arg1);
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/FilePatch.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/FilePatch.java
new file mode 100644
index 000000000..5841f269e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/FilePatch.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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.patch;
+
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.patch.IFilePatch;
+import org.eclipse.compare.patch.IFilePatchResult;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class FilePatch extends FilePatch2 implements IFilePatch {
+
+ public FilePatch(IPath oldPath, long oldDate, IPath newPath,
+ long newDate) {
+ super(oldPath, oldDate, newPath, newDate);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.compare.patch.IFilePatch#apply(org.eclipse.core.resources
+ * .IStorage, org.eclipse.compare.patch.PatchConfiguration,
+ * org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IFilePatchResult apply(IStorage content,
+ PatchConfiguration configuration, IProgressMonitor monitor) {
+ return apply(content != null ? Utilities.getReaderCreator(content)
+ : null, configuration, monitor);
+ }
+
+ protected FilePatch2 create(IPath oldPath, long oldDate, IPath newPath,
+ long newDate) {
+ return new FilePatch(oldPath, oldDate, newPath, newDate);
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkDiffNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkDiffNode.java
new file mode 100644
index 000000000..1fa0f3a79
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkDiffNode.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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.patch;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.internal.core.patch.HunkResult;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.core.resources.IResource;
+
+public class HunkDiffNode extends PatchDiffNode {
+
+ private final HunkResult result;
+
+ public static HunkDiffNode createDiffNode(PatchFileDiffNode parent, HunkResult result, boolean fullContext) {
+ return createDiffNode(parent, result, fullContext, fullContext, fullContext);
+ }
+
+ public static HunkDiffNode createDiffNode(PatchFileDiffNode parent, HunkResult result, boolean ancestorFullContext, boolean leftFullContext, boolean rightFullContext) {
+ return new HunkDiffNode(result, parent, Differencer.CHANGE, getAncestorElement(result, ancestorFullContext), getLeftElement(result, leftFullContext), getRightElement(result, rightFullContext));
+ }
+
+ public static ITypedElement getRightElement(HunkResult result, boolean fullContext) {
+ return new HunkTypedElement(result, true /* isResult */, fullContext);
+ }
+
+ private static ITypedElement getLeftElement(HunkResult result,
+ boolean fullContext) {
+ if (fullContext && !result.isOK())
+ return new UnmatchedHunkTypedElement(result);
+ return new HunkTypedElement(result, false /* before state */, fullContext);
+ }
+
+ public static ITypedElement getAncestorElement(HunkResult result, boolean fullContext) {
+ if (!fullContext && result.isOK()) {
+ return new HunkTypedElement(result, false /* before state */, fullContext);
+ }
+ if (!fullContext) {
+ // Don't provide an ancestor if the hunk didn't match or we're not doing fullContext
+ return null;
+ }
+ // Make the ancestor the same as the left so we have an incoming change
+ return new HunkTypedElement(result, false /* before state */, result.isOK());
+ }
+
+ public HunkDiffNode(HunkResult result, PatchFileDiffNode parent, int kind, ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ super(result.getHunk(), parent, kind, ancestor, left, right);
+ this.result = result;
+ }
+
+ public HunkResult getHunkResult() {
+ return result;
+ }
+
+ protected PatchConfiguration getConfiguration() {
+ return result.getDiffResult().getConfiguration();
+ }
+
+ public boolean isManuallyMerged() {
+ Object left = getLeft();
+ if (left instanceof UnmatchedHunkTypedElement) {
+ UnmatchedHunkTypedElement element = (UnmatchedHunkTypedElement) left;
+ return element.isManuallyMerged();
+ }
+ return false;
+ }
+
+ public boolean isFuzzUsed() {
+ return result.getFuzz() > 0;
+ }
+
+ public boolean isAllContextIgnored() {
+ int fuzz = result.getFuzz();
+ if (fuzz > 0) {
+ String[] lines = result.getHunk().getLines();
+ int contextLines = 0;
+ for (int i = 0; i < lines.length; i++) {
+ String line = lines[i];
+ char c = line.charAt(0);
+ if (c == ' ') {
+ contextLines++;
+ } else {
+ if (contextLines > 0 && fuzz >= contextLines) {
+ return true;
+ }
+ contextLines = 0;
+ }
+ }
+ if (contextLines > 0 && fuzz >= contextLines) {
+ return true;
+ }
+
+ }
+ return false;
+ }
+
+ public IResource getResource() {
+ return ((PatchFileDiffNode)getParent()).getResource();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkTypedElement.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkTypedElement.java
new file mode 100644
index 000000000..4ff4ab349
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkTypedElement.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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.patch;
+
+import java.io.InputStream;
+
+import org.eclipse.compare.IEncodedStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.DiffImageDescriptor;
+import org.eclipse.compare.internal.ICompareUIConstants;
+import org.eclipse.compare.internal.core.patch.FileDiffResult;
+import org.eclipse.compare.internal.core.patch.HunkResult;
+import org.eclipse.compare.patch.IHunk;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.swt.graphics.Image;
+
+public class HunkTypedElement implements ITypedElement, IEncodedStreamContentAccessor, IAdaptable {
+
+ private final HunkResult fHunkResult;
+ private final boolean fIsAfterState;
+ private final boolean fFullContext;
+
+ public HunkTypedElement(HunkResult result, boolean isAfterState, boolean fullContext) {
+ this.fHunkResult = result;
+ this.fIsAfterState = isAfterState;
+ this.fFullContext = fullContext;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getImage()
+ */
+ public Image getImage() {
+ LocalResourceManager imageCache = PatchCompareEditorInput.getImageCache(fHunkResult.getDiffResult().getConfiguration());
+ ImageDescriptor imageDesc = CompareUIPlugin.getImageDescriptor(ICompareUIConstants.HUNK_OBJ);
+ Image image = imageCache.createImage(imageDesc);
+ if (!fHunkResult.isOK()) {
+ return getHunkErrorImage(image, imageCache, true);
+ } else if (fHunkResult.getFuzz() > 0) {
+ return getHunkOverlayImage(image, imageCache, ICompareUIConstants.WARNING_OVERLAY, true);
+ }
+ return image;
+ }
+
+ public static Image getHunkErrorImage(Image baseImage, LocalResourceManager imageCache, boolean onLeft) {
+ return getHunkOverlayImage(baseImage, imageCache, ICompareUIConstants.ERROR_OVERLAY, onLeft);
+ }
+
+ private static Image getHunkOverlayImage(Image baseImage, LocalResourceManager imageCache, String path, boolean onLeft) {
+ ImageDescriptor desc = new DiffImageDescriptor(baseImage, CompareUIPlugin.getImageDescriptor(path), ICompareUIConstants.COMPARE_IMAGE_WIDTH, onLeft);
+ Image image = imageCache.createImage(desc);
+ return image;
+ }
+
+ public boolean isManuallyMerged() {
+ return getPatcher().isManuallyMerged(getHunkResult().getHunk());
+ }
+
+ private Patcher getPatcher() {
+ return Patcher.getPatcher(fHunkResult.getDiffResult().getConfiguration());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getName()
+ */
+ public String getName() {
+ return fHunkResult.getHunk().getLabel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getType()
+ */
+ public String getType() {
+ return fHunkResult.getDiffResult().getDiff().getTargetPath(fHunkResult.getDiffResult().getConfiguration()).getFileExtension();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IStreamContentAccessor#getContents()
+ */
+ public InputStream getContents() throws CoreException {
+ String contents = fHunkResult.getContents(fIsAfterState, fFullContext);
+ return FileDiffResult.asInputStream(contents, fHunkResult.getCharset());
+ }
+
+ public String getCharset() throws CoreException {
+ return fHunkResult.getCharset();
+ }
+
+ public HunkResult getHunkResult() {
+ return fHunkResult;
+ }
+
+ public Object getAdapter(Class adapter) {
+ if (adapter == IHunk.class)
+ return fHunkResult;
+ if (adapter == HunkResult.class)
+ return fHunkResult;
+ return Platform.getAdapterManager().getAdapter(this, adapter);
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
new file mode 100644
index 000000000..7248fccb3
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/InputPatchPage.java
@@ -0,0 +1,990 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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
+ * Sebastian Davids <sdavids@gmx.de> - layout tweaks
+ * Matt McCutchen <hashproduct+eclipse@gmail.com> - Bug 180358 [Apply Patch] Cursor jumps to beginning of filename field on keystroke
+ *******************************************************************************/
+package org.eclipse.compare.internal.patch;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.compare.internal.ICompareContextIds;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.OperationCanceledException;
+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.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.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+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.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+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.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.views.navigator.ResourceComparator;
+
+import com.ibm.icu.text.MessageFormat;
+
+public class InputPatchPage extends WizardPage {
+
+ // constants
+ protected static final int SIZING_TEXT_FIELD_WIDTH= 250;
+ protected static final int COMBO_HISTORY_LENGTH= 5;
+
+ // dialog store id constants
+ private final static String PAGE_NAME= "PatchWizardPage1"; //$NON-NLS-1$
+ private final static String STORE_PATCH_FILES_ID= PAGE_NAME+".PATCH_FILES"; //$NON-NLS-1$
+ private final static String STORE_PATCH_URLS_ID= PAGE_NAME+".PATCH_URLS"; //$NON-NLS-1$
+ private final static String STORE_INPUT_METHOD_ID= PAGE_NAME+".INPUT_METHOD"; //$NON-NLS-1$
+ private final static String STORE_WORKSPACE_PATH_ID= PAGE_NAME+".WORKSPACE_PATH"; //$NON-NLS-1$
+
+ //patch input constants
+ protected final static int CLIPBOARD= 1;
+ protected final static int FILE= 2;
+ protected final static int WORKSPACE= 3;
+ protected final static int URL= 4;
+
+ protected final static String INPUTPATCHPAGE_NAME= "InputPatchPage"; //$NON-NLS-1$
+
+ static final char SEPARATOR= System.getProperty("file.separator").charAt(0); //$NON-NLS-1$
+
+ private boolean fShowError= false;
+ private String fPatchSource;
+ private boolean fPatchRead= false;
+ private PatchWizard fPatchWizard;
+ private ActivationListener fActivationListener= new ActivationListener();
+
+ // SWT widgets
+ private Button fUseClipboardButton;
+ private Combo fPatchFileNameField;
+ private Button fPatchFileBrowseButton;
+ private Button fUsePatchFileButton;
+ private Button fUseWorkspaceButton;
+ private Button fUseURLButton;
+ private Combo fPatchURLField;
+ private Label fWorkspaceSelectLabel;
+ private TreeViewer fTreeViewer;
+
+ class ActivationListener extends ShellAdapter {
+ public void shellActivated(ShellEvent e) {
+ // allow error messages if the selected input actually has something selected in it
+ fShowError=true;
+ switch(getInputMethod()) {
+ case FILE:
+ fShowError= (fPatchFileNameField.getText() != ""); //$NON-NLS-1$
+ break;
+ case URL:
+ fShowError = (fPatchURLField.getText() != ""); //$NON-NLS-1$
+ break;
+ case WORKSPACE:
+ fShowError= (!fTreeViewer.getSelection().isEmpty());
+ break;
+ }
+ updateWidgetEnablements();
+ }
+ }
+
+ public InputPatchPage(PatchWizard pw) {
+ super(INPUTPATCHPAGE_NAME, PatchMessages.InputPatchPage_title, null);
+ fPatchWizard= pw;
+ setMessage(PatchMessages.InputPatchPage_message);
+ }
+
+ /*
+ * Get a path from the supplied text widget.
+ * @return org.eclipse.core.runtime.IPath
+ */
+ protected IPath getPathFromText(Text textField) {
+ return (new Path(textField.getText())).makeAbsolute();
+ }
+
+ /* package */ String getPatchName() {
+ if (getInputMethod() == CLIPBOARD)
+ return PatchMessages.InputPatchPage_Clipboard;
+ return getPatchFilePath();
+ }
+
+ public void createControl(Composite parent) {
+
+ Composite composite= new Composite(parent, SWT.NULL);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
+ setControl(composite);
+
+ initializeDialogUnits(parent);
+
+ buildPatchFileGroup(composite);
+
+ // by default, whatever was used last was selected or
+ // default to File if nothing has been selected
+ restoreWidgetValues();
+
+ // see if there are any better options presently selected (i.e workspace
+ // or clipboard or URL from clipboard)
+ adjustToCurrentTarget();
+
+ // No error for dialog opening
+ fShowError= false;
+ clearErrorMessage();
+ updateWidgetEnablements();
+
+ Shell shell= getShell();
+ shell.addShellListener(fActivationListener);
+
+ Dialog.applyDialogFont(composite);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, ICompareContextIds.PATCH_INPUT_WIZARD_PAGE);
+ }
+
+ /**
+ * Returns the next page depending on what type of patch is being applied:
+ * i) If the patch is a Workspace patch then it will proceed right to the PreviewPatchPage
+ * ii) If the patch is a single project patch then it will proceed to the PatchTargetPage, which
+ * allows the user to specify where to root the patch
+ * @return PreviewPatchPage if multi-project patch, PatchTargetPage if single project patch
+ */
+ public IWizardPage getNextPage() {
+
+ WorkspacePatcher patcher= ((PatchWizard) getWizard()).getPatcher();
+
+ // Read in the patch
+ readInPatch();
+
+ FilePatch2[] diffs= patcher.getDiffs();
+ if (diffs == null || diffs.length == 0) {
+ String format= PatchMessages.InputPatchPage_NoDiffsFound_format;
+ String message= MessageFormat.format(format, new String[] { fPatchSource });
+ MessageDialog.openInformation(null,
+ PatchMessages.InputPatchPage_PatchErrorDialog_title, message);
+ return this;
+ }
+
+ // guess prefix count
+ int guess= 0; // guessPrefix(diffs);
+ patcher.setStripPrefixSegments(guess);
+
+ // If this is a workspace patch we don't need to set a target as the targets will be figured out from
+ // all of the projects that make up the patch and continue on to final preview page
+ // else go on to target selection page
+ if (patcher.isWorkspacePatch()) {
+ // skip 'Patch Target' page
+ IWizardPage page = super.getNextPage();
+ if (page.getName().equals(PatchTargetPage.PATCHTARGETPAGE_NAME))
+ return page.getNextPage();
+ }
+
+ return super.getNextPage();
+ }
+
+ /*
+ * Reads in the patch contents
+ */
+ public void readInPatch(){
+ WorkspacePatcher patcher= ((PatchWizard) getWizard()).getPatcher();
+ // Create a reader for the input
+ Reader reader= null;
+ try {
+ int inputMethod= getInputMethod();
+ if (inputMethod == CLIPBOARD) {
+ Control c= getControl();
+ if (c != null) {
+ Clipboard clipboard= new Clipboard(c.getDisplay());
+ Object o= clipboard.getContents(TextTransfer.getInstance());
+ clipboard.dispose();
+ if (o instanceof String)
+ reader= new StringReader((String)o);
+ }
+ fPatchSource= PatchMessages.InputPatchPage_Clipboard_title;
+ } else if (inputMethod==FILE) {
+ String patchFilePath= getPatchFilePath();
+ if (patchFilePath != null) {
+ try {
+ reader= new FileReader(patchFilePath);
+ } catch (FileNotFoundException ex) {
+ MessageDialog.openError(null,
+ PatchMessages.InputPatchPage_PatchErrorDialog_title,
+ PatchMessages.InputPatchPage_PatchFileNotFound_message);
+ }
+ }
+ fPatchSource= PatchMessages.InputPatchPage_PatchFile_title;
+ } else if (inputMethod==URL) {
+ String patchFileURL = fPatchURLField.getText();
+ if (patchFileURL != null) {
+ try {
+ String contents = Utilities.getURLContents(new URL(
+ patchFileURL), getContainer());
+ if (contents != null)
+ reader = new StringReader(contents);
+ } catch (MalformedURLException e) {
+ // ignore as we tested it with modify listener on combo
+ } catch (InvocationTargetException e) { // ignore
+ } catch (OperationCanceledException e) { // ignore
+ } catch (InterruptedException e) { // ignore
+ }
+ }
+ fPatchSource= PatchMessages.InputPatchPage_URL_title;
+ } else if (inputMethod==WORKSPACE) {
+ // Get the selected patch file (tree will only allow for one selection)
+ IResource[] resources= Utilities.getResources(fTreeViewer.getSelection());
+ IResource patchFile= resources[0];
+ if (patchFile != null) {
+ try {
+ reader= new FileReader(patchFile.getLocation().toFile());
+ } catch (FileNotFoundException ex) {
+ MessageDialog.openError(null, PatchMessages.InputPatchPage_PatchErrorDialog_title, PatchMessages.InputPatchPage_PatchFileNotFound_message);
+ } catch (NullPointerException nex) {
+ //in case the path doesn't exist (eg. getLocation() returned null)
+ MessageDialog.openError(null, PatchMessages.InputPatchPage_PatchErrorDialog_title, PatchMessages.InputPatchPage_PatchFileNotFound_message);
+ }
+ }
+ fPatchSource= PatchMessages.InputPatchPage_WorkspacePatch_title;
+ }
+
+ // parse the input
+ if (reader != null) {
+ try {
+ patcher.parse(new BufferedReader(reader));
+ //report back to the patch wizard that the patch has been read in
+ fPatchWizard.patchReadIn();
+ fPatchRead=true;
+ } catch (Exception ex) {
+ // Ignore. User will be informed of error since patcher contains no diffs
+ }
+ }
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException x) {
+ // silently ignored
+ }
+ }
+ }
+ }
+
+ /* (non-JavaDoc)
+ * Method declared in IWizardPage.
+ */
+ public boolean canFlipToNextPage() {
+ // we can't call getNextPage to determine if flipping is allowed since computing
+ // the next page is quite expensive. So we say yes if the page is complete.
+ return isPageComplete();
+ }
+
+ private void setEnablePatchFile(boolean enable) {
+ fPatchFileNameField.setEnabled(enable);
+ fPatchFileBrowseButton.setEnabled(enable);
+ }
+
+ private void setEnableWorkspacePatch(boolean enable) {
+ fWorkspaceSelectLabel.setEnabled(enable);
+ fTreeViewer.getTree().setEnabled(enable);
+ }
+
+ private void setEnableURLPatch(boolean enable) {
+ fPatchURLField.setEnabled(enable);
+ }
+
+ /*
+ * Create the group for selecting the patch file
+ */
+ private void buildPatchFileGroup(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));
+
+ // 1st row
+ GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ gd.horizontalSpan= 3;
+ fUseClipboardButton= new Button(composite, SWT.RADIO);
+ fUseClipboardButton.setText(PatchMessages.InputPatchPage_UseClipboardButton_text);
+ fUseClipboardButton.setLayoutData(gd);
+
+ // 2nd row
+ fUsePatchFileButton= new Button(composite, SWT.RADIO);
+ fUsePatchFileButton.setText(PatchMessages.InputPatchPage_FileButton_text);
+
+ fPatchFileNameField= new Combo(composite, SWT.BORDER);
+ gd= new GridData(GridData.FILL_HORIZONTAL);
+ gd.widthHint= SIZING_TEXT_FIELD_WIDTH;
+ fPatchFileNameField.setLayoutData(gd);
+
+ fPatchFileBrowseButton= new Button(composite, SWT.PUSH);
+ fPatchFileBrowseButton.setText(PatchMessages.InputPatchPage_ChooseFileButton_text);
+ GridData data= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ int widthHint= convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+ Point minSize= fPatchFileBrowseButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+ data.widthHint= Math.max(widthHint, minSize.x);
+ fPatchFileBrowseButton.setLayoutData(data);
+
+ //3rd row
+ fUseURLButton = new Button(composite, SWT.RADIO);
+ fUseURLButton.setText(PatchMessages.InputPatchPage_URLButton_text);
+
+ fPatchURLField = new Combo(composite, SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 2;
+ fPatchURLField.setLayoutData(gd);
+
+ //4th row
+ fUseWorkspaceButton= new Button(composite, SWT.RADIO);
+ fUseWorkspaceButton.setText(PatchMessages.InputPatchPage_UseWorkspaceButton_text);
+ gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ fUseWorkspaceButton.setLayoutData(gd);
+
+ addWorkspaceControls(parent);
+
+ // Add listeners
+ fUseClipboardButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if (!fUseClipboardButton.getSelection())
+ return;
+
+ clearErrorMessage();
+ fShowError= true;
+ int state= getInputMethod();
+ setEnablePatchFile(state == FILE);
+ setEnableURLPatch(state == URL);
+ setEnableWorkspacePatch(state == WORKSPACE);
+ updateWidgetEnablements();
+ fPatchRead = false;
+ }
+ });
+
+ fUsePatchFileButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if (!fUsePatchFileButton.getSelection())
+ return;
+ //If there is anything typed in at all
+ clearErrorMessage();
+ fShowError= (fPatchFileNameField.getText() != ""); //$NON-NLS-1$
+ int state= getInputMethod();
+ setEnablePatchFile(state == FILE);
+ setEnableURLPatch(state == URL);
+ setEnableWorkspacePatch(state == WORKSPACE);
+ updateWidgetEnablements();
+ fPatchRead = false;
+ }
+ });
+ fPatchFileNameField.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ updateWidgetEnablements();
+ }
+ });
+ fPatchFileNameField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ clearErrorMessage();
+ fShowError= true;
+ updateWidgetEnablements();
+ }
+ });
+ fPatchFileBrowseButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ clearErrorMessage();
+ fShowError= true;
+ handlePatchFileBrowseButtonPressed();
+ updateWidgetEnablements();
+ }
+ });
+ fUseURLButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ clearErrorMessage();
+ fShowError= (fPatchURLField.getText() != ""); //$NON-NLS-1$
+ int state= getInputMethod();
+ setEnablePatchFile(state == FILE);
+ setEnableURLPatch(state == URL);
+ setEnableWorkspacePatch(state == WORKSPACE);
+ updateWidgetEnablements();
+ }
+ });
+ fPatchURLField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ clearErrorMessage();
+ fShowError = true;
+ updateWidgetEnablements();
+ }
+ });
+ fUseWorkspaceButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if (!fUseWorkspaceButton.getSelection())
+ return;
+ clearErrorMessage();
+ // If there is anything typed in at all
+ fShowError= (!fTreeViewer.getSelection().isEmpty());
+ int state= getInputMethod();
+ setEnablePatchFile(state == FILE);
+ setEnableURLPatch(state == URL);
+ setEnableWorkspacePatch(state == WORKSPACE);
+ updateWidgetEnablements();
+ fPatchRead = false;
+ }
+ });
+
+ fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ clearErrorMessage();
+ updateWidgetEnablements();
+ }
+ });
+
+ fTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ ISelection selection= event.getSelection();
+ if (selection instanceof TreeSelection) {
+ TreeSelection treeSel= (TreeSelection) selection;
+ Object res= treeSel.getFirstElement();
+ if (res != null) {
+ if (res instanceof IProject || res instanceof IFolder) {
+ if (fTreeViewer.getExpandedState(res))
+ fTreeViewer.collapseToLevel(res, 1);
+ else
+ fTreeViewer.expandToLevel(res, 1);
+ } else if (res instanceof IFile)
+ fPatchWizard.showPage(getNextPage());
+ }
+ }
+ }
+ });
+ }
+
+ private void addWorkspaceControls(Composite composite) {
+
+ Composite newComp= new Composite(composite, SWT.NONE);
+ GridLayout layout= new GridLayout(1, false);
+ layout.marginLeft= 16; // align w/ lable of check button
+ newComp.setLayout(layout);
+ newComp.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ fWorkspaceSelectLabel= new Label(newComp, SWT.LEFT);
+ fWorkspaceSelectLabel.setText(PatchMessages.InputPatchPage_WorkspaceSelectPatch_text);
+
+ fTreeViewer= new TreeViewer(newComp, SWT.BORDER);
+ fTreeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ fTreeViewer.setLabelProvider(new WorkbenchLabelProvider());
+ fTreeViewer.setContentProvider(new WorkbenchContentProvider());
+ fTreeViewer.setComparator(new ResourceComparator(ResourceComparator.NAME));
+ fTreeViewer.setInput(ResourcesPlugin.getWorkspace().getRoot());
+ }
+
+
+ /**
+ * Updates the enable state of this page's controls.
+ */
+ private void updateWidgetEnablements() {
+
+ String error= null;
+
+ boolean gotPatch= false;
+ int inputMethod= getInputMethod();
+ if (inputMethod==CLIPBOARD) {
+ Control c= getControl();
+ if (c != null) {
+ Clipboard clipboard= new Clipboard(c.getDisplay());
+ Object o= clipboard.getContents(TextTransfer.getInstance());
+ clipboard.dispose();
+ if (o instanceof String) {
+ String s= ((String) o).trim();
+ if (s.length() > 0)
+ gotPatch= true;
+ else
+ error= PatchMessages.InputPatchPage_ClipboardIsEmpty_message;
+ } else
+ error= PatchMessages.InputPatchPage_NoTextInClipboard_message;
+ } else
+ error= PatchMessages.InputPatchPage_CouldNotReadClipboard_message;
+ } else if (inputMethod==FILE) {
+ String path= fPatchFileNameField.getText();
+ if (path != null && path.length() > 0) {
+ File file= new File(path);
+ gotPatch= file.exists() && file.isFile() && file.length() > 0;
+ if (!gotPatch)
+ error= PatchMessages.InputPatchPage_CannotLocatePatch_message + path;
+ } else {
+ error= PatchMessages.InputPatchPage_NoFileName_message;
+ }
+ } else if (inputMethod == URL) {
+ String urlText = fPatchURLField.getText();
+ if(urlText != null) {
+ try {
+ new URL(urlText);
+ // Checking the URL is a bit too heavy for each keystroke.
+ // Let's assume it contains a valid patch.
+ gotPatch = true;
+ } catch (MalformedURLException e) {
+ error= PatchMessages.InputPatchPage_MalformedURL;
+ }
+ } else {
+ error= PatchMessages.InputPatchPage_NoURL;
+ }
+ } else if (inputMethod == WORKSPACE) {
+ //Get the selected patch file (tree will only allow for one selection)
+ IResource[] resources= Utilities.getResources(fTreeViewer.getSelection());
+ if (resources != null && resources.length > 0) {
+ IResource patchFile= resources[0];
+ if (patchFile != null && patchFile.getType() == IResource.FILE) {
+ IPath location = patchFile.getLocation();
+ if (location == null) {
+ error = PatchMessages.InputPatchPage_PatchFileNotFound_message;
+ } else {
+ File actualFile= location.toFile();
+ gotPatch= actualFile.exists()&&actualFile.isFile()&&actualFile.length() > 0;
+ if (!gotPatch)
+ error= PatchMessages.InputPatchPage_FileSelectedNotPatch_message;
+ }
+ }
+ } else {
+ error= PatchMessages.InputPatchPage_NoFileName_message;
+ }
+ }
+
+ setPageComplete(gotPatch);
+
+ if (fShowError)
+ setErrorMessage(error);
+ }
+
+ protected void handlePatchFileBrowseButtonPressed() {
+ FileDialog dialog= new FileDialog(getShell(), SWT.NONE);
+ dialog.setText(PatchMessages.InputPatchPage_SelectPatchFileDialog_title);
+ String patchFilePath= getPatchFilePath();
+ if (patchFilePath != null) {
+ int lastSegment= patchFilePath.lastIndexOf(SEPARATOR);
+ if (lastSegment > 0) {
+ patchFilePath= patchFilePath.substring(0, lastSegment);
+ }
+ }
+ dialog.setFilterPath(patchFilePath);
+ String res= dialog.open();
+ if (res == null)
+ return;
+
+ patchFilePath= dialog.getFileName();
+ IPath filterPath= new Path(dialog.getFilterPath());
+ IPath path= filterPath.append(patchFilePath).makeAbsolute();
+ patchFilePath= path.toOSString();
+ //fDialogSettings.put(IUIConstants.DIALOGSTORE_LASTEXTJAR, filterPath.toOSString());
+
+ fPatchFileNameField.setText(patchFilePath);
+ //setSourceName(patchFilePath);
+ }
+
+ /**
+ * Sets the source name of the import to be the supplied path.
+ * Adds the name of the path to the list of items in the
+ * source combo and selects it.
+ *
+ * @param path the path to be added
+ */
+ protected void setSourceName(String path) {
+
+ if (path.length() > 0) {
+
+ String[] currentItems= fPatchFileNameField.getItems();
+ int selectionIndex= -1;
+ for (int i= 0; i < currentItems.length; i++)
+ if (currentItems[i].equals(path))
+ selectionIndex= i;
+
+ if (selectionIndex < 0) { // not found in history
+ int oldLength= currentItems.length;
+ String[] newItems= new String[oldLength + 1];
+ System.arraycopy(currentItems, 0, newItems, 0, oldLength);
+ newItems[oldLength]= path;
+ fPatchFileNameField.setItems(newItems);
+ selectionIndex= oldLength;
+ }
+ fPatchFileNameField.select(selectionIndex);
+
+ //resetSelection();
+ }
+ }
+
+ /**
+ * The Finish button was pressed. Try to do the required work now and answer
+ * a boolean indicating success. If false is returned then the wizard will
+ * not close.
+ *
+ * @return boolean
+ */
+ public boolean finish() {
+// if (!ensureSourceIsValid())
+// return false;
+
+ saveWidgetValues();
+
+// Iterator resourcesEnum= getSelectedResources().iterator();
+// List fileSystemObjects= new ArrayList();
+// while (resourcesEnum.hasNext()) {
+// fileSystemObjects.add(
+// ((FileSystemElement) resourcesEnum.next()).getFileSystemObject());
+// }
+//
+// if (fileSystemObjects.size() > 0)
+// return importResources(fileSystemObjects);
+//
+// MessageDialog
+// .openInformation(
+// getContainer().getShell(),
+// DataTransferMessages.getString("DataTransfer.information"), //$NON-NLS-1$
+// DataTransferMessages.getString("FileImport.noneSelected")); //$NON-NLS-1$
+//
+// return false;
+
+ return true;
+ }
+
+ /**
+ * Use the dialog store to restore widget values to the values that they held
+ * last time this wizard was used to completion
+ */
+ private void restoreWidgetValues() {
+
+ int inputMethod= FILE;
+
+ IDialogSettings settings= getDialogSettings();
+ if (settings != null) {
+
+ try {
+ inputMethod= settings.getInt(STORE_INPUT_METHOD_ID);
+ } catch (NumberFormatException ex) {
+ //OK - no value stored in settings; just use CLIPBOARD
+ }
+
+ // set filenames history
+ String[] sourceNames= settings.getArray(STORE_PATCH_FILES_ID);
+ if (sourceNames != null)
+ for (int i= 0; i < sourceNames.length; i++)
+ if (sourceNames[i] != null && sourceNames[i].length() > 0)
+ fPatchFileNameField.add(sourceNames[i]);
+
+ // set patch file path
+ String patchFilePath= settings.get(STORE_PATCH_FILES_ID);
+ if (patchFilePath != null)
+ setSourceName(patchFilePath);
+
+ // set URLs history
+ String[] sourceURLs= settings.getArray(STORE_PATCH_URLS_ID);
+ if (sourceURLs != null)
+ for (int i= 0; i < sourceURLs.length; i++)
+ if (sourceURLs[i] != null && sourceURLs[i].length() > 0)
+ fPatchURLField.add(sourceURLs[i]);
+
+ // If the previous apply patch was used with a clipboard, we need to check
+ // if there is a valid patch on the clipboard. This will be done in adjustToCurrentTarget()
+ // so just set it to FILE now and, if there exists a patch on the clipboard, then clipboard
+ // will be selected automatically
+ if (inputMethod == CLIPBOARD){
+ inputMethod= FILE;
+ fPatchFileNameField.deselectAll();
+ }
+
+ //set the workspace patch selection
+ String workspaceSetting= settings.get(STORE_WORKSPACE_PATH_ID);
+ if (workspaceSetting != null && workspaceSetting.length() > 0) {
+ // See if this resource still exists in the workspace
+ try {
+ IPath path= new Path(workspaceSetting);
+ IFile targetFile= ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+ if (fTreeViewer != null && targetFile.exists()){
+ fTreeViewer.expandToLevel(targetFile, 0);
+ fTreeViewer.setSelection(new StructuredSelection(targetFile));
+ }
+ } catch (RuntimeException e) {
+ // Ignore. The setting was invalid
+ }
+ } else {
+ //check to see if the current input is set to workspace - if it is switch it
+ //back to clipboard since there is no corresponding element to go along with
+ //the tree viewer
+ if (inputMethod == WORKSPACE)
+ inputMethod= FILE;
+ }
+ }
+
+ // set radio buttons state
+ setInputButtonState(inputMethod);
+ }
+
+ /**
+ * Since Finish was pressed, write widget values to the dialog store so that they
+ * will persist into the next invocation of this wizard page
+ */
+ void saveWidgetValues() {
+ IDialogSettings settings= getDialogSettings();
+ if (settings != null) {
+
+ settings.put(STORE_INPUT_METHOD_ID, getInputMethod());
+ settings.put(STORE_PATCH_FILES_ID, getPatchFilePath());
+
+ // update source names history
+ String[] sourceNames= settings.getArray(STORE_PATCH_FILES_ID);
+ if (sourceNames == null)
+ sourceNames= new String[0];
+
+ sourceNames= addToHistory(sourceNames, getPatchFilePath());
+ settings.put(STORE_PATCH_FILES_ID, sourceNames);
+
+ // update source URLs history
+ String[] sourceURLs= settings.getArray(STORE_PATCH_URLS_ID);
+ if (sourceURLs == null)
+ sourceURLs= new String[0];
+
+ sourceURLs= addToHistory(sourceURLs, fPatchURLField.getText());
+ settings.put(STORE_PATCH_URLS_ID, sourceURLs);
+
+ // save the workspace selection
+ settings.put(STORE_WORKSPACE_PATH_ID, getWorkspacePath());
+
+ }
+ }
+
+ private String getWorkspacePath() {
+ if (fTreeViewer != null){
+ IResource[] resources= Utilities.getResources(fTreeViewer.getSelection());
+ if (resources.length > 0) {
+ IResource patchFile= resources[0];
+ return patchFile.getFullPath().toString();
+ }
+
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ // static helpers
+
+ /**
+ * Checks to see if the file that has been selected for Apply Patch is
+ * actually a patch
+ *
+ * @return true if the file selected to run Apply Patch on in the workspace
+ * is a patch file or if the clipboard contains a patch or if the
+ * clipboard contains an URL (we assume it points to a patch )
+ */
+ private boolean adjustToCurrentTarget() {
+ // readjust selection if there is a patch selected in the workspace or on the clipboard
+ // check workspace first
+ IResource patchTarget= fPatchWizard.getTarget();
+ if (patchTarget instanceof IFile) {
+ Reader reader= null;
+ try {
+ try {
+ reader= new FileReader(patchTarget.getLocation().toFile());
+ if (isPatchFile(reader)) {
+ // set choice to workspace
+ setInputButtonState(WORKSPACE);
+ if (fTreeViewer != null && patchTarget.exists()) {
+ fTreeViewer.expandToLevel(patchTarget, 0);
+ fTreeViewer.setSelection(new StructuredSelection(patchTarget));
+ }
+ return true;
+ }
+ } catch (FileNotFoundException ex) {
+ // silently ignored
+ } catch (NullPointerException nex) {
+ // silently ignored
+ }
+
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException x) {
+ // silently ignored
+ }
+ }
+ }
+ }
+ // check out clipboard contents
+ Reader reader = null;
+ Control c = getControl();
+ if (c != null) {
+ Clipboard clipboard= new Clipboard(c.getDisplay());
+ Object o= clipboard.getContents(TextTransfer.getInstance());
+ clipboard.dispose();
+ try {
+ if (o instanceof String) {
+ reader= new StringReader((String) o);
+ if (isPatchFile(reader)) {
+ setInputButtonState(CLIPBOARD);
+ return true;
+ }
+ // maybe it's an URL
+ try {
+ URL url = new URL((String)o);
+ if(url != null) {
+ setInputButtonState(URL);
+ fPatchURLField.setText((String)o);
+ return true;
+ }
+ } catch (MalformedURLException e) {
+ // ignore
+ }
+ }
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException x) {
+ // silently ignored
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isPatchFile(Reader reader) {
+ WorkspacePatcher patcher= ((PatchWizard) getWizard()).getPatcher();
+
+ try {
+ patcher.parse(new BufferedReader(reader));
+ } catch (Exception ex) {
+ return false;
+ }
+
+ FilePatch2[] diffs= patcher.getDiffs();
+ if (diffs == null || diffs.length == 0)
+ return false;
+ return true;
+ }
+
+ /*
+ * Clears the dialog message box
+ */
+ private void clearErrorMessage(){
+ setErrorMessage(null);
+ }
+
+ private void setInputButtonState(int state) {
+
+ switch (state) {
+ case CLIPBOARD:
+ fUseClipboardButton.setSelection(true);
+ fUsePatchFileButton.setSelection(false);
+ fUseURLButton.setSelection(false);
+ fUseWorkspaceButton.setSelection(false);
+ break;
+
+ case FILE:
+ fUseClipboardButton.setSelection(false);
+ fUsePatchFileButton.setSelection(true);
+ fUseURLButton.setSelection(false);
+ fUseWorkspaceButton.setSelection(false);
+ break;
+
+ case URL:
+ fUseClipboardButton.setSelection(false);
+ fUsePatchFileButton.setSelection(false);
+ fUseURLButton.setSelection(true);
+ fUseWorkspaceButton.setSelection(false);
+ break;
+
+ case WORKSPACE:
+ fUseClipboardButton.setSelection(false);
+ fUsePatchFileButton.setSelection(false);
+ fUseURLButton.setSelection(false);
+ fUseWorkspaceButton.setSelection(true);
+ break;
+ }
+
+ setEnablePatchFile(state == FILE);
+ setEnableWorkspacePatch(state == WORKSPACE);
+ setEnableURLPatch(state == URL);
+ }
+
+ protected int getInputMethod() {
+ if (fUseClipboardButton.getSelection())
+ return CLIPBOARD;
+ if (fUsePatchFileButton.getSelection())
+ return FILE;
+ if(fUseURLButton.getSelection())
+ return URL;
+ return WORKSPACE;
+ }
+
+ private String getPatchFilePath() {
+ if (fPatchFileNameField != null)
+ return fPatchFileNameField.getText();
+ return ""; //$NON-NLS-1$
+ }
+
+ /*
+ * Adds an entry to a history, while taking care of duplicate history items
+ * and excessively long histories. The assumption is made that all histories
+ * should be of length <code>COMBO_HISTORY_LENGTH</code>.
+ *
+ * @param history the current history
+ * @param newEntry the entry to add to the history
+ */
+ protected static String[] addToHistory(String[] history, String newEntry) {
+ java.util.ArrayList l= new java.util.ArrayList(java.util.Arrays.asList(history));
+
+ l.remove(newEntry);
+ l.add(0,newEntry);
+
+ // since only one new item was added, we can be over the limit
+ // by at most one item
+ if (l.size() > COMBO_HISTORY_LENGTH)
+ l.remove(COMBO_HISTORY_LENGTH);
+
+ return (String[]) l.toArray(new String[l.size()]);
+ }
+
+ public boolean isPatchRead() {
+ return fPatchRead;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/LineReader.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/LineReader.java
new file mode 100644
index 000000000..735b1c3d3
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/LineReader.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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
+ * Brock Janiczak <brockj@tpg.com.au> - Bug 181919 LineReader creating unneeded garbage
+ *******************************************************************************/
+package org.eclipse.compare.internal.patch;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Platform;
+
+public class LineReader {
+
+ /*
+ * Reads the contents from the given file and returns them as a List of
+ * lines.
+ */
+ public static List load(IStorage file, boolean create) {
+ List lines = null;
+ if (!create && file != null && exists(file)) {
+ // read current contents
+ String charset = Utilities.getCharset(file);
+ InputStream is = null;
+ try {
+ is = file.getContents();
+
+ Reader streamReader = null;
+ try {
+ streamReader = new InputStreamReader(is, charset);
+ } catch (UnsupportedEncodingException x) {
+ // use default encoding
+ streamReader = new InputStreamReader(is);
+ }
+
+ BufferedReader reader = new BufferedReader(streamReader);
+ lines = readLines(reader);
+ } catch (CoreException ex) {
+ // TODO
+ CompareUIPlugin.log(ex);
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException ex) {
+ // silently ignored
+ }
+ }
+ }
+
+ if (lines == null)
+ lines = new ArrayList();
+ return lines;
+ }
+
+ private static boolean exists(IStorage file) {
+ if (file instanceof IFile) {
+ return ((IFile) file).exists();
+ }
+ return true;
+ }
+
+ public static List readLines(BufferedReader reader) {
+ List lines;
+ LineReader lr = new LineReader(reader);
+ if (!Platform.WS_CARBON.equals(Platform.getWS()))
+ // Don't treat single CRs as line feeds to be consistent with command line patch
+ lr.ignoreSingleCR();
+ lines = lr.readLines();
+ return lines;
+ }
+
+ /*
+ * Concatenates all strings found in the given List.
+ */
+ public static String createString(boolean preserveLineDelimeters, List lines) {
+ StringBuffer sb = new StringBuffer();
+ Iterator iter = lines.iterator();
+ if (preserveLineDelimeters) {
+ while (iter.hasNext())
+ sb.append((String) iter.next());
+ } else {
+ String lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$
+ while (iter.hasNext()) {
+ String line = (String) iter.next();
+ int l = length(line);
+ if (l < line.length()) { // line has delimiter
+ sb.append(line.substring(0, l));
+ sb.append(lineSeparator);
+ } else {
+ sb.append(line);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /*
+ * Returns the length (excluding a line delimiter CR, LF, CR/LF) of the
+ * given string.
+ */
+ /* package */static int length(String s) {
+ int l = s.length();
+ if (l > 0) {
+ char c = s.charAt(l - 1);
+ if (c == '\r')
+ return l - 1;
+ if (c == '\n') {
+ if (l > 1 && s.charAt(l - 2) == '\r')
+ return l - 2;
+ return l - 1;
+ }
+ }
+ return l;
+ }
+
+ private boolean fHaveChar = false;
+ private int fLastChar;
+ private boolean fSawEOF = false;
+ private BufferedReader fReader;
+ private boolean fIgnoreSingleCR = false;
+ private StringBuffer fBuffer = new StringBuffer();
+
+ public LineReader(BufferedReader reader) {
+ fReader = reader;
+ Assert.isNotNull(reader);
+ }
+
+ public void ignoreSingleCR() {
+ fIgnoreSingleCR = true;
+ }
+
+ /**
+ * Reads a line of text. A line is considered to be terminated by any one of
+ * a line feed ('\n'), a carriage return ('\r'), or a carriage return
+ * followed immediately by a line-feed.
+ *
+ * @return A string containing the contents of the line including the
+ * line-termination characters, or <code>null</code> if the end of
+ * the stream has been reached
+ * @exception IOException
+ * If an I/O error occurs
+ */
+ /* package */String readLine() throws IOException {
+ try {
+ while (!fSawEOF) {
+ int c = readChar();
+ if (c == -1) {
+ fSawEOF = true;
+ break;
+ }
+ fBuffer.append((char) c);
+ if (c == '\n')
+ break;
+ if (c == '\r') {
+ c = readChar();
+ if (c == -1) {
+ fSawEOF = true;
+ break; // EOF
+ }
+ if (c != '\n') {
+ if (fIgnoreSingleCR) {
+ fBuffer.append((char) c);
+ continue;
+ }
+ fHaveChar = true;
+ fLastChar = c;
+ } else
+ fBuffer.append((char) c);
+ break;
+ }
+ }
+
+ if (fBuffer.length() != 0) {
+ return fBuffer.toString();
+ }
+ return null;
+ } finally {
+ fBuffer.setLength(0);
+ }
+ }
+
+ /* package */void close() {
+ try {
+ fReader.close();
+ } catch (IOException ex) {
+ // silently ignored
+ }
+ }
+
+ public List readLines() {
+ try {
+ List lines = new ArrayList();
+ String line;
+ while ((line = readLine()) != null)
+ lines.add(line);
+ return lines;
+ } catch (IOException ex) {
+ // NeedWork
+ // System.out.println("error while reading file: " + fileName + "("
+ // + ex + ")");
+ } finally {
+ close();
+ }
+ return null;
+ }
+
+ /*
+ * Returns the number of characters in the given string without counting a
+ * trailing line separator.
+ */
+ /* package */int lineContentLength(String line) {
+ if (line == null)
+ return 0;
+ int length = line.length();
+ for (int i = length - 1; i >= 0; i--) {
+ char c = line.charAt(i);
+ if (c == '\n' || c == '\r')
+ length--;
+ else
+ break;
+ }
+ return length;
+ }
+
+ // ---- private
+
+ private int readChar() throws IOException {
+ if (fHaveChar) {
+ fHaveChar = false;
+ return fLastChar;
+ }
+ return fReader.read();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchCompareEditorInput.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchCompareEditorInput.java
new file mode 100644
index 000000000..c8bc3da77
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchCompareEditorInput.java
@@ -0,0 +1,433 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 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.patch;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.CompareViewerPane;
+import org.eclipse.compare.IContentChangeListener;
+import org.eclipse.compare.IContentChangeNotifier;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.ICompareUIConstants;
+import org.eclipse.compare.internal.core.patch.DiffProject;
+import org.eclipse.compare.internal.core.patch.FileDiffResult;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.internal.core.patch.HunkResult;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.DiffTreeViewer;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.viewers.IDecoration;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class PatchCompareEditorInput extends CompareEditorInput {
+
+ private static final String IMAGE_CACHE_KEY = "IMAGE_CACHE"; //$NON-NLS-1$
+
+ public static ImageDescriptor createOverlay(Image baseImage, ImageDescriptor overlayImage, int quadrant) {
+ return new DecoratorOverlayIcon(baseImage, createArrayFrom(overlayImage, quadrant), new Point(Math.max(baseImage.getBounds().width, 16), Math.max(baseImage.getBounds().height, 16)));
+ }
+
+ private static ImageDescriptor[] createArrayFrom(
+ ImageDescriptor overlayImage, int quadrant) {
+ ImageDescriptor[] descs = new ImageDescriptor[] { null, null, null, null, null };
+ descs[quadrant] = overlayImage;
+ return descs;
+ }
+
+ class PatcherCompareEditorLabelProvider extends LabelProvider {
+ private ILabelProvider wrappedProvider;
+
+ public PatcherCompareEditorLabelProvider(ILabelProvider labelProvider) {
+ wrappedProvider = labelProvider;
+ }
+
+ public String getText(Object element) {
+ String text = wrappedProvider.getText(element);
+ if (element instanceof PatchDiffNode){
+ PatchDiffNode node = (PatchDiffNode) element;
+ if (node instanceof PatchProjectDiffNode) {
+ PatchProjectDiffNode projectNode = (PatchProjectDiffNode) node;
+ if (!Utilities.getProject(projectNode.getDiffProject()).exists()) {
+ text = NLS.bind(PatchMessages.Diff_2Args, new String[]{text, PatchMessages.PreviewPatchLabelDecorator_ProjectDoesNotExist});
+ }
+ }
+ if (!node.isEnabled()) {
+ return NLS.bind(PatchMessages.Diff_2Args,
+ new String[]{text, PatchMessages.PatcherCompareEditorInput_NotIncluded});
+ }
+ if (node instanceof PatchFileDiffNode) {
+ PatchFileDiffNode fileNode = (PatchFileDiffNode) node;
+ if (getPatcher().hasCachedContents(fileNode.getDiffResult().getDiff())) {
+ text = NLS.bind(PatchMessages.Diff_2Args, new String[] {text, PatchMessages.HunkMergePage_Merged});
+ }
+ if (!fileNode.fileExists()) {
+ text = NLS.bind(PatchMessages.Diff_2Args, new String[] {text, PatchMessages.PatchCompareEditorInput_0});
+ }
+ }
+ if (node instanceof HunkDiffNode) {
+ HunkDiffNode hunkNode = (HunkDiffNode) node;
+ if (hunkNode.isManuallyMerged()) {
+ text = NLS.bind(PatchMessages.Diff_2Args, new String[] {text, PatchMessages.HunkMergePage_Merged});
+ }
+ if (hunkNode.isFuzzUsed()) {
+ text = NLS.bind(PatchMessages.Diff_2Args,
+ new String[] { text,
+ NLS.bind(hunkNode.isAllContextIgnored() ? PatchMessages.PreviewPatchPage_AllContextIgnored : PatchMessages.PreviewPatchPage_FuzzUsed,
+ new String[] { hunkNode.getHunkResult().getFuzz() + ""}) }); //$NON-NLS-1$
+ }
+ }
+ if (getPatcher().isRetargeted(node.getPatchElement()))
+ return NLS.bind(PatchMessages.Diff_2Args,
+ new String[]{getPatcher().getOriginalPath(node.getPatchElement()).toString(),
+ NLS.bind(PatchMessages.PreviewPatchPage_Target, new String[]{node.getName()})});
+ }
+ return text;
+ }
+
+ public Image getImage(Object element) {
+ Image image = wrappedProvider.getImage(element);
+ if (element instanceof PatchDiffNode){
+ PatchDiffNode node = (PatchDiffNode) element;
+ if (!node.isEnabled() && image != null) {
+ LocalResourceManager imageCache = PatchCompareEditorInput.getImageCache(getPatcher().getConfiguration());
+ return imageCache.createImage(createOverlay(image, CompareUIPlugin.getImageDescriptor(ICompareUIConstants.REMOVED_OVERLAY), IDecoration.TOP_LEFT));
+ }
+ }
+ if (element instanceof HunkDiffNode) {
+ HunkDiffNode node = (HunkDiffNode) element;
+ if (node.isManuallyMerged()) {
+ LocalResourceManager imageCache = PatchCompareEditorInput.getImageCache(getPatcher().getConfiguration());
+ return imageCache.createImage(PatchCompareEditorInput.createOverlay(image, CompareUIPlugin.getImageDescriptor(ICompareUIConstants.IS_MERGED_OVERLAY), IDecoration.TOP_LEFT));
+ }
+ }
+ return image;
+ }
+ }
+
+ private final DiffNode root;
+ private final WorkspacePatcher patcher;
+ private TreeViewer viewer;
+ private boolean fShowAll;
+ private boolean showMatched = false;
+
+ /**
+ * Creates a new PatchCompareEditorInput and makes use of the passed in CompareConfiguration
+ * to configure the UI elements.
+ * @param patcher
+ * @param configuration
+ */
+ public PatchCompareEditorInput(WorkspacePatcher patcher, CompareConfiguration configuration) {
+ super(configuration);
+ Assert.isNotNull(patcher);
+ this.patcher = patcher;
+ root = new DiffNode(Differencer.NO_CHANGE) {
+ public boolean hasChildren() {
+ return true;
+ }
+ };
+ initializeImageCache();
+ }
+
+ private void initializeImageCache() {
+ initializeImageCache(patcher.getConfiguration());
+ }
+
+ private static LocalResourceManager initializeImageCache(PatchConfiguration patchConfiguration) {
+ LocalResourceManager imageCache = new LocalResourceManager(JFaceResources.getResources());
+ patchConfiguration.setProperty(IMAGE_CACHE_KEY, imageCache);
+ return imageCache;
+ }
+
+ protected void handleDispose() {
+ super.handleDispose();
+ getImageCache(getPatcher().getConfiguration()).dispose();
+ }
+
+ public static LocalResourceManager getImageCache(PatchConfiguration patchConfiguration) {
+ LocalResourceManager cache = (LocalResourceManager)patchConfiguration.getProperty(IMAGE_CACHE_KEY);
+ if (cache == null) {
+ return initializeImageCache(patchConfiguration);
+ }
+ return cache;
+ }
+
+ protected Object prepareInput(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ initLabels();
+ return root;
+ }
+
+ private void initLabels() {
+ CompareConfiguration cc = getCompareConfiguration();
+ // set left editable so that unmatched hunks can be edited
+ cc.setLeftEditable(true);
+ cc.setRightEditable(false);
+ //cc.setProperty(CompareEditor.CONFIRM_SAVE_PROPERTY, new Boolean(false));
+ cc.setLeftLabel(getCompareConfiguration().getLeftLabel(root));
+ cc.setLeftImage(getCompareConfiguration().getLeftImage(root));
+ cc.setRightLabel(getCompareConfiguration().getRightLabel(root));
+ cc.setRightImage(getCompareConfiguration().getRightImage(root));
+ }
+
+ /**
+ * Update the presentation of the diff tree.
+ */
+ protected void updateTree() {
+ if (getViewer() != null && !getViewer().getControl().isDisposed())
+ getViewer().refresh(true);
+ }
+
+ /**
+ * Build the diff tree.
+ */
+ protected void buildTree(){
+
+ // Reset the input node so it is empty
+ if (getRoot().hasChildren()) {
+ resetRoot();
+ }
+ // Reset the input of the viewer so the old state is no longer used
+ getViewer().setInput(getRoot());
+
+ // Refresh the patcher state
+ getPatcher().refresh();
+
+ // Build the diff tree
+ if (getPatcher().isWorkspacePatch()){
+ processProjects(getPatcher().getDiffProjects());
+ } else {
+ processDiffs(getPatcher().getDiffs());
+ }
+
+ // Refresh the viewer
+ getViewer().refresh();
+ }
+
+ private void processDiffs(FilePatch2[] diffs) {
+ for (int i = 0; i < diffs.length; i++) {
+ processDiff(diffs[i], getRoot());
+ }
+ }
+
+ private void processProjects(DiffProject[] diffProjects) {
+ //create diffProject nodes
+ for (int i = 0; i < diffProjects.length; i++) {
+ PatchProjectDiffNode projectNode = new PatchProjectDiffNode(getRoot(), diffProjects[i], getPatcher().getConfiguration());
+ FilePatch2[] diffs = diffProjects[i].getFileDiffs();
+ for (int j = 0; j < diffs.length; j++) {
+ FilePatch2 fileDiff = diffs[j];
+ processDiff(fileDiff, projectNode);
+ }
+ }
+ }
+
+ private void processDiff(FilePatch2 diff, DiffNode parent) {
+ FileDiffResult diffResult = getPatcher().getDiffResult(diff);
+ PatchFileDiffNode node = PatchFileDiffNode.createDiffNode(parent, diffResult);
+ HunkResult[] hunkResults = diffResult.getHunkResults();
+ for (int i = 0; i < hunkResults.length; i++) {
+ HunkResult hunkResult = hunkResults[i];
+ if (!hunkResult.isOK()) {
+ HunkDiffNode hunkNode = HunkDiffNode.createDiffNode(node, hunkResult, true);
+ Object left = hunkNode.getLeft();
+ if (left instanceof UnmatchedHunkTypedElement) {
+ UnmatchedHunkTypedElement element = (UnmatchedHunkTypedElement) left;
+ element.addContentChangeListener(new IContentChangeListener() {
+ public void contentChanged(IContentChangeNotifier source) {
+ if (getViewer() == null || getViewer().getControl().isDisposed())
+ return;
+ getViewer().refresh(true);
+ }
+ });
+ }
+ } else if (showMatched) {
+ HunkDiffNode.createDiffNode(node, hunkResult, false, true, false);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#createDiffViewer(org.eclipse.swt.widgets.Composite)
+ */
+ public Viewer createDiffViewer(Composite parent) {
+ viewer = new DiffTreeViewer(parent, getCompareConfiguration()){
+ protected void fillContextMenu(IMenuManager manager) {
+ PatchCompareEditorInput.this.fillContextMenu(manager);
+ }
+ };
+
+ viewer.setLabelProvider(new PatcherCompareEditorLabelProvider((ILabelProvider)viewer.getLabelProvider()));
+ viewer.getTree().setData(CompareUI.COMPARE_VIEWER_TITLE, PatchMessages.PatcherCompareEditorInput_PatchContents);
+ viewer.addOpenListener(new IOpenListener() {
+ public void open(OpenEvent event) {
+ IStructuredSelection sel= (IStructuredSelection) event.getSelection();
+ Object obj= sel.getFirstElement();
+ if (obj instanceof HunkDiffNode) {
+ if (((HunkDiffNode) obj).getHunkResult().isOK()) {
+ getCompareConfiguration().setLeftLabel(PatchMessages.PatcherCompareEditorInput_LocalCopy);
+ getCompareConfiguration().setRightLabel(PatchMessages.PreviewPatchPage2_MatchedHunk);
+ } else {
+ getCompareConfiguration().setLeftLabel(PatchMessages.PreviewPatchPage2_PatchedLocalFile);
+ getCompareConfiguration().setRightLabel(PatchMessages.PreviewPatchPage2_OrphanedHunk);
+ }
+ } else {
+ getCompareConfiguration().setLeftLabel(PatchMessages.PatcherCompareEditorInput_LocalCopy);
+ getCompareConfiguration().setRightLabel(PatchMessages.PatcherCompareEditorInput_AfterPatch);
+ }
+ }
+
+ });
+ viewer.setFilters(getFilters());
+ viewer.setInput(root);
+ return viewer;
+ }
+
+ private ViewerFilter[] getFilters() {
+ return new ViewerFilter[] { new ViewerFilter() {
+ public boolean select(Viewer v, Object parentElement, Object element) {
+ if (element instanceof PatchDiffNode) {
+ PatchDiffNode node = (PatchDiffNode) element;
+ return node.isEnabled() || isShowAll();
+ }
+ return false;
+ }
+ } };
+ }
+
+ protected boolean isShowAll() {
+ return fShowAll;
+ }
+
+ protected void setShowAll(boolean show) {
+ fShowAll = show;
+ }
+
+ public boolean isShowMatched() {
+ return showMatched;
+ }
+
+ protected void setShowMatched(boolean show) {
+ showMatched = show;
+ }
+
+ public void contributeDiffViewerToolbarItems(Action[] actions, boolean workspacePatch){
+ ToolBarManager tbm= CompareViewerPane.getToolBarManager(viewer.getControl().getParent());
+ if (tbm != null) {
+ tbm.removeAll();
+
+ tbm.add(new Separator("contributed")); //$NON-NLS-1$
+
+ for (int i = 0; i < actions.length; i++) {
+ tbm.appendToGroup("contributed", actions[i]); //$NON-NLS-1$
+ }
+
+ tbm.update(true);
+ }
+ }
+
+ public TreeViewer getViewer() {
+ return viewer;
+ }
+
+ public DiffNode getRoot() {
+ return root;
+ }
+
+ public void resetRoot() {
+ IDiffElement[] children = root.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement child = children[i];
+ root.remove(child);
+ }
+ }
+
+ public WorkspacePatcher getPatcher() {
+ return patcher;
+ }
+
+ public boolean confirmRebuild(String message) {
+ if (getPatcher().hasCachedContents()) {
+ if (promptToDiscardCachedChanges(message)) {
+ getPatcher().clearCachedContents();
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private boolean promptToDiscardCachedChanges(String message) {
+ return MessageDialog.openConfirm(viewer.getControl().getShell(), PatchMessages.PatcherCompareEditorInput_0, message);
+ }
+
+ /**
+ * Return whether this input has a result to apply. The input
+ * has a result to apply if at least one hunk is selected for inclusion.
+ * @return whether this input has a result to apply
+ */
+ public boolean hasResultToApply() {
+ boolean atLeastOneIsEnabled = false;
+ if (getViewer() != null) {
+ IDiffElement[] elements = getRoot().getChildren();
+ for (int i = 0; i < elements.length; i++) {
+ IDiffElement element = elements[i];
+ if (isEnabled(element)) {
+ atLeastOneIsEnabled = true;
+ break;
+ }
+ }
+ }
+ return atLeastOneIsEnabled;
+ }
+
+ private boolean isEnabled(IDiffElement element) {
+ if (element instanceof PatchDiffNode) {
+ PatchDiffNode node = (PatchDiffNode) element;
+ return node.isEnabled();
+ }
+ return false;
+ }
+
+ protected abstract void fillContextMenu(IMenuManager manager);
+
+ public Viewer findStructureViewer(Viewer oldViewer, ICompareInput input,
+ Composite parent) {
+ if (org.eclipse.compare.internal.Utilities.isHunk(input))
+ return null;
+ return super.findStructureViewer(oldViewer, input, parent);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchDiffNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchDiffNode.java
new file mode 100644
index 000000000..351af89d3
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchDiffNode.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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.patch;
+
+import org.eclipse.compare.IResourceProvider;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.IDiffContainer;
+import org.eclipse.core.resources.IResource;
+
+public abstract class PatchDiffNode extends DiffNode implements IResourceProvider {
+
+ private Object fElement;
+
+ public PatchDiffNode(Object patchElement, IDiffContainer parent, int kind,
+ ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ super(parent, kind, ancestor, left, right);
+ fElement = patchElement;
+ }
+
+ public PatchDiffNode(Object patchElement, IDiffContainer parent, int kind) {
+ super(parent, kind);
+ fElement = patchElement;
+ }
+
+ public boolean isEnabled() {
+ return getPatcher().isEnabled(getPatchElement());
+ }
+
+ public void setEnabled(boolean enabled) {
+ getPatcher().setEnabled(getPatchElement(), enabled);
+ }
+
+ protected final Patcher getPatcher() {
+ return Patcher.getPatcher(getConfiguration());
+ }
+
+ public Object getPatchElement() {
+ return fElement;
+ }
+
+ protected abstract PatchConfiguration getConfiguration();
+
+ public boolean equals(Object other) {
+ if (other instanceof PatchDiffNode) {
+ PatchDiffNode node = (PatchDiffNode) other;
+ return (node.getPatchElement().equals(getPatchElement()));
+ }
+ return super.equals(other);
+ }
+
+ public int hashCode() {
+ return getPatchElement().hashCode();
+ }
+
+ public IResource getResource() {
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchErrorDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchErrorDialog.java
new file mode 100644
index 000000000..e0a60b348
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchErrorDialog.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.patch;
+
+public class PatchErrorDialog {
+
+ private PatchErrorDialog() {
+ // no instance.
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileDiffNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileDiffNode.java
new file mode 100644
index 000000000..949b1cd02
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileDiffNode.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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.patch;
+
+import org.eclipse.compare.IContentChangeListener;
+import org.eclipse.compare.IContentChangeNotifier;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.internal.core.patch.FileDiffResult;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.IDiffContainer;
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.resources.IResource;
+
+public class PatchFileDiffNode extends PatchDiffNode implements IContentChangeListener {
+
+ private final FileDiffResult result;
+
+ public static PatchFileDiffNode createDiffNode(DiffNode parent, FileDiffResult result) {
+ return new PatchFileDiffNode(result, parent, getKind(result), getAncestorElement(result), getLeftElement(result), getRightElement(result));
+ }
+
+ public static int getKind(FileDiffResult result) {
+ if (!result.hasMatches())
+ return Differencer.NO_CHANGE;
+ int fileDiffKind = result.getDiff().getDiffType(result.getConfiguration().isReversed());
+ int kind = convertFileDiffTypeToDifferencerType(fileDiffKind);
+ return kind | Differencer.RIGHT;
+ }
+
+ private static int convertFileDiffTypeToDifferencerType(int fileDiffKind) {
+ int kind;
+ switch (fileDiffKind) {
+ case FilePatch2.ADDITION:
+ kind = Differencer.ADDITION;
+ break;
+ case FilePatch2.DELETION:
+ kind = Differencer.DELETION;
+ break;
+ case FilePatch2.CHANGE:
+ kind = Differencer.CHANGE;
+ break;
+ default:
+ kind = Differencer.CHANGE;
+ break;
+ }
+ return kind;
+ }
+
+ public static ITypedElement getRightElement(FileDiffResult result) {
+ return new PatchFileTypedElement(result, true);
+ }
+
+ private static ITypedElement getLeftElement(FileDiffResult result) {
+ return new PatchFileTypedElement(result, false);
+ }
+
+ public static ITypedElement getAncestorElement(FileDiffResult result) {
+ return new PatchFileTypedElement(result, false);
+ }
+
+ public PatchFileDiffNode(FileDiffResult result, IDiffContainer parent, int kind,
+ ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ super(result.getDiff(), parent, kind, ancestor, left, right);
+ this.result = result;
+ }
+
+ public FileDiffResult getDiffResult() {
+ return result;
+ }
+
+ protected PatchConfiguration getConfiguration() {
+ return result.getConfiguration();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.DiffContainer#add(org.eclipse.compare.structuremergeviewer.IDiffElement)
+ */
+ public void add(IDiffElement diff) {
+ super.add(diff);
+ // Listen for content changes in unmatched children so we can fire an input change
+ if (diff instanceof HunkDiffNode) {
+ HunkDiffNode node = (HunkDiffNode) diff;
+ Object left = node.getLeft();
+ if (left instanceof IContentChangeNotifier) {
+ IContentChangeNotifier notifier = (IContentChangeNotifier) left;
+ notifier.addContentChangeListener(this);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IContentChangeListener#contentChanged(org.eclipse.compare.IContentChangeNotifier)
+ */
+ public void contentChanged(IContentChangeNotifier source) {
+ fireChange();
+ }
+
+ public int getKind() {
+ int kind = super.getKind();
+ if (kind == Differencer.NO_CHANGE && getPatcher().hasCachedContents(getDiffResult().getDiff())) {
+ return Differencer.CHANGE | Differencer.RIGHT;
+ }
+ return kind;
+ }
+
+ public boolean fileExists() {
+ IResource file = getResource();
+ return file != null && file.isAccessible();
+ }
+
+ public IResource getResource() {
+ return ((WorkspaceFileDiffResult)getDiffResult()).getTargetFile();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileTypedElement.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileTypedElement.java
new file mode 100644
index 000000000..86c5fff2d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchFileTypedElement.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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.patch;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.IEncodedStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.core.patch.DiffProject;
+import org.eclipse.compare.internal.core.patch.FileDiffResult;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.swt.graphics.Image;
+
+public class PatchFileTypedElement implements ITypedElement,
+ IEncodedStreamContentAccessor {
+
+ private final FileDiffResult result;
+ private final boolean isAfterState;
+
+ public PatchFileTypedElement(FileDiffResult result, boolean isAfterState) {
+ this.result = result;
+ this.isAfterState = isAfterState;
+ }
+
+ public Image getImage() {
+ IFile file = getPatcher().getTargetFile(result.getDiff());
+ if (file == null) {
+ // We don't get a target file if the file doesn't exist
+ DiffProject project = result.getDiff().getProject();
+ if (project != null) {
+ file = Utilities.getProject(project).getFile(
+ result.getDiff().getPath(
+ result.getConfiguration().isReversed()));
+ } else {
+ IResource target = getPatcher().getTarget();
+ if (target instanceof IFile) {
+ file = (IFile) target;
+ } else if (target instanceof IContainer) {
+ IContainer container = (IContainer) target;
+ file = container.getFile(result.getTargetPath());
+ }
+ }
+ }
+ Image image = null;
+ if (file != null) {
+ image = CompareUI.getImage(file);
+ }
+ if (result.containsProblems()) {
+ LocalResourceManager imageCache = PatchCompareEditorInput
+ .getImageCache(result.getConfiguration());
+ image = HunkTypedElement.getHunkErrorImage(image, imageCache, true);
+ }
+ return image;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.compare.ITypedElement#getName()
+ */
+ public String getName() {
+ return result.getTargetPath().toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.compare.ITypedElement#getType()
+ */
+ public String getType() {
+ return result.getTargetPath().getFileExtension();
+ }
+
+ public String getCharset() throws CoreException {
+ return result.getCharset();
+ }
+
+ public InputStream getContents() throws CoreException {
+ // If there are cached contents, use them
+ if (isAfterState && getPatcher().hasCachedContents(result.getDiff()))
+ return new ByteArrayInputStream(getPatcher().getCachedContents(
+ result.getDiff()));
+ // Otherwise, get the lines from the diff result
+ List lines;
+ if (isAfterState) {
+ lines = result.getAfterLines();
+ } else {
+ lines = result.getBeforeLines();
+ }
+ String contents = LineReader.createString(getPatcher()
+ .isPreserveLineDelimeters(), lines);
+ String charSet = getCharset();
+ byte[] bytes = null;
+ if (charSet != null) {
+ try {
+ bytes = contents.getBytes(charSet);
+ } catch (UnsupportedEncodingException e) {
+ CompareUIPlugin.log(e);
+ }
+ }
+ if (bytes == null) {
+ bytes = contents.getBytes();
+ }
+ return new ByteArrayInputStream(bytes);
+ }
+
+ private Patcher getPatcher() {
+ return Patcher.getPatcher(result.getConfiguration());
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.java
new file mode 100644
index 000000000..6fde7b42a
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.patch;
+
+import org.eclipse.osgi.util.NLS;
+
+public final class PatchMessages extends NLS {
+
+ private static final String BUNDLE_NAME = "org.eclipse.compare.internal.patch.PatchMessages";//$NON-NLS-1$
+
+ private PatchMessages() {
+ // Do not instantiate
+ }
+
+ public static String HunkMergePage_GenerateRejectFile;
+ public static String HunkMergePage_Merged;
+ public static String InputPatchPage_MalformedURL;
+ public static String InputPatchPage_NoURL;
+ public static String InputPatchPage_URLButton_text;
+ public static String InputPatchPage_URL_title;
+ public static String PatchCompareEditorInput_0;
+ public static String PatcherCompareEditorInput_0;
+ public static String PatcherCompareEditorInput_AfterPatch;
+ public static String PatcherCompareEditorInput_LocalCopy;
+ public static String PatcherCompareEditorInput_NotIncluded;
+ public static String PatcherCompareEditorInput_PatchContents;
+ public static String PatchTargetPage_0;
+ public static String PatchWizard_0;
+ public static String PatchWizard_1;
+ public static String PatchWizard_title;
+ public static String PatchWizard_unexpectedException_message;
+ public static String InputPatchPage_title;
+ public static String InputPatchPage_message;
+ public static String InputPatchPage_Clipboard;
+ public static String InputPatchPage_SelectInput;
+ public static String InputPatchPage_PatchErrorDialog_title;
+ public static String InputPatchPage_FileButton_text;
+ public static String InputPatchPage_ChooseFileButton_text;
+ public static String InputPatchPage_UseClipboardButton_text;
+ public static String InputPatchPage_UseWorkspaceButton_text;
+ public static String InputPatchPage_WorkspaceSelectPatch_text;
+ public static String InputPatchPage_NothingSelected_message;
+ public static String InputPatchPage_ClipboardIsEmpty_message;
+ public static String InputPatchPage_NoTextInClipboard_message;
+ public static String InputPatchPage_CouldNotReadClipboard_message;
+ public static String InputPatchPage_CannotLocatePatch_message;
+ public static String InputPatchPage_NoFileName_message;
+ public static String InputPatchPage_FileSelectedNotPatch_message;
+ public static String InputPatchPage_SelectPatchFileDialog_title;
+ public static String InputPatchPage_PatchFileNotFound_message;
+ public static String InputPatchPage_ParseError_message;
+ public static String InputPatchPage_Clipboard_title;
+ public static String InputPatchPage_PatchFile_title;
+ public static String InputPatchPage_WorkspacePatch_title;
+ public static String InputPatchPage_NoDiffsFound_format;
+ public static String InputPatchPage_SingleFileError_format;
+ public static String InputPatchPage_URLConnecting;
+ public static String InputPatchPage_URLFetchingContent;
+ public static String PatchTargetPage_title;
+ public static String PatchTargetPage_message;
+ public static String PreviewPatchPage_title;
+ public static String PreviewPatchPage_PatchOptions_title;
+ public static String PreviewPatchPage_IgnoreSegments_text;
+ public static String PreviewPatchPage_ReversePatch_text;
+ public static String PreviewPatchPage_FuzzFactor_text;
+ public static String PreviewPatchPage_FuzzFactor_tooltip;
+ public static String PreviewPatchPage_GuessFuzz_text;
+ public static String PreviewPatchPage_FuzzUsed;
+ public static String PreviewPatchPage_AllContextIgnored;
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, PatchMessages.class);
+ }
+
+ public static String Diff_2Args;
+ public static String PreviewPatchPage_RetargetPatch;
+ public static String PreviewPatchPage_SelectProject;
+ public static String PreviewPatchPage_Target;
+ public static String PreviewPatchLabelDecorator_ProjectDoesNotExist;
+ public static String PreviewPatchPage2_0;
+ public static String PreviewPatchPage2_1;
+ public static String PreviewPatchPage2_2;
+ public static String PreviewPatchPage2_3;
+ public static String PreviewPatchPage2_4;
+ public static String PreviewPatchPage2_5;
+ public static String PreviewPatchPage2_6;
+ public static String PreviewPatchPage2_7;
+ public static String PreviewPatchPage2_8;
+ public static String PreviewPatchPage2_9;
+ public static String PreviewPatchPage2_CalculateReverse;
+ public static String PreviewPatchPage2_IgnoreWhitespace;
+ public static String PreviewPatchPage2_IgnoreWSAction;
+ public static String PreviewPatchPage2_IgnoreWSTooltip;
+ public static String PreviewPatchPage2_OrphanedHunk;
+ public static String PreviewPatchPage2_MatchedHunk;
+ public static String PreviewPatchPage2_PatchedLocalFile;
+ public static String PreviewPatchPage2_RetargetAction;
+ public static String PreviewPatchPage2_RetargetTooltip;
+ public static String PreviewPatchPage2_ShowMatched;
+ public static String PreviewPatchPage2_AddedRemovedLines;
+ public static String RetargetPatchElementDialog_0;
+ public static String RetargetPatchElementDialog_1;
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.properties
new file mode 100644
index 000000000..95ac4b8f2
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchMessages.properties
@@ -0,0 +1,117 @@
+###############################################################################
+# Copyright (c) 2000, 2009 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
+# Sebastian Davids <sdavids@gmx.de> - layout tweaks
+###############################################################################
+
+#
+# 'Compare with Patch' Action
+#
+PatcherCompareEditorInput_LocalCopy=Local Copy
+PatcherCompareEditorInput_0=Discard Changes?
+PatcherCompareEditorInput_AfterPatch=After Patch
+
+#
+# PatchWizard
+#
+PatchWizard_title=Apply Patch
+PatchWizard_0=Patch has rejects
+PatchWizard_1=The patch you are applying has segments that did not match. Are you sure you want to apply it?
+PatchWizard_unexpectedException_message= Unexpected exception while applying the patch. See log for a detailed error description.
+PatcherCompareEditorInput_PatchContents=Patch Contents
+PatcherCompareEditorInput_NotIncluded=(Not included)
+
+#
+# InputPatchPage
+#
+InputPatchPage_title= Patch Input Specification
+InputPatchPage_NoURL=Please enter an URL
+InputPatchPage_message= Select the patch location.
+InputPatchPage_Clipboard=Clipboard
+InputPatchPage_SelectInput=Apply the patch to the &selected file, folder or project:
+InputPatchPage_MalformedURL=Malformed URL
+InputPatchPage_PatchErrorDialog_title=Patch Error
+InputPatchPage_FileButton_text=Fil&e
+InputPatchPage_URLButton_text=&URL
+InputPatchPage_ChooseFileButton_text=&Browse...
+InputPatchPage_UseClipboardButton_text=&Clipboard
+InputPatchPage_UseWorkspaceButton_text=&Workspace
+InputPatchPage_WorkspaceSelectPatch_text=&Select the location of the patch:
+InputPatchPage_NothingSelected_message=Select a file or folder to be patched
+InputPatchPage_ClipboardIsEmpty_message=Clipboard is empty
+InputPatchPage_NoTextInClipboard_message=Clipboard does not contain text
+InputPatchPage_CouldNotReadClipboard_message=Cannot retrieve clipboard contents
+InputPatchPage_CannotLocatePatch_message=Cannot locate patch file:
+InputPatchPage_NoFileName_message=No file name
+InputPatchPage_FileSelectedNotPatch_message=Selected resource is not a valid patch
+#SI - Select file name ?
+InputPatchPage_SelectPatchFileDialog_title=Select Patch File
+InputPatchPage_PatchFileNotFound_message=Patch file not found.
+InputPatchPage_ParseError_message=Error while parsing patch
+InputPatchPage_Clipboard_title=Clipboard
+InputPatchPage_PatchFile_title=Patch file
+InputPatchPage_URL_title=URL
+InputPatchPage_WorkspacePatch_title=Workspace file
+InputPatchPage_NoDiffsFound_format={0} does not contain valid patch.
+InputPatchPage_SingleFileError_format={0} contains multiple patches. You cannot apply them to a single file.
+InputPatchPage_URLConnecting=Opening connection to the URL
+InputPatchPage_URLFetchingContent=Fetching content from the URL
+#
+# PatchTargetPage
+#
+PatchTargetPage_title=Target Resource
+PatchTargetPage_0=Apply the patch to the &workspace root
+PatchTargetPage_message=Select the target workspace resource for the patch.
+PatchCompareEditorInput_0=(file does not exist)
+#
+# PreviewPatchPage
+#
+PreviewPatchPage_title=Review Patch
+PreviewPatchPage2_0=&Exclude
+PreviewPatchPage2_1=&Include
+PreviewPatchPage2_2=Performing this operation will require that your manual changes be discarded.
+PreviewPatchPage2_3=Reversing the patch will require that your manual changes be discarded.
+PreviewPatchPage2_4=Performing this operation will require that your manual changes be discarded.
+PreviewPatchPage2_5=Changing the fuzz factor will require that your manual changes be discarded.
+PreviewPatchPage2_6=Changing the fuzz factor will require that your manual changes be discarded.
+PreviewPatchPage2_7=&Show Excluded
+PreviewPatchPage2_8=Review the patch with respect to the local file system and manually merge any unmatched portions.
+PreviewPatchPage2_9=Double-click on file or patch segment entries to view their content:
+PreviewPatchPage_Target=(target: {0})
+PreviewPatchPage_PatchOptions_title=Patch options
+PreviewPatchPage_IgnoreSegments_text=&Ignore leading path name segments:
+PreviewPatchPage_ReversePatch_text=&Reverse patch
+PreviewPatchPage_FuzzFactor_text=Fu&zz factor:
+PreviewPatchPage2_RetargetAction=&Move
+PreviewPatchPage2_RetargetTooltip=Move the selected patch element to another resource
+PreviewPatchPage2_OrphanedHunk=Unmatched Patch Segment
+PreviewPatchPage2_MatchedHunk=Matched Hunk
+PreviewPatchPage2_IgnoreWSAction=Ignore whitespace
+PreviewPatchPage_FuzzFactor_tooltip=Allow this number of context lines to be ignored
+PreviewPatchPage2_IgnoreWSTooltip=Ignore whitespace
+PreviewPatchPage2_IgnoreWhitespace=Ignore whitespace
+PreviewPatchPage2_PatchedLocalFile=Patched Local File
+PreviewPatchPage2_CalculateReverse=Calculating reverse
+PreviewPatchPage_RetargetPatch=Retarget Patch
+PreviewPatchPage_SelectProject=Select the new target project for the portion of the patch targeted to project ''{0}'':
+PreviewPatchPage_GuessFuzz_text= &Guess
+PreviewPatchPage_FuzzUsed=(fuzz factor used: {0})
+PreviewPatchPage_AllContextIgnored=(fuzz factor used: {0}, all context lines ignored)
+PreviewPatchPage2_ShowMatched=Show &matched hunks
+PreviewPatchPage2_AddedRemovedLines=Patch contains {0} added and {1} removed lines.
+PreviewPatchLabelDecorator_ProjectDoesNotExist=(Project does not exist in workspace)
+
+#
+# Patcher
+#
+Diff_2Args={0} {1}
+HunkMergePage_Merged=(merged)
+HunkMergePage_GenerateRejectFile=G&enerate a .rej file for unmerged hunks
+RetargetPatchElementDialog_0=Select the new target file for the portion of the patch targeted to file ''{0}''
+RetargetPatchElementDialog_1=Select the new target file for this portion of the patch targeted to file ''{0}''
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchProjectDiffNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchProjectDiffNode.java
new file mode 100644
index 000000000..1aa09b3d5
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchProjectDiffNode.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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.patch;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.internal.core.patch.DiffProject;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.IDiffContainer;
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.swt.graphics.Image;
+
+public class PatchProjectDiffNode extends PatchDiffNode {
+
+ private final DiffProject project;
+ private final PatchConfiguration configuration;
+
+ public PatchProjectDiffNode(IDiffContainer parent, DiffProject project, PatchConfiguration configuration) {
+ super(project, parent, Differencer.NO_CHANGE);
+ this.project = project;
+ this.configuration = configuration;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.DiffNode#getName()
+ */
+ public String getName() {
+ return project.getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.DiffNode#getImage()
+ */
+ public Image getImage() {
+ Image image = CompareUI.getImage(Utilities.getProject(project));
+ if (containsProblems()) {
+ LocalResourceManager imageCache = PatchCompareEditorInput.getImageCache(getConfiguration());
+ image = HunkTypedElement.getHunkErrorImage(image, imageCache, true);
+ }
+ return image;
+ }
+
+ private boolean containsProblems() {
+ IDiffElement[] elements = getChildren();
+ for (int i = 0; i < elements.length; i++) {
+ IDiffElement diffElement = elements[i];
+ if (diffElement instanceof PatchFileDiffNode) {
+ PatchFileDiffNode node = (PatchFileDiffNode) diffElement;
+ if (node.getDiffResult().containsProblems())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.DiffNode#getType()
+ */
+ public String getType() {
+ return ITypedElement.FOLDER_TYPE;
+ }
+
+ protected PatchConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ public DiffProject getDiffProject() {
+ return project;
+ }
+
+ public IResource getResource() {
+ return ResourcesPlugin.getWorkspace().getRoot().getProject(getDiffProject().getName());
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchTargetPage.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchTargetPage.java
new file mode 100644
index 000000000..0f7317461
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchTargetPage.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 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.patch;
+
+import com.ibm.icu.text.MessageFormat;
+
+import org.eclipse.swt.SWT;
+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.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.views.navigator.ResourceComparator;
+
+import org.eclipse.compare.internal.ICompareContextIds;
+import org.eclipse.compare.internal.Utilities;
+
+/***
+ * This page only shows up if the user is trying to apply
+ * a non-workspace rooted patch.
+ */
+public class PatchTargetPage extends WizardPage {
+
+ private boolean fShowError = false;
+
+ // SWT widgets
+ private TreeViewer fPatchTargets;
+ private Button useWorkspaceAsTarget;
+ private Button selectTarget;
+
+ protected WorkspacePatcher fPatcher;
+
+ protected final static String PATCHTARGETPAGE_NAME = "PatchTargetPage"; //$NON-NLS-1$
+
+ public PatchTargetPage(WorkspacePatcher patcher) {
+ super(PATCHTARGETPAGE_NAME, PatchMessages.PatchTargetPage_title, null);
+ setMessage(PatchMessages.PatchTargetPage_message);
+ fPatcher = patcher;
+ }
+
+ /*
+ * Get a path from the supplied text widget.
+ * @return org.eclipse.core.runtime.IPath
+ */
+ protected IPath getPathFromText(Text textField) {
+ return (new Path(textField.getText())).makeAbsolute();
+ }
+
+ public void createControl(Composite parent) {
+
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
+ setControl(composite);
+
+ useWorkspaceAsTarget = createRadioButton(composite, PatchMessages.PatchTargetPage_0, 1);
+ selectTarget = createRadioButton(composite, PatchMessages.InputPatchPage_SelectInput, 1);
+
+ buildInputGroup(composite);
+
+ updateWidgetEnablements();
+
+ Dialog.applyDialogFont(composite);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, ICompareContextIds.PATCH_INPUT_WIZARD_PAGE);
+
+ useWorkspaceAsTarget.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ fShowError = true;
+ if (useWorkspaceAsTarget.getSelection()) {
+ fPatchTargets.getTree().setEnabled(false);
+ fPatcher.setTarget(ResourcesPlugin.getWorkspace().getRoot());
+ } else {
+ fPatchTargets.getTree().setEnabled(true);
+ fPatcher.setTarget(Utilities.getFirstResource(fPatchTargets.getSelection()));
+ }
+ updateWidgetEnablements();
+ }
+ });
+ }
+
+ private Button createRadioButton(Composite parent, String label, int span) {
+ Button button = new Button(parent, SWT.RADIO);
+ button.setText(label);
+ GridData data = new GridData();
+ data.horizontalSpan = span;
+ button.setLayoutData(data);
+ return button;
+ }
+
+ /* (non-JavaDoc)
+ * Method declared in IWizardPage.
+ */
+ public IWizardPage getNextPage() {
+
+ // if selected target is file ensure that patch file
+ // contains only a patch for a single file
+ if (!fPatcher.isWorkspacePatch() && fPatcher.getTarget() instanceof IFile && fPatcher.getDiffs().length > 1) {
+ InputPatchPage inputPage = (InputPatchPage) getWizard().getPage(InputPatchPage.INPUTPATCHPAGE_NAME);
+ String source = ""; //$NON-NLS-1$
+ switch (inputPage.getInputMethod()) {
+ case InputPatchPage.CLIPBOARD :
+ source = PatchMessages.InputPatchPage_Clipboard_title;
+ break;
+
+ case InputPatchPage.FILE :
+ source = PatchMessages.InputPatchPage_PatchFile_title;
+ break;
+
+ case InputPatchPage.WORKSPACE :
+ source = PatchMessages.InputPatchPage_WorkspacePatch_title;
+ break;
+ }
+ String format = PatchMessages.InputPatchPage_SingleFileError_format;
+ String message = MessageFormat.format(format, new String[] {source});
+ MessageDialog.openInformation(null, PatchMessages.InputPatchPage_PatchErrorDialog_title, message);
+ return this;
+ }
+
+ return super.getNextPage();
+ }
+
+ /* (non-JavaDoc)
+ * Method declared in IWizardPage.
+ */
+ public boolean canFlipToNextPage() {
+ // we can't call getNextPage to determine if flipping is allowed since computing
+ // the next page is quite expensive. So we say yes if the page is complete.
+ return isPageComplete();
+ }
+
+ private void buildInputGroup(Composite parent) {
+ Tree tree = new Tree(parent, SWT.BORDER);
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.heightHint = 200;
+ tree.setLayoutData(gd);
+
+ fPatchTargets = new TreeViewer(tree);
+ fPatchTargets.setLabelProvider(new WorkbenchLabelProvider());
+ fPatchTargets.setContentProvider(new WorkbenchContentProvider());
+ fPatchTargets.setComparator(new ResourceComparator(ResourceComparator.NAME));
+ fPatchTargets.setInput(ResourcesPlugin.getWorkspace().getRoot());
+
+ IResource target = fPatcher.getTarget();
+ if (target != null && !(target instanceof IWorkspaceRoot)) {
+ fPatchTargets.expandToLevel(target, 0);
+ fPatchTargets.setSelection(new StructuredSelection(target));
+ }
+
+ // register listeners
+ fPatchTargets.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ fShowError = true;
+ fPatcher.setTarget(Utilities.getFirstResource(event.getSelection()));
+ updateWidgetEnablements();
+ }
+ });
+
+ fPatchTargets.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ ((PatchWizard)getWizard()).showPage(getNextPage());
+ }
+ });
+ }
+
+ /**
+ * Updates the enable state of this page's controls.
+ */
+ private void updateWidgetEnablements() {
+ String error = null;
+
+ if (fPatcher.getTarget() == null) {
+ useWorkspaceAsTarget.setSelection(false);
+ selectTarget.setSelection(true);
+ error = PatchMessages.InputPatchPage_NothingSelected_message;
+ setPageComplete(false);
+ if (fShowError)
+ setErrorMessage(error);
+ return;
+ }
+ setErrorMessage(null);
+ useWorkspaceAsTarget.setSelection(fPatcher.getTarget() instanceof IWorkspaceRoot);
+ selectTarget.setSelection(!useWorkspaceAsTarget.getSelection());
+ setPageComplete(true);
+ }
+
+ /**
+ * The Finish button was pressed. Try to do the required work now and answer
+ * a boolean indicating success. If false is returned then the wizard will
+ * not close.
+ *
+ * @return boolean
+ */
+ public boolean finish() {
+ return true;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java
new file mode 100644
index 000000000..ede6e0649
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizard.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.patch;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.ExceptionHandler;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class PatchWizard extends Wizard {
+
+ // dialog store id constants
+ private final static String DIALOG_SETTINGS_KEY= "PatchWizard"; //$NON-NLS-1$
+
+ private boolean fHasNewDialogSettings;
+
+ protected InputPatchPage fPatchWizardPage;
+ protected PatchTargetPage fPatchTargetPage;
+ protected PreviewPatchPage2 fPreviewPage2;
+
+ private final WorkspacePatcher fPatcher;
+
+ private CompareConfiguration fConfiguration;
+ private IStorage patch;
+
+ private boolean patchReadIn = false;
+
+ public PatchWizard(IStorage patch, IResource target, CompareConfiguration configuration) {
+ Assert.isNotNull(configuration);
+ this.fConfiguration = configuration;
+ setDefaultPageImageDescriptor(CompareUIPlugin.getImageDescriptor("wizban/applypatch_wizban.png")); //$NON-NLS-1$
+ setWindowTitle(PatchMessages.PatchWizard_title);
+ initializeDialogSettings();
+ fPatcher= new WorkspacePatcher(target);
+ if (patch != null) {
+ try {
+ fPatcher.parse(patch);
+ this.patch = patch;
+ patchReadIn = true;
+ } catch (IOException e) {
+ MessageDialog.openError(null,
+ PatchMessages.InputPatchPage_PatchErrorDialog_title,
+ PatchMessages.InputPatchPage_ParseError_message);
+ } catch (CoreException e) {
+ ErrorDialog.openError(getShell(),
+ PatchMessages.InputPatchPage_PatchErrorDialog_title,
+ PatchMessages.InputPatchPage_PatchFileNotFound_message, e.getStatus());
+ }
+ }
+ }
+
+ private void initializeDialogSettings() {
+ IDialogSettings workbenchSettings= CompareUIPlugin.getDefault().getDialogSettings();
+ IDialogSettings section= workbenchSettings.getSection(DIALOG_SETTINGS_KEY);
+ if (section == null) {
+ fHasNewDialogSettings= true;
+ } else {
+ fHasNewDialogSettings= false;
+ setDialogSettings(section);
+ }
+ }
+
+ protected WorkspacePatcher getPatcher() {
+ return fPatcher;
+ }
+
+ protected IStorage getPatch() {
+ return patch;
+ }
+
+ IResource getTarget() {
+ return fPatcher.getTarget();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IWizard.
+ */
+ public void addPages() {
+ if (patch == null)
+ addPage(fPatchWizardPage = new InputPatchPage(this));
+ if (patch == null || !fPatcher.isWorkspacePatch())
+ addPage(fPatchTargetPage = new PatchTargetPage(fPatcher));
+ fPreviewPage2 = new PreviewPatchPage2(fPatcher, fConfiguration);
+ addPage(fPreviewPage2);
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IWizard.
+ */
+ public boolean performFinish() {
+
+ IWizardPage currentPage = getContainer().getCurrentPage();
+ if (currentPage.getName().equals(PreviewPatchPage2.PREVIEWPATCHPAGE_NAME)){
+ PreviewPatchPage2 previewPage = (PreviewPatchPage2) currentPage;
+ previewPage.ensureContentsSaved();
+ }
+
+ if (fPatchWizardPage != null){
+ // make sure that the patch has been read
+ if (!fPatchWizardPage.isPatchRead())
+ fPatchWizardPage.readInPatch();
+ fPatcher.refresh();
+ } else {
+ //either we have a patch from the patch input page or one has
+ //been specified; double check this
+ Assert.isNotNull(patch);
+ //make sure that the patch has been read in
+ Assert.isTrue(patchReadIn);
+ }
+
+ if (!currentPage.getName().equals(PreviewPatchPage2.PREVIEWPATCHPAGE_NAME) && fPatcher.hasRejects()){
+ if (!MessageDialog.openConfirm(getShell(), PatchMessages.PatchWizard_0, PatchMessages.PatchWizard_1)) {
+ return false;
+ }
+ }
+
+ try {
+ // create scheduling rule based on the type of patch - single or workspace
+ ISchedulingRule scheduleRule = null;
+ if (fPatcher.isWorkspacePatch()) {
+ // workspace patch
+ ISchedulingRule[] projectRules = fPatcher.getTargetProjects();
+ scheduleRule = new MultiRule(projectRules);
+ } else {
+ // single patch
+ IResource resource = getTarget();
+ if (resource.getType() == IResource.FILE) {
+ // For a file, use the modify rule for the parent since we may need to include a reject file
+ resource = resource.getParent();
+ }
+ scheduleRule = ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(resource);
+ }
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation(scheduleRule) {
+ protected void execute(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ fPatcher.applyAll(monitor, new Patcher.IFileValidator() {
+ public boolean validateResources(IFile[] resoures) {
+ return Utilities.validateResources(resoures, getShell(), PatchMessages.PatchWizard_title);
+ }
+ });
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ };
+ getContainer().run(true, false, op);
+
+ } catch (InvocationTargetException e) {
+ ExceptionHandler.handle(e, PatchMessages.PatchWizard_title, PatchMessages.PatchWizard_unexpectedException_message);
+ } catch (InterruptedException e) {
+ // cannot happen
+ // NeedWork: use assert!
+ }
+
+ // Save the dialog settings
+ if (fHasNewDialogSettings) {
+ IDialogSettings workbenchSettings = CompareUIPlugin.getDefault().getDialogSettings();
+ IDialogSettings section = workbenchSettings.getSection(DIALOG_SETTINGS_KEY);
+ section = workbenchSettings.addNewSection(DIALOG_SETTINGS_KEY);
+ setDialogSettings(section);
+ }
+
+ if (fPatchWizardPage != null)
+ fPatchWizardPage.saveWidgetValues();
+ fPreviewPage2.saveWidgetValues();
+ return true;
+ }
+
+ public void showPage(IWizardPage page) {
+ getContainer().showPage(page);
+ }
+
+ public IWizardPage getNextPage(IWizardPage page) {
+ //no patch has been read in yet, input patch page
+ if (!patchReadIn)
+ return fPatchWizardPage;
+
+ //Check to see if we're already on the patch target page and if
+ //a target has been set - if it has return the next page in sequence (the preview patch page)
+ if (page instanceof PatchTargetPage && getTarget() != null) {
+ return super.getNextPage(page);
+ } else if (page instanceof InputPatchPage && !fPatcher.isWorkspacePatch()) {
+ //Check to see if we need a target
+ return fPatchTargetPage;
+ }
+ return super.getNextPage(page);
+ }
+
+ /**
+ * Used to report that the patch has
+ *
+ */
+ protected void patchReadIn() {
+ patchReadIn = true;
+ }
+
+ public CompareConfiguration getCompareConfiguration() {
+ return fConfiguration;
+ }
+
+ public boolean canFinish() {
+ IWizardPage currentPage = getContainer().getCurrentPage();
+ if (currentPage.getName().equals(PreviewPatchPage2.PREVIEWPATCHPAGE_NAME)){
+ return currentPage.isPageComplete();
+ }
+ return super.canFinish();
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizardDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizardDialog.java
new file mode 100644
index 000000000..bd61ac389
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PatchWizardDialog.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 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.patch;
+
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+
+public class PatchWizardDialog extends WizardDialog {
+ private static final String PATCH_WIZARD_SETTINGS_SECTION = "PatchWizard"; //$NON-NLS-1$
+
+ public PatchWizardDialog(Shell parent, IWizard wizard) {
+ super(parent, wizard);
+
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ setMinimumPageSize(700, 500);
+ }
+
+ protected IDialogSettings getDialogBoundsSettings() {
+ IDialogSettings settings = CompareUIPlugin.getDefault().getDialogSettings();
+ IDialogSettings section = settings.getSection(PATCH_WIZARD_SETTINGS_SECTION);
+ if (section == null) {
+ section = settings.addNewSection(PATCH_WIZARD_SETTINGS_SECTION);
+ }
+ return section;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Patcher.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Patcher.java
new file mode 100644
index 000000000..a059819db
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Patcher.java
@@ -0,0 +1,763 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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
+ * Martin Burger <m@rtin-burger.de> patch for #93810 and #93901
+ *******************************************************************************/
+package org.eclipse.compare.internal.patch;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.compare.internal.core.Messages;
+import org.eclipse.compare.internal.core.patch.DiffProject;
+import org.eclipse.compare.internal.core.patch.FileDiffResult;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.internal.core.patch.Hunk;
+import org.eclipse.compare.internal.core.patch.PatchReader;
+import org.eclipse.compare.patch.IHunk;
+import org.eclipse.compare.patch.IHunkFilter;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+/**
+ * A Patcher
+ * - knows how to parse various patch file formats into some in-memory structure,
+ * - holds onto the parsed data and the options to use when applying the patches,
+ * - knows how to apply the patches to files and folders.
+ */
+public class Patcher implements IHunkFilter {
+
+ static protected final String REJECT_FILE_EXTENSION= ".rej"; //$NON-NLS-1$
+
+ static protected final String MARKER_TYPE= "org.eclipse.compare.rejectedPatchMarker"; //$NON-NLS-1$
+
+ /**
+ * Property used to associate a patcher with a {@link PatchConfiguration}
+ */
+ public static final String PROP_PATCHER = "org.eclipse.compare.patcher"; //$NON-NLS-1$
+
+ public interface IFileValidator {
+ boolean validateResources(IFile[] array);
+ }
+
+ // diff formats
+ // private static final int CONTEXT= 0;
+ // private static final int ED= 1;
+ // private static final int NORMAL= 2;
+ // private static final int UNIFIED= 3;
+
+ private FilePatch2[] fDiffs;
+ private IResource fTarget;
+ // patch options
+ private Set disabledElements = new HashSet();
+ private Map diffResults = new HashMap();
+ private final Map contentCache = new HashMap();
+ private Set mergedHunks = new HashSet();
+
+ private final PatchConfiguration configuration;
+ private boolean fGenerateRejectFile = false;
+
+ public Patcher() {
+ configuration = new PatchConfiguration();
+ configuration.setProperty(PROP_PATCHER, this);
+ configuration.addHunkFilter(this);
+ }
+
+ /*
+ * Returns an array of Diffs after a sucessfull call to <code>parse</code>.
+ * If <code>parse</code> hasn't been called returns <code>null</code>.
+ */
+ public FilePatch2[] getDiffs() {
+ if (fDiffs == null)
+ return new FilePatch2[0];
+ return fDiffs;
+ }
+
+ public IPath getPath(FilePatch2 diff) {
+ return diff.getStrippedPath(getStripPrefixSegments(), isReversed());
+ }
+
+ /*
+ * Returns <code>true</code> if new value differs from old.
+ */
+ public boolean setStripPrefixSegments(int strip) {
+ if (strip != getConfiguration().getPrefixSegmentStripCount()) {
+ getConfiguration().setPrefixSegmentStripCount(strip);
+ return true;
+ }
+ return false;
+ }
+
+ int getStripPrefixSegments() {
+ return getConfiguration().getPrefixSegmentStripCount();
+ }
+
+ /*
+ * Returns <code>true</code> if new value differs from old.
+ */
+ public boolean setFuzz(int fuzz) {
+ if (fuzz != getConfiguration().getFuzz()) {
+ getConfiguration().setFuzz(fuzz);
+ return true;
+ }
+ return false;
+ }
+
+ public int getFuzz(){
+ return getConfiguration().getFuzz();
+ }
+
+ /*
+ * Returns <code>true</code> if new value differs from old.
+ */
+ public boolean setIgnoreWhitespace(boolean ignoreWhitespace) {
+ if (ignoreWhitespace != getConfiguration().isIgnoreWhitespace()) {
+ getConfiguration().setIgnoreWhitespace(ignoreWhitespace);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isIgnoreWhitespace() {
+ return getConfiguration().isIgnoreWhitespace();
+ }
+
+ public boolean isGenerateRejectFile() {
+ return fGenerateRejectFile;
+ }
+
+ public void setGenerateRejectFile(boolean generateRejectFile) {
+ fGenerateRejectFile = generateRejectFile;
+ }
+
+ //---- parsing patch files
+
+ public void parse(IStorage storage) throws IOException, CoreException {
+ BufferedReader reader = Utilities.createReader(storage);
+ try {
+ parse(reader);
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) { //ignored
+ }
+ }
+ }
+
+ public void parse(BufferedReader reader) throws IOException {
+ PatchReader patchReader = new PatchReader() {
+ protected FilePatch2 createFileDiff(IPath oldPath, long oldDate,
+ IPath newPath, long newDate) {
+ return new FilePatch(oldPath, oldDate, newPath, newDate);
+ }
+ };
+ patchReader.parse(reader);
+ patchParsed(patchReader);
+ }
+
+ protected void patchParsed(PatchReader patchReader) {
+ fDiffs = patchReader.getDiffs();
+ }
+
+ public void countLines() {
+ FilePatch2[] fileDiffs = getDiffs();
+ for (int i = 0; i < fileDiffs.length; i++) {
+ int addedLines = 0;
+ int removedLines = 0;
+ FilePatch2 fileDiff = fileDiffs[i];
+ for (int j = 0; j < fileDiff.getHunkCount(); j++) {
+ IHunk hunk = fileDiff.getHunks()[j];
+ String[] lines = ((Hunk) hunk).getLines();
+ for (int k = 0; k < lines.length; k++) {
+ char c = lines[k].charAt(0);
+ switch (c) {
+ case '+':
+ addedLines++;
+ continue;
+ case '-':
+ removedLines++;
+ continue;
+ }
+ }
+ }
+ fileDiff.setAddedLines(addedLines);
+ fileDiff.setRemovedLines(removedLines);
+ }
+ }
+
+ //---- applying a patch file
+
+ public void applyAll(IProgressMonitor pm, IFileValidator validator) throws CoreException {
+
+ int i;
+
+ IFile singleFile= null; // file to be patched
+ IContainer container= null;
+ if (fTarget instanceof IContainer)
+ container= (IContainer) fTarget;
+ else if (fTarget instanceof IFile) {
+ singleFile= (IFile) fTarget;
+ container= singleFile.getParent();
+ } else {
+ Assert.isTrue(false);
+ }
+
+ // get all files to be modified in order to call validateEdit
+ List list= new ArrayList();
+ if (singleFile != null)
+ list.add(singleFile);
+ else {
+ for (i= 0; i < fDiffs.length; i++) {
+ FilePatch2 diff= fDiffs[i];
+ if (isEnabled(diff)) {
+ switch (diff.getDiffType(isReversed())) {
+ case FilePatch2.CHANGE:
+ list.add(createPath(container, getPath(diff)));
+ break;
+ }
+ }
+ }
+ }
+ if (! validator.validateResources((IFile[])list.toArray(new IFile[list.size()]))) {
+ return;
+ }
+
+ final int WORK_UNIT= 10;
+ if (pm != null) {
+ String message= Messages.Patcher_0;
+ pm.beginTask(message, fDiffs.length*WORK_UNIT);
+ }
+
+ for (i= 0; i < fDiffs.length; i++) {
+
+ int workTicks= WORK_UNIT;
+
+ FilePatch2 diff= fDiffs[i];
+ if (isEnabled(diff)) {
+
+ IPath path= getPath(diff);
+ if (pm != null)
+ pm.subTask(path.toString());
+
+ IFile file= singleFile != null
+ ? singleFile
+ : createPath(container, path);
+
+ List failed= new ArrayList();
+
+ int type= diff.getDiffType(isReversed());
+ switch (type) {
+ case FilePatch2.ADDITION:
+ // patch it and collect rejected hunks
+ List result= apply(diff, file, true, failed);
+ if (result != null)
+ store(LineReader.createString(isPreserveLineDelimeters(), result), file, new SubProgressMonitor(pm, workTicks));
+ workTicks-= WORK_UNIT;
+ break;
+ case FilePatch2.DELETION:
+ file.delete(true, true, new SubProgressMonitor(pm, workTicks));
+ workTicks-= WORK_UNIT;
+ break;
+ case FilePatch2.CHANGE:
+ // patch it and collect rejected hunks
+ result= apply(diff, file, false, failed);
+ if (result != null)
+ store(LineReader.createString(isPreserveLineDelimeters(), result), file, new SubProgressMonitor(pm, workTicks));
+ workTicks-= WORK_UNIT;
+ break;
+ }
+
+ if (isGenerateRejectFile() && failed.size() > 0) {
+ IPath pp = getRejectFilePath(path);
+ file= createPath(container, pp);
+ if (file != null) {
+ store(getRejected(failed), file, pm);
+ try {
+ IMarker marker= file.createMarker(MARKER_TYPE);
+ marker.setAttribute(IMarker.MESSAGE, Messages.Patcher_1);
+ marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
+ } catch (CoreException ex) {
+ // NeedWork
+ }
+ }
+ }
+ }
+
+ if (pm != null) {
+ if (pm.isCanceled())
+ break;
+ if (workTicks > 0)
+ pm.worked(workTicks);
+ }
+ }
+ }
+
+ private IPath getRejectFilePath(IPath path) {
+ IPath pp= null;
+ if (path.segmentCount() > 1) {
+ pp= path.removeLastSegments(1);
+ pp= pp.append(path.lastSegment() + REJECT_FILE_EXTENSION);
+ } else
+ pp= new Path(path.lastSegment() + REJECT_FILE_EXTENSION);
+ return pp;
+ }
+
+ List apply(FilePatch2 diff, IFile file, boolean create, List failedHunks) {
+ FileDiffResult result = getDiffResult(diff);
+ List lines = LineReader.load(file, create);
+ result.patch(lines, null);
+ failedHunks.addAll(result.getFailedHunks());
+ if (hasCachedContents(diff)) {
+ // Used the cached contents since they would have been provided by the user
+ return getCachedLines(diff);
+ } else if (!result.hasMatches()) {
+ // Return null if there were no matches
+ return null;
+ }
+ return result.getLines();
+ }
+
+ /*
+ * Converts the string into bytes and stores them in the given file.
+ */
+ protected void store(String contents, IFile file, IProgressMonitor pm) throws CoreException {
+
+ byte[] bytes;
+ try {
+ bytes= contents.getBytes(Utilities.getCharset(file));
+ } catch (UnsupportedEncodingException x) {
+ // uses default encoding
+ bytes= contents.getBytes();
+ }
+
+ store(bytes,file, pm);
+ }
+
+ protected void store(byte[] bytes, IFile file, IProgressMonitor pm) throws CoreException {
+ InputStream is= new ByteArrayInputStream(bytes);
+ try {
+ if (file.exists()) {
+ file.setContents(is, false, true, pm);
+ } else {
+ file.create(is, false, pm);
+ }
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch(IOException ex) {
+ // silently ignored
+ }
+ }
+ }
+
+ public boolean isPreserveLineDelimeters() {
+ return true;
+ }
+
+ public static String getRejected(List failedHunks) {
+ if (failedHunks.size() <= 0)
+ return null;
+
+ String lineSeparator= System.getProperty("line.separator"); //$NON-NLS-1$
+ StringBuffer sb= new StringBuffer();
+ Iterator iter= failedHunks.iterator();
+ while (iter.hasNext()) {
+ Hunk hunk= (Hunk) iter.next();
+ sb.append(hunk.getRejectedDescription());
+ sb.append(lineSeparator);
+ sb.append(hunk.getContent());
+ }
+ return sb.toString();
+ }
+
+ /*
+ * Ensures that a file with the given path exists in
+ * the given container. Folder are created as necessary.
+ */
+ protected IFile createPath(IContainer container, IPath path) throws CoreException {
+ if (path.segmentCount() > 1) {
+ IContainer childContainer;
+ if (container instanceof IWorkspaceRoot) {
+ IProject project = ((IWorkspaceRoot)container).getProject(path.segment(0));
+ if (!project.exists())
+ project.create(null);
+ if (!project.isOpen())
+ project.open(null);
+ childContainer = project;
+ } else {
+ IFolder f= container.getFolder(path.uptoSegment(1));
+ if (!f.exists())
+ f.create(false, true, null);
+ childContainer = f;
+ }
+ return createPath(childContainer, path.removeFirstSegments(1));
+ }
+ // a leaf
+ return container.getFile(path);
+ }
+
+ public IResource getTarget() {
+ return fTarget;
+ }
+
+ public void setTarget(IResource target) {
+ fTarget= target;
+ }
+
+
+ public IFile getTargetFile(FilePatch2 diff) {
+ IPath path = diff.getStrippedPath(getStripPrefixSegments(), isReversed());
+ return existsInTarget(path);
+ }
+
+ /**
+ * Iterates through all of the resources contained in the Patch Wizard target
+ * and looks to for a match to the passed in file
+ * @param path
+ * @return IFile which matches the passed in path or null if none found
+ */
+ public IFile existsInTarget(IPath path) {
+ if (fTarget instanceof IFile) { // special case
+ IFile file= (IFile) fTarget;
+ if (matches(file.getFullPath(), path))
+ return file;
+ } else if (fTarget instanceof IContainer) {
+ IContainer c= (IContainer) fTarget;
+ if (c.exists(path))
+ return c.getFile(path);
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if path completely matches the end of fullpath
+ * @param fullpath
+ * @param path
+ * @return true if path matches, false otherwise
+ */
+ private boolean matches(IPath fullpath, IPath path) {
+ for (IPath p= fullpath; path.segmentCount()<=p.segmentCount(); p= p.removeFirstSegments(1)) {
+ if (p.equals(path))
+ return true;
+ }
+ return false;
+ }
+
+ public int calculatePrefixSegmentCount() {
+ //Update prefix count - go through all of the diffs and find the smallest
+ //path segment contained in all diffs.
+ int length= 99;
+ if (fDiffs!=null)
+ for (int i= 0; i<fDiffs.length; i++) {
+ FilePatch2 diff= fDiffs[i];
+ length= Math.min(length, diff.segmentCount());
+ }
+ return length;
+ }
+
+ public void addDiff(FilePatch2 newDiff){
+ FilePatch2[] temp = new FilePatch2[fDiffs.length + 1];
+ System.arraycopy(fDiffs,0, temp, 0, fDiffs.length);
+ temp[fDiffs.length] = newDiff;
+ fDiffs = temp;
+ }
+
+ public void removeDiff(FilePatch2 diffToRemove){
+ FilePatch2[] temp = new FilePatch2[fDiffs.length - 1];
+ int counter = 0;
+ for (int i = 0; i < fDiffs.length; i++) {
+ if (fDiffs[i] != diffToRemove){
+ temp[counter++] = fDiffs[i];
+ }
+ }
+ fDiffs = temp;
+ }
+
+ public void setEnabled(Object element, boolean enabled) {
+ if (element instanceof DiffProject)
+ setEnabledProject((DiffProject) element, enabled);
+ if (element instanceof FilePatch2)
+ setEnabledFile((FilePatch2)element, enabled);
+ if (element instanceof Hunk)
+ setEnabledHunk((Hunk) element, enabled);
+ }
+
+ private void setEnabledProject(DiffProject projectDiff, boolean enabled) {
+ FilePatch2[] diffFiles = projectDiff.getFileDiffs();
+ for (int i = 0; i < diffFiles.length; i++) {
+ setEnabledFile(diffFiles[i], enabled);
+ }
+ }
+
+ private void setEnabledFile(FilePatch2 fileDiff, boolean enabled) {
+ IHunk[] hunks = fileDiff.getHunks();
+ for (int i = 0; i < hunks.length; i++) {
+ setEnabledHunk((Hunk) hunks[i], enabled);
+ }
+ }
+
+ private void setEnabledHunk(Hunk hunk, boolean enabled) {
+ if (enabled) {
+ disabledElements.remove(hunk);
+ FilePatch2 file = hunk.getParent();
+ disabledElements.remove(file);
+ DiffProject project = file.getProject();
+ if (project != null)
+ disabledElements.remove(project);
+ } else {
+ disabledElements.add(hunk);
+ FilePatch2 file = hunk.getParent();
+ if (disabledElements.containsAll(Arrays.asList(file.getHunks()))) {
+ disabledElements.add(file);
+ DiffProject project = file.getProject();
+ if (project != null
+ && disabledElements.containsAll(Arrays.asList(project
+ .getFileDiffs())))
+ disabledElements.add(project);
+ }
+ }
+ }
+
+ public boolean isEnabled(Object element) {
+ if (disabledElements.contains(element))
+ return false;
+ Object parent = getElementParent(element);
+ if (parent == null)
+ return true;
+ return isEnabled(parent);
+ }
+
+ protected Object getElementParent(Object element) {
+ if (element instanceof Hunk) {
+ Hunk hunk = (Hunk) element;
+ return hunk.getParent();
+ }
+ return null;
+ }
+
+ /**
+ * Calculate the fuzz factor that will allow the most hunks to be matched.
+ * @param monitor a progress monitor
+ * @return the fuzz factor or <code>-1</code> if no hunks could be matched
+ */
+ public int guessFuzzFactor(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask(Messages.Patcher_2, IProgressMonitor.UNKNOWN);
+ FilePatch2[] diffs= getDiffs();
+ if (diffs==null||diffs.length<=0)
+ return -1;
+ int fuzz= -1;
+ for (int i= 0; i<diffs.length; i++) {
+ FilePatch2 d= diffs[i];
+ IFile file= getTargetFile(d);
+ if (file != null && file.exists()) {
+ List lines= LineReader.load(file, false);
+ FileDiffResult result = getDiffResult(d);
+ int f = result.calculateFuzz(lines, monitor);
+ if (f > fuzz)
+ fuzz = f;
+ }
+ }
+ return fuzz;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ public void refresh() {
+ diffResults.clear();
+ refresh(getDiffs());
+ }
+
+ public void refresh(FilePatch2[] diffs) {
+ for (int i = 0; i < diffs.length; i++) {
+ FilePatch2 diff = diffs[i];
+ FileDiffResult result = getDiffResult(diff);
+ ((WorkspaceFileDiffResult)result).refresh();
+ }
+ }
+
+ public FileDiffResult getDiffResult(FilePatch2 diff) {
+ FileDiffResult result = (FileDiffResult)diffResults.get(diff);
+ if (result == null) {
+ result = new WorkspaceFileDiffResult(diff, getConfiguration());
+ diffResults.put(diff, result);
+ }
+ return result;
+ }
+
+ public PatchConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ /**
+ * Return the project that contains this diff or <code>null</code>
+ * if the patch is not a workspace patch.
+ * @param diff the diff
+ * @return the project that contains the diff
+ */
+ public DiffProject getProject(FilePatch2 diff) {
+ return diff.getProject();
+ }
+
+ /*
+ * Returns <code>true</code> if new value differs from old.
+ */
+ public boolean setReversed(boolean reverse) {
+ if (getConfiguration().isReversed() != reverse) {
+ getConfiguration().setReversed(reverse);
+ refresh();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isReversed() {
+ return getConfiguration().isReversed();
+ }
+
+ /**
+ * Cache the contents for the given file diff. These contents
+ * will be used for the diff when the patch is applied. When the
+ * patch is applied, it is assumed that the provided contents
+ * already have all relevant hunks applied.
+ * @param diff the file diff
+ * @param contents the contents for the file diff
+ */
+ public void cacheContents(FilePatch2 diff, byte[] contents) {
+ contentCache.put(diff, contents);
+ }
+
+ /**
+ * Return whether contents have been cached for the
+ * given file diff.
+ * @param diff the file diff
+ * @return whether contents have been cached for the file diff
+ * @see #cacheContents(FilePatch2, byte[])
+ */
+ public boolean hasCachedContents(FilePatch2 diff) {
+ return contentCache.containsKey(diff);
+ }
+
+ /**
+ * Return the content lines that are cached for the given
+ * file diff.
+ * @param diff the file diff
+ * @return the content lines that are cached for the file diff
+ */
+ public List getCachedLines(FilePatch2 diff) {
+ byte[] contents = (byte[])contentCache.get(diff);
+ if (contents != null) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(contents)));
+ return LineReader.readLines(reader);
+ }
+ return null;
+ }
+
+ /**
+ * Return the contents that are cached for the given diff or
+ * <code>null</code> if there is no contents cached.
+ * @param diff the diff
+ * @return the contents that are cached for the given diff or
+ * <code>null</code>
+ */
+ public byte[] getCachedContents(FilePatch2 diff) {
+ return (byte[])contentCache.get(diff);
+ }
+
+ /**
+ * Return whether the patcher has any cached contents.
+ * @return whether the patcher has any cached contents
+ */
+ public boolean hasCachedContents() {
+ return !contentCache.isEmpty();
+ }
+
+ /**
+ * Clear any cached contents.
+ */
+ public void clearCachedContents() {
+ contentCache.clear();
+ mergedHunks.clear();
+ }
+
+ public void setProperty(String key, Object value) {
+ getConfiguration().setProperty(key, value);
+ }
+
+ public Object getProperty(String key) {
+ return getConfiguration().getProperty(key);
+ }
+
+ public boolean isManuallyMerged(Hunk hunk) {
+ return mergedHunks.contains(hunk);
+ }
+
+ public void setManuallyMerged(Hunk hunk, boolean merged) {
+ if (merged)
+ mergedHunks.add(hunk);
+ else
+ mergedHunks.remove(hunk);
+ }
+
+ public IProject getTargetProject(FilePatch2 diff) {
+ DiffProject dp = getProject(diff);
+ if (dp != null)
+ return Utilities.getProject(dp);
+ IResource tr = getTarget();
+ if (tr instanceof IWorkspaceRoot) {
+ IWorkspaceRoot root = (IWorkspaceRoot) tr;
+ return root.getProject(diff.getPath(isReversed()).segment(0));
+ }
+ return tr.getProject();
+ }
+
+ public static Patcher getPatcher(PatchConfiguration configuration) {
+ return (Patcher)configuration.getProperty(PROP_PATCHER);
+ }
+
+ public boolean hasRejects() {
+ for (Iterator iterator = diffResults.values().iterator(); iterator.hasNext();) {
+ FileDiffResult result = (FileDiffResult) iterator.next();
+ if (result.hasRejects())
+ return true;
+ }
+ return false;
+ }
+
+ public boolean select(IHunk hunk) {
+ return isEnabled(hunk);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java
new file mode 100644
index 000000000..7414cbef3
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/PreviewPatchPage2.java
@@ -0,0 +1,747 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 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.patch;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
+import java.util.regex.Pattern;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.internal.ComparePreferencePage;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.ICompareUIConstants;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.internal.core.patch.Hunk;
+import org.eclipse.compare.patch.IHunk;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+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.TreeSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.osgi.util.NLS;
+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.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.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.forms.events.ExpansionAdapter;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+
+public class PreviewPatchPage2 extends WizardPage {
+
+ protected final static String PREVIEWPATCHPAGE_NAME= "PreviewPatchPage"; //$NON-NLS-1$
+
+ private static final String EXPAND_PATCH_OPTIONS = "expandPatchOptions"; //$NON-NLS-1$
+ private static final String GENERATE_REJECTS = "generateRejects"; //$NON-NLS-1$
+
+ final WorkspacePatcher fPatcher;
+ private final CompareConfiguration fConfiguration;
+ private PatchCompareEditorInput fInput;
+
+ private Combo fStripPrefixSegments;
+ private Text fFuzzField;
+ private Label addedRemovedLines;
+
+ private Action fExcludeAction;
+ private Action fIncludeAction;
+ private Action fIgnoreWhiteSpace;
+ private Action fReversePatch;
+ private Action fMoveAction;
+
+ protected boolean pageRecalculate= true;
+
+ private IDialogSettings settings;
+ private ExpandableComposite patchOptions;
+ private Button generateRejects;
+ private FormToolkit fToolkit;
+
+ public PreviewPatchPage2(WorkspacePatcher patcher, CompareConfiguration configuration) {
+ super(PREVIEWPATCHPAGE_NAME, PatchMessages.PreviewPatchPage_title, null);
+ setDescription(PatchMessages.PreviewPatchPage2_8);
+ Assert.isNotNull(patcher);
+ Assert.isNotNull(configuration);
+ this.fPatcher = patcher;
+ this.fConfiguration = configuration;
+ this.fConfiguration.addPropertyChangeListener(new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(CompareConfiguration.IGNORE_WHITESPACE)){
+ rebuildTree();
+ }
+ }
+ });
+ }
+
+ public void createControl(Composite parent) {
+ fToolkit = new FormToolkit(parent.getDisplay());
+ fToolkit.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
+
+ final Form form = fToolkit.createForm(parent);
+ Composite composite = form.getBody();
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ initializeDialogUnits(parent);
+
+ fInput = new PatchCompareEditorInput(getPatcher(), getCompareConfiguration()) {
+ protected void fillContextMenu(IMenuManager manager) {
+ if (isShowAll()) {
+ manager.add(fIncludeAction);
+ }
+ manager.add(fExcludeAction);
+ manager.add(new Separator());
+ manager.add(fMoveAction);
+ }
+ };
+
+ buildPatchOptionsGroup(form);
+
+ // Initialize the input
+ try {
+ fInput.run(null);
+ } catch (InterruptedException e) {//ignore
+ } catch (InvocationTargetException e) {//ignore
+ }
+
+ Label label = new Label(composite, SWT.NONE);
+ label.setText(PatchMessages.PreviewPatchPage2_9);
+ Control c = fInput.createContents(composite);
+ initializeActions();
+ fInput.contributeDiffViewerToolbarItems(getContributedActions(), getPatcher().isWorkspacePatch());
+ fInput.getViewer().addSelectionChangedListener(new ISelectionChangedListener(){
+ public void selectionChanged(SelectionChangedEvent event) {
+ ISelection s = event.getSelection();
+ if (s != null && !s.isEmpty()) {
+ if (s instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection) s;
+ updateActions(ss);
+ }
+ }
+ }});
+
+ c.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ addedRemovedLines = new Label(composite, SWT.NONE);
+ addedRemovedLines.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
+ | GridData.VERTICAL_ALIGN_BEGINNING));
+
+ setControl(composite);
+
+ restoreWidgetValues();
+
+ Dialog.applyDialogFont(composite);
+ }
+
+ private void updateActions(IStructuredSelection ss) {
+ fExcludeAction.setEnabled(false);
+ fIncludeAction.setEnabled(false);
+ for (Iterator it = ss.iterator(); it.hasNext();) {
+ Object element = it.next();
+ if (element instanceof PatchDiffNode) {
+ if (((PatchDiffNode) element).isEnabled()) {
+ fExcludeAction.setEnabled(true);
+ } else {
+ fIncludeAction.setEnabled(true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Makes sure that at least one hunk is checked off in the tree before
+ * allowing the patch to be applied.
+ */
+ private void updateEnablements() {
+ boolean atLeastOneIsEnabled = false;
+ if (fInput != null)
+ atLeastOneIsEnabled = fInput.hasResultToApply();
+ setPageComplete(atLeastOneIsEnabled);
+ }
+
+ private Action[] getContributedActions() {
+ return new Action[]{ fIgnoreWhiteSpace };
+ }
+
+ private void initializeActions() {
+
+ fMoveAction = new Action(PatchMessages.PreviewPatchPage2_RetargetAction, null) {
+ public void run() {
+ Shell shell = getShell();
+ ISelection selection = fInput.getViewer().getSelection();
+ PatchDiffNode node = null;
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection) selection;
+ if (ss.getFirstElement() instanceof PatchDiffNode) {
+ node = (PatchDiffNode) ss.getFirstElement();
+ }
+ }
+ if (node == null)
+ return;
+ final RetargetPatchElementDialog dialog = new RetargetPatchElementDialog(shell, fPatcher, node);
+ int returnCode = dialog.open();
+ if (returnCode == Window.OK) {
+ // TODO: This could be a problem. We should only rebuild the affected nodes
+ rebuildTree();
+ }
+ }
+ };
+ fMoveAction .setToolTipText(PatchMessages.PreviewPatchPage2_RetargetTooltip);
+ fMoveAction.setEnabled(true);
+ fInput.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection sel= (IStructuredSelection) event.getSelection();
+ Object obj= sel.getFirstElement();
+ boolean enable = false;
+ if (obj instanceof PatchProjectDiffNode) {
+ enable = true;
+ } else if (obj instanceof PatchFileDiffNode) {
+ PatchFileDiffNode node = (PatchFileDiffNode) obj;
+ enable = node.getDiffResult().getDiffProblem();
+ } else if (obj instanceof HunkDiffNode) {
+ enable = true;
+ }
+ fMoveAction.setEnabled(enable);
+ }
+ });
+
+ fExcludeAction = new Action(PatchMessages.PreviewPatchPage2_0) {
+ public void run() {
+ ISelection selection = fInput.getViewer().getSelection();
+ if (selection instanceof TreeSelection){
+ TreeSelection treeSelection = (TreeSelection) selection;
+ Iterator iter = treeSelection.iterator();
+ while (iter.hasNext()){
+ Object obj = iter.next();
+ if (obj instanceof PatchDiffNode){
+ PatchDiffNode node = ((PatchDiffNode) obj);
+ node.setEnabled(false);
+ // TODO: This may require a rebuild if matched hunks are shown
+ }
+ }
+ updateActions(treeSelection);
+ }
+ fInput.getViewer().refresh();
+ }
+ };
+ fExcludeAction.setEnabled(true);
+
+ fIncludeAction = new Action(PatchMessages.PreviewPatchPage2_1) {
+ public void run() {
+ ISelection selection = fInput.getViewer().getSelection();
+ if (selection instanceof TreeSelection){
+ TreeSelection treeSelection = (TreeSelection) selection;
+ Iterator iter = treeSelection.iterator();
+ while (iter.hasNext()){
+ Object obj = iter.next();
+ if (obj instanceof PatchDiffNode){
+ PatchDiffNode node = ((PatchDiffNode) obj);
+ node.setEnabled(true);
+ // TODO: This may require a rebuild if matched hunks are shown
+ }
+ }
+ updateActions(treeSelection);
+ }
+ fInput.getViewer().refresh();
+ }
+ };
+ fIncludeAction.setEnabled(true);
+
+ fIgnoreWhiteSpace = new Action(PatchMessages.PreviewPatchPage2_IgnoreWSAction, CompareUIPlugin.getImageDescriptor(ICompareUIConstants.IGNORE_WHITESPACE_ENABLED)){
+ public void run(){
+ try {
+ getContainer().run(false, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ monitor.beginTask(PatchMessages.PreviewPatchPage2_IgnoreWhitespace, IProgressMonitor.UNKNOWN);
+ if (isChecked() != getPatcher().isIgnoreWhitespace()) {
+ if (promptToRebuild(PatchMessages.PreviewPatchPage2_2)) {
+ if (getPatcher().setIgnoreWhitespace(isChecked())){
+ getCompareConfiguration().setProperty(CompareConfiguration.IGNORE_WHITESPACE, new Boolean(isChecked()));
+ }
+ } else {
+ fIgnoreWhiteSpace.setChecked(!isChecked());
+ }
+ }
+ monitor.done();
+ }
+ });
+ } catch (InvocationTargetException e) { //ignore
+ } catch (InterruptedException e) { //ignore
+ }
+ }
+ };
+ fIgnoreWhiteSpace.setChecked(false);
+ fIgnoreWhiteSpace.setToolTipText(PatchMessages.PreviewPatchPage2_IgnoreWSTooltip);
+ fIgnoreWhiteSpace.setDisabledImageDescriptor(CompareUIPlugin.getImageDescriptor(ICompareUIConstants.IGNORE_WHITESPACE_DISABLED));
+
+ fReversePatch = new Action(PatchMessages.PreviewPatchPage_ReversePatch_text){
+ public void run(){
+ try {
+ getContainer().run(true, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ monitor.beginTask(PatchMessages.PreviewPatchPage2_CalculateReverse, IProgressMonitor.UNKNOWN);
+ if (isChecked() != getPatcher().isReversed()) {
+ if (promptToRebuild(PatchMessages.PreviewPatchPage2_3)) {
+ if (getPatcher().setReversed(isChecked())){
+ rebuildTree();
+ }
+ } else {
+ fReversePatch.setChecked(!isChecked());
+ }
+ }
+ monitor.done();
+ }
+ });
+ } catch (InvocationTargetException e) { //ignore
+ } catch (InterruptedException e) { //ignore
+ }
+
+ }
+
+ };
+ fReversePatch.setChecked(false);
+ fReversePatch.setToolTipText(PatchMessages.PreviewPatchPage_ReversePatch_text);
+ }
+
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ //Need to handle input and rebuild tree only when becoming visible
+ if (visible){
+ fillSegmentCombo();
+ // TODO: We should only do this if the tree needs to be rebuilt
+ rebuildTree();
+ updateEnablements();
+ addedRemovedLines.setText(countLines());
+ // expand the first tree item i.e. change
+ getCompareConfiguration().getContainer().getNavigator().selectChange(true);
+ getContainer().updateButtons();
+ getShell().getDefaultButton().setFocus();
+ }
+ }
+
+ private boolean promptToRebuild(final String promptToConfirm){
+ final Control ctrl = getControl();
+ final boolean[] result = new boolean[] { false };
+ if (ctrl != null && !ctrl.isDisposed()){
+ Runnable runnable = new Runnable() {
+ public void run() {
+ if (!ctrl.isDisposed()) {
+ // flush any viewers before prompting
+ try {
+ fInput.saveChanges(null);
+ } catch (CoreException e) {
+ CompareUIPlugin.log(e);
+ }
+ result[0] = fInput.confirmRebuild(promptToConfirm);
+ }
+ }
+ };
+ if (Display.getCurrent() == null)
+ ctrl.getDisplay().syncExec(runnable);
+ else
+ runnable.run();
+ }
+ return result[0];
+ }
+
+ private void rebuildTree(){
+ final Control ctrl = getControl();
+ if (ctrl != null && !ctrl.isDisposed()){
+ Runnable runnable = new Runnable() {
+ public void run() {
+ if (!ctrl.isDisposed()) {
+ fInput.buildTree();
+ updateEnablements();
+ }
+ }
+ };
+ if (Display.getCurrent() == null)
+ ctrl.getDisplay().syncExec(runnable);
+ else
+ runnable.run();
+ }
+ }
+
+ private void fillSegmentCombo() {
+ if (getPatcher().isWorkspacePatch()) {
+ fStripPrefixSegments.setEnabled(false);
+ } else {
+ fStripPrefixSegments.setEnabled(true);
+ int length= 99;
+ if (fStripPrefixSegments!=null && pageRecalculate) {
+ length= getPatcher().calculatePrefixSegmentCount();
+ if (length!=99) {
+ for (int k= 1; k<length; k++)
+ fStripPrefixSegments.add(Integer.toString(k));
+ pageRecalculate= false;
+ }
+ }
+ }
+ }
+ /*
+ * Create the group for setting various patch options
+ */
+ private void buildPatchOptionsGroup(final Form form) {
+ Composite parent = form.getBody();
+
+ patchOptions = fToolkit.createExpandableComposite(parent, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);
+ patchOptions.setText(PatchMessages.PreviewPatchPage_PatchOptions_title);
+ patchOptions.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
+ patchOptions.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, 3, 1));
+ patchOptions.addExpansionListener(new ExpansionAdapter() {
+ public void expansionStateChanged(ExpansionEvent e) {
+ form.layout();
+ }
+ });
+
+ Composite c = new Composite(patchOptions, SWT.NONE);
+ patchOptions.setClient(c);
+ patchOptions.setExpanded(true);
+ GridLayout gl= new GridLayout(); gl.numColumns= 3;
+ c.setLayout(gl);
+ c.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL|GridData.GRAB_HORIZONTAL));
+
+ // 1st row
+ createStripSegmentCombo(c);
+ createShowMatchedToggle(c);
+ createFuzzFactorChooser(c);
+
+ // 2nd row
+ createReversePatchToggle(c);
+ createShowRemovedToggle(c);
+ createGenerateRejectsToggle(c);
+
+ // register listeners
+ final WorkspacePatcher patcher= getPatcher();
+ if (fStripPrefixSegments!=null)
+ fStripPrefixSegments.addSelectionListener(
+ new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if (patcher.getStripPrefixSegments() != getStripPrefixSegments()) {
+ if (promptToRebuild(PatchMessages.PreviewPatchPage2_4)) {
+ if (patcher.setStripPrefixSegments(getStripPrefixSegments()))
+ rebuildTree();
+ }
+ }
+ }
+ }
+ );
+
+
+ fFuzzField.addModifyListener(
+ new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ if (patcher.getFuzz() != getFuzzFactor()) {
+ if (promptToRebuild(PatchMessages.PreviewPatchPage2_5)) {
+ if (patcher.setFuzz(getFuzzFactor()))
+ rebuildTree();
+ } else {
+ fFuzzField.setText(Integer.toString(patcher.getFuzz()));
+ }
+ }
+ }
+ });
+ }
+
+ private void createFuzzFactorChooser(Composite parent) {
+ final WorkspacePatcher patcher= getPatcher();
+ Composite pair= new Composite(parent, SWT.NONE);
+ GridLayout gl= new GridLayout(); gl.numColumns= 3; gl.marginHeight= gl.marginWidth= 0;
+ pair.setLayout(gl);
+ GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ pair.setLayoutData(gd);
+
+ Label l= new Label(pair, SWT.NONE);
+ l.setText(PatchMessages.PreviewPatchPage_FuzzFactor_text);
+ l.setToolTipText(PatchMessages.PreviewPatchPage_FuzzFactor_tooltip);
+ gd= new GridData(GridData.VERTICAL_ALIGN_CENTER|GridData.HORIZONTAL_ALIGN_BEGINNING|GridData.GRAB_HORIZONTAL);
+ l.setLayoutData(gd);
+
+ fFuzzField= new Text(pair, SWT.BORDER);
+ fFuzzField.setText("0"); //$NON-NLS-1$
+ gd= new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
+ gd.widthHint= 30;
+ fFuzzField.setLayoutData(gd);
+
+ Button b= new Button(pair, SWT.PUSH);
+ b.setText(PatchMessages.PreviewPatchPage_GuessFuzz_text);
+ b.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if (promptToRebuild(PatchMessages.PreviewPatchPage2_6)) {
+ // Reset the fuzz. We don't use HunkResult.MAXIMUM_FUZZ_FACTOR on purpose here,
+ // in order to refresh the tree the result of the calculation needs to be different
+ // than the fuzz set in the configuration (see fFuzzField modify listener).
+ patcher.setFuzz(-1);
+ int fuzz= guessFuzzFactor(patcher);
+ if (fuzz>=0)
+ fFuzzField.setText(Integer.toString(fuzz));
+ }
+ }
+ }
+ );
+ gd= new GridData(GridData.VERTICAL_ALIGN_CENTER);
+ int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+ Point minSize = b.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+ gd.widthHint = Math.max(widthHint, minSize.x);
+ b.setLayoutData(gd);
+ }
+
+ private void createGenerateRejectsToggle(Composite pair) {
+ generateRejects = new Button(pair, SWT.CHECK);
+ generateRejects.setText(PatchMessages.HunkMergePage_GenerateRejectFile);
+ GridData gd = new GridData(GridData.VERTICAL_ALIGN_CENTER
+ | GridData.HORIZONTAL_ALIGN_BEGINNING
+ | GridData.GRAB_HORIZONTAL);
+ generateRejects.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ getPatcher().setGenerateRejectFile(
+ generateRejects.getSelection());
+ }
+ });
+ generateRejects.setSelection(false);
+ generateRejects.setLayoutData(gd);
+ }
+
+ private void createShowRemovedToggle(Composite pair) {
+ final Button showRemoved = new Button(pair, SWT.CHECK);
+ showRemoved.setText(PatchMessages.PreviewPatchPage2_7);
+ GridData gd = new GridData(GridData.VERTICAL_ALIGN_CENTER
+ | GridData.HORIZONTAL_ALIGN_BEGINNING
+ | GridData.GRAB_HORIZONTAL);
+ showRemoved.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ fInput.setShowAll(showRemoved.getSelection());
+ fInput.updateTree();
+ }
+ });
+ showRemoved.setSelection(fInput.isShowAll());
+ showRemoved.setLayoutData(gd);
+ }
+
+ private void createReversePatchToggle(Composite pair) {
+ final Button reversePatch = new Button(pair, SWT.CHECK);
+ reversePatch.setText(PatchMessages.PreviewPatchPage_ReversePatch_text);
+ GridData gd = new GridData(GridData.VERTICAL_ALIGN_CENTER
+ | GridData.HORIZONTAL_ALIGN_BEGINNING
+ | GridData.GRAB_HORIZONTAL);
+ reversePatch.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if (fReversePatch != null) {
+ fReversePatch.setChecked(reversePatch.getSelection());
+ fReversePatch.run();
+ if (fReversePatch.isChecked() != reversePatch.getSelection()) {
+ reversePatch.setSelection(fReversePatch.isChecked());
+ }
+ }
+ }
+ });
+ reversePatch.setSelection(getPatcher().isReversed());
+ reversePatch.setLayoutData(gd);
+ }
+
+ private void createStripSegmentCombo(Composite parent) {
+ final WorkspacePatcher patcher= getPatcher();
+
+ Composite pair= new Composite(parent, SWT.NONE);
+ GridLayout gl= new GridLayout(); gl.numColumns= 2; gl.marginHeight= gl.marginWidth= 0;
+ pair.setLayout(gl);
+ GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ pair.setLayoutData(gd);
+
+ Label l= new Label(pair, SWT.NONE);
+ l.setText(PatchMessages.PreviewPatchPage_IgnoreSegments_text);
+ gd= new GridData(GridData.VERTICAL_ALIGN_CENTER|GridData.HORIZONTAL_ALIGN_BEGINNING);
+ l.setLayoutData(gd);
+
+ fStripPrefixSegments= new Combo(pair, SWT.DROP_DOWN|SWT.READ_ONLY|SWT.SIMPLE);
+ int prefixCnt= patcher.getStripPrefixSegments();
+ String prefix= Integer.toString(prefixCnt);
+ fStripPrefixSegments.add(prefix);
+ fStripPrefixSegments.setText(prefix);
+ gd= new GridData(GridData.VERTICAL_ALIGN_CENTER|GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
+ fStripPrefixSegments.setLayoutData(gd);
+ }
+
+ private void createShowMatchedToggle(Composite parent) {
+ final Button showMatched = new Button(parent, SWT.CHECK);
+ showMatched.setText(PatchMessages.PreviewPatchPage2_ShowMatched);
+ GridData gd = new GridData(GridData.VERTICAL_ALIGN_CENTER
+ | GridData.HORIZONTAL_ALIGN_BEGINNING
+ | GridData.GRAB_HORIZONTAL);
+ showMatched.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ fInput.setShowMatched(showMatched.getSelection());
+ rebuildTree();
+ }
+ });
+ showMatched.setSelection(fInput.isShowMatched());
+ showMatched.setLayoutData(gd);
+ }
+
+ public int getFuzzFactor() {
+ int fuzzFactor= 0;
+ if (fFuzzField!=null) {
+ String s= fFuzzField.getText();
+ try {
+ fuzzFactor= Integer.parseInt(s);
+ } catch (NumberFormatException ex) {
+ // silently ignored
+ }
+ }
+ return fuzzFactor;
+ }
+
+ public int getStripPrefixSegments() {
+ int stripPrefixSegments= 0;
+ if (fStripPrefixSegments!=null) {
+ String s= fStripPrefixSegments.getText();
+ try {
+ stripPrefixSegments= Integer.parseInt(s);
+ } catch (NumberFormatException ex) {
+ // silently ignored
+ }
+ }
+ return stripPrefixSegments;
+ }
+
+ private int guessFuzzFactor(final WorkspacePatcher patcher) {
+ final int[] result= new int[] { -1 };
+ try {
+ PlatformUI.getWorkbench().getProgressService().run(true, true,
+ new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) {
+ result[0]= patcher.guessFuzzFactor(monitor);
+ }
+ }
+ );
+ } catch (InvocationTargetException ex) {
+ // NeedWork
+ } catch (InterruptedException ex) {
+ // NeedWork
+ }
+ return result[0];
+ }
+
+ public void ensureContentsSaved() {
+ try {
+ fInput.saveChanges(new NullProgressMonitor());
+ } catch (CoreException e) {
+ //ignore
+ }
+ }
+
+ public WorkspacePatcher getPatcher() {
+ return fPatcher;
+ }
+
+ public CompareConfiguration getCompareConfiguration() {
+ return fConfiguration;
+ }
+
+ private void restoreWidgetValues() {
+ IDialogSettings dialogSettings = CompareUI.getPlugin().getDialogSettings();
+ settings = dialogSettings.getSection(PREVIEWPATCHPAGE_NAME);
+ if (settings == null) {
+ settings = dialogSettings.addNewSection(PREVIEWPATCHPAGE_NAME);
+ }
+ if (settings != null) {
+ if (settings.get(EXPAND_PATCH_OPTIONS) != null)
+ patchOptions.setExpanded(settings.getBoolean(EXPAND_PATCH_OPTIONS));
+ if (settings.get(GENERATE_REJECTS) != null) {
+ generateRejects.setSelection(settings.getBoolean(GENERATE_REJECTS));
+ getPatcher().setGenerateRejectFile(generateRejects.getSelection());
+ }
+ }
+ }
+
+ void saveWidgetValues() {
+ settings.put(EXPAND_PATCH_OPTIONS, patchOptions.isExpanded());
+ settings.put(GENERATE_REJECTS, generateRejects.getSelection());
+ }
+
+ private String countLines() {
+ int added = 0, removed = 0;
+
+ IPreferenceStore store = CompareUIPlugin.getDefault().getPreferenceStore();
+ String addedLinesRegex = store.getString(ComparePreferencePage.ADDED_LINES_REGEX);
+ String removedLinesRegex = store.getString(ComparePreferencePage.REMOVED_LINES_REGEX);
+
+ if ((addedLinesRegex == null || "".equals(addedLinesRegex)) //$NON-NLS-1$
+ && (removedLinesRegex == null || "".equals(removedLinesRegex))) { //$NON-NLS-1$
+
+ fPatcher.countLines();
+ FilePatch2[] fileDiffs = fPatcher.getDiffs();
+ for (int i = 0; i < fileDiffs.length; i++) {
+ added += fileDiffs[i].getAddedLines();
+ removed += fileDiffs[i].getRemovedLines();
+ }
+
+ } else {
+
+ Pattern addedPattern = Pattern.compile(addedLinesRegex);
+ Pattern removedPattern = Pattern.compile(removedLinesRegex);
+
+ FilePatch2[] fileDiffs = fPatcher.getDiffs();
+ for (int i = 0; i < fileDiffs.length; i++) {
+ IHunk[] hunks = fileDiffs[i].getHunks();
+ for (int j = 0; j < hunks.length; j++) {
+ String[] lines = ((Hunk) hunks[j]).getLines();
+ for (int k = 0; k < lines.length; k++) {
+ String line = lines[k];
+ if (addedPattern.matcher(line).find())
+ added++;
+ if (removedPattern.matcher(line).find())
+ removed++;
+ }
+ }
+ }
+ }
+
+ return NLS.bind(PatchMessages.PreviewPatchPage2_AddedRemovedLines,
+ new String[] { added + "", removed + "" }); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void dispose() {
+ fToolkit.dispose();
+ super.dispose();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/RetargetPatchElementDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/RetargetPatchElementDialog.java
new file mode 100644
index 000000000..3efc37027
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/RetargetPatchElementDialog.java
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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.patch;
+
+import java.util.ArrayList;
+
+import org.eclipse.compare.internal.core.patch.DiffProject;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.internal.core.patch.Hunk;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.model.BaseWorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.views.navigator.ResourceComparator;
+
+class RetargetPatchElementDialog extends Dialog {
+
+ private static class RetargetPatchContentProvider extends BaseWorkbenchContentProvider {
+ private final PatchDiffNode node;
+ public RetargetPatchContentProvider(PatchDiffNode node) {
+ this.node = node;
+ }
+ public Object[] getChildren(Object element) {
+ if (element instanceof IWorkspaceRoot) {
+ // Don't show closed projects
+ IProject[] allProjects= ((IWorkspaceRoot) element).getProjects();
+ ArrayList accessibleProjects= new ArrayList();
+ for (int i= 0; i<allProjects.length; i++) {
+ if (allProjects[i].isOpen()) {
+ accessibleProjects.add(allProjects[i]);
+ }
+ }
+ return accessibleProjects.toArray();
+ }
+ // When retargeting a diff project, don't support expansion
+ if (element instanceof IProject && node instanceof PatchProjectDiffNode) {
+ return new Object[0];
+ }
+ return super.getChildren(element);
+ }
+ }
+
+ private final PatchDiffNode fSelectedNode;
+ private final WorkspacePatcher fPatcher;
+ private TreeViewer fViewer;
+ private IResource fSelectedResource;
+
+ public RetargetPatchElementDialog(Shell shell, WorkspacePatcher patcher, PatchDiffNode node) {
+ super(shell);
+ Assert.isNotNull(patcher);
+ Assert.isNotNull(node);
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ this.fPatcher = patcher;
+ fSelectedNode= node;
+ }
+
+ protected Control createButtonBar(Composite parent) {
+ Control control = super.createButtonBar(parent);
+ Button okButton = this.getButton(IDialogConstants.OK_ID);
+ okButton.setEnabled(false);
+ return control;
+ }
+
+ protected Control createDialogArea(Composite parent) {
+ Composite composite= (Composite) super.createDialogArea(parent);
+
+ initializeDialogUnits(parent);
+
+ getShell().setText(PatchMessages.PreviewPatchPage_RetargetPatch);
+
+ GridLayout layout= new GridLayout();
+ layout.numColumns= 1;
+ layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+ layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+ composite.setLayout(layout);
+ final GridData data= new GridData(SWT.FILL, SWT.FILL, true, true);
+ composite.setLayoutData(data);
+
+ //add controls to composite as necessary
+ Label label= new Label(composite, SWT.LEFT|SWT.WRAP);
+ label.setText(getTreeLabel());
+ final GridData data2= new GridData(SWT.FILL, SWT.BEGINNING, true, false);
+ label.setLayoutData(data2);
+
+ fViewer= new TreeViewer(composite, SWT.BORDER);
+ GridData gd= new GridData(SWT.FILL, SWT.FILL, true, true);
+ gd.widthHint= 0;
+ gd.heightHint= 0;
+ fViewer.getTree().setLayoutData(gd);
+ fViewer.setContentProvider(new RetargetPatchContentProvider(fSelectedNode));
+ fViewer.setLabelProvider(new WorkbenchLabelProvider());
+ fViewer.setComparator(new ResourceComparator(ResourceComparator.NAME));
+ fViewer.setInput(getViewerInput());
+ IResource resource = getInitialSelection();
+ if (resource != null) {
+ fViewer.setSelection(new StructuredSelection(resource));
+ fViewer.expandToLevel(resource, 0);
+ }
+ setupListeners();
+
+ Dialog.applyDialogFont(composite);
+ return parent;
+ }
+
+ private IResource getViewerInput() {
+ if (fPatcher.isWorkspacePatch())
+ return ResourcesPlugin.getWorkspace().getRoot();
+ return fPatcher.getTarget();
+ }
+
+ private IResource getInitialSelection() {
+ if (fSelectedNode instanceof PatchFileDiffNode) {
+ PatchFileDiffNode node = (PatchFileDiffNode) fSelectedNode;
+ return fPatcher.getTargetFile(node.getDiffResult().getDiff());
+ } else if (fSelectedNode instanceof HunkDiffNode) {
+ HunkDiffNode node = (HunkDiffNode) fSelectedNode;
+ return fPatcher.getTargetFile(node.getHunkResult().getDiffResult().getDiff());
+ } else if (fSelectedNode instanceof PatchProjectDiffNode) {
+ PatchProjectDiffNode node = (PatchProjectDiffNode) fSelectedNode;
+ DiffProject diffProject = node.getDiffProject();
+ return Utilities.getProject(diffProject);
+ }
+ return null;
+ }
+
+ private String getTreeLabel() {
+ if (fSelectedNode instanceof PatchProjectDiffNode) {
+ PatchProjectDiffNode node = (PatchProjectDiffNode) fSelectedNode;
+ DiffProject project = node.getDiffProject();
+ return NLS.bind(PatchMessages.PreviewPatchPage_SelectProject, project.getName());
+ } else if (fSelectedNode instanceof PatchFileDiffNode) {
+ PatchFileDiffNode node = (PatchFileDiffNode) fSelectedNode;
+ //copy over all hunks to new target resource
+ FilePatch2 diff = node.getDiffResult().getDiff();
+ return NLS.bind(PatchMessages.RetargetPatchElementDialog_0, fPatcher.getPath(diff));
+ } else if (fSelectedNode instanceof HunkDiffNode) {
+ HunkDiffNode node = (HunkDiffNode) fSelectedNode;
+ Hunk hunk = node.getHunkResult().getHunk();
+ return NLS.bind(PatchMessages.RetargetPatchElementDialog_1, fPatcher.getPath(hunk.getParent()));
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ protected void okPressed() {
+ if (fSelectedResource != null){
+ if (fSelectedNode instanceof PatchProjectDiffNode && fSelectedResource instanceof IProject) {
+ PatchProjectDiffNode node = (PatchProjectDiffNode) fSelectedNode;
+ DiffProject project = node.getDiffProject();
+ fPatcher.retargetProject(project, (IProject)fSelectedResource);
+ } else if (fSelectedNode instanceof PatchFileDiffNode && fSelectedResource instanceof IFile) {
+ PatchFileDiffNode node = (PatchFileDiffNode) fSelectedNode;
+ //copy over all hunks to new target resource
+ FilePatch2 diff = node.getDiffResult().getDiff();
+ fPatcher.retargetDiff(diff, (IFile)fSelectedResource);
+ } else if (fSelectedNode instanceof HunkDiffNode && fSelectedResource instanceof IFile) {
+ HunkDiffNode node = (HunkDiffNode) fSelectedNode;
+ fPatcher.retargetHunk(node.getHunkResult().getHunk(), (IFile)fSelectedResource);
+ }
+ }
+ super.okPressed();
+ }
+
+ void setupListeners() {
+ fViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection s= (IStructuredSelection) event.getSelection();
+ Object obj= s.getFirstElement();
+ if (obj instanceof IResource){
+ fSelectedResource = (IResource) obj;
+ if (fSelectedNode instanceof PatchProjectDiffNode) {
+ if (fSelectedResource instanceof IProject){
+ Button okButton = getButton(IDialogConstants.OK_ID);
+ okButton.setEnabled(true);
+ }
+ } else if (fSelectedNode instanceof PatchFileDiffNode
+ || fSelectedNode instanceof HunkDiffNode) {
+ if (fSelectedResource instanceof IFile){
+ Button okButton = getButton(IDialogConstants.OK_ID);
+ okButton.setEnabled(true);
+ }
+ }
+ }
+ }
+ });
+
+ fViewer.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ ISelection s= event.getSelection();
+ if (s instanceof IStructuredSelection) {
+ Object item= ((IStructuredSelection) s).getFirstElement();
+ if (fViewer.getExpandedState(item))
+ fViewer.collapseToLevel(item, 1);
+ else
+ fViewer.expandToLevel(item, 1);
+ }
+ }
+ });
+
+ }
+
+ protected Point getInitialSize() {
+ final Point size= super.getInitialSize();
+ size.x= convertWidthInCharsToPixels(75);
+ size.y+= convertHeightInCharsToPixels(20);
+ return size;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/UnmatchedHunkTypedElement.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/UnmatchedHunkTypedElement.java
new file mode 100644
index 000000000..09eb69c83
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/UnmatchedHunkTypedElement.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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.patch;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import org.eclipse.compare.IContentChangeListener;
+import org.eclipse.compare.IContentChangeNotifier;
+import org.eclipse.compare.IEditableContent;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.ContentChangeNotifier;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.internal.core.patch.HunkResult;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+
+public class UnmatchedHunkTypedElement extends HunkTypedElement implements IContentChangeNotifier, IEditableContent {
+
+ private ContentChangeNotifier changeNotifier;
+
+ public UnmatchedHunkTypedElement(HunkResult result) {
+ // An unmatched hunk element is always used for the before state and is full context
+ super(result, false, true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IContentChangeNotifier#addContentChangeListener(org.eclipse.compare.IContentChangeListener)
+ */
+ public synchronized void addContentChangeListener(IContentChangeListener listener) {
+ if (changeNotifier == null)
+ changeNotifier = new ContentChangeNotifier(this);
+ changeNotifier.addContentChangeListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IContentChangeNotifier#removeContentChangeListener(org.eclipse.compare.IContentChangeListener)
+ */
+ public synchronized void removeContentChangeListener(IContentChangeListener listener) {
+ if (changeNotifier != null)
+ changeNotifier.removeContentChangeListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IEditableContent#isEditable()
+ */
+ public boolean isEditable() {
+ IFile file = ((WorkspaceFileDiffResult)getHunkResult().getDiffResult()).getTargetFile();
+ return file != null && file.isAccessible();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IEditableContent#replace(org.eclipse.compare.ITypedElement, org.eclipse.compare.ITypedElement)
+ */
+ public ITypedElement replace(ITypedElement dest, ITypedElement src) {
+ // Not supported
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IEditableContent#setContent(byte[])
+ */
+ public void setContent(byte[] newContent) {
+ getPatcher().setManuallyMerged(getHunkResult().getHunk(), true);
+ getPatcher().cacheContents(getDiff(), newContent);
+ if (changeNotifier != null)
+ changeNotifier.fireContentChanged();
+ }
+
+ private FilePatch2 getDiff() {
+ return getHunkResult().getDiffResult().getDiff();
+ }
+
+ private Patcher getPatcher() {
+ return Patcher.getPatcher(getConfiguration());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.internal.patch.HunkTypedElement#getContents()
+ */
+ public InputStream getContents() throws CoreException {
+ // If there are cached contents, use them
+ if (getPatcher().hasCachedContents(getDiff()))
+ return new ByteArrayInputStream(getPatcher().getCachedContents(getDiff()));
+ // Otherwise return the after state of the diff result
+ List lines = getHunkResult().getDiffResult().getAfterLines();
+ String content = LineReader.createString(getHunkResult().getDiffResult().isPreserveLineDelimeters(), lines);
+ byte[] bytes = null;
+ if (getCharset() != null)
+ try {
+ bytes = content.getBytes(getCharset());
+ } catch (UnsupportedEncodingException e) {
+ CompareUIPlugin.log(e);
+ }
+ if (bytes == null)
+ bytes = content.getBytes();
+ return new ByteArrayInputStream(bytes);
+ }
+
+ private PatchConfiguration getConfiguration() {
+ return getHunkResult().getDiffResult().getConfiguration();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Utilities.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Utilities.java
new file mode 100644
index 000000000..17c2d88a7
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Utilities.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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.patch;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.compare.internal.CompareMessages;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.core.patch.DiffProject;
+import org.eclipse.compare.patch.ReaderCreator;
+import org.eclipse.core.resources.IEncodedStorage;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+public class Utilities {
+
+ public static String getCharset(Object resource) {
+ if (resource instanceof IEncodedStorage) {
+ try {
+ return ((IEncodedStorage) resource).getCharset();
+ } catch (CoreException ex) {
+ CompareUIPlugin.log(ex);
+ }
+ }
+ return ResourcesPlugin.getEncoding();
+ }
+
+ public static IProject getProject(DiffProject diffProject) {
+ return ResourcesPlugin.getWorkspace().getRoot().getProject(
+ diffProject.getName());
+ }
+
+ public static ReaderCreator getReaderCreator(final IStorage storage) {
+ return new ReaderCreator() {
+ public Reader createReader() throws CoreException {
+ return Utilities.createReader(storage);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.compare.patch.ReaderCreator#canCreateReader()
+ */
+ public boolean canCreateReader() {
+ if (storage == null
+ || (storage != null && storage instanceof IFile && !((IFile) storage)
+ .isAccessible())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static BufferedReader createReader(IStorage storage)
+ throws CoreException {
+ if (storage == null
+ || (storage != null && storage instanceof IFile && !((IFile) storage)
+ .isAccessible())) {
+ throw new CoreException(new Status(IStatus.WARNING,
+ CompareUIPlugin.PLUGIN_ID,
+ CompareMessages.ReaderCreator_fileIsNotAccessible));
+ }
+ String charset = null;
+ if (storage instanceof IEncodedStorage) {
+ IEncodedStorage es = (IEncodedStorage) storage;
+ charset = es.getCharset();
+ }
+ InputStreamReader in = null;
+ if (charset != null) {
+ InputStream contents = storage.getContents();
+ try {
+ in = new InputStreamReader(contents, charset);
+ } catch (UnsupportedEncodingException e) {
+ CompareUIPlugin.log(e);
+ try {
+ contents.close();
+ } catch (IOException e1) {
+ // Ignore
+ }
+ }
+ }
+ if (in == null) {
+ in = new InputStreamReader(storage.getContents());
+ }
+ return new BufferedReader(in);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspaceFileDiffResult.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspaceFileDiffResult.java
new file mode 100644
index 000000000..494dda42c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspaceFileDiffResult.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.patch;
+
+import java.util.List;
+
+import org.eclipse.compare.internal.core.patch.FileDiffResult;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.patch.PatchConfiguration;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+
+public class WorkspaceFileDiffResult extends FileDiffResult {
+
+ public WorkspaceFileDiffResult(FilePatch2 diff,
+ PatchConfiguration configuration) {
+ super(diff, configuration);
+ }
+
+ protected boolean canCreateTarget(IStorage storage) {
+ IProject project = getPatcher().getTargetProject(getDiff());
+ return project != null && project.isAccessible();
+ }
+
+ protected boolean targetExists(IStorage storage) {
+ IFile file= (IFile)storage;
+ return file != null && file.isAccessible();
+ }
+
+ protected List getLines(IStorage storage, boolean create) {
+ IFile file= getTargetFile();
+ List lines = LineReader.load(file, create);
+ return lines;
+ }
+
+ protected Patcher getPatcher() {
+ return Patcher.getPatcher(getConfiguration());
+ }
+
+ public IFile getTargetFile() {
+ return getPatcher().getTargetFile(getDiff());
+ }
+
+ public void refresh() {
+ refresh(Utilities.getReaderCreator(getTargetFile()), null);
+ }
+
+ public String getCharset() {
+ IFile file = getTargetFile();
+ try {
+ if (file != null)
+ return file.getCharset();
+ } catch (CoreException e) {
+ }
+ return ResourcesPlugin.getEncoding();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java
new file mode 100644
index 000000000..0699d116c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/WorkspacePatcher.java
@@ -0,0 +1,382 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.patch;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.compare.internal.core.Messages;
+import org.eclipse.compare.internal.core.patch.DiffProject;
+import org.eclipse.compare.internal.core.patch.FilePatch2;
+import org.eclipse.compare.internal.core.patch.Hunk;
+import org.eclipse.compare.internal.core.patch.PatchReader;
+import org.eclipse.compare.patch.IHunk;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceRuleFactory;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+
+/**
+ * A Patcher
+ * - knows how to parse various patch file formats into some in-memory structure,
+ * - holds onto the parsed data and the options to use when applying the patches,
+ * - knows how to apply the patches to files and folders.
+ */
+public class WorkspacePatcher extends Patcher {
+
+ private DiffProject[] fDiffProjects;
+ private boolean fIsWorkspacePatch= false;
+ private final Map retargetedDiffs = new HashMap();
+
+ public WorkspacePatcher() {
+ // nothing to do
+ }
+
+ public WorkspacePatcher(IResource target) {
+ setTarget(target);
+ }
+
+ protected void patchParsed(PatchReader patchReader) {
+ super.patchParsed(patchReader);
+ fDiffProjects = patchReader.getDiffProjects();
+ fIsWorkspacePatch = patchReader.isWorkspacePatch();
+ }
+
+ public DiffProject[] getDiffProjects() {
+ return fDiffProjects;
+ }
+
+ public boolean isWorkspacePatch() {
+ return fIsWorkspacePatch;
+ }
+
+ //---- parsing patch files
+
+ public void applyAll(IProgressMonitor pm, IFileValidator validator) throws CoreException {
+ if (!fIsWorkspacePatch) {
+ super.applyAll(pm, validator);
+ } else {
+ final int WORK_UNIT= 10;
+
+ // get all files to be modified in order to call validateEdit
+ List list= new ArrayList();
+ for (int j= 0; j < fDiffProjects.length; j++) {
+ DiffProject diffProject= fDiffProjects[j];
+ if (Utilities.getProject(diffProject).isAccessible())
+ list.addAll(Arrays.asList(getTargetFiles(diffProject)));
+ }
+ // validate the files for editing
+ if (!validator.validateResources((IFile[])list.toArray(new IFile[list.size()]))) {
+ return;
+ }
+
+ FilePatch2[] diffs = getDiffs();
+ if (pm != null) {
+ String message= Messages.WorkspacePatcher_0;
+ pm.beginTask(message, diffs.length * WORK_UNIT);
+ }
+
+ for (int i= 0; i < diffs.length; i++) {
+
+ int workTicks= WORK_UNIT;
+
+ FilePatch2 diff= diffs[i];
+ if (isAccessible(diff)) {
+ IFile file= getTargetFile(diff);
+ IPath path= file.getProjectRelativePath();
+ if (pm != null)
+ pm.subTask(path.toString());
+ createPath(file.getProject(), path);
+
+ List failed= new ArrayList();
+
+ int type= diff.getDiffType(isReversed());
+ switch (type) {
+ case FilePatch2.ADDITION :
+ // patch it and collect rejected hunks
+ List result= apply(diff, file, true, failed);
+ if (result != null)
+ store(LineReader.createString(isPreserveLineDelimeters(), result), file, new SubProgressMonitor(pm, workTicks));
+ workTicks -= WORK_UNIT;
+ break;
+ case FilePatch2.DELETION :
+ file.delete(true, true, new SubProgressMonitor(pm, workTicks));
+ workTicks -= WORK_UNIT;
+ break;
+ case FilePatch2.CHANGE :
+ // patch it and collect rejected hunks
+ result= apply(diff, file, false, failed);
+ if (result != null)
+ store(LineReader.createString(isPreserveLineDelimeters(), result), file, new SubProgressMonitor(pm, workTicks));
+ workTicks -= WORK_UNIT;
+ break;
+ }
+
+ if (isGenerateRejectFile() && failed.size() > 0) {
+ IPath pp= null;
+ if (path.segmentCount() > 1) {
+ pp= path.removeLastSegments(1);
+ pp= pp.append(path.lastSegment() + REJECT_FILE_EXTENSION);
+ } else
+ pp= new Path(path.lastSegment() + REJECT_FILE_EXTENSION);
+ file= createPath(file.getProject(), pp);
+ if (file != null) {
+ store(getRejected(failed), file, pm);
+ try {
+ IMarker marker= file.createMarker(MARKER_TYPE);
+ marker.setAttribute(IMarker.MESSAGE, Messages.WorkspacePatcher_1);
+ marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
+ } catch (CoreException ex) {
+ // NeedWork
+ }
+ }
+ }
+ }
+
+ if (pm != null) {
+ if (pm.isCanceled())
+ break;
+ if (workTicks > 0)
+ pm.worked(workTicks);
+ }
+ }
+ }
+ }
+
+ private boolean isAccessible(FilePatch2 diff) {
+ return isEnabled(diff) && Utilities.getProject(diff.getProject()).isAccessible();
+ }
+
+ /**
+ * Returns the target files of all the Diffs contained by this
+ * DiffProject.
+ * @param project
+ * @return An array of IFiles that are targeted by the Diffs
+ */
+ public IFile[] getTargetFiles(DiffProject project) {
+ List files= new ArrayList();
+ FilePatch2[] diffs = project.getFileDiffs();
+ for (int i = 0; i < diffs.length; i++) {
+ FilePatch2 diff = diffs[i];
+ if (isEnabled(diff)) {
+ files.add(getTargetFile(diff));
+ }
+ }
+ return (IFile[]) files.toArray(new IFile[files.size()]);
+ }
+
+ public IFile getTargetFile(FilePatch2 diff) {
+ IPath path = diff.getStrippedPath(getStripPrefixSegments(), isReversed());
+ DiffProject project = getProject(diff);
+ if (project != null)
+ return Utilities.getProject(project).getFile(path);
+ return super.getTargetFile(diff);
+ }
+
+ private IPath getFullPath(FilePatch2 diff) {
+ IPath path = diff.getStrippedPath(getStripPrefixSegments(), isReversed());
+ DiffProject project = getProject(diff);
+ if (project != null)
+ return Utilities.getProject(project).getFile(path).getFullPath();
+ return getTarget().getFullPath().append(path);
+ }
+
+ public ISchedulingRule[] getTargetProjects() {
+ List projects= new ArrayList();
+ IResourceRuleFactory ruleFactory= ResourcesPlugin.getWorkspace().getRuleFactory();
+ // Determine the appropriate scheduling rules
+ for (int i= 0; i < fDiffProjects.length; i++) {
+ IProject tempProject= Utilities.getProject(fDiffProjects[i]);
+ // The goal here is to lock as little of the workspace as necessary
+ // but still allow the patcher to obtain the locks it needs.
+ // As such, we need to get the modify rules from the rule factory for the .project file. A pessimistic
+ // rule factory will return the root, while others might return just the project. Combining
+ // this rule with the project will result in the smallest possible locking set.
+ ISchedulingRule scheduleRule= ruleFactory.modifyRule(tempProject.getFile(IProjectDescription.DESCRIPTION_FILE_NAME));
+ MultiRule multiRule= new MultiRule(new ISchedulingRule[] { scheduleRule, tempProject } );
+ projects.add(multiRule);
+ }
+
+ return (ISchedulingRule[]) projects.toArray(new ISchedulingRule[projects.size()]);
+ }
+
+ public void setDiffProjects(DiffProject[] newProjectArray) {
+ fDiffProjects = new DiffProject[newProjectArray.length];
+ System.arraycopy(newProjectArray,0, fDiffProjects, 0, newProjectArray.length);
+ }
+
+ public void removeProject(DiffProject project) {
+ DiffProject[] temp = new DiffProject[fDiffProjects.length - 1];
+ int counter = 0;
+ for (int i = 0; i < fDiffProjects.length; i++) {
+ if (fDiffProjects[i] != project){
+ temp[counter++] = fDiffProjects[i];
+ }
+ }
+ fDiffProjects = temp;
+ }
+
+ protected Object getElementParent(Object element) {
+ if (element instanceof FilePatch2 && fDiffProjects != null) {
+ FilePatch2 diff = (FilePatch2) element;
+ for (int i = 0; i < fDiffProjects.length; i++) {
+ DiffProject project = fDiffProjects[i];
+ if (project.contains(diff))
+ return project;
+ }
+ }
+ return null;
+ }
+
+ public boolean isRetargeted(Object object) {
+ return retargetedDiffs.containsKey(object);
+ }
+
+ public IPath getOriginalPath(Object object) {
+ return (IPath)retargetedDiffs.get(object);
+ }
+
+ public void retargetDiff(FilePatch2 diff, IFile file) {
+ retargetedDiffs.put(diff, diff.getPath(false));
+ IHunk[] hunks = diff.getHunks();
+
+ if (isWorkspacePatch()){
+ //since the diff has no more hunks to apply, remove it from the parent and the patcher
+ diff.getProject().remove(diff);
+ }
+ removeDiff(diff);
+ FilePatch2 newDiff = getDiffForFile(file);
+ for (int i = 0; i < hunks.length; i++) {
+ Hunk hunk = (Hunk) hunks[i];
+ newDiff.add(hunk);
+ }
+ }
+
+ private FilePatch2 getDiffForFile(IFile file) {
+ DiffProject diffProject = null;
+ FilePatch2[] diffsToCheck;
+ if (isWorkspacePatch()){
+ // Check if the diff project already exists for the file
+ IProject project = file.getProject();
+ DiffProject[] diffProjects = getDiffProjects();
+ for (int i = 0; i < diffProjects.length; i++) {
+ if (Utilities.getProject(diffProjects[i]).equals(project)){
+ diffProject = diffProjects[i];
+ break;
+ }
+ }
+ // If the project doesn't exist yet, create it and add it to the project list
+ if (diffProject == null){
+ diffProject = addDiffProjectForProject(project);
+ }
+ diffsToCheck = diffProject.getFileDiffs();
+ } else {
+ diffsToCheck = getDiffs();
+ }
+ // Check to see if a diff already exists for the file
+ for (int i = 0; i < diffsToCheck.length; i++) {
+ FilePatch2 fileDiff = diffsToCheck[i];
+ if (isDiffForFile(fileDiff, file)) {
+ return fileDiff;
+ }
+ }
+
+ // Create a new diff for the file
+ IPath path = getDiffPath(file);
+ FilePatch2 newDiff = new FilePatch2(path, 0, path, 0);
+ if (diffProject != null){
+ diffProject.add(newDiff);
+ }
+ addDiff(newDiff);
+ return newDiff;
+ }
+
+ private IPath getDiffPath(IFile file) {
+ DiffProject project = getDiffProject(file.getProject());
+ if (project != null) {
+ return file.getProjectRelativePath();
+ }
+ return file.getFullPath().removeFirstSegments(getTarget().getFullPath().segmentCount());
+ }
+
+ private boolean isDiffForFile(FilePatch2 fileDiff, IFile file) {
+ return getFullPath(fileDiff).equals(file.getFullPath());
+ }
+
+ private DiffProject addDiffProjectForProject(IProject project) {
+ DiffProject[] diffProjects = getDiffProjects();
+ DiffProject diffProject = new DiffProject(project.getName());
+ DiffProject[] newProjectArray = new DiffProject[diffProjects.length + 1];
+ System.arraycopy(diffProjects, 0, newProjectArray, 0, diffProjects.length);
+ newProjectArray[diffProjects.length] = diffProject;
+ setDiffProjects(newProjectArray);
+ return diffProject;
+ }
+
+ public void retargetHunk(Hunk hunk, IFile file) {
+ FilePatch2 newDiff = getDiffForFile(file);
+ newDiff.add(hunk);
+ }
+
+ public void retargetProject(DiffProject project, IProject targetProject) {
+ retargetedDiffs.put(project, Utilities.getProject(project).getFullPath());
+ FilePatch2[] diffs = project.getFileDiffs();
+ DiffProject selectedProject = getDiffProject(targetProject);
+ if (selectedProject == null)
+ selectedProject = addDiffProjectForProject(targetProject);
+ // Copy over the diffs to the new project
+ for (int i = 0; i < diffs.length; i++) {
+ selectedProject.add(diffs[i]);
+ }
+ // Since the project has been retargeted, remove it from the patcher
+ removeProject(project);
+ }
+
+ /**
+ * Return the diff project for the given project
+ * or <code>null</code> if the diff project doesn't exist
+ * or if the patch is not a workspace patch.
+ * @param project the project
+ * @return the diff project for the given project
+ * or <code>null</code>
+ */
+ private DiffProject getDiffProject(IProject project) {
+ if (!isWorkspacePatch())
+ return null;
+ DiffProject[] projects = getDiffProjects();
+ for (int i = 0; i < projects.length; i++) {
+ if (Utilities.getProject(projects[i]).equals(project))
+ return projects[i];
+ }
+ return null;
+ }
+
+ public int getStripPrefixSegments() {
+ // Segments are never stripped from a workspace patch
+ if (isWorkspacePatch())
+ return 0;
+ return super.getStripPrefixSegments();
+ }
+
+}

Back to the top