/* | |
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved. | |
* This file is made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*/ | |
package org.eclipse.compare.structuremergeviewer; | |
import org.eclipse.swt.events.DisposeEvent; | |
import org.eclipse.swt.widgets.*; | |
import org.eclipse.jface.util.PropertyChangeEvent; | |
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 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 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 IContentChangeListener fContentChangedListener; | |
private ICompareInputChangeListener fThreeWayInputChangedListener; | |
private CompareViewerSwitchingPane fParent; | |
/** | |
* 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); | |
Composite c= tree.getParent(); | |
if (c instanceof CompareViewerSwitchingPane) | |
fParent= (CompareViewerSwitchingPane) c; | |
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); | |
if (parent instanceof CompareViewerSwitchingPane) | |
fParent= (CompareViewerSwitchingPane) parent; | |
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>. | |
* | |
* @param structureCreator the new structure creator | |
*/ | |
public void setStructureCreator(IStructureCreator structureCreator) { | |
if (fStructureCreator != structureCreator) { | |
fStructureCreator= structureCreator; | |
Control tree= getControl(); | |
if (tree != null && !tree.isDisposed()) | |
tree.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle()); | |
} | |
} | |
/** | |
* 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); | |
if (input != oldInput) | |
initialSelection(); | |
} | |
} | |
protected void initialSelection() { | |
expandToLevel(2); | |
} | |
/* (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. | |
*/ | |
protected void compareInputChanged(ICompareInput input) { | |
ITypedElement t= null; | |
boolean changed= false; | |
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); | |
changed= true; | |
} 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); | |
changed= true; | |
} 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); | |
changed= true; | |
} else | |
fRightStructure= null; | |
if (fRightInput instanceof IContentChangeNotifier) | |
((IContentChangeNotifier)fRightInput).addContentChangeListener(fContentChangedListener); | |
} | |
if (changed) | |
diff(); | |
} | |
/** | |
* Calls <code>diff</code> whenever the byte contents changes. | |
*/ | |
protected void contentChanged(IContentChangeNotifier changed) { | |
if (fStructureCreator == null) | |
return; | |
if (changed != null) { | |
if (changed == fAncestorInput) { | |
fAncestorStructure= fStructureCreator.getStructure(fAncestorInput); | |
} else if (changed == fLeftInput) { | |
fLeftStructure= fStructureCreator.getStructure(fLeftInput); | |
} else if (changed == fRightInput) { | |
fRightStructure= fStructureCreator.getStructure(fRightInput); | |
} else | |
return; | |
} else { | |
fAncestorStructure= fStructureCreator.getStructure(fAncestorInput); | |
fLeftStructure= fStructureCreator.getStructure(fLeftInput); | |
fRightStructure= fStructureCreator.getStructure(fRightInput); | |
} | |
diff(); | |
} | |
/** | |
* This method is called from within <code>diff()</code> before the difference | |
* tree is being built. | |
* Clients may override this method to perform their own pre-processing. | |
* This default implementation does nothing. | |
* @param ancestor the ancestor input to the differencing operation | |
* @param left the left input to the differencing operation | |
* @param right the right input to the differencing operation | |
* @since 2.0 | |
*/ | |
protected void preDiffHook(IStructureComparator ancestor, IStructureComparator left, IStructureComparator right) { | |
// we do nothing here | |
} | |
/** | |
* Runs the difference engine and refreshes the tree. | |
*/ | |
protected void diff() { | |
preDiffHook(fAncestorStructure, fLeftStructure, fRightStructure); | |
String message= null; | |
if ((fThreeWay && fAncestorStructure == null) || fLeftStructure == null || fRightStructure == null) { | |
// could not get structure of one (or more) of the legs | |
fRoot= null; | |
message= CompareMessages.getString("StructureDiffViewer.StructureError"); //$NON-NLS-1$ | |
} 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); | |
} | |
protected Object visit(Object data, int result, Object ancestor, Object left, Object right) { | |
Object o= super.visit(data, result, ancestor, left, right); | |
if (fLeftIsLocal && o instanceof DiffNode) | |
((DiffNode)o).swapSides(fLeftIsLocal); | |
return o; | |
} | |
}; | |
fRoot= (IDiffContainer) fDifferencer.findDifferences(fThreeWay, null, null, | |
fAncestorStructure, fLeftStructure, fRightStructure); | |
if (fRoot == null || fRoot.getChildren().length == 0) { | |
message= CompareMessages.getString("StructureDiffViewer.NoStructuralDifferences"); //$NON-NLS-1$ | |
} else { | |
postDiffHook(fDifferencer, fRoot); | |
} | |
} | |
if (fParent != null) | |
fParent.setTitleArgument(message); | |
refresh(getRoot()); | |
} | |
/** | |
* This method is called from within <code>diff()</code> after the difference | |
* tree has been built. | |
* Clients may override this method to perform their own post-processing. | |
* This default implementation does nothing. | |
* @param differencer the differencer used to perform the differencing | |
* @param root the non-<code>null</code> root node of the difference tree | |
* @since 2.0 | |
*/ | |
protected void postDiffHook(Differencer differencer, IDiffContainer root) { | |
// we do nothing here | |
} | |
/** | |
* 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); | |
if (s1 == null || s2 == null) | |
return false; | |
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)) | |
diff(); | |
else | |
super.propertyChange(event); | |
} | |
/** | |
* 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); | |
} | |
} | |