Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcvs2001-05-02 16:48:05 -0400
committercvs2001-05-02 16:48:05 -0400
commit7e3be1fe036be304c22583c9bf88abb0dfe9fa9d (patch)
treec9290a61836cf8eef6c94b83abbcf60646c6e077 /bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer
downloadeclipse.platform.team-7e3be1fe036be304c22583c9bf88abb0dfe9fa9d.tar.gz
eclipse.platform.team-7e3be1fe036be304c22583c9bf88abb0dfe9fa9d.tar.xz
eclipse.platform.team-7e3be1fe036be304c22583c9bf88abb0dfe9fa9d.zip
*** empty log message ***v0_102+
Diffstat (limited to 'bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer')
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffContainer.java109
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffElement.java87
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffNode.java311
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java561
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewerResources.properties50
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java511
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DocumentRangeNode.java352
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInput.java137
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInputChangeListener.java33
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffContainer.java59
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffElement.java56
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureComparator.java44
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureCreator.java131
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java336
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/package.html49
15 files changed, 2826 insertions, 0 deletions
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffContainer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffContainer.java
new file mode 100644
index 000000000..40f970c5e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffContainer.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import java.util.Iterator;
+import java.util.ArrayList;
+import org.eclipse.swt.graphics.Image;
+
+
+/**
+ * The standard implementation of a diff container element.
+ * <p>
+ * This class may be instantiated, or further subclassed.
+ * </p>
+ */
+public abstract class DiffContainer extends DiffElement implements IDiffContainer {
+
+ private static IDiffElement[] fgEmptyArray= new IDiffElement[0];
+ private ArrayList fChildren;
+
+ /**
+ * Creates a new container with the specified kind under the given parent.
+ *
+ * @param parent under which the new container is added as a child or <code>null</code>.
+ * @param name of the container
+ * @param kind of difference (defined in <code>Differencer</code>).
+ */
+ public DiffContainer(IDiffContainer parent, int kind) {
+ super(parent, kind);
+ }
+
+ /**
+ * Tries to find the child with the given name.
+ * Returns <code>null</code> if no such child exists.
+ *
+ * @param name of the child to find
+ * @return the first element with a matching name
+ */
+ public IDiffElement findChild(String name) {
+ Object[] children= getChildren();
+ for (int i= 0; i < children.length; i++) {
+ IDiffElement child= (IDiffElement) children[i];
+ if (name.equals(child.getName()))
+ return child;
+ }
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IDiffContainer.add
+ */
+ public void add(IDiffElement diff) {
+ if (fChildren == null)
+ fChildren= new ArrayList();
+ fChildren.add(diff);
+ diff.setParent(this);
+ }
+
+ /**
+ * Removes the given child from this container.
+ * If the container becomes empty it is removed from its container.
+ */
+ /* (non Javadoc)
+ * see IDiffContainer.removeToRoot
+ */
+ public void removeToRoot(IDiffElement child) {
+ if (fChildren != null) {
+ fChildren.remove(child);
+ child.setParent(null);
+ if (fChildren.size() == 0) {
+ IDiffContainer p= getParent();
+ if (p != null)
+ p.removeToRoot(this);
+ }
+ }
+ }
+
+ /**
+ * Removes the given child (non-recursively) from this container.
+ *
+ * @param child to remove
+ */
+ public void remove(IDiffElement child) {
+ if (fChildren != null) {
+ fChildren.remove(child);
+ child.setParent(null);
+ }
+ }
+
+ /* (non Javadoc)
+ * see IDiffContainer.hasChildren
+ */
+ public boolean hasChildren() {
+ return fChildren != null && fChildren.size() > 0;
+ }
+
+ /* (non Javadoc)
+ * see IDiffContainer.getChildren
+ */
+ public IDiffElement[] getChildren() {
+ if (fChildren != null)
+ return (IDiffElement[]) fChildren.toArray(fgEmptyArray);
+ return fgEmptyArray;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffElement.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffElement.java
new file mode 100644
index 000000000..645ae7a64
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffElement.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.jface.util.Assert;
+
+/**
+ * An abstract base implementation of the <code>IDiffElement</code> interface.
+ * <p>
+ * Subclasses may add behavior and state, and may override <code>getImage</code>
+ * and <code>getType</code> to suit.
+ * </p>
+ */
+public abstract class DiffElement implements IDiffElement {
+
+ private int fKind;
+ private IDiffContainer fParent;
+
+ /**
+ * Creates a new <code>DiffElement</code> as a child of the given parent.
+ * If parent is not <code>null</code> the new element is added to the parent.
+ *
+ * @param parent the parent of this child; if not <code>null</code> this element is automatically added as a child
+ * @param kind the kind of change
+ */
+ public DiffElement(IDiffContainer parent, int kind) {
+ fParent= parent;
+ fKind= kind;
+ if (parent != null)
+ parent.add(this);
+ }
+
+ /**
+ * The <code>DiffElement</code> implementation of this <code>ITypedInput</code>
+ * method returns <code>null</code>. Subclasses may re-implement to provide
+ * an image for this element.
+ */
+ public Image getImage() {
+ return null;
+ }
+
+ /**
+ * The <code>DiffElement</code> implementation of this <code>ITypedElement</code>
+ * method returns <code>ITypedElement.UNKNOWN_TYPE</code>. Subclasses may
+ * re-implement to provide a type for this element.
+ */
+ public String getType() {
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ /**
+ * Sets the kind of difference for this element.
+ *
+ * @param kind set the kind of difference this element represents
+ * @see Differencer
+ */
+ public void setKind(int kind) {
+ fKind= kind;
+ }
+
+ /* (non Javadoc)
+ * see IDiffElement.getKind
+ */
+ public int getKind() {
+ return fKind;
+ }
+
+ /* (non Javadoc)
+ * see IDiffElement.getParent
+ */
+ public IDiffContainer getParent() {
+ return fParent;
+ }
+
+ /* (non Javadoc)
+ * see IDiffElement.setParent
+ */
+ public void setParent(IDiffContainer parent) {
+ fParent= parent;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffNode.java
new file mode 100644
index 000000000..953e6c640
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffNode.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.util.ListenerList;
+
+import org.eclipse.compare.*;
+
+/**
+ * Diff node are used as the compare result of the differencing engine.
+ * Since it implements the <code>ITypedElement</code> and <code>ICompareInput</code>
+ * interfaces it can be used directly to display the
+ * compare result in a <code>DiffTreeViewer</code> and as the input to any other
+ * compare/merge viewer.
+ * <p>
+ * <code>DiffNode</code>s are typically created as the result of performing
+ * a compare with the <code>Differencer</code>.
+ * <p>
+ * Clients typically use this class as is, but may subclass if required.
+ *
+ * @see DiffTreeViewer
+ * @see Differencer
+ */
+public class DiffNode extends DiffContainer implements ITypedElement, ICompareInput {
+
+ private ITypedElement fAncestor;
+ private ITypedElement fLeft;
+ private ITypedElement fRight;
+ private boolean fDontExpand;
+ private ListenerList fListener;
+
+
+ /**
+ * Creates a new <code>DiffNode</code> and initializes with the given values.
+ *
+ * @param parent under which the new container is added as a child or <code>null</code>
+ * @param kind of difference (defined in <code>Differencer</code>)
+ * @param ancestor the common ancestor input to a compare
+ * @param left the left input to a compare
+ * @param right the right input to a compare
+ */
+ public DiffNode(IDiffContainer parent, int kind, ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ this(parent, kind);
+ fAncestor= ancestor;
+ fLeft= left;
+ fRight= right;
+ }
+
+ /**
+ * Creates a new <code>DiffNode</code> with diff kind <code>Differencer.CHANGE</code>
+ * and initializes with the given values.
+ *
+ * @param left the left input to a compare
+ * @param right the right input to a compare
+ */
+ public DiffNode(ITypedElement left, ITypedElement right) {
+ this(null, Differencer.CHANGE, null, left, right);
+ }
+
+ /**
+ * Creates a new <code>DiffNode</code> and initializes with the given values.
+ *
+ * @param kind of difference (defined in <code>Differencer</code>)
+ * @param ancestor the common ancestor input to a compare
+ * @param left the left input to a compare
+ * @param right the right input to a compare
+ */
+ public DiffNode(int kind, ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ this(null, kind, ancestor, left, right);
+ }
+
+ /**
+ * Creates a new <code>DiffNode</code> with the given diff kind.
+ *
+ * @param kind of difference (defined in <code>Differencer</code>)
+ */
+ public DiffNode(int kind) {
+ super(null, kind);
+ }
+
+ /**
+ * Creates a new <code>DiffNode</code> and initializes with the given values.
+ *
+ * @param parent under which the new container is added as a child or <code>null</code>
+ * @param kind of difference (defined in <code>Differencer</code>)
+ */
+ public DiffNode(IDiffContainer parent, int kind) {
+ super(parent, kind);
+ }
+
+ /**
+ * Registers a listener for changes of this <code>ICompareInput</code>.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param listener the listener to add
+ */
+ public void addCompareInputChangeListener(ICompareInputChangeListener listener) {
+ if (fListener == null)
+ fListener= new ListenerList();
+ fListener.add(listener);
+ }
+
+ /**
+ * Unregisters a <code>ICompareInput</code> listener.
+ * Has no effect if listener is not registered.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeCompareInputChangeListener(ICompareInputChangeListener listener) {
+ if (fListener != null) {
+ fListener.remove(listener);
+ if (fListener.isEmpty())
+ fListener= null;
+ }
+ }
+
+ /**
+ * Sends out notification that a change has occured on the <code>ICompareInput</code>.
+ */
+ protected void fireChange() {
+ if (fListener != null) {
+ Object[] listeners= fListener.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((ICompareInputChangeListener) listeners[i]).compareInputChanged(this);
+ }
+ }
+
+ //---- getters & setters
+
+ /**
+ * Returns <code>true</code> if this node shouldn't automatically be expanded in
+ * a </code>DiffTreeViewer</code>.
+ *
+ * @return <code>true</code> if node shouldn't automatically be expanded
+ */
+ public boolean dontExpand() {
+ return fDontExpand;
+ }
+
+ /**
+ * Controls whether this node is not automatically expanded when displayed in
+ * a </code>DiffTreeViewer</code>.
+ *
+ * @param dontExpand if <code>true</code> this node is not automatically expanded in </code>DiffTreeViewer</code>
+ */
+ public void setDontExpand(boolean dontExpand) {
+ fDontExpand= dontExpand;
+ }
+
+ /**
+ * Returns the first not-<code>null</code> input of this node.
+ * Method checks the three inputs in the order: ancestor, right, left.
+ *
+ * @return the first not-<code>null</code> input of this node
+ */
+ public ITypedElement getId() {
+ if (fAncestor != null)
+ return fAncestor;
+ if (fRight != null)
+ return fRight;
+ return fLeft;
+ }
+
+ /**
+ * Returns the (non-<code>null</code>) name of the left or right side if they are identical.
+ * Otherwise both names are concatenated (separated with a slash ('/')).
+ * <p>
+ * Subclasses may re-implement to provide a different name for this node.
+ */
+ /* (non Javadoc)
+ * see ITypedElement.getName
+ */
+ public String getName() {
+
+ String s1= null;
+ if (fRight != null)
+ s1= fRight.getName();
+
+ String s2= null;
+ if (fLeft != null)
+ s2= fLeft.getName();
+
+ if (s1 == null && s2 == null) {
+ if (fAncestor != null)
+ return fAncestor.getName();
+ return "<no name>";
+ }
+
+ if (s1 == null)
+ return s2;
+ if (s2 == null)
+ return s1;
+
+ if (s1.equals(s2))
+ return s1;
+ StringBuffer sb= new StringBuffer(s1);
+ sb.append(" / ");
+ sb.append(s2);
+ return sb.toString();
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getImage
+ */
+ public Image getImage() {
+ ITypedElement id= getId();
+ if (id != null)
+ return id.getImage();
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getType
+ */
+ public String getType() {
+ ITypedElement id= getId();
+ if (id != null)
+ return id.getType();
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ /* (non Javadoc)
+ * see ICompareInput.getAncestor
+ */
+ public ITypedElement getAncestor() {
+ return fAncestor;
+ }
+
+ /**
+ * Sets the left input to the given value.
+ *
+ * @param left the new value for the left input
+ */
+ public void setLeft(ITypedElement left) {
+ fLeft= left;
+ }
+
+ /* (non Javadoc)
+ * see ICompareInput.getLeft
+ */
+ public ITypedElement getLeft() {
+ return fLeft;
+ }
+
+ /**
+ * Sets the right input to the given value.
+ *
+ * @param right the new value for the right input
+ */
+ public void setRight(ITypedElement right) {
+ fRight= right;
+ }
+
+ /* (non Javadoc)
+ * see ICompareInput.getRight
+ */
+ public ITypedElement getRight() {
+ return fRight;
+ }
+
+ /* (non Javadoc)
+ * see ICompareInput.copy
+ */
+ public void copy(boolean leftToRight) {
+ //System.out.println("DiffNode.copy: " + leftToRight);
+
+ IDiffContainer pa= getParent();
+ if (pa instanceof ICompareInput) {
+ ICompareInput parent= (ICompareInput) pa;
+ Object dstParent= leftToRight ? parent.getRight() : parent.getLeft();
+
+ if (dstParent instanceof IEditableContent) {
+ ITypedElement dst= leftToRight ? getRight() : getLeft();
+ ITypedElement src= leftToRight ? getLeft() : getRight();
+ //dst= ((IEditableContent)dstParent).replace(dst, src);
+ if (leftToRight)
+ setRight(dst);
+ else
+ setLeft(dst);
+ setKind(Differencer.NO_CHANGE);
+
+ fireChange();
+ }
+ }
+ }
+
+ //---- object
+
+ public int hashCode() {
+ Object id= getId();
+ if (id != null)
+ return id.hashCode();
+ return super.hashCode();
+ }
+
+ public boolean equals(Object other) {
+ if (other != null && getClass() == other.getClass()) {
+ DiffNode d= (DiffNode) other;
+ Object id1= getId();
+ Object id2= d.getId();
+ if (id1 != null && id2 != null)
+ return id1.equals(id2);
+ }
+ return super.equals(other);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java
new file mode 100644
index 000000000..bfbcbb742
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java
@@ -0,0 +1,561 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import java.util.Iterator;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.*;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.viewers.*;
+
+import org.eclipse.compare.internal.*;
+import org.eclipse.compare.*;
+
+
+/**
+ * A tree viewer that works on objects implementing
+ * the <code>IDiffContainer</code> and <code>IDiffElement</code> interfaces.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed outside
+ * this package.
+ * </p>
+ *
+ * @see IDiffContainer
+ * @see IDiffElement
+ */
+public class DiffTreeViewer extends TreeViewer {
+
+ static class DiffViewerSorter 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;
+ }
+ }
+
+ class DiffViewerContentProvider implements ITreeContentProvider {
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // System.out.println("input: " + newInput);
+ }
+
+ public boolean isDeleted(Object element) {
+ return false;
+ }
+
+ public void dispose() {
+ inputChanged(DiffTreeViewer.this, getInput(), null);
+ }
+
+ public Object getParent(Object element) {
+ if (element instanceof IDiffElement)
+ return ((IDiffElement)element).getParent();
+ return null;
+ }
+
+ public final boolean hasChildren(Object element) {
+ if (element instanceof IDiffContainer)
+ return ((IDiffContainer)element).hasChildren();
+ return false;
+ }
+
+ public final Object[] getChildren(Object element) {
+ if (element instanceof IDiffContainer)
+ return ((IDiffContainer)element).getChildren();
+ return new Object[0];
+ }
+
+ public Object[] getElements(Object element) {
+ return getChildren(element);
+ }
+ }
+
+ class DiffViewerLabelProvider extends LabelProvider {
+
+ public String getText(Object element) {
+ if (element instanceof IDiffElement)
+ return ((IDiffElement)element).getName();
+ return "<null>";
+ }
+
+ public Image getImage(Object element) {
+ if (element instanceof IDiffElement) {
+ IDiffElement input= (IDiffElement) element;
+ return fCompareConfiguration.getImage(input.getImage(), input.getKind());
+ }
+ return null;
+ }
+ }
+
+ static class FilterSame extends ViewerFilter {
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if (element instanceof IDiffElement)
+ return (((IDiffElement)element).getKind() & Differencer.PSEUDO_CONFLICT) == 0;
+ return true;
+ }
+ public boolean isFilterProperty(Object element, Object property) {
+ return false;
+ }
+ }
+
+ private ResourceBundle fBundle;
+ private CompareConfiguration fCompareConfiguration;
+ private ViewerFilter fViewerFilter;
+ private IPropertyChangeListener fPropertyChangeListener;
+
+ private Action fCopyLeftToRightAction;
+ private Action fCopyRightToLeftAction;
+ private Action fNextAction;
+ private Action fPreviousAction;
+
+ /**
+ * Creates a new viewer for the given SWT tree control with the specified configuration.
+ *
+ * @param tree the tree control
+ * @param configuration the configuration for this viewer
+ */
+ public DiffTreeViewer(Tree tree, CompareConfiguration configuration) {
+ super(tree);
+ initialize(configuration);
+ }
+
+ /**
+ * Creates a new viewer under the given SWT parent and with the specified configuration.
+ *
+ * @param parent the SWT control under which to create the viewer
+ * @param configuration the configuration for this viewer
+ */
+ public DiffTreeViewer(Composite parent, CompareConfiguration configuration) {
+ super(new Tree(parent, SWT.MULTI));
+ initialize(configuration);
+ }
+
+ private void initialize(CompareConfiguration configuration) {
+
+ Control tree= getControl();
+
+ tree.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
+
+ Composite parent= tree.getParent();
+
+ fBundle= ResourceBundle.getBundle("org.eclipse.compare.structuremergeviewer.DiffTreeViewerResources");
+
+ fCompareConfiguration= configuration;
+ if (fCompareConfiguration != null) {
+ fPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ DiffTreeViewer.this.propertyChange(event);
+ }
+ };
+
+ fCompareConfiguration.addPropertyChangeListener(fPropertyChangeListener);
+ }
+
+ setContentProvider(new DiffViewerContentProvider());
+ setLabelProvider(new DiffViewerLabelProvider());
+
+ addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent se) {
+ updateActions();
+ }
+ }
+ );
+
+ syncShowPseudoConflictFilter();
+
+ setSorter(new DiffViewerSorter());
+
+ ToolBarManager tbm= CompareViewerSwitchingPane.getToolBarManager(parent);
+ if (tbm != null) {
+ tbm.removeAll();
+
+ tbm.add(new Separator("merge"));
+ tbm.add(new Separator("modes"));
+ tbm.add(new Separator("navigation"));
+
+ createToolItems(tbm);
+ updateActions();
+
+ tbm.update(true);
+ }
+
+ MenuManager mm= new MenuManager();
+ mm.setRemoveAllWhenShown(true);
+ mm.addMenuListener(
+ new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mm) {
+ fillContextMenu(mm);
+ }
+ }
+ );
+ tree.setMenu(mm.createContextMenu(tree));
+ }
+
+ /**
+ * Returns the viewer's name.
+ *
+ * @return the viewer's name
+ */
+ public String getTitle() {
+ return "Structure Compare";
+ }
+
+ /**
+ * Returns the resource bundle.
+ *
+ * @return the viewer's resource bundle
+ */
+ protected ResourceBundle getBundle() {
+ return fBundle;
+ }
+
+ /**
+ * Returns the compare configuration of this viewer.
+ *
+ * @return the compare configuration of this viewer
+ */
+ public CompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * Called on the viewer disposal.
+ * Unregisters from the compare configuration.
+ * Clients may extend if they have to do additional cleanup.
+ */
+ protected void handleDispose(DisposeEvent event) {
+
+ if (fCompareConfiguration != null) {
+ if (fPropertyChangeListener != null)
+ fCompareConfiguration.removePropertyChangeListener(fPropertyChangeListener);
+ fCompareConfiguration= null;
+ }
+ fPropertyChangeListener= null;
+
+ super.handleDispose(event);
+ }
+
+ /**
+ * Tracks property changes of the configuration object.
+ * Clients may extend to track their own property changes.
+ */
+ protected void propertyChange(PropertyChangeEvent event) {
+
+ if (event.getProperty().equals(CompareConfiguration.SHOW_PSEUDO_CONFLICTS))
+ syncShowPseudoConflictFilter();
+ }
+
+ protected void inputChanged(Object in, Object oldInput) {
+ super.inputChanged(in, oldInput);
+ expandToLevel(2);
+ }
+
+ /**
+ * Overridden to avoid expanding <code>DiffNode</code>s that shouldn't expand
+ * (i.e. where the <code>dontExpand</code> method returns <code>true</code>).
+ */
+ protected void internalExpandToLevel(Widget node, int level) {
+
+ Object data= node.getData();
+ if (data instanceof DiffNode && ((DiffNode)data).dontExpand())
+ return;
+
+ super.internalExpandToLevel(node, level);
+ }
+
+ //---- merge action support
+
+ /**
+ * This factory method is called after the viewer's controls have been created.
+ * It installs four actions in the given <code>ToolBarManager</code>. Two actions
+ * allow for copying one side of a <code>DiffNode</code> to the other side.
+ * Two other actions are for navigating from one node to the next (previous).
+ * <p>
+ * Clients can override this method and are free to decide whether they want to call
+ * the inherited method.
+ *
+ * @param toolbarManager the toolbar manager for which to add the actions
+ */
+ protected void createToolItems(ToolBarManager toolbarManager) {
+
+ fCopyLeftToRightAction= new Action() {
+ public void run() {
+ copySelected(true);
+ }
+ };
+ Utilities.initAction(fCopyLeftToRightAction, fBundle, "action.TakeLeft.");
+ toolbarManager.appendToGroup("merge", fCopyLeftToRightAction);
+
+ fCopyRightToLeftAction= new Action() {
+ public void run() {
+ copySelected(false);
+ }
+ };
+ Utilities.initAction(fCopyRightToLeftAction, fBundle, "action.TakeRight.");
+ toolbarManager.appendToGroup("merge", fCopyRightToLeftAction);
+
+ fNextAction= new Action() {
+ public void run() {
+ navigate(true);
+ }
+ };
+ Utilities.initAction(fNextAction, fBundle, "action.NextDiff.");
+ toolbarManager.appendToGroup("navigation", fNextAction);
+
+ fPreviousAction= new Action() {
+ public void run() {
+ navigate(false);
+ }
+ };
+ Utilities.initAction(fPreviousAction, fBundle, "action.PrevDiff.");
+ toolbarManager.appendToGroup("navigation", fPreviousAction);
+ }
+
+ /**
+ * This method is called to add actions to the viewer's context menu.
+ * It installs actions for copying one side of a <code>DiffNode</code> to the other side.
+ * Clients can override this method and are free to decide whether they want to call
+ * the inherited method.
+ *
+ * @param manager the menu manager for which to add the actions
+ */
+ protected void fillContextMenu(IMenuManager manager) {
+ if (fCopyLeftToRightAction != null)
+ manager.add(fCopyLeftToRightAction);
+ if (fCopyRightToLeftAction != null)
+ manager.add(fCopyRightToLeftAction);
+ }
+
+ /**
+ * Copies one side of all <code>DiffNode</code>s in the current selection to the other side.
+ * Called from the (internal) actions for copying the sides of a <code>DiffNode</code>.
+ * Clients may override.
+ *
+ * @param leftToRight if <code>true</code> the left side is copied to the right side.
+ * If <code>false</code> the right side is copied to the left side
+ */
+ protected void copySelected(boolean leftToRight) {
+ ISelection selection= getSelection();
+ if (selection instanceof IStructuredSelection) {
+ Iterator e= ((IStructuredSelection) selection).iterator();
+ while (e.hasNext()) {
+ Object element= e.next();
+ if (element instanceof ICompareInput)
+ copyOne((ICompareInput) element, leftToRight);
+ }
+ }
+ }
+
+ /**
+ * Called to copy one side of the given node to the other.
+ * This default implementation delegates the call to <code>ICompareInput.copy(...)</code>.
+ * Clients may override.
+ *
+ * @param leftToRight if <code>true</code> the left side is copied to the right side.
+ * If <code>false</code> the right side is copied to the left side
+ */
+ protected void copyOne(ICompareInput node, boolean leftToRight) {
+
+ node.copy(leftToRight);
+
+ // update node's image
+ update(new Object[] { node }, null);
+ }
+
+ /**
+ * Selects the next (or previous) node of the current selection.
+ * If there is no current selection the first (last) node in the tree is selected.
+ * Wraps around at end or beginning.
+ * Clients may override.
+ *
+ * @param next if <code>true</code> the next node is selected, otherwise the previous node
+ */
+ protected void navigate(boolean next) {
+
+ Control c= getControl();
+ if (!(c instanceof Tree))
+ return;
+
+ Tree tree= (Tree) c;
+ TreeItem children[]= tree.getSelection();
+ TreeItem item= null;
+
+ if (children != null && children.length > 0)
+ item= children[0];
+
+ if (item != null) {
+ if (!next) {
+
+ TreeItem parent= item.getParentItem();
+ if (parent != null)
+ children= parent.getItems();
+ else
+ children= tree.getItems();
+
+ if (children != null && children.length > 0) {
+ // goto previous child
+ int index= 0;
+ for (; index < children.length; index++)
+ if (children[index] == item)
+ break;
+
+ if (index > 0) {
+
+ item= children[index-1];
+
+ while (true) {
+ int n= item.getItemCount();
+ if (n <= 0)
+ break;
+
+ item.setExpanded(true);
+ item= item.getItems()[n-1];
+ }
+
+ // previous
+ internalSetSelection(item);
+ return;
+ }
+ }
+
+ // go up
+ if (parent != null) {
+ internalSetSelection(parent);
+ return;
+ }
+ item= null;
+
+ } else {
+ item.setExpanded(true);
+ createChildren(item);
+
+ if (item.getItemCount() > 0) {
+ // has children: go down
+ children= item.getItems();
+ internalSetSelection(children[0]);
+ return;
+ }
+
+ while (item != null) {
+ children= null;
+ TreeItem parent= item.getParentItem();
+ if (parent != null)
+ children= parent.getItems();
+ else
+ children= tree.getItems();
+
+ if (children != null && children.length > 0) {
+ // goto next child
+ int index= 0;
+ for (; index < children.length; index++)
+ if (children[index] == item)
+ break;
+
+ if (index < children.length-1) {
+ // next
+ internalSetSelection(children[index+1]);
+ return;
+ }
+ }
+
+ // go up
+ item= parent;
+ }
+ }
+ }
+
+ // at end (or beginning): wrap around
+ if (item == null) {
+ children= tree.getItems();
+ if (children != null && children.length > 0)
+ internalSetSelection(children[next ? 0 : children.length-1]);
+ }
+ }
+
+ private void internalSetSelection(TreeItem ti) {
+ if (ti != null) {
+ Object data= ti.getData();
+ setSelection(new StructuredSelection(data));
+ }
+ }
+
+ //---- private
+
+ private void syncShowPseudoConflictFilter() {
+
+ boolean showPseudoConflicts= Utilities.getBoolean(fCompareConfiguration, CompareConfiguration.SHOW_PSEUDO_CONFLICTS, false);
+
+ if (showPseudoConflicts) {
+ if (fViewerFilter != null) {
+ removeFilter(fViewerFilter);
+ }
+ } else {
+ if (fViewerFilter == null)
+ fViewerFilter= new FilterSame();
+ addFilter(fViewerFilter);
+ }
+ }
+
+ private final boolean isEditable(Object element, boolean left) {
+ if (element instanceof ICompareInput) {
+ ICompareInput diff= (ICompareInput) element;
+ Object side= left ? diff.getLeft() : diff.getRight();
+ if (side == null && diff instanceof IDiffElement) {
+ IDiffContainer container= ((IDiffElement)diff).getParent();
+ if (container instanceof ICompareInput) {
+ ICompareInput parent= (ICompareInput) container;
+ side= left ? parent.getLeft() : parent.getRight();
+ }
+ }
+ if (side instanceof IEditableContent)
+ return ((IEditableContent) side).isEditable();
+ }
+ return false;
+ }
+
+ private void updateActions() {
+ int leftToRight= 0;
+ int rightToLeft= 0;
+ ISelection selection= getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss= (IStructuredSelection) selection;
+ Iterator e= ss.iterator();
+ while (e.hasNext()) {
+ Object element= e.next();
+ if (element instanceof ICompareInput) {
+ ICompareInput diff= (ICompareInput) element;
+ if (isEditable(element, false))
+ leftToRight++;
+ if (isEditable(element, true))
+ rightToLeft++;
+ if (leftToRight > 0 && rightToLeft > 0)
+ break;
+ }
+ }
+ }
+ if (fCopyLeftToRightAction != null)
+ fCopyLeftToRightAction.setEnabled(leftToRight > 0);
+ if (fCopyRightToLeftAction != null)
+ fCopyRightToLeftAction.setEnabled(rightToLeft > 0);
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewerResources.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewerResources.properties
new file mode 100644
index 000000000..50c6f69c4
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewerResources.properties
@@ -0,0 +1,50 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 1999, 2000
+# =====================================
+
+# @(#)DiffTreeViewerResources.properties
+#
+# Resource strings for DiffTreeViewer.java
+
+#####################################################
+# Images
+#####################################################
+
+#####################################################
+# Status messages
+#####################################################
+
+#####################################################
+# Dialogs
+#####################################################
+
+#####################################################
+# Actions
+#####################################################
+
+action.Smart.label=Smart
+action.Smart.tooltip=Try to guess corresponding elements
+action.Smart.image=ctool16/smart.gif
+
+action.FilterSame.label=Show pseudo conflicts
+action.FilterSame.tooltip=Show pseudo conflicts
+action.FilterSame.image=ctool16/showpseudoconflicts.gif
+
+action.NextDiff.label=Next
+action.NextDiff.tooltip=Select next change
+action.NextDiff.image=ctool16/next.gif
+
+action.PrevDiff.label=Previous
+action.PrevDiff.tooltip=Select previous change
+action.PrevDiff.image=ctool16/prev.gif
+
+action.TakeLeft.label=Copy Left to Right
+action.TakeLeft.tooltip=Copy selected nodes from left to right
+action.TakeLeft.image=ctool16/lefttoright.gif
+
+action.TakeRight.label=Copy Right to Left
+action.TakeRight.tooltip=Copy selected nodes from right to left
+action.TakeRight.image=ctool16/righttoleft.gif
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java
new file mode 100644
index 000000000..01ea51cc9
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java
@@ -0,0 +1,511 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+import org.eclipse.compare.*;
+
+
+/**
+ * A generic two-way or three-way differencing engine.
+ * <p>
+ * The engine is used by calling one of the <code>findDifferences</code> methods and passing
+ * in the objects to compare.
+ * The engine calls the following methods on the input objects to perform the compare:
+ * <UL>
+ * <LI><code>getChildren</code>: for enumerating the children of an object (if any),
+ * <LI><code>contentsEqual</code>: for comparing the content of leaf objects, that is, objects without children,
+ * <LI><code>visit</code>: for every pair of compared object the compare result is passed in.
+ * </UL>
+ * Clients may use as is, or subclass to provide a custom implementation for the three hooks.
+ * However the default implementation already deals with the typical case:
+ * <UL>
+ * <LI><code>getChildren</code>: tries to apply the <code>IStructureComparator</code>
+ * interface to enumerate the children,
+ * <LI><code>contentsEqual</code>: tries to apply the <code>IStreamContentAccessor</code> interface
+ * to perform a byte-wise content comparison,
+ * <LI><code>visit</code>: creates a <code>DiffNode</code> for any detected difference between the compared objects and
+ * links it under a parent node effectively creating a tree of differences.
+ * </UL>
+ * The different kind of changes detected by the engine are decoded as follows:
+ * In the two-way case only NO_CHANGE, ADDITION, DELETION, and CHANGE are used.
+ * In the three-way case these constants are bitwise ORed with one of directional constants
+ * LEFT, RIGHT, and CONFLICTING.
+ */
+public class Differencer {
+
+ // The kind of differences.
+ /**
+ * Difference constant (value 0) indicating no difference.
+ */
+ public static final int NO_CHANGE= 0;
+ /**
+ * Difference constant (value 1) indicating one side was added.
+ */
+ public static final int ADDITION= 1;
+ /**
+ * Difference constant (value 2) indicating one side was removed.
+ */
+ public static final int DELETION= 2;
+ /**
+ * Difference constant (value 3) indicating side changed.
+ */
+ public static final int CHANGE= 3;
+
+ /**
+ * Bit mask (value 3) for extracting the kind of difference.
+ */
+ public static final int CHANGE_TYPE_MASK= 3;
+
+ // The direction of a three-way change.
+ /**
+ * Three-way change constant (value 4) indicating a change on left side.
+ */
+ public static final int LEFT= 4;
+
+ /**
+ * Three-way change constant (value 8) indicating a change on right side.
+ */
+ public static final int RIGHT= 8;
+
+ /**
+ * Three-way change constant (value 12) indicating a change on left and
+ * right sides.
+ */
+ public static final int CONFLICTING= 12;
+
+ /**
+ * Bit mask (value 12) for extracting the direction of a three-way change.
+ */
+ public static final int DIRECTION_MASK= 12;
+
+ /**
+ * Constant (value 16) indicating a change on left and
+ * right side (with respect to ancestor) but left and right are identical.
+ */
+ public static final int PSEUDO_CONFLICT= 16;
+
+
+ static class Node {
+ List fChildren;
+ int fCode;
+ Object fAncestor;
+ Object fLeft;
+ Object fRight;
+
+ Node() {
+ }
+ Node(Node parent, Object ancestor, Object left, Object right) {
+ parent.add(this);
+ fAncestor= ancestor;
+ fLeft= left;
+ fRight= right;
+ }
+ void add(Node child) {
+ if (fChildren == null)
+ fChildren= new ArrayList();
+ fChildren.add(child);
+ }
+ Object visit(Differencer d, Object parent, int level) {
+ if (fCode == NO_CHANGE)
+ return null;
+ //dump(level);
+ Object data= d.visit(parent, fCode, fAncestor, fLeft, fRight);
+ if (fChildren != null) {
+ Iterator i= fChildren.iterator();
+ while (i.hasNext()) {
+ Node n= (Node) i.next();
+ n.visit(d, data, level+1);
+ }
+ }
+ return data;
+ }
+ private void dump(int level) {
+ String name= null;
+ if (fAncestor instanceof ITypedElement)
+ name= ((ITypedElement)fAncestor).getName();
+ if (name == null && fLeft instanceof ITypedElement)
+ name= ((ITypedElement)fLeft).getName();
+ if (name == null && fRight instanceof ITypedElement)
+ name= ((ITypedElement)fRight).getName();
+ if (name == null)
+ name= "???";
+
+ for (int i= 0; i < level; i++)
+ System.out.print(" ");
+
+ System.out.println(getDiffType(fCode) + name);
+ }
+ private String getDiffType(int code) {
+ String dir= " ";
+ switch (code & DIRECTION_MASK) {
+ case LEFT:
+ dir= ">";
+ break;
+ case RIGHT:
+ dir= "<";
+ break;
+ case CONFLICTING:
+ dir= "!";
+ break;
+ }
+ String change= "=";
+ switch (code & CHANGE_TYPE_MASK) {
+ case ADDITION:
+ change= "+";
+ break;
+ case DELETION:
+ change= "-";
+ break;
+ case CHANGE:
+ change= "#";
+ break;
+ }
+ return dir + change + " ";
+ }
+ }
+
+ /**
+ * Creates a new differencing engine.
+ */
+ public Differencer() {
+ }
+
+ /**
+ * Starts the differencing engine on the three input objects. If threeWay is <code>true</code> a
+ * three-way comparison is performed, otherwise a two-way compare (in the latter case the ancestor argument is ignored).
+ * The progress monitor is passed to the method <code>updateProgress</code> which is called for every node or
+ * leaf compare. The method returns the object that was returned from the top-most call to method <code>visit</code>.
+ * At most two of the ancestor, left, and right parameters are allowed to be <code>null</code>.
+ *
+ * @param threeWay if <code>true</code> a three-way comparison is performed, otherwise a two-way compare
+ * @param pm a progress monitor which is passed to method <code>updateProgress</code>
+ * @param data a client data that is passed to the top-level call to <code>visit</code>
+ * @param ancestor the ancestor object of the compare (may be <code>null</code>)
+ * @param left the left object of the compare
+ * @param right the right object of the compare
+ * @return the object returned from the top most call to method <code>visit</code>,
+ * possibly <code>null</code>
+ */
+ public Object findDifferences(boolean threeWay, IProgressMonitor pm, Object data, Object ancestor, Object left, Object right) {
+
+ Node root= new Node();
+
+ int code= traverse(threeWay, root, pm, threeWay ? ancestor : null, left, right);
+
+ if (code != NO_CHANGE) {
+ List l= root.fChildren;
+ if (l.size() > 0) {
+ Node first= (Node)l.get(0);
+ return first.visit(this, data, 0);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Traverse tree in postorder.
+ */
+ private int traverse(boolean threeWay, Node parent, IProgressMonitor pm, Object ancestor, Object left, Object right) {
+
+ Object[] ancestorChildren= getChildren(ancestor);
+ Object[] rightChildren= getChildren(right);
+ Object[] leftChildren= getChildren(left);
+
+ int code= NO_CHANGE;
+
+ Node node= new Node(parent, ancestor, left, right);
+
+ boolean content= true; // we reset this if we have at least one child
+
+ if (((threeWay && ancestorChildren != null) || !threeWay)
+ && rightChildren != null && leftChildren != null) {
+ // we only recurse down if no leg is null
+ // a node
+
+ Set allSet= new HashSet(20);
+ Map ancestorSet= null;
+ Map rightSet= null;
+ Map leftSet= null;
+
+ if (ancestorChildren != null) {
+ ancestorSet= new HashMap(10);
+ for (int i= 0; i < ancestorChildren.length; i++) {
+ Object ancestorChild= ancestorChildren[i];
+ ancestorSet.put(ancestorChild, ancestorChild);
+ allSet.add(ancestorChild);
+ }
+ }
+
+ if (rightChildren != null) {
+ rightSet= new HashMap(10);
+ for (int i= 0; i < rightChildren.length; i++) {
+ Object rightChild= rightChildren[i];
+ rightSet.put(rightChild, rightChild);
+ allSet.add(rightChild);
+ }
+ }
+
+ if (leftChildren != null) {
+ leftSet= new HashMap(10);
+ for (int i= 0; i < leftChildren.length; i++) {
+ Object leftChild= leftChildren[i];
+ leftSet.put(leftChild, leftChild);
+ allSet.add(leftChild);
+ }
+ }
+
+ Iterator e= allSet.iterator();
+ while (e.hasNext()) {
+ Object keyChild= e.next();
+
+ content= false;
+
+ if (pm != null) {
+
+ if (pm.isCanceled())
+ throw new OperationCanceledException("Compare cancelled");
+
+ updateProgress(pm, keyChild);
+ }
+
+ Object ancestorChild= ancestorSet != null ? ancestorSet.get(keyChild) : null;
+ Object leftChild= leftSet != null ? leftSet.get(keyChild) : null;
+ Object rightChild= rightSet != null ? rightSet.get(keyChild) : null;
+
+ int c= traverse(threeWay, node, pm, ancestorChild, leftChild, rightChild);
+
+ if ((c & CHANGE_TYPE_MASK) != NO_CHANGE) {
+ code|= CHANGE; // deletions and additions of child result in a change of the container
+ code|= (c & DIRECTION_MASK); // incoming & outgoing are just ored
+ }
+ }
+ }
+
+ if (content) // a leaf
+ code= compare(threeWay, ancestor, left, right);
+
+ node.fCode= code;
+
+ return code;
+ }
+
+ /**
+ * Called for every node or leaf comparison.
+ * The differencing engine passes in the input objects of the compare and the result of the compare.
+ * The data object is the value returned from a call to the <code>visit</code> method on the parent input.
+ * It can be considered the "parent" reference and is useful when building a tree.
+ * <p>
+ * The <code>Differencer</code> implementation returns a new
+ * <code>DiffNode</code> which is initialized with the corresponding values.
+ * Subclasses may override.
+ *
+ * @param data object returned from parent call to <code>visit</code>,
+ * possibly <code>null</code>
+ * @param result the result of the compare operation performed on the three inputs
+ * @param ancestor the compare ancestor of the left and right inputs
+ * @param left the left input to the compare
+ * @param right the right input to the compare
+ * @return the result, possibly <code>null</code>
+ */
+ protected Object visit(Object data, int result, Object ancestor, Object left, Object right) {
+ return new DiffNode((IDiffContainer) data, result, (ITypedElement)ancestor, (ITypedElement)left, (ITypedElement)right);
+ }
+
+ /**
+ * Performs a 2-way or 3-way compare of the given leaf elements and returns an integer
+ * describing the kind of difference.
+ */
+ private int compare(boolean threeway, Object ancestor, Object left, Object right) {
+
+ int description= NO_CHANGE;
+
+ if (threeway) {
+ if (ancestor == null) {
+ if (left == null) {
+ if (right == null) {
+ Assert.isTrue(false);
+ // shouldn't happen
+ } else {
+ description= RIGHT | ADDITION;
+ }
+ } else {
+ if (right == null) {
+ description= LEFT | ADDITION;
+ } else {
+ description= CONFLICTING | ADDITION;
+ if (contentsEqual(left, right))
+ description|= PSEUDO_CONFLICT;
+ }
+ }
+ } else {
+ if (left == null) {
+ if (right == null) {
+ description= CONFLICTING | DELETION | PSEUDO_CONFLICT;
+ } else {
+ if (contentsEqual(ancestor, right))
+ description= LEFT | DELETION;
+ else
+ description= CONFLICTING | CHANGE;
+ }
+ } else {
+ if (right == null) {
+ if (contentsEqual(ancestor, left))
+ description= RIGHT | DELETION;
+ else
+ description= CONFLICTING | CHANGE;
+ } else {
+ boolean ay= contentsEqual(ancestor, left);
+ boolean am= contentsEqual(ancestor, right);
+
+ if (ay && am)
+ ;
+ else if (ay && !am) {
+ description= RIGHT | CHANGE;
+ } else if (!ay && am) {
+ description= LEFT | CHANGE;
+ } else {
+ description= CONFLICTING | CHANGE;
+ if (contentsEqual(left, right))
+ description|= PSEUDO_CONFLICT;
+ }
+ }
+ }
+ }
+ } else { // two way compare ignores ancestor
+ if (left == null) {
+ if (right == null) {
+ Assert.isTrue(false);
+ // shouldn't happen
+ } else {
+ description= ADDITION;
+ }
+ } else {
+ if (right == null) {
+ description= DELETION;
+ } else {
+ if (! contentsEqual(left, right))
+ description= CHANGE;
+ }
+ }
+ }
+
+ return description;
+ }
+
+ /**
+ * Performs a content compare on the two given inputs.
+ * <p>
+ * The <code>Differencer</code> implementation
+ * returns <code>true</code> if both inputs implement <code>IStreamContentAccessor</code>
+ * and their byte contents is identical. Subclasses may override to implement
+ * a different content compare on the given inputs.
+ * </p>
+ *
+ * @param input1 first input to contents compare
+ * @param input2 second input to contents compare
+ * @return <code>true</code> if content is equal
+ */
+ protected boolean contentsEqual(Object input1, Object input2) {
+
+ if (input1 == input2)
+ return true;
+
+ InputStream is1= getStream(input1);
+ InputStream is2= getStream(input2);
+
+ if (is1 == null && is2 == null) // no byte contents
+ return true;
+
+ if (is1 == null || is2 == null) // only one has contents
+ return false;
+
+ try {
+ while (true) {
+ int c1= is1.read();
+ int c2= is2.read();
+ if (c1 == -1 && c2 == -1)
+ return true;
+ if (c1 != c2)
+ break;
+
+ }
+ } catch (IOException ex) {
+ } finally {
+ try {
+ is1.close();
+ } catch(IOException ex) {
+ }
+ try {
+ is2.close();
+ } catch(IOException ex) {
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tries to return an InputStream for the given object.
+ * Returns <code>null</code> if the object not an IStreamContentAccessor
+ * or an error occured.
+ */
+ private InputStream getStream(Object o) {
+ if (o instanceof IStreamContentAccessor) {
+ try {
+ return ((IStreamContentAccessor)o).getContents();
+ } catch(CoreException ex) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the children of the given input or <code>null</code> if there are no children.
+ * <p>
+ * The <code>Differencer</code> implementation checks whether the input
+ * implements the <code>IStructureComparator</code> interface. If yes it is used
+ * to return an array containing all children. Otherwise <code>null</code> is returned.
+ * Subclasses may override to implement a different strategy to enumerate children.
+ * </p>
+ *
+ * @param input the object for which to return children
+ */
+ protected Object[] getChildren(Object input) {
+ if (input instanceof IStructureComparator) {
+ Object[] children= ((IStructureComparator)input).getChildren();
+ if (children != null && children.length > 0)
+ return children;
+ }
+ return null;
+ }
+
+ /**
+ * Called for every leaf or node compare to update progress information.
+ * <p>
+ * The <code>Differencer</code> implementation shows the name of the input object
+ * as a subtask. Subclasses may override.
+ * </p>
+ *
+ * @param progressMonitor the progress monitor for reporting progress
+ * @name input a non-<code>null</code> input object
+ */
+ protected void updateProgress(IProgressMonitor progressMonitor, Object node) {
+ if (node instanceof ITypedElement) {
+ String name= ((ITypedElement)node).getName();
+ progressMonitor.subTask("Comparing " + name);
+ //progressMonitor.worked(1);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DocumentRangeNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DocumentRangeNode.java
new file mode 100644
index 000000000..6c69e1352
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DocumentRangeNode.java
@@ -0,0 +1,352 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.jface.text.*;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.*;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.contentmergeviewer.IDocumentRange;
+
+
+/**
+ * A document range node represents a structural element
+ * when performing a structure compare of documents.
+ * <code>DocumentRangeNodes</code> are created while parsing the document and represent
+ * a semantic entity (e.g. a Java class or method).
+ * As a consequence of the parsing a <code>DocumentRangeNode</code> maps to a range
+ * of characters in the document.
+ * <p>
+ * Since a <code>DocumentRangeNode</code> implements the <code>IStructureComparator</code>
+ * and <code>IStreamContentAccessor</code> interfaces it can be used as input to the
+ * differencing engine. This makes it possible to perform
+ * a structural diff on a document and have the nodes and leaves of the compare easily map
+ * to character ranges within the document.
+ * <p>
+ * Subclasses may add additional state collected while parsing the document.
+ * </p>
+ * @see Differencer
+ */
+public class DocumentRangeNode
+ implements IDocumentRange, IStructureComparator, IEditableContent, IStreamContentAccessor {
+
+ private static final boolean POS_UPDATE= true;
+
+ private IDocument fBaseDocument;
+ private Position fRange; // the range in the base document
+ private int fTypeCode;
+ private String fID;
+ private Position fAppendPosition; // a position where to insert a child textually
+ private ArrayList fChildren;
+
+ /**
+ * Creates a new <code>DocumentRangeNode</code> for the given range within the specified
+ * document. The <code>typeCode</code> is uninterpreted client data. The ID is used when comparing
+ * two nodes with each other: i.e. the differencing engine performs a content compare
+ * on two nodes if their IDs are equal.
+ *
+ * @param typeCode a type code for this node
+ * @param id an identifier for this node
+ * @param document document on which this node is based on
+ * @param start start position of range within document
+ * @param length length of range
+ */
+ public DocumentRangeNode(int typeCode, String id, IDocument document, int start, int length) {
+
+ fTypeCode= typeCode;
+ fID= id;
+
+ fBaseDocument= document;
+ fRange= new Position(start, length);
+
+ if (POS_UPDATE) {
+ try {
+ document.addPosition(fRange);
+ } catch (BadLocationException ex) {
+ }
+ }
+ }
+
+ /* (non Javadoc)
+ * see IDocumentRange.getDocument
+ */
+ public IDocument getDocument() {
+ return fBaseDocument;
+ }
+
+ /* (non Javadoc)
+ * see IDocumentRange.getRange
+ */
+ public Position getRange() {
+ return fRange;
+ }
+
+ /**
+ * Returns the type code of this node.
+ * The type code is uninterpreted client data which can be set in the constructor.
+ *
+ * @return the type code of this node
+ */
+ public int getTypeCode() {
+ return fTypeCode;
+ }
+
+ /**
+ * Returns this node's id.
+ * It is used in <code>equals</code> and <code>hashcode</code>.
+ *
+ * @return the node's id
+ */
+ public String getId() {
+ return fID;
+ }
+
+ /**
+ * Sets this node's id.
+ * It is used in <code>equals</code> and <code>hashcode</code>.
+ *
+ * @param id the new id for this node
+ */
+ public void setId(String id) {
+ fID= id;
+ }
+
+ /**
+ * Adds the given node as a child.
+ *
+ * @param node the node to add as a child
+ */
+ public void addChild(DocumentRangeNode node) {
+ if (fChildren == null)
+ fChildren= new ArrayList();
+ fChildren.add(node);
+ }
+
+ /* (non Javadoc)
+ * see IStructureComparator.getChildren
+ */
+ public Object[] getChildren() {
+ if (fChildren != null)
+ return fChildren.toArray();
+ return null;
+ }
+
+ /**
+ * Sets the length of the range of this node.
+ *
+ * @param length the length of the range
+ */
+ public void setLength(int length) {
+ getRange().setLength(length);
+ }
+
+ /**
+ * Sets a position within the document range that can be used to (legally) insert
+ * text without breaking the syntax of the document.
+ * <p>
+ * E.g. when parsing a Java document the "append position" of a <code>DocumentRangeNode</code>
+ * representating a Java class could be the character position just before the closing bracket.
+ * Inserting the text of a new method there would not disturb the syntax of the class.
+ *
+ * @param pos the character position within the underlying document where text can be legally inserted
+ */
+ public void setAppendPosition(int pos) {
+ if (POS_UPDATE) {
+ fBaseDocument.removePosition(fAppendPosition);
+ try {
+ Position p= new Position(pos);
+ fBaseDocument.addPosition(p);
+ fAppendPosition= p;
+ } catch (BadLocationException ex) {
+ System.out.println("setAppendPosition: BadLocationException");
+ }
+ } else {
+ fAppendPosition= new Position(pos);
+ }
+ }
+
+ /**
+ * Returns the position that has been set with <code>setAppendPosition</code>.
+ * If <code>setAppendPosition</code> hasn't been called, the position after the last character
+ * of this range is returned.
+ *
+ * @return a position where text can be legally inserted
+ */
+ public Position getAppendPosition() {
+ if (fAppendPosition == null) {
+ if (POS_UPDATE) {
+ try {
+ Position p= new Position(fBaseDocument.getLength());
+ fBaseDocument.addPosition(p);
+ fAppendPosition= p;
+ } catch (BadLocationException ex) {
+ System.out.println("setAppendPosition: BadLocationException");
+ }
+ } else {
+ fAppendPosition= new Position(fBaseDocument.getLength());
+ }
+ }
+ return fAppendPosition;
+ }
+
+ /**
+ * Implementation based on <code>getID</code>.
+ */
+ public boolean equals(Object other) {
+ if (other != null && other.getClass() == getClass()) {
+ DocumentRangeNode tn= (DocumentRangeNode) other;
+ return fTypeCode == tn.fTypeCode && fID.equals(tn.fID);
+ }
+ return super.equals(other);
+ }
+
+ /**
+ * Implementation based on <code>getID</code>.
+ */
+ public int hashCode() {
+ return fID.hashCode();
+ }
+
+ /**
+ * Find corresponding position
+ */
+ private Position findCorrespondingPosition(DocumentRangeNode otherParent, DocumentRangeNode child) {
+
+ //System.out.println("findCorrespondingPosition " + child);
+ // we try to find a predecessor of left Node which exists on the right side
+
+ if (child != null && fChildren != null) {
+ int ix= otherParent.fChildren.indexOf(child);
+ if (ix >= 0) {
+ //System.out.println(" found at position " + ix);
+
+ for (int i= ix - 1; i >= 0; i--) {
+ DocumentRangeNode c1= (DocumentRangeNode) otherParent.fChildren.get(i);
+ int i2= fChildren.indexOf(c1);
+ if (i2 >= 0) {
+ DocumentRangeNode c= (DocumentRangeNode) fChildren.get(i2);
+ //System.out.println(" found corresponding: " + i2 + " " + c);
+ Position p= c.fRange;
+
+ //try {
+ Position po= new Position(p.getOffset() + p.getLength() + 1, 0);
+ //c.fBaseDocument.addPosition(po);
+ return po;
+ //} catch (BadLocationException ex) {
+ //}
+ //break;
+ }
+ }
+
+ for (int i= ix; i < otherParent.fChildren.size(); i++) {
+ DocumentRangeNode c1= (DocumentRangeNode) otherParent.fChildren.get(i);
+ int i2= fChildren.indexOf(c1);
+ if (i2 >= 0) {
+ DocumentRangeNode c= (DocumentRangeNode) fChildren.get(i2);
+ //System.out.println(" found corresponding: " + i2 + " " + c);
+ Position p= c.fRange;
+ //try {
+ Position po= new Position(p.getOffset(), 0);
+ //c.fBaseDocument.addPosition(po);
+ return po;
+ //} catch (BadLocationException ex) {
+ //}
+ //break;
+ }
+ }
+
+ }
+ }
+ return getAppendPosition();
+ }
+
+ private void add(String s, DocumentRangeNode parent, DocumentRangeNode child) {
+
+ Position p= findCorrespondingPosition(parent, child);
+ if (p != null) {
+ try {
+ //System.out.println("inserting <"+s+"> at " + p.getOffset());
+ fBaseDocument.replace(p.getOffset(), p.getLength(), s);
+ } catch (BadLocationException ex) {
+ System.out.println("BadLocationException " + ex);
+ }
+ }
+ }
+
+ /* (non Javadoc)
+ * see IStreamContentAccessor.getContents
+ */
+ public InputStream getContents() {
+ String s;
+ try {
+ s= fBaseDocument.get(fRange.getOffset(), fRange.getLength());
+ } catch (BadLocationException ex) {
+ s= "";
+ }
+ return new ByteArrayInputStream(s.getBytes());
+ }
+
+ /* (non Javadoc)
+ * see IEditableContent.isEditable
+ */
+ public boolean isEditable() {
+ return true;
+ }
+
+ /* (non Javadoc)
+ * see IEditableContent.replace
+ */
+ public ITypedElement replace(ITypedElement child, ITypedElement other) {
+
+ DocumentRangeNode src= null;
+ String srcContents= "";
+
+ if (other != null) {
+ src= (DocumentRangeNode) child;
+
+ if (other instanceof IStreamContentAccessor) {
+ try {
+ InputStream is= ((IStreamContentAccessor)other).getContents();
+ byte[] bytes= Utilities.readBytes(is);
+ srcContents= new String(bytes);
+ } catch(CoreException ex) {
+ }
+ }
+ }
+
+ if (child != null) { // there is a destination
+
+ if (child instanceof DocumentRangeNode) {
+ DocumentRangeNode drn= (DocumentRangeNode) child;
+
+ IDocument doc= drn.getDocument();
+ Position range= drn.getRange();
+ try {
+ doc.replace(range.getOffset(), range.getLength(), srcContents);
+ } catch (BadLocationException ex) {
+ }
+ }
+ } else {
+ // no destination: we have to add the contents into the parent
+ add(srcContents, null /*srcParentNode*/, src);
+ }
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IEditableContent.setContent
+ */
+ public void setContent(byte[] content) {
+ fBaseDocument.set(new String(content));
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInput.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInput.java
new file mode 100644
index 000000000..4a227242d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInput.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Interface for objects used as input to a two-way or three-way compare viewer.
+ * It defines API for accessing the three sides for the compare,
+ * and a name and image which is used when displaying the three way input
+ * in the UI, for example, in a title bar.
+ * <p>
+ * Note: at most two sides of an <code>ICompareInput</code> can be <code>null</code>,
+ * (as it is normal for additions or deletions) but not all three.
+ * <p>
+ * <code>ICompareInput</code> provides methods for registering
+ * <code>ICompareInputChangeListener</code>s
+ * that get informed if one (or more)
+ * of the three sides of an <code>ICompareInput</code> object changes its value.
+ * <p>
+ * For example when accepting an incoming addition
+ * the (non-<code>null</code>) left side of an <code>ICompareInput</code>
+ * is copied to the right side by means of method <code>copy</code>.
+ * This should trigger a call to <code>compareInputChanged</code> of registered
+ * <code>ICompareInputChangeListener</code>s.
+ * <p>
+ * Clients can implement this interface, or use the convenience implementation
+ * <code>DiffNode</code>.
+ * </p>
+ *
+ * @see StructureDiffViewer
+ * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer
+ * @see DiffNode
+ */
+public interface ICompareInput {
+
+ /**
+ * Returns name of input.
+ * This name is displayed when this input is shown in a viewer.
+ * In many cases this name is the name of one of the non-<code>null</code> sides or a combination
+ * thereof.
+ *
+ * @return name of input
+ */
+ String getName();
+
+ /**
+ * Returns an image representing this input.
+ * This image is typically displayed when this input is shown in a viewer.
+ * In many cases this image is the image of one of the non-<code>null</code> sides.
+ *
+ * @return image representing this input, or <code>null</code> if no icon should be shown
+ */
+ Image getImage();
+
+ /**
+ * Returns the kind of difference between the
+ * three sides ancestor, left and right.
+ * This field is only meaningful if the <code>ICompareInput</code>
+ * is the result of another compare. In this case it is used
+ * together with <code>getImage</code> to compose a icon
+ * which reflects the kind of difference between the two or three elements.
+ *
+ * @return kind of difference (see <code>Differencer</code>)
+ */
+ int getKind();
+
+ /**
+ * Returns the ancestor side of this input.
+ * Returns <code>null</code> if this input has no ancestor
+ * or in the two-way compare case.
+ *
+ * @return the ancestor of this input, or <code>null</code>
+ */
+ ITypedElement getAncestor();
+
+ /**
+ * Returns the left side of this input.
+ * Returns <code>null</code> if there is no left side (deletion or addition).
+ *
+ * @return the left side of this input, or <code>null</code>
+ */
+ ITypedElement getLeft();
+
+ /**
+ * Returns the right side of this input.
+ * Returns <code>null</code> if there is no right side (deletion or addition).
+ *
+ * @return the right side of this input, or <code>null</code>
+ */
+ ITypedElement getRight();
+
+ /**
+ * Registers the given listener for notification.
+ * If the identical listener is already registered the method has no effect.
+ *
+ * @param listener the listener to register for changes of this input
+ */
+ void addCompareInputChangeListener(ICompareInputChangeListener listener);
+
+ /**
+ * Unregisters the given listener.
+ * If the identical listener is not registered the method has no effect.
+ *
+ * @param listener the listener to unregister
+ */
+ void removeCompareInputChangeListener(ICompareInputChangeListener listener);
+
+ /**
+ * Copy one side (source) to the other side (destination) depending on the
+ * value of <code>leftToRight</code>. This method is called from
+ * a merge viewer if a corresponding action ("take left" or "take right")
+ * has been pressed.
+ * <p>
+ * The implementation should handle the following cases:
+ * <UL>
+ * <LI>
+ * if the source side is <code>null</code> the destination must be deleted,
+ * <LI>
+ * if the destination is <code>null</code> the destination must be created
+ * and filled with the contents from the source,
+ * <LI>
+ * if both sides are non-<code>null</code> the contents of source must be copied to destination.
+ * </UL>
+ * In addition the implementation should send out notification to the registered
+ * <code>ICompareInputChangeListener</code>.
+ *
+ * @param leftToRight if <code>true</code> the left side is copied to the right side.
+ * If <code>false</code> the right side is copied to the left side
+ */
+ void copy(boolean leftToRight);
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInputChangeListener.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInputChangeListener.java
new file mode 100644
index 000000000..e182ad749
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInputChangeListener.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+/**
+ * Listener that gets informed if one (or more)
+ * of the three sides of an <code>ICompareInput</code> object changes its value.
+ * <p>
+ * For example when accepting an incoming addition
+ * the (non-null) left side of an <code>ICompareInput</code>
+ * is copied to the right side (which was <code>null</code>).
+ * This triggers a call to <code>compareInputChanged</code> of registered
+ * <code>ICompareInputChangeListener</code>.
+ * <p>
+ * Note however, that listener are not informed if the content of one of the sides changes.
+ * <p>
+ * Clients may implement this interface. It is also implemented by viewers that take
+ * an <code>ICompareInput</code> as input.
+ * </p>
+ */
+public interface ICompareInputChangeListener {
+
+ /**
+ * Called whenever the value (not the content) of one or more of the three sides
+ * of a <code>ICompareInput</code> has changed.
+ *
+ * @param source the <code>ICompareInput</code> that has changed
+ */
+ void compareInputChanged(ICompareInput source);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffContainer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffContainer.java
new file mode 100644
index 000000000..376a18178
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffContainer.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import java.util.Iterator;
+
+/**
+ * <code>IDiffContainer</code> is a <code>IDiffElement</code> with children.
+ * <p>
+ * <code>IDiffContainer</code> are the inner nodes displayed
+ * by the <code>DiffTreeViewer</code>.
+ * <code>IDiffContainer</code> are typically created as the result of performing
+ * a compare with the <code>Differencer</code>.
+ * <p>
+ * Clients may implement this interface, or use one of the standard implementations,
+ * <code>DiffContainer</code> or <code>DiffNode</code>.
+ *
+ * @see Differencer
+ * @see DiffTreeViewer
+ */
+public interface IDiffContainer extends IDiffElement {
+
+ /**
+ * Returns whether this container has at least one child.
+ * In some cases this methods avoids having to call the
+ * potential more costly <code>getChildren</code> method.
+ *
+ * @return <code>true</code> if this container has at least one child
+ */
+ boolean hasChildren();
+
+ /**
+ * Returns the children of this container.
+ * If this container has no children an empty array is returned (not <code>null</code>).
+ *
+ * @return the children of this container as an array
+ */
+ IDiffElement[] getChildren();
+
+ /**
+ * Adds the given child to this container.
+ * If the child is already contained in this container, this method has no effect.
+ *
+ * @param child the child to be added to this container
+ */
+ void add(IDiffElement child);
+
+ /**
+ * Removes the given child from this container.
+ * If the container becomes empty it is removed from its container.
+ * If the child is not contained in this container, this method has no effect.
+ *
+ * @param child the child to be removed from this container
+ */
+ void removeToRoot(IDiffElement child);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffElement.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffElement.java
new file mode 100644
index 000000000..8bff24bf4
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffElement.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import org.eclipse.compare.ITypedElement;
+
+/**
+ * An <code>IDiffElement</code> is used in the <code>DiffTreeViewer</code>
+ * to display the kind of change detected as the result of a two-way or three-way compare.
+ * <p>
+ * The base interface <code>ITypedElement</code> provides a name, a type, and an image.
+ * <code>IDiffElement</code> adds API for maintaining a parent relationship.
+ * <p>
+ * <code>DiffTreeViewer</code> works on a tree of <code>IDiffElements</code>.
+ * Leaf elements must implement the
+ * <code>IDiffElement</code> interface, inner nodes the <code>IDiffContainer</code> interface.
+ * <p>
+ * <code>IDiffElement</code>s are typically created as the result of performing
+ * a compare with the <code>Differencer</code>.
+ * <p>
+ * Clients may implement this interface, or use one of the standard implementations,
+ * <code>DiffElement</code>, <code>DiffContainer</code>, or <code>DiffNode</code>.
+ *
+ * @see DiffTreeViewer
+ * @see DiffElement
+ * @see DiffContainer
+ * @see DiffNode
+ */
+public interface IDiffElement extends ITypedElement {
+
+ /**
+ * Returns the kind of difference as defined in <code>Differencer</code>.
+ *
+ * @return the kind of difference as defined in <code>Differencer</code>
+ */
+ int getKind();
+
+ /**
+ * Returns the parent of this element.
+ * If the object is the root of a hierarchy <code>null</code> is returned.
+ *
+ * @return the parent of this element, or <code>null</code> if the element has no parent
+ */
+ IDiffContainer getParent();
+
+ /**
+ * Sets the parent of this element.
+ *
+ * @param parent the new parent of this element, or <code>null</code> if this
+ * element is to have no parent
+ */
+ void setParent(IDiffContainer parent);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureComparator.java
new file mode 100644
index 000000000..185dc4d60
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureComparator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import java.util.Iterator;
+
+import org.eclipse.compare.ITypedElement;
+
+/**
+ * Interface used to compare hierarchical structures.
+ * It is used by the differencing engine.
+ * <p>
+ * Clients typically implement this interface in an adaptor class which
+ * wrappers the objects to be compared.
+ *
+ * @see org.eclipse.compare.ResourceNode
+ * @see Differencer
+ */
+public interface IStructureComparator {
+
+ /**
+ * Returns an iterator for all children of this object or <code>null</code>
+ * if there are no children.
+ *
+ * @return an array with all children of this object, or an empty array if there are no children
+ */
+ Object[] getChildren();
+
+ /**
+ * Returns whether some other object is "equal to" this one
+ * with respect to a structural comparison. For example, when comparing
+ * Java class methods, <code>equals</code> would return <code>true</code>
+ * if two methods have the same signature (the argument names and the
+ * method body might differ).
+ *
+ * @param other the reference object with which to compare
+ * @return <code>true</code> if this object is the same as the other argument; <code>false</code> otherwise
+ * @see java.lang.Object#equals
+ */
+ boolean equals(Object other);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureCreator.java
new file mode 100644
index 000000000..ccfd6ff2c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureCreator.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * For creating a hierarchical structure of <code>IStructureComparators</code> for a
+ * given input object.
+ * In addition, it provides methods for locating a path in the hierarchical structure
+ * and to map a node of this structure back to the corresponding input object.
+ * <p>
+ * Structure creators are used in the following contexts:
+ * <ul>
+ * <li>
+ * the <code>StructureDiffViewer</code> uses an <code>IStructureCreator</code> to
+ * build two (or three) tree structures of its input elements (method <code>getStructure</code>).
+ * These trees are then compared with each other by means of the differencing engine and displayed
+ * with the <code>DiffTreeViewer</code>,
+ * </li>
+ * <li>
+ * the <code>ReplaceWithEditionDialog</code> uses an <code>IStructureCreator</code>
+ * to map a path back to a range of characters in the textual representation.
+ * </li>
+ * </ul>
+ * A <code>IStructureCreator</code> provides methods for rewriting the tree produced by the differencing
+ * engine to support "smart" structural differencing. E.g. certain patterns of pairs of "addition"
+ * and "deletion" nodes can be detected as renames and merged into a single node.
+ * </p>
+ * <p>
+ * Clients may implement this interface; there is no standard implementation.
+ * </p>
+ *
+ * @see StructureDiffViewer
+ * @see org.eclipse.compare.EditionSelectionDialog
+ * @see Differencer
+ */
+public interface IStructureCreator {
+
+ /**
+ * Returns a descriptive name which can be used in the UI of the <code>StructureDiffViewer</code>.
+ *
+ * @return a descriptive name for this <code>IStructureCreator</code>
+ */
+ String getName();
+
+ /**
+ * Creates a tree structure consisting of <code>IStructureComparator</code>s
+ * from the given object and returns its root object.
+ * Implementing this method typically involves parsing the input object.
+ * In case of an error (e.g. a parsing error) the value <code>null</code> is returned.
+ *
+ * @param input the object from which to create the tree of <code>IStructureComparator</code>
+ * @return the root node of the structure or <code>null</code> in case of error
+ */
+ IStructureComparator getStructure(Object input);
+
+ /**
+ * Creates the single node specified by path from the given input object.
+ * In case of an error (e.g. a parsing error) the value <code>null</code> is returned.
+ * This method is similar to <code>getStructure</code> but in
+ * contrast to <code>getStructure</code> only a single node without any children must be returned.
+ * This method is used in the <code>ReplaceWithEditionDialog</code> to locate a sub element
+ * (e.g. a method) within an input object (e.g. a file containing source code).
+ * <p>
+ * One (not optimized) approach to implement this method is calling <code>getStructure(input)</code>
+ * to build the full tree, and then finding that node within the tree that is specified
+ * by <code>path</code>.
+ * <p>
+ * The syntax of <code>path</code> is not specified, because it is treated by the compare subsystem
+ * as an opaque entity and is not further interpreted. Clients using this functionality
+ * will pass a value of <code>path</code> to the <code>selectEdition</code>
+ * method of <code>ReplaceWithEditionDialog</code> and will receive this value unchanged
+ * as an argument to <code>locate</code>.
+ *
+ * @param path specifies a sub object within the input object
+ * @param input the object from which to create the <code>IStructureComparator</code>
+ * @return the single node specified by <code>path</code> or <code>null</code>
+ *
+ * @see org.eclipse.compare.EditionSelectionDialog#selectEdition
+ */
+ IStructureComparator locate(Object path, Object input);
+
+ /**
+ * Returns the contents of the given node as a string for the purpose
+ * of performing a content comparison only (that is the string will not be visible in the UI).
+ * If <code>ignoreWhitespace</code> is <code>true</code> all character sequences considered
+ * whitespace should be removed from the returned string.
+ *
+ * @param node the node for which to return a string representation
+ * @param ignoreWhitespace if <code>true</code> the returned string should not contain whitespace
+ * @return the string contents of the given node
+ */
+ String getContents(Object node, boolean ignoreWhitespace);
+
+ /**
+ * Returns whether this structure creator supports tree rewriting.
+ * The <code>StructureDiffViewer</code> uses this method to determine
+ * whether to enable a "smart" button in the UI and whether to call
+ * the <code>rewriteTree</code> method.
+ *
+ * @return <code>true</code> if this structure creator supports tree rewriting
+ */
+ boolean canRewriteTree();
+
+ /**
+ * Rewrites the tree.
+ * This method is only called if <code>canRewriteTree</code>
+ * returns <code>true</code>, and only after the difference engine has constructed the diff tree.
+ * Implementation of this method may perform tree rewriting such as merging
+ * separate but related diff elements into a single node.
+ *
+ * @param differencer the differencing engine which was used to construct the diff tree
+ * @param root the root of the tree returned from the differencing engine
+ */
+ void rewriteTree(Differencer differencer, IDiffContainer root);
+
+ /**
+ * FIXME: need better name?
+ * Called whenever a copy operation has been performed on a tree node.
+ *
+ * @param node the node for which to save the new content
+ * @param input the object from which the structure tree was created in <code>getStructure</code>
+ */
+ void save(IStructureComparator node, Object input);
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java
new file mode 100644
index 000000000..07296cccf
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java
@@ -0,0 +1,336 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.structuremergeviewer;
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.text.*;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.internal.*;
+
+
+/**
+ * A diff tree viewer that can be configured with a <code>IStructureCreator</code>
+ * to retrieve a hierarchical structure from the input object (an <code>ICompareInput</code>)
+ * and perform a two-way or three-way compare on it.
+ * <p>
+ * This <code>DiffTreeViewer</code> supports the so called "smart" mode of the structure creator
+ * by installing a button in the viewer's pane title bar.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed outside
+ * this package.
+ * </p>
+ *
+ * @see IStructureCreator
+ * @see ICompareInput
+ */
+public class StructureDiffViewer extends DiffTreeViewer {
+
+ private static final String SMART= "SMART";
+
+ private Differencer fDifferencer;
+ private boolean fThreeWay= false;
+
+ private ITypedElement fAncestorInput;
+ private ITypedElement fLeftInput;
+ private ITypedElement fRightInput;
+
+ private IStructureComparator fAncestorStructure;
+ private IStructureComparator fLeftStructure;
+ private IStructureComparator fRightStructure;
+
+ private IStructureCreator fStructureCreator;
+ private IDiffContainer fRoot;
+ private ChangePropertyAction fSmartAction;
+ private IContentChangeListener fContentChangedListener;
+ private ICompareInputChangeListener fThreeWayInputChangedListener;
+
+ /**
+ * Creates a new viewer for the given SWT tree control with the specified configuration.
+ *
+ * @param tree the tree control
+ * @param configuration the configuration for this viewer
+ */
+ public StructureDiffViewer(Tree tree, CompareConfiguration configuration) {
+ super(tree, configuration);
+ initialize();
+ }
+
+ /**
+ * Creates a new viewer under the given SWT parent with the specified configuration.
+ *
+ * @param parent the SWT control under which to create the viewer
+ * @param configuration the configuration for this viewer
+ */
+ public StructureDiffViewer(Composite parent, CompareConfiguration configuration) {
+ super(parent, configuration);
+ initialize();
+ }
+
+ private void initialize() {
+
+ setAutoExpandLevel(3);
+
+ fContentChangedListener= new IContentChangeListener() {
+ public void contentChanged(IContentChangeNotifier changed) {
+ StructureDiffViewer.this.contentChanged(changed);
+ }
+ };
+ fThreeWayInputChangedListener= new ICompareInputChangeListener() {
+ public void compareInputChanged(ICompareInput input) {
+ StructureDiffViewer.this.compareInputChanged(input);
+ }
+ };
+ }
+
+ /**
+ * Configures the <code>StructureDiffViewer</code> with a structure creator.
+ * The structure creator is used to create a hierarchical structure
+ * for each side of the viewer's input element of type <code>ICompareInput</code>.
+ * <p>
+ * If the structure creator's <code>canRewriteTree</code> returns <code>true</code>
+ * the "smart" button in the viewer's pane control bar is enabled.
+ *
+ * @param structureCreator the new structure creator
+ */
+ public void setStructureCreator(IStructureCreator structureCreator) {
+ if (fStructureCreator != structureCreator) {
+ fStructureCreator= structureCreator;
+
+ if (fStructureCreator != null) {
+ if (fSmartAction != null)
+ fSmartAction.setEnabled(fStructureCreator.canRewriteTree());
+ // FIXME: if there is an input we should create the trees!
+ }
+ }
+ }
+
+ /**
+ * Returns the structure creator or <code>null</code> if no
+ * structure creator has been set with <code>setStructureCreator</code>.
+ *
+ * @return the structure creator or <code>null</code>
+ */
+ public IStructureCreator getStructureCreator() {
+ return fStructureCreator;
+ }
+
+ /**
+ * Reimplemented to get the descriptive title for this viewer from the <code>IStructureCreator</code>.
+ */
+ public String getTitle() {
+ if (fStructureCreator != null)
+ return fStructureCreator.getName();
+ return super.getTitle();
+ }
+
+ /**
+ * Overridden because the input of this viewer is not identical to the root of the tree.
+ * The tree's root is a IDiffContainer that was returned from the method <code>diff</code>.
+ *
+ * @return the root of the diff tree produced by method <code>diff</code>
+ */
+ protected Object getRoot() {
+ return fRoot;
+ }
+
+ /**
+ * Overridden to create the comparable structures from the input object
+ * and to feed them through the differencing engine. Note: for this viewer
+ * the value from <code>getInput</code> is not identical to <code>getRoot</code>.
+ */
+ protected void inputChanged(Object input, Object oldInput) {
+ if (input instanceof ICompareInput) {
+ compareInputChanged((ICompareInput) input);
+ diff();
+ expandToLevel(3);
+ }
+ }
+
+ /* (non Javadoc)
+ * Overridden to unregister all listeners.
+ */
+ protected void handleDispose(DisposeEvent event) {
+
+ compareInputChanged(null);
+
+ fContentChangedListener= null;
+ fThreeWayInputChangedListener= null;
+
+ super.handleDispose(event);
+ }
+
+ /**
+ * Recreates the comparable structures for the input sides.
+ */
+ private void compareInputChanged(ICompareInput input) {
+ ITypedElement t= null;
+
+ if (input != null)
+ t= input.getAncestor();
+ fThreeWay= t != null;
+ if (t != fAncestorInput) {
+ if (fAncestorInput instanceof IContentChangeNotifier)
+ ((IContentChangeNotifier)fAncestorInput).removeContentChangeListener(fContentChangedListener);
+ fAncestorInput= t;
+ if (fAncestorInput != null)
+ fAncestorStructure= fStructureCreator.getStructure(fAncestorInput);
+ else
+ fAncestorStructure= null;
+ if (fAncestorInput instanceof IContentChangeNotifier)
+ ((IContentChangeNotifier)fAncestorInput).addContentChangeListener(fContentChangedListener);
+ }
+
+ if (input != null)
+ t= input.getLeft();
+ if (t != fLeftInput) {
+ if (fLeftInput instanceof IContentChangeNotifier)
+ ((IContentChangeNotifier)fLeftInput).removeContentChangeListener(fContentChangedListener);
+ fLeftInput= t;
+ if (fLeftInput != null)
+ fLeftStructure= fStructureCreator.getStructure(fLeftInput);
+ else
+ fLeftStructure= null;
+ if (fLeftInput instanceof IContentChangeNotifier)
+ ((IContentChangeNotifier)fLeftInput).addContentChangeListener(fContentChangedListener);
+ }
+
+ if (input != null)
+ t= input.getRight();
+ if (t != fRightInput) {
+ if (fRightInput instanceof IContentChangeNotifier)
+ ((IContentChangeNotifier)fRightInput).removeContentChangeListener(fContentChangedListener);
+ fRightInput= t;
+ if (fRightInput != null)
+ fRightStructure= fStructureCreator.getStructure(fRightInput);
+ else
+ fRightStructure= null;
+ if (fRightInput instanceof IContentChangeNotifier)
+ ((IContentChangeNotifier)fRightInput).addContentChangeListener(fContentChangedListener);
+ }
+ }
+
+ /**
+ * Calls <code>diff</code> whenever the byte contents changes.
+ */
+ private void contentChanged(IContentChangeNotifier changed) {
+
+ if (changed == fAncestorInput) {
+ IStructureComparator drn= fStructureCreator.getStructure(fAncestorInput);
+// if (drn == null)
+// return;
+ fAncestorStructure= drn;
+ } else if (changed == fLeftInput) {
+ IStructureComparator drn= fStructureCreator.getStructure(fLeftInput);
+// if (drn == null)
+// return;
+ fLeftStructure= drn;
+ } else if (changed == fRightInput) {
+ IStructureComparator drn= fStructureCreator.getStructure(fRightInput);
+// if (drn == null)
+// return;
+ fRightStructure= drn;
+ } else
+ return;
+
+ diff();
+ }
+
+ /**
+ * Runs the difference engine and refreshes the tree.
+ */
+ private void diff() {
+
+ if ((fThreeWay && fAncestorStructure == null) || fLeftStructure == null || fRightStructure == null) {
+ // could not get structure of one (or more) of the legs
+ fRoot= null;
+
+ } else { // calculate difference of the two (or three) structures
+
+ if (fDifferencer == null)
+ fDifferencer= new Differencer() {
+ protected boolean contentsEqual(Object o1, Object o2) {
+ return StructureDiffViewer.this.contentsEqual(o1, o2);
+ }
+ };
+
+ fRoot= (IDiffContainer) fDifferencer.findDifferences(fThreeWay, null, null,
+ fAncestorStructure, fLeftStructure, fRightStructure);
+
+ if (fStructureCreator.canRewriteTree()) {
+ boolean smart= Utilities.getBoolean(getCompareConfiguration(), SMART, false);
+ if (smart)
+ fStructureCreator.rewriteTree(fDifferencer, fRoot);
+ }
+ }
+ refresh(getRoot());
+ }
+
+ /**
+ * Performs a byte compare on the given objects.
+ * Called from the difference engine.
+ * Returns <code>null</code> if no structure creator has been set.
+ */
+ private boolean contentsEqual(Object o1, Object o2) {
+ if (fStructureCreator != null) {
+ boolean ignoreWhiteSpace= Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false);
+ String s1= fStructureCreator.getContents(o1, ignoreWhiteSpace);
+ String s2= fStructureCreator.getContents(o2, ignoreWhiteSpace);
+ return s1.equals(s2);
+ }
+ return false;
+ }
+
+ /**
+ * Tracks property changes of the configuration object.
+ * Clients may override to track their own property changes.
+ * In this case they must call the inherited method.
+ */
+ protected void propertyChange(PropertyChangeEvent event) {
+ String key= event.getProperty();
+ if (key.equals(CompareConfiguration.IGNORE_WHITESPACE) || key.equals(SMART))
+ diff();
+ else
+ super.propertyChange(event);
+ }
+
+ /**
+ * Overriden to create a "smart" button in the viewer's pane control bar.
+ * <p>
+ * Clients can override this method and are free to decide whether they want to call
+ * the inherited method.
+ *
+ * @param toolbarManager the toolbar manager for which to add the buttons
+ */
+ protected void createToolItems(ToolBarManager toolBarManager) {
+
+ super.createToolItems(toolBarManager);
+
+ fSmartAction= new ChangePropertyAction(getBundle(), getCompareConfiguration(), "action.Smart.", SMART);
+ toolBarManager.appendToGroup("modes", fSmartAction);
+ }
+
+ /**
+ * Overridden to call the <code>save</code> method on the structure creator after
+ * nodes have been copied from one side to the other side of an input object.
+ *
+ * @param leftToRight if <code>true</code> the left side is copied to the right side.
+ * If <code>false</code> the right side is copied to the left side
+ */
+ protected void copySelected(boolean leftToRight) {
+ super.copySelected(leftToRight);
+
+ if (fStructureCreator != null)
+ fStructureCreator.save(leftToRight ? fRightStructure : fLeftStructure,
+ leftToRight ? fRightInput : fLeftInput);
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/package.html b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/package.html
new file mode 100644
index 000000000..e8567dda6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/package.html
@@ -0,0 +1,49 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides support for finding and displaying the differences
+between hierarchically structured data.
+<h2>
+Package Specification</h2>
+The class <tt>Differencer</tt> is a differencing engine for hierarchically
+structured data. It takes two or three inputs and performs a two-way or
+three-way compare on them.
+<p>If the input elements to the differencing engine implement the <tt>IStructureComparator</tt>
+interface the engine recursively applies itself&nbsp; to the children of
+the input element. Leaf elements must implement the <tt>IStreamContentAccessor</tt>
+interface (see package <tt>org.eclipse.compare</tt>) so that the
+differencer can perform a bytewise comparison on them.
+<p>By default the differencing engine returns the result of the compare
+as a tree of <tt>DiffNode</tt> objects. Every <tt>DiffNode</tt> describes
+the changes among the two or three inputs.
+<p>A tree of <tt>DiffNodes</tt> can be displayed in a <tt>DiffTreeViewer</tt>.
+The <tt>DiffTreeViewer</tt> requires that inner nodes of the tree implement
+the <tt>IDiffContainer</tt> interface and leafs the <tt>IDiffElement</tt>
+interface.
+<p>The typical steps to compare hierarchically structured data and to display
+the differences would be to:
+<blockquote>
+<li>
+map the input data into a tree of <tt>IStructureComparator</tt> and <tt>IStreamContentAccessor</tt>s,</li>
+
+<li>
+perform the compare operation by means of the <tt>Differencer</tt>, and</li>
+
+<li>
+feed the differencing result into the <tt>DiffTreeViewer</tt>.</li>
+</blockquote>
+The <tt>StructureDiffViewer</tt> is a specialized <tt>DiffTreeViewer</tt>
+that automates the three steps from above. It takes a single input object
+of type <tt>ICompareInput</tt> from which it retrieves the two or three
+input elements to compare. Then it uses a <tt>IStructureCreator</tt> to
+extract a tree of <tt>IStructureComparator</tt> and <tt>IStreamContentAccessor</tt>
+from them. These trees are then compared with the differencing engine and
+the result is displayed in the tree viewer.
+</body>
+</html>

Back to the top