Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcvs2001-05-02 20:48:05 +0000
committercvs2001-05-02 20:48:05 +0000
commit7e3be1fe036be304c22583c9bf88abb0dfe9fa9d (patch)
treec9290a61836cf8eef6c94b83abbcf60646c6e077
downloadeclipse.platform.team-7e3be1fe036be304c22583c9bf88abb0dfe9fa9d.tar.gz
eclipse.platform.team-7e3be1fe036be304c22583c9bf88abb0dfe9fa9d.tar.xz
eclipse.platform.team-7e3be1fe036be304c22583c9bf88abb0dfe9fa9d.zip
*** empty log message ***v0_102+
-rw-r--r--.gitignore8
-rw-r--r--bundles/org.eclipse.compare/.cvsignore2
-rw-r--r--bundles/org.eclipse.compare/.vcm_meta6
-rw-r--r--bundles/org.eclipse.compare/compare/changes.html624
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java124
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java360
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java623
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java182
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java699
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java79
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java25
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java34
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java50
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java27
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java36
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java31
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java63
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java29
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java227
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java948
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java42
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java150
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java52
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java2372
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties114
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html35
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java30
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java17
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java92
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java24
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java86
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java39
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java32
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java137
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java159
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java44
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties35
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java706
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java251
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java54
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java216
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java28
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java36
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java21
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java137
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java127
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java25
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties55
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java218
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java142
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java94
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java90
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties22
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java153
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java21
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java69
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java26
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java64
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java27
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java190
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java237
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java56
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java253
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/package.html50
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java73
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java55
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java54
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java206
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java460
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html37
-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
-rw-r--r--bundles/org.eclipse.compare/doc/hglegal.htm19
-rw-r--r--bundles/org.eclipse.compare/doc/ngibmcpy.gifbin0 -> 1542 bytes
-rw-r--r--bundles/org.eclipse.compare/doc/org_eclipse_compare.html39
-rw-r--r--bundles/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html55
-rw-r--r--bundles/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html53
-rw-r--r--bundles/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html53
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/accept.gifbin0 -> 849 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/both.gifbin0 -> 842 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/compare.gifbin0 -> 130 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/ignorews.gifbin0 -> 864 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright.gifbin0 -> 141 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gifbin0 -> 870 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/next.gifbin0 -> 138 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/panes3.gifbin0 -> 117 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/prev.gifbin0 -> 139 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/reject.gifbin0 -> 858 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft.gifbin0 -> 140 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gifbin0 -> 869 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gifbin0 -> 852 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/smart.gifbin0 -> 849 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/synchmode.gifbin0 -> 125 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ctool16/toggle.gifbin0 -> 127 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/obj16/compare.gifbin0 -> 130 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/obj16/date.gifbin0 -> 894 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/obj16/time.gifbin0 -> 887 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/add.gifbin0 -> 833 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/confl_add.gifbin0 -> 866 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/confl_change.gifbin0 -> 849 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/confl_del.gifbin0 -> 861 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/del.gifbin0 -> 827 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/in_add.gifbin0 -> 862 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/in_change.gifbin0 -> 843 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/in_del.gifbin0 -> 855 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/out_add.gifbin0 -> 860 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/out_change.gifbin0 -> 837 bytes
-rw-r--r--bundles/org.eclipse.compare/icons/basic/ovr16/out_del.gifbin0 -> 851 bytes
-rw-r--r--bundles/org.eclipse.compare/plugin.jars1
-rw-r--r--bundles/org.eclipse.compare/plugin.properties9
-rw-r--r--bundles/org.eclipse.compare/plugin.xml125
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/.cvsignore2
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/.vcm_meta6
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/changes.html624
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java124
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java360
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java623
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java182
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java699
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java79
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java25
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java34
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java50
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java27
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java36
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java31
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java63
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java29
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java227
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java948
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java42
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java150
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java52
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java2372
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties114
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html35
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java30
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java17
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java92
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java24
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java86
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java39
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java32
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java137
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java159
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java44
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties35
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java706
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java251
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java54
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java216
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java28
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java36
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java21
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java137
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java127
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java25
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties55
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java218
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java142
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java94
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java90
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties22
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java153
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java21
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java69
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java26
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java64
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java27
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java190
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java237
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java56
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java253
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/package.html50
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java73
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java55
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java54
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java206
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java460
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html37
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffContainer.java109
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffElement.java87
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffNode.java311
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java561
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewerResources.properties50
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java511
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DocumentRangeNode.java352
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInput.java137
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInputChangeListener.java33
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffContainer.java59
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffElement.java56
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureComparator.java44
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureCreator.java131
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java336
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/package.html49
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/hglegal.htm19
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/ngibmcpy.gifbin0 -> 1542 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare.html39
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html55
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html53
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html53
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/accept.gifbin0 -> 849 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/both.gifbin0 -> 842 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/compare.gifbin0 -> 130 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/ignorews.gifbin0 -> 864 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright.gifbin0 -> 141 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gifbin0 -> 870 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/next.gifbin0 -> 138 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/panes3.gifbin0 -> 117 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/prev.gifbin0 -> 139 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/reject.gifbin0 -> 858 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft.gifbin0 -> 140 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gifbin0 -> 869 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gifbin0 -> 852 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/smart.gifbin0 -> 849 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/synchmode.gifbin0 -> 125 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/toggle.gifbin0 -> 127 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/compare.gifbin0 -> 130 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/date.gifbin0 -> 894 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/time.gifbin0 -> 887 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/add.gifbin0 -> 833 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_add.gifbin0 -> 866 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_change.gifbin0 -> 849 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_del.gifbin0 -> 861 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/del.gifbin0 -> 827 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_add.gifbin0 -> 862 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_change.gifbin0 -> 843 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_del.gifbin0 -> 855 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_add.gifbin0 -> 860 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_change.gifbin0 -> 837 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_del.gifbin0 -> 851 bytes
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.jars1
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.properties9
-rw-r--r--bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.xml125
247 files changed, 30132 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..b22cae386
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+/*/*/bin/
+*~
+*.rej
+*.bak
+javacore.*
+heapdump.*
+core.*
+Snap.*
diff --git a/bundles/org.eclipse.compare/.cvsignore b/bundles/org.eclipse.compare/.cvsignore
new file mode 100644
index 000000000..3f041525e
--- /dev/null
+++ b/bundles/org.eclipse.compare/.cvsignore
@@ -0,0 +1,2 @@
+bin
+.classpath
diff --git a/bundles/org.eclipse.compare/.vcm_meta b/bundles/org.eclipse.compare/.vcm_meta
new file mode 100644
index 000000000..aae2e1b3e
--- /dev/null
+++ b/bundles/org.eclipse.compare/.vcm_meta
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-description>
+ <nature id="org.eclipse.jdt.core.javanature"/>
+ <builder name="org.eclipse.jdt.core.javabuilder">
+ </builder>
+</project-description>
diff --git a/bundles/org.eclipse.compare/compare/changes.html b/bundles/org.eclipse.compare/compare/changes.html
new file mode 100644
index 000000000..24e156e1f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/changes.html
@@ -0,0 +1,624 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <meta name="Author" content="IBM">
+ <title>Eclipse Platform Release Notes - Desktop</title>
+</head>
+<body>
+
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+SDK Build 102
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GCPBTE: ITPVCM:WINNT - Catchup/release shouldn't show structure on multi-selection
+<br>1GCP1DH: ITPJUI:WINNT - Double click on calendar dismisses replace
+dialog
+<br>1GCJBUT: ITPJUI:ALL - No indication when there are no editions available
+<br>1GCFU1D: ITPJUI:ALL - Compare: incorrect icon Replace Java Element
+Dialog
+<h2>
+Problem reports closed</h2>
+1G8BNIK: ITPJUI:WINNT - strange naming in compare view
+<p><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.048
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GCOZHF: ITPUI:WINNT - Error navigating stucture in release view
+<br>1GCOLNU: ITPUI:WINNT - Double clicking in compare editor is slow
+<br>1GCJWNF: ITPJUI:ALL - "no differences" message is not a sentence
+<br>1GCHN93: ITPUI:WINNT - Walkback in DiffTreeViewer
+<br>1G9UVEC: ITPJUI:WINNT - compare viewer: no syntax coloring
+<br>1GAOS9R: ITPUI:WINNT - Unable to cancel compare
+<br>1GBCZXK: ITPUI:WINNT - Failed assertion when java structure fails to
+parse
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.046
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GCDH47: ITPUI:ALL - Next and Prev buttons don't work in strcuture compare
+viewers
+<br>1GCBHKO: ITPUI:ALL - Illegal uses of FileImageDescriptor in Compare
+plugin
+<br>1GCDHNA: ITPUI:ALL - remove "..."&nbsp;&nbsp; from CompareWith and
+ReplaceWith ... cascade menus
+<br>1GCF6T6: ITPUI:ALL - Inconsistent contextmenu labels in TextMergeViewer
+<br>1GBYOQY: ITPUI:WIN2000 - Need to configure label in EditionSelectionDialog
+<br>1GCF905: ITPUI:ALL - EditionSelectionDialog must sort input array
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.044
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GBBL6K: ITPUI:ALL - SH: NullPointer in StructuredDiffViewer
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.043
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+Added new constructor taking a SWT style bits to ContentMergeViewer &amp;
+TextMergeViewer</li>
+
+<li>
+Added API to EditionSelectionDialog to control on which side of a compare
+viewer to show the workspace and editions.</li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+the javadoc in package.html files is incorrect.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+1GBPOEB: ITPJUI:WINNT - Layout trouble in the default diff editor
+<br>1GBPOZE: ITPJUI:WINNT - compare: synchronize panes button disappears
+<br>1GBWQ5V: ITPUI:WIN2000 - Infinite loop in BinaryCompareViewer
+<br>1GBWUJ8: ITPUI:WIN2000 - Compare editor has wrong title
+<br>1GBYJ41: ITPUI:WIN2000 - Need style bits for top level composite of
+TextMergeViewer
+<br>1GBYOJX: ITPUI:WIN2000 - Better name for ReplaceWithEditionDialog
+<br>1GB0P4S: ITPVCM:WINNT - Structure viewer on jar is brutal
+<br>1GBYONP: ITPUI:WIN2000 - Flipping sides of EditionSelectionDialog
+<br>1GBYOQY: ITPUI:WIN2000 - Need to configure label in EditionSelectionDialog
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.042
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+new method CompareEditorInput.setFocus</li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+Adapted to new workbench UI look.</li>
+
+<li>
+Removed VCM synch stuff</li>
+
+<li>
+Clarification how to do filtering in ResourceNode.createChild(...): if
+null is returned given child is not added to list of children.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+the javadoc in package.html files is incorrect.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+1GBPN75: ITPJUI:WINNT - Endless loop when copying diffs in diff editor
+<br>1GBM7QL: ITPJUI:WINNT - replace with catchup?
+<br>1GBMJ9I: ITPUI:WINNT - ResourceNode resource and children should be
+protected
+<br>1GBM3AQ: ITPUI:WIN2000 - Null as input for TextMergeViewer
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.040
+<h2>
+What's new in this drop</h2>
+Javadoc &amp; Incorporated API review.
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+too many</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+none</li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+none</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+lots</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 032
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+deprecated <tt>ViewerPane</tt>; use <tt>CompareViewerSwitchingPane</tt>
+instead</li>
+
+<li>
+deprecated <tt>ByteContentAccessor</tt> and <tt>IByteContentAccessor</tt>.
+Use <tt>IStreamContentAccessor </tt>instead<tt>.</tt></li>
+
+<li>
+deprecated <tt>IDiffParent</tt>; Use <tt>IDiffElement</tt> instead<tt>.</tt></li>
+
+<li>
+Moved more classes to internal package.</li>
+
+<li>
+removed byte[] argument from <tt>IEditable.replace</tt>. Replace only handles
+add, remove, and copy contents. For setting a contents use <tt>IEditable.setContents</tt>.</li>
+
+<li>
+Renamed <tt>IByteContentChangedListener</tt> to <tt>IContentChangedListener</tt>.</li>
+
+<li>
+removed methods <tt>addChangeListener</tt> and <tt>removeChangeListener</tt>
+from <tt>IByteContentAccessor</tt>. Use <tt>IContentChangedProvider</tt>
+instead.</li>
+
+<li>
+Deprecated <tt>IDiffConstants.INCOMING</tt>, <tt>IDiffConstants.OUTGOING</tt>.
+Use <tt>LEFT</tt> and <tt>RIGHT</tt> instead. The interpretation of LEFT
+and RIGHT (e.g. Incoming and Outgoing) is left to the client.</li>
+
+<li>
+Changed return type of&nbsp; <tt>DiffContainer.findChild(String name)</tt>
+from <tt>DiffContainer</tt> to <tt>IDiffElement</tt>.</li>
+
+<li>
+Changed return type of <tt>IDiffContainer.getChildren</tt> from <tt>Iterator</tt>
+to <tt>Object[]</tt>.</li>
+
+<br>&nbsp;</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+&nbsp;added method <tt>ResourceNode.createChild(IResource child)</tt></li>
+
+<br>(can be used to filter children)
+<li>
+added method <tt>setContents(byte[] ...)</tt> to <tt>IEditable</tt></li>
+
+<li>
+added interface <tt>IContentChangedProvider.</tt></li>
+
+<br>&nbsp;</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+Panes within compare editor can be resized</li>
+
+<li>
+Replace from history buffer.</li>
+
+<li>
+Support for method level editions.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+lots</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<ul>
+<li>
+yes!</li>
+</ul>
+
+<h2>
+Problem reports closed</h2>
+
+<ul>
+<li>
+not yet</li>
+</ul>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 027
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+deprecated <tt>ViewerPane</tt>; use <tt>deprectCompareViewerSwitchingPane</tt>
+instead</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 026
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+changed <tt>IViewer</tt> to <tt>ICompareViewer</tt></li>
+
+<li>
+renamed <tt>AbstractViewer</tt> to <tt>AbstractCompareViewer</tt></li>
+
+<li>
+removed interface <tt>IDocumentRange</tt></li>
+
+<li>
+removed class <tt>HistoryCompareOp</tt> (Use the new command "Replace with
+edition") instead</li>
+
+<li>
+removed the history mechanism of <tt>ResourceNode</tt>. Use the new class
+<tt>HistoryItem</tt>
+instead.</li>
+
+<li>
+removed <tt>NullViewer</tt> from API.</li>
+
+<li>
+new method <tt>IEditable.isEditable()</tt></li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+first cut of new <tt>ReplaceWithActionAction</tt></li>
+
+<li>
+new class <tt>HistoryItem</tt></li>
+
+<li>
+new constructor: <tt>DiffNode(ITypedInput left, ITypedInput right)</tt></li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+first cut of "Replace with edition". A dialog is there but pressing "OK"
+has no effect.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+correct focus/activation handling still waiting for SWT focus/activation
+fix.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+1G8FFQ7: ITPUI:WIN2000 - Walkback
+<br>1G8BRDW: ITPUI:ALL - Reference to deprecated DesktopPlugin
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 022
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+changed visibility of <tt>ByteContentAccessor.loadContent()</tt> from public
+to protected</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+differencing engine (<tt>Differencer</tt>) supports <tt>IProgressMonitor</tt></li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+correct focus/activation handling is still broken.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 021
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+Harmonized usage of the compare directions: Mine/Yours/Your, Incoming/Outgoing,
+Left/Right. Everywhere <b>Left/Right </b>is used.</li>
+
+<br>API changes: ContentMergeViewer, ImageMergeViewer, TextMergeViewer,
+IMergeViewerContentProvider, MergeViewerContentProvider, ICompareConfiguration,
+CompareConfiguration, IThreeWayInput, StructureDiffViewer, DiffNode, DiffTreeViewer
+<li>
+changed signature of <tt>IStructureCreator.save</tt> from <tt>void save(Object
+input, IDocument document)</tt> to <tt>void save(Object input, IStructureComparator
+structure)</tt></li>
+
+<li>
+made <tt>com.ibm.eclipse.ui.compare.structuremergeviewer.ArrayIterator</tt>
+package private.</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+Added new classes <tt>CompareOp</tt>, <tt>ResourceCompareOp, HistoryCompareOp</tt></li>
+
+<br>All comparison operations can now be written as subclasses of <tt>CompareOp</tt>.
+<tt>CompareUIPlugin.runCompareOp()</tt>
+generically opens a <tt>CompareEditor</tt> for it. <tt>ResourceCompareOp</tt>
+implements a universal two/threeway compare on desktop resources. <tt>HistoryCompareOp</tt>
+compares a resource with its most recent edition from the history buffer.
+Another example would be a <tt>RepositoryCompareOp</tt>.</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+background coloring in the TextMergeViewer no longer depend on StyledText
+background coloring support.</li>
+
+<li>
+generic <i>copy-left-to-right</i> and <i>copy-right-to-left</i> action
+now work in second and third structure panes.</li>
+
+<br>In the resource pane they just print a message to the console but don't
+do anything.
+<li>
+Progress is shown while a compare runs.</li>
+
+<li>
+Support for comparing jpegs.</li>
+
+<li>
+if resources with unknown types are compared the Compare Plugin tries to
+guess whether they contain text and whether the TextMergeViewer can be
+used.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+correct focus/activation handling is still broken.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java
new file mode 100644
index 000000000..9834071bd
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.io.*;
+import org.eclipse.jface.util.ListenerList;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.internal.Utilities;
+
+/**
+ * Abstract implementation for a buffered <code>IStreamContentAccessor</code>.
+ * <p>
+ * Subclasses must implement the <code>createStream</code> method
+ * to connect the buffered content with a streamable source (e.g., a file).
+ * <p>
+ * As long as the contents of <code>BufferedContent</code> is only retrieved as an input stream
+ * (by means of <code>getContents</code>) and the <code>BufferedContent</code> is not modified (with
+ * <code>setContent</code>) no buffering takes place.
+ * Buffering starts when either methods <code>getContent</code> or <code>setContent</code> is called.
+ *
+ * @see IContentChangeNotifier
+ * @see IStreamContentAccessor
+ */
+public abstract class BufferedContent implements IContentChangeNotifier, IStreamContentAccessor {
+
+ byte[] fContent;
+ private ListenerList fListenerList;
+
+ /**
+ * Creates a buffered stream content accessor.
+ */
+ protected BufferedContent() {
+ }
+
+ /* (non-Javadoc)
+ * see IStreamContentAccessor.getContents
+ */
+ public InputStream getContents() throws CoreException {
+ if (fContent != null)
+ return new ByteArrayInputStream(fContent);
+ return createStream();
+ }
+
+ /**
+ * Creates and returns a stream for reading the contents.
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ *
+ * @return the stream from which the content is read
+ * @exception CoreException if the contents could not be accessed
+ */
+ protected abstract InputStream createStream() throws CoreException;
+
+ /**
+ * Sets the contents. Registered content change listeners are notified.
+ *
+ * @param contents the new contents
+ */
+ public void setContent(byte[] contents) {
+ fContent= contents;
+ fireContentChanged();
+ }
+
+ /**
+ * Returns the contents as an array of bytes.
+ *
+ * @return the contents as an array of bytes, or <code>null</code> if
+ * the contents could not be accessed
+ */
+ public byte[] getContent() {
+ if (fContent == null) {
+ try {
+ InputStream is= createStream();
+ fContent= Utilities.readBytes(is);
+ } catch(CoreException ex) {
+ }
+ }
+ return fContent;
+ }
+
+ /**
+ * Discards the buffered content.
+ */
+ public void discardBuffer() {
+ fContent= null;
+ //fireContentChanged();
+ }
+
+ /* (non-Javadoc)
+ * see IContentChangeNotifier.addChangeListener
+ */
+ public void addContentChangeListener(IContentChangeListener listener) {
+ if (fListenerList == null)
+ fListenerList= new ListenerList();
+ fListenerList.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * see IContentChangeNotifier.removeChangeListener
+ */
+ public void removeContentChangeListener(IContentChangeListener listener) {
+ if (fListenerList != null) {
+ fListenerList.remove(listener);
+ if (fListenerList.isEmpty())
+ fListenerList= null;
+ }
+ }
+
+ /**
+ * Notifies all registered <code>IContentChangeListener</code>s of a content change.
+ */
+ protected void fireContentChanged() {
+ if (fListenerList != null) {
+ Object[] listeners= fListenerList.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((IContentChangeListener)listeners[i]).contentChanged(this);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java
new file mode 100644
index 000000000..58218dd1f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java
@@ -0,0 +1,360 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.util.HashMap;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.*;
+
+import org.eclipse.compare.internal.DiffImage;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.structuremergeviewer.DiffTreeViewer;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+
+/**
+ * A <code>CompareConfiguration</code> object
+ * controls various UI aspects of compare/merge viewers like
+ * title labels and images, or whether a side of a merge viewer is editable.
+ * In addition to these fixed properties <code>ICompareConfiguration</code> provides
+ * API for an open ended set of properties. Different viewers which share the same
+ * configuration can communicate via this mechanism. E.g. if a compare editor
+ * has a button for controlling whether compare viewers ignore white space,
+ * the button would trigger a change of the boolean <code>IGNORE_WHITESPACE</code> property
+ * and all interested viewers would receive notification.
+ * <p>
+ * Suitable default labels are provided (without images); both the left and right sides
+ * are editable.
+ * </p>
+ * <p>
+ * Clients may use this class as is, or subclass to add new state and behavior.
+ * </p>
+ */
+public class CompareConfiguration {
+
+ /**
+ * Name of the ignore whitespace property.
+ */
+ public static final String IGNORE_WHITESPACE= "IGNORE_WHITESPACE";
+ /**
+ * Name of the show pseudo conflicts property.
+ */
+ public static final String SHOW_PSEUDO_CONFLICTS= "SHOW_PSEUDO_CONFLICTS";
+
+
+ private static final int WIDTH= 22;
+
+ private static ImageDescriptor[] fgImages= new ImageDescriptor[16];
+ private static Object fgDummy= new Object();
+ private static HashMap fgMap= new HashMap(20);
+
+ static {
+ int INCOMING= Differencer.LEFT;
+ int OUTGOING= Differencer.RIGHT;
+
+ fgImages[Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/add.gif");
+ fgImages[INCOMING + Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/in_add.gif");
+ fgImages[OUTGOING + Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/out_add.gif");
+ fgImages[Differencer.CONFLICTING + Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/confl_add.gif");
+
+ fgImages[Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/del.gif");
+ fgImages[INCOMING + Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/in_del.gif");
+ fgImages[OUTGOING + Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/out_del.gif");
+ fgImages[Differencer.CONFLICTING + Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/confl_del.gif");
+
+ fgImages[INCOMING + Differencer.CHANGE]= CompareUIPlugin.getImageDescriptor("ovr16/in_change.gif");
+ fgImages[OUTGOING + Differencer.CHANGE]= CompareUIPlugin.getImageDescriptor("ovr16/out_change.gif");
+ fgImages[Differencer.CONFLICTING + Differencer.CHANGE]= CompareUIPlugin.getImageDescriptor("ovr16/confl_change.gif");
+ }
+
+ private ListenerList fListeners= new ListenerList();
+ private HashMap fProperties= new HashMap();
+ private boolean fLeftEditable= true;
+ private boolean fRightEditable= true;
+ private String fAncestorLabel= "Ancestor";
+ private String fLeftLabel= "Left";
+ private String fRightLabel= "Right";
+ private Image fAncestorImage;
+ private Image fRightImage;
+ private Image fLeftImage;
+
+ /**
+ * Creates a new configuration with editable left and right sides,
+ * suitable default labels, and no images.
+ */
+ public CompareConfiguration() {
+ }
+
+ /**
+ * Returns an image showing the specified change kind applied to a
+ * given base image. The different kind of changes are defined in the <code>Differencer</code>.
+ * Typically an implementation would build a composite image
+ * from the given base image and an image representing the change kind.
+ * Newly created images are remembered by this class and
+ * disposed when the <code>dispose</code> method is called.
+ *
+ * @param base the image which is modified to reflect the kind of change
+ * @param kind the kind of change as defined in <code>Differencer</code>.
+ * @return an modification of the base image reflecting the kind of change.
+ * @see org.eclipse.compare.structuremergeviewer.Differencer
+ */
+ public Image getImage(Image base, int kind) {
+
+ Object key= base;
+ if (key == null)
+ key= fgDummy;
+
+ kind &= 15;
+
+ Image[] a= (Image[]) fgMap.get(key);
+ if (a == null) {
+ a= new Image[16];
+ fgMap.put(key, a);
+ }
+ Image b= a[kind];
+ if (b == null) {
+ b= new DiffImage(base, fgImages[kind], WIDTH).createImage();
+ CompareUI.disposeOnShutdown(b);
+ a[kind]= b;
+ }
+ return b;
+ }
+
+ /**
+ * Dispose of this compare configuration.
+ * This method is called if the compare configuration is no longer used.
+ * An implementation must dispose of all resources.
+ */
+ public void dispose() {
+ }
+
+ /**
+ * Fires a <code>PropertyChangeEvent</code> to registered listeners.
+ *
+ * @param propertyName the name of the property that has changed
+ * @param oldValue the property's old value
+ * @param newValue the property's new value
+ */
+ private void fireChange(String propertyName, Object oldValue, Object newValue) {
+ PropertyChangeEvent event= null;
+ Object[] listeners= fListeners.getListeners();
+ if (listeners != null) {
+ for (int i= 0; i < listeners.length; i++) {
+ IPropertyChangeListener l= (IPropertyChangeListener) listeners[i];
+ if (event == null)
+ event= new PropertyChangeEvent(this, propertyName, oldValue, newValue);
+ l.propertyChange(event);
+ }
+ }
+ }
+
+ /* (non javadoc)
+ * see IPropertyChangeNotifier.addListener
+ */
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ fListeners.add(listener);
+ }
+
+ /* (non javadoc)
+ * see IPropertyChangeNotifier.removeListener
+ */
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ fListeners.remove(listener);
+ }
+
+ /**
+ * Sets the property with the given name.
+ * If the new value differs from the old a <code>PropertyChangeEvent</code>
+ * is sent to registered listeners.
+ *
+ * @param propertyName the name of the property to set
+ * @param value the new value of the property
+ */
+ public void setProperty(String key, Object newValue) {
+ Object oldValue= fProperties.get(key);
+ fProperties.put(key, newValue);
+ if (oldValue == null || !oldValue.equals(newValue))
+ fireChange(key, oldValue, newValue);
+ }
+
+ /**
+ * Returns the property with the given name, or <code>null</code>
+ * if no such property exists.
+ *
+ * @param propertyName the name of the property to retrieve
+ * @return the property with the given name, or <code>null</code> if not found
+ */
+ public Object getProperty(String key) {
+ return fProperties.get(key);
+ }
+
+ //---- ancestor
+
+ /**
+ * Sets the label to use for the ancestor of compare/merge viewers.
+ *
+ * @param label the new label for the ancestor of compare/merge viewers
+ */
+ public void setAncestorLabel(String label) {
+ fAncestorLabel= label;
+ }
+
+ /**
+ * Returns the label for the ancestor side of compare/merge viewers.
+ * This label is typically shown in the title of the ancestor area in a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the label for the ancestor side or <code>null</code>
+ */
+ public String getAncestorLabel(Object element) {
+ return fAncestorLabel;
+ }
+
+ /**
+ * Sets the image to use for the ancestor of compare/merge viewers.
+ * The CompareConfiguration does not automatically dispose the old image.
+ *
+ * @param image the new image for the ancestor of compare/merge viewers
+ */
+ public void setAncestorImage(Image image) {
+ fAncestorImage= image;
+ }
+
+ /**
+ * Returns the image for the ancestor side of compare/merge viewers.
+ * This image is typically shown in the title of the ancestor area in a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the image for the ancestor side or <code>null</code>
+ */
+ public Image getAncestorImage(Object element) {
+ return fAncestorImage;
+ }
+
+ //---- left side
+
+ /**
+ * Controls whether the left side of a merge viewer is editable.
+ *
+ * @param editable if the value is <code>true</code> left side is editable
+ */
+ public void setLeftEditable(boolean editable) {
+ fLeftEditable= editable;
+ }
+
+ /**
+ * Returns whether the left hand side of a merge viewer is editable.
+ *
+ * @return <code>true</code> if the left hand side is editable
+ */
+ public boolean isLeftEditable() {
+ return fLeftEditable;
+ }
+
+ /**
+ * Sets the label to use for the left side of compare/merge viewers.
+ *
+ * @param label the new label for the left side of compare/merge viewers
+ */
+ public void setLeftLabel(String label) {
+ fLeftLabel= label;
+ }
+
+ /**
+ * Returns the label for the left hand side of compare/merge viewers.
+ * This label is typically shown in the title of the left side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the label for the left hand side or <code>null</code>
+ */
+ public String getLeftLabel(Object element) {
+ return fLeftLabel;
+ }
+
+ /**
+ * Sets the image to use for the left side of compare/merge viewers.
+ * The compare configuration does not automatically dispose the old image.
+ *
+ * @param image the new image for the left side of compare/merge viewers
+ */
+ public void setLeftImage(Image image) {
+ fLeftImage= image;
+ }
+
+ /**
+ * Returns the image for the left hand side of compare/merge viewers.
+ * This image is typically shown in the title of the left side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the image for the left hand side or <code>null</code>
+ */
+ public Image getLeftImage(Object element) {
+ return fLeftImage;
+ }
+
+ //---- right side
+
+ /**
+ * Controls whether the right side of a merge viewer is editable.
+ *
+ * @param editable if the value is <code>true</code> right side is editable
+ */
+ public void setRightEditable(boolean editable) {
+ fRightEditable= editable;
+ }
+
+ /**
+ * Returns whether the right hand side of a merge viewer is editable.
+ *
+ * @return <code>true</code> if the right hand side is editable
+ */
+ public boolean isRightEditable() {
+ return fRightEditable;
+ }
+
+ /**
+ * Sets the label to use for the right side of compare/merge viewers.
+ *
+ * @param label the new label for the right side of compare/merge viewers
+ */
+ public void setRightLabel(String label) {
+ fRightLabel= label;
+ }
+
+ /**
+ * Returns the label for the right hand side of compare/merge viewers.
+ * This label is typically shown in the title of the right side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the label for the right hand side or <code>null</code>
+ */
+ public String getRightLabel(Object element) {
+ return fRightLabel;
+ }
+
+ /**
+ * Sets the image to use for the right side of compare/merge viewers.
+ * The compare configuration does not automatically dispose the old image.
+ *
+ * @param image the new image for the right side of compare/merge viewers
+ */
+ public void setRightImage(Image image) {
+ fRightImage= image;
+ }
+
+ /**
+ * Returns the image for the right hand side of compare/merge viewers.
+ * This image is typically shown in the title of the right side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the image for the right hand side or <code>null</code>
+ */
+ public Image getRightImage(Object element) {
+ return fRightImage;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
new file mode 100644
index 000000000..2d3cd5754
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
@@ -0,0 +1,623 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.lang.reflect.InvocationTargetException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.IPersistableElement;
+import org.eclipse.ui.IEditorInput;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.compare.internal.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+
+/**
+ * A compare operation which can present its results in a special editor.
+ * Running the compare operation and presentating the results in a compare editor
+ * are combined in one interface because it allows a client to keep the implementation
+ * all in one place while separating it from the innards of a specific UI implementation of compare/merge.
+ * <p>
+ * A <code>CompareEditorInput</code> defines methods for the following sequence steps:
+ * <UL>
+ * <LI>running a lengthy compare operation under progress monitor control,
+ * <LI>creating a UI for displaying the model and initializing the some widgets with the compare result,
+ * <LI>tracking the dirty state of the model in case of merge,
+ * <LI>saving the model.
+ * </UL>
+ * The Compare plug-in's <code>openCompareEditor</code> method takes an <code>ICompareEditorInput</code>
+ * and starts sequencing through the above steps. If the compare result is not empty a new compare editor
+ * is opened and takes over the sequence until eventually closed.
+ * <p>
+ * The <code>prepareInput</code> method should contain the
+ * code of the compare operation. It is executed under control of a progress monitor
+ * and can be canceled. If the result of the compare is not empty, that is if there are differences
+ * that needs to be presented, the <code>ICompareEditorInput</code> should hold onto them and return them with
+ * the <code>getCompareResult</code> method.
+ * If the value returned from <code>getCompareResult</code> is not <code>null</code>
+ * a compare editor is opened on the <code>ICompareEditorInput</code> with title and title image initialized by the
+ * corresponding methods of the <code>ICompareEditorInput</code>.
+ * <p>
+ * Creation of the editor's SWT controls is delegated to the <code>createContents</code> method.
+ * Here the SWT controls must be created and initialized with the result of the compare operation.
+ * <p>
+ * If merging is allowed, the modification state of the compared constituents must be tracked and the dirty
+ * state returned from method <code>isSaveNeeded</code>. The value <code>true</code> triggers a subsequent call
+ * to <code>save</code> where the modified resources can be saved.
+ * <p>
+ * The most important part of this implementation is the setup of the compare/merge UI.
+ * The UI uses a simple browser metaphor to present compare results.
+ * The top half of the layout shows the structural compare results (e.g. added, deleted, and changed files),
+ * the bottom half the content compare results (e.g. textual differences between two files).
+ * A selection in the top pane is fed to the bottom pane. If a content viewer is registered
+ * for the type of the selected object, this viewer is installed in the pane.
+ * In addition if a structure viewer is registered for the selection type the top pane
+ * is split horizontally to make room for another pane and the structure viewer is installed
+ * in it. When comparing Java files this second structure viewer would show the structural
+ * differences within a Java file, e.g. added, deleted or changed methods and fields.
+ * <p>
+ * Subclasses provide custom setups, e.g. for a Catchup/Release operation
+ * by passing a subclass of <code>CompareConfiguration</code> and by implementing the <code>prepareInput</code> method.
+ * If a subclass cannot use the <code>DiffTreeViewer</code> which is installed by default in the
+ * top left pane, method <code>createDiffViewer</code> can be overridden.
+ *
+ * @see CompareUI
+ * @see CompareEditorInput
+ */
+public abstract class CompareEditorInput implements IEditorInput, IPropertyChangeNotifier, IRunnableWithProgress {
+
+ /**
+ * The name of the "dirty" property.
+ */
+ public static final String DIRTY_STATE= "DIRTY_STATE";
+
+ private static boolean PR1GB0P4S= true;
+
+ private static Image fgTitleImage;
+
+ private Splitter fComposite;
+ private CompareConfiguration fCompareConfiguration;
+ private CompareViewerSwitchingPane fStructureInputPane;
+ private CompareViewerSwitchingPane fStructurePane1;
+ private CompareViewerSwitchingPane fStructurePane2;
+ private CompareViewerSwitchingPane fContentInputPane;
+ private CompareViewerSwitchingPane fFocusPane;
+ private String fMessage;
+ private ISelection fSelection2;
+ private Object fInput;
+ private String fTitle= "Compare";
+ private ListenerList fListenerList= new ListenerList();
+
+ private boolean fDirty= false;
+ private IPropertyChangeListener fDirtyStateListener;
+
+ /**
+ * Creates a <code>CompareEditorInput</code> which is initialized with the given
+ * compare configuration.
+ * The compare configuration is passed to subsequently created viewers.
+ *
+ * @param configuration the compare configuration
+ */
+ public CompareEditorInput(CompareConfiguration configuration) {
+ fCompareConfiguration= configuration;
+ Assert.isNotNull(configuration);
+
+ fDirtyStateListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent e) {
+ if (Utilities.getValue(e, false))
+ setDirty(true);
+ }
+ };
+ }
+
+ /* (non Javadoc)
+ * see IAdaptable.getAdapter
+ */
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IEditorInput.getImageDescriptor
+ */
+ public ImageDescriptor getImageDescriptor() {
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IEditorInput.getToolTipText
+ */
+ public String getToolTipText() {
+ return fTitle;
+ }
+
+ /* (non Javadoc)
+ * see IEditorInput.getName
+ */
+ public String getName() {
+ return fTitle;
+ }
+
+ /**
+ * Returns <code>null</code> since this editor cannot be persisted.
+ *
+ * @return <code>null</code> because this editor cannot be persisted
+ */
+ public IPersistableElement getPersistable() {
+ return null;
+ }
+
+ /**
+ * Returns <code>false</code> to indicate that this input
+ * should not appear in the "File Most Recently Used" menu.
+ *
+ * @return <code>false</code>
+ */
+ public boolean exists() {
+ return false;
+ }
+
+ /*
+ * FIXME!
+ */
+ protected void setMessage(String message) {
+ fMessage= message;
+ }
+
+ /*
+ * FIXME!
+ */
+ public String getMessage() {
+ return fMessage;
+ }
+
+ /**
+ * Returns the title which will be used in the compare editor's title bar.
+ * It can be set with <code>setTitle</code>.
+ *
+ * @return the title
+ */
+ public String getTitle() {
+ return fTitle;
+ }
+
+ /**
+ * Sets the title which will be used when presenting the compare result.
+ * This method must be called before the editor is opened.
+ *
+ * @param title the title to use for the CompareEditor
+ */
+ public void setTitle(String title) {
+ fTitle= title;
+ }
+
+ /**
+ * Returns the title image which will be used in the compare editor's title bar.
+ * Returns the title image which will be used when presenting the compare result.
+ * This implementation returns a generic compare icon.
+ * Subclasses can override.
+ *
+ * @return the title image, or <code>null</code> if none
+ */
+ public Image getTitleImage() {
+ if (fgTitleImage == null) {
+ fgTitleImage= CompareUIPlugin.getImageDescriptor("ctool16/compare.gif").createImage();
+ CompareUI.disposeOnShutdown(fgTitleImage);
+ }
+ return fgTitleImage;
+ }
+
+ /**
+ * Returns the configuration object for the viewers within the compare editor.
+ * Returns the configuration which was passed to the constructor.
+ *
+ * @return the compare configuration
+ */
+ public CompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * Adds standard actions to the given <code>ToolBarManager</code>.
+ * <p>
+ * Subclasses may override to add their own actions.
+ * </p>
+ *
+ * @param toolBarManager the <code>ToolBarManager</code> to which to contribute
+ */
+ public void contributeToToolBar(ToolBarManager toolBarManager) {
+
+ ResourceBundle bundle= CompareUIPlugin.getResourceBundle();
+
+ toolBarManager.add(new Separator());
+ toolBarManager.add(new IgnoreWhiteSpaceAction(bundle, fCompareConfiguration));
+ toolBarManager.add(new ShowPseudoConflicts(bundle, fCompareConfiguration));
+ }
+
+ /**
+ * Runs the compare operation and stores the compare result.
+ *
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancelation
+ * @exception InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception,
+ * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
+ * wrapped in an <code>InvocationTargetException</code> by the calling context
+ * @exception InterruptedException if the operation detects a request to cancel,
+ * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
+ * <code>InterruptedException</code>
+ */
+ public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException {
+ fInput= prepareInput(monitor);
+ }
+
+ /**
+ * Runs the compare operation and returns the compare result.
+ * If <code>null</code> is returned no differences were found and no compare editor needs to be opened.
+ * Progress should be reported to the given progress monitor.
+ * A request to cancel the operation should be honored and acknowledged
+ * by throwing <code>InterruptedException</code>.
+ *
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancelation
+ * @return the result of the compare operation, or <code>null</code> if there are no differences
+ * @exception InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception,
+ * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
+ * wrapped in an <code>InvocationTargetException</code> by the calling context
+ * @exception InterruptedException if the operation detects a request to cancel,
+ * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
+ * <code>InterruptedException</code>
+ */
+ protected abstract Object prepareInput(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException;
+
+ /**
+ * Returns the compare result computed by the most recent call to the
+ * <code>run</code> method. Returns <code>null</code> if no
+ * differences were found.
+ *
+ * @return the compare result prepared in method <code>prepareInput</code>
+ * or <code>null</code> if there were no differences
+ */
+ public Object getCompareResult() {
+ return fInput;
+ }
+
+ /**
+ * Create the SWT controls that are used to display the result of the compare operation.
+ * Creates the SWT Controls and sets up the wiring between the individual panes.
+ * This implementation creates all four panes but makes only the necessary ones visible.
+ * Finally it feeds the compare result into the top left structure viewer
+ * and the content viewer.
+ * <p>
+ * Subclasses may override if they need to change the layout or wiring between panes.
+ *
+ * @param parent the parent control under which the control must be created
+ * @return the SWT control hierarchy for the compare editor
+ */
+ public Control createContents(Composite parent) {
+
+ fComposite= new Splitter(parent, SWT.VERTICAL);
+
+ final Splitter h= new Splitter(fComposite, SWT.HORIZONTAL);
+
+ fStructureInputPane= new CompareViewerSwitchingPane(h, SWT.NONE, true) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ if (input instanceof DiffNode) {
+ DiffNode dn= (DiffNode) input;
+ if (dn.hasChildren())
+ return createDiffViewer(this);
+ }
+ return findStructureViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+
+ fStructurePane1= new CompareViewerSwitchingPane(h, SWT.NONE, true) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return findStructureViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+ h.setVisible(fStructurePane1, false);
+
+ fStructurePane2= new CompareViewerSwitchingPane(h, SWT.NONE, true) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return findStructureViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+ h.setVisible(fStructurePane2, false);
+
+ fContentInputPane= new CompareViewerSwitchingPane(fComposite, SWT.NONE) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return findContentViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+ fComposite.setVisible(h, false);
+ fComposite.setVisible(fContentInputPane, true);
+
+ fComposite.setWeights(new int[] { 30, 70 });
+
+ fComposite.layout();
+
+ // setup the wiring (no focus wiring yet)
+ fStructureInputPane.addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent e) {
+ ISelection s= e.getSelection();
+ Object input= null;
+ if (s.isEmpty()) {
+ input= fStructureInputPane.getInput();
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(null); // clear downstream pane
+ fStructurePane1.setInput(null);
+ } else {
+ Object o= getElement(e.getSelection());
+ if (o != null /*o instanceof ICompareInput*/)
+ input= (ICompareInput) o;
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(null); // clear downstream pane
+
+ if (PR1GB0P4S) {
+ if (fStructurePane1.getInput() != input)
+ fStructurePane1.setInput(null);
+ } else
+ fStructurePane1.setInput(input);
+ }
+ }
+ }
+ );
+
+ if (PR1GB0P4S) {
+ fStructureInputPane.addDoubleClickListener(
+ new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent e) {
+ ISelection s= e.getSelection();
+ ICompareInput input= null;
+ if (!s.isEmpty()) {
+ Object o= getElement(e.getSelection());
+ if (o instanceof ICompareInput)
+ input= (ICompareInput) o;
+ fStructurePane1.setInput(input);
+ }
+ }
+ }
+ );
+ }
+
+ fStructurePane1.addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent e) {
+ ISelection s= e.getSelection();
+ Object input= null;
+ if (s.isEmpty()) {
+ input= fStructurePane1.getInput();
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(null);
+ } else {
+ Object o= getElement(e.getSelection());
+ if (o != null /* o instanceof ICompareInput */)
+ input= (ICompareInput) o;
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(input);
+ }
+ }
+ }
+ );
+
+ fStructurePane2.addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent e) {
+ ISelection s= e.getSelection();
+ Object input= null;
+ if (s.isEmpty()) {
+ input= fStructurePane2.getInput();
+ } else {
+ Object o= getElement(e.getSelection());
+ if (o != null /* o instanceof ICompareInput */)
+ input= (ICompareInput) o;
+ }
+ fContentInputPane.setInput(input);
+ }
+ }
+ );
+
+ // now deal with activation
+ Listener activationListener= new Listener() {
+ public void handleEvent(Event event) {
+ if (event.widget instanceof CompareViewerSwitchingPane) {
+ fFocusPane= (CompareViewerSwitchingPane) event.widget;
+ }
+ }
+ };
+ fStructureInputPane.addListener(SWT.Activate, activationListener);
+ fStructurePane1.addListener(SWT.Activate, activationListener);
+ fStructurePane2.addListener(SWT.Activate, activationListener);
+ fContentInputPane.addListener(SWT.Activate, activationListener);
+
+ if (fInput instanceof ICompareInput) {
+ ICompareInput input2= (ICompareInput) fInput;
+ fStructureInputPane.setInput(input2);
+ fContentInputPane.setInput(input2);
+ }
+
+ return fComposite;
+ }
+
+ /**
+ * Returns the first element of the given selection if the selection
+ * is a <code>IStructuredSelection</code> with exactly one element. Returns
+ * <code>null</code> otherwise.
+ *
+ * @param selection the selection
+ * @return the first element of the selection, or <code>null</code>
+ */
+ private static Object getElement(ISelection selection) {
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss= (IStructuredSelection) selection;
+ if (ss.size() == 1)
+ return ss.getFirstElement();
+ }
+ return null;
+ }
+
+ /**
+ * Asks this input to take focus within its container (editor).
+ * <p>
+ * Clients should not call this method but they may
+ * override if they implement a different layout with different visual
+ * components. Clients are free to call the inherited method.
+ * </p>
+ */
+ public void setFocus() {
+ if (fFocusPane != null) {
+ Viewer v= fFocusPane.getViewer();
+ if (v != null) {
+ Control c= v.getControl();
+ if (c != null)
+ c.setFocus();
+ }
+ } else if (fComposite != null)
+ fComposite.setFocus();
+ }
+
+ /**
+ * Factory method for creating a differences viewer for the top left pane.
+ * It is called from <code>createContents</code> and returns a <code>DiffTreeViewer</code>.
+ * <p>
+ * Subclasses may override if they need a different viewer.
+ * </p>
+ *
+ * @param parent the SWT parent control under which to create the viewer's SWT controls
+ * @return a compare viewer for the top left pane
+ */
+ public Viewer createDiffViewer(Composite parent) {
+ return new DiffTreeViewer(parent, fCompareConfiguration);
+ }
+
+ /**
+ * Implements the dynamic viewer switching for structure viewers.
+ * The method must return a compare viewer based on the old (or current) viewer
+ * and a new input object. If the old viewer is suitable for showing the new input the old viewer
+ * can be returned. Otherwise a new viewer must be created under the given parent composite or
+ * <code>null</code> can be returned to indicate that no viewer could be found.
+ * <p>
+ * This implementation forwards the request to <code>CompareUI.findStructureViewer</code>.
+ * <p>
+ * Subclasses may override to implement a different strategy.
+ * </p>
+ *
+ * @return a compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent) {
+ return CompareUIPlugin.findStructureViewer(oldViewer, input, parent, fCompareConfiguration);
+ }
+
+ /**
+ * Implements the dynamic viewer switching for content viewers.
+ * The method must return a compare viewer based on the old (or current) viewer
+ * and a new input object. If the old viewer is suitable for showing the new input the old viewer
+ * can be returned. Otherwise a new viewer must be created under the given parent composite or
+ * <code>null</code> can be returned to indicate that no viewer could be found.
+ * <p>
+ * This implementation forwards the request to <code>CompareUI.findContentViewer</code>.
+ * <p>
+ * Subclasses may override to implement a different strategy.
+ * </p>
+ *
+ * @return a compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public Viewer findContentViewer(Viewer oldViewer, ICompareInput input, Composite parent) {
+ Viewer v= CompareUIPlugin.findContentViewer(oldViewer, input, parent, fCompareConfiguration);
+
+ if (v instanceof IPropertyChangeNotifier) {
+ final IPropertyChangeNotifier dsp= (IPropertyChangeNotifier) v;
+
+ dsp.addPropertyChangeListener(fDirtyStateListener);
+
+ Control c= v.getControl();
+ c.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ dsp.removePropertyChangeListener(fDirtyStateListener);
+ }
+ }
+ );
+ }
+
+ return v;
+ }
+
+ /**
+ * Returns <code>true</code> if there are unsaved changes.
+ * The value returned is the value of the <code>DIRTY_STATE</code> property of this input object.
+
+ * Returns <code>true</code> if this input has unsaved changes,
+ * that is if <code>setDirty(true)</code> has been called.
+ * Subclasses don't have to override if the functionality provided by <doce>setDirty</code>
+ * is sufficient.
+ *
+ * @return <code>true</code> if there are changes that need to be saved
+ */
+ public boolean isSaveNeeded() {
+ return fDirty;
+ }
+
+ /**
+ * Sets the dirty state of this input to the given
+ * value and sends out a <code>PropertyChangeEvent</code> if the new value differs from the old value.
+ *
+ * @param dirty the dirty state for this compare input
+ */
+ public void setDirty(boolean dirty) {
+ if (dirty != fDirty) {
+ boolean old= fDirty;
+ fDirty= dirty;
+ Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, new Boolean(old), new Boolean(fDirty));
+ }
+ }
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.addListener
+ */
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ fListenerList.add(listener);
+ }
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.removeListener
+ */
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ fListenerList.remove(listener);
+ }
+
+ /**
+ * Save any unsaved changes.
+ * Empty implementation.
+ * Subclasses must override to save any changes.
+ *
+ * @param progressMonitor an <code>IProgressMonitor</code> that the implementation of save may use to show progress
+ */
+ public void save(IProgressMonitor pm) {
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java
new file mode 100644
index 000000000..1c92fde92
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.IStructureCreatorDescriptor;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.IStructureCreator;
+
+
+/**
+ * The class <code>CompareUI</code> defines the entry point to initiate a configurable
+ * compare operation on arbitrary resources. The result of the compare
+ * is opened into a compare editor where the details can be browsed and
+ * edited in dynamically selected structure and content viewers.
+ * <p>
+ * The Compare UI provides a registry for content and structure compare viewers,
+ * which is initialized from extensions contributed to extension points
+ * declared by this plug-in.
+ */
+public final class CompareUI {
+
+ /**
+ * Name of the title property of a compare viewer.
+ * If a property with this name is set
+ * on the top level SWT control of a viewer, it is used as a title in the pane's
+ * title bar.
+ */
+ public static final String COMPARE_VIEWER_TITLE= "org.eclipse.compare.CompareUI.CompareViewerTitle";
+
+ /* (non Javadoc)
+ * non inatiatiable!
+ */
+ private CompareUI() {
+ }
+
+ public static AbstractUIPlugin getPlugin() {
+ return CompareUIPlugin.getDefault();
+ }
+
+ /**
+ * Returns this plug-in's resource bundle.
+ *
+ * @return the plugin's resource bundle
+ */
+ public static ResourceBundle getResourceBundle() {
+ return CompareUIPlugin.getResourceBundle();
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * compare editor on the result.
+ *
+ * @param input the input on which to open the compare editor
+ * @see ICompareEditorInput
+ */
+ public static void openCompareEditor(CompareEditorInput input) {
+ CompareUIPlugin plugin= CompareUIPlugin.getDefault();
+ if (plugin != null)
+ plugin.openCompareEditor(input);
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * modal compare dialog on the result.
+ *
+ * @param input the input on which to open the compare dialog
+ * @see ICompareEditorInput
+ */
+ public static void openCompareDialog(CompareEditorInput input) {
+ CompareUIPlugin plugin= CompareUIPlugin.getDefault();
+ if (plugin != null)
+ plugin.openCompareDialog(input);
+ }
+
+ /**
+ * Registers an image descriptor for the given type.
+ *
+ * @param type the type
+ * @param descriptor the image descriptor
+ */
+ public static void registerImageDescriptor(String type, ImageDescriptor descriptor) {
+ CompareUIPlugin.registerImageDescriptor(type, descriptor);
+ }
+
+ /**
+ * Returns a shared image for the given type, or a generic image if none
+ * has been registered for the given type.
+ * <p>
+ * Note: Images returned from this method will be automatically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param type the type
+ * @return the image
+ */
+ public static Image getImage(String type) {
+ return CompareUIPlugin.getImage(type);
+ }
+
+ /**
+ * Registers the given image for being disposed when this plug-in is shutdown.
+ *
+ * @param image the image to register for disposal
+ */
+ public static void disposeOnShutdown(Image image) {
+ CompareUIPlugin.disposeOnShutdown(image);
+ }
+
+ /**
+ * Returns a shared image for the given adaptable.
+ * This convenience method queries the given adaptable
+ * for its <code>IWorkbenchAdapter.getImageDescriptor</code>, which it
+ * uses to create an image if it does not already have one.
+ * <p>
+ * Note: Images returned from this method will be automatically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param adaptable the adaptable for which to find an image
+ * @return an image
+ */
+ public static Image getImage(IAdaptable adaptable) {
+ return CompareUIPlugin.getImage(adaptable);
+ }
+
+ /**
+ * Returns a structure compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input, the old viewer
+ * is returned. Otherwise, the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a structure compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a structure viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent,
+ CompareConfiguration configuration) {
+
+ return CompareUIPlugin.findStructureViewer(oldViewer, input, parent, configuration);
+ }
+
+ /**
+ * Returns a content compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input the old viewer
+ * is returned. Otherwise the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a content compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a content viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findContentViewer(Viewer oldViewer, ICompareInput input, Composite parent,
+ CompareConfiguration configuration) {
+
+ return CompareUIPlugin.findContentViewer(oldViewer, input, parent, configuration);
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java
new file mode 100644
index 000000000..76ccf17b8
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java
@@ -0,0 +1,699 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import java.util.HashMap;
+import java.util.ResourceBundle;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.text.*;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFileState;
+
+import org.eclipse.compare.internal.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+/**
+ * A dialog where one input element can be compared against
+ * a list of historic variants (editions) of the same input element.
+ * The dialog can be used to implement functions like "Replace with Version" or
+ * "Replace with Edition" on workbench resources.
+ * <p>
+ * In addition it is possible to specify a subsection of the input element
+ * (e.g. a method in a Java source file) by means of a "path".
+ * In this case the dialog compares only the subsection (as specified by the path)
+ * with the corresponding subsection in the list of editions.
+ * Only those editions are shown where the subsection differs from the same subsection in
+ * another edition thereby minimizing the number of presented variants.
+ * This functionality can be used to implement "Replace with Method Edition"
+ * for the Java language.
+ * <p>
+ * Subsections of an input element are determined by first finding an
+ * <code>IStructureCreator</code> for the input's type.
+ * Then the method <code>locate</code> is used to extract the subsection.
+ * <p>
+ * Each edition (variant in the list of variants) must implement the <code>IModificationDate</code> interface
+ * so that the dialog can sort the editions and present them in a tree structure where every
+ * node corresponds one day.
+ * <p>
+ * The functionality is surfaced in a single function <code>selectEdition</code>.
+ * <p>
+ * Clients may instantiate this class; it is not intended to be subclassed.
+ * </p>
+ *
+ * @see IModificationDate
+ * @see ITypedElement
+ */
+public class EditionSelectionDialog extends Dialog {
+
+ private static class Pair {
+
+ private ITypedElement fEdition;
+ private ITypedElement fItem;
+ private String fContent;
+
+ Pair(ITypedElement edition, ITypedElement item) {
+ fEdition= edition;
+ fItem= item;
+ }
+
+ ITypedElement getEdition() {
+ return fEdition;
+ }
+
+ ITypedElement getItem() {
+ return fItem;
+ }
+
+ void setContent(String content) {
+ fContent= content;
+ }
+
+ String getContent() {
+ return fContent;
+ }
+ }
+
+ private static final int ONE_DAY_MS= 86400 * 1000; // one day in milli seconds
+
+ private boolean fAddMode= false;
+
+ private ResourceBundle fBundle;
+ private boolean fTargetIsRight;
+
+ private HashMap fMemberEditions;
+
+ private ITypedElement fTargetItem;
+ private ITypedElement fSelectedItem;
+
+ private Tree fMemberTree;
+ private Pane fMemberPane;
+
+ private Tree fEditionTree;
+ private Pane fEditionPane;
+
+ private CompareViewerSwitchingPane fContentPane;
+ private Thread fThread;
+
+ private Image fDateImage;
+ private Image fTimeImage;
+
+ private CompareConfiguration fCompareConfiguration;
+
+ /**
+ * Creates a new modal, resizable dialog.
+ * Various titles, icons, and labels are configured from the given resource bundle.
+ * The following resource keys are used:
+ * <pre>
+ * key type description
+ * title String dialog title
+ * width Integer initial width of dialog
+ * height Integer initial height of dialog
+ * targetSide String whether target is on "right" or "left" side; default is "right"
+ * treeTitleFormat MessageFormat pane title for edition tree; arg 0 is the target
+ * dateIcon String icon for node in edition tree; path relative to class
+ * timeIcon String icon for leaf in edition tree; path relative to class
+ * todayFormat MessageFormat format string if date is todays date; arg 0 is date
+ * yesterdayFormat MessageFormat format string if date is yesterdays date; arg 0 is date
+ * dayFormat MessageFormat format string if date is any other date; arg 0 is date
+ * editionLabel String label for editions side of compare viewer; arg 0 is the date
+ * targetLabel String label for target side of compare viewer; arg 0 is
+ * </pre>
+ *
+ * @param parent if not <code>null</code> the new dialog stays on top of this parent shell
+ * @param bundle <code>ResourceBundle</code> to configure the dialog
+ */
+ public EditionSelectionDialog(Shell parent, ResourceBundle bundle) {
+ super(parent);
+ setShellStyle(SWT.CLOSE | SWT.APPLICATION_MODAL | SWT.RESIZE);
+
+ fBundle= bundle;
+
+ fTargetIsRight= "right".equals(Utilities.getString(fBundle, "targetSide", "right"));
+
+ fCompareConfiguration= new CompareConfiguration();
+ fCompareConfiguration.setLeftEditable(false);
+ fCompareConfiguration.setRightEditable(false);
+
+ String iconName= Utilities.getString(fBundle, "dateIcon", "obj16/date.gif");
+ ImageDescriptor id= CompareUIPlugin.getImageDescriptor(iconName);
+ if (id != null)
+ fDateImage= id.createImage();
+ iconName= Utilities.getString(fBundle, "timeIcon", "obj16/time.gif");
+ id= CompareUIPlugin.getImageDescriptor(iconName);
+ if (id != null)
+ fTimeImage= id.createImage();
+ }
+
+ public void setAddMode(boolean mode) {
+ fAddMode= mode;
+ }
+
+ /**
+ * Presents this modal dialog with the functionality described in the class comment above.
+ *
+ * @param target the input object against which the editions are compared; must not be <code>null</code>
+ * @param editions the list of editions (element type: <code>ITypedElement</code>s)
+ * @param path If <code>null</code> dialog shows full input; if non <code>null</code> it extracts a subsection
+ * @return returns the selected edition or <code>null</code> if dialog was cancelled.
+ * The returned <code>ITypedElement</code> is one of the original editions
+ * if <code>path</code> was <code>null</code>; otherwise
+ * it is an <code>ITypedElement</code> returned from <code>IStructureCreator.locate(path, item)</code>
+ */
+ public ITypedElement selectEdition(final ITypedElement target, ITypedElement[] inputEditions, Object ppath) {
+
+ Assert.isNotNull(target);
+ fTargetItem= target;
+
+ // sort input editions
+ final int count= inputEditions.length;
+ final IModificationDate[] editions= new IModificationDate[count];
+ for (int i= 0; i < count; i++)
+ editions[i]= (IModificationDate) inputEditions[i];
+ if (count > 1)
+ internalSort(editions, 0, count-1);
+
+ if (fAddMode)
+ return selectEdition2(target, count, editions, ppath);
+
+ IStructureCreator structureCreator= null;
+ if (ppath != null) { // try to extract subelement
+ String type= target.getType();
+ IStructureCreatorDescriptor scd= CompareUIPlugin.getStructureCreator(type);
+ if (scd != null) {
+ structureCreator= scd.createStructureCreator();
+ if (structureCreator != null) {
+ Object item= structureCreator.locate(ppath, target);
+ if (item instanceof ITypedElement)
+ fTargetItem= (ITypedElement) item;
+ else
+ ppath= null; // couldn't extract item
+ }
+ }
+ }
+
+ // set the left and right labels for the compare viewer
+ String targetLabel= getTargetLabel(target, fTargetItem);
+ if (fTargetIsRight)
+ fCompareConfiguration.setRightLabel(targetLabel);
+ else
+ fCompareConfiguration.setLeftLabel(targetLabel);
+
+ if (structureCreator != null && ppath != null) { // extract sub element
+
+ final IStructureCreator sc= structureCreator;
+ final Object path= ppath;
+
+ // construct the comparer thread
+ // and perform the background extract
+ fThread= new Thread() {
+ public void run() {
+
+ // we only show an edition if its contents is different than
+ // the preceding one.
+ //String lastContents= sc.getContents(fTargetItem, false);
+
+ // from front (newest) to back (oldest)
+ for (int i= 0; i < count; i++) {
+
+ if (fEditionTree == null || fEditionTree.isDisposed())
+ break;
+ ITypedElement edition= (ITypedElement) editions[i];
+
+ // extract sub element from edition
+ Object r= sc.locate(path, edition);
+ if (r instanceof ITypedElement) { // if not empty
+ ITypedElement item= (ITypedElement) r;
+ final Pair pair= new Pair(edition, item);
+ pair.setContent(sc.getContents(item, false));
+ /*
+ if (lastContents != null) {
+ String contents2= sc.getContents(item, false);
+ if (lastContents.equals(contents2))
+ continue;
+ lastContents= contents2;
+ }
+ */
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ addEdition(pair);
+ }
+ }
+ );
+ }
+ }
+ if (fEditionTree != null && !fEditionTree.isDisposed()) {
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ end();
+ }
+ }
+ );
+ }
+ }
+ };
+ } else {
+ // create tree widget
+ create();
+
+ // from front (newest) to back (oldest)
+ for (int i= 0; i < count; i++)
+ addEdition(new Pair((ITypedElement) editions[i], (ITypedElement) editions[i]));
+ }
+
+ open();
+
+ if (getReturnCode() == OK)
+ return fSelectedItem;
+ return null;
+ }
+
+ private ITypedElement selectEdition2(final ITypedElement target, final int count, final IModificationDate[] editions, final Object container) {
+
+ Assert.isNotNull(container);
+
+ // find StructureCreator
+ IStructureCreator structureCreator= null;
+ String type= target.getType();
+ IStructureCreatorDescriptor scd= CompareUIPlugin.getStructureCreator(type);
+ if (scd != null)
+ structureCreator= scd.createStructureCreator();
+ if (structureCreator == null)
+ return null; // error
+
+ // extract all elements of container
+ final HashSet current= new HashSet();
+ IStructureComparator sco= structureCreator.locate(container, target);
+ if (sco != null) {
+ Object[] children= sco.getChildren();
+ if (children != null)
+ for (int i= 0; i < children.length; i++)
+ current.add(children[i]);
+ } else
+ return null; // error
+
+
+ final IStructureCreator sc= structureCreator;
+
+ // construct the comparer thread
+ // and perform the background extract
+ fThread= new Thread() {
+ public void run() {
+
+ // from front (newest) to back (oldest)
+ for (int i= 0; i < count; i++) {
+
+ if (fEditionTree == null || fEditionTree.isDisposed())
+ break;
+ ITypedElement edition= (ITypedElement) editions[i];
+
+ IStructureComparator sco2= sc.locate(container, edition);
+ if (sco2 != null) {
+ Object[] children= sco2.getChildren();
+ if (children != null) {
+ for (int i2= 0; i2 < children.length; i2++) {
+ ITypedElement child= (ITypedElement) children[i2];
+ if (!current.contains(child)) {
+ final Pair pair= new Pair(edition, child);
+ pair.setContent(sc.getContents(child, false));
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ addMemberEdition(pair);
+ }
+ }
+ );
+ }
+ }
+ }
+ }
+ }
+ if (fEditionTree != null && !fEditionTree.isDisposed()) {
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ end();
+ }
+ }
+ );
+ }
+ }
+ };
+
+ open();
+
+ if (getReturnCode() == OK)
+ return fSelectedItem;
+ return null;
+ }
+
+ /**
+ * Returns the input target that has been specified with the most recent call
+ * to <code>selectEdition</code>. If a not <code>null</code> path was specified this method
+ * returns a subsection of this target (<code>IStructureCreator.locate(path, target)</code>)
+ * instead of the input target.
+ * <p>
+ * For example if the <code>target</code> is a Java compilation unit and <code>path</code> specifies
+ * a method, the value returned from <code>getTarget</code> will be the method not the compilation unit.
+ *
+ * @return the last specified target or a subsection thereof.
+ */
+ public ITypedElement getTarget() {
+ return fTargetItem;
+ }
+
+ /**
+ * Returns a label for identifying the target side of a compare viewer.
+ * This implementation extracts the value for the key "targetLabel" from the resource bundle
+ * and passes it as the format argument to <code>MessageFormat.format</code>.
+ * The single format argument for <code>MessageFormat.format</code> ("{0}" in the format string)
+ * is the name of the given input element.
+ * <p>
+ * Subclasses may override to create their own label.
+ * </p>
+ *
+ * @param target the target element for which a label must be returned
+ * @param item if a path has been specified in <code>selectEdition</code> a sub element of the given target; otherwise the same as target
+ * @return a label the target side of a compare viewer
+ */
+ protected String getTargetLabel(ITypedElement target, ITypedElement item) {
+ String format= Utilities.getString(fBundle, "targetLabel", "targetLabel");
+ return MessageFormat.format(format, new Object[] { target.getName() });
+ }
+
+ /**
+ * Returns a label for identifying the edition side of a compare viewer.
+ * This implementation extracts the value for the key "editionLabel" from the resource bundle
+ * and passes it as the format argument to <code>MessageFormat.format</code>.
+ * The single format argument for <code>MessageFormat.format</code> ("{0}" in the format string)
+ * is the formatted modification date of the given input element.
+ * <p>
+ * Subclasses may override to create their own label.
+ * </p>
+ *
+ * @param selectedEdition the selected edition for which a label must be returned
+ * @param item if a path has been specified in <code>selectEdition</code> a sub element of the given selectedEdition; otherwise the same as selectedEdition
+ * @return a label the edition side of a compare viewer
+ */
+ protected String getEditionLabel(ITypedElement selectedEdition, ITypedElement item) {
+ String label= Utilities.getString(fBundle, "editionLabel", "editionLabel");
+
+ if (selectedEdition instanceof IModificationDate) {
+ long modDate= ((IModificationDate)selectedEdition).getModificationDate();
+ String date= DateFormat.getDateTimeInstance().format(new Date(modDate));
+ label= MessageFormat.format(label, new Object[] { date });
+ }
+
+ return label;
+ }
+
+ /* (non Javadoc)
+ * Returns the size initialized with the constructor.
+ */
+ protected Point getInitialSize() {
+ Point size= new Point(Utilities.getInteger(fBundle, "width", 0),
+ Utilities.getInteger(fBundle, "height", 0));
+
+ Shell shell= getParentShell();
+ if (shell != null) {
+ Point parentSize= shell.getSize();
+ if (size.x <= 0)
+ size.x= parentSize.x-300;
+ if (size.y <= 0)
+ size.y= parentSize.y-200;
+ }
+ if (size.x < 700)
+ size.x= 700;
+ if (size.y < 500)
+ size.y= 500;
+ return size;
+ }
+
+ /* (non Javadoc)
+ * Creates SWT control tree.
+ */
+ protected synchronized Control createDialogArea(Composite parent) {
+
+ getShell().setText(Utilities.getString(fBundle, "title", "title"));
+
+ Splitter vsplitter= new Splitter(parent, SWT.VERTICAL);
+ vsplitter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL
+ | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
+
+ vsplitter.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fDateImage != null)
+ fDateImage.dispose();
+ if (fTimeImage != null)
+ fTimeImage.dispose();
+ }
+ }
+ );
+
+
+ if (fAddMode) {
+
+ Splitter hsplitter= new Splitter(vsplitter, SWT.HORIZONTAL);
+
+ fMemberPane= new Pane(hsplitter, SWT.NONE);
+ fMemberPane.setText("Available Members");
+ fMemberTree= new Tree(fMemberPane, SWT.H_SCROLL + SWT.V_SCROLL);
+ fMemberTree.addSelectionListener(
+ new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleMemberSelect(e.item);
+ }
+ }
+ );
+
+ fMemberPane.setContent(fMemberTree);
+
+ fEditionPane= new Pane(hsplitter, SWT.NONE);
+ } else {
+ fEditionPane= new Pane(vsplitter, SWT.NONE);
+ }
+ String titleFormat= Utilities.getString(fBundle, "treeTitleFormat", "treeTitleFormat");
+ String title= MessageFormat.format(titleFormat, new Object[] { fTargetItem.getName() });
+ fEditionPane.setText(title);
+
+ fEditionTree= new Tree(fEditionPane, SWT.H_SCROLL + SWT.V_SCROLL);
+ fEditionTree.addSelectionListener(
+ new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ handleDefaultSelected();
+ }
+ public void widgetSelected(SelectionEvent e) {
+ feedInput(e.item);
+ }
+ }
+ );
+ fEditionPane.setContent(fEditionTree);
+
+ // now start the thread (and forget about it)
+ if (fThread != null) {
+ fThread.start();
+ fThread= null;
+ }
+
+ fContentPane= new CompareViewerSwitchingPane(vsplitter, SWT.NONE) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return CompareUIPlugin.findContentViewer(oldViewer, input, this, fCompareConfiguration);
+ }
+ };
+ vsplitter.setWeights(new int[] { 30, 70 });
+
+ return vsplitter;
+ }
+
+ //---- private stuff ----------------------------------------------------------------------------------------
+
+ private void handleDefaultSelected() {
+ if (fSelectedItem != null)
+ okPressed();
+ }
+
+ private static void internalSort(IModificationDate[] keys, int left, int right) {
+
+ int original_left= left;
+ int original_right= right;
+
+ IModificationDate mid= keys[(left + right) / 2];
+ do {
+ while (keys[left].getModificationDate() > mid.getModificationDate())
+ left++;
+
+ while (mid.getModificationDate() > keys[right].getModificationDate())
+ right--;
+
+ if (left <= right) {
+ IModificationDate tmp= keys[left];
+ keys[left]= keys[right];
+ keys[right]= tmp;
+ left++;
+ right--;
+ }
+ } while (left <= right);
+
+ if (original_left < right)
+ internalSort(keys, original_left, right);
+
+ if (left < original_right)
+ internalSort(keys, left, original_right);
+
+ }
+
+ private void addEdition(Pair pair) {
+
+ if (fEditionTree == null || fEditionTree.isDisposed())
+ return;
+
+ // find last day
+ TreeItem[] days= fEditionTree.getItems();
+ TreeItem lastDay= null;
+ if (days.length > 0)
+ lastDay= days[days.length-1];
+
+ boolean first= lastDay == null;
+
+ ITypedElement edition= pair.getEdition();
+ ITypedElement item= pair.getItem();
+
+ long ldate= ((IModificationDate)edition).getModificationDate();
+ long day= ldate / ONE_DAY_MS;
+ Date date= new Date(ldate);
+ if (lastDay == null || day != ((Date)lastDay.getData()).getTime() / ONE_DAY_MS) {
+ lastDay= new TreeItem(fEditionTree, SWT.NONE);
+ lastDay.setImage(fDateImage);
+ String df= DateFormat.getDateInstance().format(date);
+ long today= System.currentTimeMillis() / ONE_DAY_MS;
+
+ String formatKey;
+ if (day == today)
+ formatKey= "todayFormat";
+ else if (day == today-1)
+ formatKey= "yesterdayFormat";
+ else
+ formatKey= "dayFormat";
+ String pattern= Utilities.getString(fBundle, formatKey, null);
+ if (pattern != null)
+ df= MessageFormat.format(pattern, new Object[] { df });
+ lastDay.setText(df);
+ lastDay.setData(date);
+ }
+ TreeItem ti= new TreeItem(lastDay, SWT.NONE);
+ ti.setImage(fTimeImage);
+ ti.setText(DateFormat.getTimeInstance().format(date));
+ ti.setData(new Pair(edition, item));
+ if (first) {
+ fEditionTree.setSelection(new TreeItem[] {ti});
+ fEditionTree.setFocus();
+ feedInput(ti);
+ }
+ //if (first) // expand first node
+ lastDay.setExpanded(true);
+ }
+
+ private void addMemberEdition(Pair pair) {
+
+ if (fMemberEditions == null)
+ fMemberEditions= new HashMap();
+
+ ITypedElement item= pair.getItem();
+ List editions= (List) fMemberEditions.get(item);
+ if (editions == null) {
+ editions= new ArrayList();
+ fMemberEditions.put(item, editions);
+ if (fMemberTree != null && !fMemberTree.isDisposed()) {
+ TreeItem ti= new TreeItem(fMemberTree, SWT.NULL);
+ String name= ((ITypedElement)item).getName();
+ ti.setText(name);
+ ti.setData(editions);
+ }
+ }
+ editions.add(pair);
+ }
+
+ private void end() {
+ Tree tree= fMemberTree;
+ if (tree == null)
+ tree= fEditionTree;
+ if (tree != null && !tree.isDisposed() && tree.getItemCount() == 0) {
+ TreeItem ti= new TreeItem(tree, SWT.NONE);
+ ti.setText("No Editions found");
+ }
+ }
+
+ private void feedInput(Widget w) {
+ Object input= w.getData();
+ if (input instanceof Pair) {
+ Pair pair= (Pair) input;
+ fSelectedItem= pair.getItem();
+
+ String editionLabel= getEditionLabel(pair.getEdition(), fSelectedItem);
+
+ if (fAddMode) {
+ fContentPane.setInput(fSelectedItem);
+ fContentPane.setText(editionLabel);
+ } else {
+ if (fTargetIsRight) {
+ fCompareConfiguration.setLeftLabel(editionLabel);
+ fContentPane.setInput(new DiffNode(fSelectedItem, fTargetItem));
+ } else {
+ fCompareConfiguration.setRightLabel(editionLabel);
+ fContentPane.setInput(new DiffNode(fTargetItem, fSelectedItem));
+ }
+ }
+ } else {
+ fSelectedItem= null;
+ fContentPane.setInput(null);
+ }
+ }
+
+ private void handleMemberSelect(Widget w) {
+ Object data= w.getData();
+ if (w instanceof TreeItem && data instanceof List) {
+ List list= (List) data;
+ fEditionTree.removeAll();
+ fEditionPane.setText("Editions of " + ((TreeItem)w).getText());
+ Iterator iter= list.iterator();
+ while (iter.hasNext()) {
+ Object item= iter.next();
+ if (item instanceof Pair)
+ addEdition((Pair) item);
+ }
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java
new file mode 100644
index 000000000..898d8bdc1
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import java.io.InputStream;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.core.resources.IFileState;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.*;
+
+/**
+ * A combination <code>IFileState</code> and <code>ITypedElement</code> that can be used as
+ * an input to a compare viewer or other places where an <code>IStreamContentAccessor</code>
+ * is needed.
+ * <p>
+ * <p>
+ * Clients may instantiate this class; it is not intended to be subclassed.
+ * </p>
+ */
+public class HistoryItem implements IStreamContentAccessor, ITypedElement, IModificationDate {
+
+ private ITypedElement fBase;
+ private IFileState fFileState;
+
+ /**
+ * Creates a <code>HistoryItem</code> object which combines the given <code>IFileState</code>
+ * and <code>ITypedElement</code> into an object
+ * which is suitable as input for a compare viewer or <code>ReplaceWithEditionDialog</code>.
+ *
+ * @param base the implementation of the <code>ITypedElement</code> interface delegates to this base <code>ITypedElement</code>
+ * @param fileState the <code>IFileState</code> from which the streamable contents and the modification time is derived from
+ */
+ public HistoryItem(ITypedElement base, IFileState fileState) {
+ fBase= base;
+ fFileState= fileState;
+ }
+
+ /* (non-Javadoc)
+ * see ITypedElement.getName
+ */
+ public String getName() {
+ return fBase.getName();
+ }
+
+ /* (non-Javadoc)
+ * see ITypedElement.getImage
+ */
+ public Image getImage() {
+ return fBase.getImage();
+ }
+
+ /* (non-Javadoc)
+ * see ITypedElement.getType
+ */
+ public String getType() {
+ return fBase.getType();
+ }
+
+ /* (non-Javadoc)
+ * see IModificationDate.getModificationDate
+ */
+ public long getModificationDate() {
+ return fFileState.getModificationTime();
+ }
+
+ /* (non-Javadoc)
+ * see IStreamContentAccessor.getContents
+ */
+ public InputStream getContents() throws CoreException {
+ return fFileState.getContents();
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java
new file mode 100644
index 000000000..01365ae80
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * An <code>IContentChangeListener</code> is informed about content changes of a
+ * <code>IContentChangeNotifier</code>.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IContentChangeNotifier
+ */
+public interface IContentChangeListener {
+
+ /**
+ * Called whenever the content of the given source has changed.
+ *
+ * @param source the source whose contents has changed
+ */
+ void contentChanged(IContentChangeNotifier source);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java
new file mode 100644
index 000000000..714f73775
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * Interface common to all objects that provide a means for registering
+ * for content change notification.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IContentChangeListener
+ */
+public interface IContentChangeNotifier {
+
+ /**
+ * Adds a content change listener to this notifier.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param listener a content changed listener
+ */
+ void addContentChangeListener(IContentChangeListener listener);
+
+ /**
+ * Removes the given content changed listener from this notifier.
+ * Has no effect if the listener is not registered.
+ *
+ * @param listener a content changed listener
+ */
+ void removeContentChangeListener(IContentChangeListener listener);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java
new file mode 100644
index 000000000..f41eecf20
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * Common interface for objects with editable contents.
+ * Typically it is implemented by objects that also implement
+ * the <code>IStreamContentAccessor</code> interface.
+ * <p>
+ * Clients may implement this interface.
+ * <p>
+ * Note that implementing <code>IEditableContent</code> does not
+ * automatically mean that it is editable. An object is only editable if
+ * it implements <code>IEditableContent</code> and the <code>isEditable</code> method returns <code>true</code>.
+ *
+ * @see IStreamContentAccessor
+ */
+public interface IEditableContent {
+
+ /**
+ * Returns <code>true</code> if this object can be modified.
+ * If it returns <code>false</code> the other methods of this API must not be called.
+ *
+ * @return <code>true</code> if this object can be modified
+ */
+ boolean isEditable();
+
+ /**
+ * Replaces the current content with the given new bytes.
+ *
+ * @param newContent this new contents replaces the old contents
+ */
+ void setContent(byte[] newContent);
+
+ /**
+ * This method is called on a parent to
+ * - add a child,
+ * - remove a child,
+ * - copy the contents of a child
+ *
+ * What to do is encoded in the two arguments as follows:
+ * add: child == null other != null
+ * remove: child != null other == null
+ * copy: child != null other != null
+ */
+ ITypedElement replace(ITypedElement child, ITypedElement other);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java
new file mode 100644
index 000000000..a0bcbb9a2
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * Common interface for objects with a modification date. The modification date
+ * can be used in the UI to give the user a general idea of how old an object is.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ */
+public interface IModificationDate {
+
+ /**
+ * Returns the modification time of this object.
+ * <p>
+ * Note that this value should only be used to give the user a general idea of how
+ * old the object is.
+ *
+ * @return the time of last modification, in milliseconds since
+ * January 1, 1970, 00:00:00 GMT
+ */
+ long getModificationDate();
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java
new file mode 100644
index 000000000..b2b0f66ec
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+/**
+ * Interface common to all objects that provide a means for registering
+ * for property change notification.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see org.eclipse.jface.util.IPropertyChangeListener
+ */
+public interface IPropertyChangeNotifier {
+
+ /**
+ * Adds a listener for property changes to this notifier.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param listener a property change listener
+ */
+ void addPropertyChangeListener(IPropertyChangeListener listener);
+
+ /**
+ * Removes the given content change listener from this notifier.
+ * Has no effect if the identical listener is not registered.
+ *
+ * @param listener a property change listener
+ */
+ void removePropertyChangeListener(IPropertyChangeListener listener);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java
new file mode 100644
index 000000000..72c7198ba
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * An <code>IStreamContentAccessor</code> object represents a set of bytes which can be
+ * accessed by means of a stream.
+ * <p>
+ * Clients may implement this interface, or use the standard implementation,
+ * <code>BufferedContent</code>.
+ *
+ * @see BufferedContent
+ */
+public interface IStreamContentAccessor {
+ /**
+ * Returns an open <code>InputStream</code> for this object which can be used to retrieve the object's content.
+ * The client is responsible for closing the stream when finished.
+ * Returns <code>null</code> if this object has no streamable contents.
+ *
+ * @return an input stream containing the contents of this object
+ * @exception CoreException if the contents of this object could not be accessed
+ */
+ InputStream getContents() throws CoreException;
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java
new file mode 100644
index 000000000..83dcda31d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Interface for getting the name, image, and type for an object.
+ * <p>
+ * These methods are typically used to present an input object in the compare UI
+ * (<code>getName</code> and <code>getImage</code>)
+ * and for finding a viewer for a given input type (<code>getType</code>).
+ * <p>
+ * Clients may implement this interface.
+ */
+public interface ITypedElement {
+
+ /**
+ * Type for a folder input. Folders are comparison
+ * elements that have no contents, only a name and children.
+ */
+ public static final String FOLDER_TYPE= "FOLDER";
+
+ /**
+ * Type for an element whose actual type is text.
+ */
+ public static final String TEXT_TYPE= "txt";
+
+ /**
+ * Type for an element whose actual type could not
+ * be determined.
+ */
+ public static final String UNKNOWN_TYPE= "???";
+
+ /**
+ * Returns the name of this object.
+ * The name is used when displaying this object in the UI.
+ *
+ * @return the name of this object
+ */
+ String getName();
+
+ /**
+ * Returns an image for this object.
+ * This image is used when displaying this object in the UI.
+ *
+ * @return the image of this object or <code>null</code> if this type of input has no image
+ */
+ Image getImage();
+
+ /**
+ * Returns the type of this object. For objects with a file name
+ * this is typically the file extension. For folders its the constant
+ * <code>FOLDER_TYPE</code>.
+ * The type is used for determining a suitable viewer for this object.
+ *
+ * @return the type of this object
+ */
+ String getType();
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java
new file mode 100644
index 000000000..142341827
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A factory object for <code>Viewer</code>.
+ * <p>
+ * This interface is only required when creating a <code>Viewer</code> from a plugin.xml file.
+ * Since <code>Viewer</code>s have no default constructor they cannot be
+ * instantiated directly with <code>Class.forName</code>.
+ */
+public interface IViewerCreator {
+
+ /**
+ * Creates a new viewer under the given SWT parent control.
+ *
+ * @param parent the SWT parent control under which to create the viewer's SWT control
+ * @param config a compare configuration the newly created viewer might want to use
+ * @return a new viewer
+ */
+ Viewer createViewer(Composite parent, CompareConfiguration config);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java
new file mode 100644
index 000000000..445c90a18
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.structuremergeviewer.IStructureComparator;
+
+/**
+ * A <code>ResourceNode</code> wrappers an <code>IResources</code> so that it can be used
+ * as input for the differencing engine (interfaces <code>IStructureComparator</code> and <code>ITypedElement</code>)
+ * and the <code>ReplaceWithEditionDialog</code> (interfaces <code>ITypedElement</code> and <code>IModificationDate</code>).
+ * <p>
+ * Clients may instantiate this class; it is not intended to be subclassed.
+ * </p>
+ *
+ * @see EditionSelectionDialog
+ */
+public class ResourceNode extends BufferedContent
+ implements IStructureComparator, ITypedElement, IEditableContent, IModificationDate {
+
+ private static final int NOTHING= 0; // nothing to do
+ private static final int UPDATE= 1; // need to save content
+ private static final int DELETE= 2; // need to delete file
+
+ private IResource fResource;
+ private ArrayList fChildren;
+ private String fName;
+ private int fAction= NOTHING; // what to do on commit
+
+
+ /**
+ * Creates a <code>ResourceNode</code> for the given resource.
+ *
+ * @param resource the resource
+ */
+ public ResourceNode(IResource resource) {
+ fResource= resource;
+ Assert.isNotNull(resource);
+ }
+
+ /**
+ * Returns the corresponding resource for this object.
+ *
+ * @return the corresponding resource
+ */
+ public IResource getResource() {
+ return fResource;
+ }
+
+ /* (non Javadoc)
+ * see IStreamContentAccessor.getContents
+ */
+ public InputStream getContents() throws CoreException {
+ if (fResource instanceof IStorage)
+ return super.getContents();
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IModificationDate.getModificationDate
+ */
+ public long getModificationDate() {
+ IPath path= fResource.getLocation();
+ File file= path.toFile();
+ return file.lastModified();
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getName
+ */
+ public String getName() {
+ if (fResource != null)
+ return fResource.getName();
+ return fName;
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getType
+ */
+ public String getType() {
+ if (fResource instanceof IContainer)
+ return ITypedElement.FOLDER_TYPE;
+ if (fResource != null) {
+ String s= fResource.getFileExtension();
+ if (s != null)
+ return s;
+ }
+ if (fName != null) {
+ int pos= fName.lastIndexOf('.');
+ if (pos >= 0)
+ return fName.substring(pos+1);
+ }
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getImage
+ */
+ public Image getImage() {
+ return CompareUI.getImage(fResource);
+ }
+
+ /**
+ * Returns <code>true</code> if the other object is of type <code>ITypedElement</code>
+ * and their names are identical. The content is not considered.
+ */
+ /* (non Javadoc)
+ * see IStructureComparator.equals
+ */
+ public boolean equals(Object other) {
+ if (other instanceof ITypedElement) {
+ String otherName= ((ITypedElement)other).getName();
+ return getName().equals(otherName);
+ }
+ return super.equals(other);
+ }
+
+ /**
+ * Returns the hash code of the name.
+ */
+ /* (non Javadoc)
+ * see IStructureComparator.hashCode
+ */
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ /* (non Javadoc)
+ * see IStructureComparator.getChildren
+ */
+ public Object[] getChildren() {
+ if (fChildren == null) {
+ fChildren= new ArrayList();
+ if (fResource instanceof IContainer) {
+ try {
+ IResource members[]= ((IContainer)fResource).members();
+ for (int i= 0; i < members.length; i++) {
+ IStructureComparator child= createChild(members[i]);
+ if (child != null)
+ fChildren.add(child);
+ }
+ } catch (CoreException ex) {
+ }
+ }
+ }
+ return fChildren.toArray();
+ }
+
+ /**
+ * This hook method is called from <code>getChildren</code> once for every
+ * member of a container resource. This implementation
+ * creates a new <code>ResourceNode</code> for the given child resource.
+ * Clients may override this method to create a different type of
+ * <code>IStructureComparator</code> or to filter children by returning <code>null</code>.
+ *
+ * @param child the child resource for which a <code>IStructureComparator</code> must be returned
+ * @return a <code>ResourceNode</code> for the given child or <code>null</code>
+ */
+ protected IStructureComparator createChild(IResource child) {
+ return new ResourceNode(child);
+ }
+
+ /**
+ * Returns an open stream if the corresponding resource implements the
+ * <code>IStorage</code> interface. Otherwise the value <code>null</code> is returned.
+ *
+ * @return a buffered input stream containing the contents of this storage
+ * @exception CoreException if the contents of this storage could not be accessed
+ */
+ protected InputStream createStream() throws CoreException {
+ if (fResource instanceof IStorage)
+ return new BufferedInputStream(((IStorage)fResource).getContents());
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IEditableContent.isEditable
+ */
+ public boolean isEditable() {
+ return true;
+ }
+
+ /**
+ * FIXME: Really needed?
+ */
+ public ITypedElement replace(ITypedElement child, ITypedElement other) {
+ if (other == null) {
+ if (child != null)
+ System.out.println("delete " + child.getName());
+ else
+ System.out.println("ResourceNode.replace: oops");
+ } else {
+ if (child == null) {
+ System.out.println("create " + other.getName());
+ //child= new ResourceNode(fResource, other.getName());
+ } else {
+ System.out.println("update " + child.getName());
+ // copy contents
+ if (other instanceof IStreamContentAccessor) {
+ try {
+ InputStream is= ((IStreamContentAccessor)other).getContents();
+ byte[] bytes= Utilities.readBytes(is);
+ if (bytes != null)
+ setContent(bytes);
+ } catch (CoreException ex) {
+ }
+ }
+ }
+ }
+ return child;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java
new file mode 100644
index 000000000..a456529e1
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java
@@ -0,0 +1,948 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.custom.CLabel;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.compare.contentmergeviewer.IMergeViewerContentProvider;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+import org.eclipse.compare.internal.*;
+
+/**
+ * An abstract compare and merge viewer with two side-by-side content areas
+ * and an optional content area for the ancestor. The implementation makes no
+ * assumptions about the content type.
+ * <p>
+ * <code>ContentMergeViewer</code>
+ * <ul>
+ * <li>implements the overall layout and defines hooks so that subclasses
+ * can easily provide an implementation for a specific content type,
+ * <li>implements the UI for making the areas resizable,
+ * <li>has an action for controlling whether the ancestor area is visible or not,
+ * <li>has actions for copying one side of the input to the other side,
+ * <li>tracks the dirty state of the left and right sides and send out notification
+ * on state changes.
+ * </ul>
+ * A <code>ContentMergeViewer</code> accesses its
+ * model by means of a content provider which must implement the
+ * <code>IMergeViewerContentProvider</code> interface.
+ * </p>
+ * <p>
+ * Clients may wish to use the standard concrete subclass <code>TextMergeViewer</code>,
+ * or define their own subclass.
+ *
+ * @see IMergeViewerContentProvider
+ * @see TextMergeViewer
+ */
+public abstract class ContentMergeViewer extends ContentViewer implements IPropertyChangeNotifier {
+
+ /**
+ * Property names.
+ */
+ private static final String ANCESTOR_ENABLED= "ANCESTOR_ENABLED";
+
+ /* package */ static final int HORIZONTAL= 1;
+ /* package */ static final int VERTICAL= 2;
+
+ static final double HSPLIT= 0.5;
+ static final double VSPLIT= 0.3;
+
+ private class ContentMergeViewerLayout extends Layout {
+
+ public Point computeSize(Composite c, int w, int h, boolean force) {
+ return new Point(100, 100);
+ }
+
+ public void layout(Composite composite, boolean force) {
+
+ // determine some derived sizes
+ int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
+ Rectangle r= composite.getClientArea();
+
+ int centerWidth= getCenterWidth();
+ int width1= (int)((r.width-centerWidth)*fHSplit);
+ int width2= r.width-width1-centerWidth;
+
+ int height1= 0;
+ int height2= 0;
+ if (fAncestorEnabled && fShowAncestor) {
+ height1= (int)((r.height-(2*headerHeight))*fVSplit);
+ height2= r.height-(2*headerHeight)-height1;
+ } else {
+ height1= 0;
+ height2= r.height-headerHeight;
+ }
+
+ int y= 0;
+
+ if (fAncestorEnabled && fShowAncestor) {
+ fAncestorLabel.setBounds(0, y, r.width, headerHeight);
+ fAncestorLabel.setVisible(true);
+ y+= headerHeight;
+ handleResizeAncestor(0, y, r.width, height1);
+ y+= height1;
+ } else {
+ fAncestorLabel.setVisible(false);
+ handleResizeAncestor(0, 0, 0, 0);
+ }
+
+ fLeftLabel.getSize(); // WORKAROUND FOR PR
+
+ if (centerWidth > 3) {
+ fLeftLabel.setBounds(0, y, width1+1, headerHeight);
+ fDirectionLabel.setVisible(true);
+ fDirectionLabel.setBounds(width1+1, y, centerWidth-1, headerHeight);
+ fRightLabel.setBounds(width1+centerWidth, y, width2, headerHeight);
+ } else {
+ fLeftLabel.setBounds(0, y, width1, headerHeight);
+ fDirectionLabel.setVisible(false);
+ fRightLabel.setBounds(width1, y, r.width-width1, headerHeight);
+ }
+
+ y+= headerHeight;
+
+ if (fCenter != null && !fCenter.isDisposed())
+ fCenter.setBounds(width1, y, centerWidth, height2);
+
+ handleResizeLeftRight(0, y, width1, centerWidth, width2, height2);
+ }
+ }
+
+ class Resizer extends MouseAdapter implements MouseMoveListener {
+
+ Control fControl;
+ int fX, fY;
+ int fWidth1, fWidth2;
+ int fHeight1, fHeight2;
+ int fDirection;
+ boolean fLiveResize;
+
+ public Resizer(Control c, int dir) {
+ fDirection= dir;
+ fControl= c;
+ fControl.addMouseListener(this);
+ fLiveResize= !(fControl instanceof Sash);
+
+ fControl.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ fControl= null;
+ }
+ }
+ );
+ }
+
+ public void mouseDoubleClick(MouseEvent e) {
+ if ((fDirection & HORIZONTAL) != 0)
+ fHSplit= HSPLIT;
+ if ((fDirection & VERTICAL) != 0)
+ fVSplit= VSPLIT;
+ fComposite.layout(true);
+ }
+
+ public void mouseDown(MouseEvent e) {
+ Composite parent= fControl.getParent();
+
+ Point s= parent.getSize();
+ Point as= fAncestorLabel.getSize();
+ Point ys= fLeftLabel.getSize();
+ Point ms= fRightLabel.getSize();
+
+ fWidth1= ys.x;
+ fWidth2= ms.x;
+ fHeight1= fLeftLabel.getLocation().y-as.y;
+ fHeight2= s.y-(fLeftLabel.getLocation().y+ys.y);
+
+ fX= e.x;
+ fY= e.y;
+ fControl.addMouseMoveListener(this);
+ }
+
+ public void mouseUp(MouseEvent e) {
+ fControl.removeMouseMoveListener(this);
+ if (!fLiveResize)
+ resize(e);
+ }
+
+ public void mouseMove(MouseEvent e) {
+ if (fLiveResize)
+ resize(e);
+ }
+
+ private void resize(MouseEvent e) {
+ int dx= e.x-fX;
+ int dy= e.y-fY;
+
+ int centerWidth= fCenter.getSize().x;
+
+ if (fWidth1 + dx > centerWidth && fWidth2 - dx > centerWidth) {
+ fWidth1+= dx;
+ fWidth2-= dx;
+ if ((fDirection & HORIZONTAL) != 0)
+ fHSplit= (double)fWidth1/(double)(fWidth1+fWidth2);
+ }
+ if (fHeight1 + dy > centerWidth && fHeight2 - dy > centerWidth) {
+ fHeight1+= dy;
+ fHeight2-= dy;
+ if ((fDirection & VERTICAL) != 0)
+ fVSplit= (double)fHeight1/(double)(fHeight1+fHeight2);
+ }
+
+ fComposite.layout(true);
+ fControl.getDisplay().update();
+ }
+ };
+
+ /** Style bits for top level composite */
+ private int fStyles;
+ private ResourceBundle fBundle;
+ private CompareConfiguration fCompareConfiguration;
+ private IPropertyChangeListener fPropertyChangeListener;
+ private ICompareInputChangeListener fCompareInputChangeListener;
+ private ListenerList fListenerList;
+
+ private boolean fLeftDirty; // left side is dirty
+ private boolean fRightDirty; // right side is dirty
+
+ private double fHSplit= HSPLIT; // width ratio of left and right panes
+ private double fVSplit= VSPLIT; // height ratio of ancestor and bottom panes
+
+ private boolean fAncestorEnabled= true; // show ancestor in case of conflicts
+ /* package */ boolean fShowAncestor= false; // if current input has conflicts
+ private boolean fIsThreeWay= false;
+ private ActionContributionItem fAncestorItem;
+
+ private Action fCopyLeftToRightAction; // copy from left to right
+ private Action fCopyRightToLeftAction; // copy from right to left
+
+ // SWT widgets
+ /* package */ Composite fComposite;
+ private CLabel fAncestorLabel;
+ private CLabel fLeftLabel;
+ private CLabel fRightLabel;
+ private CLabel fDirectionLabel;
+ /* package */ Control fCenter;
+
+ //---- SWT resources to be disposed
+ private Image fRightArrow;
+ private Image fLeftArrow;
+ private Image fBothArrow;
+ //---- end
+
+ /**
+ * Creates a new content merge viewer and initializes with a resource bundle and a
+ * configuration.
+ *
+ * @param bundle the resource bundle
+ * @param cc the configuration object
+ */
+ protected ContentMergeViewer(int style, ResourceBundle bundle, CompareConfiguration cc) {
+ fStyles= style;
+ fBundle= bundle;
+
+ fAncestorEnabled= Utilities.getBoolean(cc, ANCESTOR_ENABLED, fAncestorEnabled);
+
+ setContentProvider(new MergeViewerContentProvider(cc));
+
+ fCompareInputChangeListener= new ICompareInputChangeListener() {
+ public void compareInputChanged(ICompareInput input) {
+ ContentMergeViewer.this.compareInputChanged(input);
+ }
+ };
+
+ fPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ ContentMergeViewer.this.propertyChange(event);
+ }
+ };
+
+ fCompareConfiguration= cc;
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.addPropertyChangeListener(fPropertyChangeListener);
+ }
+
+ //---- hooks ---------------------
+
+ /**
+ * Returns the viewer's name.
+ *
+ * @return the viewer's name
+ */
+ public String getTitle() {
+ return "Content Compare";
+ }
+
+ /**
+ * Creates the SWT controls for the ancestor, left, and right
+ * content areas of this compare viewer.
+ * Implementations typically hold onto the controls
+ * so that they can be initialized with the input objects in method
+ * <code>updateContent</code>.
+ *
+ * @param composite the container for the three areas
+ */
+ abstract protected void createControls(Composite composite);
+
+ /**
+ * Lays out the ancestor area of the compare viewer.
+ * It is called whenever the viewer is resized or when the sashes between
+ * the areas are moved to adjust the size of the areas.
+ *
+ * @param x the horizontal position of the ancestor area within its container
+ * @param y the vertical position of the ancestor area within its container
+ * @param width the width of the ancestor area
+ * @param height the height of the ancestor area
+ */
+ abstract protected void handleResizeAncestor(int x, int y, int width, int height);
+
+ /**
+ * Lays out the left and right areas of the compare viewer.
+ * It is called whenever the viewer is resized or when the sashes between
+ * the areas are moved to adjust the size of the areas.
+ *
+ * @param x the horizontal position of the left area within its container
+ * @param y the vertical position of the left and right area within its container
+ * @param leftWidth the width of the left area
+ * @param centerWidth the width of the gap between the left and right areas
+ * @param rightWidth the width of the right area
+ * @param height the height of the left and right areas
+ */
+ abstract protected void handleResizeLeftRight(int x, int y, int leftWidth, int centerWidth,
+ int rightWidth, int height);
+
+ /**
+ * Contributes items to the given <code>ToolBarManager</code>.
+ * It is called when this viewer is installed in its container and if the container
+ * has a <code>ToolBarManager</code>.
+ * The <code>ContentMergeViewer</code> implementation of this method does nothing.
+ * Subclasses may reimplement.
+ *
+ * @param toolBarManager the toolbar manager to contribute to
+ */
+ protected void createToolItems(ToolBarManager toolBarManager) {
+ }
+
+ /**
+ * Initializes the controls of the three content areas with the given input objects.
+ *
+ * @param ancestor the input for the ancestor area
+ * @param left the input for the left area
+ * @param right the input for the right area
+ */
+ abstract protected void updateContent(Object ancestor, Object left, Object right);
+
+ /**
+ * Copies the content of one side to the other side.
+ * Called from the (internal) actions for copying the sides of the viewer's 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
+ */
+ abstract protected void copy(boolean leftToRight);
+
+ /**
+ * Returns the byte contents of the left or right side. If the viewer
+ * has no editable content <code>null</code> can be returned.
+ *
+ * @param left if <code>true</code>, the byte contents of the left area is returned;
+ * if <code>false</code>, the byte contents of the right area
+ * @return the content as an array of bytes, or <code>null</code>
+ */
+ abstract protected byte[] getContents(boolean left);
+
+ //----------------------------
+
+ /**
+ * Returns the resource bundle of this viewer.
+ *
+ * @return the resource bundle
+ */
+ protected ResourceBundle getResourceBundle() {
+ return fBundle;
+ }
+
+ /**
+ * Returns the compare configuration of this viewer,
+ * or <code>null</code> if this viewer does not yet have a configuration.
+ *
+ * @return the compare configuration, or <code>null</code> if none
+ */
+ protected CompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>ContentViewer</code> method
+ * checks to ensure that the content provider is an <code>IMergeViewerContentProvider</code>.
+ */
+ public void setContentProvider(IContentProvider contentProvider) {
+ Assert.isTrue(contentProvider instanceof IMergeViewerContentProvider);
+ super.setContentProvider(contentProvider);
+ }
+
+ /* package */ IMergeViewerContentProvider getMergeContentProvider() {
+ return (IMergeViewerContentProvider) getContentProvider();
+ }
+
+ public void refresh() {
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>Viewer</code> method returns the empty selection. Subclasses may override.
+ */
+ public ISelection getSelection() {
+ return new ISelection() {
+ public boolean isEmpty() {
+ return true;
+ }
+ };
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>Viewer</code> method does nothing. Subclasses may reimplement.
+ */
+ public void setSelection(ISelection s, boolean reveal) {
+ }
+
+ /* package */ void propertyChange(PropertyChangeEvent event) {
+
+ String key= event.getProperty();
+// if (key.equals(ICompareConfiguration.MERGE_DIRECTION)) {
+// if (isDirty() && !saveContents(true, true))
+// return;
+// ToolBarManager tbm= null;
+// IVisualContainer vc= getContainer();
+// if (vc instanceof Pane)
+// tbm= ((Pane)vc).getToolBarManager();
+// if (tbm != null) {
+// updateToolItems();
+// tbm.update(true);
+// }
+//
+// updateDirectionLabel();
+// } else
+ if (key.equals(ANCESTOR_ENABLED)) {
+ fAncestorEnabled= Utilities.getBoolean(getCompareConfiguration(), ANCESTOR_ENABLED, fAncestorEnabled);
+ fComposite.layout(true);
+ }
+ }
+
+ //---- input
+
+ /* package */ boolean isThreeWay() {
+ return fIsThreeWay;
+ }
+
+ /**
+ * Internal hook method called when the input to this viewer is
+ * initially set or subsequently changed.
+ * <p>
+ * The <code>ContentMergeViewer</code> implementation of this <code>Viewer</code>
+ * method retrieves the content from the three sides by calling the methods
+ * <code>getAncestorContent</code>, <code>getLeftContent</code>,
+ * and <code>getRightContent</code> on the content provider.
+ * The values returned from these calls are passed to the hook method <code>updateContent</code>.
+ * </p>
+ *
+ * @param input the new input of this viewer, or <code>null</code> if none
+ * @param oldInput the old input element, or <code>null</code> if there
+ * was previously no input
+ */
+ protected final void inputChanged(Object input, Object oldInput) {
+
+ if (oldInput instanceof ICompareInput)
+ ((ICompareInput)oldInput).removeCompareInputChangeListener(fCompareInputChangeListener);
+
+ // before setting the new input we have to save the old
+ if (fLeftDirty || fRightDirty)
+ saveContent(oldInput);
+
+ if (input instanceof ICompareInput)
+ ((ICompareInput)input).addCompareInputChangeListener(fCompareInputChangeListener);
+
+ setLeftDirty(false);
+ setRightDirty(false);
+
+ // determine the merge direction
+ //boolean rightEditable= fMergeViewerContentProvider.isRightEditable(input);
+ //boolean leftEditable= fMergeViewerContentProvider.isLeftEditable(input);
+
+// if (fInput instanceof ICompareInput)
+// fIsThreeWay= (((ICompareInput)fInput).getChangeType() & Differencer.DIRECTION_MASK) != 0;
+// else
+// fIsThreeWay= true;
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ Object ancestor= content.getAncestorContent(input);
+ fIsThreeWay= ancestor != null;
+
+ if (fAncestorItem != null)
+ fAncestorItem.setVisible(fIsThreeWay);
+
+ boolean oldFlag= fShowAncestor;
+ fShowAncestor= fIsThreeWay && content.showAncestor(input);
+
+ if (fAncestorEnabled && oldFlag != fShowAncestor)
+ fComposite.layout(true);
+
+ ToolBarManager tbm= CompareViewerSwitchingPane.getToolBarManager(fComposite.getParent());
+ if (tbm != null) {
+ updateToolItems();
+ tbm.update(true);
+ tbm.getControl().getParent().layout(true);
+ }
+
+ updateHeader();
+
+ Object left= content.getLeftContent(input);
+ Object right= content.getRightContent(input);
+ updateContent(ancestor, left, right);
+ }
+
+ private void compareInputChanged(ICompareInput input) {
+
+ if (input != null) {
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ Object ancestor= content.getAncestorContent(input);
+ Object left= content.getLeftContent(input);
+ Object right= content.getRightContent(input);
+
+ updateContent(ancestor, left, right);
+ }
+ }
+
+ //---- layout & SWT control creation
+
+ /**
+ * Builds the SWT controls for the three areas of a compare/merge viewer.
+ * <p>
+ * Calls the hooks <code>createControls</code> and <code>createToolItems</code>
+ * to let subclasses build the specific content areas and to add items to
+ * an enclosing toolbar.
+ * <p>
+ * This method must only be called in the constructor of subclasses.
+ *
+ * @param parent the parent control
+ * @return the new control
+ */
+ protected final Control buildControl(Composite parent) {
+
+ fComposite= new Composite(parent, fStyles) {
+ public boolean setFocus() {
+ return internalSetFocus();
+ }
+ };
+ fComposite.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
+
+ hookControl(fComposite); // hook help & dispose listener
+
+ fComposite.setLayout(new ContentMergeViewerLayout());
+
+ int style= SWT.SHADOW_OUT;
+ fAncestorLabel= new CLabel(fComposite, style);
+
+ fLeftLabel= new CLabel(fComposite, style);
+ new Resizer(fLeftLabel, VERTICAL);
+
+ fDirectionLabel= new CLabel(fComposite, style);
+ fDirectionLabel.setAlignment(SWT.CENTER);
+ new Resizer(fDirectionLabel, HORIZONTAL | VERTICAL);
+
+ fRightLabel= new CLabel(fComposite, style);
+ new Resizer(fRightLabel, VERTICAL);
+
+ if (fCenter == null || fCenter.isDisposed())
+ fCenter= createCenter(fComposite);
+
+ createControls(fComposite);
+
+ ToolBarManager tbm= CompareViewerSwitchingPane.getToolBarManager(parent);
+ if (tbm != null) {
+ tbm.removeAll();
+
+ // define groups
+ tbm.add(new Separator("merge"));
+ tbm.add(new Separator("modes"));
+ tbm.add(new Separator("navigation"));
+
+ CompareConfiguration cc= getCompareConfiguration();
+
+ if (cc.isRightEditable()) {
+ fCopyLeftToRightAction=
+ new Action() {
+ public void run() {
+ copy(true);
+ }
+ };
+ Utilities.initAction(fCopyLeftToRightAction, getResourceBundle(), "action.CopyLeftToRight.");
+ tbm.appendToGroup("merge", fCopyLeftToRightAction);
+ }
+
+ if (cc.isLeftEditable()) {
+ fCopyRightToLeftAction=
+ new Action() {
+ public void run() {
+ copy(false);
+ }
+ };
+ Utilities.initAction(fCopyRightToLeftAction, getResourceBundle(), "action.CopyRightToLeft.");
+ tbm.appendToGroup("merge", fCopyRightToLeftAction);
+ }
+
+ Action a= new ChangePropertyAction(fBundle, fCompareConfiguration, "action.EnableAncestor.", ANCESTOR_ENABLED);
+ a.setChecked(fAncestorEnabled);
+ fAncestorItem= new ActionContributionItem(a);
+ //fAncestorItem.setVisible(false);
+ tbm.appendToGroup("modes", fAncestorItem);
+
+ createToolItems(tbm);
+ updateToolItems();
+
+ tbm.update(true);
+ }
+
+ return fComposite;
+ }
+
+ /* package */ boolean internalSetFocus() {
+ return false;
+ }
+
+ /* package */ int getCenterWidth() {
+ return 3;
+ }
+
+ /* package */ Control createCenter(Composite parent) {
+ Sash sash= new Sash(parent, SWT.VERTICAL);
+ new Resizer(sash, HORIZONTAL);
+ return sash;
+ }
+
+ /* package */ Control getCenter() {
+ return fCenter;
+ }
+
+ /*
+ * @see Viewer.getControl()
+ */
+ public Control getControl() {
+ return fComposite;
+ }
+
+ /**
+ * 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) {
+
+ Object input= getInput();
+ if (input instanceof ICompareInput)
+ ((ICompareInput)input).removeCompareInputChangeListener(fCompareInputChangeListener);
+
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.removePropertyChangeListener(fPropertyChangeListener);
+
+ fAncestorLabel= null;
+ fLeftLabel= null;
+ fDirectionLabel= null;
+ fRightLabel= null;
+ fCenter= null;
+
+ if (fRightArrow != null) {
+ fRightArrow.dispose();
+ fRightArrow= null;
+ }
+ if (fLeftArrow != null) {
+ fLeftArrow.dispose();
+ fLeftArrow= null;
+ }
+ if (fBothArrow != null) {
+ fBothArrow.dispose();
+ fBothArrow= null;
+ }
+
+ super.handleDispose(event);
+ }
+
+ /**
+ * Updates the enabled state of the toolbar items.
+ * <p>
+ * This method is called whenever the state of the items needs updating.
+ * <p>
+ * Subclasses may extend this method, although this is generally not required.
+ */
+ protected void updateToolItems() {
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+
+ if (fCopyLeftToRightAction != null)
+ fCopyLeftToRightAction.setEnabled(content.isRightEditable(input));
+
+ if (fCopyRightToLeftAction != null)
+ fCopyRightToLeftAction.setEnabled(content.isLeftEditable(input));
+ }
+
+// protected void createToolItems(ToolBarManager tbm) {
+//
+// if (USE_MORE_CONTROLS) {
+// fBothItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptBoth.") {
+// public void actionPerformed(Window w) {
+// accept(fCurrentDiff, true, false);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fBothItem);
+//
+// fAutoItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptAll.") {
+// public void actionPerformed(Window w) {
+// autoResolve();
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fAutoItem);
+// }
+// fRejectItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptIgnoreNow.") {
+// public void actionPerformed(Window w) {
+// reject(fCurrentDiff, true);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fRejectItem);
+//
+// Action a= new ChangePropertyAction(getBundle(), TextMergeViewer.class, getCompareConfiguration(), "action.SynchMode.", SYNC_SCROLLING);
+// a.setChecked(fSynchronizedScrolling);
+//// tbm.appendToGroup("modes", a);
+// tbm.add(a);
+//
+// tbm.add(new Separator());
+//
+// a= new Action() {
+// public void actionPerformed() {
+// navigate(true);
+// }
+// };
+// CompareUIPlugin.init(a, TextMergeViewer.class, getBundle(), "action.NextDiff.");
+// fNextItem= new ActionContributionItem(a);
+// //tbm.appendToGroup("navigation", fNextItem);
+// tbm.add(fNextItem);
+//
+// a= new Action() {
+// public void actionPerformed() {
+// navigate(false);
+// }
+// };
+// CompareUIPlugin.init(a, TextMergeViewer.class, getBundle(), "action.PrevDiff.");
+// fPreviousItem= new ActionContributionItem(a);
+// //tbm.appendToGroup("navigation", fPreviousItem);
+// tbm.add(fPreviousItem);
+// }
+
+ /**
+ * Updates the headers of the three areas
+ * by querying the content provider for a name and image for
+ * the three sides of the input object.
+ * <p>
+ * This method is called whenever the header must be updated.
+ * <p>
+ * Subclasses may extend this method, although this is generally not required.
+ */
+ protected void updateHeader() {
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+
+ if (fAncestorLabel != null) {
+ fAncestorLabel.setImage(content.getAncestorImage(input));
+ fAncestorLabel.setText(content.getAncestorLabel(input));
+ }
+ if (fLeftLabel != null) {
+ fLeftLabel.setImage(content.getLeftImage(input));
+ fLeftLabel.setText(content.getLeftLabel(input));
+ }
+ if (fRightLabel != null) {
+ fRightLabel.setImage(content.getRightImage(input));
+ fRightLabel.setText(content.getRightLabel(input));
+ }
+
+ updateDirectionLabel();
+ }
+
+ private Image loadImage(String name) {
+ ImageDescriptor id= ImageDescriptor.createFromFile(ContentMergeViewer.class, name);
+ if (id != null)
+ return id.createImage();
+ return null;
+ }
+
+ private void updateDirectionLabel() {
+// if (fDirectionLabel != null) {
+// Image image= null;
+//
+// //if (fMergePolicy.hasMergeDirection()) {
+// boolean y= fCompareConfiguration.isLeftEditable();
+// boolean m= fCompareConfiguration.isRightEditable();
+//
+// if (y && m) {
+// if (fBothArrow == null)
+// fBothArrow= loadImage("images/both.gif");
+// image= fBothArrow;
+// } else if (y) {
+// if (fLeftArrow == null)
+// fLeftArrow= loadImage("images/yours.gif");
+// image= fLeftArrow;
+// } else if (m) {
+// if (fRightArrow == null)
+// fRightArrow= loadImage("images/mine.gif");
+// image= fRightArrow;
+// }
+// //}
+//
+// if (image != null)
+// fDirectionLabel.setImage(image);
+// else
+// fDirectionLabel.setText("");
+// }
+ }
+
+ /**
+ * Calculates the height of the header.
+ */
+ /* package */ int getHeaderHeight() {
+ int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
+ headerHeight= Math.max(headerHeight, fDirectionLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y);
+ return headerHeight;
+ }
+
+ //---- merge direction
+
+ /**
+ * Returns true if both sides are editable.
+ */
+ /* package */ boolean canToggleMergeDirection() {
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+ return content.isLeftEditable(input) && content.isRightEditable(input);
+ }
+
+ //---- dirty state & saving state
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.addPropertyChangeListener
+ */
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ if (fListenerList == null)
+ fListenerList= new ListenerList();
+ fListenerList.add(listener);
+ }
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.removePropertyChangeListener
+ */
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ if (fListenerList != null) {
+ fListenerList.remove(listener);
+ if (fListenerList.isEmpty())
+ fListenerList= null;
+ }
+ }
+
+ /* package */ void fireDirtyState(boolean state) {
+ Utilities.firePropertyChange(fListenerList, this, CompareEditorInput.DIRTY_STATE, null, new Boolean(state));
+ }
+
+ /**
+ * Sets the dirty state of the left side of this viewer.
+ * If the new value differs from the old
+ * all registered listener are notified with
+ * a <code>PropertyChangeEvent</code> with the
+ * property name <code>CompareEditorInput.DIRTY_STATE</code>.
+ *
+ * @param dirty the state of the left side dirty flag
+ */
+ protected void setLeftDirty(boolean dirty) {
+ if (fLeftDirty != dirty) {
+ fLeftDirty= dirty;
+ fireDirtyState(dirty);
+// if (fActions != null) {
+// Action saveAction= (Action) fActions.get("Save");
+// if (saveAction != null)
+// saveAction.setEnabled(dirty);
+// }
+ }
+ }
+
+ /**
+ * Sets the dirty state of the right side of this viewer.
+ * If the new value differs from the old
+ * all registered listener are notified with
+ * a <code>PropertyChangeEvent</code> with the
+ * property name <code>CompareEditorInput.DIRTY_STATE</code>.
+ *
+ * @param dirty the state of the right side dirty flag
+ */
+ protected void setRightDirty(boolean dirty) {
+ if (fRightDirty != dirty) {
+ fRightDirty= dirty;
+ fireDirtyState(dirty);
+// if (fActions != null) {
+// Action saveAction= (Action) fActions.get("Save");
+// if (saveAction != null)
+// saveAction.setEnabled(dirty);
+// }
+ }
+ }
+
+ /**
+ * Save modified content back to input elements via the content provider.
+ */
+ /* package */ void saveContent(Object oldInput) {
+
+ // write back modified contents
+ IMergeViewerContentProvider content= (IMergeViewerContentProvider) getContentProvider();
+
+ if (fCompareConfiguration.isLeftEditable() && fLeftDirty) {
+
+ byte[] bytes= getContents(true);
+ content.saveLeftContent(oldInput, bytes);
+ setLeftDirty(false);
+ }
+
+ if (fCompareConfiguration.isRightEditable() && fRightDirty) {
+
+ byte[] bytes= getContents(false);
+ content.saveRightContent(oldInput, bytes);
+ setRightDirty(false);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java
new file mode 100644
index 000000000..6da51771b
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * Defines a subrange in a document.
+ * <p>
+ * It is used by text viewers that can work on a subrange of a document. For example,
+ * a text viewer for Java compilation units might use this to restrict the view
+ * to a single method.
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see TextMergeViewer
+ * @see org.eclipse.compare.structuremergeviewer.DocumentRangeNode
+ */
+public interface IDocumentRange {
+
+ /**
+ * Returns the underlying document.
+ *
+ * @return the underlying document
+ */
+ IDocument getDocument();
+
+ /**
+ * Returns a position that specifies a subrange in the underlying document,
+ * or <code>null</code> if this document range spans the whole underlying document.
+ *
+ * @return a position that specifies a subrange in the underlying document, or <code>null</code>
+ */
+ Position getRange();
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java
new file mode 100644
index 000000000..5448ebf9f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.jface.viewers.IContentProvider;
+
+
+/**
+ * A content provider that mediates between a <code>ContentMergeViewer</code>'s model
+ * and the viewer itself.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see ContentMergeViewer
+ */
+public interface IMergeViewerContentProvider extends IContentProvider {
+
+ //---- ancestor side
+
+ /**
+ * Returns the label for the ancestor side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the label for the ancestor side of a <code>ContentMergeViewer</code>
+ */
+ String getAncestorLabel(Object input);
+
+ /**
+ * Returns an optional image for the ancestor side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the image for the ancestor side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Image getAncestorImage(Object input);
+
+ /**
+ * Returns the contents for the ancestor side of a <code>ContentMergeViewer</code>.
+ * The interpretation of the returned object depends on the concrete <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the content for the ancestor side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Object getAncestorContent(Object input);
+
+ /**
+ * Returns whether the ancestor side of the given input element should be shown.
+ *
+ * @return <code>true</code> if the ancestor side of the given input element should be shown
+ */
+ boolean showAncestor(Object input);
+
+ //---- left side
+
+ /**
+ * Returns the label for the left side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the label for the left side of a <code>ContentMergeViewer</code>
+ */
+ String getLeftLabel(Object input);
+
+ /**
+ * Returns an optional image for the left side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the image for the left side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Image getLeftImage(Object input);
+
+ /**
+ * Returns the contents for the left side of a <code>ContentMergeViewer</code>.
+ * The interpretation of the returned object depends on the concrete <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the content for the left side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Object getLeftContent(Object input);
+
+ /**
+ * Returns whether the left side is editable.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return <code>true</code> if the left side of a <code>ContentMergeViewer</code> is editable
+ */
+ boolean isLeftEditable(Object input);
+
+ /**
+ * Saves new contents for the left side of the <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @param bytes the new contents to save for the left side
+ */
+ void saveLeftContent(Object input, byte[] bytes);
+
+ //---- right side
+
+ /**
+ * Returns the label for the right side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the label for the right side of a <code>ContentMergeViewer</code>
+ */
+ String getRightLabel(Object input);
+
+ /**
+ * Returns an optional image for the right side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the image for the right side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Image getRightImage(Object input);
+
+ /**
+ * Returns the contents for the right side of a <code>ContentMergeViewer</code>.
+ * The interpretation of the returned object depends on the concrete <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the content for the right side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Object getRightContent(Object input);
+
+ /**
+ * Returns whether the right side is editable.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return <code>true</code> if the right side of a <code>ContentMergeViewer</code> is editable
+ */
+ boolean isRightEditable(Object input);
+
+ /**
+ * Saves new contents for the right side of the <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @param bytes the new contents to save for the right side
+ */
+ void saveRightContent(Object input, byte[] bytes);
+}
+
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java
new file mode 100644
index 000000000..9fc23cb0d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+
+
+/**
+ * For performing a so-called "token compare" on a line of text.
+ * This interface extends the <code>IRangeComparator</code> interface
+ * so that it can be used by the <code>TextMergeViewer</code>.
+ * <p>
+ * <code>TextMergeViewer</code> activates the token compare when navigating into
+ * a range of differing lines. At first the lines are selected as a block.
+ * When navigating into this block the token compare shows for every line
+ * the differing token by selecting them.
+ * <p>
+ * <code>TextMergeViewer</code>'s default token comparator works on characters separated
+ * by whitespace. If a different strategy is needed (for example, to use Java tokens in
+ * a Java-aware merge viewer), clients may create their own token
+ * comparators by implementing this interface (and overriding the
+ * <code>TextMergeViewer.createTokenComparator</code> factory method).
+ * </p>
+ *
+ * @see TextMergeViewer
+ */
+public interface ITokenComparator extends IRangeComparator {
+
+ /**
+ * Returns the start character position of the token with the given index.
+ * If the index is out of range (but not negative) the character position
+ * behind the last character (the length of the input string) is returned.
+ *
+ * @param index index of the token for which to return the start position
+ * @return the start position of the token with the given index
+ * @throws java.lang.IndexOutOfBoundsException if index is negative
+ */
+ int getTokenStart(int index);
+
+ /**
+ * Returns the character length of the token with the given index.
+ * If the index is out of range (but not negative) the value 0 is returned.
+ *
+ * @param index index of the token for which to return the start position
+ * @return the character length of the token with the given index
+ * @throws java.lang.IndexOutOfBoundsException if index is negative
+ */
+ int getTokenLength(int index);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java
new file mode 100644
index 000000000..86111de15
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java
@@ -0,0 +1,2372 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ResourceBundle;
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.custom.*;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.ui.texteditor.IUpdate;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.internal.MergeSourceViewer;
+import org.eclipse.compare.internal.BufferedCanvas;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.internal.TokenComparator;
+import org.eclipse.compare.internal.ChangePropertyAction;
+import org.eclipse.compare.rangedifferencer.RangeDifference;
+import org.eclipse.compare.rangedifferencer.RangeDifferencer;
+import org.eclipse.compare.internal.DocLineComparator;
+
+
+/**
+ * A text merge viewer uses the <code>RangeDifferencer</code> to perform a
+ * textual, line-by-line comparison of two (or three) input documents.
+ * It is based on the <code>ContentMergeViewer</code> and uses <code>TextViewer</code>s
+ * to implement the ancestor, left, and right content areas.
+ * <p>
+ * In the three-way compare case ranges of differing lines are highlighted and framed
+ * with different colors to show whether the difference is an incoming, outgoing, or conflicting change.
+ * The <code>TextMergeViewer</code> supports the notion of a current "differing range"
+ * and provides toolbar buttons to navigate from one range to the next (or previous).
+ * <p>
+ * If there is a current "differing range" and the underlying document is editable
+ * the <code>TextMergeViewer</code> enables actions in context menu and toolbar to
+ * copy a range from one side to the other side, thereby performing a merge operation.
+ * <p>
+ * In addition to a line-by-line comparison the <code>TextMergeViewer</code>
+ * uses a token based compare on differing lines.
+ * The token compare is activated when navigating into
+ * a range of differing lines. At first the lines are selected as a block.
+ * When navigating into this block the token compare shows for every line
+ * the differing token by selecting them.
+ * <p>
+ * The <code>TextMergeViewer</code>'s default token compare works on characters separated
+ * by whitespace. If a different strategy is needed (for example, Java tokens in
+ * a Java-aware merge viewer), clients can create their own token
+ * comparators by implementing the <code>ITokenComparator</code> interface and overriding the
+ * <code>TextMergeViewer.createTokenComparator</code> factory method).
+ * <p>
+ * Access to the <code>TextMergeViewer</code>'s model is by means of an
+ * <code>IMergeViewerContentProvider</code>. Its <code>get<it>X</it></code>Content</code> methods must return
+ * either an <code>IDocument</code>, an <code>IDocumentRange</code>, or an <code>IStreamContentAccessor</code>.
+ * In the <code>IDocumentRange</code> case the <code>TextMergeViewer</code>
+ * works on a subrange of a document. In the <code>IStreamContentAccessor</code> case
+ * a document is created internally and initialized from the stream.
+ * <p>
+ * A <code>TextMergeViewer</code> can be used as is. However clients may subclass
+ * to customize the behavior. For example a <code>MergeTextViewer</code> for Java would override
+ * the <code>configureTextViewer</code> method to configure the <code>TextViewer</code> for Java source code,
+ * the <code>createTokenComparator</code> method to create a Java specific tokenizer.
+ *
+ * @see org.eclipse.compare.rangedifferencer.RangeDifferencer
+ * @see org.eclipse.jface.text.TextViewer
+ * @see ITokenComparator
+ * @see IDocumentRange
+ * @see org.eclipse.compare.IStreamContentAccessor
+ */
+public class TextMergeViewer extends ContentMergeViewer {
+
+ static class TextMergeAction extends Action implements IUpdate {
+
+ int fOperationCode;
+ ITextOperationTarget fTarget;
+
+ TextMergeAction(ResourceBundle bundle, String key, ITextOperationTarget target, int operationCode) {
+ Utilities.initAction(this, bundle, key);
+ fTarget= target;
+ fOperationCode= operationCode;
+ update();
+ }
+
+ public void run() {
+ if (fOperationCode != -1 && fTarget.canDoOperation(fOperationCode))
+ fTarget.doOperation(fOperationCode);
+ }
+
+ public boolean isEnabled() {
+ return fTarget.canDoOperation(fOperationCode);
+ }
+
+ public void update() {
+ setEnabled(isEnabled());
+ }
+ }
+
+ private static final String MY_UPDATER= "my_updater";
+
+ private static final String SYNC_SCROLLING= "SYNC_SCROLLING";
+
+ private static final String BUNDLE_NAME= "org.eclipse.compare.contentmergeviewer.TextMergeViewerResources";
+
+ // constants
+ /** Width of left and right vertical bar */
+ private static final int MARGIN_WIDTH= 10;
+ /** Width of center bar */
+ private static final int CENTER_WIDTH= 40;
+ /** */
+ private static final int LW= 1;
+ /** Provide more merge controls in Pane toolbar */
+ private static final boolean USE_MORE_CONTROLS= true;
+ /** Selects between smartTokenDiff and mergingTokenDiff */
+ private static final boolean USE_MERGING_TOKEN_DIFF= false;
+ /** if DEAD_STEP is true navigation with the next/previous buttons needs an extra step
+ when wrapping around the beginning or end */
+ private static final boolean DEAD_STEP= false;
+
+ private static final boolean IS_MOTIF= false;
+
+ // Colors to use
+ private static final RGB INCOMING= new RGB(100, 100, 200);
+ private static final RGB INCOMING_FILL= new RGB(230, 230, 240);
+ private static final RGB SELECTED_INCOMING= new RGB(0, 0, 255);
+ private static final RGB SELECTED_INCOMING_FILL= new RGB(255, 255, 255);
+
+ private static final RGB CONFLICT= new RGB(200, 100, 100);
+ private static final RGB CONFLICT_FILL= new RGB(240, 230, 230);
+ private static final RGB SELECTED_CONFLICT= new RGB(255, 0, 0);
+ private static final RGB SELECTED_CONFLICT_FILL= new RGB(255, 255, 255);
+
+ private static final RGB OUTGOING= new RGB(100, 100, 100);
+ private static final RGB OUTGOING_FILL= new RGB(230, 230, 230);
+ private static final RGB SELECTED_OUTGOING= new RGB(0, 0, 0);
+ private static final RGB SELECTED_OUTGOING_FILL= new RGB(255, 255, 255);
+
+ private IDocumentListener fDocumentListener;
+
+ /** All diffs for calculating scrolling position (includes line ranges without changes) */
+ private ArrayList fAllDiffs;
+ /** Subset of above: just real differences. */
+ private ArrayList fChangeDiffs;
+ /** The current diff */
+ private Diff fCurrentDiff;
+
+ private MergeSourceViewer fAncestor;
+ private MergeSourceViewer fLeft;
+ private MergeSourceViewer fRight;
+
+ private int fLeftLineCount;
+ private int fRightLineCount;
+
+ private boolean fLeftContentsChanged;
+ private boolean fRightContentsChanged;
+
+ private boolean fInScrolling;
+
+ private int fPts[]= new int[8]; // scratch area for polygon drawing
+
+ private ActionContributionItem fNextItem; // goto next difference
+ private ActionContributionItem fPreviousItem; // goto previous difference
+ private ActionContributionItem fCopyDiffLeftToRightItem;
+ private ActionContributionItem fCopyDiffRightToLeftItem;
+
+ private boolean fSynchronizedScrolling= true;
+
+ private MergeSourceViewer fFocusPart;
+
+ private boolean fSubDoc= true;
+ private IPositionUpdater fPositionUpdater;
+
+
+ // SWT widgets
+ private BufferedCanvas fAncestorCanvas;
+ private BufferedCanvas fLeftCanvas;
+ private BufferedCanvas fRightCanvas;
+ private ScrollBar fVScrollBar;
+
+ // SWT resources to be disposed
+ private Map fColors;
+
+
+ /**
+ * A Diff represents synchronized character ranges in two or three Documents.
+ * The MergeTextViewer uses Diffs to find differences in line and token ranges.
+ */
+ /* package */ class Diff {
+ /** character range in ancestor document */
+ Position fAncestorPos;
+ /** character range in left document */
+ Position fLeftPos;
+ /** character range in right document */
+ Position fRightPos;
+ /** if this is a TokenDiff fParent points to the enclosing LineDiff */
+ Diff fParent;
+ /** if Diff has been resolved */
+ boolean fResolved;
+ int fDirection;
+ boolean fIsToken= false;
+ ArrayList fDiffs;
+
+ /**
+ * Create Diff from two ranges and an optional parent diff.
+ */
+ Diff(Diff parent, int dir, IDocument ancestorDoc, int ancestorStart, int ancestorEnd,
+ IDocument leftDoc, int leftStart, int leftEnd,
+ IDocument rightDoc, int rightStart, int rightEnd) {
+ fParent= parent != null ? parent : this;
+ fDirection= dir;
+
+ fLeftPos= createPosition(leftDoc, leftStart, leftEnd);
+ fRightPos= createPosition(rightDoc, rightStart, rightEnd);
+ if (ancestorDoc != null)
+ fAncestorPos= createPosition(ancestorDoc, ancestorStart, ancestorEnd);
+ }
+
+ Position createPosition(IDocument doc, int start, int end) {
+ try {
+ int dl= doc.getLength();
+ int l= end-start;
+ if (start+l > dl)
+ l= dl-start;
+
+ Position p= null;
+ try {
+ p= new Position(start, l);
+ } catch (RuntimeException ex) {
+ System.out.println("Diff.createPosition: " + start + " " + l);
+ }
+
+ try {
+ doc.addPosition(MY_UPDATER, p);
+ } catch (BadPositionCategoryException ex) {
+ }
+ return p;
+ } catch (BadLocationException ee) {
+ //System.out.println("Diff.createPosition: " + start + " " + end);
+ }
+ return null;
+ }
+
+ void add(Diff d) {
+ if (fDiffs == null)
+ fDiffs= new ArrayList();
+ fDiffs.add(d);
+ }
+
+ boolean isDeleted() {
+ if (fAncestorPos != null && fAncestorPos.isDeleted())
+ return true;
+ return fLeftPos.isDeleted() || fRightPos.isDeleted();
+ }
+
+ void setResolved(boolean r) {
+ fResolved= r;
+ if (r)
+ fDiffs= null;
+ }
+
+ boolean isResolved() {
+ if (!fResolved && fDiffs != null) {
+ Iterator e= fDiffs.iterator();
+ while (e.hasNext()) {
+ Diff d= (Diff) e.next();
+ if (!d.isResolved())
+ return false;
+ }
+ return true;
+ }
+ return fResolved;
+ }
+
+ Position getPosition(MergeSourceViewer w) {
+ if (w == fLeft)
+ return fLeftPos;
+ if (w == fRight)
+ return fRightPos;
+ if (w == fAncestor)
+ return fAncestorPos;
+ return null;
+ }
+
+ /**
+ * Returns true if given character range overlaps with this Diff.
+ */
+ boolean contains(MergeSourceViewer w, int start, int end) {
+ Position h= getPosition(w);
+ if (h != null) {
+ int offset= h.getOffset();
+ if (start >= offset) {
+ int endPos= offset+h.getLength();
+ if (end < endPos)
+ return true;
+ if (endPos == w.getDocument().getLength())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int getMaxDiffHeight(boolean withAncestor) {
+ Point region= new Point(0, 0);
+ int h= fLeft.getLineRange(fLeftPos, region).y;
+ if (withAncestor)
+ h= Math.max(h, fAncestor.getLineRange(fAncestorPos, region).y);
+ return Math.max(h, fRight.getLineRange(fRightPos, region).y);
+ }
+ }
+
+ //---- MergeTextViewer
+
+ /**
+ * Creates a text merge viewer under the given parent control.
+ *
+ * @param parent the parent control
+ * @param configuration the configuration object
+ */
+ public TextMergeViewer(Composite parent, CompareConfiguration configuration) {
+ this(parent, SWT.NULL, configuration);
+ }
+
+ /**
+ * Creates a text merge viewer under the given parent control.
+ *
+ * @param parent the parent control
+ * @param style SWT style bits for top level composite of this viewer
+ * @param configuration the configuration object
+ */
+ public TextMergeViewer(Composite parent, int style, CompareConfiguration configuration) {
+ super(style, ResourceBundle.getBundle(BUNDLE_NAME), configuration);
+
+ buildControl(parent);
+
+ fSynchronizedScrolling= Utilities.getBoolean(configuration, SYNC_SCROLLING, fSynchronizedScrolling);
+
+ fDocumentListener= new IDocumentListener() {
+
+ public void documentAboutToBeChanged(DocumentEvent e) {
+ }
+
+ public void documentChanged(DocumentEvent e) {
+ TextMergeViewer.this.documentChanged(e);
+ }
+ };
+ }
+
+ public String getTitle() {
+ return "Text Compare";
+ }
+
+ /**
+ * Configures the passed text viewer.
+ * This method is called after the three text viewers have been created for the
+ * content areas.
+ * The <code>TextMergeViewer</code> implementation of this method does nothing.
+ * Subclasses may reimplement to provide a specific configuration for the text viewer.
+ *
+ * @param textViewer the text viewer to configure
+ */
+ protected void configureTextViewer(TextViewer textViewer) {
+ }
+
+ /**
+ * Creates an <code>ITokenComparator</code> which is used to show the
+ * intra line differences.
+ * The <code>TextMergeViewer</code> implementation of this method returns a
+ * tokenizer that breaks a line into words separated by whitespace.
+ * Subclasses may reimplement to provide a specific tokenizer.
+ *
+ * @return a ITokenComparator which is used for a second level token compare.
+ */
+ protected ITokenComparator createTokenComparator(String s) {
+ return new TokenComparator(s);
+ }
+
+ /**
+ * Returns a document partitioner which is suitable for the underlying content type.
+ * This method is only called if the input provided by the content provider is a
+ * <code>IStreamContentAccessor</code> and an internal document must be created. This
+ * document is initialized with the partitioner returned from this method.
+ * <p>
+ * The <code>TextMergeViewer</code> implementation of this method returns
+ * <code>null</code>. Subclasses may reimplement to create a partitioner for a
+ * specific content type.
+ *
+ * @return a document partitioner, or <code>null</code>
+ */
+ protected IDocumentPartitioner getDocumentPartitioner() {
+ return null;
+ }
+
+ /**
+ * 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) {
+
+ fLeftCanvas= null;
+ fRightCanvas= null;
+ fVScrollBar= null;
+
+ unsetDocument(fAncestor);
+ unsetDocument(fLeft);
+ unsetDocument(fRight);
+
+ if (fColors != null) {
+ Iterator i= fColors.values().iterator();
+ while (i.hasNext()) {
+ Color color= (Color) i.next();
+ if (!color.isDisposed())
+ color.dispose();
+ }
+ }
+
+ super.handleDispose(event);
+ }
+
+ //-------------------------------------------------------------------------------------------------------------
+ //--- internal ------------------------------------------------------------------------------------------------
+ //-------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Creates the specific SWT controls for the content areas.
+ * Clients must not call or override this method.
+ */
+ protected void createControls(Composite composite) {
+
+ // 1st row
+ fAncestorCanvas= new BufferedCanvas(composite, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fAncestor, fAncestorCanvas, false);
+ }
+ };
+
+ fAncestor= createPart(composite);
+ fAncestor.setEditable(false);
+
+ // 2nd row
+ fLeftCanvas= new BufferedCanvas(composite, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fLeft, fLeftCanvas, false);
+ }
+ };
+
+ fLeft= createPart(composite);
+ fLeft.getTextWidget().getVerticalBar().setVisible(false);
+
+ fRight= createPart(composite);
+ fRight.getTextWidget().getVerticalBar().setVisible(false);
+
+ fRightCanvas= new BufferedCanvas(composite, SWT.V_SCROLL) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fRight, fRightCanvas, fSynchronizedScrolling);
+ }
+ };
+
+ fVScrollBar= fRightCanvas.getVerticalBar();
+ fVScrollBar.setIncrement(1);
+ fVScrollBar.setVisible(true);
+ fVScrollBar.addListener(SWT.Selection,
+ new Listener() {
+ public void handleEvent(Event e) {
+ scrollVertical(((ScrollBar)e.widget).getSelection(), null);
+ }
+ }
+ );
+ }
+
+ /* package */ boolean internalSetFocus() {
+ if (fFocusPart != null) {
+ StyledText st= fFocusPart.getTextWidget();
+ if (st != null)
+ return st.setFocus();
+ }
+ return false; // could not set focus
+ }
+
+ /**
+ * Creates the central Canvas.
+ * Called from ContentMergeViewer.
+ */
+ /* package */ Control createCenter(Composite parent) {
+ if (fSynchronizedScrolling) {
+ final Canvas canvas= new BufferedCanvas(parent, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintCenter(this, gc);
+ }
+ };
+ new Resizer(canvas, HORIZONTAL);
+ return canvas;
+ }
+ return super.createCenter(parent);
+ }
+
+ /**
+ * Returns width of central canvas.
+ * Overridden from ContentMergeViewer.
+ */
+ /* package */ int getCenterWidth() {
+ if (fSynchronizedScrolling)
+ return CENTER_WIDTH;
+ return super.getCenterWidth();
+ }
+
+ /**
+ * Creates and initializes a text part.
+ */
+ private MergeSourceViewer createPart(Composite parent) {
+
+ final MergeSourceViewer part= new MergeSourceViewer(parent);
+ final StyledText te= part.getTextWidget();
+
+ te.addPaintListener(
+ new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ paint(e, part);
+ }
+ }
+ );
+ te.addKeyListener(
+ new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ handleSelectionChanged(part);
+ }
+ }
+ );
+ te.addMouseListener(
+ new MouseAdapter() {
+ public void mouseDown(MouseEvent e) {
+ //syncViewport(part);
+ handleSelectionChanged(part);
+ }
+ }
+ );
+
+ te.addFocusListener(
+ new FocusAdapter() {
+ public void focusGained(FocusEvent fe) {
+ fFocusPart= part;
+ }
+ }
+ );
+
+ MenuManager mm= new MenuManager();
+ mm.setRemoveAllWhenShown(true);
+ mm.addMenuListener(
+ new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mm) {
+ fillContextMenu(mm, part);
+ }
+ }
+ );
+ te.setMenu(mm.createContextMenu(te));
+
+ part.addViewportListener(
+ new IViewportListener() {
+ public void viewportChanged(int verticalPosition) {
+ syncViewport(part);
+ }
+ }
+ );
+
+ configureTextViewer(part);
+
+ return part;
+ }
+
+ /**
+ * Allows the viewer to add menus and/or tools to the context menu.
+ */
+ private void fillContextMenu(IMenuManager menu, MergeSourceViewer part) {
+
+ ResourceBundle rb= getResourceBundle();
+ boolean mutable= part.isEditable();
+
+ menu.add(new Separator("undo"));
+ if (mutable) {
+ menu.add(new TextMergeAction(rb, "action.UndoText.", part, MergeSourceViewer.UNDO));
+ menu.add(new TextMergeAction(rb, "action.RedoText.", part, MergeSourceViewer.REDO));
+ }
+
+ menu.add(new Separator("ccp"));
+ if (mutable)
+ menu.add(new TextMergeAction(rb, "action.CutText.", part, MergeSourceViewer.CUT));
+ menu.add(new TextMergeAction(rb, "action.CopyText.", part, MergeSourceViewer.COPY));
+ if (mutable) {
+ menu.add(new TextMergeAction(rb, "action.PasteText.", part, MergeSourceViewer.PASTE));
+ menu.add(new TextMergeAction(rb, "action.DeleteText.", part, MergeSourceViewer.DELETE));
+ }
+ menu.add(new TextMergeAction(rb, "action.SelectAllText.", part, MergeSourceViewer.SELECT_ALL));
+
+ menu.add(new Separator("edit"));
+ menu.add(new Separator("find"));
+ //contributeAction(menu, part, "find", "Find");
+ //menu.add(new TextMergeAction(rb, "action.ContentAssistProposal.", part, MergeSourceViewer.CONTENTASSIST_PROPOSAL));
+ //menu.add(new TextMergeAction(rb, "action.ContentAssistTip.", part, MergeSourceViewer.CONTENTASSIST_TIP));
+
+ menu.add(new Separator("save"));
+ //if (mutable)
+ // contributeAction(menu, part, "save", "Save");
+
+ menu.add(new Separator("rest"));
+ }
+
+ /**
+ * Initializes the text viewers of the three content areas with the given input objects.
+ * Subclasses may extend.
+ */
+ protected void updateContent(Object ancestor, Object left, Object right) {
+
+ // clear stuff
+ fCurrentDiff= null;
+ fChangeDiffs= null;
+ fAllDiffs= null;
+
+ fLeftContentsChanged= false;
+ fRightContentsChanged= false;
+
+ CompareConfiguration cc= getCompareConfiguration();
+ IMergeViewerContentProvider cp= getMergeContentProvider();
+
+ boolean rightEditable= cc.isRightEditable() && cp.isRightEditable(getInput());
+ boolean leftEditable= cc.isLeftEditable() && cp.isLeftEditable(getInput());
+
+ fRight.setEditable(rightEditable);
+ fLeft.setEditable(leftEditable);
+
+ // set new documents
+ setDocument(fAncestor, ancestor);
+
+ setDocument(fLeft, left);
+ fLeftLineCount= fLeft.getLineCount();
+
+ setDocument(fRight, right);
+ fRightLineCount= fRight.getLineCount();
+
+ doDiff();
+
+ invalidateLines();
+ updateVScrollBar();
+ selectFirstDiff();
+ }
+
+ private void updateDiffBackground(Diff diff) {
+
+ if (diff == null || diff.fIsToken)
+ return;
+ Point region= new Point(0, 0);
+
+ Color c= getColor(getFillColor(diff));
+ if (c == null)
+ return;
+
+ if (isThreeWay())
+ fAncestor.setLineBackground(diff.fAncestorPos, c);
+ fLeft.setLineBackground(diff.fLeftPos, c);
+ fRight.setLineBackground(diff.fRightPos, c);
+ }
+
+ private void unsetDocument(MergeSourceViewer tp) {
+ IDocument oldDoc= tp.getDocument();
+ if (oldDoc != null) { // deinstall old positions
+ if (fPositionUpdater != null)
+ oldDoc.removePositionUpdater(fPositionUpdater);
+ try {
+ oldDoc.removePositionCategory(MY_UPDATER);
+ } catch (BadPositionCategoryException ex) {
+ }
+ }
+ }
+
+ /**
+ * Called whenver one of the documents changes.
+ * Sets the dirty state of this viewer and updates the lines.
+ * Implements IDocumentListener.
+ */
+ private void documentChanged(DocumentEvent e) {
+
+ IDocument doc= e.getDocument();
+
+ if (doc == fLeft.getDocument()) {
+ fLeftContentsChanged= true;
+ setLeftDirty(true);
+ } else if (doc == fRight.getDocument()) {
+ setRightDirty(true);
+ fRightContentsChanged= true;
+ }
+
+ updateLines(doc);
+ }
+
+ /**
+ * Returns true if a new Document could be installed.
+ */
+ private boolean setDocument(MergeSourceViewer tp, Object o) {
+
+ if (tp == null)
+ return false;
+
+ IDocument newDoc= null;
+
+ if (o instanceof IDocumentRange) {
+ newDoc= ((IDocumentRange)o).getDocument();
+
+ } else if (o instanceof Document) {
+ newDoc= (Document) o;
+
+ } else if (o instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) o;
+ if (sca != null) {
+ String s= null;
+
+ try {
+ s= Utilities.readString(sca.getContents());
+ } catch (CoreException ex) {
+ }
+
+ newDoc= new Document(s != null ? s : "");
+ IDocumentPartitioner partitioner= getDocumentPartitioner();
+ if (partitioner != null) {
+ newDoc.setDocumentPartitioner(partitioner);
+ partitioner.connect(newDoc);
+ }
+ }
+ }
+
+ boolean enabled= true;
+ if (newDoc == null) {
+ newDoc= new Document("");
+ enabled= false;
+ }
+
+ IDocument oldDoc= tp.getDocument();
+
+ unsetDocument(tp);
+
+ if (newDoc != null) {
+ newDoc.addPositionCategory(MY_UPDATER);
+ if (fPositionUpdater == null)
+ fPositionUpdater= new DefaultPositionUpdater(MY_UPDATER);
+ newDoc.addPositionUpdater(fPositionUpdater);
+ }
+
+ if (newDoc != oldDoc) { // new document
+
+ // deinstall old document
+ if (oldDoc != null)
+ oldDoc.removeDocumentListener(fDocumentListener);
+
+ // install new document
+ if (newDoc != null) {
+
+ IRegion region= null;
+ if (o instanceof IDocumentRange) {
+ Position range= ((IDocumentRange) o).getRange();
+ if (range != null)
+ region= new Region(range.getOffset(), range.getLength());
+ }
+
+ tp.setRegion(region);
+ if (fSubDoc) {
+ if (region != null) {
+ IRegion r= normalizeDocumentRegion(newDoc, region);
+ tp.setDocument(newDoc, r.getOffset(), r.getLength());
+ } else
+ tp.setDocument(newDoc);
+ } else
+ tp.setDocument(newDoc);
+
+ newDoc.addDocumentListener(fDocumentListener);
+ }
+
+ } else { // just different range
+
+ IRegion region= null;
+ if (o instanceof IDocumentRange) {
+ Position range= ((IDocumentRange) o).getRange();
+ if (range != null)
+ region= new Region(range.getOffset(), range.getLength());
+ }
+
+ tp.setRegion(region);
+ if (fSubDoc) {
+ if (region != null) {
+ IRegion r= normalizeDocumentRegion(tp.getDocument(), region);
+ tp.setVisibleRegion(r.getOffset(), r.getLength());
+ }
+ }
+ }
+
+ tp.setEnabled(enabled);
+
+ return enabled;
+ }
+
+ /**
+ * Returns the contents of the underlying document as an array of bytes.
+ *
+ * @param left if <code>true</code> the contents of the left side is returned; otherwise the right side
+ * @return the contents of the left or right document
+ */
+ protected byte[] getContents(boolean left) {
+
+ if (left) {
+ if (fLeftContentsChanged)
+ return fLeft.getDocument().get().getBytes();
+ } else {
+ if (fRightContentsChanged)
+ return fRight.getDocument().get().getBytes();
+ }
+ return null;
+ }
+
+ private IRegion normalizeDocumentRegion(IDocument doc, IRegion region) {
+
+ if (region == null || doc == null)
+ return region;
+
+ int maxLength= doc.getLength();
+
+ int start= region.getOffset();
+ if (start < 0)
+ start= 0;
+ else if (start > maxLength)
+ start= maxLength;
+
+ int length= region.getLength();
+ if (length < 0)
+ length= 0;
+ else if (start + length > maxLength)
+ length= maxLength - start;
+
+ return new Region(start, length);
+ }
+
+ protected final void handleResizeAncestor(int x, int y, int width, int height) {
+ if (width > 0) {
+ Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.setVisible(true);
+ if (fAncestor.isControlOkToUse())
+ fAncestor.getTextWidget().setVisible(true);
+ fAncestorCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ fAncestor.getTextWidget().setBounds(x+MARGIN_WIDTH, y, width-MARGIN_WIDTH, height);
+ } else {
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.setVisible(false);
+ if (fAncestor.isControlOkToUse()) {
+ StyledText t= fAncestor.getTextWidget();
+ t.setVisible(false);
+ t.setBounds(0, 0, 0, 0);
+ }
+ }
+ }
+
+ /**
+ * Lays out everything.
+ */
+ protected final void handleResizeLeftRight(int x, int y, int width1, int centerWidth, int width2, int height) {
+
+ // determine some minimal sizes
+ int scrollbarWidth= 0;
+ if (fSynchronizedScrolling && fRightCanvas != null)
+ scrollbarWidth= fRightCanvas.computeTrim(0, 0, 0, 0).width;
+
+ Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+
+ // determine some derived sizes
+ Composite composite= (Composite) getControl();
+
+ int leftTextWidth= width1-MARGIN_WIDTH;
+ int rightTextWidth= width2-MARGIN_WIDTH-scrollbarWidth;
+
+ fLeftCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ x+= MARGIN_WIDTH;
+
+ fLeft.getTextWidget().setBounds(x, y, leftTextWidth, height);
+ x+= leftTextWidth;
+
+ if (fCenter == null || fCenter.isDisposed())
+ fCenter= createCenter(composite);
+ fCenter.setBounds(x, y, centerWidth, height-scrollbarHeight);
+ x+= centerWidth;
+
+ if (!fSynchronizedScrolling) {
+ if (fRightCanvas != null) {
+ fRightCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ fRightCanvas.redraw();
+ }
+ // we draw the canvas to the left of the text widget
+ x+= MARGIN_WIDTH;
+ }
+
+ fRight.getTextWidget().setBounds(x, y, rightTextWidth, height);
+ x+= rightTextWidth;
+
+ if (fSynchronizedScrolling && fRightCanvas != null)
+ fRightCanvas.setBounds(x, y, scrollbarWidth+MARGIN_WIDTH, height-scrollbarHeight);
+
+ // doesn't work since TextEditors don't have their correct size yet.
+ updateVScrollBar();
+ }
+
+ /**
+ * Track selection changes to update the current Diff.
+ */
+ private void handleSelectionChanged(MergeSourceViewer tw) {
+ Point p= tw.getSelectedRange();
+ Diff d= findDiff(tw, p.x, p.x+p.y);
+ setCurrentDiff(d, false); // don't select or reveal
+ }
+
+ //---- the differencing
+
+ /**
+ * Perform a two level 2- or 3-way diff.
+ * The first level is based on line comparison, the second level on token comparison.
+ */
+ private void doDiff() {
+
+ fAllDiffs= new ArrayList();
+ fChangeDiffs= new ArrayList();
+
+ IDocument aDoc= null;
+ IDocument iDoc= fLeft.getDocument();
+ IDocument oDoc= fRight.getDocument();
+
+ if (iDoc == null || oDoc == null)
+ return;
+
+ IRegion aRegion= null;
+ IRegion iRegion= fLeft.getRegion();
+ IRegion oRegion= fRight.getRegion();
+
+ boolean threeWay= isThreeWay();
+
+ if (threeWay) {
+ aDoc= fAncestor.getDocument();
+ aRegion= fAncestor.getRegion();
+ }
+
+ fAncestor.resetLineBackground();
+ fLeft.resetLineBackground();
+ fRight.resetLineBackground();
+
+ boolean ignoreWhiteSpace= Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false);
+
+ DocLineComparator sright= new DocLineComparator(oDoc, oRegion, ignoreWhiteSpace);
+ DocLineComparator sleft= new DocLineComparator(iDoc, iRegion, ignoreWhiteSpace);
+ DocLineComparator sancestor= null;
+ if (aDoc != null)
+ sancestor= new DocLineComparator(aDoc, aRegion, ignoreWhiteSpace);
+
+ if (!fSubDoc && oRegion != null && iRegion != null) {
+ // we have to add a diff for the ignored lines
+
+ int astart= 0;
+ int as= 0;
+ if (aRegion != null) {
+ astart= aRegion.getOffset();
+ as= Math.max(0, astart-1);
+ }
+ int ys= Math.max(0, iRegion.getOffset()-1);
+ int ms= Math.max(0, oRegion.getOffset()-1);
+
+ if (as > 0 || ys > 0 || ms > 0) {
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, 0, astart,
+ iDoc, 0, iRegion.getOffset(),
+ oDoc, 0, oRegion.getOffset());
+ fAllDiffs.add(diff);
+ }
+ }
+
+ RangeDifference[] e= RangeDifferencer.findRanges(sancestor, sleft, sright);
+
+ for (int i= 0; i < e.length; i++) {
+ String a= null, s= null, d= null;
+ RangeDifference es= e[i];
+
+ int kind= es.kind();
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (sancestor != null) {
+ ancestorStart= sancestor.getTokenStart(es.ancestorStart());
+ ancestorEnd= sancestor.getTokenEnd(es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart= sleft.getTokenStart(es.leftStart());
+ int leftEnd= sleft.getTokenEnd(es.leftStart(), es.leftLength());
+
+ int rightStart= sright.getTokenStart(es.rightStart());
+ int rightEnd= sright.getTokenEnd(es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(null, kind,
+ aDoc, ancestorStart, ancestorEnd,
+ iDoc, leftStart, leftEnd,
+ oDoc, rightStart, rightEnd);
+
+ fAllDiffs.add(diff); // remember all range diffs for scrolling
+
+ if (ignoreWhiteSpace) {
+ if (sancestor != null)
+ a= sancestor.extract(es.ancestorStart(), es.ancestorLength());
+ s= sleft.extract(es.leftStart(), es.leftLength());
+ d= sright.extract(es.rightStart(), es.rightLength());
+
+ if ((a == null || a.trim().length() == 0) && s.trim().length() == 0 && d.trim().length() == 0)
+ continue;
+ }
+
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ fChangeDiffs.add(diff); // here we remember only the real diffs
+ updateDiffBackground(diff);
+
+ if (s == null)
+ s= sleft.extract(es.leftStart(), es.leftLength());
+ if (d == null)
+ d= sright.extract(es.rightStart(), es.rightLength());
+
+ if (s.length() > 0 && d.length() > 0) {
+ if (a == null && sancestor != null)
+ a= sancestor.extract(es.ancestorStart(), es.ancestorLength());
+ if (USE_MERGING_TOKEN_DIFF)
+ mergingTokenDiff(diff, aDoc, a, oDoc, d, iDoc, s);
+ else
+ simpleTokenDiff(diff, aDoc, a, oDoc, d, iDoc, s);
+ }
+ }
+ }
+
+ if (!fSubDoc && oRegion != null && iRegion != null) {
+ // we have to add a diff for the ignored lines
+
+ int aEnd= 0;
+ int aLen= 0;
+ if (aRegion != null && aDoc != null) {
+ aEnd= aRegion.getOffset()+aRegion.getLength();
+ aLen= aDoc.getLength();
+ }
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, aEnd, aLen,
+ iDoc, iRegion.getOffset()+iRegion.getLength(), iDoc.getLength(),
+ oDoc, oRegion.getOffset()+oRegion.getLength(), oDoc.getLength());
+ fAllDiffs.add(diff);
+ }
+ }
+
+ private int getTokenEnd(ITokenComparator tc, int start, int count) {
+ if (count <= 0)
+ return tc.getTokenStart(start);
+ int index= start + count - 1;
+ int l= tc.getTokenLength(index);
+ if (l < 0)
+ System.out.println("getTokenEnd: l < 0");
+ return tc.getTokenStart(index) + l;
+ }
+
+ /**
+ * Performs a token based 3-way diff on the character range specified by the given baseDiff.
+ */
+ private void simpleTokenDiff(final Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ ITokenComparator sa= null;
+ if (ancestorDoc != null) {
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ ancestorEnd= ancestorStart + baseDiff.fAncestorPos.getLength();
+ sa= createTokenComparator(a);
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ int rightEnd= rightStart + baseDiff.fRightPos.getLength();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ int leftEnd= leftStart + baseDiff.fLeftPos.getLength();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] e= RangeDifferencer.findRanges(sa, sy, sm);
+ for (int i= 0; i < e.length; i++) {
+ RangeDifference es= e[i];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+
+ int ancestorStart2= ancestorStart;
+ int ancestorEnd2= ancestorStart;
+ if (ancestorDoc != null) {
+ ancestorStart2 += sa.getTokenStart(es.ancestorStart());
+ ancestorEnd2 += getTokenEnd(sa, es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart2= leftStart + sy.getTokenStart(es.leftStart());
+ int leftEnd2= leftStart + getTokenEnd(sy, es.leftStart(), es.leftLength());
+
+ int rightStart2= rightStart + sm.getTokenStart(es.rightStart());
+ int rightEnd2= rightStart + getTokenEnd(sm, es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(baseDiff, kind,
+ ancestorDoc, ancestorStart2, ancestorEnd2,
+ leftDoc, leftStart2, leftEnd2,
+ rightDoc, rightStart2, rightEnd2);
+ diff.fIsToken= true;
+ // add to base Diff
+ baseDiff.add(diff);
+ }
+ }
+ }
+
+ /**
+ * Performs a "smart" token based 3-way diff on the character range specified by the given baseDiff.
+ * It is smart because it tries to minimize the number of token diffs by merging them.
+ */
+ private void mergingTokenDiff(Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+
+ ITokenComparator sa= null;
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (ancestorDoc != null) {
+ sa= createTokenComparator(a);
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ ancestorEnd= ancestorStart + baseDiff.fAncestorPos.getLength();
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ int rightEnd= rightStart + baseDiff.fRightPos.getLength();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ int leftEnd= leftStart + baseDiff.fLeftPos.getLength();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] r= RangeDifferencer.findRanges(sa, sy, sm);
+
+ for (int i= 0; i < r.length; i++) {
+ RangeDifference es= r[i];
+
+ // determine range of diffs in one line
+ int start= i;
+ int leftLine= -1;
+ int rightLine= -1;
+ try {
+ leftLine= leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart()));
+ rightLine= rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart()));
+ } catch (BadLocationException e) {
+ }
+ i++;
+ for (; i < r.length; i++) {
+ es= r[i];
+ int ll, rl;
+ try {
+ if (leftLine != leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart())))
+ break;
+ if (rightLine != rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart())))
+ break;
+ } catch (BadLocationException e) {
+ }
+ }
+ int end= i;
+
+ // find first diff from left
+ RangeDifference first= null;
+ for (int ii= start; ii < end; ii++) {
+ es= r[ii];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ first= es;
+ break;
+ }
+ }
+
+ // find first diff from mine
+ RangeDifference last= null;
+ for (int ii= end-1; ii >= start; ii--) {
+ es= r[ii];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ last= es;
+ break;
+ }
+ }
+
+
+ if (first != null && last != null) {
+
+ int ancestorStart2= 0;
+ int ancestorEnd2= 0;
+ if (ancestorDoc != null) {
+ ancestorStart2= ancestorStart+sa.getTokenStart(first.ancestorStart());
+ ancestorEnd2= ancestorStart+getTokenEnd(sa, last.ancestorStart(), last.ancestorLength());
+ }
+
+ int leftStart2= leftStart+sy.getTokenStart(first.leftStart());
+ int leftEnd2= leftStart+getTokenEnd(sy, last.leftStart(), last.leftLength());
+
+ int rightStart2= rightStart+sm.getTokenStart(first.rightStart());
+ int rightEnd2= rightStart+getTokenEnd(sm, last.rightStart(), last.rightLength());
+
+ Diff diff= new Diff(baseDiff, first.kind(),
+ ancestorDoc, ancestorStart2, ancestorEnd2+1,
+ leftDoc, leftStart2, leftEnd2+1,
+ rightDoc, rightStart2, rightEnd2+1);
+ diff.fIsToken= true;
+ baseDiff.add(diff);
+ }
+ }
+ }
+
+ //---- update UI stuff
+
+ private void updateControls() {
+
+ boolean leftToRight= false;
+ boolean rightToLeft= false;
+
+ if (fCurrentDiff != null) {
+ IMergeViewerContentProvider cp= getMergeContentProvider();
+ if (cp != null) {
+ rightToLeft= cp.isLeftEditable(getInput());
+ leftToRight= cp.isRightEditable(getInput());
+ }
+ }
+
+ if (fCopyDiffLeftToRightItem != null)
+ ((Action)fCopyDiffLeftToRightItem.getAction()).setEnabled(leftToRight);
+ if (fCopyDiffRightToLeftItem != null)
+ ((Action)fCopyDiffRightToLeftItem.getAction()).setEnabled(rightToLeft);
+//
+// int fAutoResolve= 0;
+// int fUnresolvedDiffs= 0;
+// if (fChangeDiffs != null) {
+// fUnresolvedDiffs= fChangeDiffs.size();
+// if (fUnresolvedDiffs > 0) {
+// Iterator e= fChangeDiffs.iterator();
+// while (e.hasNext()) {
+// Diff diff= (Diff) e.next();
+// if (diff.isResolved()) {
+// fUnresolvedDiffs--;
+// } else {
+// if (diff.fDirection == RangeDifference.RIGHT || diff.fDirection == RangeDifference.LEFT) {
+// fAutoResolve++;
+// }
+// }
+// }
+// }
+// }
+//
+// boolean acceptReject= false;
+// boolean both= false;
+//
+// String s= "";
+//
+// if (fCurrentDiff != null) {
+// if (fCurrentDiff.isResolved()) {
+// s= "resolved";
+// } else {
+// s= "unresolved";
+//
+// IMergeViewerContentProvider twr= getContentProvider();
+// Object input= getInput();
+// boolean rightEditable= twr.isRightEditable(input);
+// boolean leftEditable= twr.isLeftEditable(input);
+//
+// switch (fCurrentDiff.fDirection) {
+// case RangeDifference.RIGHT: // outgoing
+// if (rightEditable)
+// acceptReject= true;
+// break;
+// case RangeDifference.LEFT: // incoming
+// if (leftEditable)
+// acceptReject= true;
+// break;
+// case RangeDifference.CONFLICT:
+// if (rightEditable) {
+// acceptReject= true;
+// both= true;
+// }
+// break;
+// }
+// }
+// } else {
+// if (fUnresolvedDiffs <= 0)
+// s= "allresolved";
+// else
+// s= "same";
+// }
+//
+// getAction(fTakeLeftActionItem).setEnabled(acceptReject);
+// getAction(fRejectItem).setEnabled(acceptReject);
+// if (fBothItem != null)
+// getAction(fBothItem).setEnabled(both);
+// if (fAutoItem != null)
+// getAction(fAutoItem).setEnabled(fAutoResolve > 0);
+//
+// if (s.length() > 0)
+// s= getBundle().getString("status." + s);
+//
+// ApplicationWindow b= getApplicationWindow();
+// if (b != null) {
+// String format= fBundle.getString(fUnresolvedDiffs > 0
+// ? "status.unresolvedformat"
+// : "status.resolvedformat");
+// b.setStatus(MessageFormat.format(format, new String[] { s, "" + fUnresolvedDiffs } ));
+// }
+ }
+
+
+ protected void updateHeader() {
+
+ super.updateHeader();
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+ boolean m= content.isRightEditable(input);
+ boolean y= content.isLeftEditable(input);
+
+ CompareConfiguration mp= getCompareConfiguration();
+ //fLeft.setEditable(y && mp.isLeftEditable());
+ //fRight.setEditable(m && mp.isRightEditable());
+
+ updateControls();
+ }
+
+ /**
+ * Creates the two items for copying a difference range from one side to the other
+ * and adds them to the given toolbar manager.
+ */
+ protected void createToolItems(ToolBarManager tbm) {
+
+// if (USE_MORE_CONTROLS) {
+// fBothItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptBoth.") {
+// public void actionPerformed(Window w) {
+// accept(fCurrentDiff, true, false);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fBothItem);
+//
+// fAutoItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptAll.") {
+// public void actionPerformed(Window w) {
+// autoResolve();
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fAutoItem);
+// }
+// fRejectItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptIgnoreNow.") {
+// public void actionPerformed(Window w) {
+// reject(fCurrentDiff, true);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fRejectItem);
+//
+ Action a= new ChangePropertyAction(getResourceBundle(), getCompareConfiguration(), "action.SynchMode.", SYNC_SCROLLING);
+ a.setChecked(fSynchronizedScrolling);
+ tbm.appendToGroup("modes", a);
+
+ tbm.add(new Separator());
+
+ a= new Action() {
+ public void run() {
+ navigate(true);
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.NextDiff.");
+ fNextItem= new ActionContributionItem(a);
+ tbm.appendToGroup("navigation", fNextItem);
+
+ a= new Action() {
+ public void run() {
+ navigate(false);
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.PrevDiff.");
+ fPreviousItem= new ActionContributionItem(a);
+ tbm.appendToGroup("navigation", fPreviousItem);
+
+
+ CompareConfiguration cc= getCompareConfiguration();
+
+ if (cc.isRightEditable()) {
+ a= new Action() {
+ public void run() {
+ copyDiffLeftToRight();
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.CopyDiffLeftToRight.");
+ fCopyDiffLeftToRightItem= new ActionContributionItem(a);
+ tbm.appendToGroup("merge", fCopyDiffLeftToRightItem);
+ }
+
+ if (cc.isLeftEditable()) {
+ a= new Action() {
+ public void run() {
+ copyDiffRightToLeft();
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.CopyDiffRightToLeft.");
+ fCopyDiffRightToLeftItem= new ActionContributionItem(a);
+ tbm.appendToGroup("merge", fCopyDiffRightToLeftItem);
+ }
+ }
+
+ /* package */ void propertyChange(PropertyChangeEvent event) {
+
+ String key= event.getProperty();
+
+ if (key.equals(CompareConfiguration.IGNORE_WHITESPACE)) {
+ // clear stuff
+ fCurrentDiff= null;
+ fChangeDiffs= null;
+ fAllDiffs= null;
+
+ doDiff();
+
+ invalidateLines();
+ updateVScrollBar();
+
+ selectFirstDiff();
+ } else if (key.equals(SYNC_SCROLLING)) {
+
+ boolean b= Utilities.getBoolean(getCompareConfiguration(), SYNC_SCROLLING, true);
+ if (b != fSynchronizedScrolling)
+ toggleSynchMode();
+
+ } else
+ super.propertyChange(event);
+ }
+
+ private void selectFirstDiff() {
+ Diff firstDiff= findNext(fRight, fChangeDiffs, -1, -1);
+ setCurrentDiff(firstDiff, true);
+ }
+
+ private void toggleSynchMode() {
+ fSynchronizedScrolling= ! fSynchronizedScrolling;
+
+ scrollVertical(0, null);
+
+ // throw away central control (Sash or Canvas)
+ Control center= getCenter();
+ if (center != null && !center.isDisposed())
+ center.dispose();
+
+ fLeft.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
+ fRight.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
+
+ // recreates central control (Sash or Canvas)
+ //handleResize();
+ fComposite.layout(true);
+ }
+
+ protected void updateToolItems() {
+
+ boolean visible= false;
+ Object input= getInput();
+ if (input != null) {
+ visible= true;
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ //boolean y= getMergePolicy().isLeftEditable();
+ //boolean m= getMergePolicy().isRightEditable();
+
+ //destinationEditable= content.isRightEditable(getInput());
+ //destinationEditable= content.isLeftEditable(getInput());
+ /*
+ if (USE_MORE_CONTROLS) {
+ fBothItem.setVisible(destinationEditable);
+ fAutoItem.setVisible(destinationEditable);
+ }
+ fRejectItem.setVisible(destinationEditable);
+ */
+ }
+
+ //fNextItem.setVisible(visible);
+ //fPreviousItem.setVisible(visible);
+
+ super.updateToolItems();
+ }
+
+ //---- painting lines
+
+ /**
+ *
+ */
+ private void updateLines(IDocument d) {
+
+ boolean left= false;
+ boolean right= false;
+
+ // FIXME: this optimization is incorrect because
+ // it doesn't take replace operations into account where
+ // the old and new line count does not differ
+ if (d == fLeft.getDocument()) {
+ int l= fLeft.getLineCount();
+ left= fLeftLineCount != l;
+ fLeftLineCount= l;
+ } else if (d == fRight.getDocument()) {
+ int l= fRight.getLineCount();
+ right= fRightLineCount != l;
+ fRightLineCount= l;
+ }
+
+ if (left || right) {
+
+ if (left) {
+ if (fLeftCanvas != null)
+ fLeftCanvas.redraw();
+ } else {
+ if (fRightCanvas != null)
+ fRightCanvas.redraw();
+ }
+ Control center= getCenter();
+ if (center != null)
+ center.redraw();
+
+ updateVScrollBar();
+ }
+ }
+
+ private void invalidateLines() {
+ if (isThreeWay()) {
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.redraw();
+ if (fAncestor.isControlOkToUse())
+ fAncestor.getTextWidget().redraw();
+ }
+
+ if (Utilities.okToUse(fLeftCanvas))
+ fLeftCanvas.redraw();
+
+ if (fLeft.isControlOkToUse())
+ fLeft.getTextWidget().redraw();
+
+ if (Utilities.okToUse(getCenter()))
+ getCenter().redraw();
+
+ if (fRight.isControlOkToUse())
+ fRight.getTextWidget().redraw();
+
+ if (Utilities.okToUse(fRightCanvas))
+ fRightCanvas.redraw();
+ }
+
+ private void paintCenter(Canvas canvas, GC g) {
+
+ if (! fSynchronizedScrolling)
+ return;
+
+ int lineHeight= fLeft.getTextWidget().getLineHeight();
+ int visibleHeight= fRight.getViewportHeight();
+
+ Point size= canvas.getSize();
+ int x= 0;
+ int w= size.x;
+
+ g.setBackground(canvas.getBackground());
+ g.fillRectangle(x+1, 0, w-2, size.y);
+
+ if (!IS_MOTIF) {
+ // draw thin line between center ruler and both texts
+ g.setBackground(fLeftCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ g.fillRectangle(0, 0, 1, size.y);
+ g.fillRectangle(w-1, 0, 1, size.y);
+ }
+
+ if (fChangeDiffs != null) {
+ int lshift= fLeft.getVerticalScrollOffset();
+ int rshift= fRight.getVerticalScrollOffset();
+
+ Point region= new Point(0, 0);
+
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ fLeft.getLineRange(diff.fLeftPos, region);
+ int ly= (region.x * lineHeight) + lshift;
+ int lh= region.y * lineHeight;
+
+ fRight.getLineRange(diff.fRightPos, region);
+ int ry= (region.x * lineHeight) + rshift;
+ int rh= region.y * lineHeight;
+
+ if (Math.max(ly+lh, ry+rh) < 0)
+ continue;
+ if (Math.min(ly, ry) >= visibleHeight)
+ break;
+
+ fPts[0]= x; fPts[1]= ly; fPts[2]= w; fPts[3]= ry;
+ fPts[6]= x; fPts[7]= ly+lh; fPts[4]= w; fPts[5]= ry+rh;
+
+ g.setBackground(getColor(getFillColor(diff)));
+ g.fillPolygon(fPts);
+
+ g.setLineWidth(LW);
+ g.setForeground(getColor(getStrokeColor(diff)));
+ g.drawLine(fPts[0], fPts[1], fPts[2], fPts[3]);
+ g.drawLine(fPts[6], fPts[7], fPts[4], fPts[5]);
+ }
+ }
+ }
+
+ private void paintSides(GC g, MergeSourceViewer tp, Canvas canvas, boolean right) {
+
+ int lineHeight= tp.getTextWidget().getLineHeight();
+ int visibleHeight= tp.getViewportHeight();
+
+ Point size= canvas.getSize();
+ int x= 0;
+ int w= MARGIN_WIDTH;
+
+ g.setBackground(canvas.getBackground());
+ g.fillRectangle(x, 0, w, size.y);
+
+ if (!IS_MOTIF) {
+ // draw thin line between ruler and text
+ g.setBackground(canvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ if (right)
+ g.fillRectangle(0, 0, 1, size.y);
+ else
+ g.fillRectangle(size.x-1, 0, 1, size.y);
+ }
+
+ if (fChangeDiffs != null) {
+ int shift= tp.getVerticalScrollOffset() + (2-LW);
+
+ Point region= new Point(0, 0);
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ tp.getLineRange(diff.getPosition(tp), region);
+ int y= (region.x * lineHeight) + shift;
+ int h= region.y * lineHeight;
+
+ if (y+h < 0)
+ continue;
+ if (y >= visibleHeight)
+ break;
+
+ g.setBackground(getColor(getFillColor(diff)));
+ if (right)
+ g.fillRectangle(x, y, w-5, h);
+ else
+ g.fillRectangle(x+5, y, w-3, h);
+
+ g.setBackground(getColor(getStrokeColor(diff)));
+ if (right) {
+ g.fillRectangle(x, y-1, w-4, LW);
+ g.fillRectangle(x+5, y, LW, h);
+ g.fillRectangle(x, y+h-1, w-4, LW);
+ } else {
+ g.fillRectangle(x+3, y-1, w-3, LW);
+ g.fillRectangle(x+3, y, LW, h);
+ g.fillRectangle(x+3, y+h-1, w-3, LW);
+ }
+ }
+ }
+ }
+
+ private void paint(PaintEvent event, MergeSourceViewer tp) {
+
+ if (fChangeDiffs == null)
+ return;
+
+ Control canvas= (Control) event.widget;
+ GC g= event.gc;
+
+ int lineHeight= tp.getTextWidget().getLineHeight();
+ int w= canvas.getSize().x;
+ int shift= tp.getVerticalScrollOffset() + (2-LW);
+ int maxh= event.y+event.height; // visibleHeight
+
+ Point range= new Point(0, 0);
+
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ tp.getLineRange(diff.getPosition(tp), range);
+ int y= (range.x * lineHeight) + shift;
+ int h= range.y * lineHeight;
+
+ if (y+h < event.y)
+ continue;
+ if (y > maxh)
+ break;
+
+ g.setBackground(getColor(getStrokeColor(diff)));
+ g.fillRectangle(0, y-1, w, LW);
+ g.fillRectangle(0, y+h-1, w, LW);
+ }
+ }
+
+ private RGB getFillColor(Diff diff) {
+ boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ return selected ? SELECTED_OUTGOING_FILL : OUTGOING_FILL;
+ case RangeDifference.ANCESTOR:
+ return selected ? SELECTED_CONFLICT_FILL : CONFLICT_FILL;
+ case RangeDifference.LEFT:
+ return selected ? SELECTED_INCOMING_FILL : INCOMING_FILL;
+ case RangeDifference.CONFLICT:
+ return selected ? SELECTED_CONFLICT_FILL : CONFLICT_FILL;
+ }
+ return null;
+ }
+
+ private RGB getStrokeColor(Diff diff) {
+ boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ return selected ? SELECTED_OUTGOING : OUTGOING;
+ case RangeDifference.ANCESTOR:
+ return selected ? SELECTED_CONFLICT : CONFLICT;
+ case RangeDifference.LEFT:
+ return selected ? SELECTED_INCOMING : INCOMING;
+ case RangeDifference.CONFLICT:
+ return selected ? SELECTED_CONFLICT : CONFLICT;
+ }
+ return null;
+ }
+
+ private Color getColor(RGB rgb) {
+ if (rgb == null)
+ return null;
+ if (fColors == null)
+ fColors= new HashMap(20);
+ Color c= (Color) fColors.get(rgb);
+ if (c == null) {
+ c= new Color(fComposite.getDisplay(), rgb);
+ fColors.put(rgb, c);
+ }
+ return c;
+ }
+
+ //---- Navigating and resolving Diffs
+
+ /**
+ */
+ private void navigate(boolean down) {
+
+ Diff diff= null;
+ if (fChangeDiffs != null) {
+ MergeSourceViewer part= fFocusPart;
+ if (part == null)
+ part= fRight;
+
+ if (part != null) {
+ Point s= part.getSelectedRange();
+ if (down)
+ diff= findNext(part, fChangeDiffs, s.x, s.x+s.y);
+ else
+ diff= findPrev(part, fChangeDiffs, s.x, s.x+s.y);
+ }
+ }
+
+ if (diff == null) {
+ Control c= getControl();
+ if (Utilities.okToUse(c))
+ c.getDisplay().beep();
+ if (DEAD_STEP)
+ return;
+ if (fChangeDiffs.size() > 0) {
+ if (down)
+ diff= (Diff) fChangeDiffs.get(0);
+ else
+ diff= (Diff) fChangeDiffs.get(fChangeDiffs.size()-1);
+ }
+ }
+
+ setCurrentDiff(diff, true);
+ }
+
+ /**
+ * Find the Diff that overlaps with the given TextPart's text range.
+ * If the range doesn't overlap with any range <code>null</code>
+ * is returned.
+ */
+ private Diff findDiff(MergeSourceViewer tp, int rangeStart, int rangeEnd) {
+ if (fChangeDiffs != null) {
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.contains(tp, rangeStart, rangeEnd))
+ return diff;
+ }
+ }
+ return null;
+ }
+
+ private static Diff findNext(MergeSourceViewer tp, List v, int start, int end) {
+ for (int i= 0; i < v.size(); i++) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(tp);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ if (end < startOffset)
+ return diff;
+ if (diff.fDiffs != null) {
+ Diff d= null;
+ int endOffset= startOffset + p.getLength();
+ if (start == startOffset && end == endOffset) {
+ d= findNext(tp, diff.fDiffs, start, start);
+ } else if (end < endOffset) {
+ d= findNext(tp, diff.fDiffs, start, end);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Diff findPrev(MergeSourceViewer tp, List v, int start, int end) {
+ for (int i= v.size()-1; i >= 0; i--) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(tp);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ int endOffset= startOffset + p.getLength();
+ if (start > endOffset)
+ return diff;
+ if (diff.fDiffs != null) {
+ Diff d= null;
+ if (start == startOffset && end == endOffset) {
+ d= findPrev(tp, diff.fDiffs, end, end);
+ } else if (start >= startOffset) {
+ d= findPrev(tp, diff.fDiffs, start, end);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Set the currently active Diff and update the toolbars controls and lines.
+ * If <code>revealAndSelect</code> is <code>true</code> the Diff is revealed and
+ * selected in both TextParts.
+ */
+ private void setCurrentDiff(Diff d, boolean revealAndSelect) {
+
+ if (d == fCurrentDiff)
+ return;
+
+ Diff oldDiff= fCurrentDiff;
+
+ if (d != null && revealAndSelect) {
+
+ // before we set fCurrentDiff we change the selection
+ // so that the paint code uses the old background colors
+ // otherwise we get screen cheese
+ if (isThreeWay())
+ fAncestor.setSelection(d.fAncestorPos);
+ fLeft.setSelection(d.fLeftPos);
+ fRight.setSelection(d.fRightPos);
+
+ // now switch diffs
+ fCurrentDiff= d;
+ revealDiff(d, d.fIsToken);
+ } else {
+ fCurrentDiff= d;
+ }
+
+ if (d != null && !d.fIsToken) {
+ updateDiffBackground(oldDiff);
+ updateDiffBackground(fCurrentDiff);
+ }
+
+ updateControls();
+ invalidateLines();
+ }
+
+ private void revealDiff(Diff d, boolean smart) {
+
+ boolean ancestorIsVisible= false;
+ boolean leftIsVisible= false;
+ boolean rightIsVisible= false;
+
+ if (smart) {
+ Point region= new Point(0, 0);
+ // find the starting line of the diff in all text widgets
+ int ls= fLeft.getLineRange(d.fLeftPos, region).x;
+ int rs= fRight.getLineRange(d.fRightPos, region).x;
+
+ if (isThreeWay()) {
+ int as= fAncestor.getLineRange(d.fAncestorPos, region).x;
+ if (as >= fAncestor.getTopIndex() && as <= fAncestor.getBottomIndex())
+ ancestorIsVisible= true;
+ }
+
+ if (ls >= fLeft.getTopIndex() && ls <= fLeft.getBottomIndex())
+ leftIsVisible= true;
+
+ if (rs >= fRight.getTopIndex() && rs <= fRight.getBottomIndex())
+ rightIsVisible= true;
+
+ if (leftIsVisible && rightIsVisible)
+ return;
+ }
+
+ int vpos= 0;
+
+ MergeSourceViewer allButThis= null;
+ if (leftIsVisible) {
+ vpos= realToVirtualPosition(fLeft, fLeft.getTopIndex());
+ allButThis= fLeft;
+ } else if (rightIsVisible) {
+ vpos= realToVirtualPosition(fRight, fRight.getTopIndex());
+ allButThis= fRight;
+ } else if (ancestorIsVisible) {
+ vpos= realToVirtualPosition(fAncestor, fAncestor.getTopIndex());
+ allButThis= fAncestor;
+ } else {
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ for (int i= 0; e.hasNext(); i++) {
+ Diff diff= (Diff) e.next();
+ if (diff == d)
+ break;
+ vpos+= diff.getMaxDiffHeight(fShowAncestor);
+ }
+ }
+ //vpos-= fRight.getViewportLines()/4;
+ }
+
+ scrollVertical(vpos, allButThis);
+
+ if (fVScrollBar != null) {
+ //int value= Math.max(0, Math.min(vpos, getVirtualHeight() - maxExtentHeight));
+ fVScrollBar.setSelection(vpos);
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+
+ protected void copy(boolean leftToRight) {
+ if (leftToRight) {
+ if (fLeft.getEnabled()) {
+ // copy text
+ String text= fLeft.getTextWidget().getText();
+ fRight.getTextWidget().setText(text);
+ fRight.setEnabled(true);
+ } else {
+ // delete
+ fRight.getTextWidget().setText("");
+ fRight.setEnabled(false);
+ }
+ fRightLineCount= fRight.getLineCount();
+ setRightDirty(true);
+ fRightContentsChanged= false;
+ } else {
+ if (fRight.getEnabled()) {
+ // copy text
+ String text= fRight.getTextWidget().getText();
+ fLeft.getTextWidget().setText(text);
+ fLeft.setEnabled(true);
+ } else {
+ // delete
+ fLeft.getTextWidget().setText("");
+ fLeft.setEnabled(false);
+ }
+ fLeftLineCount= fLeft.getLineCount();
+ setLeftDirty(true);
+ fLeftContentsChanged= false;
+ }
+ doDiff();
+ invalidateLines();
+ updateVScrollBar();
+ selectFirstDiff();
+ }
+
+ private void copyDiffLeftToRight() {
+ copy(fCurrentDiff, true, false, false);
+ }
+
+ private void copyDiffRightToLeft() {
+ copy(fCurrentDiff, false, false, false);
+ }
+
+// private void accept(Diff diff, boolean both, boolean gotoNext) {
+// if (getCompareConfiguration().isRightEditable())
+// copy(diff, true, both, gotoNext);
+// else if (getCompareConfiguration().isLeftEditable())
+// copy(diff, false, both, gotoNext);
+// }
+
+ private void copy(Diff diff, boolean leftToRight, boolean both, boolean gotoNext) {
+
+ if (diff != null && !diff.isResolved()) {
+
+ Position fromPos= null;
+ Position toPos= null;
+ IDocument fromDoc= null;
+ IDocument toDoc= null;
+
+ if (leftToRight) {
+ fRight.setEnabled(true);
+ fromPos= diff.fLeftPos;
+ toPos= diff.fRightPos;
+ fromDoc= fLeft.getDocument();
+ toDoc= fRight.getDocument();
+ } else {
+ fLeft.setEnabled(true);
+ fromPos= diff.fRightPos;
+ toPos= diff.fLeftPos;
+ fromDoc= fRight.getDocument();
+ toDoc= fLeft.getDocument();
+ }
+
+ if (fromDoc != null) {
+
+ int fromStart= fromPos.getOffset();
+ int fromLen= fromPos.getLength();
+
+ int toStart= toPos.getOffset();
+ int toLen= toPos.getLength();
+
+ try {
+ String s= null;
+
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ case RangeDifference.LEFT:
+ s= fromDoc.get(fromStart, fromLen);
+ break;
+ case RangeDifference.ANCESTOR:
+ break;
+ case RangeDifference.CONFLICT:
+ s= fromDoc.get(fromStart, fromLen);
+ if (both)
+ s+= toDoc.get(toStart, toLen);
+ break;
+ }
+ if (s != null) {
+ toDoc.replace(toStart, toLen, s);
+ toPos.setOffset(toStart);
+ toPos.setLength(s.length());
+ }
+
+ } catch (BadLocationException e) {
+ }
+ }
+
+ diff.setResolved(true);
+
+ if (gotoNext) {
+ navigate(true/*, true*/);
+ } else {
+ revealDiff(diff, true);
+ updateControls();
+ }
+ }
+ }
+
+ /**
+ */
+// private void reject(Diff diff, boolean gotoNext) {
+//
+// if (diff != null && !diff.isResolved()) {
+//
+// switch (diff.fDirection) {
+// case RangeDifference.RIGHT:
+// setRightDirty(true); // mark dirty to force save!
+// break;
+// case RangeDifference.LEFT:
+// setLeftDirty(true); // mark dirty to force save!
+// break;
+// case RangeDifference.ANCESTOR:
+// break;
+// case RangeDifference.CONFLICT:
+// setLeftDirty(true); // mark dirty to force save!
+// setRightDirty(true); // mark dirty to force save!
+// break;
+// }
+//
+// diff.setResolved(true);
+//
+// if (gotoNext) {
+// navigate(true/*, true*/);
+// } else {
+// revealDiff(diff, true);
+// updateControls();
+// }
+// }
+// }
+
+
+// private void autoResolve() {
+// fCurrentDiff= null;
+// Diff firstConflict= null;
+//
+// Iterator e= fChangeDiffs.iterator();
+// for (int i= 0; e.hasNext(); i++) {
+// Diff diff= (Diff) e.next();
+// if (!diff.isResolved()) {
+// switch (diff.fDirection) {
+// case RangeDifference.RIGHT: // outgoing
+// case RangeDifference.LEFT: // incoming
+// accept(diff, false, false);
+// break;
+// case RangeDifference.CONFLICT: // incoming
+// if (firstConflict == null)
+// firstConflict= diff;
+// break;
+// }
+// }
+// }
+//
+// if (firstConflict == null)
+// firstConflict= (Diff) fChangeDiffs.get(0);
+// setCurrentDiff(firstConflict, true);
+// }
+
+ //---- scrolling
+
+ /**
+ * Calculates virtual height (in lines) of views by adding the maximum of corresponding diffs.
+ */
+ private int getVirtualHeight() {
+ int h= 1;
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ for (int i= 0; e.hasNext(); i++) {
+ Diff diff= (Diff) e.next();
+ h+= diff.getMaxDiffHeight(fShowAncestor);
+ }
+ }
+ return h;
+ }
+
+ /**
+ * The height of the TextEditors in lines.
+ */
+ private int getViewportHeight() {
+ StyledText te= fLeft.getTextWidget();
+
+ int vh= te.getClientArea().height;
+ if (vh == 0) {
+ // seems to be a bug in TextEditor.getClientArea(): returns bogus value on first
+ // call; as a workaround we calculate the clientArea from its container...
+ Rectangle trim= te.computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+
+ int headerHeight= getHeaderHeight();
+
+ Composite composite= (Composite) getControl();
+ Rectangle r= composite.getClientArea();
+
+ vh= r.height-headerHeight-scrollbarHeight;
+ }
+
+ return vh / te.getLineHeight();
+ }
+
+ /**
+ * Returns the virtual position for the given view position.
+ */
+ private int realToVirtualPosition(MergeSourceViewer w, int vpos) {
+
+ if (! fSynchronizedScrolling || fAllDiffs == null)
+ return vpos;
+
+ int viewPos= 0; // real view position
+ int virtualPos= 0; // virtual position
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(w);
+ w.getLineRange(pos, region);
+ int realHeight= region.y;
+ int virtualHeight= diff.getMaxDiffHeight(fShowAncestor);
+ if (vpos <= viewPos + realHeight) { // OK, found!
+ vpos-= viewPos; // make relative to this slot
+ // now scale position within this slot to virtual slot
+ if (realHeight <= 0)
+ vpos= 0;
+ else
+ vpos= (vpos*virtualHeight)/realHeight;
+ return virtualPos+vpos;
+ }
+ viewPos+= realHeight;
+ virtualPos+= virtualHeight;
+ }
+ return virtualPos;
+ }
+
+ private void scrollVertical(int virtualPos, MergeSourceViewer allBut) {
+
+ if (virtualPos < 0)
+ virtualPos= virtualPos;
+
+ GC gc;
+ if (fSynchronizedScrolling) {
+ int s= 0;
+
+ if (true) {
+ s= getVirtualHeight() - virtualPos;
+ int height= fRight.getViewportLines()/4;
+ if (s < 0)
+ s= 0;
+ if (s > height)
+ s= height;
+ }
+
+ fInScrolling= true;
+
+ if (isThreeWay() && allBut != fAncestor) {
+ int y= virtualToRealPosition(fAncestor, virtualPos+s)-s;
+ fAncestor.vscroll(y);
+ }
+
+ if (allBut != fLeft) {
+ int y= virtualToRealPosition(fLeft, virtualPos+s)-s;
+ fLeft.vscroll(y);
+ }
+
+ if (allBut != fRight) {
+ int y= virtualToRealPosition(fRight, virtualPos+s)-s;
+ fRight.vscroll(y);
+ }
+
+ fInScrolling= false;
+
+ if (isThreeWay())
+ fAncestorCanvas.repaint();
+
+ fLeftCanvas.repaint();
+
+ Control center= getCenter();
+ if (center instanceof BufferedCanvas)
+ ((BufferedCanvas)center).repaint();
+
+ fRightCanvas.repaint();
+ } else {
+ if (allBut == fAncestor && isThreeWay())
+ fAncestorCanvas.repaint();
+
+ if (allBut == fLeft)
+ fLeftCanvas.repaint();
+
+ if (allBut == fRight)
+ fRightCanvas.repaint();
+ }
+ }
+
+ /**
+ * Updates Scrollbars with viewports.
+ */
+ private void syncViewport(MergeSourceViewer w) {
+
+ if (fInScrolling)
+ return;
+
+ int ix= w.getTopIndex();
+ int ix2= w.getDocumentRegionOffset();
+
+ int viewPosition= realToVirtualPosition(w, ix-ix2);
+
+ scrollVertical(viewPosition, w); // scroll all but the given views
+
+ if (fVScrollBar != null) {
+ int value= Math.max(0, Math.min(viewPosition, getVirtualHeight() - getViewportHeight()));
+ fVScrollBar.setSelection(value);
+ }
+ }
+
+ /**
+ */
+ private void updateVScrollBar() {
+
+ if (Utilities.okToUse(fVScrollBar) && fVScrollBar.isVisible()) {
+ int virtualHeight= getVirtualHeight();
+ int viewPortHeight= getViewportHeight();
+ fVScrollBar.setPageIncrement(viewPortHeight-1);
+ fVScrollBar.setMaximum(virtualHeight);
+ if (viewPortHeight > virtualHeight)
+ fVScrollBar.setThumb(virtualHeight);
+ else
+ fVScrollBar.setThumb(viewPortHeight);
+ }
+ }
+
+ /**
+ * maps given virtual position into a real view position of this view.
+ */
+ private int virtualToRealPosition(MergeSourceViewer part, int v) {
+
+ if (! fSynchronizedScrolling || fAllDiffs == null)
+ return v;
+
+ int virtualPos= 0;
+ int viewPos= 0;
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(part);
+ int viewHeight= part.getLineRange(pos, region).y;
+ int virtualHeight= diff.getMaxDiffHeight(fShowAncestor);
+ if (v < (virtualPos + virtualHeight)) {
+ v-= virtualPos; // make relative to this slot
+ if (viewHeight <= 0) {
+ v= 0;
+ } else {
+ v= (v*viewHeight)/virtualHeight;
+ }
+ return viewPos+v;
+ }
+ virtualPos+= virtualHeight;
+ viewPos+= viewHeight;
+ }
+ return viewPos;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties
new file mode 100644
index 000000000..7ff3f9d06
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties
@@ -0,0 +1,114 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 1999-2001
+# =====================================
+
+# @(#)TextMergeViewerResources.properties
+#
+# Resource strings for TextMergeViewer.java
+
+#####################################################
+# Images
+#####################################################
+
+#####################################################
+# Status messages
+#####################################################
+status.same=No Change
+status.resolved=Resolved
+status.unresolved=Unresolved
+status.allresolved=All Resolved
+
+status.unresolvedformat={0} (remaining differences: {1})
+status.resolvedformat={0}
+
+#####################################################
+# Dialogs
+#####################################################
+
+CantSaveProperty.title=Error while saving contents
+CantSaveProperty.message=Couldn''t save contents.\nReason: {0}
+
+ContentInaccessible.title=Error while accessing content
+ContentInaccessible.message=Content currently inaccessible.
+
+#####################################################
+# Actions
+#####################################################
+
+action.CopyLeftToRight.label=Copy Left to Right
+action.CopyLeftToRight.tooltip=Copy whole document from left to right
+action.CopyLeftToRight.image=ctool16/lefttoright2.gif
+
+action.CopyRightToLeft.label=Copy Right to Left
+action.CopyRightToLeft.tooltip=Copy whole document from right to left
+action.CopyRightToLeft.image=ctool16/righttoleft2.gif
+
+action.CopyDiffLeftToRight.label=Copy Current Diff
+action.CopyDiffLeftToRight.tooltip=Copy current change from left to right
+action.CopyDiffLeftToRight.image=ctool16/lefttoright.gif
+
+action.CopyDiffRightToLeft.label=Copy Current Diff
+action.CopyDiffRightToLeft.tooltip=Copy current change from right to left
+action.CopyDiffRightToLeft.image=ctool16/righttoleft.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.ToggleMergeDirection.label=Toggle Merge Direction
+#action.ToggleMergeDirection.tooltip=Toggle merge direction
+#action.ToggleMergeDirection.image=ctool16/toggle.gif
+
+action.EnableAncestor.label=Enable Ancestor Pane
+action.EnableAncestor.tooltip=Controls visibility of ancestor pane
+action.EnableAncestor.image=ctool16/panes3.gif
+
+action.SynchMode.label=Synchonize Panes
+action.SynchMode.tooltip=Synchonize pane scrolling
+action.SynchMode.image=ctool16/synchmode.gif
+
+#action.IgnoreWhiteSpace.label=Ignore White Space
+#action.IgnoreWhiteSpace.tooltip=Ignore white space
+#action.IgnoreWhiteSpace.image=ctool16/ignorews.gif
+
+# Pop up menu
+
+action.CopyText.label=Copy@Ctrl+C
+action.CopyText.tooltip=Copy Text Selection to Clipboard
+
+action.CutText.label=Cut@Ctrl+X
+action.CutText.tooltip=Cut Text Selection to Clipboard
+
+action.DeleteText.label=Delete
+action.DeleteText.tooltip=Delete current Text Selection
+
+action.Find.label=Find...@Ctrl+F
+action.Find.tooltip=Find Occurance
+
+action.PasteText.label=Paste@Ctrl+V
+action.PasteText.tooltip=Replace Text Selection with Clipboard
+
+action.RedoText.label=Redo@Ctrl+Y
+action.RedoText.tooltip=Redo last Operation
+
+action.SelectAllText.label=Select All@Ctrl+A
+action.SelectAllText.tooltip=Select all Changes
+
+action.Save.label=Save@Ctrl+S
+action.Save.tooltip=Save Changes
+
+action.UndoText.label=Undo@Ctrl+Z
+action.UndoText.tooltip=Undo last Operation
+
+action.ContentAssistProposal.label=Code Ass&ist Proposals@Ctrl+Space
+action.ContentAssistProposal.description=Show content assist proposals
+
+action.ContentAssistTip.label=Code Ass&ist Tips@Ctrl+Shift+Space
+action.ContentAssistTip.description=Show content assist tips
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html
new file mode 100644
index 000000000..28d91eedb
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html
@@ -0,0 +1,35 @@
+<!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>
+Support for compare and merge viewers which show the
+content side-by-side.
+<h2>
+Package Specification</h2>
+The <tt>ContentMergeViewer</tt> is an abstract compare and merge viewer
+with two side-by-side content areas and an optional content area for a
+common ancestor (for three-way compare). Because the implementation makes
+no assumptions about the content type it is a subclass responsibility to
+deal with a specific type.
+<p>A <tt>ContentMergeViewer</tt> accesses its model by means of a content
+provider which must implement the
+<br><tt>IMergeViewerContentProvider</tt> interface.
+<p>The <tt>TextMergeViewer</tt> is the standard concrete subclass of <tt>ContentMergeViewer</tt>.
+A text merge viewer uses the <tt>RangeDifferencer</tt> to perform a textual,
+line-by-line comparison of two (or three) input documents. For text lines
+that differ the <tt>TextMergeViewer</tt> uses an <tt>ITokenComparator</tt>
+to find longest sequences of matching and non-matching tokens. The <tt>TextMergeViewer</tt>'s
+default token compare works on characters separated by whitespace. If a
+different strategy is needed (for example, Java tokens in a Java-aware
+merge viewer), clients can create their own token comparators by implementing
+the <tt>ITokenComparator</tt> interface.
+<p>The <tt>TextMergeViewer</tt> not only works on whole documents but on
+subranges of documents too. In this case the viewer's input must be an
+<tt>IDocumentRange</tt> instead of an <tt>IDocument</tt>.
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java
new file mode 100644
index 000000000..2017e31de
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.Viewer;
+
+
+abstract class AbstractViewer extends Viewer {
+
+ public void setInput(Object input) {
+ }
+
+ public Object getInput() {
+ return null;
+ }
+
+ public ISelection getSelection() {
+ return null;
+ }
+
+ public void setSelection(ISelection s, boolean reveal) {
+ }
+
+ public void refresh() {
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java
new file mode 100644
index 000000000..eb6c44eab
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java
@@ -0,0 +1,17 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000,2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.widgets.Shell;
+
+
+public class AddFromHistoryDialog extends Dialog {
+
+ public AddFromHistoryDialog(Shell parent) {
+ super(parent);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java
new file mode 100644
index 000000000..07be03f22
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.*;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+
+/**
+ * A simple compare viewer for binary files.
+ * Shows the position of the first non-matching byte.
+ */
+public class BinaryCompareViewer extends AbstractViewer {
+
+ private static final int EOF= -1;
+ private Text fControl;
+ private ICompareInput fInput;
+
+
+ public BinaryCompareViewer(Composite parent, CompareConfiguration cc) {
+ fControl= new Text(parent, SWT.NONE);
+ fControl.setEditable(false);
+ fControl.setData(CompareUI.COMPARE_VIEWER_TITLE, "Binary Compare");
+ }
+
+ public Control getControl() {
+ return fControl;
+ }
+
+ public void setInput(Object input) {
+ if (fControl != null && input instanceof ICompareInput) {
+ fInput= (ICompareInput) input;
+
+ InputStream left= null;
+ InputStream right= null;
+
+ try {
+ left= getStream(fInput.getLeft());
+ right= getStream(fInput.getRight());
+
+ int pos= 0;
+ while (true) {
+ int l= left.read();
+ int r= right.read();
+ if (l != r) {
+ fControl.setText("first bytes differ at position " + pos);
+ break;
+ }
+ if (l == EOF)
+ break;
+ pos++;
+ }
+ } catch (CoreException ex) {
+ fControl.setText("CoreException " + ex);
+ } catch (IOException ex) {
+ fControl.setText("IOException " + ex);
+ } finally {
+ if (left != null) {
+ try {
+ left.close();
+ } catch (IOException ex) {
+ }
+ }
+ if (right != null) {
+ try {
+ right.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ private InputStream getStream(ITypedElement input) throws CoreException {
+ if (input instanceof IStreamContentAccessor)
+ return ((IStreamContentAccessor)input).getContents();
+ return new ByteArrayInputStream(new byte[0]);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java
new file mode 100644
index 000000000..503756b10
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+
+/**
+ * A factory object for the <code>BinaryCompareViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class BinaryCompareViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new BinaryCompareViewer(parent, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java
new file mode 100644
index 000000000..6a073a3e7
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.events.*;
+
+/**
+ * A Canvas which reduces flicker by drawing in an off screen buffer.
+ */
+public abstract class BufferedCanvas extends Canvas {
+
+ /** The drawable for double buffering */
+ private Image fBuffer;
+
+ public BufferedCanvas(Composite parent, int flags) {
+ super(parent, flags + SWT.NO_BACKGROUND);
+
+ addPaintListener(
+ new PaintListener() {
+ public void paintControl(PaintEvent event) {
+ doubleBufferPaint(event.gc);
+ }
+ }
+ );
+
+ addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fBuffer != null) {
+ fBuffer.dispose();
+ fBuffer= null;
+ }
+ }
+ }
+ );
+ }
+
+ public void repaint() {
+ if (!isDisposed()) {
+ GC gc= new GC(this);
+ doubleBufferPaint(gc);
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Double buffer drawing.
+ * @private
+ */
+ private void doubleBufferPaint(GC dest) {
+
+ Point size= getSize();
+
+ if (size.x <= 0 || size.y <= 0)
+ return;
+
+ if (fBuffer != null) {
+ Rectangle r= fBuffer.getBounds();
+ if (r.width != size.x || r.height != size.y) {
+ fBuffer.dispose();
+ fBuffer= null;
+ }
+ }
+ if (fBuffer == null)
+ fBuffer= new Image(getDisplay(), size.x, size.y);
+
+ GC gc= new GC(fBuffer);
+ try {
+ gc.setBackground(getBackground());
+ gc.fillRectangle(0, 0, size.x, size.y);
+ doPaint(gc);
+ } finally {
+ gc.dispose();
+ }
+
+ dest.drawImage(fBuffer, 0, 0);
+ }
+
+ abstract public void doPaint(GC gc);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java
new file mode 100644
index 000000000..5f54e30f7
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * Toggles a boolean property of an <code>ICompareConfiguration</code>.
+ */
+public class ChangePropertyAction extends Action {
+
+ private CompareConfiguration fCompareConfiguration;
+ private String fPropertyKey;
+
+
+ public ChangePropertyAction(ResourceBundle bundle, CompareConfiguration cc, String rkey, String pkey) {
+ fPropertyKey= pkey;
+ Utilities.initAction(this, bundle, rkey);
+ setCompareConfiguration(cc);
+ }
+
+ public void run() {
+ boolean b= !Utilities.getBoolean(fCompareConfiguration, fPropertyKey, false);
+ setChecked(b);
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.setProperty(fPropertyKey, new Boolean(b));
+ }
+
+ public void setCompareConfiguration(CompareConfiguration cc) {
+ fCompareConfiguration= cc;
+ setChecked(Utilities.getBoolean(fCompareConfiguration, fPropertyKey, false));
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java
new file mode 100644
index 000000000..4193d9a80
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.ui.IActionDelegate;
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+
+
+public class CompareAction implements IActionDelegate {
+
+ private ISelection fSelection;
+
+ public void run(IAction action) {
+ CompareUI.openCompareEditor(new ResourceCompareInput(new CompareConfiguration(), fSelection));
+ }
+
+ public void selectionChanged(IAction a, ISelection s) {
+ fSelection= s;
+ // the following check is disabled because it results in a confusing UI:
+ // action might be enabled if plugin is not loaded but
+ // it gets disabled after plugin has been loaded...
+ //Object[] selection= StructuredSelection.toArray(s);
+ //((Action)a).setEnabled(selection.length == 2);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java
new file mode 100644
index 000000000..cb6ce6526
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.layout.*;
+
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.util.Assert;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+
+public class CompareDialog extends Dialog implements IPropertyChangeListener {
+
+ private static final String COMMIT_LABEL= "Commit";
+
+ private CompareEditorInput fCompareEditorInput;
+ private Button fCommitButton;
+
+
+ CompareDialog(Shell shell, CompareEditorInput input) {
+ super(shell);
+ setShellStyle(SWT.CLOSE | SWT.APPLICATION_MODAL | SWT.RESIZE);
+
+ Assert.isNotNull(input);
+ fCompareEditorInput= input;
+ fCompareEditorInput.addPropertyChangeListener(this);
+ }
+
+ public boolean close() {
+ if (super.close()) {
+ if (fCompareEditorInput != null)
+ fCompareEditorInput.addPropertyChangeListener(this);
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Dialog.
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+ fCommitButton= createButton(parent, IDialogConstants.OK_ID, COMMIT_LABEL, true);
+ fCommitButton.setEnabled(false);
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (fCommitButton != null && fCompareEditorInput != null)
+ fCommitButton.setEnabled(fCompareEditorInput.isSaveNeeded());
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Dialog.
+ */
+ protected Control createDialogArea(Composite parent) {
+
+ Control c= fCompareEditorInput.createContents(parent);
+ c.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Shell shell= c.getShell();
+ shell.setText(fCompareEditorInput.getTitle());
+ shell.setImage(fCompareEditorInput.getTitleImage());
+
+ return c;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Window.
+ */
+ protected Point getInitialSize() {
+ Point size= new Point(0, 0);
+ Shell shell= getParentShell();
+ if (shell != null) {
+ Point parentSize= shell.getSize();
+ size.x= parentSize.x-100;
+ size.y= parentSize.y-100;
+ }
+ if (size.x < 800)
+ size.x= 800;
+ if (size.y < 600)
+ size.y= 600;
+ return size;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Window.
+ */
+ public int open() {
+
+ int rc= super.open();
+
+ if (rc == OK && fCompareEditorInput.isSaveNeeded()) {
+
+ WorkspaceModifyOperation operation= new WorkspaceModifyOperation() {
+ public void execute(IProgressMonitor pm) throws CoreException {
+ fCompareEditorInput.save(pm);
+ }
+ };
+
+ Shell shell= getParentShell();
+ ProgressMonitorDialog pmd= new ProgressMonitorDialog(shell);
+ try {
+ operation.run(pmd.getProgressMonitor());
+
+ } catch (InterruptedException x) {
+ } catch (OperationCanceledException x) {
+ } catch (InvocationTargetException x) {
+ //String title= getResourceString("Error.save.title");
+ //String msg= getResourceString("Error.save.message");
+ String title= "Save Error";
+ String msg= "Can't save ";
+ MessageDialog.openError(shell, title, msg + x.getTargetException().getMessage());
+ }
+ }
+
+ return rc;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java
new file mode 100644
index 000000000..cfc6ccf6c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.*;
+import org.eclipse.ui.*;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.compare.*;
+import org.eclipse.ui.part.EditorPart;
+
+
+/**
+ * A CompareEditor takes a ICompareEditorInput as input.
+ * Most functionality is delegated to the ICompareEditorInput.
+ */
+public class CompareEditor extends EditorPart implements IPropertyChangeListener {
+
+ public CompareEditor() {
+ }
+
+ /* package */ CompareConfiguration getCompareConfiguration() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ return ((CompareEditorInput)input).getCompareConfiguration();
+ return null;
+ }
+
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+
+ if (!(input instanceof CompareEditorInput))
+ throw new PartInitException("Invalid Input: Must be CompareEditorInput");
+
+ CompareEditorInput cei= (CompareEditorInput) input;
+
+ setSite(site);
+ setInput(input);
+
+ setTitleImage(cei.getTitleImage());
+ setTitle(cei.getTitle());
+
+ if (input instanceof IPropertyChangeNotifier)
+ ((IPropertyChangeNotifier)input).addPropertyChangeListener(this);
+ }
+
+ /*
+ * @see IDesktopPart#createPartControl(Composite)
+ */
+ public void createPartControl(Composite parent) {
+
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ ((CompareEditorInput) input).createContents(parent);
+ }
+
+ /*
+ * @see DesktopPart#dispose
+ */
+ public void dispose() {
+
+ IEditorInput input= getEditorInput();
+ if (input instanceof IPropertyChangeNotifier)
+ ((IPropertyChangeNotifier)input).removePropertyChangeListener(this);
+
+ super.dispose();
+ }
+
+ /*
+ * @see IDesktopPart#setFocus
+ */
+ public void setFocus() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ ((CompareEditorInput)input).setFocus();
+ }
+
+ /**
+ * Returns false because the editor doesn't support "Save As...".
+ */
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ public void gotoMarker(IMarker marker) {
+ }
+
+ /**
+ * Always throws an AssertionFailedException.
+ */
+ /*
+ * @see IEditorPart#doSaveAs()
+ */
+ public void doSaveAs() {
+ Assert.isTrue(false, "Save As not supported for CompareEditor");
+ }
+
+ /*
+ * @see IEditorPart#doSave()
+ */
+ public void doSave(IProgressMonitor progressMonitor) {
+
+ final IEditorInput input= getEditorInput();
+
+ WorkspaceModifyOperation operation= new WorkspaceModifyOperation() {
+ public void execute(IProgressMonitor pm) throws CoreException {
+ if (input instanceof CompareEditorInput)
+ ((CompareEditorInput)input).save(pm);
+ }
+ };
+
+ Shell shell= getSite().getWorkbenchWindow().getShell();
+
+ try {
+
+ operation.run(progressMonitor);
+
+ firePropertyChange(PROP_DIRTY);
+
+
+ } catch (InterruptedException x) {
+ } catch (OperationCanceledException x) {
+ } catch (InvocationTargetException x) {
+ //String title= getResourceString("Error.save.title");
+ //String msg= getResourceString("Error.save.message");
+ String title= "Save Error";
+ String msg= "Can't save ";
+ MessageDialog.openError(shell, title, msg + x.getTargetException().getMessage());
+ }
+ }
+
+ /*
+ * @see IEditorPart#isDirty()
+ */
+ public boolean isDirty() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ return ((CompareEditorInput)input).isSaveNeeded();
+ return false;
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (isDirty())
+ firePropertyChange(PROP_DIRTY);
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java
new file mode 100644
index 000000000..be21b8ab3
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.part.EditorActionBarContributor;
+
+import org.eclipse.compare.*;
+
+
+public class CompareEditorContributor extends EditorActionBarContributor {
+
+ private IgnoreWhiteSpaceAction fIgnoreWhitespace;
+ private ShowPseudoConflicts fShowPseudoConflicts;
+
+
+ public CompareEditorContributor() {
+ ResourceBundle bundle= CompareUIPlugin.getResourceBundle();
+ fIgnoreWhitespace= new IgnoreWhiteSpaceAction(bundle, null);
+ fShowPseudoConflicts= new ShowPseudoConflicts(bundle, null);
+ }
+
+ public void contributeToToolBar(IToolBarManager tbm) {
+ tbm.add(new Separator());
+ tbm.add(fIgnoreWhitespace);
+ tbm.add(fShowPseudoConflicts);
+ }
+
+ public void setActiveEditor(IEditorPart targetEditor) {
+ if (targetEditor instanceof CompareEditor) {
+ CompareConfiguration cc= ((CompareEditor) targetEditor).getCompareConfiguration();
+ fIgnoreWhitespace.setCompareConfiguration(cc);
+ fShowPseudoConflicts.setCompareConfiguration(cc);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties
new file mode 100644
index 000000000..7c77fdb76
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties
@@ -0,0 +1,35 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 1999-2001
+# =====================================
+# @(#)ComparePluginResources.properties
+#
+# Resource strings for Compare Plugin
+
+#####################################################
+# Actions
+#####################################################
+
+action.IgnoreWhiteSpace.label=Ignore White Space
+action.IgnoreWhiteSpace.tooltip=Ignore white space where applicable
+action.IgnoreWhiteSpace.image=ctool16/ignorews.gif
+
+action.ShowPseudoConflicts.label=Show Pseudo Conflicts
+action.ShowPseudoConflicts.tooltip=Show pseudo conflicts where applicable
+action.ShowPseudoConflicts.image=ctool16/showpseudoconflicts.gif
+
+#action.ToggleMergeDirection.label=Toggle Merge Direction
+#action.ToggleMergeDirection.tooltip=Toggle merge direction
+#action.ToggleMergeDirection.image=ctool16/toggle.gif
+
+#action.NextDiff.label=Next
+#action.NextDiff.tooltip=Show Next Difference
+#action.NextDiff.image=ctool16/next.gif
+
+#action.PrevDiff.label=Previous
+#action.PrevDiff.tooltip=Show Previous Difference
+#action.PrevDiff.image=ctool16/prev.gif
+
+
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java
new file mode 100644
index 000000000..696dfca4c
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java
@@ -0,0 +1,706 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.ui.internal.SharedImages;
+import org.eclipse.core.runtime.*;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+import org.eclipse.ui.*;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+
+/**
+ * The Compare UI plug-in defines the entry point to initiate a configurable
+ * compare operation on arbitrary resources. The result of the compare
+ * is opened into a compare editor where the details can be browsed and
+ * edited in dynamically selected structure and content viewers.
+ * <p>
+ * The Compare UI provides a registry for content and structure compare viewers,
+ * which is initialized from extensions contributed to extension points
+ * declared by this plug-in.
+ * <p>
+ * This class is the plug-in runtime class for the
+ * <code>"org.eclipse.compare"</code> plug-in.
+ * </p>
+ */
+public final class CompareUIPlugin extends AbstractUIPlugin {
+
+ private static boolean NORMALIZE_CASE= true;
+
+ private final static String CLASS_ATTRIBUTE= "class";
+ private final static String EXTENSIONS_ATTRIBUTE= "extensions";
+
+ private static final String RESOURCE_BUNDLE= "org.eclipse.compare.internal.ComparePluginResources";
+
+ private static final String PLUGIN_ID= "org.eclipse.compare";
+
+ private static final String STRUCTURE_CREATOR_EXTENSION_POINT= "structureCreators";
+ private static final String STRUCTURE_MERGEVIEWER_EXTENSION_POINT= "structureMergeViewers";
+ private static final String CONTENT_MERGEVIEWER_EXTENSION_POINT= "contentMergeViewers";
+ private static final String CONTENT_VIEWER_EXTENSION_POINT= "contentViewers";
+
+ private static final String COMPARE_EDITOR= "org.eclipse.compare.CompareEditor";
+
+ private static final String COMPARE_FAILED= "Compare failed";
+ private static final String PROBLEMS_OPENING_EDITOR= "Problems Opening Editor";
+
+ /** Maps type to icons */
+ private static Map fgImages= new Hashtable(10);
+ /** Maps type to ImageDescriptors */
+ private static Map fgImageDescriptors= new Hashtable(10);
+ /** Maps ImageDescriptors to Images */
+ private static Map fgImages2= new Hashtable(10);
+
+ private static Map fgStructureCreators= new Hashtable(10);
+ private static Map fgStructureViewerDescriptors= new Hashtable(10);
+ private static Map fgContentViewerDescriptors= new Hashtable(10);
+ private static Map fgContentMergeViewerDescriptors= new Hashtable(10);
+
+ private static List fgDisposeOnShutdownImages= new ArrayList();
+
+ private static ResourceBundle fgResourceBundle;
+
+ private static CompareUIPlugin fgComparePlugin;
+
+ /**
+ * Creates the <code>CompareUIPlugin</code> object and registers all
+ * structure creators, content merge viewers, and structure merge viewers
+ * contributed to this plug-in's extension points.
+ * <p>
+ * Note that instances of plug-in runtime classes are automatically created
+ * by the platform in the course of plug-in activation.
+ * </p>
+ *
+ * @param descriptor the plug-in descriptor
+ */
+ public CompareUIPlugin(IPluginDescriptor descriptor) {
+ super(descriptor);
+
+ fgComparePlugin= this;
+
+ registerExtensions();
+ }
+
+ /**
+ * Registers all structure creators, content merge viewers, and structure merge viewers
+ * that are found in the XML plugin files.
+ */
+ private void registerExtensions() {
+ IPluginRegistry registry= Platform.getPluginRegistry();
+
+ // collect all IStructureCreators
+ IConfigurationElement[] elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_CREATOR_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ final IConfigurationElement conf= elements[i];
+ String extensions= conf.getAttribute(EXTENSIONS_ATTRIBUTE);
+ registerStructureCreator(extensions,
+ new IStructureCreatorDescriptor() {
+ public IStructureCreator createStructureCreator() {
+ try {
+ return (IStructureCreator) conf.createExecutableExtension(CLASS_ATTRIBUTE);
+ } catch (CoreException ex) {
+ }
+ return null;
+ }
+ }
+ );
+ }
+
+ // collect all viewers which define the structure mergeviewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_MERGEVIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ ViewerDescriptor desc= new ViewerDescriptor(elements[i]);
+ String ext= desc.getExtension();
+ if (ext != null)
+ registerStructureViewerDescriptor(desc.getExtension(), desc);
+ }
+
+ // collect all viewers which define the content mergeviewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_MERGEVIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ ViewerDescriptor desc= new ViewerDescriptor(elements[i]);
+ String ext= desc.getExtension();
+ if (ext != null)
+ registerContentMergeViewerDescriptor(desc.getExtension(), desc);
+ }
+
+ // collect all viewers which define the content viewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_VIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ ViewerDescriptor desc= new ViewerDescriptor(elements[i]);
+ String ext= desc.getExtension();
+ if (ext != null)
+ registerContentViewerDescriptor(desc.getExtension(), desc);
+ }
+ }
+
+ /**
+ * Returns the singleton instance of this plug-in runtime class.
+ *
+ * @return the compare plug-in instance
+ */
+ public static CompareUIPlugin getDefault() {
+ return fgComparePlugin;
+ }
+
+ /**
+ * Returns this plug-in's resource bundle.
+ *
+ * @return the plugin's resource bundle
+ */
+ public static ResourceBundle getResourceBundle() {
+ if (fgResourceBundle == null)
+ fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+ return fgResourceBundle;
+ }
+
+ /**
+ * Returns the active workkbench page or <code>null</code> if
+ * no active workkbench page can be determined.
+ *
+ * @return the active workkbench page or <code>null</code> if
+ * no active workkbench page can be determined
+ */
+ private static IWorkbenchPage getActivePage() {
+ CompareUIPlugin plugin= getDefault();
+ if (plugin == null)
+ return null;
+ IWorkbench workbench= plugin.getWorkbench();
+ if (workbench == null)
+ return null;
+ IWorkbenchWindow window= workbench.getActiveWorkbenchWindow();
+ if (window == null)
+ return null;
+ return window.getActivePage();
+ }
+
+ /**
+ * Returns the SWT Shell of the active workbench window or <code>null</code> if
+ * no workbench window is active.
+ *
+ * @return the SWT Shell of the active workbench window, or <code>null</code> if
+ * no workbench window is active
+ */
+ public static Shell getShell() {
+ CompareUIPlugin p= getDefault();
+ if (p == null)
+ return null;
+ IWorkbench wb= p.getWorkbench();
+ if (wb == null)
+ return null;
+ IWorkbenchWindow ww= wb.getActiveWorkbenchWindow();
+ if (ww == null)
+ return null;
+ return ww.getShell();
+ }
+
+ /**
+ * Registers the given image for being disposed when this plug-in is shutdown.
+ *
+ * @param image the image to register for disposal
+ */
+ public static void disposeOnShutdown(Image image) {
+ if (image != null)
+ fgDisposeOnShutdownImages.add(image);
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Plugin.
+ * Frees all resources of the compare plug-in.
+ */
+ public void shutdown() throws CoreException {
+ super.shutdown();
+
+ if (fgDisposeOnShutdownImages != null) {
+ Iterator i= fgDisposeOnShutdownImages.iterator();
+ while (i.hasNext()) {
+ Image img= (Image) i.next();
+ if (!img.isDisposed())
+ img.dispose();
+ }
+ fgImages= null;
+ }
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * compare editor on the result.
+ *
+ * @param input the input on which to open the compare editor
+ * @see ICompareEditorInput
+ */
+ public void openCompareEditor(CompareEditorInput input) {
+
+ if (compareResultOK(input)) {
+ IWorkbenchPage activePage= getActivePage();
+ if (activePage != null) {
+ try {
+ activePage.openEditor(input, COMPARE_EDITOR);
+ } catch (PartInitException e) {
+ MessageDialog.openError(getShell(), PROBLEMS_OPENING_EDITOR, e.getMessage());
+ }
+ } else {
+ MessageDialog.openError(getShell(), PROBLEMS_OPENING_EDITOR, "Can't find active workbench page");
+ }
+ }
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * compare dialog on the result.
+ *
+ * @param input the input on which to open the compare editor
+ * @see ICompareEditorInput
+ */
+ public void openCompareDialog(final CompareEditorInput input) {
+
+ if (compareResultOK(input)) {
+ CompareDialog dialog= new CompareDialog(getShell(), input);
+ dialog.open();
+ }
+ }
+
+ /**
+ * @return <code>true</code> if compare result is OK to show, <code>false</code> otherwise
+ */
+ private boolean compareResultOK(CompareEditorInput input) {
+ final Shell shell= getShell();
+ try {
+
+ // run operation in separate thread and make it canceable
+ new ProgressMonitorDialog(shell).run(true, true, input);
+
+ String message= input.getMessage();
+ if (message != null) {
+ MessageDialog.openError(shell, COMPARE_FAILED, message);
+ return false;
+ }
+
+ if (input.getCompareResult() == null) {
+ MessageDialog.openInformation(shell, COMPARE_FAILED, "There are no differences between the selected inputs");
+ return false;
+ }
+
+ return true;
+
+ } catch (InterruptedException x) {
+ // cancelled by user
+ } catch (InvocationTargetException x) {
+ MessageDialog.openError(shell, COMPARE_FAILED, x.getTargetException().getMessage());
+ }
+ return false;
+ }
+
+ /**
+ * Registers an image for the given type.
+ */
+ private static void registerImage(String type, Image image, boolean dispose) {
+ fgImages.put(normalizeCase(type), image);
+ if (image != null && dispose) {
+ fgDisposeOnShutdownImages.add(image);
+ }
+ }
+
+ /**
+ * Registers an image descriptor for the given type.
+ *
+ * @param type the type
+ * @param descriptor the image descriptor
+ */
+ public static void registerImageDescriptor(String type, ImageDescriptor descriptor) {
+ fgImageDescriptors.put(normalizeCase(type), descriptor);
+ }
+
+ public static ImageDescriptor getImageDescriptor(String relativePath) {
+
+ URL installURL= null;
+ if (fgComparePlugin != null)
+ installURL= fgComparePlugin.getDescriptor().getInstallURL();
+
+ if (installURL != null) {
+ try {
+ URL url= new URL(installURL, "icons/basic/" + relativePath);
+ return ImageDescriptor.createFromURL(url);
+ } catch (MalformedURLException e) {
+ Assert.isTrue(false);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a shared image for the given type, or a generic image if none
+ * has been registered for the given type.
+ * <p>
+ * Note: Images returned from this method will be automitically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param type the type
+ * @return the image
+ */
+ public static Image getImage(String type) {
+
+ type= normalizeCase(type);
+
+ boolean dispose= false;
+ Image image= null;
+ if (type != null)
+ image= (Image) fgImages.get(type);
+ if (image == null) {
+ ImageDescriptor id= (ImageDescriptor) fgImageDescriptors.get(type);
+ if (id != null) {
+ image= id.createImage();
+ dispose= true;
+ }
+
+ if (image == null) {
+ if (fgComparePlugin != null) {
+ if (ITypedElement.FOLDER_TYPE.equals(type)) {
+ image= getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
+ //image= SharedImages.getImage(ISharedImages.IMG_OBJ_FOLDER);
+ } else {
+ image= createWorkbenchImage(type);
+ dispose= true;
+ }
+ } else {
+ id= (ImageDescriptor) fgImageDescriptors.get(normalizeCase("file"));
+ image= id.createImage();
+ dispose= true;
+ }
+ }
+ if (image != null)
+ registerImage(type, image, dispose);
+ }
+ return image;
+ }
+
+ /**
+ * Returns a shared image for the given adaptable.
+ * This convenience method queries the given adaptable
+ * for its <code>IWorkbenchAdapter.getImageDescriptor</code>, which it
+ * uses to create an image if it does not already have one.
+ * <p>
+ * Note: Images returned from this method will be automitically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param adaptable the adaptable for which to find an image
+ * @return an image
+ */
+ public static Image getImage(IAdaptable adaptable) {
+ if (adaptable != null) {
+ Object o= adaptable.getAdapter(IWorkbenchAdapter.class);
+ if (o instanceof IWorkbenchAdapter) {
+ ImageDescriptor id= ((IWorkbenchAdapter) o).getImageDescriptor(adaptable);
+ if (id != null) {
+ Image image= (Image)fgImages2.get(id);
+ if (image == null) {
+ image= id.createImage();
+ try {
+ fgImages2.put(id, image);
+ } catch (NullPointerException ex) {
+ System.out.println("NPE in CompareUIPlugin.getImage");
+ }
+ fgDisposeOnShutdownImages.add(image);
+
+ }
+ return image;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Image createWorkbenchImage(String type) {
+ IEditorRegistry er= getDefault().getWorkbench().getEditorRegistry();
+ ImageDescriptor id= er.getImageDescriptor("foo." + type);
+ return id.createImage();
+ }
+
+ /**
+ * Registers the given structure creator descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param descriptor the descriptor to register
+ */
+ public static void registerStructureCreator(String types, IStructureCreatorDescriptor descriptor) {
+ if (types != null) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgStructureCreators.put(normalizeCase(extension), descriptor);
+ }
+ }
+ }
+
+ /**
+ * Returns an structure creator descriptor for the given type.
+ *
+ * @param type the type for which to find a descriptor
+ * @return a descriptor for the given type, or <code>null</code> if no
+ * descriptor has been registered
+ */
+ public static IStructureCreatorDescriptor getStructureCreator(String type) {
+ return (IStructureCreatorDescriptor) fgStructureCreators.get(normalizeCase(type));
+ }
+
+ /**
+ * Registers the given structure viewer descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param the descriptor to register
+ */
+ public static void registerStructureViewerDescriptor(String types, IViewerDescriptor descriptor) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgStructureViewerDescriptors.put(normalizeCase(extension), descriptor);
+ }
+ }
+
+ /**
+ * Registers the given content merge viewer descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param descriptor the descriptor to register
+ */
+ public static void registerContentMergeViewerDescriptor(String types, IViewerDescriptor descriptor) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgContentMergeViewerDescriptors.put(normalizeCase(extension), descriptor);
+ }
+ }
+
+ /**
+ * Registers the given content viewer descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param descriptor the descriptor to register
+ */
+ public static void registerContentViewerDescriptor(String types, IViewerDescriptor descriptor) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgContentViewerDescriptors.put(normalizeCase(extension), descriptor);
+ }
+ }
+
+ /**
+ * Returns a structure compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input, the old viewer
+ * is returned. Otherwise, the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a structure compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a structure viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent,
+ CompareConfiguration configuration) {
+
+ if (input.getLeft() == null || input.getRight() == null) // we don't show the structure of additions or deletions
+ return null;
+
+ String type= getType(input);
+ if (type == null)
+ return null;
+
+ type= normalizeCase(type);
+
+ IViewerDescriptor vd= (IViewerDescriptor) fgStructureViewerDescriptors.get(type);
+ if (vd != null)
+ return vd.createViewer(oldViewer, parent, configuration);
+
+ IStructureCreatorDescriptor scc= getStructureCreator(type);
+ if (scc != null) {
+ IStructureCreator sc= scc.createStructureCreator();
+ if (sc != null) {
+ StructureDiffViewer sdv= new StructureDiffViewer(parent, configuration);
+ sdv.setStructureCreator(sc);
+ return sdv;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a content compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input the old viewer
+ * is returned. Otherwise the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a content compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a content viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findContentViewer(Viewer oldViewer, Object in, Composite parent, CompareConfiguration cc) {
+
+ if (! (in instanceof ICompareInput)) {
+ String type= ITypedElement.TEXT_TYPE;
+ if (in instanceof ITypedElement) {
+ ITypedElement tin= (ITypedElement) in;
+ type= tin.getType();
+ }
+ type= normalizeCase(type);
+
+ IViewerDescriptor vd= (IViewerDescriptor) fgContentViewerDescriptors.get(type);
+ Viewer viewer= null;
+ if (vd != null) {
+ viewer= vd.createViewer(oldViewer, parent, cc);
+ if (viewer != null)
+ return viewer;
+ }
+ // fallback
+ return new TextViewer(parent);
+ }
+
+ ICompareInput input= (ICompareInput) in;
+ String type= getType(input);
+ type= normalizeCase(type);
+
+ if (ITypedElement.FOLDER_TYPE.equals(type))
+ return null;
+
+ if (type != null) {
+ IViewerDescriptor vd= (IViewerDescriptor) fgContentMergeViewerDescriptors.get(type);
+ Viewer viewer= null;
+ if (vd != null) {
+ viewer= vd.createViewer(oldViewer, parent, cc);
+ if (viewer != null)
+ return viewer;
+ }
+ }
+
+ // fallback
+ String leftType= guessType(input.getLeft());
+ String rightType= guessType(input.getRight());
+
+ if (leftType != null && rightType != null) {
+ if (ITypedElement.TEXT_TYPE.equals(leftType) && ITypedElement.TEXT_TYPE.equals(rightType))
+ type= ITypedElement.TEXT_TYPE;
+ else
+ type= "binary";
+
+ IViewerDescriptor vd= (IViewerDescriptor) fgContentMergeViewerDescriptors.get(normalizeCase(type));
+ if (vd != null)
+ return vd.createViewer(oldViewer, parent, cc);
+ }
+ return null;
+ }
+
+ /**
+ * Determines the type of the given threeway input by analyzing
+ * the types (file extension) of the individual parts.
+ * Returns null if no type can be determined.
+ */
+ private static String getType(ICompareInput input) {
+ ITypedElement ancestor= input.getAncestor();
+ ITypedElement left= input.getLeft();
+ ITypedElement right= input.getRight();
+
+ String[] types= new String[3];
+ int cnt= 0;
+
+ if (ancestor != null) {
+ String type= ancestor.getType();
+ if (type != null)
+ types[cnt++]= type;
+ }
+ if (left != null) {
+ String type= left.getType();
+ if (type != null)
+ types[cnt++]= type;
+ }
+ if (right != null) {
+ String type= right.getType();
+ if (type != null)
+ types[cnt++]= type;
+ }
+ boolean homogenous= false;
+ switch (cnt) {
+ case 1:
+ homogenous= true;
+ break;
+ case 2:
+ homogenous= types[0].equals(types[1]);
+ break;
+ case 3:
+ homogenous= types[0].equals(types[1]) && types[1].equals(types[2]);
+ break;
+ }
+ if (homogenous)
+ return types[0];
+ return null;
+ }
+
+ /**
+ * Guesses the file type of the given input.
+ * Returns ITypedElement.TEXT_TYPE if the first 1000 bytes contain only values in the range 0-127.
+ * Returns ITypedElement.UNKNOWN_TYPE if a single byte is out of this range.
+ * Returns <code>null</code> if the input isn't an <code>IStreamContentAccessor</code>.
+ */
+ private static String guessType(ITypedElement input) {
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ try {
+ InputStream is= sca.getContents();
+ if (is == null)
+ return null;
+ for (int i= 0; i < 1000; i++)
+ if (is.read() >= 128)
+ return ITypedElement.UNKNOWN_TYPE;
+ return ITypedElement.TEXT_TYPE;
+ } catch (CoreException ex) {
+ // be silent and return UNKNOWN_TYPE
+ } catch (IOException ex) {
+ // be silent and return UNKNOWN_TYPE
+ }
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+ return null;
+ }
+
+ private static IViewerDescriptor getContentViewerDescriptor2(String type) {
+ return (IViewerDescriptor) fgContentMergeViewerDescriptors.get(normalizeCase(type));
+ }
+
+ private static String normalizeCase(String s) {
+ if (NORMALIZE_CASE && s != null)
+ return s.toUpperCase();
+ return s;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java
new file mode 100644
index 000000000..62d9d55bf
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.*;
+
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.util.ListenerList;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+import org.eclipse.compare.structuremergeviewer.DiffTreeViewer;
+
+
+/**
+ * A custom <code>Pane</code> which supports viewer switching.
+ * <p>
+ * If a property with the name CompareUI.COMPARE_VIEWER_TITLE is set
+ * on the top level SWT control of a viewer, it is used as a title in the pane's
+ * title bar.
+ */
+public abstract class CompareViewerSwitchingPane extends Pane
+ implements ISelectionChangedListener, ISelectionProvider, IDoubleClickListener {
+
+ /**
+ * Used whenever the input is null or no viewer can be found.
+ */
+ private static class NullViewer extends AbstractViewer {
+
+ private Control fDummy;
+
+ public NullViewer(Composite parent) {
+
+ fDummy= new Tree(parent, SWT.NULL);
+
+ CompareViewerSwitchingPane.clearToolBar(parent);
+ }
+
+ public Control getControl() {
+ return fDummy;
+ }
+ }
+
+ private Viewer fViewer;
+ private Object fInput;
+ private ListenerList fSelectionListeners= new ListenerList();
+ private ListenerList fOpenListeners= new ListenerList();
+ private boolean fControlVisibility= false;
+
+ /**
+ * Creates a ViewerPane as a child of the given parent and with the
+ * specified SWT style bits.
+ */
+ public CompareViewerSwitchingPane(Composite parent, int style) {
+ this(parent, style, false);
+ }
+
+ /**
+ * Creates a ViewerPane as a child of the given parent and with the
+ * specified SWT style bits.
+ */
+ public CompareViewerSwitchingPane(Composite parent, int style, boolean visibility) {
+ super(parent, style);
+
+ fControlVisibility= visibility;
+
+ setViewer(new NullViewer(this));
+
+ addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).removeSelectionChangedListener(CompareViewerSwitchingPane.this);
+ if (fViewer instanceof StructuredViewer)
+ ((StructuredViewer) fViewer).removeDoubleClickListener(CompareViewerSwitchingPane.this);
+ fViewer= null;
+ fInput= null;
+ fSelectionListeners= null;
+ }
+ }
+ );
+ }
+
+ /**
+ * Returns the current viewer.
+ */
+ public Viewer getViewer() {
+ return fViewer;
+ }
+
+ /**
+ * Sets the current viewer.
+ */
+ private void setViewer(Viewer newViewer) {
+
+ if (newViewer == fViewer)
+ return;
+
+ boolean oldEmpty= isEmpty();
+
+ if (fViewer != null) {
+
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).removeSelectionChangedListener(this);
+
+ if (fViewer instanceof StructuredViewer)
+ ((StructuredViewer)fViewer).removeDoubleClickListener(this);
+
+ Control content= getContent();
+ setContent(null);
+
+ fViewer.setInput(null);
+
+ if (content != null && !content.isDisposed()) {
+ content.dispose();
+ }
+ } else
+ oldEmpty= false;
+ setContent(null);
+
+ fViewer= newViewer;
+
+ if (fViewer != null) {
+ // workaround: setContent changes the visibility of the CustomPane
+ boolean old= getVisible();
+ setContent(fViewer.getControl());
+ setVisible(old);
+ // end of workaround
+
+ boolean newEmpty= isEmpty();
+
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).addSelectionChangedListener(this);
+ if (fViewer instanceof StructuredViewer)
+ ((StructuredViewer)fViewer).addDoubleClickListener(this);
+
+ if (oldEmpty != newEmpty) {// relayout my container
+ Composite parent= getParent();
+ if (parent instanceof Splitter)
+ ((Splitter)parent).setVisible(this, fControlVisibility ? !newEmpty : true);
+ //else
+ // parent.layout(true);
+ }
+
+ //else if (!newEmpty)// otherwise just relayout myself
+ layout(true);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if no viewer is installed or if the current viewer
+ * is a <code>NullViewer</code>.
+ */
+ public boolean isEmpty() {
+ return fViewer == null || fViewer instanceof NullViewer;
+ }
+
+ public void addSelectionChangedListener(ISelectionChangedListener l) {
+ fSelectionListeners.add(l);
+ }
+
+ public void removeSelectionChangedListener(ISelectionChangedListener l) {
+ fSelectionListeners.remove(l);
+ }
+
+ public void addDoubleClickListener(IDoubleClickListener l) {
+ fOpenListeners.add(l);
+ }
+
+ public void removeDoubleClickListener(IDoubleClickListener l) {
+ fOpenListeners.remove(l);
+ }
+
+ public void doubleClick(DoubleClickEvent event) {
+ Object[] listeners= fOpenListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((IDoubleClickListener) listeners[i]).doubleClick(event);
+ }
+
+ public ISelection getSelection() {
+ if (fViewer instanceof ISelectionProvider)
+ return ((ISelectionProvider) fViewer).getSelection();
+ return null;
+ }
+
+ public void setSelection(ISelection s) {
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).setSelection(s);
+ }
+
+ public void selectionChanged(SelectionChangedEvent ev) {
+ Object[] listeners= fSelectionListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((ISelectionChangedListener) listeners[i]).selectionChanged(ev);
+ }
+
+ public void setInput(Object input) {
+
+ if (fInput == input)
+ return;
+
+ fInput= input;
+
+ // viewer switching
+ Viewer newViewer= null;
+ if (input != null)
+ newViewer= getViewer(fViewer, input);
+
+ if (newViewer == null) {
+ if (fViewer instanceof NullViewer)
+ return;
+ newViewer= new NullViewer(this);
+ }
+
+ setViewer(newViewer);
+
+ // set input
+ fViewer.setInput(input);
+
+ Image image= null;
+ if (!(fViewer instanceof NullViewer) && input instanceof ICompareInput)
+ image= ((ICompareInput)input).getImage();
+ setImage(image);
+
+ String title= null;
+ if (fViewer != null) {
+ Control c= fViewer.getControl();
+ if (c != null) {
+ Object data= c.getData(CompareUI.COMPARE_VIEWER_TITLE);
+ if (data instanceof String)
+ title= (String) data;
+ }
+ }
+
+ setText(title != null ? title : "");
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ abstract protected Viewer getViewer(Viewer oldViewer, Object input);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java
new file mode 100644
index 000000000..dce0ae6b9
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.graphics.*;
+
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * Combines an image with an overlay.
+ */
+public class DiffImage extends CompositeImageDescriptor {
+
+ static final int HEIGHT= 16;
+
+ private Image fBaseImage;
+ private ImageDescriptor fOverlayImage;
+ private int fWidth;
+
+ public DiffImage(Image base, ImageDescriptor overlay, int w) {
+ fBaseImage= base;
+ fOverlayImage= overlay;
+ fWidth= w;
+ }
+
+ protected Point getSize() {
+ return new Point(fWidth, HEIGHT);
+ }
+
+ protected void drawCompositeImage(int width, int height) {
+
+ if (fBaseImage != null) {
+ ImageData base= fBaseImage.getImageData();
+ if (base == null)
+ base= DEFAULT_IMAGE_DATA;
+ try {
+ drawImage(base, fWidth - base.width, 0);
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // workaround for PR 1GCQKWP
+ }
+ }
+
+ if (fOverlayImage != null) {
+ ImageData dir= fOverlayImage.getImageData();
+ if (dir == null)
+ dir= DEFAULT_IMAGE_DATA;
+ drawImage(dir, 0, (HEIGHT - dir.height) / 2);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java
new file mode 100644
index 000000000..81591752b
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.jface.text.*;
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+import org.eclipse.compare.contentmergeviewer.ITokenComparator;
+
+/**
+ * Implements the <code>ITokenComparator</code> interface for lines in a document.
+ * A <code>DocLineComparator</code> is used as the input for the <code>RangeDifferencer</code>
+ * engine to perform a line oriented compare on documents.
+ * <p>
+ * A <code>DocLineComparator</code> doesn't know anything about line separators because
+ * its notion of lines is solely defined in the underlying <code>IDocument</code>.
+ */
+public class DocLineComparator implements IRangeComparator {
+
+ private IDocument fDocument;
+ private int fLineOffset;
+ private int fLineCount;
+ private int fLength;
+ private boolean fIgnoreWhiteSpace;
+
+ /**
+ * Creates a <code>DocLineComparator</code> for the given document range.
+ * ignoreWhiteSpace controls whether comparing lines (in method
+ * <code>rangesEqual<code>) should ignore whitespace.
+ *
+ * @param document the document from which the lines are taken
+ * @param region if non-<code>null</code> only lines within this range are taken
+ * @param ignoreWhiteSpace if <code>true</code> white space is ignored when comparing lines
+ */
+ public DocLineComparator(IDocument document, IRegion region, boolean ignoreWhiteSpace) {
+
+ fDocument= document;
+ fIgnoreWhiteSpace= ignoreWhiteSpace;
+
+ fLineOffset= 0;
+ if (region != null) {
+ fLength= region.getLength();
+ int start= region.getOffset();
+ try {
+ fLineOffset= fDocument.getLineOfOffset(start);
+ } catch (BadLocationException ex) {
+ }
+
+ if (fLength == 0)
+ fLineCount= 0;
+ else {
+ int endLine= fDocument.getNumberOfLines();
+ try {
+ endLine= fDocument.getLineOfOffset(start + fLength);
+ } catch (BadLocationException ex) {
+ }
+ fLineCount= endLine - fLineOffset + 1;
+ }
+
+ } else {
+ fLength= document.getLength();
+ fLineCount= fDocument.getNumberOfLines();
+ }
+ }
+
+ /**
+ * Returns the content of lines in the specified range as a String.
+ * This includes the line separators.
+ *
+ * @param start index of first line
+ * @param length number of lines
+ * @return the contents of the specified line range as a String
+ */
+ public String extract(int start, int length) {
+ if (fLength > 0) {
+ if (fLength == 1)
+ return extract(start);
+ int startPos= getTokenStart(start);
+ int endPos= getTokenStart(start + length);
+ try {
+ return fDocument.get(startPos, endPos - startPos);
+ } catch (BadLocationException e) {
+ System.out.println("extract("+fDocument.getLength()+"): " + startPos + " " + endPos);
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Extract a single line from the underlying document without the line separator.
+ *
+ * @param line the number of the line to extract
+ * @return the contents of the line as a String
+ */
+ public String extract(int line) {
+ line += fLineOffset;
+ if (line < fLength) {
+ try {
+ IRegion r= fDocument.getLineInformation(line);
+ return fDocument.get(r.getOffset(), r.getLength());
+ // return fDocument.getLine(line);
+ } catch(BadLocationException e) {
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Returns the number of lines in the document.
+ *
+ * @return number of lines
+ */
+ public int getRangeCount() {
+ return fLineCount;
+ }
+
+ /**
+ * Returns <code>true</code> if a line given by the first index
+ * matches a line specified by the other <code>IRangeComparator</code> and index.
+ *
+ * @param thisIndex the number of the line within this range comparator
+ * @param other the range comparator to compare this with
+ * @param otherIndex the number of the line within the other comparator
+ * @return <code>true</code> if the lines are equal
+ */
+ public boolean rangesEqual(int thisIndex, IRangeComparator other0, int otherIndex) {
+
+ if (other0 != null && other0.getClass() == getClass()) {
+ DocLineComparator other= (DocLineComparator) other0;
+
+ if (fIgnoreWhiteSpace) {
+ String s1= extract(thisIndex);
+ String s2= other.extract(otherIndex);
+ return compare(s1, s2);
+ }
+
+ int tlen= getTokenLength(thisIndex);
+ int olen= other.getTokenLength(otherIndex);
+ if (tlen == olen) {
+ String s1= extract(thisIndex);
+ String s2= other.extract(otherIndex);
+ return s1.equals(s2);
+ }
+ }
+ return false;
+ }
+
+ /* (non Javadoc)
+ * see IRangeComparator.skipRangeComparison
+ */
+ public boolean skipRangeComparison(int length, int max, IRangeComparator other) {
+ return false;
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenStart
+ */
+ public int getTokenStart(int line) {
+ try {
+ IRegion r= fDocument.getLineInformation(fLineOffset + line);
+ return r.getOffset();
+ //return fDocument.getLineStartOffset(fLineOffset + line);
+ } catch (BadLocationException ex) {
+ return fDocument.getLength();
+ }
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenEnd
+ */
+ public int getTokenEnd(int start, int length) {
+ return getTokenStart(start + length);
+ }
+
+ //---- private methods
+
+ private int getTokenLength(int line) {
+ if (fLength == 0)
+ return 0;
+ return getTokenStart(line + 1) - getTokenStart(line);
+ }
+
+ private boolean compare(String s1, String s2) {
+ int i1= 0;
+ int i2= 0;
+ int l1= s1.length();
+ int l2= s2.length();
+ char c1= ' ';
+ char c2= ' ';
+ while (i1 < l1 || i2 < l2) {
+ if (i1 < l1) {
+ c1= s1.charAt(i1);
+ if (Character.isWhitespace(c1)) {
+ i1++;
+ continue;
+ }
+ }
+ if (i2 < l2) {
+ c2= s2.charAt(i2);
+ if (Character.isWhitespace(c2)) {
+ i2++;
+ continue;
+ }
+ }
+ if (c1 != c2)
+ return false;
+ i1++;
+ i2++;
+ }
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java
new file mode 100644
index 000000000..cd866e6c1
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.compare.structuremergeviewer.IStructureCreator;
+
+/**
+ * A factory object for creating <code>IStructureCreator</code>s from a descriptor.
+ * <p>
+ * It is used when registering <code>IStructureCreator</code> for types
+ * in <code>CompareUIPlugin.registerStructureCreator</code>.
+ * </p>
+ *
+ * @see IStructureCreator
+ * @see CompareUIPlugin
+ */
+public interface IStructureCreatorDescriptor {
+
+ /**
+ * Creates a new structure creator.
+ *
+ * @return a newly created structure creator
+ */
+ IStructureCreator createStructureCreator();
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java
new file mode 100644
index 000000000..f22578149
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * A factory object for creating a <code>Viewer</code>s from a descriptor.
+ * <p>
+ * It is used when registering a viewer for a specific type
+ * in <code>CompareUIPlugin.registerContentViewerDescriptor</code> and
+ * in <code>CompareUIPlugin.registerStructureViewerDescriptor</code>.
+ *
+ * @see org.eclipse.compare.structuremergeviewer.IStructureCreator
+ * @see CompareUIPlugin
+ */
+public interface IViewerDescriptor {
+
+ /**
+ * Creates a new viewer from this descriptor under the given STW parent control.
+ * If the current viewer has the same type as a new viewer
+ * the implementation of this method is free to return the current viewer instead.
+ *
+ * @param currentViewer the current viewer which is going to be replaced with a new viewer.
+ * @param parent the SWT parent control under which the new viewer has to be created.
+ * @param config a compare configuration the new viewer might be interested in.
+ * @return a new viewer or the current viewer.
+ */
+ Viewer createViewer(Viewer currentViewer, Composite parent, CompareConfiguration config);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java
new file mode 100644
index 000000000..64aed3b09
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.*;
+
+/**
+ * Toggles the <code>ICompareConfiguration.IGNORE_WS</code> property of an
+ * <code>ICompareConfiguration</code>.
+ */
+public class IgnoreWhiteSpaceAction extends ChangePropertyAction {
+
+ public IgnoreWhiteSpaceAction(ResourceBundle bundle, CompareConfiguration cc) {
+ super(bundle, cc, "action.IgnoreWhiteSpace.", CompareConfiguration.IGNORE_WHITESPACE);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java
new file mode 100644
index 000000000..2d56fc406
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A <code>Canvas</code> showing a single centered SWT <code>Image</code>.
+ * If the <code>Image</code> is larger than the <code>Canvas<code>,
+ * <code>Scrollbars</code> will appear.
+ */
+class ImageCanvas extends Canvas {
+
+ private Image fImage;
+
+ /**
+ * Create a new ImageCanvas with the given SWT stylebits.
+ * (SWT.H_SCROLL and SWT.V_SCROLL are automtically added).
+ */
+ public ImageCanvas(Composite parent, int style) {
+ super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL);
+
+ ScrollBar sb= getHorizontalBar();
+ sb.setIncrement(20);
+ sb.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ repaint();
+ }
+ });
+
+ sb= getVerticalBar();
+ sb.setIncrement(20);
+ sb.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ repaint();
+ }
+ });
+
+ addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event e) {
+ updateScrollbars();
+ }
+ });
+
+ addListener(SWT.Paint, new Listener() {
+ public void handleEvent(Event event) {
+ paint(event.gc);
+ }
+ });
+ }
+
+ /**
+ * Set the SWT Image to use as the ImageCanvas contents.
+ */
+ public void setImage(Image img) {
+ fImage= img;
+
+ if (!isDisposed()) {
+ getHorizontalBar().setSelection(0);
+ getVerticalBar().setSelection(0);
+ updateScrollbars();
+ getParent().layout();
+ redraw();
+ }
+ }
+
+ public void repaint() {
+ if (!isDisposed()) {
+ GC gc= new GC(this);
+ paint(gc);
+ gc.dispose();
+ }
+ }
+
+ /**
+ * @private
+ */
+ private void paint(GC gc) {
+ if (fImage != null) {
+ Rectangle bounds= fImage.getBounds();
+ Rectangle clientArea= getClientArea();
+
+ int x;
+ if (bounds.width < clientArea.width)
+ x= (clientArea.width - bounds.width) / 2;
+ else
+ x= -getHorizontalBar().getSelection();
+
+ int y;
+ if (bounds.height < clientArea.height)
+ y= (clientArea.height - bounds.height) / 2;
+ else
+ y= -getVerticalBar().getSelection();
+
+ gc.drawImage(fImage, x, y);
+ }
+ }
+
+ /**
+ * @private
+ */
+ private void updateScrollbars() {
+ Rectangle bounds= fImage != null ? fImage.getBounds() : new Rectangle(0, 0, 0, 0);
+ Point size= getSize();
+ Rectangle clientArea= getClientArea();
+
+ ScrollBar horizontal= getHorizontalBar();
+ if (bounds.width <= clientArea.width) {
+ horizontal.setVisible(false);
+ horizontal.setSelection(0);
+ } else {
+ horizontal.setPageIncrement(clientArea.width - horizontal.getIncrement());
+ int max= bounds.width + (size.x - clientArea.width);
+ horizontal.setMaximum(max);
+ horizontal.setThumb(size.x > max ? max : size.x);
+ horizontal.setVisible(true);
+ }
+
+ ScrollBar vertical= getVerticalBar();
+ if (bounds.height <= clientArea.height) {
+ vertical.setVisible(false);
+ vertical.setSelection(0);
+ } else {
+ vertical.setPageIncrement(clientArea.height - vertical.getIncrement());
+ int max= bounds.height + (size.y - clientArea.height);
+ vertical.setMaximum(max);
+ vertical.setThumb(size.y > max ? max : size.y);
+ vertical.setVisible(true);
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java
new file mode 100644
index 000000000..0114a22c4
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+
+/**
+ */
+public class ImageMergeViewer extends ContentMergeViewer {
+
+ private static final String BUNDLE_NAME= "org.eclipse.compare.internal.ImageMergeViewerResources";
+
+ private Object fAncestorImage;
+ private Object fLeftImage;
+ private Object fRightImage;
+
+ private ImageCanvas fAncestor;
+ private ImageCanvas fLeft;
+ private ImageCanvas fRight;
+
+
+ public ImageMergeViewer(Composite parent, int styles, CompareConfiguration mp) {
+ super(styles, ResourceBundle.getBundle(BUNDLE_NAME), mp);
+ buildControl(parent);
+ getControl().setData(CompareUI.COMPARE_VIEWER_TITLE, "Image Compare");
+ }
+
+ protected void updateContent(Object ancestor, Object left, Object right) {
+
+ fAncestorImage= ancestor;
+ setInput(fAncestor, ancestor);
+
+ fLeftImage= left;
+ setInput(fLeft, left);
+
+ fRightImage= right;
+ setInput(fRight, right);
+ }
+
+ /**
+ * We can't modify the contents of either side we just return null.
+ */
+ protected byte[] getContents(boolean left) {
+ return null;
+ }
+
+ public void createControls(Composite composite) {
+ fAncestor= new ImageCanvas(composite, SWT.NONE);
+ fLeft= new ImageCanvas(composite, SWT.NONE);
+ fRight= new ImageCanvas(composite, SWT.NONE);
+ }
+
+ private static void setInput(ImageCanvas canvas, Object input) {
+ if (canvas != null) {
+
+ InputStream stream= null;
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ if (sca != null) {
+ try {
+ stream= sca.getContents();
+ } catch (CoreException ex) {
+ }
+ }
+ }
+
+ Image image= null;
+ Display display= canvas.getDisplay();
+ if (stream != null) {
+ try {
+ image= new Image(display, stream);
+ } catch (SWTException ex) {
+ }
+ }
+
+ canvas.setImage(image);
+ if (image != null) {
+ canvas.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ } else {
+ canvas.setBackground(null);
+ }
+ }
+ }
+
+ protected void handleResizeAncestor(int x, int y, int width, int height) {
+ if (width > 0) {
+ fAncestor.setVisible(true);
+ fAncestor.setBounds(x, y, width, height);
+ } else {
+ fAncestor.setVisible(false);
+ }
+ }
+
+ protected void handleResizeLeftRight(int x, int y, int width1, int centerWidth, int width2, int height) {
+ fLeft.setBounds(x, y, width1, height);
+ fRight.setBounds(x+width1+centerWidth, y, width2, height);
+ }
+
+ protected void copy(boolean leftToRight) {
+ if (leftToRight) {
+ fRightImage= fLeftImage;
+ setInput(fRight, fRightImage);
+ setRightDirty(true);
+ } else {
+ fLeftImage= fRightImage;
+ setInput(fLeft, fLeftImage);
+ setLeftDirty(true);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java
new file mode 100644
index 000000000..e57c9c99d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.compare.*;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A factory object for the <code>ImageMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class ImageMergeViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new ImageMergeViewer(parent, SWT.NULL, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties
new file mode 100644
index 000000000..dcc529805
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties
@@ -0,0 +1,55 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 1999-2001
+# =====================================
+
+# @(#)ImageMergeViewerResources.properties
+#
+# Resource strings for ImageMergeViewer.java
+
+#####################################################
+# Images
+#####################################################
+
+#####################################################
+# Status messages
+#####################################################
+
+#####################################################
+# Dialogs
+#####################################################
+
+CantSaveProperty.title=Error while saving contents
+CantSaveProperty.message=Couldn''t save contents.\nReason: {0}
+
+ContentInaccessible.title=Error while accessing content
+ContentInaccessible.message=Content currently inaccessible.
+
+#####################################################
+# Actions
+#####################################################
+
+action.CopyLeftToRight.label=Copy Left to Right
+action.CopyLeftToRight.tooltip=Copy image from left to right
+action.CopyLeftToRight.image=ctool16/lefttoright.gif
+
+action.CopyRightToLeft.label=Copy Right to Left
+action.CopyRightToLeft.tooltip=Copy image from right to left
+action.CopyRightToLeft.image=ctool16/righttoleft.gif
+
+#action.ToggleMergeDirection.label=Toggle Merge Direction
+#action.ToggleMergeDirection.tooltip=Toggle merge direction
+#action.ToggleMergeDirection.image=ctool16/toggle.gif
+
+action.EnableAncestor.label=Enable Ancestor Pane
+action.EnableAncestor.tooltip=Controls visibility of ancestor pane
+action.EnableAncestor.image=ctool16/panes3.gif
+
+# Pop up menu
+
+#action.Save.label=Save@Ctrl+S
+#action.Save.tooltip=Save Changes
+
+
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java
new file mode 100644
index 000000000..ce4780b9f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.Color;
+
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+
+/**
+ * Extends the SEF SourceViewer with some convenience methods.
+ */
+public class MergeSourceViewer extends SourceViewer {
+
+ private IRegion fRegion;
+ private boolean fEnabled= true;
+
+
+ public MergeSourceViewer(Composite parent) {
+ super(parent, null, SWT.H_SCROLL + SWT.V_SCROLL);
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (enabled != fEnabled) {
+ fEnabled= enabled;
+ StyledText c= getTextWidget();
+ if (c != null) {
+ c.setEnabled(enabled);
+ Display d= c.getDisplay();
+ c.setBackground(enabled ? d.getSystemColor(SWT.COLOR_LIST_BACKGROUND) : null);
+ }
+ }
+ }
+
+ public boolean getEnabled() {
+ return fEnabled;
+ }
+
+ public void setRegion(IRegion region) {
+ fRegion= region;
+ }
+
+ public IRegion getRegion() {
+ return fRegion;
+ }
+
+ public boolean isControlOkToUse() {
+ StyledText t= getTextWidget();
+ return t != null && !t.isDisposed();
+ }
+
+ public void setSelection(Position p) {
+ setSelectedRange(p.getOffset(), p.getLength());
+ }
+
+ public void setLineBackground(Position p, Color c) {
+ StyledText t= getTextWidget();
+ if (t != null && !t.isDisposed()) {
+ Point region= new Point(0, 0);
+ getLineRange(p, region);
+
+ region.x-= getDocumentRegionOffset();
+
+ t.setLineBackground(region.x, region.y, c);
+ }
+ }
+
+ public void resetLineBackground() {
+ StyledText t= getTextWidget();
+ if (t != null && !t.isDisposed()) {
+ int lines= getLineCount();
+ t.setLineBackground(0, lines, null);
+ }
+ }
+
+ /**
+ * Returns number of lines in document region.
+ */
+ public int getLineCount() {
+ IRegion region= getVisibleRegion();
+
+ int length= region.getLength();
+ if (length == 0)
+ return 0;
+
+ IDocument doc= getDocument();
+ int startLine= 0;
+ int endLine= 0;
+
+ int start= region.getOffset();
+ try {
+ startLine= doc.getLineOfOffset(start);
+ } catch(BadLocationException ex) {
+ }
+ try {
+ endLine= doc.getLineOfOffset(start+length);
+ } catch(BadLocationException ex) {
+ }
+
+ return endLine-startLine+1;
+ }
+
+ public int getViewportLines() {
+ StyledText te= getTextWidget();
+ Rectangle clArea= te.getClientArea();
+ if (!clArea.isEmpty())
+ return clArea.height / te.getLineHeight();
+ return 0;
+ }
+
+ public int getViewportHeight() {
+ StyledText te= getTextWidget();
+ Rectangle clArea= te.getClientArea();
+ if (!clArea.isEmpty())
+ return clArea.height;
+ return 0;
+ }
+
+ /**
+ * Returns lines
+ */
+ public int getDocumentRegionOffset() {
+ int start= getVisibleRegion().getOffset();
+ IDocument doc= getDocument();
+ if (doc != null) {
+ try {
+ return doc.getLineOfOffset(start);
+ } catch(BadLocationException ex) {
+ }
+ }
+ return 0;
+ }
+
+ public int getVerticalScrollOffset() {
+ StyledText st= getTextWidget();
+ int lineHeight= st.getLineHeight();
+ return getTopInset() - ((getDocumentRegionOffset()*lineHeight) + st.getTopPixel());
+ }
+
+ /**
+ * Returns the start line and the number of lines which correspond to the given position.
+ * Starting line number is 0 based.
+ */
+ public Point getLineRange(Position p, Point region) {
+
+ if (p == null) {
+ region.x= 0;
+ region.y= 0;
+ return region;
+ }
+
+ IDocument doc= getDocument();
+
+ int start= p.getOffset();
+ int length= p.getLength();
+
+ int startLine= 0;
+ try {
+ startLine= doc.getLineOfOffset(start);
+ } catch (BadLocationException e) {
+ }
+
+ int lineCount= 0;
+
+ if (length == 0) {
+// // if range length is 0 and if range starts a new line
+// try {
+// if (start == doc.getLineStartOffset(startLine)) {
+// lines--;
+// }
+// } catch (BadLocationException e) {
+// lines--;
+// }
+
+ } else {
+ int endLine= 0;
+ try {
+ endLine= doc.getLineOfOffset(start + length - 1); // why -1?
+ } catch (BadLocationException e) {
+ }
+ lineCount= endLine-startLine+1;
+ }
+
+ region.x= startLine;
+ region.y= lineCount;
+ return region;
+ }
+
+ /**
+ * Scroll TextPart to the given line.
+ */
+ public void vscroll(int line) {
+
+ int srcViewSize= getLineCount();
+ int srcExtentSize= getViewportLines();
+
+ if (srcViewSize > srcExtentSize) {
+ //if (pos + srcExtentSize > srcViewSize)
+ // pos= srcViewSize-srcExtentSize;
+
+ if (line < 0)
+ line= 0;
+
+ int cp= getTopIndex();
+ if (cp != line)
+ setTopIndex(line + getDocumentRegionOffset());
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java
new file mode 100644
index 000000000..29994d804
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.compare.contentmergeviewer.IMergeViewerContentProvider;
+
+/**
+ * Adapts any <code>ContentMergeViewer</code> to work on an <code>ICompareInput</code>
+ * e.g. a <code>DiffNode</code>.
+ */
+public class MergeViewerContentProvider implements IMergeViewerContentProvider {
+
+ private CompareConfiguration fCompareConfiguration;
+
+ public MergeViewerContentProvider(CompareConfiguration cc) {
+ fCompareConfiguration= cc;
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer v, Object o1, Object o2) {
+ }
+
+ //---- ancestor
+
+ public String getAncestorLabel(Object element) {
+ return fCompareConfiguration.getAncestorLabel(element);
+ }
+
+ public Image getAncestorImage(Object element) {
+ return fCompareConfiguration.getAncestorImage(element);
+ }
+
+ public Object getAncestorContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getAncestor();
+ return null;
+ }
+
+ public boolean showAncestor(Object element) {
+ if (element instanceof ICompareInput)
+ return (((ICompareInput)element).getKind() & Differencer.DIRECTION_MASK) == Differencer.CONFLICTING;
+ return false;
+ }
+
+ //---- left
+
+ public String getLeftLabel(Object element) {
+ return fCompareConfiguration.getLeftLabel(element);
+ }
+
+ public Image getLeftImage(Object element) {
+ return fCompareConfiguration.getLeftImage(element);
+ }
+
+ public Object getLeftContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getLeft();
+ return null;
+ }
+
+ public boolean isLeftEditable(Object element) {
+ if (element instanceof ICompareInput) {
+ Object left= ((ICompareInput) element).getLeft();
+ if (left == null) {
+ IDiffElement parent= ((IDiffElement)element).getParent();
+ if (parent instanceof ICompareInput)
+ left= ((ICompareInput) parent).getLeft();
+ }
+ if (left instanceof IEditableContent)
+ return ((IEditableContent)left).isEditable();
+ }
+ return false;
+ }
+
+ public void saveLeftContent(Object element, byte[] bytes) {
+ if (element instanceof ICompareInput) {
+ ICompareInput node= (ICompareInput) element;
+ if (bytes != null) {
+ ITypedElement left= node.getLeft();
+ if (left instanceof IEditableContent)
+ ((IEditableContent)left).setContent(bytes);
+ } else {
+ node.copy(false);
+ }
+ }
+ }
+
+ //---- right
+
+ public String getRightLabel(Object element) {
+ return fCompareConfiguration.getRightLabel(element);
+ }
+
+ public Image getRightImage(Object element) {
+ return fCompareConfiguration.getRightImage(element);
+ }
+
+ public Object getRightContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getRight();
+ return null;
+ }
+
+ public boolean isRightEditable(Object element) {
+ if (element instanceof ICompareInput) {
+ Object right= ((ICompareInput) element).getRight();
+ if (right == null) {
+ IDiffContainer parent= ((IDiffElement)element).getParent();
+ if (parent instanceof ICompareInput)
+ right= ((ICompareInput) parent).getRight();
+ }
+ if (right instanceof IEditableContent)
+ return ((IEditableContent)right).isEditable();
+ }
+ return false;
+ }
+
+ public void saveRightContent(Object element, byte[] bytes) {
+ if (element instanceof ICompareInput) {
+ ICompareInput node= (ICompareInput) element;
+ if (bytes != null) {
+ ITypedElement right= node.getRight();
+ if (right instanceof IEditableContent)
+ ((IEditableContent)right).setContent(bytes);
+ } else {
+ node.copy(true);
+ }
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java
new file mode 100644
index 000000000..be0612dca
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.ViewForm;
+
+import org.eclipse.jface.action.ToolBarManager;
+
+/**
+ * A Pane is a convenience class which installs a CustomLabel and a Toolbar (on demand).
+ * Double clicking onto the Pane's title bar maximizes the Pane
+ * to the size of an enclosing Splitter (if there is one).
+ * If more Splitters are nested maximizing walks up and maximizes to the outermost Splitter.
+ */
+public class Pane extends ViewForm {
+
+ private ToolBarManager fToolBarManager;
+
+ public Pane(Composite parent, int style) {
+ super(parent, SWT.BORDER);
+
+ marginWidth= 0;
+ marginHeight= 0;
+
+ CLabel label= new CLabel(this, SWT.NONE);
+ setTopLeft(label);
+
+ MouseAdapter ml= new MouseAdapter() {
+ public void mouseDoubleClick(MouseEvent e) {
+ Control parent= getParent();
+ if (parent instanceof Splitter)
+ ((Splitter)parent).setMaximizedControl(Pane.this);
+ }
+ };
+
+ addMouseListener(ml);
+ label.addMouseListener(ml);
+ }
+
+ public void setText(String label) {
+ CLabel cl= (CLabel) getTopLeft();
+ cl.setText(label);
+ }
+
+ public void setImage(Image image) {
+ CLabel cl= (CLabel) getTopLeft();
+ cl.setImage(image);
+ }
+
+ /**
+ * Returns a <code>ToolBarManager</code> if the given parent is a <code>ViewerPane</code>.
+ */
+ public static ToolBarManager getToolBarManager(Composite parent) {
+ if (parent instanceof Pane) {
+ Pane pane= (Pane) parent;
+ return pane.getToolBarManager();
+ }
+ return null;
+ }
+
+ /**
+ * Clear tool items in <code>ViewerPane</code>'s control bar.
+ */
+ public static void clearToolBar(Composite parent) {
+ ToolBarManager tbm= getToolBarManager(parent);
+ if (tbm != null) {
+ tbm.removeAll();
+ tbm.update(true);
+ }
+ }
+
+ //---- private stuff
+
+ private ToolBarManager getToolBarManager() {
+ if (fToolBarManager == null) {
+ ToolBar tb= new ToolBar(this, SWT.FLAT);
+ setTopCenter(tb);
+ fToolBarManager= new ToolBarManager(tb);
+ }
+ return fToolBarManager;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java
new file mode 100644
index 000000000..e62dee7c4
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.InputStream;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.IActionDelegate;
+import org.eclipse.compare.*;
+
+
+public class ReplaceWithEditionAction implements IActionDelegate {
+
+ private static final String ACTION_LABEL= "Replace with Edition";
+ private ISelection fSelection;
+
+
+ public void run(IAction action) {
+
+ Object[] s= Utilities.toArray(fSelection);
+
+ for (int i= 0; i < s.length; i++) {
+ Object o= s[i];
+ if (o instanceof IFile) {
+ replaceFromHistory((IFile)o);
+ continue;
+ }
+ if (o instanceof IAdaptable) {
+ IAdaptable a= (IAdaptable) o;
+ Object adapter= a.getAdapter(IResource.class);
+ if (adapter instanceof IFile)
+ replaceFromHistory((IFile)adapter);
+ continue;
+ }
+ }
+ }
+
+ public void selectionChanged(IAction a, ISelection s) {
+ fSelection= s;
+ }
+
+ void replaceFromHistory(IFile file) {
+
+ Shell parent= CompareUIPlugin.getShell();
+
+ IFileState states[]= null;
+ try {
+ states= file.getHistory(null);
+ } catch (CoreException ex) {
+ MessageDialog.openError(parent, ACTION_LABEL, ex.getMessage());
+ return;
+ }
+
+ if (states != null && states.length > 0) {
+
+ ITypedElement base= new ResourceNode(file);
+
+ ITypedElement[] editions= new ITypedElement[states.length];
+ for (int i= 0; i < states.length; i++)
+ editions[i]= new HistoryItem(base, states[i]);
+
+ ResourceBundle bundle= ResourceBundle.getBundle("org.eclipse.compare.internal.ReplaceWithEditionAction");
+ EditionSelectionDialog d= new EditionSelectionDialog(parent, bundle);
+
+ ITypedElement ti= d.selectEdition(base, editions, null);
+ if (ti instanceof IStreamContentAccessor) {
+ try {
+ InputStream is= ((IStreamContentAccessor)ti).getContents();
+ file.setContents(is, false, true, null);
+ } catch (CoreException ex) {
+ MessageDialog.openError(parent, ACTION_LABEL, ex.getMessage());
+ }
+ }
+ } else
+ MessageDialog.openInformation(parent, ACTION_LABEL, "No local editions available for selected resource.");
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties
new file mode 100644
index 000000000..6f591e9d5
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties
@@ -0,0 +1,22 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 2001
+# ====================================
+
+# @(#)ReplaceWithEditionAction.properties
+#
+# Resources for ReplaceWithEditionAction.java
+
+title= Replace from Local History
+
+treeTitleFormat= Editions of "{0}"
+dateIcon= obj16/date.gif
+timeIcon= obj16/time.gif
+
+editionLabel= Local History ({0})
+targetLabel= Workbench
+
+todayFormat= Today ({0})
+yesterdayFormat= Yesterday ({0})
+dayFormat= {0} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
new file mode 100644
index 000000000..6c6e0735d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+
+/**
+ * A two-way or three-way compare for arbitrary IResources.
+ */
+class ResourceCompareInput extends CompareEditorInput {
+
+ private ISelection fSelection;
+
+ private class MyDiffNode extends DiffNode {
+ public MyDiffNode(IDiffContainer parent, int description, ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ super(parent, description, ancestor, left, right);
+ }
+ protected void fireChange() {
+ super.fireChange();
+ setDirty(true);
+ }
+ }
+
+ /**
+ * Creates an compare editor input for the given selection.
+ */
+ ResourceCompareInput(CompareConfiguration config, ISelection selection) {
+ super(config);
+ fSelection= selection;
+ }
+
+ /**
+ * Performs a two-way or three-way diff on the current selection.
+ */
+ public Object prepareInput(IProgressMonitor pm) {
+
+ try {
+ pm.beginTask("Comparing:", IProgressMonitor.UNKNOWN);
+
+ IResource[] selection= Utilities.getResources(fSelection);
+
+ if (selection.length < 2 || selection.length > 3) {
+ setMessage("Selection must contain two or three resources");
+ return null;
+ }
+
+ boolean threeWay= selection.length == 3;
+
+ IResource lr= selection[0];
+ IResource rr= selection[1];
+ if (threeWay) {
+ lr= selection[1];
+ rr= selection[2];
+ }
+
+ IStructureComparator ancestor= null;
+ IStructureComparator left= getStructure(lr);
+ IStructureComparator right= getStructure(rr);
+
+ if (right == null || left == null) {
+ setMessage("Selected resources must be of same type");
+ return null;
+ }
+
+ CompareConfiguration cc= (CompareConfiguration) getCompareConfiguration();
+
+ String leftLabel= lr.getName();
+ cc.setLeftLabel(leftLabel);
+ cc.setLeftImage(CompareUIPlugin.getImage(lr));
+
+ String rightLabel= rr.getName();
+ cc.setRightLabel(rightLabel);
+ cc.setRightImage(CompareUIPlugin.getImage(rr));
+
+ StringBuffer title= new StringBuffer();
+ title.append("Compare (");
+ if (threeWay) {
+ IResource ar= selection[0];
+ ancestor= getStructure(ar);
+ String ancestorLabel= ar.getName();
+ cc.setAncestorLabel(ancestorLabel);
+ cc.setAncestorImage(CompareUIPlugin.getImage(ar));
+ title.append(ancestorLabel);
+ title.append("-");
+ }
+ title.append(leftLabel);
+ title.append("-");
+ title.append(rightLabel);
+ title.append(")");
+ setTitle(title.toString());
+
+ Differencer d= new Differencer() {
+ protected Object visit(Object parent, int description, Object ancestor, Object left, Object right) {
+ return new MyDiffNode((IDiffContainer) parent, description, (ITypedElement)ancestor, (ITypedElement)left, (ITypedElement)right);
+ }
+ };
+
+ return d.findDifferences(threeWay, pm, null, ancestor, left, right);
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ /**
+ * Creates a <code>IStructureComparator</code> for the given input.
+ * Returns <code>null</code> if no <code>IStructureComparator</code>
+ * can be found for the <code>IResource</code>.
+ */
+ private IStructureComparator getStructure(IResource input) {
+
+ if (input instanceof IContainer)
+ return new ResourceNode(input);
+
+ if (input instanceof IFile) {
+ ResourceNode rn= new ResourceNode(input);
+ IFile file= (IFile) input;
+ String type= normalizeCase(file.getFileExtension());
+ if ("JAR".equals(type) || "ZIP".equals(type)) { // FIXME
+
+ return new ZipStructureCreator().getStructure(rn);
+
+// IStructureCreatorDescriptor scd= CompareUIPlugin.getStructureCreator(type);
+// if (scd != null) {
+// IStructureCreator sc= scd.createStructureCreator();
+// if (sc != null)
+// return sc.getStructure(rn);
+// }
+ }
+ return rn;
+ }
+ return null;
+ }
+
+ private static final boolean NORMALIZE_CASE= true;
+
+ private static String normalizeCase(String s) {
+ if (NORMALIZE_CASE && s != null)
+ return s.toUpperCase();
+ return s;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java
new file mode 100644
index 000000000..9c7f8391d
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.*;
+
+/**
+ * Toggles the <code>ICompareConfiguration.SHOW_PSEUDO_CONFLICTS</code> property of an
+ * <code>ICompareConfiguration</code>.
+ */
+public class ShowPseudoConflicts extends ChangePropertyAction {
+
+ public ShowPseudoConflicts(ResourceBundle bundle, CompareConfiguration cc) {
+ super(bundle, cc, "action.ShowPseudoConflicts.", CompareConfiguration.SHOW_PSEUDO_CONFLICTS);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java
new file mode 100644
index 000000000..14c835e73
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.custom.SashForm;
+
+public class Splitter extends SashForm {
+
+ private static final String VISIBILITY= "org.eclipse.compare.internal.visibility";
+
+
+ public Splitter(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ public void setVisible(Control child, boolean visible) {
+
+ boolean wasEmpty= isEmpty();
+
+ child.setVisible(visible);
+ child.setData(VISIBILITY, new Boolean(visible));
+
+ if (wasEmpty != isEmpty()) {
+ Composite parent= getParent();
+ if (parent instanceof Splitter) {
+ Splitter sp= (Splitter) parent;
+ sp.setVisible(this, visible);
+ sp.layout();
+ }
+ } else {
+ layout();
+ }
+ }
+
+ private boolean isEmpty() {
+ Control[] controls= getChildren();
+ for (int i= 0; i < controls.length; i++)
+ if (isVisible(controls[i]))
+ return false;
+ return true;
+ }
+
+ private boolean isVisible(Control child) {
+ if (child instanceof Sash)
+ return false;
+ Object data= child.getData(VISIBILITY);
+ if (data instanceof Boolean)
+ return ((Boolean)data).booleanValue();
+ return true;
+ }
+
+ public void setMaximizedControl(Control control) {
+ if (control == null || control == getMaximizedControl())
+ super.setMaximizedControl(null);
+ else
+ super.setMaximizedControl(control);
+
+ // walk up
+ Composite parent= getParent();
+ if (parent instanceof Splitter)
+ ((Splitter) parent).setMaximizedControl(this);
+ else
+ layout(true);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java
new file mode 100644
index 000000000..d316705aa
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+
+/**
+ * A factory object for the <code>TextMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class TextMergeViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new TextMergeViewer(parent, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java
new file mode 100644
index 000000000..6a1ed00e5
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.IOException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.Document;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+
+
+public class TextViewer extends AbstractViewer {
+
+ private SourceViewer fSourceViewer;
+ private ICompareInput fInput;
+
+
+ TextViewer(Composite parent) {
+ fSourceViewer= new SourceViewer(parent, null, SWT.H_SCROLL + SWT.V_SCROLL);
+ fSourceViewer.setEditable(false);
+ }
+
+ public Control getControl() {
+ return fSourceViewer.getTextWidget();
+ }
+
+ public void setInput(Object input) {
+ if (input instanceof ICompareInput) {
+ fInput= (ICompareInput) input;
+ ITypedElement left= ((ICompareInput) fInput).getLeft();
+ fSourceViewer.setDocument(new Document(getString(left)));
+
+ } else if (input instanceof IStreamContentAccessor) {
+ fSourceViewer.setDocument(new Document(getString(input)));
+ }
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ private String getString(Object input) {
+
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ try {
+ return Utilities.readString(sca.getContents());
+ } catch (CoreException ex) {
+ }
+ }
+ return "";
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java
new file mode 100644
index 000000000..943a775e8
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.IViewerCreator;
+
+
+/**
+ * A factory object for the <code>TextMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class TextViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new TextViewer(parent);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java
new file mode 100644
index 000000000..62d96e185
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.StringTokenizer;
+
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+import org.eclipse.compare.contentmergeviewer.ITokenComparator;
+
+/**
+ * Implements the <code>ITokenComparator</code> interface for words (or tokens) in a string.
+ * A <code>TokenComparator</code> is used as the input for the <code>RangeDifferencer</code>
+ * engine to perform a token oriented compare on strings.
+ */
+public class TokenComparator implements ITokenComparator {
+
+ private boolean fShouldEscape= true;
+ private String fText;
+ private int fCount;
+ private int[] fStarts;
+ private int[] fLengths;
+
+ /**
+ * Creates a <code>TokenComparator</code> for the given string.
+ *
+ * @param string the string that is split into token
+ */
+ public TokenComparator(String string) {
+
+ if (string != null)
+ fText= string;
+ else
+ fText= "";
+
+ fStarts= new int[fText.length()];
+ fLengths= new int[fText.length()];
+ fCount= 0;
+
+ StringTokenizer tokenizer= new StringTokenizer(fText, " \t\n\r", true);
+
+ for (int pos= 0; tokenizer.hasMoreElements();) {
+ fStarts[fCount]= pos;
+ String s= tokenizer.nextToken();
+ int l= 0;
+ if (s != null)
+ l= s.length();
+ pos += l;
+ fLengths[fCount]= l;
+ fCount++;
+ }
+ }
+
+ /**
+ * Creates a <code>TokenComparator</code> for the given string.
+ *
+ * @param string the string that is split into token
+ * @param shouldEscape
+ */
+ public TokenComparator(String s, boolean shouldEscape) {
+ this(s);
+ fShouldEscape= shouldEscape;
+ }
+
+ /**
+ * Returns the number of token in the string.
+ *
+ * @return number of token in the string
+ */
+ public int getRangeCount() {
+ return fCount;
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenStart
+ */
+ public int getTokenStart(int index) {
+ if (index < fCount)
+ return fStarts[index];
+ return fText.length();
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenLength
+ */
+ public int getTokenLength(int index) {
+ if (index < fCount)
+ return fLengths[index];
+ return 0;
+ }
+
+ /**
+ * Returns the content of tokens in the specified range as a String.
+ * If the number of token is 0 the empty string ("") is returned.
+ *
+ * @param start index of first token
+ * @param length number of tokens
+ * @return the contents of the specified token range as a String
+ */
+ public String extract(int start, int length) {
+ if (start >= fStarts.length)
+ System.out.println("oops");
+ int startPos= fStarts[start];
+ int endPos= 0;
+ if (length > 0) {
+ int e= start + length-1;
+ if (e >= fStarts.length)
+ System.out.println("oops");
+ endPos= fStarts[e] + fLengths[e];
+ } else {
+ endPos= fStarts[start];
+ }
+ //int endPos= getTokenStart(start + length);
+ if (endPos >= fText.length())
+ return fText.substring(startPos);
+ return fText.substring(startPos, endPos);
+ }
+
+ /**
+ * Returns <code>true</code> if a token given by the first index
+ * matches a token specified by the other <code>IRangeComparator</code> and index.
+ *
+ * @param thisIndex the number of the token within this range comparator
+ * @param other the range comparator to compare this with
+ * @param otherIndex the number of the token within the other comparator
+ * @return <code>true</code> if the token are equal
+ */
+ public boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex) {
+ if (other != null && getClass() == other.getClass()) {
+ TokenComparator tc= (TokenComparator) other;
+ int thisLen= getTokenLength(thisIndex);
+ int otherLen= tc.getTokenLength(otherIndex);
+ if (thisLen == otherLen)
+ return fText.regionMatches(false, getTokenStart(thisIndex), tc.fText, tc.getTokenStart(otherIndex), thisLen);
+ }
+ return false;
+ }
+
+ /**
+ * Aborts the comparison if the number of tokens is too large.
+ *
+ * @return <code>true</code> to abort a token comparison
+ */
+ public boolean skipRangeComparison(int length, int max, IRangeComparator other) {
+
+ if (!fShouldEscape)
+ return false;
+
+ if (getRangeCount() < 50 || other.getRangeCount() < 50)
+ return false;
+
+ if (max < 100)
+ return false;
+
+ if (length < 100)
+ return false;
+
+ if (max > 800)
+ return true;
+
+ if (length < max / 4)
+ return false;
+
+ return true;
+ }
+
+// public static void main(String args[]) {
+// //String in= "private static boolean isWhitespace(char c) {";
+// //String in= "for (int j= 0; j < l-1; j++) {";
+// String in= "for do";
+// TokenComparator tc= new TokenComparator(in, false);
+//
+// System.out.println("n: " + tc.getRangeCount());
+// System.out.println(in);
+//
+// int p= 0;
+// for (int i= 0; i < tc.getRangeCount(); i++) {
+// int l= tc.getTokenLength(i);
+// System.out.print("<");
+//
+// for (int j= 0; j < l-1; j++)
+// System.out.print(" ");
+// }
+// System.out.println();
+//
+// //System.out.println("extract: <" + tc.extract(16, 1) + ">");
+// }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
new file mode 100644
index 000000000..b280afc1f
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+import org.eclipse.swt.widgets.Widget;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * Convenience and utility methods.
+ */
+public class Utilities {
+
+ public static boolean getBoolean(CompareConfiguration cc, String key, boolean dflt) {
+ if (cc != null) {
+ Object value= cc.getProperty(key);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ }
+ return dflt;
+ }
+
+ /**
+ * Retrieves the value from a property change event as a boolean.
+ */
+ public static boolean getValue(PropertyChangeEvent event, boolean dflt) {
+ Object newValue= event.getNewValue();
+ if (newValue instanceof Boolean)
+ return ((Boolean)newValue).booleanValue();
+ return dflt;
+ }
+
+ public static void firePropertyChange(ListenerList ll, Object source, String property, Object old, Object newValue) {
+ if (ll != null) {
+ PropertyChangeEvent event= null;
+ Object[] listeners= ll.getListeners();
+ for (int i= 0; i < listeners.length; i++) {
+ IPropertyChangeListener l= (IPropertyChangeListener) listeners[i];
+ if (event == null)
+ event= new PropertyChangeEvent(source, property, old, newValue);
+ l.propertyChange(event);
+ }
+ }
+ }
+
+ public static boolean okToUse(Widget widget) {
+ return widget != null && !widget.isDisposed();
+ }
+
+ public static boolean isMotif() {
+ return false;
+ }
+
+ /**
+ * Returns the elements of the given selection.
+ * Returns an empty array if the selection is empty or if
+ * the given selection is not of type <code>IStructuredSelection</code>.
+ *
+ * @param selection the selection
+ * @return the selected elements
+ */
+ public static Object[] toArray(ISelection selection) {
+ if (!(selection instanceof IStructuredSelection)) {
+ return new Object[0];
+ }
+ IStructuredSelection ss= (IStructuredSelection) selection;
+ return ss.toArray();
+ }
+
+ /**
+ * Convenience method: extract all <code>IResources</code> from given selection.
+ * Never returns null.
+ */
+ public static IResource[] getResources(ISelection selection) {
+
+ List tmp= new ArrayList();
+
+ if (selection instanceof IStructuredSelection) {
+
+ Object[] s= ((IStructuredSelection)selection).toArray();
+
+ for (int i= 0; i < s.length; i++) {
+ Object o= s[i];
+ if (o instanceof IResource) {
+ tmp.add(o);
+ continue;
+ }
+ if (o instanceof IAdaptable) {
+ IAdaptable a= (IAdaptable) o;
+ Object adapter= a.getAdapter(IResource.class);
+ if (adapter instanceof IResource)
+ tmp.add(adapter);
+ continue;
+ }
+ }
+ }
+ IResource[] resourceSelection= new IResource[tmp.size()];
+ tmp.toArray(resourceSelection);
+ return resourceSelection;
+ }
+
+ public static byte[] readBytes(InputStream in) {
+ ByteArrayOutputStream bos= new ByteArrayOutputStream();
+ try {
+ while (true) {
+ int c= in.read();
+ if (c == -1)
+ break;
+ bos.write(c);
+ }
+
+ } catch (IOException ex) {
+ return null;
+
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException x) {
+ }
+ }
+ try {
+ bos.close();
+ } catch (IOException x) {
+ }
+ }
+
+ return bos.toByteArray();
+ }
+
+ /**
+ * Returns null if an error occurred.
+ */
+ public static String readString(InputStream is) {
+ if (is == null)
+ return null;
+ BufferedReader reader= null;
+ try {
+ StringBuffer buffer= new StringBuffer();
+ char[] part= new char[2048];
+ int read= 0;
+ reader= new BufferedReader(new InputStreamReader(is));
+
+ while ((read= reader.read(part)) != -1)
+ buffer.append(part, 0, read);
+
+ return buffer.toString();
+
+ } catch (IOException ex) {
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Initialize the given Action from a ResourceBundle.
+ */
+ public static void initAction(IAction a, ResourceBundle bundle, String prefix) {
+
+ String labelKey= "label";
+ String tooltipKey= "tooltip";
+ String imageKey= "image";
+ String descriptionKey= "description";
+
+ if (prefix != null && prefix.length() > 0) {
+ labelKey= prefix + labelKey;
+ tooltipKey= prefix + tooltipKey;
+ imageKey= prefix + imageKey;
+ descriptionKey= prefix + descriptionKey;
+ }
+
+ a.setText(getString(bundle, labelKey, labelKey));
+ a.setToolTipText(getString(bundle, tooltipKey, null));
+ a.setDescription(getString(bundle, descriptionKey, null));
+
+ String relPath= getString(bundle, imageKey, null);
+ if (relPath != null && relPath.trim().length() > 0) {
+ ImageDescriptor id= CompareUIPlugin.getImageDescriptor(relPath);
+ if (id != null)
+ a.setImageDescriptor(id);
+ }
+ }
+
+ public static String getString(ResourceBundle bundle, String key, String dfltValue) {
+
+ if (bundle != null) {
+ try {
+ return bundle.getString(key);
+ } catch (MissingResourceException x) {
+ }
+ }
+ return dfltValue;
+ }
+
+ public static int getInteger(ResourceBundle bundle, String key, int dfltValue) {
+
+ if (bundle != null) {
+ try {
+ String s= bundle.getString(key);
+ if (s != null)
+ return Integer.parseInt(s);
+ } catch (NumberFormatException x) {
+ } catch (MissingResourceException x) {
+ }
+ }
+ return dfltValue;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java
new file mode 100644
index 000000000..d6ccd98aa
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.compare.*;
+
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Creates <code>Viewer</code>s from an <code>IConfigurationElement</code>.
+ */
+public class ViewerDescriptor implements IViewerDescriptor {
+
+ private final static String CLASS_ATTRIBUTE= "class";
+ private final static String EXTENSIONS_ATTRIBUTE= "extensions";
+
+ private IConfigurationElement fConfiguration;
+ private IViewerCreator fViewerCreator;
+
+ public ViewerDescriptor(IConfigurationElement config) {
+ fConfiguration= config;
+ }
+
+ public Viewer createViewer(Viewer currentViewer, Composite parent, CompareConfiguration mp) {
+ String className= fConfiguration.getAttribute(CLASS_ATTRIBUTE);
+ if (currentViewer != null && currentViewer.getClass().getName().equals(className)) {
+ return currentViewer;
+ }
+ if (fViewerCreator == null) {
+ try {
+ fViewerCreator= (IViewerCreator) fConfiguration.createExecutableExtension(CLASS_ATTRIBUTE);
+ } catch (CoreException e) {
+ }
+ }
+
+ if (fViewerCreator != null) {
+ Viewer viewer= fViewerCreator.createViewer(parent, mp);
+ //if (viewer != null && currentViewer != null && viewer.getClass() == currentViewer.getClass())
+ // return currentViewer;
+ return viewer;
+ }
+
+ return null;
+ }
+
+ public String getExtension() {
+ return fConfiguration.getAttribute(EXTENSIONS_ATTRIBUTE);
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java
new file mode 100644
index 000000000..d18bc84e5
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000,2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.*;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.zip.*;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+
+public class ZipStructureCreator implements IStructureCreator {
+
+ /**
+ * Common base class for ZipFolder and ZipFile
+ */
+ static abstract class ZipResource implements IStructureComparator, ITypedElement {
+
+ private String fName;
+
+ ZipResource(String name) {
+ fName= name;
+ }
+
+ public String getName() {
+ return fName;
+ }
+
+ public Image getImage() {
+ return CompareUIPlugin.getImage(getType());
+ }
+
+ /**
+ * Returns true if other is ITypedElement and names are equal.
+ * @see IComparator#equals
+ */
+ public boolean equals(Object other) {
+ if (other instanceof ITypedElement)
+ return fName.equals(((ITypedElement) other).getName());
+ return super.equals(other);
+ }
+
+ public int hashCode() {
+ return fName.hashCode();
+ }
+ }
+
+ static class ZipFolder extends ZipResource {
+
+ private HashMap fChildren= new HashMap(10);
+
+ ZipFolder(String name) {
+ super(name);
+ }
+
+ public String getType() {
+ return ITypedElement.FOLDER_TYPE;
+ }
+
+ public Object[] getChildren() {
+ Object[] children= new Object[fChildren.size()];
+ Iterator iter= fChildren.values().iterator();
+ for (int i= 0; iter.hasNext(); i++)
+ children[i]= iter.next();
+ return children;
+ }
+
+ ZipFile createContainer(String path) {
+ String entry= path;
+ int pos= path.indexOf('/');
+ if (pos < 0)
+ pos= path.indexOf('\\');
+ if (pos >= 0) {
+ entry= path.substring(0, pos);
+ path= path.substring(pos + 1);
+ } else if (entry.length() > 0) {
+ ZipFile ze= new ZipFile(entry);
+ fChildren.put(entry, ze);
+ return ze;
+ } else
+ return null;
+
+ ZipFolder folder= null;
+ if (fChildren != null) {
+ Object o= fChildren.get(entry);
+ if (o instanceof ZipFolder)
+ folder= (ZipFolder) o;
+ }
+
+ if (folder == null) {
+ folder= new ZipFolder(entry);
+ fChildren.put(entry, folder);
+ }
+
+ return folder.createContainer(path);
+ }
+ }
+
+ static class ZipFile extends ZipResource implements IStreamContentAccessor {
+
+ private byte[] fContents;
+
+ ZipFile(String name) {
+ super(name);
+ }
+
+ public String getType() {
+ String s= this.getName();
+ int pos= s.lastIndexOf('.');
+ if (pos >= 0)
+ return s.substring(pos + 1);
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ public Object[] getChildren() {
+ return null;
+ }
+
+ public InputStream getContents() {
+ if (fContents == null)
+ fContents= new byte[0];
+ return new ByteArrayInputStream(fContents);
+ }
+
+ byte[] getBytes() {
+ return fContents;
+ }
+
+ void setBytes(byte[] buffer) {
+ fContents= buffer;
+ }
+ }
+
+ private String fTitle;
+
+ public ZipStructureCreator() {
+ this("Zip Archive Compare");
+ }
+
+ public ZipStructureCreator(String title) {
+ fTitle= title;
+ }
+
+ public String getName() {
+ return fTitle;
+ }
+
+ public IStructureComparator getStructure(Object input) {
+
+ InputStream is= null;
+
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ try {
+ is= sca.getContents();
+ } catch (CoreException ex) {
+ }
+ }
+
+ if (is == null)
+ return null;
+
+ ZipInputStream zip= new ZipInputStream(is);
+ ZipFolder root= new ZipFolder("");
+ try {
+ for (;;) {
+ ZipEntry entry= zip.getNextEntry();
+ if (entry == null)
+ break;
+ //System.out.println(entry.getName() + ": " + entry.getSize() + " " + entry.getCompressedSize());
+
+ ZipFile ze= root.createContainer(entry.getName());
+ if (ze != null) {
+ int length= (int) entry.getSize();
+ if (length >= 0) {
+ byte[] buffer= new byte[length];
+ int offset= 0;
+
+ do {
+ int n= zip.read(buffer, offset, length);
+ offset += n;
+ length -= n;
+ } while (length > 0);
+
+ ze.setBytes(buffer);
+ }
+ }
+ zip.closeEntry();
+ }
+ } catch (IOException ex) {
+ return null;
+ } finally {
+ try {
+ zip.close();
+ } catch (IOException ex) {
+ }
+ }
+
+ if (root.fChildren.size() == 1) {
+ Iterator iter= root.fChildren.values().iterator();
+ return (IStructureComparator) iter.next();
+ }
+ return root;
+ }
+
+ public String getContents(Object o, boolean ignoreWhitespace) {
+ if (o instanceof ZipFile) {
+ byte[] bytes= ((ZipFile)o).getBytes();
+ if (bytes != null)
+ return new String(bytes);
+ return "";
+ }
+ return null;
+ }
+
+ /**
+ * Returns <code>false</code> since we cannot update a zip archive.
+ */
+ public boolean canSave() {
+ return false;
+ }
+
+ /**
+ * Throws <code>AssertionFailedException</code> since we cannot update a zip archive.
+ */
+ public void save(IStructureComparator structure, Object input) {
+ Assert.isTrue(false, "cannot update zip archive");
+ }
+
+ public IStructureComparator locate(Object path, Object source) {
+ return null;
+ }
+
+ public boolean canRewriteTree() {
+ return false;
+ }
+
+ public void rewriteTree(Differencer diff, IDiffContainer root) {
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/package.html b/bundles/org.eclipse.compare/compare/org/eclipse/compare/package.html
new file mode 100644
index 000000000..fb0ad6215
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/package.html
@@ -0,0 +1,50 @@
+<!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 performing structural and textual
+compare operations on arbitrary data and displaying the results.
+<h2>
+Package Specification</h2>
+The class <tt>CompareUI</tt> defines the entry point to initiate a configurable
+compare operation on arbitrary resources. The result of the compare is
+opened into a compare editor where the details can be browsed and edited
+in dynamically selected structure and content viewers.
+<p>A compare operation must be implemented as a subclass of <tt>CompareEditorInput</tt>.
+A <tt>CompareEditorInput</tt> runs a (potentially lengthy) compare operation
+under progress monitor control, creates a UI for drilling-down into the
+compare results, tracks the dirty state of the result in case of merge,
+and saves any changes that occured during a merge.
+<p>An instance of <tt>CompareConfiguration</tt> configures various UI aspects
+of compare/merge viewers like title labels and images, or whether a side
+of a merge viewer is editable. It is passed to the <tt>CompareEditorInput</tt>
+on creation.
+<p>When implementing a compare operation clients have to provide a tree
+of <tt>IStructureComparator</tt> (see package org.eclipse.compare.structuremergeviewer)
+and <tt>IStreamContentAccessor</tt> that can be passed as the input to
+the differencing engine (org.eclipse.compare.structuremergeviewer.Differencer).
+A <tt>ResourceNode</tt> is a convenience class that implements both interfaces
+for Eclipse workbench resources (org.eclipse.core.resources.IResource).
+It can be used without modification as the input to the differencing engine.
+<p>The <tt>EditionSelectionDialog</tt> is a simple selection dialog where
+one input element can be compared against a list of historic variants (<i>editions</i>)
+of the same input element. The dialog can be used to implement functions
+like <i>"Replace with Version"</i> or
+<br><i>"Replace with Edition"</i> on workbench resources.
+<p>In addition it is possible to specify a subsection of the input element
+(e.g. a method in a Java source file) by means of a <i>path</i>. In this
+case the dialog compares only the subsection (as specified by the path)
+with the corresponding subsection in the list of editions. This functionality
+can be used to implement <i>"Replace with Method Edition"</i> for the Java
+language.
+<p>The <tt>EditionSelectionDialog</tt> requires that the editions implement
+the <tt>IStreamContentAccessor</tt> and <tt>IModificationDate</tt> interfaces.
+The <tt>HistoryItem</tt> is a convenience class that implements these interfaces
+for <tt>IFileState</tt> objects.
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java
new file mode 100644
index 000000000..7ea31ef38
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A custom iterator to iterate over a List of <code>RangeDifferences</code>.
+ * It is used internally by the <code>RangeDifferencer</code>.
+ */
+/* package */ class DifferencesIterator {
+
+ List fRange;
+ int fIndex;
+ RangeDifference[] fArray;
+ RangeDifference fDifference;
+
+ /**
+ * Creates a differences iterator on an array of <code>RangeDifference</code>s.
+ */
+ DifferencesIterator(RangeDifference[] differenceRanges) {
+
+ fArray= differenceRanges;
+ fIndex= 0;
+ fRange= new ArrayList();
+ if (fIndex < fArray.length)
+ fDifference= fArray[fIndex++];
+ else
+ fDifference= null;
+ }
+
+ /**
+ * Returns the number of RangeDifferences
+ */
+ int getCount() {
+ return fRange.size();
+ }
+
+ /**
+ * Appends the edit to its list and moves to the next <code>RangeDifference</code>.
+ */
+ void next() {
+ fRange.add(fDifference);
+ if (fDifference != null) {
+ if (fIndex < fArray.length)
+ fDifference= fArray[fIndex++];
+ else
+ fDifference= null;
+ }
+ }
+
+ /**
+ * Difference iterators are used in pairs.
+ * This method returns the other iterator.
+ */
+ DifferencesIterator other(DifferencesIterator right, DifferencesIterator left) {
+ if (this == right)
+ return left;
+ return right;
+ }
+
+ /**
+ * Removes all <code>RangeDifference</code>s
+ */
+ void removeAll() {
+ fRange.clear();
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java
new file mode 100644
index 000000000..2a7ca6643
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+/**
+ * For breaking an object to compare into a sequence of comparable entities.
+ * <p>
+ * It is used by <code>RangeDifferencer</code> to find longest sequences of
+ * matching and non-matching ranges.
+ * <p>
+ * For example, to compare two text documents and find longest common sequences
+ * of matching and non-matching lines, the implementation must break the document
+ * into lines. <code>getRangeCount</code> would return the number of lines in the
+ * document, and <code>rangesEqual</code> would compare a specified line given
+ * with one in another <code>IRangeComparator</code>.
+ * </p>
+ * <p>
+ * Clients should implement this interface; there is no standard implementation.
+ * </p>
+ */
+public interface IRangeComparator {
+
+ /**
+ * Returns the number of comparable entities.
+ *
+ * @return the number of comparable entities
+ */
+ int getRangeCount();
+
+ /**
+ * Returns whether the comparable entity given by the first index
+ * matches an entity specified by the other <code>IRangeComparator</code> and index.
+ *
+ * @param thisIndex the index of the comparable entity within this <code>IRangeComparator</code>
+ * @param other the IRangeComparator to compare this with
+ * @param otherIndex the index of the comparable entity within the other <code>IRangeComparator</code>
+ * @return <code>true</code> if the comparable entities are equal
+ */
+ boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex);
+
+ /**
+ * Returns whether a comparison should be skipped because it would be too costly (or lengthy).
+ *
+ * @param length a number on which to base the decision whether to return
+ * <code>true</code> or <code>false</code>
+ * @param maxLength another number on which to base the decision whether to return
+ * <code>true</code> or <code>false</code>
+ * @param other the other <code>IRangeComparator</code> to compare with
+ * @return <code>true</code> to avoid a too lengthy range comparison
+ */
+ boolean skipRangeComparison(int length, int maxLength, IRangeComparator other);
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java
new file mode 100644
index 000000000..f3ab74f91
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+/* package */ class LinkedRangeDifference extends RangeDifference {
+
+ static final int INSERT= 0;
+ static final int DELETE= 1;
+ static final int CHANGE= 2;
+ static final int ERROR= 3;
+
+ LinkedRangeDifference fNext;
+
+ /**
+ * Creates a LinkedRangeDifference an initializes it to the error state
+ */
+ LinkedRangeDifference() {
+ super(ERROR);
+ fNext= null;
+ }
+
+ /**
+ * Constructs and links a LinkeRangeDifference to another LinkedRangeDifference
+ */
+ LinkedRangeDifference(LinkedRangeDifference next, int operation) {
+ super(operation);
+ fNext= next;
+ }
+
+ /**
+ * Follows the next link
+ */
+ LinkedRangeDifference getNext() {
+ return fNext;
+ }
+
+ boolean isDelete() {
+ return kind() == DELETE;
+ }
+
+ boolean isInsert() {
+ return kind() == INSERT;
+ }
+
+ /**
+ * Sets the next link of this LinkedRangeDifference
+ */
+ void setNext(LinkedRangeDifference next) {
+ fNext= next;
+ }
+}
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java
new file mode 100644
index 000000000..608ebc9e4
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+import org.eclipse.jface.*;
+import org.eclipse.jface.util.Assert;
+
+/**
+ * Description of a change between two or three ranges of comparable entities.
+ * <p>
+ * <code>RangeDifference</code> objects are the elements of a compare result returned from
+ * the <code>RangeDifferencer</code> <code>find* </code> methods.
+ * Clients use these objects as they are returned from the differencer.
+ * This class is not intended to be instantiated or subclassed.
+ * <p>
+ * Note: A range in the <code>RangeDifference</code> object is given as a start index
+ * and length in terms of comparable entities. However, these entity indices and counts
+ * are not necessarily character positions. For example, if an entity represents a line
+ * in a document, the start index would be a line number and the count would be in lines.
+ * </p>
+ *
+ * @see RangeDifferencer
+ */
+public class RangeDifference {
+
+ /** Two-way change constant indicating no change. */
+ public final static int NOCHANGE= 0;
+ /** Two-way change constant indicating two-way change (same as <code>RIGHT</code>) */
+ public final static int CHANGE= 2;
+
+ /** Three-way change constant indicating a change in both right and left. */
+ public final static int CONFLICT= 1;
+ /** Three-way change constant indicating a change in right. */
+ public final static int RIGHT= 2;
+ /** Three-way change constant indicating a change in left. */
+ public final static int LEFT= 3;
+ /**
+ * Three-way change constant indicating the same change in both right and left,
+ * that is only the ancestor is different.
+ */
+ public final static int ANCESTOR= 4;
+
+ /** Constant indicating an unknown change kind. */
+ public final static int ERROR= 5;
+
+ /** the kind of change: NOCHANGE, CHANGE, LEFT, RIGHT, ANCESTOR, CONFLICT, ERROR */
+ int fKind;
+
+ int fLeftStart;
+ int fLeftLength;
+ int fRightStart;
+ int fRightLength;
+ int lAncestorStart;
+ int lAncestorLength;
+
+ /**
+ * Creates a new range difference with the given change kind.
+ *
+ * @param changeKind the kind of change
+ */
+ /* package */ RangeDifference(int changeKind) {
+ fKind= changeKind;
+ }
+
+ /**
+ * Creates a new <code>RangeDifference</code> with the given change kind
+ * and left and right ranges.
+ *
+ * @param changeKind the kind of change
+ * @param rightStart start index of entity on right side
+ * @param rightLength number of entities on right side
+ * @param leftStart start index of entity on left side
+ * @param leftLength number of entities on left side
+ */
+ /* package */ RangeDifference(int kind, int rightStart, int rightLength, int leftStart, int leftLength) {
+ fKind= kind;
+ fRightStart= rightStart;
+ fRightLength= rightLength;
+ fLeftStart= leftStart;
+ fLeftLength= leftLength;
+ }
+
+ /**
+ * Creates a new <code>RangeDifference</code> with the given change kind
+ * and left, right, and ancestor ranges.
+ *
+ * @param changeKind the kind of change
+ * @param rightStart start index of entity on right side
+ * @param rightLength number of entities on right side
+ * @param leftStart start index of entity on left side
+ * @param leftLength number of entities on left side
+ * @param ancestorStart start index of entity on ancestor side
+ * @param ancestorLength number of entities on ancestor side
+ */
+ /* package */ RangeDifference(int kind, int rightStart, int rightLength, int leftStart, int leftLength,
+ int ancestorStart, int ancestorLength) {
+ this(kind, rightStart, rightLength, leftStart, leftLength);
+ lAncestorStart= ancestorStart;
+ lAncestorLength= ancestorLength;
+ }
+
+ /**
+ * Returns the kind of difference.
+ *
+ * @return the kind of difference, one of
+ * <code>NOCHANGE</code>, <code>CHANGE</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>ANCESTOR</code>, <code>CONFLICT</code>, <code>ERROR</code>
+ */
+ public int kind() {
+ return fKind;
+ }
+
+ /**
+ * Returns the start index of the entity range on the ancestor side.
+ *
+ * @return the start index of the entity range on the ancestor side
+ */
+ public int ancestorStart() {
+ return lAncestorStart;
+ }
+
+ /**
+ * Returns the number of entities on the ancestor side.
+ *
+ * @return the number of entities on the ancestor side
+ */
+ public int ancestorLength() {
+ return lAncestorLength;
+ }
+
+ /**
+ * Returns the end index of the entity range on the ancestor side.
+ *
+ * @return the end index of the entity range on the ancestor side
+ */
+ public int ancestorEnd() {
+ return lAncestorStart + lAncestorLength;
+ }
+
+ /**
+ * Returns the start index of the entity range on the right side.
+ *
+ * @return the start index of the entity range on the right side
+ */
+ public int rightStart() {
+ return fRightStart;
+ }
+
+ /**
+ * Returns the number of entities on the right side.
+ *
+ * @return the number of entities on the right side
+ */
+ public int rightLength() {
+ return fRightLength;
+ }
+
+ /**
+ * Returns the end index of the entity range on the right side.
+ *
+ * @return the end index of the entity range on the right side
+ */
+ public int rightEnd() {
+ return fRightStart + fRightLength;
+ }
+
+ /**
+ * Returns the start index of the entity range on the left side.
+ *
+ * @return the start index of the entity range on the left side
+ */
+ public int leftStart() {
+ return fLeftStart;
+ }
+
+ /**
+ * Returns the number of entities on the left side.
+ *
+ * @return the number of entities on the left side
+ */
+ public int leftLength() {
+ return fLeftLength;
+ }
+
+ /**
+ * Returns the end index of the entity range on the left side.
+ *
+ * @return the end index of the entity range on the left side
+ */
+ public int leftEnd() {
+ return fLeftStart + fLeftLength;
+ }
+
+ /**
+ * Returns the maximum number of entities in the left, right, and ancestor sides of this range.
+ *
+ * @return the maximum number of entities in the left, right, and ancestor sides of this range
+ */
+ public int maxLength() {
+ return Math.max(fRightLength, Math.max(fLeftLength, lAncestorLength));
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java
new file mode 100644
index 000000000..9056493f6
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.util.Assert;
+
+/**
+ * For finding the differences between two or three <code>IRangeComparator</code>s.
+ * <p>
+ * To use the differencer, clients provide an <code>IRangeComparator</code>
+ * that breaks their input data into a sequence of comparable entities. The differencer
+ * returns the differences among these sequences as an array of <code>RangeDifference</code> objects
+ * (<code>findDifferences</code> methods).
+ * Every <code>RangeDifference</code> represents a single kind of difference
+ * and the corresponding ranges of the underlying comparable entities in the
+ * left, right, and optionally ancestor sides.
+ * <p>
+ * Alternatively, the <code>findRanges</code> methods not only return objects for
+ * the differing ranges but for non-differing ranges too.
+ * <p>
+ * The algorithm used is an objectified version of one described in:
+ * <it>A File Comparison Program,</it> by Webb Miller and Eugene W. Myers,
+ * Software Practice and Experience, Vol. 15, Nov. 1985.
+ *
+ * @see IRangeComparator
+ * @see RangeDifference
+ */
+public final class RangeDifferencer {
+
+ private static final RangeDifference[] EMPTY_RESULT= new RangeDifference[0];
+
+ /* (non Javadoc)
+ * Non instantiatiable!
+ */
+ private RangeDifferencer() {
+ }
+
+ /**
+ * Finds the differences between two <code>IRangeComparator</code>s.
+ * The differences are returned as a list of <code>RangeDifference</code>s.
+ * If no differences are detected an empty list is returned.
+ *
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences, or an empty array if no differences were found
+ */
+ public static RangeDifference[] findDifferences(IRangeComparator left, IRangeComparator right) {
+
+ // assert that both IRangeComparators are of the same class
+ Assert.isTrue(right.getClass().equals(left.getClass()));
+
+ int rightSize= right.getRangeCount();
+ int leftSize= left.getRangeCount();
+ //
+ // Differences matrix:
+ // only the last d of each diagonal is stored, i.e., lastDiagonal[k] = row of d
+ //
+ int diagLen= 2 * Math.max(rightSize, leftSize); // bound on the size of edit script
+ int maxDiagonal= diagLen;
+ int lastDiagonal[]= new int[diagLen + 1]; // the row containing the last d
+ // on diagonal k (lastDiagonal[k] = row)
+ int origin= diagLen / 2; // origin of diagonal 0
+
+ // script corresponding to d[k]
+ LinkedRangeDifference script[]= new LinkedRangeDifference[diagLen + 1];
+ int row, col;
+
+ // find common prefix
+ for (row= 0; row < rightSize && row < leftSize && rangesEqual(right, row, left, row) == true; ++row);
+
+ lastDiagonal[origin]= row;
+ script[origin]= null;
+ int lower= (row == rightSize) ? origin + 1 : origin - 1;
+ int upper= (row == leftSize) ? origin - 1 : origin + 1;
+
+ if (lower > upper)
+ return EMPTY_RESULT;
+
+ // for each value of the edit distance
+ for (int d= 1; d <= maxDiagonal; ++d) { // d is the current edit distance
+
+ if (right.skipRangeComparison(d, maxDiagonal, left))
+ return EMPTY_RESULT; // should be something we already found
+
+ // for each relevant diagonal (-d, -d+2 ..., d-2, d)
+ for (int k= lower; k <= upper; k += 2) { // k is the current diagonal
+ LinkedRangeDifference edit;
+
+ if (k == origin - d || k != origin + d && lastDiagonal[k + 1] >= lastDiagonal[k - 1]) {
+ //
+ // move down
+ //
+ row= lastDiagonal[k + 1] + 1;
+ edit= new LinkedRangeDifference(script[k + 1], LinkedRangeDifference.DELETE);
+ } else {
+ //
+ // move right
+ //
+ row= lastDiagonal[k - 1];
+ edit= new LinkedRangeDifference(script[k - 1], LinkedRangeDifference.INSERT);
+ }
+ col= row + k - origin;
+ edit.fRightStart= row;
+ edit.fLeftStart= col;
+ Assert.isTrue(k >= 0 && k <= maxDiagonal, "Indices out of range");
+ script[k]= edit;
+
+ // slide down the diagonal as far as possible
+ while (row < rightSize && col < leftSize && rangesEqual(right, row, left, col) == true) {
+ ++row;
+ ++col;
+ }
+
+ Assert.isTrue(k >= 0 && k <= maxDiagonal, "unreasonable value for diagonal index");
+ lastDiagonal[k]= row;
+
+ if (row == rightSize && col == leftSize) {
+ //showScript(script[k], right, left);
+ return createDifferencesRanges(script[k]);
+ }
+ if (row == rightSize)
+ lower= k + 2;
+ if (col == leftSize)
+ upper= k - 2;
+ }
+ --lower;
+ ++upper;
+ }
+ // too many differences
+ Assert.isTrue(false);
+ return null;
+ }
+
+ /**
+ * Finds the differences among three <code>IRangeComparator</code>s.
+ * The differences are returned as a list of <code>RangeDifference</code>s.
+ * If no differences are detected an empty list is returned.
+ * If the ancestor range comparator is <code>null</code>, a two-way
+ * comparison is performed.
+ *
+ * @param ancestor the ancestor range comparator or <code>null</code>
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences, or an empty array if no differences were found
+ */
+ public static RangeDifference[] findDifferences(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) {
+
+ if (ancestor == null)
+ return findDifferences(left, right);
+
+ RangeDifference[] leftAncestorScript= null;
+ RangeDifference[] rightAncestorScript= findDifferences(ancestor, right);
+ if (rightAncestorScript != null)
+ leftAncestorScript= findDifferences(ancestor, left);
+ if (rightAncestorScript == null || leftAncestorScript == null)
+ return null;
+
+ DifferencesIterator myIter= new DifferencesIterator(rightAncestorScript);
+ DifferencesIterator yourIter= new DifferencesIterator(leftAncestorScript);
+
+ List diff3= new ArrayList();
+ diff3.add(new RangeDifference(RangeDifference.ERROR)); // add a sentinel
+
+ int changeRangeStart= 0;
+ int changeRangeEnd= 0;
+ //
+ // Combine the two two-way edit scripts into one
+ //
+ while (myIter.fDifference != null || yourIter.fDifference != null) {
+
+ DifferencesIterator startThread;
+ myIter.removeAll();
+ yourIter.removeAll();
+ //
+ // take the next diff that is closer to the start
+ //
+ if (myIter.fDifference == null)
+ startThread= yourIter;
+ else if (yourIter.fDifference == null)
+ startThread= myIter;
+ else { // not at end of both scripts take the lowest range
+ if (myIter.fDifference.fLeftStart <= yourIter.fDifference.fLeftStart) // 2 -> common (Ancestor) change range
+ startThread= myIter;
+ else
+ startThread= yourIter;
+ }
+ changeRangeStart= startThread.fDifference.fLeftStart;
+ changeRangeEnd= startThread.fDifference.leftEnd();
+
+ startThread.next();
+ //
+ // check for overlapping changes with other thread
+ // merge overlapping changes with this range
+ //
+ DifferencesIterator other= startThread.other(myIter, yourIter);
+ while (other.fDifference != null && other.fDifference.fLeftStart <= changeRangeEnd) {
+ int newMax= other.fDifference.leftEnd();
+ other.next();
+ if (newMax >= changeRangeEnd) {
+ changeRangeEnd= newMax;
+ other= other.other(myIter, yourIter);
+ }
+ }
+ diff3.add(createRangeDifference3(myIter, yourIter, diff3, right, left, changeRangeStart, changeRangeEnd));
+ }
+
+ // remove sentinel
+ diff3.remove(0);
+ return (RangeDifference[]) diff3.toArray(EMPTY_RESULT);
+ }
+
+ /**
+ * Finds the differences among two <code>IRangeComparator</code>s.
+ * In contrast to <code>findDifferences</code>, the result
+ * contains <code>RangeDifference</code> elements for non-differing ranges too.
+ *
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences
+ */
+ public static RangeDifference[] findRanges(IRangeComparator left, IRangeComparator right) {
+ RangeDifference[] in= findDifferences(left, right);
+ List out= new ArrayList();
+
+ RangeDifference rd;
+
+ int mstart= 0;
+ int ystart= 0;
+
+ for (int i= 0; i < in.length; i++) {
+ RangeDifference es= in[i];
+
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, es.rightStart() - mstart, ystart, es.leftStart() - ystart);
+ if (rd.maxLength() != 0)
+ out.add(rd);
+
+ out.add(es);
+
+ mstart= es.rightEnd();
+ ystart= es.leftEnd();
+ }
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, right.getRangeCount() - mstart, ystart, left.getRangeCount() - ystart);
+ if (rd.maxLength() > 0)
+ out.add(rd);
+
+ return (RangeDifference[]) out.toArray(EMPTY_RESULT);
+ }
+
+ /**
+ * Finds the differences among three <code>IRangeComparator</code>s.
+ * In contrast to <code>findDifferences</code>, the result
+ * contains <code>RangeDifference</code> elements for non-differing ranges too.
+ * If the ancestor range comparator is <code>null</code>, a two-way
+ * comparison is performed.
+ *
+ * @param ancestor the ancestor range comparator or <code>null</code>
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences
+ */
+ public static RangeDifference[] findRanges(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) {
+
+ if (ancestor == null)
+ return findRanges(left, right);
+
+ RangeDifference[] in= findDifferences(ancestor, left, right);
+ List out= new ArrayList();
+
+ RangeDifference rd;
+
+ int mstart= 0;
+ int ystart= 0;
+ int astart= 0;
+
+ for (int i= 0; i < in.length; i++) {
+ RangeDifference es= (RangeDifference) in[i];
+
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, es.rightStart() - mstart, ystart, es.leftStart() - ystart, astart, es.ancestorStart() - astart);
+ if (rd.maxLength() > 0)
+ out.add(rd);
+
+ out.add(es);
+
+ mstart= es.rightEnd();
+ ystart= es.leftEnd();
+ astart= es.ancestorEnd();
+ }
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, right.getRangeCount() - mstart, ystart, left.getRangeCount() - ystart, astart, ancestor.getRangeCount() - astart);
+ if (rd.maxLength() > 0)
+ out.add(rd);
+
+ return (RangeDifference[]) out.toArray(EMPTY_RESULT);
+ }
+
+ //---- private methods
+
+ /**
+ * Creates a Vector of DifferencesRanges out of the LinkedRangeDifference.
+ * It coalesces adjacent changes.
+ * In addition, indices are changed such that the ranges are 1) open, i.e,
+ * the end of the range is not included, and 2) are zero based.
+ */
+ private static RangeDifference[] createDifferencesRanges(LinkedRangeDifference start) {
+
+ LinkedRangeDifference ep= reverseDifferences(start);
+ ArrayList result= new ArrayList();
+ RangeDifference es= null;
+
+ while (ep != null) {
+ es= new RangeDifference(RangeDifference.CHANGE);
+
+ if (ep.isInsert()) {
+ es.fRightStart= ep.fRightStart + 1;
+ es.fLeftStart= ep.fLeftStart;
+ RangeDifference b= ep;
+ do {
+ ep= ep.getNext();
+ es.fLeftLength++;
+ } while (ep != null && ep.isInsert() && ep.fRightStart == b.fRightStart);
+ } else {
+ es.fRightStart= ep.fRightStart;
+ es.fLeftStart= ep.fLeftStart;
+
+ RangeDifference a= ep;
+ //
+ // deleted lines
+ //
+ do {
+ a= ep;
+ ep= ep.getNext();
+ es.fRightLength++;
+ } while (ep != null && ep.isDelete() && ep.fRightStart == a.fRightStart + 1);
+
+ boolean change= (ep != null && ep.isInsert() && ep.fRightStart == a.fRightStart);
+
+ if (change) {
+ RangeDifference b= ep;
+ //
+ // replacement lines
+ //
+ do {
+ ep= ep.getNext();
+ es.fLeftLength++;
+ } while (ep != null && ep.isInsert() && ep.fRightStart == b.fRightStart);
+ } else {
+ es.fLeftLength= 0;
+ }
+ es.fLeftStart++; // meaning of range changes from "insert after", to "replace with"
+
+ }
+ //
+ // the script commands are 1 based, subtract one to make them zero based
+ //
+ es.fRightStart--;
+ es.fLeftStart--;
+ result.add(es);
+ }
+ return (RangeDifference[]) result.toArray(EMPTY_RESULT);
+ }
+
+ /**
+ * Creates a <code>RangeDifference3</code> given the
+ * state of two DifferenceIterators.
+ */
+ private static RangeDifference createRangeDifference3(
+ DifferencesIterator myIter,
+ DifferencesIterator yourIter,
+ List diff3,
+ IRangeComparator right,
+ IRangeComparator left,
+ int changeRangeStart,
+ int changeRangeEnd) {
+
+ int rightStart, rightEnd;
+ int leftStart, leftEnd;
+ int kind= RangeDifference.ERROR;
+ RangeDifference last= (RangeDifference) diff3.get(diff3.size() - 1);
+
+ Assert.isTrue((myIter.getCount() != 0 || yourIter.getCount() != 0), "at least one range array must be non-empty");
+ //
+ // find corresponding lines to fChangeRangeStart/End in right and left
+ //
+ if (myIter.getCount() == 0) { // only left changed
+ rightStart= changeRangeStart - last.ancestorEnd() + last.rightEnd();
+ rightEnd= changeRangeEnd - last.ancestorEnd() + last.rightEnd();
+ kind= RangeDifference.LEFT;
+ } else {
+ RangeDifference f= (RangeDifference) myIter.fRange.get(0);
+ RangeDifference l= (RangeDifference) myIter.fRange.get(myIter.fRange.size() - 1);
+ rightStart= changeRangeStart - f.fLeftStart + f.fRightStart;
+ rightEnd= changeRangeEnd - l.leftEnd() + l.rightEnd();
+ }
+
+ if (yourIter.getCount() == 0) { // only right changed
+ leftStart= changeRangeStart - last.ancestorEnd() + last.leftEnd();
+ leftEnd= changeRangeEnd - last.ancestorEnd() + last.leftEnd();
+ kind= RangeDifference.RIGHT;
+ } else {
+ RangeDifference f= (RangeDifference) yourIter.fRange.get(0);
+ RangeDifference l= (RangeDifference) yourIter.fRange.get(yourIter.fRange.size() - 1);
+ leftStart= changeRangeStart - f.fLeftStart + f.fRightStart;
+ leftEnd= changeRangeEnd - l.leftEnd() + l.rightEnd();
+ }
+
+ if (kind == RangeDifference.ERROR) { // overlapping change (conflict) -> compare the changed ranges
+ if (rangeSpansEqual(right, rightStart, rightEnd - rightStart, left, leftStart, leftEnd - leftStart))
+ kind= RangeDifference.ANCESTOR;
+ else
+ kind= RangeDifference.CONFLICT;
+ }
+ return new RangeDifference(kind, rightStart, rightEnd - rightStart, leftStart, leftEnd - leftStart, changeRangeStart, changeRangeEnd - changeRangeStart);
+ }
+
+ /**
+ * Tests if two ranges are equal
+ */
+ private static boolean rangesEqual(IRangeComparator a, int ai, IRangeComparator b, int bi) {
+ return a.rangesEqual(ai, b, bi);
+ }
+
+ /**
+ * Tests whether <code>right</code> and <code>left</left> changed in the same way
+ */
+ private static boolean rangeSpansEqual(IRangeComparator right, int rightStart, int rightLen, IRangeComparator left, int leftStart, int leftLen) {
+ if (rightLen == leftLen) {
+ int i= 0;
+ for (i= 0; i < rightLen; i++) {
+ if (!rangesEqual(right, rightStart + i, left, leftStart + i))
+ break;
+ }
+ if (i == rightLen)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Reverses the range differences
+ */
+ private static LinkedRangeDifference reverseDifferences(LinkedRangeDifference start) {
+ LinkedRangeDifference ep, behind, ahead;
+
+ ahead= start;
+ ep= null;
+ while (ahead != null) {
+ behind= ep;
+ ep= ahead;
+ ahead= ahead.getNext();
+ ep.setNext(behind);
+ }
+ return ep;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html
new file mode 100644
index 000000000..2e4ffaf2b
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html
@@ -0,0 +1,37 @@
+<!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 the differences between
+two or three sequences of comparable entities.
+<h2>
+Package Specification</h2>
+The class <tt>Differencer</tt> finds longest sequences of matching and
+non-matching comparable entities.
+<p>Clients must supply the input to the differencer as an implementation
+of the <tt>IRangeComparator</tt> interface.
+<br>An <tt>IRangeComparator</tt> breaks the input data into a sequence
+of entities and provides a method for comparing
+<br>one entity with the entity in another <tt>IRangeComparator</tt>.
+<br>For example, to compare two text documents and find longest common
+sequences
+<br>of matching and non-matching lines, the implementation of <tt>IRangeComparator
+</tt>must break the document
+<br>into lines and provide a method for testing whether two lines are considered
+equal.
+<p>The differencer returns the differences among these sequences as an
+array of <tt>RangeDifference</tt> objects.
+<br>Every single <tt>RangeDifference</tt> describes kind of difference
+(no change, change, addition, deletion)
+<br>and the corresponding ranges of the underlying comparable entities
+in the two or three inputs.
+<p>The algorithm used is an objectified version of one described in:
+<br><i>A File Comparison Program,</i> by Webb Miller and Eugene W. Myers,
+<br>Software Practice and Experience, Vol. 15, Nov. 1985.
+</body>
+</html>
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>
diff --git a/bundles/org.eclipse.compare/doc/hglegal.htm b/bundles/org.eclipse.compare/doc/hglegal.htm
new file mode 100644
index 000000000..5ac1c6333
--- /dev/null
+++ b/bundles/org.eclipse.compare/doc/hglegal.htm
@@ -0,0 +1,19 @@
+<!doctype html public "-//w3c//dtd html 3.2 final//en">
+<html>
+
+<head>
+<title>Legal Notices</title>
+</head>
+
+<body>
+
+<h3><a name="Notices">Notices</a></h3>
+
+<p>Licensed Materials - Property of IBM<br>
+WebSphere Studio Workbench<br>
+(c) Copyright IBM Corp. 2000 All Rights Reserved.</p>
+
+<p>US Government Users Restricted Rights - Use, duplication or disclosure restricted by
+GSA ADP Schedule Contract with IBM Corp.</p>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/doc/ngibmcpy.gif b/bundles/org.eclipse.compare/doc/ngibmcpy.gif
new file mode 100644
index 000000000..e0d777bf5
--- /dev/null
+++ b/bundles/org.eclipse.compare/doc/ngibmcpy.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/doc/org_eclipse_compare.html b/bundles/org.eclipse.compare/doc/org_eclipse_compare.html
new file mode 100644
index 000000000..ce9339857
--- /dev/null
+++ b/bundles/org.eclipse.compare/doc/org_eclipse_compare.html
@@ -0,0 +1,39 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Infrastructure Extension Points</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+IBM Eclipse Platform Compare</h1></center>
+This document lists all of the extension points that the compare plug-in
+makes available to tool developers.
+<p>All XML sub-elements defined in the individual extension point documents
+may appear more than once inside an extension element. For example, a <tt>org.eclipse.compare.contentMergeViewers</tt>
+extension may contain several instances of <tt>viewer</tt> elements. Although
+making one extension per sub-element is not technically incorrect, we recommend
+grouping for consistent style. It can also improve platform startup time
+because there will be fewer extensions to process.
+<br>
+<hr WIDTH="100%">
+<h1>
+Extension Points</h1>
+The following extension points can be used to extend the capabilities of
+the compare infrastructure:
+<ul>
+<li>
+<a href="org_eclipse_ui_compare_contentMergeViewers.html">org.eclipse.compare.contentMergeViewers</a></li>
+
+<li>
+<a href="org_eclipse_ui_compare_structureMergeViewers.html">org.eclipse.compare.structureMergeViewers</a></li>
+
+<li>
+<a href="org_eclipse_ui_compare_structureCreators.html">org.eclipse.compare.structureCreators</a></li>
+</ul>
+<a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html b/bundles/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html
new file mode 100644
index 000000000..cc382ae1d
--- /dev/null
+++ b/bundles/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html
@@ -0,0 +1,55 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Extension Points: contentMergeViewers</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+ContentMergeViewer</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.compare.contentMergeViewers
+<p><b><i>Description: </i></b>This extension point allows to plug in a
+compare/merge viewer for specific content types. The viewer must extend
+<tt>org.eclipse.jface.viewers.Viewer</tt>. However since viewers don't have
+a default constructor the extension point must implement a factory object
+for viewers: the interface <tt>org.eclipse.compare.IViewerCreator</tt>.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT viewer EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST viewer</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+<ul>
+<li>
+<b>id</b> - a unique identifier that can be used to reference the viewer</li>
+
+<li>
+<b>class</b> - name of a class that implements a factory for the content
+merge viewer and implements <tt>org.eclipse.compare.IViewerCreator</tt></li>
+
+<li>
+<b>extensions </b>- a comma separated list of file extensions e.g . "java,
+gif"</li>
+</ul>
+<b><i>Examples:</i></b>
+<br>The following is an example of compare/merge viewer for text files
+(extension "txt"):
+<p>&lt;extension point = "org.eclipse.compare.contentMergeViewers">
+<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;viewer
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="org.eclipse.compare.contentmergeviewer.TextMergeViewer"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.eclipse.compare.internal.TextMergeViewerCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions="txt"
+<br>&nbsp;&nbsp;&nbsp; />
+<br>&lt;/extension>
+<p><b><i>Supplied Implementation:</i></b>
+<br>The Compare UI plugin defines content viewers for text, binary contents,
+and images.
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html b/bundles/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html
new file mode 100644
index 000000000..6cef3b64c
--- /dev/null
+++ b/bundles/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html
@@ -0,0 +1,53 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Extension Points: structureCreators</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+StructureCreator</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.compare.structureCreators
+<p><b><i>Description: </i></b>This extension point allows to plug in an
+object that produces a tree structure of <tt>IStructureComparator</tt>
+for a given content.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT structureCreator EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST structureCreator</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+<ul>
+<li>
+<b>id</b> - a unique identifier that can be used to reference the structure
+creator</li>
+
+<li>
+<b>class</b> - name of a class that implements the strcuture creator and
+implements <tt>org.eclipse.compare.structuremergeviewer.IStructureComparator</tt></li>
+
+<li>
+<b>extensions </b>- a comma separated list of file extensions e.g . "java,
+properties"</li>
+</ul>
+<b><i>Examples:</i></b>
+<br>The following is an example of a structure creator for java files (extension
+"java"):
+<p>&lt;extension point = "org.eclipse.compare.structureCreators">
+<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;structureCreator
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="org.eclipse.compare.JavaStructureCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.eclipse.compare.JavaStructureCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions="java"
+<br>&nbsp;&nbsp;&nbsp; />
+<br>&lt;/extension>
+<p><b><i>Supplied Implementation:</i></b>
+<br>The Compare UI plugin defines a structure creator for zip archives.
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html b/bundles/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html
new file mode 100644
index 000000000..0de389610
--- /dev/null
+++ b/bundles/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html
@@ -0,0 +1,53 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Extension Points: structureMergeViewers</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+StructureMergeViewer</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.compare.structureMergeViewers
+<p><b><i>Description: </i></b>This extension point allows to plug in a
+compare/merge viewer for structured content. The viewer must extend <tt>org.eclipse.jface.viewers.Viewer</tt>.
+However since viewers don't have a default constructor the extension point
+must implement a factory object for viewers: the interface <tt>org.eclipse.compare.IViewerCreator</tt>.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT viewer EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST viewer</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+<ul>
+<li>
+<b>id</b> - a unique identifier that can be used to reference the viewer</li>
+
+<li>
+<b>class</b> - name of a class that implements a factory for the strcuture
+merge viewer and implements <tt>org.eclipse.compare.IViewerCreator</tt></li>
+
+<li>
+<b>extensions </b>- a comma separated list of file extensions e.g . "zip,
+jar"</li>
+</ul>
+<b><i>Examples:</i></b>
+<br>The following is an example of compare/merge viewer for zip files (extension
+"zip"):
+<p>&lt;extension point = "org.eclipse.compare.structureMergeViewers">
+<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;viewer
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="org.eclipse.compare.ZipCompareViewer"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.eclipse.compare.ZipCompareViewerCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions="zip"
+<br>&nbsp;&nbsp;&nbsp; />
+<br>&lt;/extension>
+<p><b><i>Supplied Implementation:</i></b>
+<br>The Compare UI plugin defines a structure compare viewer for zip archives.
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/accept.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/accept.gif
new file mode 100644
index 000000000..10942a5cc
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/accept.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/both.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/both.gif
new file mode 100644
index 000000000..3bab8be02
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/both.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/compare.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/compare.gif
new file mode 100644
index 000000000..122f6816b
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/compare.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/ignorews.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/ignorews.gif
new file mode 100644
index 000000000..be6976d8f
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/ignorews.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright.gif
new file mode 100644
index 000000000..ff4da3c8c
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gif
new file mode 100644
index 000000000..cd39a8b9d
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/next.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/next.gif
new file mode 100644
index 000000000..8b72b7f61
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/next.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/panes3.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/panes3.gif
new file mode 100644
index 000000000..06adb1dc4
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/panes3.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/prev.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/prev.gif
new file mode 100644
index 000000000..c2258ef6a
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/prev.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/reject.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/reject.gif
new file mode 100644
index 000000000..df18820a1
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/reject.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft.gif
new file mode 100644
index 000000000..307d69a42
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gif
new file mode 100644
index 000000000..c8d98a4d4
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gif
new file mode 100644
index 000000000..4ecd4b29c
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/smart.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/smart.gif
new file mode 100644
index 000000000..213bc4687
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/smart.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/synchmode.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/synchmode.gif
new file mode 100644
index 000000000..a0831f04b
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/synchmode.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ctool16/toggle.gif b/bundles/org.eclipse.compare/icons/basic/ctool16/toggle.gif
new file mode 100644
index 000000000..9db5644ab
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ctool16/toggle.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/obj16/compare.gif b/bundles/org.eclipse.compare/icons/basic/obj16/compare.gif
new file mode 100644
index 000000000..122f6816b
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/obj16/compare.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/obj16/date.gif b/bundles/org.eclipse.compare/icons/basic/obj16/date.gif
new file mode 100644
index 000000000..41744b7e4
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/obj16/date.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/obj16/time.gif b/bundles/org.eclipse.compare/icons/basic/obj16/time.gif
new file mode 100644
index 000000000..d52f5487f
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/obj16/time.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/add.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/add.gif
new file mode 100644
index 000000000..edeb956cd
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/confl_add.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/confl_add.gif
new file mode 100644
index 000000000..96862a348
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/confl_add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/confl_change.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/confl_change.gif
new file mode 100644
index 000000000..c0761e473
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/confl_change.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/confl_del.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/confl_del.gif
new file mode 100644
index 000000000..6e8d1f582
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/confl_del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/del.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/del.gif
new file mode 100644
index 000000000..0db04b13d
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/in_add.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/in_add.gif
new file mode 100644
index 000000000..9f17d268a
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/in_add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/in_change.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/in_change.gif
new file mode 100644
index 000000000..83a065c6c
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/in_change.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/in_del.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/in_del.gif
new file mode 100644
index 000000000..a3310e837
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/in_del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/out_add.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/out_add.gif
new file mode 100644
index 000000000..a40943e87
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/out_add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/out_change.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/out_change.gif
new file mode 100644
index 000000000..764fbf3bf
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/out_change.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/icons/basic/ovr16/out_del.gif b/bundles/org.eclipse.compare/icons/basic/ovr16/out_del.gif
new file mode 100644
index 000000000..b8c6a1b87
--- /dev/null
+++ b/bundles/org.eclipse.compare/icons/basic/ovr16/out_del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugin.jars b/bundles/org.eclipse.compare/plugin.jars
new file mode 100644
index 000000000..9bc88429f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugin.jars
@@ -0,0 +1 @@
+compare.jar=Eclipse Compare \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/plugin.properties b/bundles/org.eclipse.compare/plugin.properties
new file mode 100644
index 000000000..565934f55
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugin.properties
@@ -0,0 +1,9 @@
+CompareWithMenu.label=Compare With
+
+CompareWithEachOtherAction.label=Each other
+CompareWithEachOtherAction.tooltip=Compare the selected resources
+
+ReplaceWithMenu.label=Replace With
+
+ReplaceFromHistoryAction.label=Edition from local History...
+ReplaceFromHistoryAction.tooltip=Replace selected resource with edition from history
diff --git a/bundles/org.eclipse.compare/plugin.xml b/bundles/org.eclipse.compare/plugin.xml
new file mode 100644
index 000000000..389c27b9e
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugin.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0"?>
+
+<!-- ======================================================================= -->
+<!-- Compare UI Plugin -->
+<!-- ======================================================================= -->
+
+<plugin
+ name="Compare"
+ id="org.eclipse.compare"
+ version="1.0"
+ vendor-name="IBM"
+ class="org.eclipse.compare.internal.CompareUIPlugin">
+
+ <requires>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.core.resources"/>
+ </requires>
+
+ <runtime>
+ <library name="compare.jar" >
+ <export name="*"/>
+ </library>
+ </runtime>
+
+<!-- Compare extension point definitions -->
+
+ <extension-point
+ name="Structure Creator"
+ id="structureCreators"/>
+
+ <extension-point
+ name="Structure Merge Viewer"
+ id="structureMergeViewers"/>
+
+ <extension-point
+ name="Content Merge Viewer"
+ id="contentMergeViewers"/>
+
+ <extension-point
+ name="Content Viewer"
+ id="contentViewers"/>
+
+<!-- Extensions -->
+
+ <extension point="org.eclipse.ui.editors">
+ <editor id="org.eclipse.compare.CompareEditor"
+ name="Default Compare Editor"
+ icon="icons/basic/ctool16/compare.gif"
+ class="org.eclipse.compare.internal.CompareEditor"
+ contributorClass="org.eclipse.compare.internal.CompareEditorContributor">
+ </editor>
+ </extension>
+
+ <extension point="org.eclipse.ui.popupMenus">
+
+ <objectContribution
+ id="org.eclipse.compare.CompareAction"
+ objectClass="org.eclipse.core.resources.IResource">
+ <menu
+ id="replaceWithMenu"
+ path="additions"
+ label="%ReplaceWithMenu.label">
+ <separator name="replaceWithGroup"/>
+ </menu>
+ <menu
+ id="compareWithMenu"
+ path="additions"
+ label="%CompareWithMenu.label">
+ <separator name="compareWithGroup"/>
+ </menu>
+ <action
+ id="compareWithEachOther"
+ label="%CompareWithEachOtherAction.label"
+ tooltip="%CompareWithEachOtherAction.tooltip"
+ menubarPath="compareWithMenu/compareWithGroup"
+ enablesFor="2+"
+ class="org.eclipse.compare.internal.CompareAction">
+ </action>
+ </objectContribution>
+
+ <objectContribution
+ id="org.eclipse.compare.ReplaceWithEditionAction"
+ objectClass="org.eclipse.core.resources.IFile">
+ <action
+ id="replaceFromHistory"
+ label="%ReplaceFromHistoryAction.label"
+ tooltip="%ReplaceFromHistoryAction.tooltip"
+ menubarPath="replaceWithMenu/replaceWithGroup"
+ enablesFor="1"
+ class="org.eclipse.compare.internal.ReplaceWithEditionAction">
+ </action>
+ </objectContribution>
+
+ </extension>
+
+ <extension point="org.eclipse.compare.structureCreators">
+ <structureCreator
+ extensions="zip"
+ class="org.eclipse.compare.internal.ZipStructureCreator">
+ </structureCreator>
+ </extension>
+
+ <extension point="org.eclipse.compare.contentMergeViewers">
+ <viewer
+ extensions="binary"
+ class="org.eclipse.compare.internal.BinaryCompareViewerCreator">
+ </viewer>
+ <viewer
+ extensions="txt"
+ class="org.eclipse.compare.internal.TextMergeViewerCreator">
+ </viewer>
+ <viewer
+ extensions="gif,jpg"
+ class="org.eclipse.compare.internal.ImageMergeViewerCreator">
+ </viewer>
+ </extension>
+
+ <extension point="org.eclipse.compare.contentViewers">
+ <viewer
+ extensions="txt"
+ class="org.eclipse.compare.internal.TextViewerCreator">
+ </viewer>
+ </extension>
+
+</plugin>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/.cvsignore b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/.cvsignore
new file mode 100644
index 000000000..3f041525e
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/.cvsignore
@@ -0,0 +1,2 @@
+bin
+.classpath
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/.vcm_meta b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/.vcm_meta
new file mode 100644
index 000000000..aae2e1b3e
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/.vcm_meta
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-description>
+ <nature id="org.eclipse.jdt.core.javanature"/>
+ <builder name="org.eclipse.jdt.core.javabuilder">
+ </builder>
+</project-description>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/changes.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/changes.html
new file mode 100644
index 000000000..24e156e1f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/changes.html
@@ -0,0 +1,624 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <meta name="Author" content="IBM">
+ <title>Eclipse Platform Release Notes - Desktop</title>
+</head>
+<body>
+
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+SDK Build 102
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GCPBTE: ITPVCM:WINNT - Catchup/release shouldn't show structure on multi-selection
+<br>1GCP1DH: ITPJUI:WINNT - Double click on calendar dismisses replace
+dialog
+<br>1GCJBUT: ITPJUI:ALL - No indication when there are no editions available
+<br>1GCFU1D: ITPJUI:ALL - Compare: incorrect icon Replace Java Element
+Dialog
+<h2>
+Problem reports closed</h2>
+1G8BNIK: ITPJUI:WINNT - strange naming in compare view
+<p><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.048
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GCOZHF: ITPUI:WINNT - Error navigating stucture in release view
+<br>1GCOLNU: ITPUI:WINNT - Double clicking in compare editor is slow
+<br>1GCJWNF: ITPJUI:ALL - "no differences" message is not a sentence
+<br>1GCHN93: ITPUI:WINNT - Walkback in DiffTreeViewer
+<br>1G9UVEC: ITPJUI:WINNT - compare viewer: no syntax coloring
+<br>1GAOS9R: ITPUI:WINNT - Unable to cancel compare
+<br>1GBCZXK: ITPUI:WINNT - Failed assertion when java structure fails to
+parse
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.046
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GCDH47: ITPUI:ALL - Next and Prev buttons don't work in strcuture compare
+viewers
+<br>1GCBHKO: ITPUI:ALL - Illegal uses of FileImageDescriptor in Compare
+plugin
+<br>1GCDHNA: ITPUI:ALL - remove "..."&nbsp;&nbsp; from CompareWith and
+ReplaceWith ... cascade menus
+<br>1GCF6T6: ITPUI:ALL - Inconsistent contextmenu labels in TextMergeViewer
+<br>1GBYOQY: ITPUI:WIN2000 - Need to configure label in EditionSelectionDialog
+<br>1GCF905: ITPUI:ALL - EditionSelectionDialog must sort input array
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.044
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+1GBBL6K: ITPUI:ALL - SH: NullPointer in StructuredDiffViewer
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.043
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+Added new constructor taking a SWT style bits to ContentMergeViewer &amp;
+TextMergeViewer</li>
+
+<li>
+Added API to EditionSelectionDialog to control on which side of a compare
+viewer to show the workspace and editions.</li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+the javadoc in package.html files is incorrect.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+1GBPOEB: ITPJUI:WINNT - Layout trouble in the default diff editor
+<br>1GBPOZE: ITPJUI:WINNT - compare: synchronize panes button disappears
+<br>1GBWQ5V: ITPUI:WIN2000 - Infinite loop in BinaryCompareViewer
+<br>1GBWUJ8: ITPUI:WIN2000 - Compare editor has wrong title
+<br>1GBYJ41: ITPUI:WIN2000 - Need style bits for top level composite of
+TextMergeViewer
+<br>1GBYOJX: ITPUI:WIN2000 - Better name for ReplaceWithEditionDialog
+<br>1GB0P4S: ITPVCM:WINNT - Structure viewer on jar is brutal
+<br>1GBYONP: ITPUI:WIN2000 - Flipping sides of EditionSelectionDialog
+<br>1GBYOQY: ITPUI:WIN2000 - Need to configure label in EditionSelectionDialog
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.042
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+new method CompareEditorInput.setFocus</li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+Adapted to new workbench UI look.</li>
+
+<li>
+Removed VCM synch stuff</li>
+
+<li>
+Clarification how to do filtering in ResourceNode.createChild(...): if
+null is returned given child is not added to list of children.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+the javadoc in package.html files is incorrect.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+1GBPN75: ITPJUI:WINNT - Endless loop when copying diffs in diff editor
+<br>1GBM7QL: ITPJUI:WINNT - replace with catchup?
+<br>1GBMJ9I: ITPUI:WINNT - ResourceNode resource and children should be
+protected
+<br>1GBM3AQ: ITPUI:WIN2000 - Null as input for TextMergeViewer
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 0.040
+<h2>
+What's new in this drop</h2>
+Javadoc &amp; Incorporated API review.
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+too many</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+none</li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+none</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+lots</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 032
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+deprecated <tt>ViewerPane</tt>; use <tt>CompareViewerSwitchingPane</tt>
+instead</li>
+
+<li>
+deprecated <tt>ByteContentAccessor</tt> and <tt>IByteContentAccessor</tt>.
+Use <tt>IStreamContentAccessor </tt>instead<tt>.</tt></li>
+
+<li>
+deprecated <tt>IDiffParent</tt>; Use <tt>IDiffElement</tt> instead<tt>.</tt></li>
+
+<li>
+Moved more classes to internal package.</li>
+
+<li>
+removed byte[] argument from <tt>IEditable.replace</tt>. Replace only handles
+add, remove, and copy contents. For setting a contents use <tt>IEditable.setContents</tt>.</li>
+
+<li>
+Renamed <tt>IByteContentChangedListener</tt> to <tt>IContentChangedListener</tt>.</li>
+
+<li>
+removed methods <tt>addChangeListener</tt> and <tt>removeChangeListener</tt>
+from <tt>IByteContentAccessor</tt>. Use <tt>IContentChangedProvider</tt>
+instead.</li>
+
+<li>
+Deprecated <tt>IDiffConstants.INCOMING</tt>, <tt>IDiffConstants.OUTGOING</tt>.
+Use <tt>LEFT</tt> and <tt>RIGHT</tt> instead. The interpretation of LEFT
+and RIGHT (e.g. Incoming and Outgoing) is left to the client.</li>
+
+<li>
+Changed return type of&nbsp; <tt>DiffContainer.findChild(String name)</tt>
+from <tt>DiffContainer</tt> to <tt>IDiffElement</tt>.</li>
+
+<li>
+Changed return type of <tt>IDiffContainer.getChildren</tt> from <tt>Iterator</tt>
+to <tt>Object[]</tt>.</li>
+
+<br>&nbsp;</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+&nbsp;added method <tt>ResourceNode.createChild(IResource child)</tt></li>
+
+<br>(can be used to filter children)
+<li>
+added method <tt>setContents(byte[] ...)</tt> to <tt>IEditable</tt></li>
+
+<li>
+added interface <tt>IContentChangedProvider.</tt></li>
+
+<br>&nbsp;</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+Panes within compare editor can be resized</li>
+
+<li>
+Replace from history buffer.</li>
+
+<li>
+Support for method level editions.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+lots</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<ul>
+<li>
+yes!</li>
+</ul>
+
+<h2>
+Problem reports closed</h2>
+
+<ul>
+<li>
+not yet</li>
+</ul>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 027
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+deprecated <tt>ViewerPane</tt>; use <tt>deprectCompareViewerSwitchingPane</tt>
+instead</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 026
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+changed <tt>IViewer</tt> to <tt>ICompareViewer</tt></li>
+
+<li>
+renamed <tt>AbstractViewer</tt> to <tt>AbstractCompareViewer</tt></li>
+
+<li>
+removed interface <tt>IDocumentRange</tt></li>
+
+<li>
+removed class <tt>HistoryCompareOp</tt> (Use the new command "Replace with
+edition") instead</li>
+
+<li>
+removed the history mechanism of <tt>ResourceNode</tt>. Use the new class
+<tt>HistoryItem</tt>
+instead.</li>
+
+<li>
+removed <tt>NullViewer</tt> from API.</li>
+
+<li>
+new method <tt>IEditable.isEditable()</tt></li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+first cut of new <tt>ReplaceWithActionAction</tt></li>
+
+<li>
+new class <tt>HistoryItem</tt></li>
+
+<li>
+new constructor: <tt>DiffNode(ITypedInput left, ITypedInput right)</tt></li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+first cut of "Replace with edition". A dialog is there but pressing "OK"
+has no effect.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+correct focus/activation handling still waiting for SWT focus/activation
+fix.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+1G8FFQ7: ITPUI:WIN2000 - Walkback
+<br>1G8BRDW: ITPUI:ALL - Reference to deprecated DesktopPlugin
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 022
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+changed visibility of <tt>ByteContentAccessor.loadContent()</tt> from public
+to protected</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+differencing engine (<tt>Differencer</tt>) supports <tt>IProgressMonitor</tt></li>
+</ul>
+
+<h3>
+Other highlights</h3>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+correct focus/activation handling is still broken.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+<h1>
+Release Notes Compare Plugin &amp; Compare Example Plugin</h1>
+Build 021
+<h2>
+What's new in this drop</h2>
+
+<h3>
+API changes</h3>
+
+<ul>
+<li>
+Harmonized usage of the compare directions: Mine/Yours/Your, Incoming/Outgoing,
+Left/Right. Everywhere <b>Left/Right </b>is used.</li>
+
+<br>API changes: ContentMergeViewer, ImageMergeViewer, TextMergeViewer,
+IMergeViewerContentProvider, MergeViewerContentProvider, ICompareConfiguration,
+CompareConfiguration, IThreeWayInput, StructureDiffViewer, DiffNode, DiffTreeViewer
+<li>
+changed signature of <tt>IStructureCreator.save</tt> from <tt>void save(Object
+input, IDocument document)</tt> to <tt>void save(Object input, IStructureComparator
+structure)</tt></li>
+
+<li>
+made <tt>com.ibm.eclipse.ui.compare.structuremergeviewer.ArrayIterator</tt>
+package private.</li>
+</ul>
+
+<h3>
+API Additions</h3>
+
+<ul>
+<li>
+Added new classes <tt>CompareOp</tt>, <tt>ResourceCompareOp, HistoryCompareOp</tt></li>
+
+<br>All comparison operations can now be written as subclasses of <tt>CompareOp</tt>.
+<tt>CompareUIPlugin.runCompareOp()</tt>
+generically opens a <tt>CompareEditor</tt> for it. <tt>ResourceCompareOp</tt>
+implements a universal two/threeway compare on desktop resources. <tt>HistoryCompareOp</tt>
+compares a resource with its most recent edition from the history buffer.
+Another example would be a <tt>RepositoryCompareOp</tt>.</ul>
+
+<h3>
+Other highlights</h3>
+
+<ul>
+<li>
+background coloring in the TextMergeViewer no longer depend on StyledText
+background coloring support.</li>
+
+<li>
+generic <i>copy-left-to-right</i> and <i>copy-right-to-left</i> action
+now work in second and third structure panes.</li>
+
+<br>In the resource pane they just print a message to the console but don't
+do anything.
+<li>
+Progress is shown while a compare runs.</li>
+
+<li>
+Support for comparing jpegs.</li>
+
+<li>
+if resources with unknown types are compared the Compare Plugin tries to
+guess whether they contain text and whether the TextMergeViewer can be
+used.</li>
+</ul>
+
+<h2>
+Known deficiencies</h2>
+
+<ul>
+<li>
+correct focus/activation handling is still broken.</li>
+</ul>
+
+<h2>
+Problem reports fixed</h2>
+
+<h2>
+Problem reports closed</h2>
+
+<p><br><i><font size=-1>(c) Copyright IBM Corp. 2001&nbsp; All Rights Reserved.</font></i>
+<br>&nbsp;
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java
new file mode 100644
index 000000000..9834071bd
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/BufferedContent.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.io.*;
+import org.eclipse.jface.util.ListenerList;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.internal.Utilities;
+
+/**
+ * Abstract implementation for a buffered <code>IStreamContentAccessor</code>.
+ * <p>
+ * Subclasses must implement the <code>createStream</code> method
+ * to connect the buffered content with a streamable source (e.g., a file).
+ * <p>
+ * As long as the contents of <code>BufferedContent</code> is only retrieved as an input stream
+ * (by means of <code>getContents</code>) and the <code>BufferedContent</code> is not modified (with
+ * <code>setContent</code>) no buffering takes place.
+ * Buffering starts when either methods <code>getContent</code> or <code>setContent</code> is called.
+ *
+ * @see IContentChangeNotifier
+ * @see IStreamContentAccessor
+ */
+public abstract class BufferedContent implements IContentChangeNotifier, IStreamContentAccessor {
+
+ byte[] fContent;
+ private ListenerList fListenerList;
+
+ /**
+ * Creates a buffered stream content accessor.
+ */
+ protected BufferedContent() {
+ }
+
+ /* (non-Javadoc)
+ * see IStreamContentAccessor.getContents
+ */
+ public InputStream getContents() throws CoreException {
+ if (fContent != null)
+ return new ByteArrayInputStream(fContent);
+ return createStream();
+ }
+
+ /**
+ * Creates and returns a stream for reading the contents.
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ *
+ * @return the stream from which the content is read
+ * @exception CoreException if the contents could not be accessed
+ */
+ protected abstract InputStream createStream() throws CoreException;
+
+ /**
+ * Sets the contents. Registered content change listeners are notified.
+ *
+ * @param contents the new contents
+ */
+ public void setContent(byte[] contents) {
+ fContent= contents;
+ fireContentChanged();
+ }
+
+ /**
+ * Returns the contents as an array of bytes.
+ *
+ * @return the contents as an array of bytes, or <code>null</code> if
+ * the contents could not be accessed
+ */
+ public byte[] getContent() {
+ if (fContent == null) {
+ try {
+ InputStream is= createStream();
+ fContent= Utilities.readBytes(is);
+ } catch(CoreException ex) {
+ }
+ }
+ return fContent;
+ }
+
+ /**
+ * Discards the buffered content.
+ */
+ public void discardBuffer() {
+ fContent= null;
+ //fireContentChanged();
+ }
+
+ /* (non-Javadoc)
+ * see IContentChangeNotifier.addChangeListener
+ */
+ public void addContentChangeListener(IContentChangeListener listener) {
+ if (fListenerList == null)
+ fListenerList= new ListenerList();
+ fListenerList.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * see IContentChangeNotifier.removeChangeListener
+ */
+ public void removeContentChangeListener(IContentChangeListener listener) {
+ if (fListenerList != null) {
+ fListenerList.remove(listener);
+ if (fListenerList.isEmpty())
+ fListenerList= null;
+ }
+ }
+
+ /**
+ * Notifies all registered <code>IContentChangeListener</code>s of a content change.
+ */
+ protected void fireContentChanged() {
+ if (fListenerList != null) {
+ Object[] listeners= fListenerList.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((IContentChangeListener)listeners[i]).contentChanged(this);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java
new file mode 100644
index 000000000..58218dd1f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareConfiguration.java
@@ -0,0 +1,360 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.util.HashMap;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.*;
+
+import org.eclipse.compare.internal.DiffImage;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.structuremergeviewer.DiffTreeViewer;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+
+/**
+ * A <code>CompareConfiguration</code> object
+ * controls various UI aspects of compare/merge viewers like
+ * title labels and images, or whether a side of a merge viewer is editable.
+ * In addition to these fixed properties <code>ICompareConfiguration</code> provides
+ * API for an open ended set of properties. Different viewers which share the same
+ * configuration can communicate via this mechanism. E.g. if a compare editor
+ * has a button for controlling whether compare viewers ignore white space,
+ * the button would trigger a change of the boolean <code>IGNORE_WHITESPACE</code> property
+ * and all interested viewers would receive notification.
+ * <p>
+ * Suitable default labels are provided (without images); both the left and right sides
+ * are editable.
+ * </p>
+ * <p>
+ * Clients may use this class as is, or subclass to add new state and behavior.
+ * </p>
+ */
+public class CompareConfiguration {
+
+ /**
+ * Name of the ignore whitespace property.
+ */
+ public static final String IGNORE_WHITESPACE= "IGNORE_WHITESPACE";
+ /**
+ * Name of the show pseudo conflicts property.
+ */
+ public static final String SHOW_PSEUDO_CONFLICTS= "SHOW_PSEUDO_CONFLICTS";
+
+
+ private static final int WIDTH= 22;
+
+ private static ImageDescriptor[] fgImages= new ImageDescriptor[16];
+ private static Object fgDummy= new Object();
+ private static HashMap fgMap= new HashMap(20);
+
+ static {
+ int INCOMING= Differencer.LEFT;
+ int OUTGOING= Differencer.RIGHT;
+
+ fgImages[Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/add.gif");
+ fgImages[INCOMING + Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/in_add.gif");
+ fgImages[OUTGOING + Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/out_add.gif");
+ fgImages[Differencer.CONFLICTING + Differencer.ADDITION]= CompareUIPlugin.getImageDescriptor("ovr16/confl_add.gif");
+
+ fgImages[Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/del.gif");
+ fgImages[INCOMING + Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/in_del.gif");
+ fgImages[OUTGOING + Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/out_del.gif");
+ fgImages[Differencer.CONFLICTING + Differencer.DELETION]= CompareUIPlugin.getImageDescriptor("ovr16/confl_del.gif");
+
+ fgImages[INCOMING + Differencer.CHANGE]= CompareUIPlugin.getImageDescriptor("ovr16/in_change.gif");
+ fgImages[OUTGOING + Differencer.CHANGE]= CompareUIPlugin.getImageDescriptor("ovr16/out_change.gif");
+ fgImages[Differencer.CONFLICTING + Differencer.CHANGE]= CompareUIPlugin.getImageDescriptor("ovr16/confl_change.gif");
+ }
+
+ private ListenerList fListeners= new ListenerList();
+ private HashMap fProperties= new HashMap();
+ private boolean fLeftEditable= true;
+ private boolean fRightEditable= true;
+ private String fAncestorLabel= "Ancestor";
+ private String fLeftLabel= "Left";
+ private String fRightLabel= "Right";
+ private Image fAncestorImage;
+ private Image fRightImage;
+ private Image fLeftImage;
+
+ /**
+ * Creates a new configuration with editable left and right sides,
+ * suitable default labels, and no images.
+ */
+ public CompareConfiguration() {
+ }
+
+ /**
+ * Returns an image showing the specified change kind applied to a
+ * given base image. The different kind of changes are defined in the <code>Differencer</code>.
+ * Typically an implementation would build a composite image
+ * from the given base image and an image representing the change kind.
+ * Newly created images are remembered by this class and
+ * disposed when the <code>dispose</code> method is called.
+ *
+ * @param base the image which is modified to reflect the kind of change
+ * @param kind the kind of change as defined in <code>Differencer</code>.
+ * @return an modification of the base image reflecting the kind of change.
+ * @see org.eclipse.compare.structuremergeviewer.Differencer
+ */
+ public Image getImage(Image base, int kind) {
+
+ Object key= base;
+ if (key == null)
+ key= fgDummy;
+
+ kind &= 15;
+
+ Image[] a= (Image[]) fgMap.get(key);
+ if (a == null) {
+ a= new Image[16];
+ fgMap.put(key, a);
+ }
+ Image b= a[kind];
+ if (b == null) {
+ b= new DiffImage(base, fgImages[kind], WIDTH).createImage();
+ CompareUI.disposeOnShutdown(b);
+ a[kind]= b;
+ }
+ return b;
+ }
+
+ /**
+ * Dispose of this compare configuration.
+ * This method is called if the compare configuration is no longer used.
+ * An implementation must dispose of all resources.
+ */
+ public void dispose() {
+ }
+
+ /**
+ * Fires a <code>PropertyChangeEvent</code> to registered listeners.
+ *
+ * @param propertyName the name of the property that has changed
+ * @param oldValue the property's old value
+ * @param newValue the property's new value
+ */
+ private void fireChange(String propertyName, Object oldValue, Object newValue) {
+ PropertyChangeEvent event= null;
+ Object[] listeners= fListeners.getListeners();
+ if (listeners != null) {
+ for (int i= 0; i < listeners.length; i++) {
+ IPropertyChangeListener l= (IPropertyChangeListener) listeners[i];
+ if (event == null)
+ event= new PropertyChangeEvent(this, propertyName, oldValue, newValue);
+ l.propertyChange(event);
+ }
+ }
+ }
+
+ /* (non javadoc)
+ * see IPropertyChangeNotifier.addListener
+ */
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ fListeners.add(listener);
+ }
+
+ /* (non javadoc)
+ * see IPropertyChangeNotifier.removeListener
+ */
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ fListeners.remove(listener);
+ }
+
+ /**
+ * Sets the property with the given name.
+ * If the new value differs from the old a <code>PropertyChangeEvent</code>
+ * is sent to registered listeners.
+ *
+ * @param propertyName the name of the property to set
+ * @param value the new value of the property
+ */
+ public void setProperty(String key, Object newValue) {
+ Object oldValue= fProperties.get(key);
+ fProperties.put(key, newValue);
+ if (oldValue == null || !oldValue.equals(newValue))
+ fireChange(key, oldValue, newValue);
+ }
+
+ /**
+ * Returns the property with the given name, or <code>null</code>
+ * if no such property exists.
+ *
+ * @param propertyName the name of the property to retrieve
+ * @return the property with the given name, or <code>null</code> if not found
+ */
+ public Object getProperty(String key) {
+ return fProperties.get(key);
+ }
+
+ //---- ancestor
+
+ /**
+ * Sets the label to use for the ancestor of compare/merge viewers.
+ *
+ * @param label the new label for the ancestor of compare/merge viewers
+ */
+ public void setAncestorLabel(String label) {
+ fAncestorLabel= label;
+ }
+
+ /**
+ * Returns the label for the ancestor side of compare/merge viewers.
+ * This label is typically shown in the title of the ancestor area in a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the label for the ancestor side or <code>null</code>
+ */
+ public String getAncestorLabel(Object element) {
+ return fAncestorLabel;
+ }
+
+ /**
+ * Sets the image to use for the ancestor of compare/merge viewers.
+ * The CompareConfiguration does not automatically dispose the old image.
+ *
+ * @param image the new image for the ancestor of compare/merge viewers
+ */
+ public void setAncestorImage(Image image) {
+ fAncestorImage= image;
+ }
+
+ /**
+ * Returns the image for the ancestor side of compare/merge viewers.
+ * This image is typically shown in the title of the ancestor area in a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the image for the ancestor side or <code>null</code>
+ */
+ public Image getAncestorImage(Object element) {
+ return fAncestorImage;
+ }
+
+ //---- left side
+
+ /**
+ * Controls whether the left side of a merge viewer is editable.
+ *
+ * @param editable if the value is <code>true</code> left side is editable
+ */
+ public void setLeftEditable(boolean editable) {
+ fLeftEditable= editable;
+ }
+
+ /**
+ * Returns whether the left hand side of a merge viewer is editable.
+ *
+ * @return <code>true</code> if the left hand side is editable
+ */
+ public boolean isLeftEditable() {
+ return fLeftEditable;
+ }
+
+ /**
+ * Sets the label to use for the left side of compare/merge viewers.
+ *
+ * @param label the new label for the left side of compare/merge viewers
+ */
+ public void setLeftLabel(String label) {
+ fLeftLabel= label;
+ }
+
+ /**
+ * Returns the label for the left hand side of compare/merge viewers.
+ * This label is typically shown in the title of the left side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the label for the left hand side or <code>null</code>
+ */
+ public String getLeftLabel(Object element) {
+ return fLeftLabel;
+ }
+
+ /**
+ * Sets the image to use for the left side of compare/merge viewers.
+ * The compare configuration does not automatically dispose the old image.
+ *
+ * @param image the new image for the left side of compare/merge viewers
+ */
+ public void setLeftImage(Image image) {
+ fLeftImage= image;
+ }
+
+ /**
+ * Returns the image for the left hand side of compare/merge viewers.
+ * This image is typically shown in the title of the left side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the image for the left hand side or <code>null</code>
+ */
+ public Image getLeftImage(Object element) {
+ return fLeftImage;
+ }
+
+ //---- right side
+
+ /**
+ * Controls whether the right side of a merge viewer is editable.
+ *
+ * @param editable if the value is <code>true</code> right side is editable
+ */
+ public void setRightEditable(boolean editable) {
+ fRightEditable= editable;
+ }
+
+ /**
+ * Returns whether the right hand side of a merge viewer is editable.
+ *
+ * @return <code>true</code> if the right hand side is editable
+ */
+ public boolean isRightEditable() {
+ return fRightEditable;
+ }
+
+ /**
+ * Sets the label to use for the right side of compare/merge viewers.
+ *
+ * @param label the new label for the right side of compare/merge viewers
+ */
+ public void setRightLabel(String label) {
+ fRightLabel= label;
+ }
+
+ /**
+ * Returns the label for the right hand side of compare/merge viewers.
+ * This label is typically shown in the title of the right side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the label for the right hand side or <code>null</code>
+ */
+ public String getRightLabel(Object element) {
+ return fRightLabel;
+ }
+
+ /**
+ * Sets the image to use for the right side of compare/merge viewers.
+ * The compare configuration does not automatically dispose the old image.
+ *
+ * @param image the new image for the right side of compare/merge viewers
+ */
+ public void setRightImage(Image image) {
+ fRightImage= image;
+ }
+
+ /**
+ * Returns the image for the right hand side of compare/merge viewers.
+ * This image is typically shown in the title of the right side of a compare viewer.
+ *
+ * @param element the input object of a compare/merge viewer or <code>null</code>
+ * @return the image for the right hand side or <code>null</code>
+ */
+ public Image getRightImage(Object element) {
+ return fRightImage;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
new file mode 100644
index 000000000..2d3cd5754
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
@@ -0,0 +1,623 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.lang.reflect.InvocationTargetException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.IPersistableElement;
+import org.eclipse.ui.IEditorInput;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.compare.internal.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+
+/**
+ * A compare operation which can present its results in a special editor.
+ * Running the compare operation and presentating the results in a compare editor
+ * are combined in one interface because it allows a client to keep the implementation
+ * all in one place while separating it from the innards of a specific UI implementation of compare/merge.
+ * <p>
+ * A <code>CompareEditorInput</code> defines methods for the following sequence steps:
+ * <UL>
+ * <LI>running a lengthy compare operation under progress monitor control,
+ * <LI>creating a UI for displaying the model and initializing the some widgets with the compare result,
+ * <LI>tracking the dirty state of the model in case of merge,
+ * <LI>saving the model.
+ * </UL>
+ * The Compare plug-in's <code>openCompareEditor</code> method takes an <code>ICompareEditorInput</code>
+ * and starts sequencing through the above steps. If the compare result is not empty a new compare editor
+ * is opened and takes over the sequence until eventually closed.
+ * <p>
+ * The <code>prepareInput</code> method should contain the
+ * code of the compare operation. It is executed under control of a progress monitor
+ * and can be canceled. If the result of the compare is not empty, that is if there are differences
+ * that needs to be presented, the <code>ICompareEditorInput</code> should hold onto them and return them with
+ * the <code>getCompareResult</code> method.
+ * If the value returned from <code>getCompareResult</code> is not <code>null</code>
+ * a compare editor is opened on the <code>ICompareEditorInput</code> with title and title image initialized by the
+ * corresponding methods of the <code>ICompareEditorInput</code>.
+ * <p>
+ * Creation of the editor's SWT controls is delegated to the <code>createContents</code> method.
+ * Here the SWT controls must be created and initialized with the result of the compare operation.
+ * <p>
+ * If merging is allowed, the modification state of the compared constituents must be tracked and the dirty
+ * state returned from method <code>isSaveNeeded</code>. The value <code>true</code> triggers a subsequent call
+ * to <code>save</code> where the modified resources can be saved.
+ * <p>
+ * The most important part of this implementation is the setup of the compare/merge UI.
+ * The UI uses a simple browser metaphor to present compare results.
+ * The top half of the layout shows the structural compare results (e.g. added, deleted, and changed files),
+ * the bottom half the content compare results (e.g. textual differences between two files).
+ * A selection in the top pane is fed to the bottom pane. If a content viewer is registered
+ * for the type of the selected object, this viewer is installed in the pane.
+ * In addition if a structure viewer is registered for the selection type the top pane
+ * is split horizontally to make room for another pane and the structure viewer is installed
+ * in it. When comparing Java files this second structure viewer would show the structural
+ * differences within a Java file, e.g. added, deleted or changed methods and fields.
+ * <p>
+ * Subclasses provide custom setups, e.g. for a Catchup/Release operation
+ * by passing a subclass of <code>CompareConfiguration</code> and by implementing the <code>prepareInput</code> method.
+ * If a subclass cannot use the <code>DiffTreeViewer</code> which is installed by default in the
+ * top left pane, method <code>createDiffViewer</code> can be overridden.
+ *
+ * @see CompareUI
+ * @see CompareEditorInput
+ */
+public abstract class CompareEditorInput implements IEditorInput, IPropertyChangeNotifier, IRunnableWithProgress {
+
+ /**
+ * The name of the "dirty" property.
+ */
+ public static final String DIRTY_STATE= "DIRTY_STATE";
+
+ private static boolean PR1GB0P4S= true;
+
+ private static Image fgTitleImage;
+
+ private Splitter fComposite;
+ private CompareConfiguration fCompareConfiguration;
+ private CompareViewerSwitchingPane fStructureInputPane;
+ private CompareViewerSwitchingPane fStructurePane1;
+ private CompareViewerSwitchingPane fStructurePane2;
+ private CompareViewerSwitchingPane fContentInputPane;
+ private CompareViewerSwitchingPane fFocusPane;
+ private String fMessage;
+ private ISelection fSelection2;
+ private Object fInput;
+ private String fTitle= "Compare";
+ private ListenerList fListenerList= new ListenerList();
+
+ private boolean fDirty= false;
+ private IPropertyChangeListener fDirtyStateListener;
+
+ /**
+ * Creates a <code>CompareEditorInput</code> which is initialized with the given
+ * compare configuration.
+ * The compare configuration is passed to subsequently created viewers.
+ *
+ * @param configuration the compare configuration
+ */
+ public CompareEditorInput(CompareConfiguration configuration) {
+ fCompareConfiguration= configuration;
+ Assert.isNotNull(configuration);
+
+ fDirtyStateListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent e) {
+ if (Utilities.getValue(e, false))
+ setDirty(true);
+ }
+ };
+ }
+
+ /* (non Javadoc)
+ * see IAdaptable.getAdapter
+ */
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IEditorInput.getImageDescriptor
+ */
+ public ImageDescriptor getImageDescriptor() {
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IEditorInput.getToolTipText
+ */
+ public String getToolTipText() {
+ return fTitle;
+ }
+
+ /* (non Javadoc)
+ * see IEditorInput.getName
+ */
+ public String getName() {
+ return fTitle;
+ }
+
+ /**
+ * Returns <code>null</code> since this editor cannot be persisted.
+ *
+ * @return <code>null</code> because this editor cannot be persisted
+ */
+ public IPersistableElement getPersistable() {
+ return null;
+ }
+
+ /**
+ * Returns <code>false</code> to indicate that this input
+ * should not appear in the "File Most Recently Used" menu.
+ *
+ * @return <code>false</code>
+ */
+ public boolean exists() {
+ return false;
+ }
+
+ /*
+ * FIXME!
+ */
+ protected void setMessage(String message) {
+ fMessage= message;
+ }
+
+ /*
+ * FIXME!
+ */
+ public String getMessage() {
+ return fMessage;
+ }
+
+ /**
+ * Returns the title which will be used in the compare editor's title bar.
+ * It can be set with <code>setTitle</code>.
+ *
+ * @return the title
+ */
+ public String getTitle() {
+ return fTitle;
+ }
+
+ /**
+ * Sets the title which will be used when presenting the compare result.
+ * This method must be called before the editor is opened.
+ *
+ * @param title the title to use for the CompareEditor
+ */
+ public void setTitle(String title) {
+ fTitle= title;
+ }
+
+ /**
+ * Returns the title image which will be used in the compare editor's title bar.
+ * Returns the title image which will be used when presenting the compare result.
+ * This implementation returns a generic compare icon.
+ * Subclasses can override.
+ *
+ * @return the title image, or <code>null</code> if none
+ */
+ public Image getTitleImage() {
+ if (fgTitleImage == null) {
+ fgTitleImage= CompareUIPlugin.getImageDescriptor("ctool16/compare.gif").createImage();
+ CompareUI.disposeOnShutdown(fgTitleImage);
+ }
+ return fgTitleImage;
+ }
+
+ /**
+ * Returns the configuration object for the viewers within the compare editor.
+ * Returns the configuration which was passed to the constructor.
+ *
+ * @return the compare configuration
+ */
+ public CompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * Adds standard actions to the given <code>ToolBarManager</code>.
+ * <p>
+ * Subclasses may override to add their own actions.
+ * </p>
+ *
+ * @param toolBarManager the <code>ToolBarManager</code> to which to contribute
+ */
+ public void contributeToToolBar(ToolBarManager toolBarManager) {
+
+ ResourceBundle bundle= CompareUIPlugin.getResourceBundle();
+
+ toolBarManager.add(new Separator());
+ toolBarManager.add(new IgnoreWhiteSpaceAction(bundle, fCompareConfiguration));
+ toolBarManager.add(new ShowPseudoConflicts(bundle, fCompareConfiguration));
+ }
+
+ /**
+ * Runs the compare operation and stores the compare result.
+ *
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancelation
+ * @exception InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception,
+ * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
+ * wrapped in an <code>InvocationTargetException</code> by the calling context
+ * @exception InterruptedException if the operation detects a request to cancel,
+ * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
+ * <code>InterruptedException</code>
+ */
+ public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException {
+ fInput= prepareInput(monitor);
+ }
+
+ /**
+ * Runs the compare operation and returns the compare result.
+ * If <code>null</code> is returned no differences were found and no compare editor needs to be opened.
+ * Progress should be reported to the given progress monitor.
+ * A request to cancel the operation should be honored and acknowledged
+ * by throwing <code>InterruptedException</code>.
+ *
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancelation
+ * @return the result of the compare operation, or <code>null</code> if there are no differences
+ * @exception InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception,
+ * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
+ * wrapped in an <code>InvocationTargetException</code> by the calling context
+ * @exception InterruptedException if the operation detects a request to cancel,
+ * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
+ * <code>InterruptedException</code>
+ */
+ protected abstract Object prepareInput(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException;
+
+ /**
+ * Returns the compare result computed by the most recent call to the
+ * <code>run</code> method. Returns <code>null</code> if no
+ * differences were found.
+ *
+ * @return the compare result prepared in method <code>prepareInput</code>
+ * or <code>null</code> if there were no differences
+ */
+ public Object getCompareResult() {
+ return fInput;
+ }
+
+ /**
+ * Create the SWT controls that are used to display the result of the compare operation.
+ * Creates the SWT Controls and sets up the wiring between the individual panes.
+ * This implementation creates all four panes but makes only the necessary ones visible.
+ * Finally it feeds the compare result into the top left structure viewer
+ * and the content viewer.
+ * <p>
+ * Subclasses may override if they need to change the layout or wiring between panes.
+ *
+ * @param parent the parent control under which the control must be created
+ * @return the SWT control hierarchy for the compare editor
+ */
+ public Control createContents(Composite parent) {
+
+ fComposite= new Splitter(parent, SWT.VERTICAL);
+
+ final Splitter h= new Splitter(fComposite, SWT.HORIZONTAL);
+
+ fStructureInputPane= new CompareViewerSwitchingPane(h, SWT.NONE, true) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ if (input instanceof DiffNode) {
+ DiffNode dn= (DiffNode) input;
+ if (dn.hasChildren())
+ return createDiffViewer(this);
+ }
+ return findStructureViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+
+ fStructurePane1= new CompareViewerSwitchingPane(h, SWT.NONE, true) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return findStructureViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+ h.setVisible(fStructurePane1, false);
+
+ fStructurePane2= new CompareViewerSwitchingPane(h, SWT.NONE, true) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return findStructureViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+ h.setVisible(fStructurePane2, false);
+
+ fContentInputPane= new CompareViewerSwitchingPane(fComposite, SWT.NONE) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return findContentViewer(oldViewer, (ICompareInput)input, this);
+ }
+ };
+ fComposite.setVisible(h, false);
+ fComposite.setVisible(fContentInputPane, true);
+
+ fComposite.setWeights(new int[] { 30, 70 });
+
+ fComposite.layout();
+
+ // setup the wiring (no focus wiring yet)
+ fStructureInputPane.addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent e) {
+ ISelection s= e.getSelection();
+ Object input= null;
+ if (s.isEmpty()) {
+ input= fStructureInputPane.getInput();
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(null); // clear downstream pane
+ fStructurePane1.setInput(null);
+ } else {
+ Object o= getElement(e.getSelection());
+ if (o != null /*o instanceof ICompareInput*/)
+ input= (ICompareInput) o;
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(null); // clear downstream pane
+
+ if (PR1GB0P4S) {
+ if (fStructurePane1.getInput() != input)
+ fStructurePane1.setInput(null);
+ } else
+ fStructurePane1.setInput(input);
+ }
+ }
+ }
+ );
+
+ if (PR1GB0P4S) {
+ fStructureInputPane.addDoubleClickListener(
+ new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent e) {
+ ISelection s= e.getSelection();
+ ICompareInput input= null;
+ if (!s.isEmpty()) {
+ Object o= getElement(e.getSelection());
+ if (o instanceof ICompareInput)
+ input= (ICompareInput) o;
+ fStructurePane1.setInput(input);
+ }
+ }
+ }
+ );
+ }
+
+ fStructurePane1.addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent e) {
+ ISelection s= e.getSelection();
+ Object input= null;
+ if (s.isEmpty()) {
+ input= fStructurePane1.getInput();
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(null);
+ } else {
+ Object o= getElement(e.getSelection());
+ if (o != null /* o instanceof ICompareInput */)
+ input= (ICompareInput) o;
+ fContentInputPane.setInput(input);
+ fStructurePane2.setInput(input);
+ }
+ }
+ }
+ );
+
+ fStructurePane2.addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent e) {
+ ISelection s= e.getSelection();
+ Object input= null;
+ if (s.isEmpty()) {
+ input= fStructurePane2.getInput();
+ } else {
+ Object o= getElement(e.getSelection());
+ if (o != null /* o instanceof ICompareInput */)
+ input= (ICompareInput) o;
+ }
+ fContentInputPane.setInput(input);
+ }
+ }
+ );
+
+ // now deal with activation
+ Listener activationListener= new Listener() {
+ public void handleEvent(Event event) {
+ if (event.widget instanceof CompareViewerSwitchingPane) {
+ fFocusPane= (CompareViewerSwitchingPane) event.widget;
+ }
+ }
+ };
+ fStructureInputPane.addListener(SWT.Activate, activationListener);
+ fStructurePane1.addListener(SWT.Activate, activationListener);
+ fStructurePane2.addListener(SWT.Activate, activationListener);
+ fContentInputPane.addListener(SWT.Activate, activationListener);
+
+ if (fInput instanceof ICompareInput) {
+ ICompareInput input2= (ICompareInput) fInput;
+ fStructureInputPane.setInput(input2);
+ fContentInputPane.setInput(input2);
+ }
+
+ return fComposite;
+ }
+
+ /**
+ * Returns the first element of the given selection if the selection
+ * is a <code>IStructuredSelection</code> with exactly one element. Returns
+ * <code>null</code> otherwise.
+ *
+ * @param selection the selection
+ * @return the first element of the selection, or <code>null</code>
+ */
+ private static Object getElement(ISelection selection) {
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss= (IStructuredSelection) selection;
+ if (ss.size() == 1)
+ return ss.getFirstElement();
+ }
+ return null;
+ }
+
+ /**
+ * Asks this input to take focus within its container (editor).
+ * <p>
+ * Clients should not call this method but they may
+ * override if they implement a different layout with different visual
+ * components. Clients are free to call the inherited method.
+ * </p>
+ */
+ public void setFocus() {
+ if (fFocusPane != null) {
+ Viewer v= fFocusPane.getViewer();
+ if (v != null) {
+ Control c= v.getControl();
+ if (c != null)
+ c.setFocus();
+ }
+ } else if (fComposite != null)
+ fComposite.setFocus();
+ }
+
+ /**
+ * Factory method for creating a differences viewer for the top left pane.
+ * It is called from <code>createContents</code> and returns a <code>DiffTreeViewer</code>.
+ * <p>
+ * Subclasses may override if they need a different viewer.
+ * </p>
+ *
+ * @param parent the SWT parent control under which to create the viewer's SWT controls
+ * @return a compare viewer for the top left pane
+ */
+ public Viewer createDiffViewer(Composite parent) {
+ return new DiffTreeViewer(parent, fCompareConfiguration);
+ }
+
+ /**
+ * Implements the dynamic viewer switching for structure viewers.
+ * The method must return a compare viewer based on the old (or current) viewer
+ * and a new input object. If the old viewer is suitable for showing the new input the old viewer
+ * can be returned. Otherwise a new viewer must be created under the given parent composite or
+ * <code>null</code> can be returned to indicate that no viewer could be found.
+ * <p>
+ * This implementation forwards the request to <code>CompareUI.findStructureViewer</code>.
+ * <p>
+ * Subclasses may override to implement a different strategy.
+ * </p>
+ *
+ * @return a compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent) {
+ return CompareUIPlugin.findStructureViewer(oldViewer, input, parent, fCompareConfiguration);
+ }
+
+ /**
+ * Implements the dynamic viewer switching for content viewers.
+ * The method must return a compare viewer based on the old (or current) viewer
+ * and a new input object. If the old viewer is suitable for showing the new input the old viewer
+ * can be returned. Otherwise a new viewer must be created under the given parent composite or
+ * <code>null</code> can be returned to indicate that no viewer could be found.
+ * <p>
+ * This implementation forwards the request to <code>CompareUI.findContentViewer</code>.
+ * <p>
+ * Subclasses may override to implement a different strategy.
+ * </p>
+ *
+ * @return a compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public Viewer findContentViewer(Viewer oldViewer, ICompareInput input, Composite parent) {
+ Viewer v= CompareUIPlugin.findContentViewer(oldViewer, input, parent, fCompareConfiguration);
+
+ if (v instanceof IPropertyChangeNotifier) {
+ final IPropertyChangeNotifier dsp= (IPropertyChangeNotifier) v;
+
+ dsp.addPropertyChangeListener(fDirtyStateListener);
+
+ Control c= v.getControl();
+ c.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ dsp.removePropertyChangeListener(fDirtyStateListener);
+ }
+ }
+ );
+ }
+
+ return v;
+ }
+
+ /**
+ * Returns <code>true</code> if there are unsaved changes.
+ * The value returned is the value of the <code>DIRTY_STATE</code> property of this input object.
+
+ * Returns <code>true</code> if this input has unsaved changes,
+ * that is if <code>setDirty(true)</code> has been called.
+ * Subclasses don't have to override if the functionality provided by <doce>setDirty</code>
+ * is sufficient.
+ *
+ * @return <code>true</code> if there are changes that need to be saved
+ */
+ public boolean isSaveNeeded() {
+ return fDirty;
+ }
+
+ /**
+ * Sets the dirty state of this input to the given
+ * value and sends out a <code>PropertyChangeEvent</code> if the new value differs from the old value.
+ *
+ * @param dirty the dirty state for this compare input
+ */
+ public void setDirty(boolean dirty) {
+ if (dirty != fDirty) {
+ boolean old= fDirty;
+ fDirty= dirty;
+ Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, new Boolean(old), new Boolean(fDirty));
+ }
+ }
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.addListener
+ */
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ fListenerList.add(listener);
+ }
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.removeListener
+ */
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ fListenerList.remove(listener);
+ }
+
+ /**
+ * Save any unsaved changes.
+ * Empty implementation.
+ * Subclasses must override to save any changes.
+ *
+ * @param progressMonitor an <code>IProgressMonitor</code> that the implementation of save may use to show progress
+ */
+ public void save(IProgressMonitor pm) {
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java
new file mode 100644
index 000000000..1c92fde92
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareUI.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.IStructureCreatorDescriptor;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.IStructureCreator;
+
+
+/**
+ * The class <code>CompareUI</code> defines the entry point to initiate a configurable
+ * compare operation on arbitrary resources. The result of the compare
+ * is opened into a compare editor where the details can be browsed and
+ * edited in dynamically selected structure and content viewers.
+ * <p>
+ * The Compare UI provides a registry for content and structure compare viewers,
+ * which is initialized from extensions contributed to extension points
+ * declared by this plug-in.
+ */
+public final class CompareUI {
+
+ /**
+ * Name of the title property of a compare viewer.
+ * If a property with this name is set
+ * on the top level SWT control of a viewer, it is used as a title in the pane's
+ * title bar.
+ */
+ public static final String COMPARE_VIEWER_TITLE= "org.eclipse.compare.CompareUI.CompareViewerTitle";
+
+ /* (non Javadoc)
+ * non inatiatiable!
+ */
+ private CompareUI() {
+ }
+
+ public static AbstractUIPlugin getPlugin() {
+ return CompareUIPlugin.getDefault();
+ }
+
+ /**
+ * Returns this plug-in's resource bundle.
+ *
+ * @return the plugin's resource bundle
+ */
+ public static ResourceBundle getResourceBundle() {
+ return CompareUIPlugin.getResourceBundle();
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * compare editor on the result.
+ *
+ * @param input the input on which to open the compare editor
+ * @see ICompareEditorInput
+ */
+ public static void openCompareEditor(CompareEditorInput input) {
+ CompareUIPlugin plugin= CompareUIPlugin.getDefault();
+ if (plugin != null)
+ plugin.openCompareEditor(input);
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * modal compare dialog on the result.
+ *
+ * @param input the input on which to open the compare dialog
+ * @see ICompareEditorInput
+ */
+ public static void openCompareDialog(CompareEditorInput input) {
+ CompareUIPlugin plugin= CompareUIPlugin.getDefault();
+ if (plugin != null)
+ plugin.openCompareDialog(input);
+ }
+
+ /**
+ * Registers an image descriptor for the given type.
+ *
+ * @param type the type
+ * @param descriptor the image descriptor
+ */
+ public static void registerImageDescriptor(String type, ImageDescriptor descriptor) {
+ CompareUIPlugin.registerImageDescriptor(type, descriptor);
+ }
+
+ /**
+ * Returns a shared image for the given type, or a generic image if none
+ * has been registered for the given type.
+ * <p>
+ * Note: Images returned from this method will be automatically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param type the type
+ * @return the image
+ */
+ public static Image getImage(String type) {
+ return CompareUIPlugin.getImage(type);
+ }
+
+ /**
+ * Registers the given image for being disposed when this plug-in is shutdown.
+ *
+ * @param image the image to register for disposal
+ */
+ public static void disposeOnShutdown(Image image) {
+ CompareUIPlugin.disposeOnShutdown(image);
+ }
+
+ /**
+ * Returns a shared image for the given adaptable.
+ * This convenience method queries the given adaptable
+ * for its <code>IWorkbenchAdapter.getImageDescriptor</code>, which it
+ * uses to create an image if it does not already have one.
+ * <p>
+ * Note: Images returned from this method will be automatically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param adaptable the adaptable for which to find an image
+ * @return an image
+ */
+ public static Image getImage(IAdaptable adaptable) {
+ return CompareUIPlugin.getImage(adaptable);
+ }
+
+ /**
+ * Returns a structure compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input, the old viewer
+ * is returned. Otherwise, the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a structure compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a structure viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent,
+ CompareConfiguration configuration) {
+
+ return CompareUIPlugin.findStructureViewer(oldViewer, input, parent, configuration);
+ }
+
+ /**
+ * Returns a content compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input the old viewer
+ * is returned. Otherwise the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a content compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a content viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findContentViewer(Viewer oldViewer, ICompareInput input, Composite parent,
+ CompareConfiguration configuration) {
+
+ return CompareUIPlugin.findContentViewer(oldViewer, input, parent, configuration);
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java
new file mode 100644
index 000000000..76ccf17b8
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/EditionSelectionDialog.java
@@ -0,0 +1,699 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import java.util.HashMap;
+import java.util.ResourceBundle;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.text.*;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFileState;
+
+import org.eclipse.compare.internal.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+/**
+ * A dialog where one input element can be compared against
+ * a list of historic variants (editions) of the same input element.
+ * The dialog can be used to implement functions like "Replace with Version" or
+ * "Replace with Edition" on workbench resources.
+ * <p>
+ * In addition it is possible to specify a subsection of the input element
+ * (e.g. a method in a Java source file) by means of a "path".
+ * In this case the dialog compares only the subsection (as specified by the path)
+ * with the corresponding subsection in the list of editions.
+ * Only those editions are shown where the subsection differs from the same subsection in
+ * another edition thereby minimizing the number of presented variants.
+ * This functionality can be used to implement "Replace with Method Edition"
+ * for the Java language.
+ * <p>
+ * Subsections of an input element are determined by first finding an
+ * <code>IStructureCreator</code> for the input's type.
+ * Then the method <code>locate</code> is used to extract the subsection.
+ * <p>
+ * Each edition (variant in the list of variants) must implement the <code>IModificationDate</code> interface
+ * so that the dialog can sort the editions and present them in a tree structure where every
+ * node corresponds one day.
+ * <p>
+ * The functionality is surfaced in a single function <code>selectEdition</code>.
+ * <p>
+ * Clients may instantiate this class; it is not intended to be subclassed.
+ * </p>
+ *
+ * @see IModificationDate
+ * @see ITypedElement
+ */
+public class EditionSelectionDialog extends Dialog {
+
+ private static class Pair {
+
+ private ITypedElement fEdition;
+ private ITypedElement fItem;
+ private String fContent;
+
+ Pair(ITypedElement edition, ITypedElement item) {
+ fEdition= edition;
+ fItem= item;
+ }
+
+ ITypedElement getEdition() {
+ return fEdition;
+ }
+
+ ITypedElement getItem() {
+ return fItem;
+ }
+
+ void setContent(String content) {
+ fContent= content;
+ }
+
+ String getContent() {
+ return fContent;
+ }
+ }
+
+ private static final int ONE_DAY_MS= 86400 * 1000; // one day in milli seconds
+
+ private boolean fAddMode= false;
+
+ private ResourceBundle fBundle;
+ private boolean fTargetIsRight;
+
+ private HashMap fMemberEditions;
+
+ private ITypedElement fTargetItem;
+ private ITypedElement fSelectedItem;
+
+ private Tree fMemberTree;
+ private Pane fMemberPane;
+
+ private Tree fEditionTree;
+ private Pane fEditionPane;
+
+ private CompareViewerSwitchingPane fContentPane;
+ private Thread fThread;
+
+ private Image fDateImage;
+ private Image fTimeImage;
+
+ private CompareConfiguration fCompareConfiguration;
+
+ /**
+ * Creates a new modal, resizable dialog.
+ * Various titles, icons, and labels are configured from the given resource bundle.
+ * The following resource keys are used:
+ * <pre>
+ * key type description
+ * title String dialog title
+ * width Integer initial width of dialog
+ * height Integer initial height of dialog
+ * targetSide String whether target is on "right" or "left" side; default is "right"
+ * treeTitleFormat MessageFormat pane title for edition tree; arg 0 is the target
+ * dateIcon String icon for node in edition tree; path relative to class
+ * timeIcon String icon for leaf in edition tree; path relative to class
+ * todayFormat MessageFormat format string if date is todays date; arg 0 is date
+ * yesterdayFormat MessageFormat format string if date is yesterdays date; arg 0 is date
+ * dayFormat MessageFormat format string if date is any other date; arg 0 is date
+ * editionLabel String label for editions side of compare viewer; arg 0 is the date
+ * targetLabel String label for target side of compare viewer; arg 0 is
+ * </pre>
+ *
+ * @param parent if not <code>null</code> the new dialog stays on top of this parent shell
+ * @param bundle <code>ResourceBundle</code> to configure the dialog
+ */
+ public EditionSelectionDialog(Shell parent, ResourceBundle bundle) {
+ super(parent);
+ setShellStyle(SWT.CLOSE | SWT.APPLICATION_MODAL | SWT.RESIZE);
+
+ fBundle= bundle;
+
+ fTargetIsRight= "right".equals(Utilities.getString(fBundle, "targetSide", "right"));
+
+ fCompareConfiguration= new CompareConfiguration();
+ fCompareConfiguration.setLeftEditable(false);
+ fCompareConfiguration.setRightEditable(false);
+
+ String iconName= Utilities.getString(fBundle, "dateIcon", "obj16/date.gif");
+ ImageDescriptor id= CompareUIPlugin.getImageDescriptor(iconName);
+ if (id != null)
+ fDateImage= id.createImage();
+ iconName= Utilities.getString(fBundle, "timeIcon", "obj16/time.gif");
+ id= CompareUIPlugin.getImageDescriptor(iconName);
+ if (id != null)
+ fTimeImage= id.createImage();
+ }
+
+ public void setAddMode(boolean mode) {
+ fAddMode= mode;
+ }
+
+ /**
+ * Presents this modal dialog with the functionality described in the class comment above.
+ *
+ * @param target the input object against which the editions are compared; must not be <code>null</code>
+ * @param editions the list of editions (element type: <code>ITypedElement</code>s)
+ * @param path If <code>null</code> dialog shows full input; if non <code>null</code> it extracts a subsection
+ * @return returns the selected edition or <code>null</code> if dialog was cancelled.
+ * The returned <code>ITypedElement</code> is one of the original editions
+ * if <code>path</code> was <code>null</code>; otherwise
+ * it is an <code>ITypedElement</code> returned from <code>IStructureCreator.locate(path, item)</code>
+ */
+ public ITypedElement selectEdition(final ITypedElement target, ITypedElement[] inputEditions, Object ppath) {
+
+ Assert.isNotNull(target);
+ fTargetItem= target;
+
+ // sort input editions
+ final int count= inputEditions.length;
+ final IModificationDate[] editions= new IModificationDate[count];
+ for (int i= 0; i < count; i++)
+ editions[i]= (IModificationDate) inputEditions[i];
+ if (count > 1)
+ internalSort(editions, 0, count-1);
+
+ if (fAddMode)
+ return selectEdition2(target, count, editions, ppath);
+
+ IStructureCreator structureCreator= null;
+ if (ppath != null) { // try to extract subelement
+ String type= target.getType();
+ IStructureCreatorDescriptor scd= CompareUIPlugin.getStructureCreator(type);
+ if (scd != null) {
+ structureCreator= scd.createStructureCreator();
+ if (structureCreator != null) {
+ Object item= structureCreator.locate(ppath, target);
+ if (item instanceof ITypedElement)
+ fTargetItem= (ITypedElement) item;
+ else
+ ppath= null; // couldn't extract item
+ }
+ }
+ }
+
+ // set the left and right labels for the compare viewer
+ String targetLabel= getTargetLabel(target, fTargetItem);
+ if (fTargetIsRight)
+ fCompareConfiguration.setRightLabel(targetLabel);
+ else
+ fCompareConfiguration.setLeftLabel(targetLabel);
+
+ if (structureCreator != null && ppath != null) { // extract sub element
+
+ final IStructureCreator sc= structureCreator;
+ final Object path= ppath;
+
+ // construct the comparer thread
+ // and perform the background extract
+ fThread= new Thread() {
+ public void run() {
+
+ // we only show an edition if its contents is different than
+ // the preceding one.
+ //String lastContents= sc.getContents(fTargetItem, false);
+
+ // from front (newest) to back (oldest)
+ for (int i= 0; i < count; i++) {
+
+ if (fEditionTree == null || fEditionTree.isDisposed())
+ break;
+ ITypedElement edition= (ITypedElement) editions[i];
+
+ // extract sub element from edition
+ Object r= sc.locate(path, edition);
+ if (r instanceof ITypedElement) { // if not empty
+ ITypedElement item= (ITypedElement) r;
+ final Pair pair= new Pair(edition, item);
+ pair.setContent(sc.getContents(item, false));
+ /*
+ if (lastContents != null) {
+ String contents2= sc.getContents(item, false);
+ if (lastContents.equals(contents2))
+ continue;
+ lastContents= contents2;
+ }
+ */
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ addEdition(pair);
+ }
+ }
+ );
+ }
+ }
+ if (fEditionTree != null && !fEditionTree.isDisposed()) {
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ end();
+ }
+ }
+ );
+ }
+ }
+ };
+ } else {
+ // create tree widget
+ create();
+
+ // from front (newest) to back (oldest)
+ for (int i= 0; i < count; i++)
+ addEdition(new Pair((ITypedElement) editions[i], (ITypedElement) editions[i]));
+ }
+
+ open();
+
+ if (getReturnCode() == OK)
+ return fSelectedItem;
+ return null;
+ }
+
+ private ITypedElement selectEdition2(final ITypedElement target, final int count, final IModificationDate[] editions, final Object container) {
+
+ Assert.isNotNull(container);
+
+ // find StructureCreator
+ IStructureCreator structureCreator= null;
+ String type= target.getType();
+ IStructureCreatorDescriptor scd= CompareUIPlugin.getStructureCreator(type);
+ if (scd != null)
+ structureCreator= scd.createStructureCreator();
+ if (structureCreator == null)
+ return null; // error
+
+ // extract all elements of container
+ final HashSet current= new HashSet();
+ IStructureComparator sco= structureCreator.locate(container, target);
+ if (sco != null) {
+ Object[] children= sco.getChildren();
+ if (children != null)
+ for (int i= 0; i < children.length; i++)
+ current.add(children[i]);
+ } else
+ return null; // error
+
+
+ final IStructureCreator sc= structureCreator;
+
+ // construct the comparer thread
+ // and perform the background extract
+ fThread= new Thread() {
+ public void run() {
+
+ // from front (newest) to back (oldest)
+ for (int i= 0; i < count; i++) {
+
+ if (fEditionTree == null || fEditionTree.isDisposed())
+ break;
+ ITypedElement edition= (ITypedElement) editions[i];
+
+ IStructureComparator sco2= sc.locate(container, edition);
+ if (sco2 != null) {
+ Object[] children= sco2.getChildren();
+ if (children != null) {
+ for (int i2= 0; i2 < children.length; i2++) {
+ ITypedElement child= (ITypedElement) children[i2];
+ if (!current.contains(child)) {
+ final Pair pair= new Pair(edition, child);
+ pair.setContent(sc.getContents(child, false));
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ addMemberEdition(pair);
+ }
+ }
+ );
+ }
+ }
+ }
+ }
+ }
+ if (fEditionTree != null && !fEditionTree.isDisposed()) {
+ Display display= fEditionTree.getDisplay();
+ display.asyncExec(
+ new Runnable() {
+ public void run() {
+ end();
+ }
+ }
+ );
+ }
+ }
+ };
+
+ open();
+
+ if (getReturnCode() == OK)
+ return fSelectedItem;
+ return null;
+ }
+
+ /**
+ * Returns the input target that has been specified with the most recent call
+ * to <code>selectEdition</code>. If a not <code>null</code> path was specified this method
+ * returns a subsection of this target (<code>IStructureCreator.locate(path, target)</code>)
+ * instead of the input target.
+ * <p>
+ * For example if the <code>target</code> is a Java compilation unit and <code>path</code> specifies
+ * a method, the value returned from <code>getTarget</code> will be the method not the compilation unit.
+ *
+ * @return the last specified target or a subsection thereof.
+ */
+ public ITypedElement getTarget() {
+ return fTargetItem;
+ }
+
+ /**
+ * Returns a label for identifying the target side of a compare viewer.
+ * This implementation extracts the value for the key "targetLabel" from the resource bundle
+ * and passes it as the format argument to <code>MessageFormat.format</code>.
+ * The single format argument for <code>MessageFormat.format</code> ("{0}" in the format string)
+ * is the name of the given input element.
+ * <p>
+ * Subclasses may override to create their own label.
+ * </p>
+ *
+ * @param target the target element for which a label must be returned
+ * @param item if a path has been specified in <code>selectEdition</code> a sub element of the given target; otherwise the same as target
+ * @return a label the target side of a compare viewer
+ */
+ protected String getTargetLabel(ITypedElement target, ITypedElement item) {
+ String format= Utilities.getString(fBundle, "targetLabel", "targetLabel");
+ return MessageFormat.format(format, new Object[] { target.getName() });
+ }
+
+ /**
+ * Returns a label for identifying the edition side of a compare viewer.
+ * This implementation extracts the value for the key "editionLabel" from the resource bundle
+ * and passes it as the format argument to <code>MessageFormat.format</code>.
+ * The single format argument for <code>MessageFormat.format</code> ("{0}" in the format string)
+ * is the formatted modification date of the given input element.
+ * <p>
+ * Subclasses may override to create their own label.
+ * </p>
+ *
+ * @param selectedEdition the selected edition for which a label must be returned
+ * @param item if a path has been specified in <code>selectEdition</code> a sub element of the given selectedEdition; otherwise the same as selectedEdition
+ * @return a label the edition side of a compare viewer
+ */
+ protected String getEditionLabel(ITypedElement selectedEdition, ITypedElement item) {
+ String label= Utilities.getString(fBundle, "editionLabel", "editionLabel");
+
+ if (selectedEdition instanceof IModificationDate) {
+ long modDate= ((IModificationDate)selectedEdition).getModificationDate();
+ String date= DateFormat.getDateTimeInstance().format(new Date(modDate));
+ label= MessageFormat.format(label, new Object[] { date });
+ }
+
+ return label;
+ }
+
+ /* (non Javadoc)
+ * Returns the size initialized with the constructor.
+ */
+ protected Point getInitialSize() {
+ Point size= new Point(Utilities.getInteger(fBundle, "width", 0),
+ Utilities.getInteger(fBundle, "height", 0));
+
+ Shell shell= getParentShell();
+ if (shell != null) {
+ Point parentSize= shell.getSize();
+ if (size.x <= 0)
+ size.x= parentSize.x-300;
+ if (size.y <= 0)
+ size.y= parentSize.y-200;
+ }
+ if (size.x < 700)
+ size.x= 700;
+ if (size.y < 500)
+ size.y= 500;
+ return size;
+ }
+
+ /* (non Javadoc)
+ * Creates SWT control tree.
+ */
+ protected synchronized Control createDialogArea(Composite parent) {
+
+ getShell().setText(Utilities.getString(fBundle, "title", "title"));
+
+ Splitter vsplitter= new Splitter(parent, SWT.VERTICAL);
+ vsplitter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL
+ | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
+
+ vsplitter.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fDateImage != null)
+ fDateImage.dispose();
+ if (fTimeImage != null)
+ fTimeImage.dispose();
+ }
+ }
+ );
+
+
+ if (fAddMode) {
+
+ Splitter hsplitter= new Splitter(vsplitter, SWT.HORIZONTAL);
+
+ fMemberPane= new Pane(hsplitter, SWT.NONE);
+ fMemberPane.setText("Available Members");
+ fMemberTree= new Tree(fMemberPane, SWT.H_SCROLL + SWT.V_SCROLL);
+ fMemberTree.addSelectionListener(
+ new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleMemberSelect(e.item);
+ }
+ }
+ );
+
+ fMemberPane.setContent(fMemberTree);
+
+ fEditionPane= new Pane(hsplitter, SWT.NONE);
+ } else {
+ fEditionPane= new Pane(vsplitter, SWT.NONE);
+ }
+ String titleFormat= Utilities.getString(fBundle, "treeTitleFormat", "treeTitleFormat");
+ String title= MessageFormat.format(titleFormat, new Object[] { fTargetItem.getName() });
+ fEditionPane.setText(title);
+
+ fEditionTree= new Tree(fEditionPane, SWT.H_SCROLL + SWT.V_SCROLL);
+ fEditionTree.addSelectionListener(
+ new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ handleDefaultSelected();
+ }
+ public void widgetSelected(SelectionEvent e) {
+ feedInput(e.item);
+ }
+ }
+ );
+ fEditionPane.setContent(fEditionTree);
+
+ // now start the thread (and forget about it)
+ if (fThread != null) {
+ fThread.start();
+ fThread= null;
+ }
+
+ fContentPane= new CompareViewerSwitchingPane(vsplitter, SWT.NONE) {
+ protected Viewer getViewer(Viewer oldViewer, Object input) {
+ return CompareUIPlugin.findContentViewer(oldViewer, input, this, fCompareConfiguration);
+ }
+ };
+ vsplitter.setWeights(new int[] { 30, 70 });
+
+ return vsplitter;
+ }
+
+ //---- private stuff ----------------------------------------------------------------------------------------
+
+ private void handleDefaultSelected() {
+ if (fSelectedItem != null)
+ okPressed();
+ }
+
+ private static void internalSort(IModificationDate[] keys, int left, int right) {
+
+ int original_left= left;
+ int original_right= right;
+
+ IModificationDate mid= keys[(left + right) / 2];
+ do {
+ while (keys[left].getModificationDate() > mid.getModificationDate())
+ left++;
+
+ while (mid.getModificationDate() > keys[right].getModificationDate())
+ right--;
+
+ if (left <= right) {
+ IModificationDate tmp= keys[left];
+ keys[left]= keys[right];
+ keys[right]= tmp;
+ left++;
+ right--;
+ }
+ } while (left <= right);
+
+ if (original_left < right)
+ internalSort(keys, original_left, right);
+
+ if (left < original_right)
+ internalSort(keys, left, original_right);
+
+ }
+
+ private void addEdition(Pair pair) {
+
+ if (fEditionTree == null || fEditionTree.isDisposed())
+ return;
+
+ // find last day
+ TreeItem[] days= fEditionTree.getItems();
+ TreeItem lastDay= null;
+ if (days.length > 0)
+ lastDay= days[days.length-1];
+
+ boolean first= lastDay == null;
+
+ ITypedElement edition= pair.getEdition();
+ ITypedElement item= pair.getItem();
+
+ long ldate= ((IModificationDate)edition).getModificationDate();
+ long day= ldate / ONE_DAY_MS;
+ Date date= new Date(ldate);
+ if (lastDay == null || day != ((Date)lastDay.getData()).getTime() / ONE_DAY_MS) {
+ lastDay= new TreeItem(fEditionTree, SWT.NONE);
+ lastDay.setImage(fDateImage);
+ String df= DateFormat.getDateInstance().format(date);
+ long today= System.currentTimeMillis() / ONE_DAY_MS;
+
+ String formatKey;
+ if (day == today)
+ formatKey= "todayFormat";
+ else if (day == today-1)
+ formatKey= "yesterdayFormat";
+ else
+ formatKey= "dayFormat";
+ String pattern= Utilities.getString(fBundle, formatKey, null);
+ if (pattern != null)
+ df= MessageFormat.format(pattern, new Object[] { df });
+ lastDay.setText(df);
+ lastDay.setData(date);
+ }
+ TreeItem ti= new TreeItem(lastDay, SWT.NONE);
+ ti.setImage(fTimeImage);
+ ti.setText(DateFormat.getTimeInstance().format(date));
+ ti.setData(new Pair(edition, item));
+ if (first) {
+ fEditionTree.setSelection(new TreeItem[] {ti});
+ fEditionTree.setFocus();
+ feedInput(ti);
+ }
+ //if (first) // expand first node
+ lastDay.setExpanded(true);
+ }
+
+ private void addMemberEdition(Pair pair) {
+
+ if (fMemberEditions == null)
+ fMemberEditions= new HashMap();
+
+ ITypedElement item= pair.getItem();
+ List editions= (List) fMemberEditions.get(item);
+ if (editions == null) {
+ editions= new ArrayList();
+ fMemberEditions.put(item, editions);
+ if (fMemberTree != null && !fMemberTree.isDisposed()) {
+ TreeItem ti= new TreeItem(fMemberTree, SWT.NULL);
+ String name= ((ITypedElement)item).getName();
+ ti.setText(name);
+ ti.setData(editions);
+ }
+ }
+ editions.add(pair);
+ }
+
+ private void end() {
+ Tree tree= fMemberTree;
+ if (tree == null)
+ tree= fEditionTree;
+ if (tree != null && !tree.isDisposed() && tree.getItemCount() == 0) {
+ TreeItem ti= new TreeItem(tree, SWT.NONE);
+ ti.setText("No Editions found");
+ }
+ }
+
+ private void feedInput(Widget w) {
+ Object input= w.getData();
+ if (input instanceof Pair) {
+ Pair pair= (Pair) input;
+ fSelectedItem= pair.getItem();
+
+ String editionLabel= getEditionLabel(pair.getEdition(), fSelectedItem);
+
+ if (fAddMode) {
+ fContentPane.setInput(fSelectedItem);
+ fContentPane.setText(editionLabel);
+ } else {
+ if (fTargetIsRight) {
+ fCompareConfiguration.setLeftLabel(editionLabel);
+ fContentPane.setInput(new DiffNode(fSelectedItem, fTargetItem));
+ } else {
+ fCompareConfiguration.setRightLabel(editionLabel);
+ fContentPane.setInput(new DiffNode(fTargetItem, fSelectedItem));
+ }
+ }
+ } else {
+ fSelectedItem= null;
+ fContentPane.setInput(null);
+ }
+ }
+
+ private void handleMemberSelect(Widget w) {
+ Object data= w.getData();
+ if (w instanceof TreeItem && data instanceof List) {
+ List list= (List) data;
+ fEditionTree.removeAll();
+ fEditionPane.setText("Editions of " + ((TreeItem)w).getText());
+ Iterator iter= list.iterator();
+ while (iter.hasNext()) {
+ Object item= iter.next();
+ if (item instanceof Pair)
+ addEdition((Pair) item);
+ }
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java
new file mode 100644
index 000000000..898d8bdc1
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/HistoryItem.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import java.io.InputStream;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.core.resources.IFileState;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.*;
+
+/**
+ * A combination <code>IFileState</code> and <code>ITypedElement</code> that can be used as
+ * an input to a compare viewer or other places where an <code>IStreamContentAccessor</code>
+ * is needed.
+ * <p>
+ * <p>
+ * Clients may instantiate this class; it is not intended to be subclassed.
+ * </p>
+ */
+public class HistoryItem implements IStreamContentAccessor, ITypedElement, IModificationDate {
+
+ private ITypedElement fBase;
+ private IFileState fFileState;
+
+ /**
+ * Creates a <code>HistoryItem</code> object which combines the given <code>IFileState</code>
+ * and <code>ITypedElement</code> into an object
+ * which is suitable as input for a compare viewer or <code>ReplaceWithEditionDialog</code>.
+ *
+ * @param base the implementation of the <code>ITypedElement</code> interface delegates to this base <code>ITypedElement</code>
+ * @param fileState the <code>IFileState</code> from which the streamable contents and the modification time is derived from
+ */
+ public HistoryItem(ITypedElement base, IFileState fileState) {
+ fBase= base;
+ fFileState= fileState;
+ }
+
+ /* (non-Javadoc)
+ * see ITypedElement.getName
+ */
+ public String getName() {
+ return fBase.getName();
+ }
+
+ /* (non-Javadoc)
+ * see ITypedElement.getImage
+ */
+ public Image getImage() {
+ return fBase.getImage();
+ }
+
+ /* (non-Javadoc)
+ * see ITypedElement.getType
+ */
+ public String getType() {
+ return fBase.getType();
+ }
+
+ /* (non-Javadoc)
+ * see IModificationDate.getModificationDate
+ */
+ public long getModificationDate() {
+ return fFileState.getModificationTime();
+ }
+
+ /* (non-Javadoc)
+ * see IStreamContentAccessor.getContents
+ */
+ public InputStream getContents() throws CoreException {
+ return fFileState.getContents();
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java
new file mode 100644
index 000000000..01365ae80
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeListener.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * An <code>IContentChangeListener</code> is informed about content changes of a
+ * <code>IContentChangeNotifier</code>.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IContentChangeNotifier
+ */
+public interface IContentChangeListener {
+
+ /**
+ * Called whenever the content of the given source has changed.
+ *
+ * @param source the source whose contents has changed
+ */
+ void contentChanged(IContentChangeNotifier source);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java
new file mode 100644
index 000000000..714f73775
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IContentChangeNotifier.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * Interface common to all objects that provide a means for registering
+ * for content change notification.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IContentChangeListener
+ */
+public interface IContentChangeNotifier {
+
+ /**
+ * Adds a content change listener to this notifier.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param listener a content changed listener
+ */
+ void addContentChangeListener(IContentChangeListener listener);
+
+ /**
+ * Removes the given content changed listener from this notifier.
+ * Has no effect if the listener is not registered.
+ *
+ * @param listener a content changed listener
+ */
+ void removeContentChangeListener(IContentChangeListener listener);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java
new file mode 100644
index 000000000..f41eecf20
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IEditableContent.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * Common interface for objects with editable contents.
+ * Typically it is implemented by objects that also implement
+ * the <code>IStreamContentAccessor</code> interface.
+ * <p>
+ * Clients may implement this interface.
+ * <p>
+ * Note that implementing <code>IEditableContent</code> does not
+ * automatically mean that it is editable. An object is only editable if
+ * it implements <code>IEditableContent</code> and the <code>isEditable</code> method returns <code>true</code>.
+ *
+ * @see IStreamContentAccessor
+ */
+public interface IEditableContent {
+
+ /**
+ * Returns <code>true</code> if this object can be modified.
+ * If it returns <code>false</code> the other methods of this API must not be called.
+ *
+ * @return <code>true</code> if this object can be modified
+ */
+ boolean isEditable();
+
+ /**
+ * Replaces the current content with the given new bytes.
+ *
+ * @param newContent this new contents replaces the old contents
+ */
+ void setContent(byte[] newContent);
+
+ /**
+ * This method is called on a parent to
+ * - add a child,
+ * - remove a child,
+ * - copy the contents of a child
+ *
+ * What to do is encoded in the two arguments as follows:
+ * add: child == null other != null
+ * remove: child != null other == null
+ * copy: child != null other != null
+ */
+ ITypedElement replace(ITypedElement child, ITypedElement other);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java
new file mode 100644
index 000000000..a0bcbb9a2
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IModificationDate.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+/**
+ * Common interface for objects with a modification date. The modification date
+ * can be used in the UI to give the user a general idea of how old an object is.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ */
+public interface IModificationDate {
+
+ /**
+ * Returns the modification time of this object.
+ * <p>
+ * Note that this value should only be used to give the user a general idea of how
+ * old the object is.
+ *
+ * @return the time of last modification, in milliseconds since
+ * January 1, 1970, 00:00:00 GMT
+ */
+ long getModificationDate();
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java
new file mode 100644
index 000000000..b2b0f66ec
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IPropertyChangeNotifier.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+/**
+ * Interface common to all objects that provide a means for registering
+ * for property change notification.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see org.eclipse.jface.util.IPropertyChangeListener
+ */
+public interface IPropertyChangeNotifier {
+
+ /**
+ * Adds a listener for property changes to this notifier.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param listener a property change listener
+ */
+ void addPropertyChangeListener(IPropertyChangeListener listener);
+
+ /**
+ * Removes the given content change listener from this notifier.
+ * Has no effect if the identical listener is not registered.
+ *
+ * @param listener a property change listener
+ */
+ void removePropertyChangeListener(IPropertyChangeListener listener);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java
new file mode 100644
index 000000000..72c7198ba
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IStreamContentAccessor.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare;
+
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * An <code>IStreamContentAccessor</code> object represents a set of bytes which can be
+ * accessed by means of a stream.
+ * <p>
+ * Clients may implement this interface, or use the standard implementation,
+ * <code>BufferedContent</code>.
+ *
+ * @see BufferedContent
+ */
+public interface IStreamContentAccessor {
+ /**
+ * Returns an open <code>InputStream</code> for this object which can be used to retrieve the object's content.
+ * The client is responsible for closing the stream when finished.
+ * Returns <code>null</code> if this object has no streamable contents.
+ *
+ * @return an input stream containing the contents of this object
+ * @exception CoreException if the contents of this object could not be accessed
+ */
+ InputStream getContents() throws CoreException;
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java
new file mode 100644
index 000000000..83dcda31d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ITypedElement.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Interface for getting the name, image, and type for an object.
+ * <p>
+ * These methods are typically used to present an input object in the compare UI
+ * (<code>getName</code> and <code>getImage</code>)
+ * and for finding a viewer for a given input type (<code>getType</code>).
+ * <p>
+ * Clients may implement this interface.
+ */
+public interface ITypedElement {
+
+ /**
+ * Type for a folder input. Folders are comparison
+ * elements that have no contents, only a name and children.
+ */
+ public static final String FOLDER_TYPE= "FOLDER";
+
+ /**
+ * Type for an element whose actual type is text.
+ */
+ public static final String TEXT_TYPE= "txt";
+
+ /**
+ * Type for an element whose actual type could not
+ * be determined.
+ */
+ public static final String UNKNOWN_TYPE= "???";
+
+ /**
+ * Returns the name of this object.
+ * The name is used when displaying this object in the UI.
+ *
+ * @return the name of this object
+ */
+ String getName();
+
+ /**
+ * Returns an image for this object.
+ * This image is used when displaying this object in the UI.
+ *
+ * @return the image of this object or <code>null</code> if this type of input has no image
+ */
+ Image getImage();
+
+ /**
+ * Returns the type of this object. For objects with a file name
+ * this is typically the file extension. For folders its the constant
+ * <code>FOLDER_TYPE</code>.
+ * The type is used for determining a suitable viewer for this object.
+ *
+ * @return the type of this object
+ */
+ String getType();
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java
new file mode 100644
index 000000000..142341827
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/IViewerCreator.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A factory object for <code>Viewer</code>.
+ * <p>
+ * This interface is only required when creating a <code>Viewer</code> from a plugin.xml file.
+ * Since <code>Viewer</code>s have no default constructor they cannot be
+ * instantiated directly with <code>Class.forName</code>.
+ */
+public interface IViewerCreator {
+
+ /**
+ * Creates a new viewer under the given SWT parent control.
+ *
+ * @param parent the SWT parent control under which to create the viewer's SWT control
+ * @param config a compare configuration the newly created viewer might want to use
+ * @return a new viewer
+ */
+ Viewer createViewer(Composite parent, CompareConfiguration config);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java
new file mode 100644
index 000000000..445c90a18
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/ResourceNode.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.structuremergeviewer.IStructureComparator;
+
+/**
+ * A <code>ResourceNode</code> wrappers an <code>IResources</code> so that it can be used
+ * as input for the differencing engine (interfaces <code>IStructureComparator</code> and <code>ITypedElement</code>)
+ * and the <code>ReplaceWithEditionDialog</code> (interfaces <code>ITypedElement</code> and <code>IModificationDate</code>).
+ * <p>
+ * Clients may instantiate this class; it is not intended to be subclassed.
+ * </p>
+ *
+ * @see EditionSelectionDialog
+ */
+public class ResourceNode extends BufferedContent
+ implements IStructureComparator, ITypedElement, IEditableContent, IModificationDate {
+
+ private static final int NOTHING= 0; // nothing to do
+ private static final int UPDATE= 1; // need to save content
+ private static final int DELETE= 2; // need to delete file
+
+ private IResource fResource;
+ private ArrayList fChildren;
+ private String fName;
+ private int fAction= NOTHING; // what to do on commit
+
+
+ /**
+ * Creates a <code>ResourceNode</code> for the given resource.
+ *
+ * @param resource the resource
+ */
+ public ResourceNode(IResource resource) {
+ fResource= resource;
+ Assert.isNotNull(resource);
+ }
+
+ /**
+ * Returns the corresponding resource for this object.
+ *
+ * @return the corresponding resource
+ */
+ public IResource getResource() {
+ return fResource;
+ }
+
+ /* (non Javadoc)
+ * see IStreamContentAccessor.getContents
+ */
+ public InputStream getContents() throws CoreException {
+ if (fResource instanceof IStorage)
+ return super.getContents();
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IModificationDate.getModificationDate
+ */
+ public long getModificationDate() {
+ IPath path= fResource.getLocation();
+ File file= path.toFile();
+ return file.lastModified();
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getName
+ */
+ public String getName() {
+ if (fResource != null)
+ return fResource.getName();
+ return fName;
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getType
+ */
+ public String getType() {
+ if (fResource instanceof IContainer)
+ return ITypedElement.FOLDER_TYPE;
+ if (fResource != null) {
+ String s= fResource.getFileExtension();
+ if (s != null)
+ return s;
+ }
+ if (fName != null) {
+ int pos= fName.lastIndexOf('.');
+ if (pos >= 0)
+ return fName.substring(pos+1);
+ }
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ /* (non Javadoc)
+ * see ITypedElement.getImage
+ */
+ public Image getImage() {
+ return CompareUI.getImage(fResource);
+ }
+
+ /**
+ * Returns <code>true</code> if the other object is of type <code>ITypedElement</code>
+ * and their names are identical. The content is not considered.
+ */
+ /* (non Javadoc)
+ * see IStructureComparator.equals
+ */
+ public boolean equals(Object other) {
+ if (other instanceof ITypedElement) {
+ String otherName= ((ITypedElement)other).getName();
+ return getName().equals(otherName);
+ }
+ return super.equals(other);
+ }
+
+ /**
+ * Returns the hash code of the name.
+ */
+ /* (non Javadoc)
+ * see IStructureComparator.hashCode
+ */
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ /* (non Javadoc)
+ * see IStructureComparator.getChildren
+ */
+ public Object[] getChildren() {
+ if (fChildren == null) {
+ fChildren= new ArrayList();
+ if (fResource instanceof IContainer) {
+ try {
+ IResource members[]= ((IContainer)fResource).members();
+ for (int i= 0; i < members.length; i++) {
+ IStructureComparator child= createChild(members[i]);
+ if (child != null)
+ fChildren.add(child);
+ }
+ } catch (CoreException ex) {
+ }
+ }
+ }
+ return fChildren.toArray();
+ }
+
+ /**
+ * This hook method is called from <code>getChildren</code> once for every
+ * member of a container resource. This implementation
+ * creates a new <code>ResourceNode</code> for the given child resource.
+ * Clients may override this method to create a different type of
+ * <code>IStructureComparator</code> or to filter children by returning <code>null</code>.
+ *
+ * @param child the child resource for which a <code>IStructureComparator</code> must be returned
+ * @return a <code>ResourceNode</code> for the given child or <code>null</code>
+ */
+ protected IStructureComparator createChild(IResource child) {
+ return new ResourceNode(child);
+ }
+
+ /**
+ * Returns an open stream if the corresponding resource implements the
+ * <code>IStorage</code> interface. Otherwise the value <code>null</code> is returned.
+ *
+ * @return a buffered input stream containing the contents of this storage
+ * @exception CoreException if the contents of this storage could not be accessed
+ */
+ protected InputStream createStream() throws CoreException {
+ if (fResource instanceof IStorage)
+ return new BufferedInputStream(((IStorage)fResource).getContents());
+ return null;
+ }
+
+ /* (non Javadoc)
+ * see IEditableContent.isEditable
+ */
+ public boolean isEditable() {
+ return true;
+ }
+
+ /**
+ * FIXME: Really needed?
+ */
+ public ITypedElement replace(ITypedElement child, ITypedElement other) {
+ if (other == null) {
+ if (child != null)
+ System.out.println("delete " + child.getName());
+ else
+ System.out.println("ResourceNode.replace: oops");
+ } else {
+ if (child == null) {
+ System.out.println("create " + other.getName());
+ //child= new ResourceNode(fResource, other.getName());
+ } else {
+ System.out.println("update " + child.getName());
+ // copy contents
+ if (other instanceof IStreamContentAccessor) {
+ try {
+ InputStream is= ((IStreamContentAccessor)other).getContents();
+ byte[] bytes= Utilities.readBytes(is);
+ if (bytes != null)
+ setContent(bytes);
+ } catch (CoreException ex) {
+ }
+ }
+ }
+ }
+ return child;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java
new file mode 100644
index 000000000..a456529e1
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java
@@ -0,0 +1,948 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.custom.CLabel;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.compare.contentmergeviewer.IMergeViewerContentProvider;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+import org.eclipse.compare.internal.*;
+
+/**
+ * An abstract compare and merge viewer with two side-by-side content areas
+ * and an optional content area for the ancestor. The implementation makes no
+ * assumptions about the content type.
+ * <p>
+ * <code>ContentMergeViewer</code>
+ * <ul>
+ * <li>implements the overall layout and defines hooks so that subclasses
+ * can easily provide an implementation for a specific content type,
+ * <li>implements the UI for making the areas resizable,
+ * <li>has an action for controlling whether the ancestor area is visible or not,
+ * <li>has actions for copying one side of the input to the other side,
+ * <li>tracks the dirty state of the left and right sides and send out notification
+ * on state changes.
+ * </ul>
+ * A <code>ContentMergeViewer</code> accesses its
+ * model by means of a content provider which must implement the
+ * <code>IMergeViewerContentProvider</code> interface.
+ * </p>
+ * <p>
+ * Clients may wish to use the standard concrete subclass <code>TextMergeViewer</code>,
+ * or define their own subclass.
+ *
+ * @see IMergeViewerContentProvider
+ * @see TextMergeViewer
+ */
+public abstract class ContentMergeViewer extends ContentViewer implements IPropertyChangeNotifier {
+
+ /**
+ * Property names.
+ */
+ private static final String ANCESTOR_ENABLED= "ANCESTOR_ENABLED";
+
+ /* package */ static final int HORIZONTAL= 1;
+ /* package */ static final int VERTICAL= 2;
+
+ static final double HSPLIT= 0.5;
+ static final double VSPLIT= 0.3;
+
+ private class ContentMergeViewerLayout extends Layout {
+
+ public Point computeSize(Composite c, int w, int h, boolean force) {
+ return new Point(100, 100);
+ }
+
+ public void layout(Composite composite, boolean force) {
+
+ // determine some derived sizes
+ int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
+ Rectangle r= composite.getClientArea();
+
+ int centerWidth= getCenterWidth();
+ int width1= (int)((r.width-centerWidth)*fHSplit);
+ int width2= r.width-width1-centerWidth;
+
+ int height1= 0;
+ int height2= 0;
+ if (fAncestorEnabled && fShowAncestor) {
+ height1= (int)((r.height-(2*headerHeight))*fVSplit);
+ height2= r.height-(2*headerHeight)-height1;
+ } else {
+ height1= 0;
+ height2= r.height-headerHeight;
+ }
+
+ int y= 0;
+
+ if (fAncestorEnabled && fShowAncestor) {
+ fAncestorLabel.setBounds(0, y, r.width, headerHeight);
+ fAncestorLabel.setVisible(true);
+ y+= headerHeight;
+ handleResizeAncestor(0, y, r.width, height1);
+ y+= height1;
+ } else {
+ fAncestorLabel.setVisible(false);
+ handleResizeAncestor(0, 0, 0, 0);
+ }
+
+ fLeftLabel.getSize(); // WORKAROUND FOR PR
+
+ if (centerWidth > 3) {
+ fLeftLabel.setBounds(0, y, width1+1, headerHeight);
+ fDirectionLabel.setVisible(true);
+ fDirectionLabel.setBounds(width1+1, y, centerWidth-1, headerHeight);
+ fRightLabel.setBounds(width1+centerWidth, y, width2, headerHeight);
+ } else {
+ fLeftLabel.setBounds(0, y, width1, headerHeight);
+ fDirectionLabel.setVisible(false);
+ fRightLabel.setBounds(width1, y, r.width-width1, headerHeight);
+ }
+
+ y+= headerHeight;
+
+ if (fCenter != null && !fCenter.isDisposed())
+ fCenter.setBounds(width1, y, centerWidth, height2);
+
+ handleResizeLeftRight(0, y, width1, centerWidth, width2, height2);
+ }
+ }
+
+ class Resizer extends MouseAdapter implements MouseMoveListener {
+
+ Control fControl;
+ int fX, fY;
+ int fWidth1, fWidth2;
+ int fHeight1, fHeight2;
+ int fDirection;
+ boolean fLiveResize;
+
+ public Resizer(Control c, int dir) {
+ fDirection= dir;
+ fControl= c;
+ fControl.addMouseListener(this);
+ fLiveResize= !(fControl instanceof Sash);
+
+ fControl.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ fControl= null;
+ }
+ }
+ );
+ }
+
+ public void mouseDoubleClick(MouseEvent e) {
+ if ((fDirection & HORIZONTAL) != 0)
+ fHSplit= HSPLIT;
+ if ((fDirection & VERTICAL) != 0)
+ fVSplit= VSPLIT;
+ fComposite.layout(true);
+ }
+
+ public void mouseDown(MouseEvent e) {
+ Composite parent= fControl.getParent();
+
+ Point s= parent.getSize();
+ Point as= fAncestorLabel.getSize();
+ Point ys= fLeftLabel.getSize();
+ Point ms= fRightLabel.getSize();
+
+ fWidth1= ys.x;
+ fWidth2= ms.x;
+ fHeight1= fLeftLabel.getLocation().y-as.y;
+ fHeight2= s.y-(fLeftLabel.getLocation().y+ys.y);
+
+ fX= e.x;
+ fY= e.y;
+ fControl.addMouseMoveListener(this);
+ }
+
+ public void mouseUp(MouseEvent e) {
+ fControl.removeMouseMoveListener(this);
+ if (!fLiveResize)
+ resize(e);
+ }
+
+ public void mouseMove(MouseEvent e) {
+ if (fLiveResize)
+ resize(e);
+ }
+
+ private void resize(MouseEvent e) {
+ int dx= e.x-fX;
+ int dy= e.y-fY;
+
+ int centerWidth= fCenter.getSize().x;
+
+ if (fWidth1 + dx > centerWidth && fWidth2 - dx > centerWidth) {
+ fWidth1+= dx;
+ fWidth2-= dx;
+ if ((fDirection & HORIZONTAL) != 0)
+ fHSplit= (double)fWidth1/(double)(fWidth1+fWidth2);
+ }
+ if (fHeight1 + dy > centerWidth && fHeight2 - dy > centerWidth) {
+ fHeight1+= dy;
+ fHeight2-= dy;
+ if ((fDirection & VERTICAL) != 0)
+ fVSplit= (double)fHeight1/(double)(fHeight1+fHeight2);
+ }
+
+ fComposite.layout(true);
+ fControl.getDisplay().update();
+ }
+ };
+
+ /** Style bits for top level composite */
+ private int fStyles;
+ private ResourceBundle fBundle;
+ private CompareConfiguration fCompareConfiguration;
+ private IPropertyChangeListener fPropertyChangeListener;
+ private ICompareInputChangeListener fCompareInputChangeListener;
+ private ListenerList fListenerList;
+
+ private boolean fLeftDirty; // left side is dirty
+ private boolean fRightDirty; // right side is dirty
+
+ private double fHSplit= HSPLIT; // width ratio of left and right panes
+ private double fVSplit= VSPLIT; // height ratio of ancestor and bottom panes
+
+ private boolean fAncestorEnabled= true; // show ancestor in case of conflicts
+ /* package */ boolean fShowAncestor= false; // if current input has conflicts
+ private boolean fIsThreeWay= false;
+ private ActionContributionItem fAncestorItem;
+
+ private Action fCopyLeftToRightAction; // copy from left to right
+ private Action fCopyRightToLeftAction; // copy from right to left
+
+ // SWT widgets
+ /* package */ Composite fComposite;
+ private CLabel fAncestorLabel;
+ private CLabel fLeftLabel;
+ private CLabel fRightLabel;
+ private CLabel fDirectionLabel;
+ /* package */ Control fCenter;
+
+ //---- SWT resources to be disposed
+ private Image fRightArrow;
+ private Image fLeftArrow;
+ private Image fBothArrow;
+ //---- end
+
+ /**
+ * Creates a new content merge viewer and initializes with a resource bundle and a
+ * configuration.
+ *
+ * @param bundle the resource bundle
+ * @param cc the configuration object
+ */
+ protected ContentMergeViewer(int style, ResourceBundle bundle, CompareConfiguration cc) {
+ fStyles= style;
+ fBundle= bundle;
+
+ fAncestorEnabled= Utilities.getBoolean(cc, ANCESTOR_ENABLED, fAncestorEnabled);
+
+ setContentProvider(new MergeViewerContentProvider(cc));
+
+ fCompareInputChangeListener= new ICompareInputChangeListener() {
+ public void compareInputChanged(ICompareInput input) {
+ ContentMergeViewer.this.compareInputChanged(input);
+ }
+ };
+
+ fPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ ContentMergeViewer.this.propertyChange(event);
+ }
+ };
+
+ fCompareConfiguration= cc;
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.addPropertyChangeListener(fPropertyChangeListener);
+ }
+
+ //---- hooks ---------------------
+
+ /**
+ * Returns the viewer's name.
+ *
+ * @return the viewer's name
+ */
+ public String getTitle() {
+ return "Content Compare";
+ }
+
+ /**
+ * Creates the SWT controls for the ancestor, left, and right
+ * content areas of this compare viewer.
+ * Implementations typically hold onto the controls
+ * so that they can be initialized with the input objects in method
+ * <code>updateContent</code>.
+ *
+ * @param composite the container for the three areas
+ */
+ abstract protected void createControls(Composite composite);
+
+ /**
+ * Lays out the ancestor area of the compare viewer.
+ * It is called whenever the viewer is resized or when the sashes between
+ * the areas are moved to adjust the size of the areas.
+ *
+ * @param x the horizontal position of the ancestor area within its container
+ * @param y the vertical position of the ancestor area within its container
+ * @param width the width of the ancestor area
+ * @param height the height of the ancestor area
+ */
+ abstract protected void handleResizeAncestor(int x, int y, int width, int height);
+
+ /**
+ * Lays out the left and right areas of the compare viewer.
+ * It is called whenever the viewer is resized or when the sashes between
+ * the areas are moved to adjust the size of the areas.
+ *
+ * @param x the horizontal position of the left area within its container
+ * @param y the vertical position of the left and right area within its container
+ * @param leftWidth the width of the left area
+ * @param centerWidth the width of the gap between the left and right areas
+ * @param rightWidth the width of the right area
+ * @param height the height of the left and right areas
+ */
+ abstract protected void handleResizeLeftRight(int x, int y, int leftWidth, int centerWidth,
+ int rightWidth, int height);
+
+ /**
+ * Contributes items to the given <code>ToolBarManager</code>.
+ * It is called when this viewer is installed in its container and if the container
+ * has a <code>ToolBarManager</code>.
+ * The <code>ContentMergeViewer</code> implementation of this method does nothing.
+ * Subclasses may reimplement.
+ *
+ * @param toolBarManager the toolbar manager to contribute to
+ */
+ protected void createToolItems(ToolBarManager toolBarManager) {
+ }
+
+ /**
+ * Initializes the controls of the three content areas with the given input objects.
+ *
+ * @param ancestor the input for the ancestor area
+ * @param left the input for the left area
+ * @param right the input for the right area
+ */
+ abstract protected void updateContent(Object ancestor, Object left, Object right);
+
+ /**
+ * Copies the content of one side to the other side.
+ * Called from the (internal) actions for copying the sides of the viewer's 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
+ */
+ abstract protected void copy(boolean leftToRight);
+
+ /**
+ * Returns the byte contents of the left or right side. If the viewer
+ * has no editable content <code>null</code> can be returned.
+ *
+ * @param left if <code>true</code>, the byte contents of the left area is returned;
+ * if <code>false</code>, the byte contents of the right area
+ * @return the content as an array of bytes, or <code>null</code>
+ */
+ abstract protected byte[] getContents(boolean left);
+
+ //----------------------------
+
+ /**
+ * Returns the resource bundle of this viewer.
+ *
+ * @return the resource bundle
+ */
+ protected ResourceBundle getResourceBundle() {
+ return fBundle;
+ }
+
+ /**
+ * Returns the compare configuration of this viewer,
+ * or <code>null</code> if this viewer does not yet have a configuration.
+ *
+ * @return the compare configuration, or <code>null</code> if none
+ */
+ protected CompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>ContentViewer</code> method
+ * checks to ensure that the content provider is an <code>IMergeViewerContentProvider</code>.
+ */
+ public void setContentProvider(IContentProvider contentProvider) {
+ Assert.isTrue(contentProvider instanceof IMergeViewerContentProvider);
+ super.setContentProvider(contentProvider);
+ }
+
+ /* package */ IMergeViewerContentProvider getMergeContentProvider() {
+ return (IMergeViewerContentProvider) getContentProvider();
+ }
+
+ public void refresh() {
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>Viewer</code> method returns the empty selection. Subclasses may override.
+ */
+ public ISelection getSelection() {
+ return new ISelection() {
+ public boolean isEmpty() {
+ return true;
+ }
+ };
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>Viewer</code> method does nothing. Subclasses may reimplement.
+ */
+ public void setSelection(ISelection s, boolean reveal) {
+ }
+
+ /* package */ void propertyChange(PropertyChangeEvent event) {
+
+ String key= event.getProperty();
+// if (key.equals(ICompareConfiguration.MERGE_DIRECTION)) {
+// if (isDirty() && !saveContents(true, true))
+// return;
+// ToolBarManager tbm= null;
+// IVisualContainer vc= getContainer();
+// if (vc instanceof Pane)
+// tbm= ((Pane)vc).getToolBarManager();
+// if (tbm != null) {
+// updateToolItems();
+// tbm.update(true);
+// }
+//
+// updateDirectionLabel();
+// } else
+ if (key.equals(ANCESTOR_ENABLED)) {
+ fAncestorEnabled= Utilities.getBoolean(getCompareConfiguration(), ANCESTOR_ENABLED, fAncestorEnabled);
+ fComposite.layout(true);
+ }
+ }
+
+ //---- input
+
+ /* package */ boolean isThreeWay() {
+ return fIsThreeWay;
+ }
+
+ /**
+ * Internal hook method called when the input to this viewer is
+ * initially set or subsequently changed.
+ * <p>
+ * The <code>ContentMergeViewer</code> implementation of this <code>Viewer</code>
+ * method retrieves the content from the three sides by calling the methods
+ * <code>getAncestorContent</code>, <code>getLeftContent</code>,
+ * and <code>getRightContent</code> on the content provider.
+ * The values returned from these calls are passed to the hook method <code>updateContent</code>.
+ * </p>
+ *
+ * @param input the new input of this viewer, or <code>null</code> if none
+ * @param oldInput the old input element, or <code>null</code> if there
+ * was previously no input
+ */
+ protected final void inputChanged(Object input, Object oldInput) {
+
+ if (oldInput instanceof ICompareInput)
+ ((ICompareInput)oldInput).removeCompareInputChangeListener(fCompareInputChangeListener);
+
+ // before setting the new input we have to save the old
+ if (fLeftDirty || fRightDirty)
+ saveContent(oldInput);
+
+ if (input instanceof ICompareInput)
+ ((ICompareInput)input).addCompareInputChangeListener(fCompareInputChangeListener);
+
+ setLeftDirty(false);
+ setRightDirty(false);
+
+ // determine the merge direction
+ //boolean rightEditable= fMergeViewerContentProvider.isRightEditable(input);
+ //boolean leftEditable= fMergeViewerContentProvider.isLeftEditable(input);
+
+// if (fInput instanceof ICompareInput)
+// fIsThreeWay= (((ICompareInput)fInput).getChangeType() & Differencer.DIRECTION_MASK) != 0;
+// else
+// fIsThreeWay= true;
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ Object ancestor= content.getAncestorContent(input);
+ fIsThreeWay= ancestor != null;
+
+ if (fAncestorItem != null)
+ fAncestorItem.setVisible(fIsThreeWay);
+
+ boolean oldFlag= fShowAncestor;
+ fShowAncestor= fIsThreeWay && content.showAncestor(input);
+
+ if (fAncestorEnabled && oldFlag != fShowAncestor)
+ fComposite.layout(true);
+
+ ToolBarManager tbm= CompareViewerSwitchingPane.getToolBarManager(fComposite.getParent());
+ if (tbm != null) {
+ updateToolItems();
+ tbm.update(true);
+ tbm.getControl().getParent().layout(true);
+ }
+
+ updateHeader();
+
+ Object left= content.getLeftContent(input);
+ Object right= content.getRightContent(input);
+ updateContent(ancestor, left, right);
+ }
+
+ private void compareInputChanged(ICompareInput input) {
+
+ if (input != null) {
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ Object ancestor= content.getAncestorContent(input);
+ Object left= content.getLeftContent(input);
+ Object right= content.getRightContent(input);
+
+ updateContent(ancestor, left, right);
+ }
+ }
+
+ //---- layout & SWT control creation
+
+ /**
+ * Builds the SWT controls for the three areas of a compare/merge viewer.
+ * <p>
+ * Calls the hooks <code>createControls</code> and <code>createToolItems</code>
+ * to let subclasses build the specific content areas and to add items to
+ * an enclosing toolbar.
+ * <p>
+ * This method must only be called in the constructor of subclasses.
+ *
+ * @param parent the parent control
+ * @return the new control
+ */
+ protected final Control buildControl(Composite parent) {
+
+ fComposite= new Composite(parent, fStyles) {
+ public boolean setFocus() {
+ return internalSetFocus();
+ }
+ };
+ fComposite.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
+
+ hookControl(fComposite); // hook help & dispose listener
+
+ fComposite.setLayout(new ContentMergeViewerLayout());
+
+ int style= SWT.SHADOW_OUT;
+ fAncestorLabel= new CLabel(fComposite, style);
+
+ fLeftLabel= new CLabel(fComposite, style);
+ new Resizer(fLeftLabel, VERTICAL);
+
+ fDirectionLabel= new CLabel(fComposite, style);
+ fDirectionLabel.setAlignment(SWT.CENTER);
+ new Resizer(fDirectionLabel, HORIZONTAL | VERTICAL);
+
+ fRightLabel= new CLabel(fComposite, style);
+ new Resizer(fRightLabel, VERTICAL);
+
+ if (fCenter == null || fCenter.isDisposed())
+ fCenter= createCenter(fComposite);
+
+ createControls(fComposite);
+
+ ToolBarManager tbm= CompareViewerSwitchingPane.getToolBarManager(parent);
+ if (tbm != null) {
+ tbm.removeAll();
+
+ // define groups
+ tbm.add(new Separator("merge"));
+ tbm.add(new Separator("modes"));
+ tbm.add(new Separator("navigation"));
+
+ CompareConfiguration cc= getCompareConfiguration();
+
+ if (cc.isRightEditable()) {
+ fCopyLeftToRightAction=
+ new Action() {
+ public void run() {
+ copy(true);
+ }
+ };
+ Utilities.initAction(fCopyLeftToRightAction, getResourceBundle(), "action.CopyLeftToRight.");
+ tbm.appendToGroup("merge", fCopyLeftToRightAction);
+ }
+
+ if (cc.isLeftEditable()) {
+ fCopyRightToLeftAction=
+ new Action() {
+ public void run() {
+ copy(false);
+ }
+ };
+ Utilities.initAction(fCopyRightToLeftAction, getResourceBundle(), "action.CopyRightToLeft.");
+ tbm.appendToGroup("merge", fCopyRightToLeftAction);
+ }
+
+ Action a= new ChangePropertyAction(fBundle, fCompareConfiguration, "action.EnableAncestor.", ANCESTOR_ENABLED);
+ a.setChecked(fAncestorEnabled);
+ fAncestorItem= new ActionContributionItem(a);
+ //fAncestorItem.setVisible(false);
+ tbm.appendToGroup("modes", fAncestorItem);
+
+ createToolItems(tbm);
+ updateToolItems();
+
+ tbm.update(true);
+ }
+
+ return fComposite;
+ }
+
+ /* package */ boolean internalSetFocus() {
+ return false;
+ }
+
+ /* package */ int getCenterWidth() {
+ return 3;
+ }
+
+ /* package */ Control createCenter(Composite parent) {
+ Sash sash= new Sash(parent, SWT.VERTICAL);
+ new Resizer(sash, HORIZONTAL);
+ return sash;
+ }
+
+ /* package */ Control getCenter() {
+ return fCenter;
+ }
+
+ /*
+ * @see Viewer.getControl()
+ */
+ public Control getControl() {
+ return fComposite;
+ }
+
+ /**
+ * 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) {
+
+ Object input= getInput();
+ if (input instanceof ICompareInput)
+ ((ICompareInput)input).removeCompareInputChangeListener(fCompareInputChangeListener);
+
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.removePropertyChangeListener(fPropertyChangeListener);
+
+ fAncestorLabel= null;
+ fLeftLabel= null;
+ fDirectionLabel= null;
+ fRightLabel= null;
+ fCenter= null;
+
+ if (fRightArrow != null) {
+ fRightArrow.dispose();
+ fRightArrow= null;
+ }
+ if (fLeftArrow != null) {
+ fLeftArrow.dispose();
+ fLeftArrow= null;
+ }
+ if (fBothArrow != null) {
+ fBothArrow.dispose();
+ fBothArrow= null;
+ }
+
+ super.handleDispose(event);
+ }
+
+ /**
+ * Updates the enabled state of the toolbar items.
+ * <p>
+ * This method is called whenever the state of the items needs updating.
+ * <p>
+ * Subclasses may extend this method, although this is generally not required.
+ */
+ protected void updateToolItems() {
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+
+ if (fCopyLeftToRightAction != null)
+ fCopyLeftToRightAction.setEnabled(content.isRightEditable(input));
+
+ if (fCopyRightToLeftAction != null)
+ fCopyRightToLeftAction.setEnabled(content.isLeftEditable(input));
+ }
+
+// protected void createToolItems(ToolBarManager tbm) {
+//
+// if (USE_MORE_CONTROLS) {
+// fBothItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptBoth.") {
+// public void actionPerformed(Window w) {
+// accept(fCurrentDiff, true, false);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fBothItem);
+//
+// fAutoItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptAll.") {
+// public void actionPerformed(Window w) {
+// autoResolve();
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fAutoItem);
+// }
+// fRejectItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptIgnoreNow.") {
+// public void actionPerformed(Window w) {
+// reject(fCurrentDiff, true);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fRejectItem);
+//
+// Action a= new ChangePropertyAction(getBundle(), TextMergeViewer.class, getCompareConfiguration(), "action.SynchMode.", SYNC_SCROLLING);
+// a.setChecked(fSynchronizedScrolling);
+//// tbm.appendToGroup("modes", a);
+// tbm.add(a);
+//
+// tbm.add(new Separator());
+//
+// a= new Action() {
+// public void actionPerformed() {
+// navigate(true);
+// }
+// };
+// CompareUIPlugin.init(a, TextMergeViewer.class, getBundle(), "action.NextDiff.");
+// fNextItem= new ActionContributionItem(a);
+// //tbm.appendToGroup("navigation", fNextItem);
+// tbm.add(fNextItem);
+//
+// a= new Action() {
+// public void actionPerformed() {
+// navigate(false);
+// }
+// };
+// CompareUIPlugin.init(a, TextMergeViewer.class, getBundle(), "action.PrevDiff.");
+// fPreviousItem= new ActionContributionItem(a);
+// //tbm.appendToGroup("navigation", fPreviousItem);
+// tbm.add(fPreviousItem);
+// }
+
+ /**
+ * Updates the headers of the three areas
+ * by querying the content provider for a name and image for
+ * the three sides of the input object.
+ * <p>
+ * This method is called whenever the header must be updated.
+ * <p>
+ * Subclasses may extend this method, although this is generally not required.
+ */
+ protected void updateHeader() {
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+
+ if (fAncestorLabel != null) {
+ fAncestorLabel.setImage(content.getAncestorImage(input));
+ fAncestorLabel.setText(content.getAncestorLabel(input));
+ }
+ if (fLeftLabel != null) {
+ fLeftLabel.setImage(content.getLeftImage(input));
+ fLeftLabel.setText(content.getLeftLabel(input));
+ }
+ if (fRightLabel != null) {
+ fRightLabel.setImage(content.getRightImage(input));
+ fRightLabel.setText(content.getRightLabel(input));
+ }
+
+ updateDirectionLabel();
+ }
+
+ private Image loadImage(String name) {
+ ImageDescriptor id= ImageDescriptor.createFromFile(ContentMergeViewer.class, name);
+ if (id != null)
+ return id.createImage();
+ return null;
+ }
+
+ private void updateDirectionLabel() {
+// if (fDirectionLabel != null) {
+// Image image= null;
+//
+// //if (fMergePolicy.hasMergeDirection()) {
+// boolean y= fCompareConfiguration.isLeftEditable();
+// boolean m= fCompareConfiguration.isRightEditable();
+//
+// if (y && m) {
+// if (fBothArrow == null)
+// fBothArrow= loadImage("images/both.gif");
+// image= fBothArrow;
+// } else if (y) {
+// if (fLeftArrow == null)
+// fLeftArrow= loadImage("images/yours.gif");
+// image= fLeftArrow;
+// } else if (m) {
+// if (fRightArrow == null)
+// fRightArrow= loadImage("images/mine.gif");
+// image= fRightArrow;
+// }
+// //}
+//
+// if (image != null)
+// fDirectionLabel.setImage(image);
+// else
+// fDirectionLabel.setText("");
+// }
+ }
+
+ /**
+ * Calculates the height of the header.
+ */
+ /* package */ int getHeaderHeight() {
+ int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
+ headerHeight= Math.max(headerHeight, fDirectionLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y);
+ return headerHeight;
+ }
+
+ //---- merge direction
+
+ /**
+ * Returns true if both sides are editable.
+ */
+ /* package */ boolean canToggleMergeDirection() {
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+ return content.isLeftEditable(input) && content.isRightEditable(input);
+ }
+
+ //---- dirty state & saving state
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.addPropertyChangeListener
+ */
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ if (fListenerList == null)
+ fListenerList= new ListenerList();
+ fListenerList.add(listener);
+ }
+
+ /* (non Javadoc)
+ * see IPropertyChangeNotifier.removePropertyChangeListener
+ */
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ if (fListenerList != null) {
+ fListenerList.remove(listener);
+ if (fListenerList.isEmpty())
+ fListenerList= null;
+ }
+ }
+
+ /* package */ void fireDirtyState(boolean state) {
+ Utilities.firePropertyChange(fListenerList, this, CompareEditorInput.DIRTY_STATE, null, new Boolean(state));
+ }
+
+ /**
+ * Sets the dirty state of the left side of this viewer.
+ * If the new value differs from the old
+ * all registered listener are notified with
+ * a <code>PropertyChangeEvent</code> with the
+ * property name <code>CompareEditorInput.DIRTY_STATE</code>.
+ *
+ * @param dirty the state of the left side dirty flag
+ */
+ protected void setLeftDirty(boolean dirty) {
+ if (fLeftDirty != dirty) {
+ fLeftDirty= dirty;
+ fireDirtyState(dirty);
+// if (fActions != null) {
+// Action saveAction= (Action) fActions.get("Save");
+// if (saveAction != null)
+// saveAction.setEnabled(dirty);
+// }
+ }
+ }
+
+ /**
+ * Sets the dirty state of the right side of this viewer.
+ * If the new value differs from the old
+ * all registered listener are notified with
+ * a <code>PropertyChangeEvent</code> with the
+ * property name <code>CompareEditorInput.DIRTY_STATE</code>.
+ *
+ * @param dirty the state of the right side dirty flag
+ */
+ protected void setRightDirty(boolean dirty) {
+ if (fRightDirty != dirty) {
+ fRightDirty= dirty;
+ fireDirtyState(dirty);
+// if (fActions != null) {
+// Action saveAction= (Action) fActions.get("Save");
+// if (saveAction != null)
+// saveAction.setEnabled(dirty);
+// }
+ }
+ }
+
+ /**
+ * Save modified content back to input elements via the content provider.
+ */
+ /* package */ void saveContent(Object oldInput) {
+
+ // write back modified contents
+ IMergeViewerContentProvider content= (IMergeViewerContentProvider) getContentProvider();
+
+ if (fCompareConfiguration.isLeftEditable() && fLeftDirty) {
+
+ byte[] bytes= getContents(true);
+ content.saveLeftContent(oldInput, bytes);
+ setLeftDirty(false);
+ }
+
+ if (fCompareConfiguration.isRightEditable() && fRightDirty) {
+
+ byte[] bytes= getContents(false);
+ content.saveRightContent(oldInput, bytes);
+ setRightDirty(false);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java
new file mode 100644
index 000000000..6da51771b
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IDocumentRange.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * Defines a subrange in a document.
+ * <p>
+ * It is used by text viewers that can work on a subrange of a document. For example,
+ * a text viewer for Java compilation units might use this to restrict the view
+ * to a single method.
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see TextMergeViewer
+ * @see org.eclipse.compare.structuremergeviewer.DocumentRangeNode
+ */
+public interface IDocumentRange {
+
+ /**
+ * Returns the underlying document.
+ *
+ * @return the underlying document
+ */
+ IDocument getDocument();
+
+ /**
+ * Returns a position that specifies a subrange in the underlying document,
+ * or <code>null</code> if this document range spans the whole underlying document.
+ *
+ * @return a position that specifies a subrange in the underlying document, or <code>null</code>
+ */
+ Position getRange();
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java
new file mode 100644
index 000000000..5448ebf9f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/IMergeViewerContentProvider.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.jface.viewers.IContentProvider;
+
+
+/**
+ * A content provider that mediates between a <code>ContentMergeViewer</code>'s model
+ * and the viewer itself.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see ContentMergeViewer
+ */
+public interface IMergeViewerContentProvider extends IContentProvider {
+
+ //---- ancestor side
+
+ /**
+ * Returns the label for the ancestor side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the label for the ancestor side of a <code>ContentMergeViewer</code>
+ */
+ String getAncestorLabel(Object input);
+
+ /**
+ * Returns an optional image for the ancestor side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the image for the ancestor side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Image getAncestorImage(Object input);
+
+ /**
+ * Returns the contents for the ancestor side of a <code>ContentMergeViewer</code>.
+ * The interpretation of the returned object depends on the concrete <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the content for the ancestor side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Object getAncestorContent(Object input);
+
+ /**
+ * Returns whether the ancestor side of the given input element should be shown.
+ *
+ * @return <code>true</code> if the ancestor side of the given input element should be shown
+ */
+ boolean showAncestor(Object input);
+
+ //---- left side
+
+ /**
+ * Returns the label for the left side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the label for the left side of a <code>ContentMergeViewer</code>
+ */
+ String getLeftLabel(Object input);
+
+ /**
+ * Returns an optional image for the left side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the image for the left side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Image getLeftImage(Object input);
+
+ /**
+ * Returns the contents for the left side of a <code>ContentMergeViewer</code>.
+ * The interpretation of the returned object depends on the concrete <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the content for the left side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Object getLeftContent(Object input);
+
+ /**
+ * Returns whether the left side is editable.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return <code>true</code> if the left side of a <code>ContentMergeViewer</code> is editable
+ */
+ boolean isLeftEditable(Object input);
+
+ /**
+ * Saves new contents for the left side of the <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @param bytes the new contents to save for the left side
+ */
+ void saveLeftContent(Object input, byte[] bytes);
+
+ //---- right side
+
+ /**
+ * Returns the label for the right side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the label for the right side of a <code>ContentMergeViewer</code>
+ */
+ String getRightLabel(Object input);
+
+ /**
+ * Returns an optional image for the right side of a <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the image for the right side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Image getRightImage(Object input);
+
+ /**
+ * Returns the contents for the right side of a <code>ContentMergeViewer</code>.
+ * The interpretation of the returned object depends on the concrete <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return the content for the right side of a <code>ContentMergeViewer</code>,
+ * or <code>null</code> if none
+ */
+ Object getRightContent(Object input);
+
+ /**
+ * Returns whether the right side is editable.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @return <code>true</code> if the right side of a <code>ContentMergeViewer</code> is editable
+ */
+ boolean isRightEditable(Object input);
+
+ /**
+ * Saves new contents for the right side of the <code>ContentMergeViewer</code>.
+ *
+ * @param input the input object of the <code>ContentMergeViewer</code>
+ * @param bytes the new contents to save for the right side
+ */
+ void saveRightContent(Object input, byte[] bytes);
+}
+
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java
new file mode 100644
index 000000000..9fc23cb0d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ITokenComparator.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+
+
+/**
+ * For performing a so-called "token compare" on a line of text.
+ * This interface extends the <code>IRangeComparator</code> interface
+ * so that it can be used by the <code>TextMergeViewer</code>.
+ * <p>
+ * <code>TextMergeViewer</code> activates the token compare when navigating into
+ * a range of differing lines. At first the lines are selected as a block.
+ * When navigating into this block the token compare shows for every line
+ * the differing token by selecting them.
+ * <p>
+ * <code>TextMergeViewer</code>'s default token comparator works on characters separated
+ * by whitespace. If a different strategy is needed (for example, to use Java tokens in
+ * a Java-aware merge viewer), clients may create their own token
+ * comparators by implementing this interface (and overriding the
+ * <code>TextMergeViewer.createTokenComparator</code> factory method).
+ * </p>
+ *
+ * @see TextMergeViewer
+ */
+public interface ITokenComparator extends IRangeComparator {
+
+ /**
+ * Returns the start character position of the token with the given index.
+ * If the index is out of range (but not negative) the character position
+ * behind the last character (the length of the input string) is returned.
+ *
+ * @param index index of the token for which to return the start position
+ * @return the start position of the token with the given index
+ * @throws java.lang.IndexOutOfBoundsException if index is negative
+ */
+ int getTokenStart(int index);
+
+ /**
+ * Returns the character length of the token with the given index.
+ * If the index is out of range (but not negative) the value 0 is returned.
+ *
+ * @param index index of the token for which to return the start position
+ * @return the character length of the token with the given index
+ * @throws java.lang.IndexOutOfBoundsException if index is negative
+ */
+ int getTokenLength(int index);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java
new file mode 100644
index 000000000..86111de15
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java
@@ -0,0 +1,2372 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ResourceBundle;
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.custom.*;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.ui.texteditor.IUpdate;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.internal.MergeSourceViewer;
+import org.eclipse.compare.internal.BufferedCanvas;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.internal.TokenComparator;
+import org.eclipse.compare.internal.ChangePropertyAction;
+import org.eclipse.compare.rangedifferencer.RangeDifference;
+import org.eclipse.compare.rangedifferencer.RangeDifferencer;
+import org.eclipse.compare.internal.DocLineComparator;
+
+
+/**
+ * A text merge viewer uses the <code>RangeDifferencer</code> to perform a
+ * textual, line-by-line comparison of two (or three) input documents.
+ * It is based on the <code>ContentMergeViewer</code> and uses <code>TextViewer</code>s
+ * to implement the ancestor, left, and right content areas.
+ * <p>
+ * In the three-way compare case ranges of differing lines are highlighted and framed
+ * with different colors to show whether the difference is an incoming, outgoing, or conflicting change.
+ * The <code>TextMergeViewer</code> supports the notion of a current "differing range"
+ * and provides toolbar buttons to navigate from one range to the next (or previous).
+ * <p>
+ * If there is a current "differing range" and the underlying document is editable
+ * the <code>TextMergeViewer</code> enables actions in context menu and toolbar to
+ * copy a range from one side to the other side, thereby performing a merge operation.
+ * <p>
+ * In addition to a line-by-line comparison the <code>TextMergeViewer</code>
+ * uses a token based compare on differing lines.
+ * The token compare is activated when navigating into
+ * a range of differing lines. At first the lines are selected as a block.
+ * When navigating into this block the token compare shows for every line
+ * the differing token by selecting them.
+ * <p>
+ * The <code>TextMergeViewer</code>'s default token compare works on characters separated
+ * by whitespace. If a different strategy is needed (for example, Java tokens in
+ * a Java-aware merge viewer), clients can create their own token
+ * comparators by implementing the <code>ITokenComparator</code> interface and overriding the
+ * <code>TextMergeViewer.createTokenComparator</code> factory method).
+ * <p>
+ * Access to the <code>TextMergeViewer</code>'s model is by means of an
+ * <code>IMergeViewerContentProvider</code>. Its <code>get<it>X</it></code>Content</code> methods must return
+ * either an <code>IDocument</code>, an <code>IDocumentRange</code>, or an <code>IStreamContentAccessor</code>.
+ * In the <code>IDocumentRange</code> case the <code>TextMergeViewer</code>
+ * works on a subrange of a document. In the <code>IStreamContentAccessor</code> case
+ * a document is created internally and initialized from the stream.
+ * <p>
+ * A <code>TextMergeViewer</code> can be used as is. However clients may subclass
+ * to customize the behavior. For example a <code>MergeTextViewer</code> for Java would override
+ * the <code>configureTextViewer</code> method to configure the <code>TextViewer</code> for Java source code,
+ * the <code>createTokenComparator</code> method to create a Java specific tokenizer.
+ *
+ * @see org.eclipse.compare.rangedifferencer.RangeDifferencer
+ * @see org.eclipse.jface.text.TextViewer
+ * @see ITokenComparator
+ * @see IDocumentRange
+ * @see org.eclipse.compare.IStreamContentAccessor
+ */
+public class TextMergeViewer extends ContentMergeViewer {
+
+ static class TextMergeAction extends Action implements IUpdate {
+
+ int fOperationCode;
+ ITextOperationTarget fTarget;
+
+ TextMergeAction(ResourceBundle bundle, String key, ITextOperationTarget target, int operationCode) {
+ Utilities.initAction(this, bundle, key);
+ fTarget= target;
+ fOperationCode= operationCode;
+ update();
+ }
+
+ public void run() {
+ if (fOperationCode != -1 && fTarget.canDoOperation(fOperationCode))
+ fTarget.doOperation(fOperationCode);
+ }
+
+ public boolean isEnabled() {
+ return fTarget.canDoOperation(fOperationCode);
+ }
+
+ public void update() {
+ setEnabled(isEnabled());
+ }
+ }
+
+ private static final String MY_UPDATER= "my_updater";
+
+ private static final String SYNC_SCROLLING= "SYNC_SCROLLING";
+
+ private static final String BUNDLE_NAME= "org.eclipse.compare.contentmergeviewer.TextMergeViewerResources";
+
+ // constants
+ /** Width of left and right vertical bar */
+ private static final int MARGIN_WIDTH= 10;
+ /** Width of center bar */
+ private static final int CENTER_WIDTH= 40;
+ /** */
+ private static final int LW= 1;
+ /** Provide more merge controls in Pane toolbar */
+ private static final boolean USE_MORE_CONTROLS= true;
+ /** Selects between smartTokenDiff and mergingTokenDiff */
+ private static final boolean USE_MERGING_TOKEN_DIFF= false;
+ /** if DEAD_STEP is true navigation with the next/previous buttons needs an extra step
+ when wrapping around the beginning or end */
+ private static final boolean DEAD_STEP= false;
+
+ private static final boolean IS_MOTIF= false;
+
+ // Colors to use
+ private static final RGB INCOMING= new RGB(100, 100, 200);
+ private static final RGB INCOMING_FILL= new RGB(230, 230, 240);
+ private static final RGB SELECTED_INCOMING= new RGB(0, 0, 255);
+ private static final RGB SELECTED_INCOMING_FILL= new RGB(255, 255, 255);
+
+ private static final RGB CONFLICT= new RGB(200, 100, 100);
+ private static final RGB CONFLICT_FILL= new RGB(240, 230, 230);
+ private static final RGB SELECTED_CONFLICT= new RGB(255, 0, 0);
+ private static final RGB SELECTED_CONFLICT_FILL= new RGB(255, 255, 255);
+
+ private static final RGB OUTGOING= new RGB(100, 100, 100);
+ private static final RGB OUTGOING_FILL= new RGB(230, 230, 230);
+ private static final RGB SELECTED_OUTGOING= new RGB(0, 0, 0);
+ private static final RGB SELECTED_OUTGOING_FILL= new RGB(255, 255, 255);
+
+ private IDocumentListener fDocumentListener;
+
+ /** All diffs for calculating scrolling position (includes line ranges without changes) */
+ private ArrayList fAllDiffs;
+ /** Subset of above: just real differences. */
+ private ArrayList fChangeDiffs;
+ /** The current diff */
+ private Diff fCurrentDiff;
+
+ private MergeSourceViewer fAncestor;
+ private MergeSourceViewer fLeft;
+ private MergeSourceViewer fRight;
+
+ private int fLeftLineCount;
+ private int fRightLineCount;
+
+ private boolean fLeftContentsChanged;
+ private boolean fRightContentsChanged;
+
+ private boolean fInScrolling;
+
+ private int fPts[]= new int[8]; // scratch area for polygon drawing
+
+ private ActionContributionItem fNextItem; // goto next difference
+ private ActionContributionItem fPreviousItem; // goto previous difference
+ private ActionContributionItem fCopyDiffLeftToRightItem;
+ private ActionContributionItem fCopyDiffRightToLeftItem;
+
+ private boolean fSynchronizedScrolling= true;
+
+ private MergeSourceViewer fFocusPart;
+
+ private boolean fSubDoc= true;
+ private IPositionUpdater fPositionUpdater;
+
+
+ // SWT widgets
+ private BufferedCanvas fAncestorCanvas;
+ private BufferedCanvas fLeftCanvas;
+ private BufferedCanvas fRightCanvas;
+ private ScrollBar fVScrollBar;
+
+ // SWT resources to be disposed
+ private Map fColors;
+
+
+ /**
+ * A Diff represents synchronized character ranges in two or three Documents.
+ * The MergeTextViewer uses Diffs to find differences in line and token ranges.
+ */
+ /* package */ class Diff {
+ /** character range in ancestor document */
+ Position fAncestorPos;
+ /** character range in left document */
+ Position fLeftPos;
+ /** character range in right document */
+ Position fRightPos;
+ /** if this is a TokenDiff fParent points to the enclosing LineDiff */
+ Diff fParent;
+ /** if Diff has been resolved */
+ boolean fResolved;
+ int fDirection;
+ boolean fIsToken= false;
+ ArrayList fDiffs;
+
+ /**
+ * Create Diff from two ranges and an optional parent diff.
+ */
+ Diff(Diff parent, int dir, IDocument ancestorDoc, int ancestorStart, int ancestorEnd,
+ IDocument leftDoc, int leftStart, int leftEnd,
+ IDocument rightDoc, int rightStart, int rightEnd) {
+ fParent= parent != null ? parent : this;
+ fDirection= dir;
+
+ fLeftPos= createPosition(leftDoc, leftStart, leftEnd);
+ fRightPos= createPosition(rightDoc, rightStart, rightEnd);
+ if (ancestorDoc != null)
+ fAncestorPos= createPosition(ancestorDoc, ancestorStart, ancestorEnd);
+ }
+
+ Position createPosition(IDocument doc, int start, int end) {
+ try {
+ int dl= doc.getLength();
+ int l= end-start;
+ if (start+l > dl)
+ l= dl-start;
+
+ Position p= null;
+ try {
+ p= new Position(start, l);
+ } catch (RuntimeException ex) {
+ System.out.println("Diff.createPosition: " + start + " " + l);
+ }
+
+ try {
+ doc.addPosition(MY_UPDATER, p);
+ } catch (BadPositionCategoryException ex) {
+ }
+ return p;
+ } catch (BadLocationException ee) {
+ //System.out.println("Diff.createPosition: " + start + " " + end);
+ }
+ return null;
+ }
+
+ void add(Diff d) {
+ if (fDiffs == null)
+ fDiffs= new ArrayList();
+ fDiffs.add(d);
+ }
+
+ boolean isDeleted() {
+ if (fAncestorPos != null && fAncestorPos.isDeleted())
+ return true;
+ return fLeftPos.isDeleted() || fRightPos.isDeleted();
+ }
+
+ void setResolved(boolean r) {
+ fResolved= r;
+ if (r)
+ fDiffs= null;
+ }
+
+ boolean isResolved() {
+ if (!fResolved && fDiffs != null) {
+ Iterator e= fDiffs.iterator();
+ while (e.hasNext()) {
+ Diff d= (Diff) e.next();
+ if (!d.isResolved())
+ return false;
+ }
+ return true;
+ }
+ return fResolved;
+ }
+
+ Position getPosition(MergeSourceViewer w) {
+ if (w == fLeft)
+ return fLeftPos;
+ if (w == fRight)
+ return fRightPos;
+ if (w == fAncestor)
+ return fAncestorPos;
+ return null;
+ }
+
+ /**
+ * Returns true if given character range overlaps with this Diff.
+ */
+ boolean contains(MergeSourceViewer w, int start, int end) {
+ Position h= getPosition(w);
+ if (h != null) {
+ int offset= h.getOffset();
+ if (start >= offset) {
+ int endPos= offset+h.getLength();
+ if (end < endPos)
+ return true;
+ if (endPos == w.getDocument().getLength())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int getMaxDiffHeight(boolean withAncestor) {
+ Point region= new Point(0, 0);
+ int h= fLeft.getLineRange(fLeftPos, region).y;
+ if (withAncestor)
+ h= Math.max(h, fAncestor.getLineRange(fAncestorPos, region).y);
+ return Math.max(h, fRight.getLineRange(fRightPos, region).y);
+ }
+ }
+
+ //---- MergeTextViewer
+
+ /**
+ * Creates a text merge viewer under the given parent control.
+ *
+ * @param parent the parent control
+ * @param configuration the configuration object
+ */
+ public TextMergeViewer(Composite parent, CompareConfiguration configuration) {
+ this(parent, SWT.NULL, configuration);
+ }
+
+ /**
+ * Creates a text merge viewer under the given parent control.
+ *
+ * @param parent the parent control
+ * @param style SWT style bits for top level composite of this viewer
+ * @param configuration the configuration object
+ */
+ public TextMergeViewer(Composite parent, int style, CompareConfiguration configuration) {
+ super(style, ResourceBundle.getBundle(BUNDLE_NAME), configuration);
+
+ buildControl(parent);
+
+ fSynchronizedScrolling= Utilities.getBoolean(configuration, SYNC_SCROLLING, fSynchronizedScrolling);
+
+ fDocumentListener= new IDocumentListener() {
+
+ public void documentAboutToBeChanged(DocumentEvent e) {
+ }
+
+ public void documentChanged(DocumentEvent e) {
+ TextMergeViewer.this.documentChanged(e);
+ }
+ };
+ }
+
+ public String getTitle() {
+ return "Text Compare";
+ }
+
+ /**
+ * Configures the passed text viewer.
+ * This method is called after the three text viewers have been created for the
+ * content areas.
+ * The <code>TextMergeViewer</code> implementation of this method does nothing.
+ * Subclasses may reimplement to provide a specific configuration for the text viewer.
+ *
+ * @param textViewer the text viewer to configure
+ */
+ protected void configureTextViewer(TextViewer textViewer) {
+ }
+
+ /**
+ * Creates an <code>ITokenComparator</code> which is used to show the
+ * intra line differences.
+ * The <code>TextMergeViewer</code> implementation of this method returns a
+ * tokenizer that breaks a line into words separated by whitespace.
+ * Subclasses may reimplement to provide a specific tokenizer.
+ *
+ * @return a ITokenComparator which is used for a second level token compare.
+ */
+ protected ITokenComparator createTokenComparator(String s) {
+ return new TokenComparator(s);
+ }
+
+ /**
+ * Returns a document partitioner which is suitable for the underlying content type.
+ * This method is only called if the input provided by the content provider is a
+ * <code>IStreamContentAccessor</code> and an internal document must be created. This
+ * document is initialized with the partitioner returned from this method.
+ * <p>
+ * The <code>TextMergeViewer</code> implementation of this method returns
+ * <code>null</code>. Subclasses may reimplement to create a partitioner for a
+ * specific content type.
+ *
+ * @return a document partitioner, or <code>null</code>
+ */
+ protected IDocumentPartitioner getDocumentPartitioner() {
+ return null;
+ }
+
+ /**
+ * 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) {
+
+ fLeftCanvas= null;
+ fRightCanvas= null;
+ fVScrollBar= null;
+
+ unsetDocument(fAncestor);
+ unsetDocument(fLeft);
+ unsetDocument(fRight);
+
+ if (fColors != null) {
+ Iterator i= fColors.values().iterator();
+ while (i.hasNext()) {
+ Color color= (Color) i.next();
+ if (!color.isDisposed())
+ color.dispose();
+ }
+ }
+
+ super.handleDispose(event);
+ }
+
+ //-------------------------------------------------------------------------------------------------------------
+ //--- internal ------------------------------------------------------------------------------------------------
+ //-------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Creates the specific SWT controls for the content areas.
+ * Clients must not call or override this method.
+ */
+ protected void createControls(Composite composite) {
+
+ // 1st row
+ fAncestorCanvas= new BufferedCanvas(composite, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fAncestor, fAncestorCanvas, false);
+ }
+ };
+
+ fAncestor= createPart(composite);
+ fAncestor.setEditable(false);
+
+ // 2nd row
+ fLeftCanvas= new BufferedCanvas(composite, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fLeft, fLeftCanvas, false);
+ }
+ };
+
+ fLeft= createPart(composite);
+ fLeft.getTextWidget().getVerticalBar().setVisible(false);
+
+ fRight= createPart(composite);
+ fRight.getTextWidget().getVerticalBar().setVisible(false);
+
+ fRightCanvas= new BufferedCanvas(composite, SWT.V_SCROLL) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fRight, fRightCanvas, fSynchronizedScrolling);
+ }
+ };
+
+ fVScrollBar= fRightCanvas.getVerticalBar();
+ fVScrollBar.setIncrement(1);
+ fVScrollBar.setVisible(true);
+ fVScrollBar.addListener(SWT.Selection,
+ new Listener() {
+ public void handleEvent(Event e) {
+ scrollVertical(((ScrollBar)e.widget).getSelection(), null);
+ }
+ }
+ );
+ }
+
+ /* package */ boolean internalSetFocus() {
+ if (fFocusPart != null) {
+ StyledText st= fFocusPart.getTextWidget();
+ if (st != null)
+ return st.setFocus();
+ }
+ return false; // could not set focus
+ }
+
+ /**
+ * Creates the central Canvas.
+ * Called from ContentMergeViewer.
+ */
+ /* package */ Control createCenter(Composite parent) {
+ if (fSynchronizedScrolling) {
+ final Canvas canvas= new BufferedCanvas(parent, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintCenter(this, gc);
+ }
+ };
+ new Resizer(canvas, HORIZONTAL);
+ return canvas;
+ }
+ return super.createCenter(parent);
+ }
+
+ /**
+ * Returns width of central canvas.
+ * Overridden from ContentMergeViewer.
+ */
+ /* package */ int getCenterWidth() {
+ if (fSynchronizedScrolling)
+ return CENTER_WIDTH;
+ return super.getCenterWidth();
+ }
+
+ /**
+ * Creates and initializes a text part.
+ */
+ private MergeSourceViewer createPart(Composite parent) {
+
+ final MergeSourceViewer part= new MergeSourceViewer(parent);
+ final StyledText te= part.getTextWidget();
+
+ te.addPaintListener(
+ new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ paint(e, part);
+ }
+ }
+ );
+ te.addKeyListener(
+ new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ handleSelectionChanged(part);
+ }
+ }
+ );
+ te.addMouseListener(
+ new MouseAdapter() {
+ public void mouseDown(MouseEvent e) {
+ //syncViewport(part);
+ handleSelectionChanged(part);
+ }
+ }
+ );
+
+ te.addFocusListener(
+ new FocusAdapter() {
+ public void focusGained(FocusEvent fe) {
+ fFocusPart= part;
+ }
+ }
+ );
+
+ MenuManager mm= new MenuManager();
+ mm.setRemoveAllWhenShown(true);
+ mm.addMenuListener(
+ new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mm) {
+ fillContextMenu(mm, part);
+ }
+ }
+ );
+ te.setMenu(mm.createContextMenu(te));
+
+ part.addViewportListener(
+ new IViewportListener() {
+ public void viewportChanged(int verticalPosition) {
+ syncViewport(part);
+ }
+ }
+ );
+
+ configureTextViewer(part);
+
+ return part;
+ }
+
+ /**
+ * Allows the viewer to add menus and/or tools to the context menu.
+ */
+ private void fillContextMenu(IMenuManager menu, MergeSourceViewer part) {
+
+ ResourceBundle rb= getResourceBundle();
+ boolean mutable= part.isEditable();
+
+ menu.add(new Separator("undo"));
+ if (mutable) {
+ menu.add(new TextMergeAction(rb, "action.UndoText.", part, MergeSourceViewer.UNDO));
+ menu.add(new TextMergeAction(rb, "action.RedoText.", part, MergeSourceViewer.REDO));
+ }
+
+ menu.add(new Separator("ccp"));
+ if (mutable)
+ menu.add(new TextMergeAction(rb, "action.CutText.", part, MergeSourceViewer.CUT));
+ menu.add(new TextMergeAction(rb, "action.CopyText.", part, MergeSourceViewer.COPY));
+ if (mutable) {
+ menu.add(new TextMergeAction(rb, "action.PasteText.", part, MergeSourceViewer.PASTE));
+ menu.add(new TextMergeAction(rb, "action.DeleteText.", part, MergeSourceViewer.DELETE));
+ }
+ menu.add(new TextMergeAction(rb, "action.SelectAllText.", part, MergeSourceViewer.SELECT_ALL));
+
+ menu.add(new Separator("edit"));
+ menu.add(new Separator("find"));
+ //contributeAction(menu, part, "find", "Find");
+ //menu.add(new TextMergeAction(rb, "action.ContentAssistProposal.", part, MergeSourceViewer.CONTENTASSIST_PROPOSAL));
+ //menu.add(new TextMergeAction(rb, "action.ContentAssistTip.", part, MergeSourceViewer.CONTENTASSIST_TIP));
+
+ menu.add(new Separator("save"));
+ //if (mutable)
+ // contributeAction(menu, part, "save", "Save");
+
+ menu.add(new Separator("rest"));
+ }
+
+ /**
+ * Initializes the text viewers of the three content areas with the given input objects.
+ * Subclasses may extend.
+ */
+ protected void updateContent(Object ancestor, Object left, Object right) {
+
+ // clear stuff
+ fCurrentDiff= null;
+ fChangeDiffs= null;
+ fAllDiffs= null;
+
+ fLeftContentsChanged= false;
+ fRightContentsChanged= false;
+
+ CompareConfiguration cc= getCompareConfiguration();
+ IMergeViewerContentProvider cp= getMergeContentProvider();
+
+ boolean rightEditable= cc.isRightEditable() && cp.isRightEditable(getInput());
+ boolean leftEditable= cc.isLeftEditable() && cp.isLeftEditable(getInput());
+
+ fRight.setEditable(rightEditable);
+ fLeft.setEditable(leftEditable);
+
+ // set new documents
+ setDocument(fAncestor, ancestor);
+
+ setDocument(fLeft, left);
+ fLeftLineCount= fLeft.getLineCount();
+
+ setDocument(fRight, right);
+ fRightLineCount= fRight.getLineCount();
+
+ doDiff();
+
+ invalidateLines();
+ updateVScrollBar();
+ selectFirstDiff();
+ }
+
+ private void updateDiffBackground(Diff diff) {
+
+ if (diff == null || diff.fIsToken)
+ return;
+ Point region= new Point(0, 0);
+
+ Color c= getColor(getFillColor(diff));
+ if (c == null)
+ return;
+
+ if (isThreeWay())
+ fAncestor.setLineBackground(diff.fAncestorPos, c);
+ fLeft.setLineBackground(diff.fLeftPos, c);
+ fRight.setLineBackground(diff.fRightPos, c);
+ }
+
+ private void unsetDocument(MergeSourceViewer tp) {
+ IDocument oldDoc= tp.getDocument();
+ if (oldDoc != null) { // deinstall old positions
+ if (fPositionUpdater != null)
+ oldDoc.removePositionUpdater(fPositionUpdater);
+ try {
+ oldDoc.removePositionCategory(MY_UPDATER);
+ } catch (BadPositionCategoryException ex) {
+ }
+ }
+ }
+
+ /**
+ * Called whenver one of the documents changes.
+ * Sets the dirty state of this viewer and updates the lines.
+ * Implements IDocumentListener.
+ */
+ private void documentChanged(DocumentEvent e) {
+
+ IDocument doc= e.getDocument();
+
+ if (doc == fLeft.getDocument()) {
+ fLeftContentsChanged= true;
+ setLeftDirty(true);
+ } else if (doc == fRight.getDocument()) {
+ setRightDirty(true);
+ fRightContentsChanged= true;
+ }
+
+ updateLines(doc);
+ }
+
+ /**
+ * Returns true if a new Document could be installed.
+ */
+ private boolean setDocument(MergeSourceViewer tp, Object o) {
+
+ if (tp == null)
+ return false;
+
+ IDocument newDoc= null;
+
+ if (o instanceof IDocumentRange) {
+ newDoc= ((IDocumentRange)o).getDocument();
+
+ } else if (o instanceof Document) {
+ newDoc= (Document) o;
+
+ } else if (o instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) o;
+ if (sca != null) {
+ String s= null;
+
+ try {
+ s= Utilities.readString(sca.getContents());
+ } catch (CoreException ex) {
+ }
+
+ newDoc= new Document(s != null ? s : "");
+ IDocumentPartitioner partitioner= getDocumentPartitioner();
+ if (partitioner != null) {
+ newDoc.setDocumentPartitioner(partitioner);
+ partitioner.connect(newDoc);
+ }
+ }
+ }
+
+ boolean enabled= true;
+ if (newDoc == null) {
+ newDoc= new Document("");
+ enabled= false;
+ }
+
+ IDocument oldDoc= tp.getDocument();
+
+ unsetDocument(tp);
+
+ if (newDoc != null) {
+ newDoc.addPositionCategory(MY_UPDATER);
+ if (fPositionUpdater == null)
+ fPositionUpdater= new DefaultPositionUpdater(MY_UPDATER);
+ newDoc.addPositionUpdater(fPositionUpdater);
+ }
+
+ if (newDoc != oldDoc) { // new document
+
+ // deinstall old document
+ if (oldDoc != null)
+ oldDoc.removeDocumentListener(fDocumentListener);
+
+ // install new document
+ if (newDoc != null) {
+
+ IRegion region= null;
+ if (o instanceof IDocumentRange) {
+ Position range= ((IDocumentRange) o).getRange();
+ if (range != null)
+ region= new Region(range.getOffset(), range.getLength());
+ }
+
+ tp.setRegion(region);
+ if (fSubDoc) {
+ if (region != null) {
+ IRegion r= normalizeDocumentRegion(newDoc, region);
+ tp.setDocument(newDoc, r.getOffset(), r.getLength());
+ } else
+ tp.setDocument(newDoc);
+ } else
+ tp.setDocument(newDoc);
+
+ newDoc.addDocumentListener(fDocumentListener);
+ }
+
+ } else { // just different range
+
+ IRegion region= null;
+ if (o instanceof IDocumentRange) {
+ Position range= ((IDocumentRange) o).getRange();
+ if (range != null)
+ region= new Region(range.getOffset(), range.getLength());
+ }
+
+ tp.setRegion(region);
+ if (fSubDoc) {
+ if (region != null) {
+ IRegion r= normalizeDocumentRegion(tp.getDocument(), region);
+ tp.setVisibleRegion(r.getOffset(), r.getLength());
+ }
+ }
+ }
+
+ tp.setEnabled(enabled);
+
+ return enabled;
+ }
+
+ /**
+ * Returns the contents of the underlying document as an array of bytes.
+ *
+ * @param left if <code>true</code> the contents of the left side is returned; otherwise the right side
+ * @return the contents of the left or right document
+ */
+ protected byte[] getContents(boolean left) {
+
+ if (left) {
+ if (fLeftContentsChanged)
+ return fLeft.getDocument().get().getBytes();
+ } else {
+ if (fRightContentsChanged)
+ return fRight.getDocument().get().getBytes();
+ }
+ return null;
+ }
+
+ private IRegion normalizeDocumentRegion(IDocument doc, IRegion region) {
+
+ if (region == null || doc == null)
+ return region;
+
+ int maxLength= doc.getLength();
+
+ int start= region.getOffset();
+ if (start < 0)
+ start= 0;
+ else if (start > maxLength)
+ start= maxLength;
+
+ int length= region.getLength();
+ if (length < 0)
+ length= 0;
+ else if (start + length > maxLength)
+ length= maxLength - start;
+
+ return new Region(start, length);
+ }
+
+ protected final void handleResizeAncestor(int x, int y, int width, int height) {
+ if (width > 0) {
+ Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.setVisible(true);
+ if (fAncestor.isControlOkToUse())
+ fAncestor.getTextWidget().setVisible(true);
+ fAncestorCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ fAncestor.getTextWidget().setBounds(x+MARGIN_WIDTH, y, width-MARGIN_WIDTH, height);
+ } else {
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.setVisible(false);
+ if (fAncestor.isControlOkToUse()) {
+ StyledText t= fAncestor.getTextWidget();
+ t.setVisible(false);
+ t.setBounds(0, 0, 0, 0);
+ }
+ }
+ }
+
+ /**
+ * Lays out everything.
+ */
+ protected final void handleResizeLeftRight(int x, int y, int width1, int centerWidth, int width2, int height) {
+
+ // determine some minimal sizes
+ int scrollbarWidth= 0;
+ if (fSynchronizedScrolling && fRightCanvas != null)
+ scrollbarWidth= fRightCanvas.computeTrim(0, 0, 0, 0).width;
+
+ Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+
+ // determine some derived sizes
+ Composite composite= (Composite) getControl();
+
+ int leftTextWidth= width1-MARGIN_WIDTH;
+ int rightTextWidth= width2-MARGIN_WIDTH-scrollbarWidth;
+
+ fLeftCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ x+= MARGIN_WIDTH;
+
+ fLeft.getTextWidget().setBounds(x, y, leftTextWidth, height);
+ x+= leftTextWidth;
+
+ if (fCenter == null || fCenter.isDisposed())
+ fCenter= createCenter(composite);
+ fCenter.setBounds(x, y, centerWidth, height-scrollbarHeight);
+ x+= centerWidth;
+
+ if (!fSynchronizedScrolling) {
+ if (fRightCanvas != null) {
+ fRightCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ fRightCanvas.redraw();
+ }
+ // we draw the canvas to the left of the text widget
+ x+= MARGIN_WIDTH;
+ }
+
+ fRight.getTextWidget().setBounds(x, y, rightTextWidth, height);
+ x+= rightTextWidth;
+
+ if (fSynchronizedScrolling && fRightCanvas != null)
+ fRightCanvas.setBounds(x, y, scrollbarWidth+MARGIN_WIDTH, height-scrollbarHeight);
+
+ // doesn't work since TextEditors don't have their correct size yet.
+ updateVScrollBar();
+ }
+
+ /**
+ * Track selection changes to update the current Diff.
+ */
+ private void handleSelectionChanged(MergeSourceViewer tw) {
+ Point p= tw.getSelectedRange();
+ Diff d= findDiff(tw, p.x, p.x+p.y);
+ setCurrentDiff(d, false); // don't select or reveal
+ }
+
+ //---- the differencing
+
+ /**
+ * Perform a two level 2- or 3-way diff.
+ * The first level is based on line comparison, the second level on token comparison.
+ */
+ private void doDiff() {
+
+ fAllDiffs= new ArrayList();
+ fChangeDiffs= new ArrayList();
+
+ IDocument aDoc= null;
+ IDocument iDoc= fLeft.getDocument();
+ IDocument oDoc= fRight.getDocument();
+
+ if (iDoc == null || oDoc == null)
+ return;
+
+ IRegion aRegion= null;
+ IRegion iRegion= fLeft.getRegion();
+ IRegion oRegion= fRight.getRegion();
+
+ boolean threeWay= isThreeWay();
+
+ if (threeWay) {
+ aDoc= fAncestor.getDocument();
+ aRegion= fAncestor.getRegion();
+ }
+
+ fAncestor.resetLineBackground();
+ fLeft.resetLineBackground();
+ fRight.resetLineBackground();
+
+ boolean ignoreWhiteSpace= Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false);
+
+ DocLineComparator sright= new DocLineComparator(oDoc, oRegion, ignoreWhiteSpace);
+ DocLineComparator sleft= new DocLineComparator(iDoc, iRegion, ignoreWhiteSpace);
+ DocLineComparator sancestor= null;
+ if (aDoc != null)
+ sancestor= new DocLineComparator(aDoc, aRegion, ignoreWhiteSpace);
+
+ if (!fSubDoc && oRegion != null && iRegion != null) {
+ // we have to add a diff for the ignored lines
+
+ int astart= 0;
+ int as= 0;
+ if (aRegion != null) {
+ astart= aRegion.getOffset();
+ as= Math.max(0, astart-1);
+ }
+ int ys= Math.max(0, iRegion.getOffset()-1);
+ int ms= Math.max(0, oRegion.getOffset()-1);
+
+ if (as > 0 || ys > 0 || ms > 0) {
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, 0, astart,
+ iDoc, 0, iRegion.getOffset(),
+ oDoc, 0, oRegion.getOffset());
+ fAllDiffs.add(diff);
+ }
+ }
+
+ RangeDifference[] e= RangeDifferencer.findRanges(sancestor, sleft, sright);
+
+ for (int i= 0; i < e.length; i++) {
+ String a= null, s= null, d= null;
+ RangeDifference es= e[i];
+
+ int kind= es.kind();
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (sancestor != null) {
+ ancestorStart= sancestor.getTokenStart(es.ancestorStart());
+ ancestorEnd= sancestor.getTokenEnd(es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart= sleft.getTokenStart(es.leftStart());
+ int leftEnd= sleft.getTokenEnd(es.leftStart(), es.leftLength());
+
+ int rightStart= sright.getTokenStart(es.rightStart());
+ int rightEnd= sright.getTokenEnd(es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(null, kind,
+ aDoc, ancestorStart, ancestorEnd,
+ iDoc, leftStart, leftEnd,
+ oDoc, rightStart, rightEnd);
+
+ fAllDiffs.add(diff); // remember all range diffs for scrolling
+
+ if (ignoreWhiteSpace) {
+ if (sancestor != null)
+ a= sancestor.extract(es.ancestorStart(), es.ancestorLength());
+ s= sleft.extract(es.leftStart(), es.leftLength());
+ d= sright.extract(es.rightStart(), es.rightLength());
+
+ if ((a == null || a.trim().length() == 0) && s.trim().length() == 0 && d.trim().length() == 0)
+ continue;
+ }
+
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ fChangeDiffs.add(diff); // here we remember only the real diffs
+ updateDiffBackground(diff);
+
+ if (s == null)
+ s= sleft.extract(es.leftStart(), es.leftLength());
+ if (d == null)
+ d= sright.extract(es.rightStart(), es.rightLength());
+
+ if (s.length() > 0 && d.length() > 0) {
+ if (a == null && sancestor != null)
+ a= sancestor.extract(es.ancestorStart(), es.ancestorLength());
+ if (USE_MERGING_TOKEN_DIFF)
+ mergingTokenDiff(diff, aDoc, a, oDoc, d, iDoc, s);
+ else
+ simpleTokenDiff(diff, aDoc, a, oDoc, d, iDoc, s);
+ }
+ }
+ }
+
+ if (!fSubDoc && oRegion != null && iRegion != null) {
+ // we have to add a diff for the ignored lines
+
+ int aEnd= 0;
+ int aLen= 0;
+ if (aRegion != null && aDoc != null) {
+ aEnd= aRegion.getOffset()+aRegion.getLength();
+ aLen= aDoc.getLength();
+ }
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, aEnd, aLen,
+ iDoc, iRegion.getOffset()+iRegion.getLength(), iDoc.getLength(),
+ oDoc, oRegion.getOffset()+oRegion.getLength(), oDoc.getLength());
+ fAllDiffs.add(diff);
+ }
+ }
+
+ private int getTokenEnd(ITokenComparator tc, int start, int count) {
+ if (count <= 0)
+ return tc.getTokenStart(start);
+ int index= start + count - 1;
+ int l= tc.getTokenLength(index);
+ if (l < 0)
+ System.out.println("getTokenEnd: l < 0");
+ return tc.getTokenStart(index) + l;
+ }
+
+ /**
+ * Performs a token based 3-way diff on the character range specified by the given baseDiff.
+ */
+ private void simpleTokenDiff(final Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ ITokenComparator sa= null;
+ if (ancestorDoc != null) {
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ ancestorEnd= ancestorStart + baseDiff.fAncestorPos.getLength();
+ sa= createTokenComparator(a);
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ int rightEnd= rightStart + baseDiff.fRightPos.getLength();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ int leftEnd= leftStart + baseDiff.fLeftPos.getLength();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] e= RangeDifferencer.findRanges(sa, sy, sm);
+ for (int i= 0; i < e.length; i++) {
+ RangeDifference es= e[i];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+
+ int ancestorStart2= ancestorStart;
+ int ancestorEnd2= ancestorStart;
+ if (ancestorDoc != null) {
+ ancestorStart2 += sa.getTokenStart(es.ancestorStart());
+ ancestorEnd2 += getTokenEnd(sa, es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart2= leftStart + sy.getTokenStart(es.leftStart());
+ int leftEnd2= leftStart + getTokenEnd(sy, es.leftStart(), es.leftLength());
+
+ int rightStart2= rightStart + sm.getTokenStart(es.rightStart());
+ int rightEnd2= rightStart + getTokenEnd(sm, es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(baseDiff, kind,
+ ancestorDoc, ancestorStart2, ancestorEnd2,
+ leftDoc, leftStart2, leftEnd2,
+ rightDoc, rightStart2, rightEnd2);
+ diff.fIsToken= true;
+ // add to base Diff
+ baseDiff.add(diff);
+ }
+ }
+ }
+
+ /**
+ * Performs a "smart" token based 3-way diff on the character range specified by the given baseDiff.
+ * It is smart because it tries to minimize the number of token diffs by merging them.
+ */
+ private void mergingTokenDiff(Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+
+ ITokenComparator sa= null;
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (ancestorDoc != null) {
+ sa= createTokenComparator(a);
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ ancestorEnd= ancestorStart + baseDiff.fAncestorPos.getLength();
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ int rightEnd= rightStart + baseDiff.fRightPos.getLength();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ int leftEnd= leftStart + baseDiff.fLeftPos.getLength();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] r= RangeDifferencer.findRanges(sa, sy, sm);
+
+ for (int i= 0; i < r.length; i++) {
+ RangeDifference es= r[i];
+
+ // determine range of diffs in one line
+ int start= i;
+ int leftLine= -1;
+ int rightLine= -1;
+ try {
+ leftLine= leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart()));
+ rightLine= rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart()));
+ } catch (BadLocationException e) {
+ }
+ i++;
+ for (; i < r.length; i++) {
+ es= r[i];
+ int ll, rl;
+ try {
+ if (leftLine != leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart())))
+ break;
+ if (rightLine != rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart())))
+ break;
+ } catch (BadLocationException e) {
+ }
+ }
+ int end= i;
+
+ // find first diff from left
+ RangeDifference first= null;
+ for (int ii= start; ii < end; ii++) {
+ es= r[ii];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ first= es;
+ break;
+ }
+ }
+
+ // find first diff from mine
+ RangeDifference last= null;
+ for (int ii= end-1; ii >= start; ii--) {
+ es= r[ii];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ last= es;
+ break;
+ }
+ }
+
+
+ if (first != null && last != null) {
+
+ int ancestorStart2= 0;
+ int ancestorEnd2= 0;
+ if (ancestorDoc != null) {
+ ancestorStart2= ancestorStart+sa.getTokenStart(first.ancestorStart());
+ ancestorEnd2= ancestorStart+getTokenEnd(sa, last.ancestorStart(), last.ancestorLength());
+ }
+
+ int leftStart2= leftStart+sy.getTokenStart(first.leftStart());
+ int leftEnd2= leftStart+getTokenEnd(sy, last.leftStart(), last.leftLength());
+
+ int rightStart2= rightStart+sm.getTokenStart(first.rightStart());
+ int rightEnd2= rightStart+getTokenEnd(sm, last.rightStart(), last.rightLength());
+
+ Diff diff= new Diff(baseDiff, first.kind(),
+ ancestorDoc, ancestorStart2, ancestorEnd2+1,
+ leftDoc, leftStart2, leftEnd2+1,
+ rightDoc, rightStart2, rightEnd2+1);
+ diff.fIsToken= true;
+ baseDiff.add(diff);
+ }
+ }
+ }
+
+ //---- update UI stuff
+
+ private void updateControls() {
+
+ boolean leftToRight= false;
+ boolean rightToLeft= false;
+
+ if (fCurrentDiff != null) {
+ IMergeViewerContentProvider cp= getMergeContentProvider();
+ if (cp != null) {
+ rightToLeft= cp.isLeftEditable(getInput());
+ leftToRight= cp.isRightEditable(getInput());
+ }
+ }
+
+ if (fCopyDiffLeftToRightItem != null)
+ ((Action)fCopyDiffLeftToRightItem.getAction()).setEnabled(leftToRight);
+ if (fCopyDiffRightToLeftItem != null)
+ ((Action)fCopyDiffRightToLeftItem.getAction()).setEnabled(rightToLeft);
+//
+// int fAutoResolve= 0;
+// int fUnresolvedDiffs= 0;
+// if (fChangeDiffs != null) {
+// fUnresolvedDiffs= fChangeDiffs.size();
+// if (fUnresolvedDiffs > 0) {
+// Iterator e= fChangeDiffs.iterator();
+// while (e.hasNext()) {
+// Diff diff= (Diff) e.next();
+// if (diff.isResolved()) {
+// fUnresolvedDiffs--;
+// } else {
+// if (diff.fDirection == RangeDifference.RIGHT || diff.fDirection == RangeDifference.LEFT) {
+// fAutoResolve++;
+// }
+// }
+// }
+// }
+// }
+//
+// boolean acceptReject= false;
+// boolean both= false;
+//
+// String s= "";
+//
+// if (fCurrentDiff != null) {
+// if (fCurrentDiff.isResolved()) {
+// s= "resolved";
+// } else {
+// s= "unresolved";
+//
+// IMergeViewerContentProvider twr= getContentProvider();
+// Object input= getInput();
+// boolean rightEditable= twr.isRightEditable(input);
+// boolean leftEditable= twr.isLeftEditable(input);
+//
+// switch (fCurrentDiff.fDirection) {
+// case RangeDifference.RIGHT: // outgoing
+// if (rightEditable)
+// acceptReject= true;
+// break;
+// case RangeDifference.LEFT: // incoming
+// if (leftEditable)
+// acceptReject= true;
+// break;
+// case RangeDifference.CONFLICT:
+// if (rightEditable) {
+// acceptReject= true;
+// both= true;
+// }
+// break;
+// }
+// }
+// } else {
+// if (fUnresolvedDiffs <= 0)
+// s= "allresolved";
+// else
+// s= "same";
+// }
+//
+// getAction(fTakeLeftActionItem).setEnabled(acceptReject);
+// getAction(fRejectItem).setEnabled(acceptReject);
+// if (fBothItem != null)
+// getAction(fBothItem).setEnabled(both);
+// if (fAutoItem != null)
+// getAction(fAutoItem).setEnabled(fAutoResolve > 0);
+//
+// if (s.length() > 0)
+// s= getBundle().getString("status." + s);
+//
+// ApplicationWindow b= getApplicationWindow();
+// if (b != null) {
+// String format= fBundle.getString(fUnresolvedDiffs > 0
+// ? "status.unresolvedformat"
+// : "status.resolvedformat");
+// b.setStatus(MessageFormat.format(format, new String[] { s, "" + fUnresolvedDiffs } ));
+// }
+ }
+
+
+ protected void updateHeader() {
+
+ super.updateHeader();
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+ boolean m= content.isRightEditable(input);
+ boolean y= content.isLeftEditable(input);
+
+ CompareConfiguration mp= getCompareConfiguration();
+ //fLeft.setEditable(y && mp.isLeftEditable());
+ //fRight.setEditable(m && mp.isRightEditable());
+
+ updateControls();
+ }
+
+ /**
+ * Creates the two items for copying a difference range from one side to the other
+ * and adds them to the given toolbar manager.
+ */
+ protected void createToolItems(ToolBarManager tbm) {
+
+// if (USE_MORE_CONTROLS) {
+// fBothItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptBoth.") {
+// public void actionPerformed(Window w) {
+// accept(fCurrentDiff, true, false);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fBothItem);
+//
+// fAutoItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptAll.") {
+// public void actionPerformed(Window w) {
+// autoResolve();
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fAutoItem);
+// }
+// fRejectItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptIgnoreNow.") {
+// public void actionPerformed(Window w) {
+// reject(fCurrentDiff, true);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fRejectItem);
+//
+ Action a= new ChangePropertyAction(getResourceBundle(), getCompareConfiguration(), "action.SynchMode.", SYNC_SCROLLING);
+ a.setChecked(fSynchronizedScrolling);
+ tbm.appendToGroup("modes", a);
+
+ tbm.add(new Separator());
+
+ a= new Action() {
+ public void run() {
+ navigate(true);
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.NextDiff.");
+ fNextItem= new ActionContributionItem(a);
+ tbm.appendToGroup("navigation", fNextItem);
+
+ a= new Action() {
+ public void run() {
+ navigate(false);
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.PrevDiff.");
+ fPreviousItem= new ActionContributionItem(a);
+ tbm.appendToGroup("navigation", fPreviousItem);
+
+
+ CompareConfiguration cc= getCompareConfiguration();
+
+ if (cc.isRightEditable()) {
+ a= new Action() {
+ public void run() {
+ copyDiffLeftToRight();
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.CopyDiffLeftToRight.");
+ fCopyDiffLeftToRightItem= new ActionContributionItem(a);
+ tbm.appendToGroup("merge", fCopyDiffLeftToRightItem);
+ }
+
+ if (cc.isLeftEditable()) {
+ a= new Action() {
+ public void run() {
+ copyDiffRightToLeft();
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.CopyDiffRightToLeft.");
+ fCopyDiffRightToLeftItem= new ActionContributionItem(a);
+ tbm.appendToGroup("merge", fCopyDiffRightToLeftItem);
+ }
+ }
+
+ /* package */ void propertyChange(PropertyChangeEvent event) {
+
+ String key= event.getProperty();
+
+ if (key.equals(CompareConfiguration.IGNORE_WHITESPACE)) {
+ // clear stuff
+ fCurrentDiff= null;
+ fChangeDiffs= null;
+ fAllDiffs= null;
+
+ doDiff();
+
+ invalidateLines();
+ updateVScrollBar();
+
+ selectFirstDiff();
+ } else if (key.equals(SYNC_SCROLLING)) {
+
+ boolean b= Utilities.getBoolean(getCompareConfiguration(), SYNC_SCROLLING, true);
+ if (b != fSynchronizedScrolling)
+ toggleSynchMode();
+
+ } else
+ super.propertyChange(event);
+ }
+
+ private void selectFirstDiff() {
+ Diff firstDiff= findNext(fRight, fChangeDiffs, -1, -1);
+ setCurrentDiff(firstDiff, true);
+ }
+
+ private void toggleSynchMode() {
+ fSynchronizedScrolling= ! fSynchronizedScrolling;
+
+ scrollVertical(0, null);
+
+ // throw away central control (Sash or Canvas)
+ Control center= getCenter();
+ if (center != null && !center.isDisposed())
+ center.dispose();
+
+ fLeft.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
+ fRight.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
+
+ // recreates central control (Sash or Canvas)
+ //handleResize();
+ fComposite.layout(true);
+ }
+
+ protected void updateToolItems() {
+
+ boolean visible= false;
+ Object input= getInput();
+ if (input != null) {
+ visible= true;
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ //boolean y= getMergePolicy().isLeftEditable();
+ //boolean m= getMergePolicy().isRightEditable();
+
+ //destinationEditable= content.isRightEditable(getInput());
+ //destinationEditable= content.isLeftEditable(getInput());
+ /*
+ if (USE_MORE_CONTROLS) {
+ fBothItem.setVisible(destinationEditable);
+ fAutoItem.setVisible(destinationEditable);
+ }
+ fRejectItem.setVisible(destinationEditable);
+ */
+ }
+
+ //fNextItem.setVisible(visible);
+ //fPreviousItem.setVisible(visible);
+
+ super.updateToolItems();
+ }
+
+ //---- painting lines
+
+ /**
+ *
+ */
+ private void updateLines(IDocument d) {
+
+ boolean left= false;
+ boolean right= false;
+
+ // FIXME: this optimization is incorrect because
+ // it doesn't take replace operations into account where
+ // the old and new line count does not differ
+ if (d == fLeft.getDocument()) {
+ int l= fLeft.getLineCount();
+ left= fLeftLineCount != l;
+ fLeftLineCount= l;
+ } else if (d == fRight.getDocument()) {
+ int l= fRight.getLineCount();
+ right= fRightLineCount != l;
+ fRightLineCount= l;
+ }
+
+ if (left || right) {
+
+ if (left) {
+ if (fLeftCanvas != null)
+ fLeftCanvas.redraw();
+ } else {
+ if (fRightCanvas != null)
+ fRightCanvas.redraw();
+ }
+ Control center= getCenter();
+ if (center != null)
+ center.redraw();
+
+ updateVScrollBar();
+ }
+ }
+
+ private void invalidateLines() {
+ if (isThreeWay()) {
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.redraw();
+ if (fAncestor.isControlOkToUse())
+ fAncestor.getTextWidget().redraw();
+ }
+
+ if (Utilities.okToUse(fLeftCanvas))
+ fLeftCanvas.redraw();
+
+ if (fLeft.isControlOkToUse())
+ fLeft.getTextWidget().redraw();
+
+ if (Utilities.okToUse(getCenter()))
+ getCenter().redraw();
+
+ if (fRight.isControlOkToUse())
+ fRight.getTextWidget().redraw();
+
+ if (Utilities.okToUse(fRightCanvas))
+ fRightCanvas.redraw();
+ }
+
+ private void paintCenter(Canvas canvas, GC g) {
+
+ if (! fSynchronizedScrolling)
+ return;
+
+ int lineHeight= fLeft.getTextWidget().getLineHeight();
+ int visibleHeight= fRight.getViewportHeight();
+
+ Point size= canvas.getSize();
+ int x= 0;
+ int w= size.x;
+
+ g.setBackground(canvas.getBackground());
+ g.fillRectangle(x+1, 0, w-2, size.y);
+
+ if (!IS_MOTIF) {
+ // draw thin line between center ruler and both texts
+ g.setBackground(fLeftCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ g.fillRectangle(0, 0, 1, size.y);
+ g.fillRectangle(w-1, 0, 1, size.y);
+ }
+
+ if (fChangeDiffs != null) {
+ int lshift= fLeft.getVerticalScrollOffset();
+ int rshift= fRight.getVerticalScrollOffset();
+
+ Point region= new Point(0, 0);
+
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ fLeft.getLineRange(diff.fLeftPos, region);
+ int ly= (region.x * lineHeight) + lshift;
+ int lh= region.y * lineHeight;
+
+ fRight.getLineRange(diff.fRightPos, region);
+ int ry= (region.x * lineHeight) + rshift;
+ int rh= region.y * lineHeight;
+
+ if (Math.max(ly+lh, ry+rh) < 0)
+ continue;
+ if (Math.min(ly, ry) >= visibleHeight)
+ break;
+
+ fPts[0]= x; fPts[1]= ly; fPts[2]= w; fPts[3]= ry;
+ fPts[6]= x; fPts[7]= ly+lh; fPts[4]= w; fPts[5]= ry+rh;
+
+ g.setBackground(getColor(getFillColor(diff)));
+ g.fillPolygon(fPts);
+
+ g.setLineWidth(LW);
+ g.setForeground(getColor(getStrokeColor(diff)));
+ g.drawLine(fPts[0], fPts[1], fPts[2], fPts[3]);
+ g.drawLine(fPts[6], fPts[7], fPts[4], fPts[5]);
+ }
+ }
+ }
+
+ private void paintSides(GC g, MergeSourceViewer tp, Canvas canvas, boolean right) {
+
+ int lineHeight= tp.getTextWidget().getLineHeight();
+ int visibleHeight= tp.getViewportHeight();
+
+ Point size= canvas.getSize();
+ int x= 0;
+ int w= MARGIN_WIDTH;
+
+ g.setBackground(canvas.getBackground());
+ g.fillRectangle(x, 0, w, size.y);
+
+ if (!IS_MOTIF) {
+ // draw thin line between ruler and text
+ g.setBackground(canvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ if (right)
+ g.fillRectangle(0, 0, 1, size.y);
+ else
+ g.fillRectangle(size.x-1, 0, 1, size.y);
+ }
+
+ if (fChangeDiffs != null) {
+ int shift= tp.getVerticalScrollOffset() + (2-LW);
+
+ Point region= new Point(0, 0);
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ tp.getLineRange(diff.getPosition(tp), region);
+ int y= (region.x * lineHeight) + shift;
+ int h= region.y * lineHeight;
+
+ if (y+h < 0)
+ continue;
+ if (y >= visibleHeight)
+ break;
+
+ g.setBackground(getColor(getFillColor(diff)));
+ if (right)
+ g.fillRectangle(x, y, w-5, h);
+ else
+ g.fillRectangle(x+5, y, w-3, h);
+
+ g.setBackground(getColor(getStrokeColor(diff)));
+ if (right) {
+ g.fillRectangle(x, y-1, w-4, LW);
+ g.fillRectangle(x+5, y, LW, h);
+ g.fillRectangle(x, y+h-1, w-4, LW);
+ } else {
+ g.fillRectangle(x+3, y-1, w-3, LW);
+ g.fillRectangle(x+3, y, LW, h);
+ g.fillRectangle(x+3, y+h-1, w-3, LW);
+ }
+ }
+ }
+ }
+
+ private void paint(PaintEvent event, MergeSourceViewer tp) {
+
+ if (fChangeDiffs == null)
+ return;
+
+ Control canvas= (Control) event.widget;
+ GC g= event.gc;
+
+ int lineHeight= tp.getTextWidget().getLineHeight();
+ int w= canvas.getSize().x;
+ int shift= tp.getVerticalScrollOffset() + (2-LW);
+ int maxh= event.y+event.height; // visibleHeight
+
+ Point range= new Point(0, 0);
+
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ tp.getLineRange(diff.getPosition(tp), range);
+ int y= (range.x * lineHeight) + shift;
+ int h= range.y * lineHeight;
+
+ if (y+h < event.y)
+ continue;
+ if (y > maxh)
+ break;
+
+ g.setBackground(getColor(getStrokeColor(diff)));
+ g.fillRectangle(0, y-1, w, LW);
+ g.fillRectangle(0, y+h-1, w, LW);
+ }
+ }
+
+ private RGB getFillColor(Diff diff) {
+ boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ return selected ? SELECTED_OUTGOING_FILL : OUTGOING_FILL;
+ case RangeDifference.ANCESTOR:
+ return selected ? SELECTED_CONFLICT_FILL : CONFLICT_FILL;
+ case RangeDifference.LEFT:
+ return selected ? SELECTED_INCOMING_FILL : INCOMING_FILL;
+ case RangeDifference.CONFLICT:
+ return selected ? SELECTED_CONFLICT_FILL : CONFLICT_FILL;
+ }
+ return null;
+ }
+
+ private RGB getStrokeColor(Diff diff) {
+ boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ return selected ? SELECTED_OUTGOING : OUTGOING;
+ case RangeDifference.ANCESTOR:
+ return selected ? SELECTED_CONFLICT : CONFLICT;
+ case RangeDifference.LEFT:
+ return selected ? SELECTED_INCOMING : INCOMING;
+ case RangeDifference.CONFLICT:
+ return selected ? SELECTED_CONFLICT : CONFLICT;
+ }
+ return null;
+ }
+
+ private Color getColor(RGB rgb) {
+ if (rgb == null)
+ return null;
+ if (fColors == null)
+ fColors= new HashMap(20);
+ Color c= (Color) fColors.get(rgb);
+ if (c == null) {
+ c= new Color(fComposite.getDisplay(), rgb);
+ fColors.put(rgb, c);
+ }
+ return c;
+ }
+
+ //---- Navigating and resolving Diffs
+
+ /**
+ */
+ private void navigate(boolean down) {
+
+ Diff diff= null;
+ if (fChangeDiffs != null) {
+ MergeSourceViewer part= fFocusPart;
+ if (part == null)
+ part= fRight;
+
+ if (part != null) {
+ Point s= part.getSelectedRange();
+ if (down)
+ diff= findNext(part, fChangeDiffs, s.x, s.x+s.y);
+ else
+ diff= findPrev(part, fChangeDiffs, s.x, s.x+s.y);
+ }
+ }
+
+ if (diff == null) {
+ Control c= getControl();
+ if (Utilities.okToUse(c))
+ c.getDisplay().beep();
+ if (DEAD_STEP)
+ return;
+ if (fChangeDiffs.size() > 0) {
+ if (down)
+ diff= (Diff) fChangeDiffs.get(0);
+ else
+ diff= (Diff) fChangeDiffs.get(fChangeDiffs.size()-1);
+ }
+ }
+
+ setCurrentDiff(diff, true);
+ }
+
+ /**
+ * Find the Diff that overlaps with the given TextPart's text range.
+ * If the range doesn't overlap with any range <code>null</code>
+ * is returned.
+ */
+ private Diff findDiff(MergeSourceViewer tp, int rangeStart, int rangeEnd) {
+ if (fChangeDiffs != null) {
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.contains(tp, rangeStart, rangeEnd))
+ return diff;
+ }
+ }
+ return null;
+ }
+
+ private static Diff findNext(MergeSourceViewer tp, List v, int start, int end) {
+ for (int i= 0; i < v.size(); i++) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(tp);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ if (end < startOffset)
+ return diff;
+ if (diff.fDiffs != null) {
+ Diff d= null;
+ int endOffset= startOffset + p.getLength();
+ if (start == startOffset && end == endOffset) {
+ d= findNext(tp, diff.fDiffs, start, start);
+ } else if (end < endOffset) {
+ d= findNext(tp, diff.fDiffs, start, end);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Diff findPrev(MergeSourceViewer tp, List v, int start, int end) {
+ for (int i= v.size()-1; i >= 0; i--) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(tp);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ int endOffset= startOffset + p.getLength();
+ if (start > endOffset)
+ return diff;
+ if (diff.fDiffs != null) {
+ Diff d= null;
+ if (start == startOffset && end == endOffset) {
+ d= findPrev(tp, diff.fDiffs, end, end);
+ } else if (start >= startOffset) {
+ d= findPrev(tp, diff.fDiffs, start, end);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Set the currently active Diff and update the toolbars controls and lines.
+ * If <code>revealAndSelect</code> is <code>true</code> the Diff is revealed and
+ * selected in both TextParts.
+ */
+ private void setCurrentDiff(Diff d, boolean revealAndSelect) {
+
+ if (d == fCurrentDiff)
+ return;
+
+ Diff oldDiff= fCurrentDiff;
+
+ if (d != null && revealAndSelect) {
+
+ // before we set fCurrentDiff we change the selection
+ // so that the paint code uses the old background colors
+ // otherwise we get screen cheese
+ if (isThreeWay())
+ fAncestor.setSelection(d.fAncestorPos);
+ fLeft.setSelection(d.fLeftPos);
+ fRight.setSelection(d.fRightPos);
+
+ // now switch diffs
+ fCurrentDiff= d;
+ revealDiff(d, d.fIsToken);
+ } else {
+ fCurrentDiff= d;
+ }
+
+ if (d != null && !d.fIsToken) {
+ updateDiffBackground(oldDiff);
+ updateDiffBackground(fCurrentDiff);
+ }
+
+ updateControls();
+ invalidateLines();
+ }
+
+ private void revealDiff(Diff d, boolean smart) {
+
+ boolean ancestorIsVisible= false;
+ boolean leftIsVisible= false;
+ boolean rightIsVisible= false;
+
+ if (smart) {
+ Point region= new Point(0, 0);
+ // find the starting line of the diff in all text widgets
+ int ls= fLeft.getLineRange(d.fLeftPos, region).x;
+ int rs= fRight.getLineRange(d.fRightPos, region).x;
+
+ if (isThreeWay()) {
+ int as= fAncestor.getLineRange(d.fAncestorPos, region).x;
+ if (as >= fAncestor.getTopIndex() && as <= fAncestor.getBottomIndex())
+ ancestorIsVisible= true;
+ }
+
+ if (ls >= fLeft.getTopIndex() && ls <= fLeft.getBottomIndex())
+ leftIsVisible= true;
+
+ if (rs >= fRight.getTopIndex() && rs <= fRight.getBottomIndex())
+ rightIsVisible= true;
+
+ if (leftIsVisible && rightIsVisible)
+ return;
+ }
+
+ int vpos= 0;
+
+ MergeSourceViewer allButThis= null;
+ if (leftIsVisible) {
+ vpos= realToVirtualPosition(fLeft, fLeft.getTopIndex());
+ allButThis= fLeft;
+ } else if (rightIsVisible) {
+ vpos= realToVirtualPosition(fRight, fRight.getTopIndex());
+ allButThis= fRight;
+ } else if (ancestorIsVisible) {
+ vpos= realToVirtualPosition(fAncestor, fAncestor.getTopIndex());
+ allButThis= fAncestor;
+ } else {
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ for (int i= 0; e.hasNext(); i++) {
+ Diff diff= (Diff) e.next();
+ if (diff == d)
+ break;
+ vpos+= diff.getMaxDiffHeight(fShowAncestor);
+ }
+ }
+ //vpos-= fRight.getViewportLines()/4;
+ }
+
+ scrollVertical(vpos, allButThis);
+
+ if (fVScrollBar != null) {
+ //int value= Math.max(0, Math.min(vpos, getVirtualHeight() - maxExtentHeight));
+ fVScrollBar.setSelection(vpos);
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+
+ protected void copy(boolean leftToRight) {
+ if (leftToRight) {
+ if (fLeft.getEnabled()) {
+ // copy text
+ String text= fLeft.getTextWidget().getText();
+ fRight.getTextWidget().setText(text);
+ fRight.setEnabled(true);
+ } else {
+ // delete
+ fRight.getTextWidget().setText("");
+ fRight.setEnabled(false);
+ }
+ fRightLineCount= fRight.getLineCount();
+ setRightDirty(true);
+ fRightContentsChanged= false;
+ } else {
+ if (fRight.getEnabled()) {
+ // copy text
+ String text= fRight.getTextWidget().getText();
+ fLeft.getTextWidget().setText(text);
+ fLeft.setEnabled(true);
+ } else {
+ // delete
+ fLeft.getTextWidget().setText("");
+ fLeft.setEnabled(false);
+ }
+ fLeftLineCount= fLeft.getLineCount();
+ setLeftDirty(true);
+ fLeftContentsChanged= false;
+ }
+ doDiff();
+ invalidateLines();
+ updateVScrollBar();
+ selectFirstDiff();
+ }
+
+ private void copyDiffLeftToRight() {
+ copy(fCurrentDiff, true, false, false);
+ }
+
+ private void copyDiffRightToLeft() {
+ copy(fCurrentDiff, false, false, false);
+ }
+
+// private void accept(Diff diff, boolean both, boolean gotoNext) {
+// if (getCompareConfiguration().isRightEditable())
+// copy(diff, true, both, gotoNext);
+// else if (getCompareConfiguration().isLeftEditable())
+// copy(diff, false, both, gotoNext);
+// }
+
+ private void copy(Diff diff, boolean leftToRight, boolean both, boolean gotoNext) {
+
+ if (diff != null && !diff.isResolved()) {
+
+ Position fromPos= null;
+ Position toPos= null;
+ IDocument fromDoc= null;
+ IDocument toDoc= null;
+
+ if (leftToRight) {
+ fRight.setEnabled(true);
+ fromPos= diff.fLeftPos;
+ toPos= diff.fRightPos;
+ fromDoc= fLeft.getDocument();
+ toDoc= fRight.getDocument();
+ } else {
+ fLeft.setEnabled(true);
+ fromPos= diff.fRightPos;
+ toPos= diff.fLeftPos;
+ fromDoc= fRight.getDocument();
+ toDoc= fLeft.getDocument();
+ }
+
+ if (fromDoc != null) {
+
+ int fromStart= fromPos.getOffset();
+ int fromLen= fromPos.getLength();
+
+ int toStart= toPos.getOffset();
+ int toLen= toPos.getLength();
+
+ try {
+ String s= null;
+
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ case RangeDifference.LEFT:
+ s= fromDoc.get(fromStart, fromLen);
+ break;
+ case RangeDifference.ANCESTOR:
+ break;
+ case RangeDifference.CONFLICT:
+ s= fromDoc.get(fromStart, fromLen);
+ if (both)
+ s+= toDoc.get(toStart, toLen);
+ break;
+ }
+ if (s != null) {
+ toDoc.replace(toStart, toLen, s);
+ toPos.setOffset(toStart);
+ toPos.setLength(s.length());
+ }
+
+ } catch (BadLocationException e) {
+ }
+ }
+
+ diff.setResolved(true);
+
+ if (gotoNext) {
+ navigate(true/*, true*/);
+ } else {
+ revealDiff(diff, true);
+ updateControls();
+ }
+ }
+ }
+
+ /**
+ */
+// private void reject(Diff diff, boolean gotoNext) {
+//
+// if (diff != null && !diff.isResolved()) {
+//
+// switch (diff.fDirection) {
+// case RangeDifference.RIGHT:
+// setRightDirty(true); // mark dirty to force save!
+// break;
+// case RangeDifference.LEFT:
+// setLeftDirty(true); // mark dirty to force save!
+// break;
+// case RangeDifference.ANCESTOR:
+// break;
+// case RangeDifference.CONFLICT:
+// setLeftDirty(true); // mark dirty to force save!
+// setRightDirty(true); // mark dirty to force save!
+// break;
+// }
+//
+// diff.setResolved(true);
+//
+// if (gotoNext) {
+// navigate(true/*, true*/);
+// } else {
+// revealDiff(diff, true);
+// updateControls();
+// }
+// }
+// }
+
+
+// private void autoResolve() {
+// fCurrentDiff= null;
+// Diff firstConflict= null;
+//
+// Iterator e= fChangeDiffs.iterator();
+// for (int i= 0; e.hasNext(); i++) {
+// Diff diff= (Diff) e.next();
+// if (!diff.isResolved()) {
+// switch (diff.fDirection) {
+// case RangeDifference.RIGHT: // outgoing
+// case RangeDifference.LEFT: // incoming
+// accept(diff, false, false);
+// break;
+// case RangeDifference.CONFLICT: // incoming
+// if (firstConflict == null)
+// firstConflict= diff;
+// break;
+// }
+// }
+// }
+//
+// if (firstConflict == null)
+// firstConflict= (Diff) fChangeDiffs.get(0);
+// setCurrentDiff(firstConflict, true);
+// }
+
+ //---- scrolling
+
+ /**
+ * Calculates virtual height (in lines) of views by adding the maximum of corresponding diffs.
+ */
+ private int getVirtualHeight() {
+ int h= 1;
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ for (int i= 0; e.hasNext(); i++) {
+ Diff diff= (Diff) e.next();
+ h+= diff.getMaxDiffHeight(fShowAncestor);
+ }
+ }
+ return h;
+ }
+
+ /**
+ * The height of the TextEditors in lines.
+ */
+ private int getViewportHeight() {
+ StyledText te= fLeft.getTextWidget();
+
+ int vh= te.getClientArea().height;
+ if (vh == 0) {
+ // seems to be a bug in TextEditor.getClientArea(): returns bogus value on first
+ // call; as a workaround we calculate the clientArea from its container...
+ Rectangle trim= te.computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+
+ int headerHeight= getHeaderHeight();
+
+ Composite composite= (Composite) getControl();
+ Rectangle r= composite.getClientArea();
+
+ vh= r.height-headerHeight-scrollbarHeight;
+ }
+
+ return vh / te.getLineHeight();
+ }
+
+ /**
+ * Returns the virtual position for the given view position.
+ */
+ private int realToVirtualPosition(MergeSourceViewer w, int vpos) {
+
+ if (! fSynchronizedScrolling || fAllDiffs == null)
+ return vpos;
+
+ int viewPos= 0; // real view position
+ int virtualPos= 0; // virtual position
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(w);
+ w.getLineRange(pos, region);
+ int realHeight= region.y;
+ int virtualHeight= diff.getMaxDiffHeight(fShowAncestor);
+ if (vpos <= viewPos + realHeight) { // OK, found!
+ vpos-= viewPos; // make relative to this slot
+ // now scale position within this slot to virtual slot
+ if (realHeight <= 0)
+ vpos= 0;
+ else
+ vpos= (vpos*virtualHeight)/realHeight;
+ return virtualPos+vpos;
+ }
+ viewPos+= realHeight;
+ virtualPos+= virtualHeight;
+ }
+ return virtualPos;
+ }
+
+ private void scrollVertical(int virtualPos, MergeSourceViewer allBut) {
+
+ if (virtualPos < 0)
+ virtualPos= virtualPos;
+
+ GC gc;
+ if (fSynchronizedScrolling) {
+ int s= 0;
+
+ if (true) {
+ s= getVirtualHeight() - virtualPos;
+ int height= fRight.getViewportLines()/4;
+ if (s < 0)
+ s= 0;
+ if (s > height)
+ s= height;
+ }
+
+ fInScrolling= true;
+
+ if (isThreeWay() && allBut != fAncestor) {
+ int y= virtualToRealPosition(fAncestor, virtualPos+s)-s;
+ fAncestor.vscroll(y);
+ }
+
+ if (allBut != fLeft) {
+ int y= virtualToRealPosition(fLeft, virtualPos+s)-s;
+ fLeft.vscroll(y);
+ }
+
+ if (allBut != fRight) {
+ int y= virtualToRealPosition(fRight, virtualPos+s)-s;
+ fRight.vscroll(y);
+ }
+
+ fInScrolling= false;
+
+ if (isThreeWay())
+ fAncestorCanvas.repaint();
+
+ fLeftCanvas.repaint();
+
+ Control center= getCenter();
+ if (center instanceof BufferedCanvas)
+ ((BufferedCanvas)center).repaint();
+
+ fRightCanvas.repaint();
+ } else {
+ if (allBut == fAncestor && isThreeWay())
+ fAncestorCanvas.repaint();
+
+ if (allBut == fLeft)
+ fLeftCanvas.repaint();
+
+ if (allBut == fRight)
+ fRightCanvas.repaint();
+ }
+ }
+
+ /**
+ * Updates Scrollbars with viewports.
+ */
+ private void syncViewport(MergeSourceViewer w) {
+
+ if (fInScrolling)
+ return;
+
+ int ix= w.getTopIndex();
+ int ix2= w.getDocumentRegionOffset();
+
+ int viewPosition= realToVirtualPosition(w, ix-ix2);
+
+ scrollVertical(viewPosition, w); // scroll all but the given views
+
+ if (fVScrollBar != null) {
+ int value= Math.max(0, Math.min(viewPosition, getVirtualHeight() - getViewportHeight()));
+ fVScrollBar.setSelection(value);
+ }
+ }
+
+ /**
+ */
+ private void updateVScrollBar() {
+
+ if (Utilities.okToUse(fVScrollBar) && fVScrollBar.isVisible()) {
+ int virtualHeight= getVirtualHeight();
+ int viewPortHeight= getViewportHeight();
+ fVScrollBar.setPageIncrement(viewPortHeight-1);
+ fVScrollBar.setMaximum(virtualHeight);
+ if (viewPortHeight > virtualHeight)
+ fVScrollBar.setThumb(virtualHeight);
+ else
+ fVScrollBar.setThumb(viewPortHeight);
+ }
+ }
+
+ /**
+ * maps given virtual position into a real view position of this view.
+ */
+ private int virtualToRealPosition(MergeSourceViewer part, int v) {
+
+ if (! fSynchronizedScrolling || fAllDiffs == null)
+ return v;
+
+ int virtualPos= 0;
+ int viewPos= 0;
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(part);
+ int viewHeight= part.getLineRange(pos, region).y;
+ int virtualHeight= diff.getMaxDiffHeight(fShowAncestor);
+ if (v < (virtualPos + virtualHeight)) {
+ v-= virtualPos; // make relative to this slot
+ if (viewHeight <= 0) {
+ v= 0;
+ } else {
+ v= (v*viewHeight)/virtualHeight;
+ }
+ return viewPos+v;
+ }
+ virtualPos+= virtualHeight;
+ viewPos+= viewHeight;
+ }
+ return viewPos;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties
new file mode 100644
index 000000000..7ff3f9d06
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewerResources.properties
@@ -0,0 +1,114 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 1999-2001
+# =====================================
+
+# @(#)TextMergeViewerResources.properties
+#
+# Resource strings for TextMergeViewer.java
+
+#####################################################
+# Images
+#####################################################
+
+#####################################################
+# Status messages
+#####################################################
+status.same=No Change
+status.resolved=Resolved
+status.unresolved=Unresolved
+status.allresolved=All Resolved
+
+status.unresolvedformat={0} (remaining differences: {1})
+status.resolvedformat={0}
+
+#####################################################
+# Dialogs
+#####################################################
+
+CantSaveProperty.title=Error while saving contents
+CantSaveProperty.message=Couldn''t save contents.\nReason: {0}
+
+ContentInaccessible.title=Error while accessing content
+ContentInaccessible.message=Content currently inaccessible.
+
+#####################################################
+# Actions
+#####################################################
+
+action.CopyLeftToRight.label=Copy Left to Right
+action.CopyLeftToRight.tooltip=Copy whole document from left to right
+action.CopyLeftToRight.image=ctool16/lefttoright2.gif
+
+action.CopyRightToLeft.label=Copy Right to Left
+action.CopyRightToLeft.tooltip=Copy whole document from right to left
+action.CopyRightToLeft.image=ctool16/righttoleft2.gif
+
+action.CopyDiffLeftToRight.label=Copy Current Diff
+action.CopyDiffLeftToRight.tooltip=Copy current change from left to right
+action.CopyDiffLeftToRight.image=ctool16/lefttoright.gif
+
+action.CopyDiffRightToLeft.label=Copy Current Diff
+action.CopyDiffRightToLeft.tooltip=Copy current change from right to left
+action.CopyDiffRightToLeft.image=ctool16/righttoleft.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.ToggleMergeDirection.label=Toggle Merge Direction
+#action.ToggleMergeDirection.tooltip=Toggle merge direction
+#action.ToggleMergeDirection.image=ctool16/toggle.gif
+
+action.EnableAncestor.label=Enable Ancestor Pane
+action.EnableAncestor.tooltip=Controls visibility of ancestor pane
+action.EnableAncestor.image=ctool16/panes3.gif
+
+action.SynchMode.label=Synchonize Panes
+action.SynchMode.tooltip=Synchonize pane scrolling
+action.SynchMode.image=ctool16/synchmode.gif
+
+#action.IgnoreWhiteSpace.label=Ignore White Space
+#action.IgnoreWhiteSpace.tooltip=Ignore white space
+#action.IgnoreWhiteSpace.image=ctool16/ignorews.gif
+
+# Pop up menu
+
+action.CopyText.label=Copy@Ctrl+C
+action.CopyText.tooltip=Copy Text Selection to Clipboard
+
+action.CutText.label=Cut@Ctrl+X
+action.CutText.tooltip=Cut Text Selection to Clipboard
+
+action.DeleteText.label=Delete
+action.DeleteText.tooltip=Delete current Text Selection
+
+action.Find.label=Find...@Ctrl+F
+action.Find.tooltip=Find Occurance
+
+action.PasteText.label=Paste@Ctrl+V
+action.PasteText.tooltip=Replace Text Selection with Clipboard
+
+action.RedoText.label=Redo@Ctrl+Y
+action.RedoText.tooltip=Redo last Operation
+
+action.SelectAllText.label=Select All@Ctrl+A
+action.SelectAllText.tooltip=Select all Changes
+
+action.Save.label=Save@Ctrl+S
+action.Save.tooltip=Save Changes
+
+action.UndoText.label=Undo@Ctrl+Z
+action.UndoText.tooltip=Undo last Operation
+
+action.ContentAssistProposal.label=Code Ass&ist Proposals@Ctrl+Space
+action.ContentAssistProposal.description=Show content assist proposals
+
+action.ContentAssistTip.label=Code Ass&ist Tips@Ctrl+Shift+Space
+action.ContentAssistTip.description=Show content assist tips
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html
new file mode 100644
index 000000000..28d91eedb
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/package.html
@@ -0,0 +1,35 @@
+<!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>
+Support for compare and merge viewers which show the
+content side-by-side.
+<h2>
+Package Specification</h2>
+The <tt>ContentMergeViewer</tt> is an abstract compare and merge viewer
+with two side-by-side content areas and an optional content area for a
+common ancestor (for three-way compare). Because the implementation makes
+no assumptions about the content type it is a subclass responsibility to
+deal with a specific type.
+<p>A <tt>ContentMergeViewer</tt> accesses its model by means of a content
+provider which must implement the
+<br><tt>IMergeViewerContentProvider</tt> interface.
+<p>The <tt>TextMergeViewer</tt> is the standard concrete subclass of <tt>ContentMergeViewer</tt>.
+A text merge viewer uses the <tt>RangeDifferencer</tt> to perform a textual,
+line-by-line comparison of two (or three) input documents. For text lines
+that differ the <tt>TextMergeViewer</tt> uses an <tt>ITokenComparator</tt>
+to find longest sequences of matching and non-matching tokens. The <tt>TextMergeViewer</tt>'s
+default token compare works on characters separated by whitespace. If a
+different strategy is needed (for example, Java tokens in a Java-aware
+merge viewer), clients can create their own token comparators by implementing
+the <tt>ITokenComparator</tt> interface.
+<p>The <tt>TextMergeViewer</tt> not only works on whole documents but on
+subranges of documents too. In this case the viewer's input must be an
+<tt>IDocumentRange</tt> instead of an <tt>IDocument</tt>.
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java
new file mode 100644
index 000000000..2017e31de
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AbstractViewer.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.Viewer;
+
+
+abstract class AbstractViewer extends Viewer {
+
+ public void setInput(Object input) {
+ }
+
+ public Object getInput() {
+ return null;
+ }
+
+ public ISelection getSelection() {
+ return null;
+ }
+
+ public void setSelection(ISelection s, boolean reveal) {
+ }
+
+ public void refresh() {
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java
new file mode 100644
index 000000000..eb6c44eab
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/AddFromHistoryDialog.java
@@ -0,0 +1,17 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000,2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.widgets.Shell;
+
+
+public class AddFromHistoryDialog extends Dialog {
+
+ public AddFromHistoryDialog(Shell parent) {
+ super(parent);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java
new file mode 100644
index 000000000..07be03f22
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewer.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.*;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+
+/**
+ * A simple compare viewer for binary files.
+ * Shows the position of the first non-matching byte.
+ */
+public class BinaryCompareViewer extends AbstractViewer {
+
+ private static final int EOF= -1;
+ private Text fControl;
+ private ICompareInput fInput;
+
+
+ public BinaryCompareViewer(Composite parent, CompareConfiguration cc) {
+ fControl= new Text(parent, SWT.NONE);
+ fControl.setEditable(false);
+ fControl.setData(CompareUI.COMPARE_VIEWER_TITLE, "Binary Compare");
+ }
+
+ public Control getControl() {
+ return fControl;
+ }
+
+ public void setInput(Object input) {
+ if (fControl != null && input instanceof ICompareInput) {
+ fInput= (ICompareInput) input;
+
+ InputStream left= null;
+ InputStream right= null;
+
+ try {
+ left= getStream(fInput.getLeft());
+ right= getStream(fInput.getRight());
+
+ int pos= 0;
+ while (true) {
+ int l= left.read();
+ int r= right.read();
+ if (l != r) {
+ fControl.setText("first bytes differ at position " + pos);
+ break;
+ }
+ if (l == EOF)
+ break;
+ pos++;
+ }
+ } catch (CoreException ex) {
+ fControl.setText("CoreException " + ex);
+ } catch (IOException ex) {
+ fControl.setText("IOException " + ex);
+ } finally {
+ if (left != null) {
+ try {
+ left.close();
+ } catch (IOException ex) {
+ }
+ }
+ if (right != null) {
+ try {
+ right.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ private InputStream getStream(ITypedElement input) throws CoreException {
+ if (input instanceof IStreamContentAccessor)
+ return ((IStreamContentAccessor)input).getContents();
+ return new ByteArrayInputStream(new byte[0]);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java
new file mode 100644
index 000000000..503756b10
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BinaryCompareViewerCreator.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+
+/**
+ * A factory object for the <code>BinaryCompareViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class BinaryCompareViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new BinaryCompareViewer(parent, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java
new file mode 100644
index 000000000..6a073a3e7
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedCanvas.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.events.*;
+
+/**
+ * A Canvas which reduces flicker by drawing in an off screen buffer.
+ */
+public abstract class BufferedCanvas extends Canvas {
+
+ /** The drawable for double buffering */
+ private Image fBuffer;
+
+ public BufferedCanvas(Composite parent, int flags) {
+ super(parent, flags + SWT.NO_BACKGROUND);
+
+ addPaintListener(
+ new PaintListener() {
+ public void paintControl(PaintEvent event) {
+ doubleBufferPaint(event.gc);
+ }
+ }
+ );
+
+ addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fBuffer != null) {
+ fBuffer.dispose();
+ fBuffer= null;
+ }
+ }
+ }
+ );
+ }
+
+ public void repaint() {
+ if (!isDisposed()) {
+ GC gc= new GC(this);
+ doubleBufferPaint(gc);
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Double buffer drawing.
+ * @private
+ */
+ private void doubleBufferPaint(GC dest) {
+
+ Point size= getSize();
+
+ if (size.x <= 0 || size.y <= 0)
+ return;
+
+ if (fBuffer != null) {
+ Rectangle r= fBuffer.getBounds();
+ if (r.width != size.x || r.height != size.y) {
+ fBuffer.dispose();
+ fBuffer= null;
+ }
+ }
+ if (fBuffer == null)
+ fBuffer= new Image(getDisplay(), size.x, size.y);
+
+ GC gc= new GC(fBuffer);
+ try {
+ gc.setBackground(getBackground());
+ gc.fillRectangle(0, 0, size.x, size.y);
+ doPaint(gc);
+ } finally {
+ gc.dispose();
+ }
+
+ dest.drawImage(fBuffer, 0, 0);
+ }
+
+ abstract public void doPaint(GC gc);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java
new file mode 100644
index 000000000..5f54e30f7
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangePropertyAction.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * Toggles a boolean property of an <code>ICompareConfiguration</code>.
+ */
+public class ChangePropertyAction extends Action {
+
+ private CompareConfiguration fCompareConfiguration;
+ private String fPropertyKey;
+
+
+ public ChangePropertyAction(ResourceBundle bundle, CompareConfiguration cc, String rkey, String pkey) {
+ fPropertyKey= pkey;
+ Utilities.initAction(this, bundle, rkey);
+ setCompareConfiguration(cc);
+ }
+
+ public void run() {
+ boolean b= !Utilities.getBoolean(fCompareConfiguration, fPropertyKey, false);
+ setChecked(b);
+ if (fCompareConfiguration != null)
+ fCompareConfiguration.setProperty(fPropertyKey, new Boolean(b));
+ }
+
+ public void setCompareConfiguration(CompareConfiguration cc) {
+ fCompareConfiguration= cc;
+ setChecked(Utilities.getBoolean(fCompareConfiguration, fPropertyKey, false));
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java
new file mode 100644
index 000000000..4193d9a80
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareAction.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.ui.IActionDelegate;
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+
+
+public class CompareAction implements IActionDelegate {
+
+ private ISelection fSelection;
+
+ public void run(IAction action) {
+ CompareUI.openCompareEditor(new ResourceCompareInput(new CompareConfiguration(), fSelection));
+ }
+
+ public void selectionChanged(IAction a, ISelection s) {
+ fSelection= s;
+ // the following check is disabled because it results in a confusing UI:
+ // action might be enabled if plugin is not loaded but
+ // it gets disabled after plugin has been loaded...
+ //Object[] selection= StructuredSelection.toArray(s);
+ //((Action)a).setEnabled(selection.length == 2);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java
new file mode 100644
index 000000000..cb6ce6526
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareDialog.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.layout.*;
+
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.util.Assert;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+
+public class CompareDialog extends Dialog implements IPropertyChangeListener {
+
+ private static final String COMMIT_LABEL= "Commit";
+
+ private CompareEditorInput fCompareEditorInput;
+ private Button fCommitButton;
+
+
+ CompareDialog(Shell shell, CompareEditorInput input) {
+ super(shell);
+ setShellStyle(SWT.CLOSE | SWT.APPLICATION_MODAL | SWT.RESIZE);
+
+ Assert.isNotNull(input);
+ fCompareEditorInput= input;
+ fCompareEditorInput.addPropertyChangeListener(this);
+ }
+
+ public boolean close() {
+ if (super.close()) {
+ if (fCompareEditorInput != null)
+ fCompareEditorInput.addPropertyChangeListener(this);
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Dialog.
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+ fCommitButton= createButton(parent, IDialogConstants.OK_ID, COMMIT_LABEL, true);
+ fCommitButton.setEnabled(false);
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (fCommitButton != null && fCompareEditorInput != null)
+ fCommitButton.setEnabled(fCompareEditorInput.isSaveNeeded());
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Dialog.
+ */
+ protected Control createDialogArea(Composite parent) {
+
+ Control c= fCompareEditorInput.createContents(parent);
+ c.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Shell shell= c.getShell();
+ shell.setText(fCompareEditorInput.getTitle());
+ shell.setImage(fCompareEditorInput.getTitleImage());
+
+ return c;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Window.
+ */
+ protected Point getInitialSize() {
+ Point size= new Point(0, 0);
+ Shell shell= getParentShell();
+ if (shell != null) {
+ Point parentSize= shell.getSize();
+ size.x= parentSize.x-100;
+ size.y= parentSize.y-100;
+ }
+ if (size.x < 800)
+ size.x= 800;
+ if (size.y < 600)
+ size.y= 600;
+ return size;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Window.
+ */
+ public int open() {
+
+ int rc= super.open();
+
+ if (rc == OK && fCompareEditorInput.isSaveNeeded()) {
+
+ WorkspaceModifyOperation operation= new WorkspaceModifyOperation() {
+ public void execute(IProgressMonitor pm) throws CoreException {
+ fCompareEditorInput.save(pm);
+ }
+ };
+
+ Shell shell= getParentShell();
+ ProgressMonitorDialog pmd= new ProgressMonitorDialog(shell);
+ try {
+ operation.run(pmd.getProgressMonitor());
+
+ } catch (InterruptedException x) {
+ } catch (OperationCanceledException x) {
+ } catch (InvocationTargetException x) {
+ //String title= getResourceString("Error.save.title");
+ //String msg= getResourceString("Error.save.message");
+ String title= "Save Error";
+ String msg= "Can't save ";
+ MessageDialog.openError(shell, title, msg + x.getTargetException().getMessage());
+ }
+ }
+
+ return rc;
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java
new file mode 100644
index 000000000..cfc6ccf6c
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditor.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.*;
+import org.eclipse.ui.*;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.compare.*;
+import org.eclipse.ui.part.EditorPart;
+
+
+/**
+ * A CompareEditor takes a ICompareEditorInput as input.
+ * Most functionality is delegated to the ICompareEditorInput.
+ */
+public class CompareEditor extends EditorPart implements IPropertyChangeListener {
+
+ public CompareEditor() {
+ }
+
+ /* package */ CompareConfiguration getCompareConfiguration() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ return ((CompareEditorInput)input).getCompareConfiguration();
+ return null;
+ }
+
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+
+ if (!(input instanceof CompareEditorInput))
+ throw new PartInitException("Invalid Input: Must be CompareEditorInput");
+
+ CompareEditorInput cei= (CompareEditorInput) input;
+
+ setSite(site);
+ setInput(input);
+
+ setTitleImage(cei.getTitleImage());
+ setTitle(cei.getTitle());
+
+ if (input instanceof IPropertyChangeNotifier)
+ ((IPropertyChangeNotifier)input).addPropertyChangeListener(this);
+ }
+
+ /*
+ * @see IDesktopPart#createPartControl(Composite)
+ */
+ public void createPartControl(Composite parent) {
+
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ ((CompareEditorInput) input).createContents(parent);
+ }
+
+ /*
+ * @see DesktopPart#dispose
+ */
+ public void dispose() {
+
+ IEditorInput input= getEditorInput();
+ if (input instanceof IPropertyChangeNotifier)
+ ((IPropertyChangeNotifier)input).removePropertyChangeListener(this);
+
+ super.dispose();
+ }
+
+ /*
+ * @see IDesktopPart#setFocus
+ */
+ public void setFocus() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ ((CompareEditorInput)input).setFocus();
+ }
+
+ /**
+ * Returns false because the editor doesn't support "Save As...".
+ */
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ public void gotoMarker(IMarker marker) {
+ }
+
+ /**
+ * Always throws an AssertionFailedException.
+ */
+ /*
+ * @see IEditorPart#doSaveAs()
+ */
+ public void doSaveAs() {
+ Assert.isTrue(false, "Save As not supported for CompareEditor");
+ }
+
+ /*
+ * @see IEditorPart#doSave()
+ */
+ public void doSave(IProgressMonitor progressMonitor) {
+
+ final IEditorInput input= getEditorInput();
+
+ WorkspaceModifyOperation operation= new WorkspaceModifyOperation() {
+ public void execute(IProgressMonitor pm) throws CoreException {
+ if (input instanceof CompareEditorInput)
+ ((CompareEditorInput)input).save(pm);
+ }
+ };
+
+ Shell shell= getSite().getWorkbenchWindow().getShell();
+
+ try {
+
+ operation.run(progressMonitor);
+
+ firePropertyChange(PROP_DIRTY);
+
+
+ } catch (InterruptedException x) {
+ } catch (OperationCanceledException x) {
+ } catch (InvocationTargetException x) {
+ //String title= getResourceString("Error.save.title");
+ //String msg= getResourceString("Error.save.message");
+ String title= "Save Error";
+ String msg= "Can't save ";
+ MessageDialog.openError(shell, title, msg + x.getTargetException().getMessage());
+ }
+ }
+
+ /*
+ * @see IEditorPart#isDirty()
+ */
+ public boolean isDirty() {
+ IEditorInput input= getEditorInput();
+ if (input instanceof CompareEditorInput)
+ return ((CompareEditorInput)input).isSaveNeeded();
+ return false;
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (isDirty())
+ firePropertyChange(PROP_DIRTY);
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java
new file mode 100644
index 000000000..be21b8ab3
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.part.EditorActionBarContributor;
+
+import org.eclipse.compare.*;
+
+
+public class CompareEditorContributor extends EditorActionBarContributor {
+
+ private IgnoreWhiteSpaceAction fIgnoreWhitespace;
+ private ShowPseudoConflicts fShowPseudoConflicts;
+
+
+ public CompareEditorContributor() {
+ ResourceBundle bundle= CompareUIPlugin.getResourceBundle();
+ fIgnoreWhitespace= new IgnoreWhiteSpaceAction(bundle, null);
+ fShowPseudoConflicts= new ShowPseudoConflicts(bundle, null);
+ }
+
+ public void contributeToToolBar(IToolBarManager tbm) {
+ tbm.add(new Separator());
+ tbm.add(fIgnoreWhitespace);
+ tbm.add(fShowPseudoConflicts);
+ }
+
+ public void setActiveEditor(IEditorPart targetEditor) {
+ if (targetEditor instanceof CompareEditor) {
+ CompareConfiguration cc= ((CompareEditor) targetEditor).getCompareConfiguration();
+ fIgnoreWhitespace.setCompareConfiguration(cc);
+ fShowPseudoConflicts.setCompareConfiguration(cc);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties
new file mode 100644
index 000000000..7c77fdb76
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePluginResources.properties
@@ -0,0 +1,35 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 1999-2001
+# =====================================
+# @(#)ComparePluginResources.properties
+#
+# Resource strings for Compare Plugin
+
+#####################################################
+# Actions
+#####################################################
+
+action.IgnoreWhiteSpace.label=Ignore White Space
+action.IgnoreWhiteSpace.tooltip=Ignore white space where applicable
+action.IgnoreWhiteSpace.image=ctool16/ignorews.gif
+
+action.ShowPseudoConflicts.label=Show Pseudo Conflicts
+action.ShowPseudoConflicts.tooltip=Show pseudo conflicts where applicable
+action.ShowPseudoConflicts.image=ctool16/showpseudoconflicts.gif
+
+#action.ToggleMergeDirection.label=Toggle Merge Direction
+#action.ToggleMergeDirection.tooltip=Toggle merge direction
+#action.ToggleMergeDirection.image=ctool16/toggle.gif
+
+#action.NextDiff.label=Next
+#action.NextDiff.tooltip=Show Next Difference
+#action.NextDiff.image=ctool16/next.gif
+
+#action.PrevDiff.label=Previous
+#action.PrevDiff.tooltip=Show Previous Difference
+#action.PrevDiff.image=ctool16/prev.gif
+
+
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java
new file mode 100644
index 000000000..696dfca4c
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java
@@ -0,0 +1,706 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.ui.internal.SharedImages;
+import org.eclipse.core.runtime.*;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+import org.eclipse.ui.*;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+
+/**
+ * The Compare UI plug-in defines the entry point to initiate a configurable
+ * compare operation on arbitrary resources. The result of the compare
+ * is opened into a compare editor where the details can be browsed and
+ * edited in dynamically selected structure and content viewers.
+ * <p>
+ * The Compare UI provides a registry for content and structure compare viewers,
+ * which is initialized from extensions contributed to extension points
+ * declared by this plug-in.
+ * <p>
+ * This class is the plug-in runtime class for the
+ * <code>"org.eclipse.compare"</code> plug-in.
+ * </p>
+ */
+public final class CompareUIPlugin extends AbstractUIPlugin {
+
+ private static boolean NORMALIZE_CASE= true;
+
+ private final static String CLASS_ATTRIBUTE= "class";
+ private final static String EXTENSIONS_ATTRIBUTE= "extensions";
+
+ private static final String RESOURCE_BUNDLE= "org.eclipse.compare.internal.ComparePluginResources";
+
+ private static final String PLUGIN_ID= "org.eclipse.compare";
+
+ private static final String STRUCTURE_CREATOR_EXTENSION_POINT= "structureCreators";
+ private static final String STRUCTURE_MERGEVIEWER_EXTENSION_POINT= "structureMergeViewers";
+ private static final String CONTENT_MERGEVIEWER_EXTENSION_POINT= "contentMergeViewers";
+ private static final String CONTENT_VIEWER_EXTENSION_POINT= "contentViewers";
+
+ private static final String COMPARE_EDITOR= "org.eclipse.compare.CompareEditor";
+
+ private static final String COMPARE_FAILED= "Compare failed";
+ private static final String PROBLEMS_OPENING_EDITOR= "Problems Opening Editor";
+
+ /** Maps type to icons */
+ private static Map fgImages= new Hashtable(10);
+ /** Maps type to ImageDescriptors */
+ private static Map fgImageDescriptors= new Hashtable(10);
+ /** Maps ImageDescriptors to Images */
+ private static Map fgImages2= new Hashtable(10);
+
+ private static Map fgStructureCreators= new Hashtable(10);
+ private static Map fgStructureViewerDescriptors= new Hashtable(10);
+ private static Map fgContentViewerDescriptors= new Hashtable(10);
+ private static Map fgContentMergeViewerDescriptors= new Hashtable(10);
+
+ private static List fgDisposeOnShutdownImages= new ArrayList();
+
+ private static ResourceBundle fgResourceBundle;
+
+ private static CompareUIPlugin fgComparePlugin;
+
+ /**
+ * Creates the <code>CompareUIPlugin</code> object and registers all
+ * structure creators, content merge viewers, and structure merge viewers
+ * contributed to this plug-in's extension points.
+ * <p>
+ * Note that instances of plug-in runtime classes are automatically created
+ * by the platform in the course of plug-in activation.
+ * </p>
+ *
+ * @param descriptor the plug-in descriptor
+ */
+ public CompareUIPlugin(IPluginDescriptor descriptor) {
+ super(descriptor);
+
+ fgComparePlugin= this;
+
+ registerExtensions();
+ }
+
+ /**
+ * Registers all structure creators, content merge viewers, and structure merge viewers
+ * that are found in the XML plugin files.
+ */
+ private void registerExtensions() {
+ IPluginRegistry registry= Platform.getPluginRegistry();
+
+ // collect all IStructureCreators
+ IConfigurationElement[] elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_CREATOR_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ final IConfigurationElement conf= elements[i];
+ String extensions= conf.getAttribute(EXTENSIONS_ATTRIBUTE);
+ registerStructureCreator(extensions,
+ new IStructureCreatorDescriptor() {
+ public IStructureCreator createStructureCreator() {
+ try {
+ return (IStructureCreator) conf.createExecutableExtension(CLASS_ATTRIBUTE);
+ } catch (CoreException ex) {
+ }
+ return null;
+ }
+ }
+ );
+ }
+
+ // collect all viewers which define the structure mergeviewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_MERGEVIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ ViewerDescriptor desc= new ViewerDescriptor(elements[i]);
+ String ext= desc.getExtension();
+ if (ext != null)
+ registerStructureViewerDescriptor(desc.getExtension(), desc);
+ }
+
+ // collect all viewers which define the content mergeviewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_MERGEVIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ ViewerDescriptor desc= new ViewerDescriptor(elements[i]);
+ String ext= desc.getExtension();
+ if (ext != null)
+ registerContentMergeViewerDescriptor(desc.getExtension(), desc);
+ }
+
+ // collect all viewers which define the content viewer extension point
+ elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_VIEWER_EXTENSION_POINT);
+ for (int i= 0; i < elements.length; i++) {
+ ViewerDescriptor desc= new ViewerDescriptor(elements[i]);
+ String ext= desc.getExtension();
+ if (ext != null)
+ registerContentViewerDescriptor(desc.getExtension(), desc);
+ }
+ }
+
+ /**
+ * Returns the singleton instance of this plug-in runtime class.
+ *
+ * @return the compare plug-in instance
+ */
+ public static CompareUIPlugin getDefault() {
+ return fgComparePlugin;
+ }
+
+ /**
+ * Returns this plug-in's resource bundle.
+ *
+ * @return the plugin's resource bundle
+ */
+ public static ResourceBundle getResourceBundle() {
+ if (fgResourceBundle == null)
+ fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+ return fgResourceBundle;
+ }
+
+ /**
+ * Returns the active workkbench page or <code>null</code> if
+ * no active workkbench page can be determined.
+ *
+ * @return the active workkbench page or <code>null</code> if
+ * no active workkbench page can be determined
+ */
+ private static IWorkbenchPage getActivePage() {
+ CompareUIPlugin plugin= getDefault();
+ if (plugin == null)
+ return null;
+ IWorkbench workbench= plugin.getWorkbench();
+ if (workbench == null)
+ return null;
+ IWorkbenchWindow window= workbench.getActiveWorkbenchWindow();
+ if (window == null)
+ return null;
+ return window.getActivePage();
+ }
+
+ /**
+ * Returns the SWT Shell of the active workbench window or <code>null</code> if
+ * no workbench window is active.
+ *
+ * @return the SWT Shell of the active workbench window, or <code>null</code> if
+ * no workbench window is active
+ */
+ public static Shell getShell() {
+ CompareUIPlugin p= getDefault();
+ if (p == null)
+ return null;
+ IWorkbench wb= p.getWorkbench();
+ if (wb == null)
+ return null;
+ IWorkbenchWindow ww= wb.getActiveWorkbenchWindow();
+ if (ww == null)
+ return null;
+ return ww.getShell();
+ }
+
+ /**
+ * Registers the given image for being disposed when this plug-in is shutdown.
+ *
+ * @param image the image to register for disposal
+ */
+ public static void disposeOnShutdown(Image image) {
+ if (image != null)
+ fgDisposeOnShutdownImages.add(image);
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Plugin.
+ * Frees all resources of the compare plug-in.
+ */
+ public void shutdown() throws CoreException {
+ super.shutdown();
+
+ if (fgDisposeOnShutdownImages != null) {
+ Iterator i= fgDisposeOnShutdownImages.iterator();
+ while (i.hasNext()) {
+ Image img= (Image) i.next();
+ if (!img.isDisposed())
+ img.dispose();
+ }
+ fgImages= null;
+ }
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * compare editor on the result.
+ *
+ * @param input the input on which to open the compare editor
+ * @see ICompareEditorInput
+ */
+ public void openCompareEditor(CompareEditorInput input) {
+
+ if (compareResultOK(input)) {
+ IWorkbenchPage activePage= getActivePage();
+ if (activePage != null) {
+ try {
+ activePage.openEditor(input, COMPARE_EDITOR);
+ } catch (PartInitException e) {
+ MessageDialog.openError(getShell(), PROBLEMS_OPENING_EDITOR, e.getMessage());
+ }
+ } else {
+ MessageDialog.openError(getShell(), PROBLEMS_OPENING_EDITOR, "Can't find active workbench page");
+ }
+ }
+ }
+
+ /**
+ * Performs the comparison described by the given input and opens a
+ * compare dialog on the result.
+ *
+ * @param input the input on which to open the compare editor
+ * @see ICompareEditorInput
+ */
+ public void openCompareDialog(final CompareEditorInput input) {
+
+ if (compareResultOK(input)) {
+ CompareDialog dialog= new CompareDialog(getShell(), input);
+ dialog.open();
+ }
+ }
+
+ /**
+ * @return <code>true</code> if compare result is OK to show, <code>false</code> otherwise
+ */
+ private boolean compareResultOK(CompareEditorInput input) {
+ final Shell shell= getShell();
+ try {
+
+ // run operation in separate thread and make it canceable
+ new ProgressMonitorDialog(shell).run(true, true, input);
+
+ String message= input.getMessage();
+ if (message != null) {
+ MessageDialog.openError(shell, COMPARE_FAILED, message);
+ return false;
+ }
+
+ if (input.getCompareResult() == null) {
+ MessageDialog.openInformation(shell, COMPARE_FAILED, "There are no differences between the selected inputs");
+ return false;
+ }
+
+ return true;
+
+ } catch (InterruptedException x) {
+ // cancelled by user
+ } catch (InvocationTargetException x) {
+ MessageDialog.openError(shell, COMPARE_FAILED, x.getTargetException().getMessage());
+ }
+ return false;
+ }
+
+ /**
+ * Registers an image for the given type.
+ */
+ private static void registerImage(String type, Image image, boolean dispose) {
+ fgImages.put(normalizeCase(type), image);
+ if (image != null && dispose) {
+ fgDisposeOnShutdownImages.add(image);
+ }
+ }
+
+ /**
+ * Registers an image descriptor for the given type.
+ *
+ * @param type the type
+ * @param descriptor the image descriptor
+ */
+ public static void registerImageDescriptor(String type, ImageDescriptor descriptor) {
+ fgImageDescriptors.put(normalizeCase(type), descriptor);
+ }
+
+ public static ImageDescriptor getImageDescriptor(String relativePath) {
+
+ URL installURL= null;
+ if (fgComparePlugin != null)
+ installURL= fgComparePlugin.getDescriptor().getInstallURL();
+
+ if (installURL != null) {
+ try {
+ URL url= new URL(installURL, "icons/basic/" + relativePath);
+ return ImageDescriptor.createFromURL(url);
+ } catch (MalformedURLException e) {
+ Assert.isTrue(false);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a shared image for the given type, or a generic image if none
+ * has been registered for the given type.
+ * <p>
+ * Note: Images returned from this method will be automitically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param type the type
+ * @return the image
+ */
+ public static Image getImage(String type) {
+
+ type= normalizeCase(type);
+
+ boolean dispose= false;
+ Image image= null;
+ if (type != null)
+ image= (Image) fgImages.get(type);
+ if (image == null) {
+ ImageDescriptor id= (ImageDescriptor) fgImageDescriptors.get(type);
+ if (id != null) {
+ image= id.createImage();
+ dispose= true;
+ }
+
+ if (image == null) {
+ if (fgComparePlugin != null) {
+ if (ITypedElement.FOLDER_TYPE.equals(type)) {
+ image= getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
+ //image= SharedImages.getImage(ISharedImages.IMG_OBJ_FOLDER);
+ } else {
+ image= createWorkbenchImage(type);
+ dispose= true;
+ }
+ } else {
+ id= (ImageDescriptor) fgImageDescriptors.get(normalizeCase("file"));
+ image= id.createImage();
+ dispose= true;
+ }
+ }
+ if (image != null)
+ registerImage(type, image, dispose);
+ }
+ return image;
+ }
+
+ /**
+ * Returns a shared image for the given adaptable.
+ * This convenience method queries the given adaptable
+ * for its <code>IWorkbenchAdapter.getImageDescriptor</code>, which it
+ * uses to create an image if it does not already have one.
+ * <p>
+ * Note: Images returned from this method will be automitically disposed
+ * of when this plug-in shuts down. Callers must not dispose of these
+ * images themselves.
+ * </p>
+ *
+ * @param adaptable the adaptable for which to find an image
+ * @return an image
+ */
+ public static Image getImage(IAdaptable adaptable) {
+ if (adaptable != null) {
+ Object o= adaptable.getAdapter(IWorkbenchAdapter.class);
+ if (o instanceof IWorkbenchAdapter) {
+ ImageDescriptor id= ((IWorkbenchAdapter) o).getImageDescriptor(adaptable);
+ if (id != null) {
+ Image image= (Image)fgImages2.get(id);
+ if (image == null) {
+ image= id.createImage();
+ try {
+ fgImages2.put(id, image);
+ } catch (NullPointerException ex) {
+ System.out.println("NPE in CompareUIPlugin.getImage");
+ }
+ fgDisposeOnShutdownImages.add(image);
+
+ }
+ return image;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Image createWorkbenchImage(String type) {
+ IEditorRegistry er= getDefault().getWorkbench().getEditorRegistry();
+ ImageDescriptor id= er.getImageDescriptor("foo." + type);
+ return id.createImage();
+ }
+
+ /**
+ * Registers the given structure creator descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param descriptor the descriptor to register
+ */
+ public static void registerStructureCreator(String types, IStructureCreatorDescriptor descriptor) {
+ if (types != null) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgStructureCreators.put(normalizeCase(extension), descriptor);
+ }
+ }
+ }
+
+ /**
+ * Returns an structure creator descriptor for the given type.
+ *
+ * @param type the type for which to find a descriptor
+ * @return a descriptor for the given type, or <code>null</code> if no
+ * descriptor has been registered
+ */
+ public static IStructureCreatorDescriptor getStructureCreator(String type) {
+ return (IStructureCreatorDescriptor) fgStructureCreators.get(normalizeCase(type));
+ }
+
+ /**
+ * Registers the given structure viewer descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param the descriptor to register
+ */
+ public static void registerStructureViewerDescriptor(String types, IViewerDescriptor descriptor) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgStructureViewerDescriptors.put(normalizeCase(extension), descriptor);
+ }
+ }
+
+ /**
+ * Registers the given content merge viewer descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param descriptor the descriptor to register
+ */
+ public static void registerContentMergeViewerDescriptor(String types, IViewerDescriptor descriptor) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgContentMergeViewerDescriptors.put(normalizeCase(extension), descriptor);
+ }
+ }
+
+ /**
+ * Registers the given content viewer descriptor for one or more types.
+ *
+ * @param types one or more types separated by commas and whitespace
+ * @param descriptor the descriptor to register
+ */
+ public static void registerContentViewerDescriptor(String types, IViewerDescriptor descriptor) {
+ StringTokenizer tokenizer= new StringTokenizer(types, ",");
+ while (tokenizer.hasMoreElements()) {
+ String extension= tokenizer.nextToken().trim();
+ fgContentViewerDescriptors.put(normalizeCase(extension), descriptor);
+ }
+ }
+
+ /**
+ * Returns a structure compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input, the old viewer
+ * is returned. Otherwise, the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a structure compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a structure viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent,
+ CompareConfiguration configuration) {
+
+ if (input.getLeft() == null || input.getRight() == null) // we don't show the structure of additions or deletions
+ return null;
+
+ String type= getType(input);
+ if (type == null)
+ return null;
+
+ type= normalizeCase(type);
+
+ IViewerDescriptor vd= (IViewerDescriptor) fgStructureViewerDescriptors.get(type);
+ if (vd != null)
+ return vd.createViewer(oldViewer, parent, configuration);
+
+ IStructureCreatorDescriptor scc= getStructureCreator(type);
+ if (scc != null) {
+ IStructureCreator sc= scc.createStructureCreator();
+ if (sc != null) {
+ StructureDiffViewer sdv= new StructureDiffViewer(parent, configuration);
+ sdv.setStructureCreator(sc);
+ return sdv;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a content compare viewer based on an old viewer and an input object.
+ * If the old viewer is suitable for showing the input the old viewer
+ * is returned. Otherwise the input's type is used to find a viewer descriptor in the registry
+ * which in turn is used to create a content compare viewer under the given parent composite.
+ * If no viewer descriptor can be found <code>null</code> is returned.
+ *
+ * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
+ * @param input the input object for which to find a content viewer
+ * @param parent the SWT parent composite under which the new viewer is created
+ * @param configuration a configuration which is passed to a newly created viewer
+ * @return the compare viewer which is suitable for the given input object or <code>null</code>
+ */
+ public static Viewer findContentViewer(Viewer oldViewer, Object in, Composite parent, CompareConfiguration cc) {
+
+ if (! (in instanceof ICompareInput)) {
+ String type= ITypedElement.TEXT_TYPE;
+ if (in instanceof ITypedElement) {
+ ITypedElement tin= (ITypedElement) in;
+ type= tin.getType();
+ }
+ type= normalizeCase(type);
+
+ IViewerDescriptor vd= (IViewerDescriptor) fgContentViewerDescriptors.get(type);
+ Viewer viewer= null;
+ if (vd != null) {
+ viewer= vd.createViewer(oldViewer, parent, cc);
+ if (viewer != null)
+ return viewer;
+ }
+ // fallback
+ return new TextViewer(parent);
+ }
+
+ ICompareInput input= (ICompareInput) in;
+ String type= getType(input);
+ type= normalizeCase(type);
+
+ if (ITypedElement.FOLDER_TYPE.equals(type))
+ return null;
+
+ if (type != null) {
+ IViewerDescriptor vd= (IViewerDescriptor) fgContentMergeViewerDescriptors.get(type);
+ Viewer viewer= null;
+ if (vd != null) {
+ viewer= vd.createViewer(oldViewer, parent, cc);
+ if (viewer != null)
+ return viewer;
+ }
+ }
+
+ // fallback
+ String leftType= guessType(input.getLeft());
+ String rightType= guessType(input.getRight());
+
+ if (leftType != null && rightType != null) {
+ if (ITypedElement.TEXT_TYPE.equals(leftType) && ITypedElement.TEXT_TYPE.equals(rightType))
+ type= ITypedElement.TEXT_TYPE;
+ else
+ type= "binary";
+
+ IViewerDescriptor vd= (IViewerDescriptor) fgContentMergeViewerDescriptors.get(normalizeCase(type));
+ if (vd != null)
+ return vd.createViewer(oldViewer, parent, cc);
+ }
+ return null;
+ }
+
+ /**
+ * Determines the type of the given threeway input by analyzing
+ * the types (file extension) of the individual parts.
+ * Returns null if no type can be determined.
+ */
+ private static String getType(ICompareInput input) {
+ ITypedElement ancestor= input.getAncestor();
+ ITypedElement left= input.getLeft();
+ ITypedElement right= input.getRight();
+
+ String[] types= new String[3];
+ int cnt= 0;
+
+ if (ancestor != null) {
+ String type= ancestor.getType();
+ if (type != null)
+ types[cnt++]= type;
+ }
+ if (left != null) {
+ String type= left.getType();
+ if (type != null)
+ types[cnt++]= type;
+ }
+ if (right != null) {
+ String type= right.getType();
+ if (type != null)
+ types[cnt++]= type;
+ }
+ boolean homogenous= false;
+ switch (cnt) {
+ case 1:
+ homogenous= true;
+ break;
+ case 2:
+ homogenous= types[0].equals(types[1]);
+ break;
+ case 3:
+ homogenous= types[0].equals(types[1]) && types[1].equals(types[2]);
+ break;
+ }
+ if (homogenous)
+ return types[0];
+ return null;
+ }
+
+ /**
+ * Guesses the file type of the given input.
+ * Returns ITypedElement.TEXT_TYPE if the first 1000 bytes contain only values in the range 0-127.
+ * Returns ITypedElement.UNKNOWN_TYPE if a single byte is out of this range.
+ * Returns <code>null</code> if the input isn't an <code>IStreamContentAccessor</code>.
+ */
+ private static String guessType(ITypedElement input) {
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ try {
+ InputStream is= sca.getContents();
+ if (is == null)
+ return null;
+ for (int i= 0; i < 1000; i++)
+ if (is.read() >= 128)
+ return ITypedElement.UNKNOWN_TYPE;
+ return ITypedElement.TEXT_TYPE;
+ } catch (CoreException ex) {
+ // be silent and return UNKNOWN_TYPE
+ } catch (IOException ex) {
+ // be silent and return UNKNOWN_TYPE
+ }
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+ return null;
+ }
+
+ private static IViewerDescriptor getContentViewerDescriptor2(String type) {
+ return (IViewerDescriptor) fgContentMergeViewerDescriptors.get(normalizeCase(type));
+ }
+
+ private static String normalizeCase(String s) {
+ if (NORMALIZE_CASE && s != null)
+ return s.toUpperCase();
+ return s;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java
new file mode 100644
index 000000000..62d9d55bf
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareViewerSwitchingPane.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.*;
+
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.util.ListenerList;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+import org.eclipse.compare.structuremergeviewer.DiffTreeViewer;
+
+
+/**
+ * A custom <code>Pane</code> which supports viewer switching.
+ * <p>
+ * If a property with the name CompareUI.COMPARE_VIEWER_TITLE is set
+ * on the top level SWT control of a viewer, it is used as a title in the pane's
+ * title bar.
+ */
+public abstract class CompareViewerSwitchingPane extends Pane
+ implements ISelectionChangedListener, ISelectionProvider, IDoubleClickListener {
+
+ /**
+ * Used whenever the input is null or no viewer can be found.
+ */
+ private static class NullViewer extends AbstractViewer {
+
+ private Control fDummy;
+
+ public NullViewer(Composite parent) {
+
+ fDummy= new Tree(parent, SWT.NULL);
+
+ CompareViewerSwitchingPane.clearToolBar(parent);
+ }
+
+ public Control getControl() {
+ return fDummy;
+ }
+ }
+
+ private Viewer fViewer;
+ private Object fInput;
+ private ListenerList fSelectionListeners= new ListenerList();
+ private ListenerList fOpenListeners= new ListenerList();
+ private boolean fControlVisibility= false;
+
+ /**
+ * Creates a ViewerPane as a child of the given parent and with the
+ * specified SWT style bits.
+ */
+ public CompareViewerSwitchingPane(Composite parent, int style) {
+ this(parent, style, false);
+ }
+
+ /**
+ * Creates a ViewerPane as a child of the given parent and with the
+ * specified SWT style bits.
+ */
+ public CompareViewerSwitchingPane(Composite parent, int style, boolean visibility) {
+ super(parent, style);
+
+ fControlVisibility= visibility;
+
+ setViewer(new NullViewer(this));
+
+ addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).removeSelectionChangedListener(CompareViewerSwitchingPane.this);
+ if (fViewer instanceof StructuredViewer)
+ ((StructuredViewer) fViewer).removeDoubleClickListener(CompareViewerSwitchingPane.this);
+ fViewer= null;
+ fInput= null;
+ fSelectionListeners= null;
+ }
+ }
+ );
+ }
+
+ /**
+ * Returns the current viewer.
+ */
+ public Viewer getViewer() {
+ return fViewer;
+ }
+
+ /**
+ * Sets the current viewer.
+ */
+ private void setViewer(Viewer newViewer) {
+
+ if (newViewer == fViewer)
+ return;
+
+ boolean oldEmpty= isEmpty();
+
+ if (fViewer != null) {
+
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).removeSelectionChangedListener(this);
+
+ if (fViewer instanceof StructuredViewer)
+ ((StructuredViewer)fViewer).removeDoubleClickListener(this);
+
+ Control content= getContent();
+ setContent(null);
+
+ fViewer.setInput(null);
+
+ if (content != null && !content.isDisposed()) {
+ content.dispose();
+ }
+ } else
+ oldEmpty= false;
+ setContent(null);
+
+ fViewer= newViewer;
+
+ if (fViewer != null) {
+ // workaround: setContent changes the visibility of the CustomPane
+ boolean old= getVisible();
+ setContent(fViewer.getControl());
+ setVisible(old);
+ // end of workaround
+
+ boolean newEmpty= isEmpty();
+
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).addSelectionChangedListener(this);
+ if (fViewer instanceof StructuredViewer)
+ ((StructuredViewer)fViewer).addDoubleClickListener(this);
+
+ if (oldEmpty != newEmpty) {// relayout my container
+ Composite parent= getParent();
+ if (parent instanceof Splitter)
+ ((Splitter)parent).setVisible(this, fControlVisibility ? !newEmpty : true);
+ //else
+ // parent.layout(true);
+ }
+
+ //else if (!newEmpty)// otherwise just relayout myself
+ layout(true);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if no viewer is installed or if the current viewer
+ * is a <code>NullViewer</code>.
+ */
+ public boolean isEmpty() {
+ return fViewer == null || fViewer instanceof NullViewer;
+ }
+
+ public void addSelectionChangedListener(ISelectionChangedListener l) {
+ fSelectionListeners.add(l);
+ }
+
+ public void removeSelectionChangedListener(ISelectionChangedListener l) {
+ fSelectionListeners.remove(l);
+ }
+
+ public void addDoubleClickListener(IDoubleClickListener l) {
+ fOpenListeners.add(l);
+ }
+
+ public void removeDoubleClickListener(IDoubleClickListener l) {
+ fOpenListeners.remove(l);
+ }
+
+ public void doubleClick(DoubleClickEvent event) {
+ Object[] listeners= fOpenListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((IDoubleClickListener) listeners[i]).doubleClick(event);
+ }
+
+ public ISelection getSelection() {
+ if (fViewer instanceof ISelectionProvider)
+ return ((ISelectionProvider) fViewer).getSelection();
+ return null;
+ }
+
+ public void setSelection(ISelection s) {
+ if (fViewer instanceof ISelectionProvider)
+ ((ISelectionProvider) fViewer).setSelection(s);
+ }
+
+ public void selectionChanged(SelectionChangedEvent ev) {
+ Object[] listeners= fSelectionListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++)
+ ((ISelectionChangedListener) listeners[i]).selectionChanged(ev);
+ }
+
+ public void setInput(Object input) {
+
+ if (fInput == input)
+ return;
+
+ fInput= input;
+
+ // viewer switching
+ Viewer newViewer= null;
+ if (input != null)
+ newViewer= getViewer(fViewer, input);
+
+ if (newViewer == null) {
+ if (fViewer instanceof NullViewer)
+ return;
+ newViewer= new NullViewer(this);
+ }
+
+ setViewer(newViewer);
+
+ // set input
+ fViewer.setInput(input);
+
+ Image image= null;
+ if (!(fViewer instanceof NullViewer) && input instanceof ICompareInput)
+ image= ((ICompareInput)input).getImage();
+ setImage(image);
+
+ String title= null;
+ if (fViewer != null) {
+ Control c= fViewer.getControl();
+ if (c != null) {
+ Object data= c.getData(CompareUI.COMPARE_VIEWER_TITLE);
+ if (data instanceof String)
+ title= (String) data;
+ }
+ }
+
+ setText(title != null ? title : "");
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ abstract protected Viewer getViewer(Viewer oldViewer, Object input);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java
new file mode 100644
index 000000000..dce0ae6b9
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DiffImage.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.graphics.*;
+
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * Combines an image with an overlay.
+ */
+public class DiffImage extends CompositeImageDescriptor {
+
+ static final int HEIGHT= 16;
+
+ private Image fBaseImage;
+ private ImageDescriptor fOverlayImage;
+ private int fWidth;
+
+ public DiffImage(Image base, ImageDescriptor overlay, int w) {
+ fBaseImage= base;
+ fOverlayImage= overlay;
+ fWidth= w;
+ }
+
+ protected Point getSize() {
+ return new Point(fWidth, HEIGHT);
+ }
+
+ protected void drawCompositeImage(int width, int height) {
+
+ if (fBaseImage != null) {
+ ImageData base= fBaseImage.getImageData();
+ if (base == null)
+ base= DEFAULT_IMAGE_DATA;
+ try {
+ drawImage(base, fWidth - base.width, 0);
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // workaround for PR 1GCQKWP
+ }
+ }
+
+ if (fOverlayImage != null) {
+ ImageData dir= fOverlayImage.getImageData();
+ if (dir == null)
+ dir= DEFAULT_IMAGE_DATA;
+ drawImage(dir, 0, (HEIGHT - dir.height) / 2);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java
new file mode 100644
index 000000000..81591752b
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.jface.text.*;
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+import org.eclipse.compare.contentmergeviewer.ITokenComparator;
+
+/**
+ * Implements the <code>ITokenComparator</code> interface for lines in a document.
+ * A <code>DocLineComparator</code> is used as the input for the <code>RangeDifferencer</code>
+ * engine to perform a line oriented compare on documents.
+ * <p>
+ * A <code>DocLineComparator</code> doesn't know anything about line separators because
+ * its notion of lines is solely defined in the underlying <code>IDocument</code>.
+ */
+public class DocLineComparator implements IRangeComparator {
+
+ private IDocument fDocument;
+ private int fLineOffset;
+ private int fLineCount;
+ private int fLength;
+ private boolean fIgnoreWhiteSpace;
+
+ /**
+ * Creates a <code>DocLineComparator</code> for the given document range.
+ * ignoreWhiteSpace controls whether comparing lines (in method
+ * <code>rangesEqual<code>) should ignore whitespace.
+ *
+ * @param document the document from which the lines are taken
+ * @param region if non-<code>null</code> only lines within this range are taken
+ * @param ignoreWhiteSpace if <code>true</code> white space is ignored when comparing lines
+ */
+ public DocLineComparator(IDocument document, IRegion region, boolean ignoreWhiteSpace) {
+
+ fDocument= document;
+ fIgnoreWhiteSpace= ignoreWhiteSpace;
+
+ fLineOffset= 0;
+ if (region != null) {
+ fLength= region.getLength();
+ int start= region.getOffset();
+ try {
+ fLineOffset= fDocument.getLineOfOffset(start);
+ } catch (BadLocationException ex) {
+ }
+
+ if (fLength == 0)
+ fLineCount= 0;
+ else {
+ int endLine= fDocument.getNumberOfLines();
+ try {
+ endLine= fDocument.getLineOfOffset(start + fLength);
+ } catch (BadLocationException ex) {
+ }
+ fLineCount= endLine - fLineOffset + 1;
+ }
+
+ } else {
+ fLength= document.getLength();
+ fLineCount= fDocument.getNumberOfLines();
+ }
+ }
+
+ /**
+ * Returns the content of lines in the specified range as a String.
+ * This includes the line separators.
+ *
+ * @param start index of first line
+ * @param length number of lines
+ * @return the contents of the specified line range as a String
+ */
+ public String extract(int start, int length) {
+ if (fLength > 0) {
+ if (fLength == 1)
+ return extract(start);
+ int startPos= getTokenStart(start);
+ int endPos= getTokenStart(start + length);
+ try {
+ return fDocument.get(startPos, endPos - startPos);
+ } catch (BadLocationException e) {
+ System.out.println("extract("+fDocument.getLength()+"): " + startPos + " " + endPos);
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Extract a single line from the underlying document without the line separator.
+ *
+ * @param line the number of the line to extract
+ * @return the contents of the line as a String
+ */
+ public String extract(int line) {
+ line += fLineOffset;
+ if (line < fLength) {
+ try {
+ IRegion r= fDocument.getLineInformation(line);
+ return fDocument.get(r.getOffset(), r.getLength());
+ // return fDocument.getLine(line);
+ } catch(BadLocationException e) {
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Returns the number of lines in the document.
+ *
+ * @return number of lines
+ */
+ public int getRangeCount() {
+ return fLineCount;
+ }
+
+ /**
+ * Returns <code>true</code> if a line given by the first index
+ * matches a line specified by the other <code>IRangeComparator</code> and index.
+ *
+ * @param thisIndex the number of the line within this range comparator
+ * @param other the range comparator to compare this with
+ * @param otherIndex the number of the line within the other comparator
+ * @return <code>true</code> if the lines are equal
+ */
+ public boolean rangesEqual(int thisIndex, IRangeComparator other0, int otherIndex) {
+
+ if (other0 != null && other0.getClass() == getClass()) {
+ DocLineComparator other= (DocLineComparator) other0;
+
+ if (fIgnoreWhiteSpace) {
+ String s1= extract(thisIndex);
+ String s2= other.extract(otherIndex);
+ return compare(s1, s2);
+ }
+
+ int tlen= getTokenLength(thisIndex);
+ int olen= other.getTokenLength(otherIndex);
+ if (tlen == olen) {
+ String s1= extract(thisIndex);
+ String s2= other.extract(otherIndex);
+ return s1.equals(s2);
+ }
+ }
+ return false;
+ }
+
+ /* (non Javadoc)
+ * see IRangeComparator.skipRangeComparison
+ */
+ public boolean skipRangeComparison(int length, int max, IRangeComparator other) {
+ return false;
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenStart
+ */
+ public int getTokenStart(int line) {
+ try {
+ IRegion r= fDocument.getLineInformation(fLineOffset + line);
+ return r.getOffset();
+ //return fDocument.getLineStartOffset(fLineOffset + line);
+ } catch (BadLocationException ex) {
+ return fDocument.getLength();
+ }
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenEnd
+ */
+ public int getTokenEnd(int start, int length) {
+ return getTokenStart(start + length);
+ }
+
+ //---- private methods
+
+ private int getTokenLength(int line) {
+ if (fLength == 0)
+ return 0;
+ return getTokenStart(line + 1) - getTokenStart(line);
+ }
+
+ private boolean compare(String s1, String s2) {
+ int i1= 0;
+ int i2= 0;
+ int l1= s1.length();
+ int l2= s2.length();
+ char c1= ' ';
+ char c2= ' ';
+ while (i1 < l1 || i2 < l2) {
+ if (i1 < l1) {
+ c1= s1.charAt(i1);
+ if (Character.isWhitespace(c1)) {
+ i1++;
+ continue;
+ }
+ }
+ if (i2 < l2) {
+ c2= s2.charAt(i2);
+ if (Character.isWhitespace(c2)) {
+ i2++;
+ continue;
+ }
+ }
+ if (c1 != c2)
+ return false;
+ i1++;
+ i2++;
+ }
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java
new file mode 100644
index 000000000..cd866e6c1
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IStructureCreatorDescriptor.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.compare.structuremergeviewer.IStructureCreator;
+
+/**
+ * A factory object for creating <code>IStructureCreator</code>s from a descriptor.
+ * <p>
+ * It is used when registering <code>IStructureCreator</code> for types
+ * in <code>CompareUIPlugin.registerStructureCreator</code>.
+ * </p>
+ *
+ * @see IStructureCreator
+ * @see CompareUIPlugin
+ */
+public interface IStructureCreatorDescriptor {
+
+ /**
+ * Creates a new structure creator.
+ *
+ * @return a newly created structure creator
+ */
+ IStructureCreator createStructureCreator();
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java
new file mode 100644
index 000000000..f22578149
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IViewerDescriptor.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * A factory object for creating a <code>Viewer</code>s from a descriptor.
+ * <p>
+ * It is used when registering a viewer for a specific type
+ * in <code>CompareUIPlugin.registerContentViewerDescriptor</code> and
+ * in <code>CompareUIPlugin.registerStructureViewerDescriptor</code>.
+ *
+ * @see org.eclipse.compare.structuremergeviewer.IStructureCreator
+ * @see CompareUIPlugin
+ */
+public interface IViewerDescriptor {
+
+ /**
+ * Creates a new viewer from this descriptor under the given STW parent control.
+ * If the current viewer has the same type as a new viewer
+ * the implementation of this method is free to return the current viewer instead.
+ *
+ * @param currentViewer the current viewer which is going to be replaced with a new viewer.
+ * @param parent the SWT parent control under which the new viewer has to be created.
+ * @param config a compare configuration the new viewer might be interested in.
+ * @return a new viewer or the current viewer.
+ */
+ Viewer createViewer(Viewer currentViewer, Composite parent, CompareConfiguration config);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java
new file mode 100644
index 000000000..64aed3b09
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/IgnoreWhiteSpaceAction.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.*;
+
+/**
+ * Toggles the <code>ICompareConfiguration.IGNORE_WS</code> property of an
+ * <code>ICompareConfiguration</code>.
+ */
+public class IgnoreWhiteSpaceAction extends ChangePropertyAction {
+
+ public IgnoreWhiteSpaceAction(ResourceBundle bundle, CompareConfiguration cc) {
+ super(bundle, cc, "action.IgnoreWhiteSpace.", CompareConfiguration.IGNORE_WHITESPACE);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java
new file mode 100644
index 000000000..2d56fc406
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageCanvas.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A <code>Canvas</code> showing a single centered SWT <code>Image</code>.
+ * If the <code>Image</code> is larger than the <code>Canvas<code>,
+ * <code>Scrollbars</code> will appear.
+ */
+class ImageCanvas extends Canvas {
+
+ private Image fImage;
+
+ /**
+ * Create a new ImageCanvas with the given SWT stylebits.
+ * (SWT.H_SCROLL and SWT.V_SCROLL are automtically added).
+ */
+ public ImageCanvas(Composite parent, int style) {
+ super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL);
+
+ ScrollBar sb= getHorizontalBar();
+ sb.setIncrement(20);
+ sb.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ repaint();
+ }
+ });
+
+ sb= getVerticalBar();
+ sb.setIncrement(20);
+ sb.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ repaint();
+ }
+ });
+
+ addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event e) {
+ updateScrollbars();
+ }
+ });
+
+ addListener(SWT.Paint, new Listener() {
+ public void handleEvent(Event event) {
+ paint(event.gc);
+ }
+ });
+ }
+
+ /**
+ * Set the SWT Image to use as the ImageCanvas contents.
+ */
+ public void setImage(Image img) {
+ fImage= img;
+
+ if (!isDisposed()) {
+ getHorizontalBar().setSelection(0);
+ getVerticalBar().setSelection(0);
+ updateScrollbars();
+ getParent().layout();
+ redraw();
+ }
+ }
+
+ public void repaint() {
+ if (!isDisposed()) {
+ GC gc= new GC(this);
+ paint(gc);
+ gc.dispose();
+ }
+ }
+
+ /**
+ * @private
+ */
+ private void paint(GC gc) {
+ if (fImage != null) {
+ Rectangle bounds= fImage.getBounds();
+ Rectangle clientArea= getClientArea();
+
+ int x;
+ if (bounds.width < clientArea.width)
+ x= (clientArea.width - bounds.width) / 2;
+ else
+ x= -getHorizontalBar().getSelection();
+
+ int y;
+ if (bounds.height < clientArea.height)
+ y= (clientArea.height - bounds.height) / 2;
+ else
+ y= -getVerticalBar().getSelection();
+
+ gc.drawImage(fImage, x, y);
+ }
+ }
+
+ /**
+ * @private
+ */
+ private void updateScrollbars() {
+ Rectangle bounds= fImage != null ? fImage.getBounds() : new Rectangle(0, 0, 0, 0);
+ Point size= getSize();
+ Rectangle clientArea= getClientArea();
+
+ ScrollBar horizontal= getHorizontalBar();
+ if (bounds.width <= clientArea.width) {
+ horizontal.setVisible(false);
+ horizontal.setSelection(0);
+ } else {
+ horizontal.setPageIncrement(clientArea.width - horizontal.getIncrement());
+ int max= bounds.width + (size.x - clientArea.width);
+ horizontal.setMaximum(max);
+ horizontal.setThumb(size.x > max ? max : size.x);
+ horizontal.setVisible(true);
+ }
+
+ ScrollBar vertical= getVerticalBar();
+ if (bounds.height <= clientArea.height) {
+ vertical.setVisible(false);
+ vertical.setSelection(0);
+ } else {
+ vertical.setPageIncrement(clientArea.height - vertical.getIncrement());
+ int max= bounds.height + (size.y - clientArea.height);
+ vertical.setMaximum(max);
+ vertical.setThumb(size.y > max ? max : size.y);
+ vertical.setVisible(true);
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java
new file mode 100644
index 000000000..0114a22c4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewer.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+
+/**
+ */
+public class ImageMergeViewer extends ContentMergeViewer {
+
+ private static final String BUNDLE_NAME= "org.eclipse.compare.internal.ImageMergeViewerResources";
+
+ private Object fAncestorImage;
+ private Object fLeftImage;
+ private Object fRightImage;
+
+ private ImageCanvas fAncestor;
+ private ImageCanvas fLeft;
+ private ImageCanvas fRight;
+
+
+ public ImageMergeViewer(Composite parent, int styles, CompareConfiguration mp) {
+ super(styles, ResourceBundle.getBundle(BUNDLE_NAME), mp);
+ buildControl(parent);
+ getControl().setData(CompareUI.COMPARE_VIEWER_TITLE, "Image Compare");
+ }
+
+ protected void updateContent(Object ancestor, Object left, Object right) {
+
+ fAncestorImage= ancestor;
+ setInput(fAncestor, ancestor);
+
+ fLeftImage= left;
+ setInput(fLeft, left);
+
+ fRightImage= right;
+ setInput(fRight, right);
+ }
+
+ /**
+ * We can't modify the contents of either side we just return null.
+ */
+ protected byte[] getContents(boolean left) {
+ return null;
+ }
+
+ public void createControls(Composite composite) {
+ fAncestor= new ImageCanvas(composite, SWT.NONE);
+ fLeft= new ImageCanvas(composite, SWT.NONE);
+ fRight= new ImageCanvas(composite, SWT.NONE);
+ }
+
+ private static void setInput(ImageCanvas canvas, Object input) {
+ if (canvas != null) {
+
+ InputStream stream= null;
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ if (sca != null) {
+ try {
+ stream= sca.getContents();
+ } catch (CoreException ex) {
+ }
+ }
+ }
+
+ Image image= null;
+ Display display= canvas.getDisplay();
+ if (stream != null) {
+ try {
+ image= new Image(display, stream);
+ } catch (SWTException ex) {
+ }
+ }
+
+ canvas.setImage(image);
+ if (image != null) {
+ canvas.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ } else {
+ canvas.setBackground(null);
+ }
+ }
+ }
+
+ protected void handleResizeAncestor(int x, int y, int width, int height) {
+ if (width > 0) {
+ fAncestor.setVisible(true);
+ fAncestor.setBounds(x, y, width, height);
+ } else {
+ fAncestor.setVisible(false);
+ }
+ }
+
+ protected void handleResizeLeftRight(int x, int y, int width1, int centerWidth, int width2, int height) {
+ fLeft.setBounds(x, y, width1, height);
+ fRight.setBounds(x+width1+centerWidth, y, width2, height);
+ }
+
+ protected void copy(boolean leftToRight) {
+ if (leftToRight) {
+ fRightImage= fLeftImage;
+ setInput(fRight, fRightImage);
+ setRightDirty(true);
+ } else {
+ fLeftImage= fRightImage;
+ setInput(fLeft, fLeftImage);
+ setLeftDirty(true);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java
new file mode 100644
index 000000000..e57c9c99d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerCreator.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.compare.*;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A factory object for the <code>ImageMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class ImageMergeViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new ImageMergeViewer(parent, SWT.NULL, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties
new file mode 100644
index 000000000..dcc529805
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ImageMergeViewerResources.properties
@@ -0,0 +1,55 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 1999-2001
+# =====================================
+
+# @(#)ImageMergeViewerResources.properties
+#
+# Resource strings for ImageMergeViewer.java
+
+#####################################################
+# Images
+#####################################################
+
+#####################################################
+# Status messages
+#####################################################
+
+#####################################################
+# Dialogs
+#####################################################
+
+CantSaveProperty.title=Error while saving contents
+CantSaveProperty.message=Couldn''t save contents.\nReason: {0}
+
+ContentInaccessible.title=Error while accessing content
+ContentInaccessible.message=Content currently inaccessible.
+
+#####################################################
+# Actions
+#####################################################
+
+action.CopyLeftToRight.label=Copy Left to Right
+action.CopyLeftToRight.tooltip=Copy image from left to right
+action.CopyLeftToRight.image=ctool16/lefttoright.gif
+
+action.CopyRightToLeft.label=Copy Right to Left
+action.CopyRightToLeft.tooltip=Copy image from right to left
+action.CopyRightToLeft.image=ctool16/righttoleft.gif
+
+#action.ToggleMergeDirection.label=Toggle Merge Direction
+#action.ToggleMergeDirection.tooltip=Toggle merge direction
+#action.ToggleMergeDirection.image=ctool16/toggle.gif
+
+action.EnableAncestor.label=Enable Ancestor Pane
+action.EnableAncestor.tooltip=Controls visibility of ancestor pane
+action.EnableAncestor.image=ctool16/panes3.gif
+
+# Pop up menu
+
+#action.Save.label=Save@Ctrl+S
+#action.Save.tooltip=Save Changes
+
+
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java
new file mode 100644
index 000000000..ce4780b9f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.Color;
+
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+
+/**
+ * Extends the SEF SourceViewer with some convenience methods.
+ */
+public class MergeSourceViewer extends SourceViewer {
+
+ private IRegion fRegion;
+ private boolean fEnabled= true;
+
+
+ public MergeSourceViewer(Composite parent) {
+ super(parent, null, SWT.H_SCROLL + SWT.V_SCROLL);
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (enabled != fEnabled) {
+ fEnabled= enabled;
+ StyledText c= getTextWidget();
+ if (c != null) {
+ c.setEnabled(enabled);
+ Display d= c.getDisplay();
+ c.setBackground(enabled ? d.getSystemColor(SWT.COLOR_LIST_BACKGROUND) : null);
+ }
+ }
+ }
+
+ public boolean getEnabled() {
+ return fEnabled;
+ }
+
+ public void setRegion(IRegion region) {
+ fRegion= region;
+ }
+
+ public IRegion getRegion() {
+ return fRegion;
+ }
+
+ public boolean isControlOkToUse() {
+ StyledText t= getTextWidget();
+ return t != null && !t.isDisposed();
+ }
+
+ public void setSelection(Position p) {
+ setSelectedRange(p.getOffset(), p.getLength());
+ }
+
+ public void setLineBackground(Position p, Color c) {
+ StyledText t= getTextWidget();
+ if (t != null && !t.isDisposed()) {
+ Point region= new Point(0, 0);
+ getLineRange(p, region);
+
+ region.x-= getDocumentRegionOffset();
+
+ t.setLineBackground(region.x, region.y, c);
+ }
+ }
+
+ public void resetLineBackground() {
+ StyledText t= getTextWidget();
+ if (t != null && !t.isDisposed()) {
+ int lines= getLineCount();
+ t.setLineBackground(0, lines, null);
+ }
+ }
+
+ /**
+ * Returns number of lines in document region.
+ */
+ public int getLineCount() {
+ IRegion region= getVisibleRegion();
+
+ int length= region.getLength();
+ if (length == 0)
+ return 0;
+
+ IDocument doc= getDocument();
+ int startLine= 0;
+ int endLine= 0;
+
+ int start= region.getOffset();
+ try {
+ startLine= doc.getLineOfOffset(start);
+ } catch(BadLocationException ex) {
+ }
+ try {
+ endLine= doc.getLineOfOffset(start+length);
+ } catch(BadLocationException ex) {
+ }
+
+ return endLine-startLine+1;
+ }
+
+ public int getViewportLines() {
+ StyledText te= getTextWidget();
+ Rectangle clArea= te.getClientArea();
+ if (!clArea.isEmpty())
+ return clArea.height / te.getLineHeight();
+ return 0;
+ }
+
+ public int getViewportHeight() {
+ StyledText te= getTextWidget();
+ Rectangle clArea= te.getClientArea();
+ if (!clArea.isEmpty())
+ return clArea.height;
+ return 0;
+ }
+
+ /**
+ * Returns lines
+ */
+ public int getDocumentRegionOffset() {
+ int start= getVisibleRegion().getOffset();
+ IDocument doc= getDocument();
+ if (doc != null) {
+ try {
+ return doc.getLineOfOffset(start);
+ } catch(BadLocationException ex) {
+ }
+ }
+ return 0;
+ }
+
+ public int getVerticalScrollOffset() {
+ StyledText st= getTextWidget();
+ int lineHeight= st.getLineHeight();
+ return getTopInset() - ((getDocumentRegionOffset()*lineHeight) + st.getTopPixel());
+ }
+
+ /**
+ * Returns the start line and the number of lines which correspond to the given position.
+ * Starting line number is 0 based.
+ */
+ public Point getLineRange(Position p, Point region) {
+
+ if (p == null) {
+ region.x= 0;
+ region.y= 0;
+ return region;
+ }
+
+ IDocument doc= getDocument();
+
+ int start= p.getOffset();
+ int length= p.getLength();
+
+ int startLine= 0;
+ try {
+ startLine= doc.getLineOfOffset(start);
+ } catch (BadLocationException e) {
+ }
+
+ int lineCount= 0;
+
+ if (length == 0) {
+// // if range length is 0 and if range starts a new line
+// try {
+// if (start == doc.getLineStartOffset(startLine)) {
+// lines--;
+// }
+// } catch (BadLocationException e) {
+// lines--;
+// }
+
+ } else {
+ int endLine= 0;
+ try {
+ endLine= doc.getLineOfOffset(start + length - 1); // why -1?
+ } catch (BadLocationException e) {
+ }
+ lineCount= endLine-startLine+1;
+ }
+
+ region.x= startLine;
+ region.y= lineCount;
+ return region;
+ }
+
+ /**
+ * Scroll TextPart to the given line.
+ */
+ public void vscroll(int line) {
+
+ int srcViewSize= getLineCount();
+ int srcExtentSize= getViewportLines();
+
+ if (srcViewSize > srcExtentSize) {
+ //if (pos + srcExtentSize > srcViewSize)
+ // pos= srcViewSize-srcExtentSize;
+
+ if (line < 0)
+ line= 0;
+
+ int cp= getTopIndex();
+ if (cp != line)
+ setTopIndex(line + getDocumentRegionOffset());
+ }
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java
new file mode 100644
index 000000000..29994d804
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeViewerContentProvider.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.compare.contentmergeviewer.IMergeViewerContentProvider;
+
+/**
+ * Adapts any <code>ContentMergeViewer</code> to work on an <code>ICompareInput</code>
+ * e.g. a <code>DiffNode</code>.
+ */
+public class MergeViewerContentProvider implements IMergeViewerContentProvider {
+
+ private CompareConfiguration fCompareConfiguration;
+
+ public MergeViewerContentProvider(CompareConfiguration cc) {
+ fCompareConfiguration= cc;
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer v, Object o1, Object o2) {
+ }
+
+ //---- ancestor
+
+ public String getAncestorLabel(Object element) {
+ return fCompareConfiguration.getAncestorLabel(element);
+ }
+
+ public Image getAncestorImage(Object element) {
+ return fCompareConfiguration.getAncestorImage(element);
+ }
+
+ public Object getAncestorContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getAncestor();
+ return null;
+ }
+
+ public boolean showAncestor(Object element) {
+ if (element instanceof ICompareInput)
+ return (((ICompareInput)element).getKind() & Differencer.DIRECTION_MASK) == Differencer.CONFLICTING;
+ return false;
+ }
+
+ //---- left
+
+ public String getLeftLabel(Object element) {
+ return fCompareConfiguration.getLeftLabel(element);
+ }
+
+ public Image getLeftImage(Object element) {
+ return fCompareConfiguration.getLeftImage(element);
+ }
+
+ public Object getLeftContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getLeft();
+ return null;
+ }
+
+ public boolean isLeftEditable(Object element) {
+ if (element instanceof ICompareInput) {
+ Object left= ((ICompareInput) element).getLeft();
+ if (left == null) {
+ IDiffElement parent= ((IDiffElement)element).getParent();
+ if (parent instanceof ICompareInput)
+ left= ((ICompareInput) parent).getLeft();
+ }
+ if (left instanceof IEditableContent)
+ return ((IEditableContent)left).isEditable();
+ }
+ return false;
+ }
+
+ public void saveLeftContent(Object element, byte[] bytes) {
+ if (element instanceof ICompareInput) {
+ ICompareInput node= (ICompareInput) element;
+ if (bytes != null) {
+ ITypedElement left= node.getLeft();
+ if (left instanceof IEditableContent)
+ ((IEditableContent)left).setContent(bytes);
+ } else {
+ node.copy(false);
+ }
+ }
+ }
+
+ //---- right
+
+ public String getRightLabel(Object element) {
+ return fCompareConfiguration.getRightLabel(element);
+ }
+
+ public Image getRightImage(Object element) {
+ return fCompareConfiguration.getRightImage(element);
+ }
+
+ public Object getRightContent(Object element) {
+ if (element instanceof ICompareInput)
+ return ((ICompareInput) element).getRight();
+ return null;
+ }
+
+ public boolean isRightEditable(Object element) {
+ if (element instanceof ICompareInput) {
+ Object right= ((ICompareInput) element).getRight();
+ if (right == null) {
+ IDiffContainer parent= ((IDiffElement)element).getParent();
+ if (parent instanceof ICompareInput)
+ right= ((ICompareInput) parent).getRight();
+ }
+ if (right instanceof IEditableContent)
+ return ((IEditableContent)right).isEditable();
+ }
+ return false;
+ }
+
+ public void saveRightContent(Object element, byte[] bytes) {
+ if (element instanceof ICompareInput) {
+ ICompareInput node= (ICompareInput) element;
+ if (bytes != null) {
+ ITypedElement right= node.getRight();
+ if (right instanceof IEditableContent)
+ ((IEditableContent)right).setContent(bytes);
+ } else {
+ node.copy(true);
+ }
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java
new file mode 100644
index 000000000..be0612dca
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Pane.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.ViewForm;
+
+import org.eclipse.jface.action.ToolBarManager;
+
+/**
+ * A Pane is a convenience class which installs a CustomLabel and a Toolbar (on demand).
+ * Double clicking onto the Pane's title bar maximizes the Pane
+ * to the size of an enclosing Splitter (if there is one).
+ * If more Splitters are nested maximizing walks up and maximizes to the outermost Splitter.
+ */
+public class Pane extends ViewForm {
+
+ private ToolBarManager fToolBarManager;
+
+ public Pane(Composite parent, int style) {
+ super(parent, SWT.BORDER);
+
+ marginWidth= 0;
+ marginHeight= 0;
+
+ CLabel label= new CLabel(this, SWT.NONE);
+ setTopLeft(label);
+
+ MouseAdapter ml= new MouseAdapter() {
+ public void mouseDoubleClick(MouseEvent e) {
+ Control parent= getParent();
+ if (parent instanceof Splitter)
+ ((Splitter)parent).setMaximizedControl(Pane.this);
+ }
+ };
+
+ addMouseListener(ml);
+ label.addMouseListener(ml);
+ }
+
+ public void setText(String label) {
+ CLabel cl= (CLabel) getTopLeft();
+ cl.setText(label);
+ }
+
+ public void setImage(Image image) {
+ CLabel cl= (CLabel) getTopLeft();
+ cl.setImage(image);
+ }
+
+ /**
+ * Returns a <code>ToolBarManager</code> if the given parent is a <code>ViewerPane</code>.
+ */
+ public static ToolBarManager getToolBarManager(Composite parent) {
+ if (parent instanceof Pane) {
+ Pane pane= (Pane) parent;
+ return pane.getToolBarManager();
+ }
+ return null;
+ }
+
+ /**
+ * Clear tool items in <code>ViewerPane</code>'s control bar.
+ */
+ public static void clearToolBar(Composite parent) {
+ ToolBarManager tbm= getToolBarManager(parent);
+ if (tbm != null) {
+ tbm.removeAll();
+ tbm.update(true);
+ }
+ }
+
+ //---- private stuff
+
+ private ToolBarManager getToolBarManager() {
+ if (fToolBarManager == null) {
+ ToolBar tb= new ToolBar(this, SWT.FLAT);
+ setTopCenter(tb);
+ fToolBarManager= new ToolBarManager(tb);
+ }
+ return fToolBarManager;
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java
new file mode 100644
index 000000000..e62dee7c4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.InputStream;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.IActionDelegate;
+import org.eclipse.compare.*;
+
+
+public class ReplaceWithEditionAction implements IActionDelegate {
+
+ private static final String ACTION_LABEL= "Replace with Edition";
+ private ISelection fSelection;
+
+
+ public void run(IAction action) {
+
+ Object[] s= Utilities.toArray(fSelection);
+
+ for (int i= 0; i < s.length; i++) {
+ Object o= s[i];
+ if (o instanceof IFile) {
+ replaceFromHistory((IFile)o);
+ continue;
+ }
+ if (o instanceof IAdaptable) {
+ IAdaptable a= (IAdaptable) o;
+ Object adapter= a.getAdapter(IResource.class);
+ if (adapter instanceof IFile)
+ replaceFromHistory((IFile)adapter);
+ continue;
+ }
+ }
+ }
+
+ public void selectionChanged(IAction a, ISelection s) {
+ fSelection= s;
+ }
+
+ void replaceFromHistory(IFile file) {
+
+ Shell parent= CompareUIPlugin.getShell();
+
+ IFileState states[]= null;
+ try {
+ states= file.getHistory(null);
+ } catch (CoreException ex) {
+ MessageDialog.openError(parent, ACTION_LABEL, ex.getMessage());
+ return;
+ }
+
+ if (states != null && states.length > 0) {
+
+ ITypedElement base= new ResourceNode(file);
+
+ ITypedElement[] editions= new ITypedElement[states.length];
+ for (int i= 0; i < states.length; i++)
+ editions[i]= new HistoryItem(base, states[i]);
+
+ ResourceBundle bundle= ResourceBundle.getBundle("org.eclipse.compare.internal.ReplaceWithEditionAction");
+ EditionSelectionDialog d= new EditionSelectionDialog(parent, bundle);
+
+ ITypedElement ti= d.selectEdition(base, editions, null);
+ if (ti instanceof IStreamContentAccessor) {
+ try {
+ InputStream is= ((IStreamContentAccessor)ti).getContents();
+ file.setContents(is, false, true, null);
+ } catch (CoreException ex) {
+ MessageDialog.openError(parent, ACTION_LABEL, ex.getMessage());
+ }
+ }
+ } else
+ MessageDialog.openInformation(parent, ACTION_LABEL, "No local editions available for selected resource.");
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties
new file mode 100644
index 000000000..6f591e9d5
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ReplaceWithEditionAction.properties
@@ -0,0 +1,22 @@
+# =====================================
+# Licensed Materials - Property of IBM,
+# WebSphere Studio Workbench
+# (c) Copyright IBM Corp 2001
+# ====================================
+
+# @(#)ReplaceWithEditionAction.properties
+#
+# Resources for ReplaceWithEditionAction.java
+
+title= Replace from Local History
+
+treeTitleFormat= Editions of "{0}"
+dateIcon= obj16/date.gif
+timeIcon= obj16/time.gif
+
+editionLabel= Local History ({0})
+targetLabel= Workbench
+
+todayFormat= Today ({0})
+yesterdayFormat= Yesterday ({0})
+dayFormat= {0} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
new file mode 100644
index 000000000..6c6e0735d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+
+/**
+ * A two-way or three-way compare for arbitrary IResources.
+ */
+class ResourceCompareInput extends CompareEditorInput {
+
+ private ISelection fSelection;
+
+ private class MyDiffNode extends DiffNode {
+ public MyDiffNode(IDiffContainer parent, int description, ITypedElement ancestor, ITypedElement left, ITypedElement right) {
+ super(parent, description, ancestor, left, right);
+ }
+ protected void fireChange() {
+ super.fireChange();
+ setDirty(true);
+ }
+ }
+
+ /**
+ * Creates an compare editor input for the given selection.
+ */
+ ResourceCompareInput(CompareConfiguration config, ISelection selection) {
+ super(config);
+ fSelection= selection;
+ }
+
+ /**
+ * Performs a two-way or three-way diff on the current selection.
+ */
+ public Object prepareInput(IProgressMonitor pm) {
+
+ try {
+ pm.beginTask("Comparing:", IProgressMonitor.UNKNOWN);
+
+ IResource[] selection= Utilities.getResources(fSelection);
+
+ if (selection.length < 2 || selection.length > 3) {
+ setMessage("Selection must contain two or three resources");
+ return null;
+ }
+
+ boolean threeWay= selection.length == 3;
+
+ IResource lr= selection[0];
+ IResource rr= selection[1];
+ if (threeWay) {
+ lr= selection[1];
+ rr= selection[2];
+ }
+
+ IStructureComparator ancestor= null;
+ IStructureComparator left= getStructure(lr);
+ IStructureComparator right= getStructure(rr);
+
+ if (right == null || left == null) {
+ setMessage("Selected resources must be of same type");
+ return null;
+ }
+
+ CompareConfiguration cc= (CompareConfiguration) getCompareConfiguration();
+
+ String leftLabel= lr.getName();
+ cc.setLeftLabel(leftLabel);
+ cc.setLeftImage(CompareUIPlugin.getImage(lr));
+
+ String rightLabel= rr.getName();
+ cc.setRightLabel(rightLabel);
+ cc.setRightImage(CompareUIPlugin.getImage(rr));
+
+ StringBuffer title= new StringBuffer();
+ title.append("Compare (");
+ if (threeWay) {
+ IResource ar= selection[0];
+ ancestor= getStructure(ar);
+ String ancestorLabel= ar.getName();
+ cc.setAncestorLabel(ancestorLabel);
+ cc.setAncestorImage(CompareUIPlugin.getImage(ar));
+ title.append(ancestorLabel);
+ title.append("-");
+ }
+ title.append(leftLabel);
+ title.append("-");
+ title.append(rightLabel);
+ title.append(")");
+ setTitle(title.toString());
+
+ Differencer d= new Differencer() {
+ protected Object visit(Object parent, int description, Object ancestor, Object left, Object right) {
+ return new MyDiffNode((IDiffContainer) parent, description, (ITypedElement)ancestor, (ITypedElement)left, (ITypedElement)right);
+ }
+ };
+
+ return d.findDifferences(threeWay, pm, null, ancestor, left, right);
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ /**
+ * Creates a <code>IStructureComparator</code> for the given input.
+ * Returns <code>null</code> if no <code>IStructureComparator</code>
+ * can be found for the <code>IResource</code>.
+ */
+ private IStructureComparator getStructure(IResource input) {
+
+ if (input instanceof IContainer)
+ return new ResourceNode(input);
+
+ if (input instanceof IFile) {
+ ResourceNode rn= new ResourceNode(input);
+ IFile file= (IFile) input;
+ String type= normalizeCase(file.getFileExtension());
+ if ("JAR".equals(type) || "ZIP".equals(type)) { // FIXME
+
+ return new ZipStructureCreator().getStructure(rn);
+
+// IStructureCreatorDescriptor scd= CompareUIPlugin.getStructureCreator(type);
+// if (scd != null) {
+// IStructureCreator sc= scd.createStructureCreator();
+// if (sc != null)
+// return sc.getStructure(rn);
+// }
+ }
+ return rn;
+ }
+ return null;
+ }
+
+ private static final boolean NORMALIZE_CASE= true;
+
+ private static String normalizeCase(String s) {
+ if (NORMALIZE_CASE && s != null)
+ return s.toUpperCase();
+ return s;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java
new file mode 100644
index 000000000..9c7f8391d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ShowPseudoConflicts.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.*;
+
+/**
+ * Toggles the <code>ICompareConfiguration.SHOW_PSEUDO_CONFLICTS</code> property of an
+ * <code>ICompareConfiguration</code>.
+ */
+public class ShowPseudoConflicts extends ChangePropertyAction {
+
+ public ShowPseudoConflicts(ResourceBundle bundle, CompareConfiguration cc) {
+ super(bundle, cc, "action.ShowPseudoConflicts.", CompareConfiguration.SHOW_PSEUDO_CONFLICTS);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java
new file mode 100644
index 000000000..14c835e73
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Splitter.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.custom.SashForm;
+
+public class Splitter extends SashForm {
+
+ private static final String VISIBILITY= "org.eclipse.compare.internal.visibility";
+
+
+ public Splitter(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ public void setVisible(Control child, boolean visible) {
+
+ boolean wasEmpty= isEmpty();
+
+ child.setVisible(visible);
+ child.setData(VISIBILITY, new Boolean(visible));
+
+ if (wasEmpty != isEmpty()) {
+ Composite parent= getParent();
+ if (parent instanceof Splitter) {
+ Splitter sp= (Splitter) parent;
+ sp.setVisible(this, visible);
+ sp.layout();
+ }
+ } else {
+ layout();
+ }
+ }
+
+ private boolean isEmpty() {
+ Control[] controls= getChildren();
+ for (int i= 0; i < controls.length; i++)
+ if (isVisible(controls[i]))
+ return false;
+ return true;
+ }
+
+ private boolean isVisible(Control child) {
+ if (child instanceof Sash)
+ return false;
+ Object data= child.getData(VISIBILITY);
+ if (data instanceof Boolean)
+ return ((Boolean)data).booleanValue();
+ return true;
+ }
+
+ public void setMaximizedControl(Control control) {
+ if (control == null || control == getMaximizedControl())
+ super.setMaximizedControl(null);
+ else
+ super.setMaximizedControl(control);
+
+ // walk up
+ Composite parent= getParent();
+ if (parent instanceof Splitter)
+ ((Splitter) parent).setMaximizedControl(this);
+ else
+ layout(true);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java
new file mode 100644
index 000000000..d316705aa
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextMergeViewerCreator.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+
+/**
+ * A factory object for the <code>TextMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class TextMergeViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new TextMergeViewer(parent, mp);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java
new file mode 100644
index 000000000..6a1ed00e5
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewer.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.IOException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.Document;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+
+
+public class TextViewer extends AbstractViewer {
+
+ private SourceViewer fSourceViewer;
+ private ICompareInput fInput;
+
+
+ TextViewer(Composite parent) {
+ fSourceViewer= new SourceViewer(parent, null, SWT.H_SCROLL + SWT.V_SCROLL);
+ fSourceViewer.setEditable(false);
+ }
+
+ public Control getControl() {
+ return fSourceViewer.getTextWidget();
+ }
+
+ public void setInput(Object input) {
+ if (input instanceof ICompareInput) {
+ fInput= (ICompareInput) input;
+ ITypedElement left= ((ICompareInput) fInput).getLeft();
+ fSourceViewer.setDocument(new Document(getString(left)));
+
+ } else if (input instanceof IStreamContentAccessor) {
+ fSourceViewer.setDocument(new Document(getString(input)));
+ }
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ private String getString(Object input) {
+
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ try {
+ return Utilities.readString(sca.getContents());
+ } catch (CoreException ex) {
+ }
+ }
+ return "";
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java
new file mode 100644
index 000000000..943a775e8
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TextViewerCreator.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.IViewerCreator;
+
+
+/**
+ * A factory object for the <code>TextMergeViewer</code>.
+ * This indirection is necessary because only objects with a default
+ * constructor can be created via an extension point
+ * (this precludes Viewers).
+ */
+public class TextViewerCreator implements IViewerCreator {
+
+ public Viewer createViewer(Composite parent, CompareConfiguration mp) {
+ return new TextViewer(parent);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java
new file mode 100644
index 000000000..62d96e185
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/TokenComparator.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.util.StringTokenizer;
+
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+import org.eclipse.compare.contentmergeviewer.ITokenComparator;
+
+/**
+ * Implements the <code>ITokenComparator</code> interface for words (or tokens) in a string.
+ * A <code>TokenComparator</code> is used as the input for the <code>RangeDifferencer</code>
+ * engine to perform a token oriented compare on strings.
+ */
+public class TokenComparator implements ITokenComparator {
+
+ private boolean fShouldEscape= true;
+ private String fText;
+ private int fCount;
+ private int[] fStarts;
+ private int[] fLengths;
+
+ /**
+ * Creates a <code>TokenComparator</code> for the given string.
+ *
+ * @param string the string that is split into token
+ */
+ public TokenComparator(String string) {
+
+ if (string != null)
+ fText= string;
+ else
+ fText= "";
+
+ fStarts= new int[fText.length()];
+ fLengths= new int[fText.length()];
+ fCount= 0;
+
+ StringTokenizer tokenizer= new StringTokenizer(fText, " \t\n\r", true);
+
+ for (int pos= 0; tokenizer.hasMoreElements();) {
+ fStarts[fCount]= pos;
+ String s= tokenizer.nextToken();
+ int l= 0;
+ if (s != null)
+ l= s.length();
+ pos += l;
+ fLengths[fCount]= l;
+ fCount++;
+ }
+ }
+
+ /**
+ * Creates a <code>TokenComparator</code> for the given string.
+ *
+ * @param string the string that is split into token
+ * @param shouldEscape
+ */
+ public TokenComparator(String s, boolean shouldEscape) {
+ this(s);
+ fShouldEscape= shouldEscape;
+ }
+
+ /**
+ * Returns the number of token in the string.
+ *
+ * @return number of token in the string
+ */
+ public int getRangeCount() {
+ return fCount;
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenStart
+ */
+ public int getTokenStart(int index) {
+ if (index < fCount)
+ return fStarts[index];
+ return fText.length();
+ }
+
+ /* (non Javadoc)
+ * see ITokenComparator.getTokenLength
+ */
+ public int getTokenLength(int index) {
+ if (index < fCount)
+ return fLengths[index];
+ return 0;
+ }
+
+ /**
+ * Returns the content of tokens in the specified range as a String.
+ * If the number of token is 0 the empty string ("") is returned.
+ *
+ * @param start index of first token
+ * @param length number of tokens
+ * @return the contents of the specified token range as a String
+ */
+ public String extract(int start, int length) {
+ if (start >= fStarts.length)
+ System.out.println("oops");
+ int startPos= fStarts[start];
+ int endPos= 0;
+ if (length > 0) {
+ int e= start + length-1;
+ if (e >= fStarts.length)
+ System.out.println("oops");
+ endPos= fStarts[e] + fLengths[e];
+ } else {
+ endPos= fStarts[start];
+ }
+ //int endPos= getTokenStart(start + length);
+ if (endPos >= fText.length())
+ return fText.substring(startPos);
+ return fText.substring(startPos, endPos);
+ }
+
+ /**
+ * Returns <code>true</code> if a token given by the first index
+ * matches a token specified by the other <code>IRangeComparator</code> and index.
+ *
+ * @param thisIndex the number of the token within this range comparator
+ * @param other the range comparator to compare this with
+ * @param otherIndex the number of the token within the other comparator
+ * @return <code>true</code> if the token are equal
+ */
+ public boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex) {
+ if (other != null && getClass() == other.getClass()) {
+ TokenComparator tc= (TokenComparator) other;
+ int thisLen= getTokenLength(thisIndex);
+ int otherLen= tc.getTokenLength(otherIndex);
+ if (thisLen == otherLen)
+ return fText.regionMatches(false, getTokenStart(thisIndex), tc.fText, tc.getTokenStart(otherIndex), thisLen);
+ }
+ return false;
+ }
+
+ /**
+ * Aborts the comparison if the number of tokens is too large.
+ *
+ * @return <code>true</code> to abort a token comparison
+ */
+ public boolean skipRangeComparison(int length, int max, IRangeComparator other) {
+
+ if (!fShouldEscape)
+ return false;
+
+ if (getRangeCount() < 50 || other.getRangeCount() < 50)
+ return false;
+
+ if (max < 100)
+ return false;
+
+ if (length < 100)
+ return false;
+
+ if (max > 800)
+ return true;
+
+ if (length < max / 4)
+ return false;
+
+ return true;
+ }
+
+// public static void main(String args[]) {
+// //String in= "private static boolean isWhitespace(char c) {";
+// //String in= "for (int j= 0; j < l-1; j++) {";
+// String in= "for do";
+// TokenComparator tc= new TokenComparator(in, false);
+//
+// System.out.println("n: " + tc.getRangeCount());
+// System.out.println(in);
+//
+// int p= 0;
+// for (int i= 0; i < tc.getRangeCount(); i++) {
+// int l= tc.getTokenLength(i);
+// System.out.print("<");
+//
+// for (int j= 0; j < l-1; j++)
+// System.out.print(" ");
+// }
+// System.out.println();
+//
+// //System.out.println("extract: <" + tc.extract(16, 1) + ">");
+// }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
new file mode 100644
index 000000000..b280afc1f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+import org.eclipse.swt.widgets.Widget;
+
+import org.eclipse.jface.util.*;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.CompareConfiguration;
+
+/**
+ * Convenience and utility methods.
+ */
+public class Utilities {
+
+ public static boolean getBoolean(CompareConfiguration cc, String key, boolean dflt) {
+ if (cc != null) {
+ Object value= cc.getProperty(key);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ }
+ return dflt;
+ }
+
+ /**
+ * Retrieves the value from a property change event as a boolean.
+ */
+ public static boolean getValue(PropertyChangeEvent event, boolean dflt) {
+ Object newValue= event.getNewValue();
+ if (newValue instanceof Boolean)
+ return ((Boolean)newValue).booleanValue();
+ return dflt;
+ }
+
+ public static void firePropertyChange(ListenerList ll, Object source, String property, Object old, Object newValue) {
+ if (ll != null) {
+ PropertyChangeEvent event= null;
+ Object[] listeners= ll.getListeners();
+ for (int i= 0; i < listeners.length; i++) {
+ IPropertyChangeListener l= (IPropertyChangeListener) listeners[i];
+ if (event == null)
+ event= new PropertyChangeEvent(source, property, old, newValue);
+ l.propertyChange(event);
+ }
+ }
+ }
+
+ public static boolean okToUse(Widget widget) {
+ return widget != null && !widget.isDisposed();
+ }
+
+ public static boolean isMotif() {
+ return false;
+ }
+
+ /**
+ * Returns the elements of the given selection.
+ * Returns an empty array if the selection is empty or if
+ * the given selection is not of type <code>IStructuredSelection</code>.
+ *
+ * @param selection the selection
+ * @return the selected elements
+ */
+ public static Object[] toArray(ISelection selection) {
+ if (!(selection instanceof IStructuredSelection)) {
+ return new Object[0];
+ }
+ IStructuredSelection ss= (IStructuredSelection) selection;
+ return ss.toArray();
+ }
+
+ /**
+ * Convenience method: extract all <code>IResources</code> from given selection.
+ * Never returns null.
+ */
+ public static IResource[] getResources(ISelection selection) {
+
+ List tmp= new ArrayList();
+
+ if (selection instanceof IStructuredSelection) {
+
+ Object[] s= ((IStructuredSelection)selection).toArray();
+
+ for (int i= 0; i < s.length; i++) {
+ Object o= s[i];
+ if (o instanceof IResource) {
+ tmp.add(o);
+ continue;
+ }
+ if (o instanceof IAdaptable) {
+ IAdaptable a= (IAdaptable) o;
+ Object adapter= a.getAdapter(IResource.class);
+ if (adapter instanceof IResource)
+ tmp.add(adapter);
+ continue;
+ }
+ }
+ }
+ IResource[] resourceSelection= new IResource[tmp.size()];
+ tmp.toArray(resourceSelection);
+ return resourceSelection;
+ }
+
+ public static byte[] readBytes(InputStream in) {
+ ByteArrayOutputStream bos= new ByteArrayOutputStream();
+ try {
+ while (true) {
+ int c= in.read();
+ if (c == -1)
+ break;
+ bos.write(c);
+ }
+
+ } catch (IOException ex) {
+ return null;
+
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException x) {
+ }
+ }
+ try {
+ bos.close();
+ } catch (IOException x) {
+ }
+ }
+
+ return bos.toByteArray();
+ }
+
+ /**
+ * Returns null if an error occurred.
+ */
+ public static String readString(InputStream is) {
+ if (is == null)
+ return null;
+ BufferedReader reader= null;
+ try {
+ StringBuffer buffer= new StringBuffer();
+ char[] part= new char[2048];
+ int read= 0;
+ reader= new BufferedReader(new InputStreamReader(is));
+
+ while ((read= reader.read(part)) != -1)
+ buffer.append(part, 0, read);
+
+ return buffer.toString();
+
+ } catch (IOException ex) {
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Initialize the given Action from a ResourceBundle.
+ */
+ public static void initAction(IAction a, ResourceBundle bundle, String prefix) {
+
+ String labelKey= "label";
+ String tooltipKey= "tooltip";
+ String imageKey= "image";
+ String descriptionKey= "description";
+
+ if (prefix != null && prefix.length() > 0) {
+ labelKey= prefix + labelKey;
+ tooltipKey= prefix + tooltipKey;
+ imageKey= prefix + imageKey;
+ descriptionKey= prefix + descriptionKey;
+ }
+
+ a.setText(getString(bundle, labelKey, labelKey));
+ a.setToolTipText(getString(bundle, tooltipKey, null));
+ a.setDescription(getString(bundle, descriptionKey, null));
+
+ String relPath= getString(bundle, imageKey, null);
+ if (relPath != null && relPath.trim().length() > 0) {
+ ImageDescriptor id= CompareUIPlugin.getImageDescriptor(relPath);
+ if (id != null)
+ a.setImageDescriptor(id);
+ }
+ }
+
+ public static String getString(ResourceBundle bundle, String key, String dfltValue) {
+
+ if (bundle != null) {
+ try {
+ return bundle.getString(key);
+ } catch (MissingResourceException x) {
+ }
+ }
+ return dfltValue;
+ }
+
+ public static int getInteger(ResourceBundle bundle, String key, int dfltValue) {
+
+ if (bundle != null) {
+ try {
+ String s= bundle.getString(key);
+ if (s != null)
+ return Integer.parseInt(s);
+ } catch (NumberFormatException x) {
+ } catch (MissingResourceException x) {
+ }
+ }
+ return dfltValue;
+ }
+
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java
new file mode 100644
index 000000000..d6ccd98aa
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ViewerDescriptor.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.internal;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.compare.*;
+
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Creates <code>Viewer</code>s from an <code>IConfigurationElement</code>.
+ */
+public class ViewerDescriptor implements IViewerDescriptor {
+
+ private final static String CLASS_ATTRIBUTE= "class";
+ private final static String EXTENSIONS_ATTRIBUTE= "extensions";
+
+ private IConfigurationElement fConfiguration;
+ private IViewerCreator fViewerCreator;
+
+ public ViewerDescriptor(IConfigurationElement config) {
+ fConfiguration= config;
+ }
+
+ public Viewer createViewer(Viewer currentViewer, Composite parent, CompareConfiguration mp) {
+ String className= fConfiguration.getAttribute(CLASS_ATTRIBUTE);
+ if (currentViewer != null && currentViewer.getClass().getName().equals(className)) {
+ return currentViewer;
+ }
+ if (fViewerCreator == null) {
+ try {
+ fViewerCreator= (IViewerCreator) fConfiguration.createExecutableExtension(CLASS_ATTRIBUTE);
+ } catch (CoreException e) {
+ }
+ }
+
+ if (fViewerCreator != null) {
+ Viewer viewer= fViewerCreator.createViewer(parent, mp);
+ //if (viewer != null && currentViewer != null && viewer.getClass() == currentViewer.getClass())
+ // return currentViewer;
+ return viewer;
+ }
+
+ return null;
+ }
+
+ public String getExtension() {
+ return fConfiguration.getAttribute(EXTENSIONS_ATTRIBUTE);
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java
new file mode 100644
index 000000000..d18bc84e5
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ZipStructureCreator.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000,2001
+ */
+package org.eclipse.compare.internal;
+
+import java.io.*;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.zip.*;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.util.Assert;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.structuremergeviewer.*;
+
+
+public class ZipStructureCreator implements IStructureCreator {
+
+ /**
+ * Common base class for ZipFolder and ZipFile
+ */
+ static abstract class ZipResource implements IStructureComparator, ITypedElement {
+
+ private String fName;
+
+ ZipResource(String name) {
+ fName= name;
+ }
+
+ public String getName() {
+ return fName;
+ }
+
+ public Image getImage() {
+ return CompareUIPlugin.getImage(getType());
+ }
+
+ /**
+ * Returns true if other is ITypedElement and names are equal.
+ * @see IComparator#equals
+ */
+ public boolean equals(Object other) {
+ if (other instanceof ITypedElement)
+ return fName.equals(((ITypedElement) other).getName());
+ return super.equals(other);
+ }
+
+ public int hashCode() {
+ return fName.hashCode();
+ }
+ }
+
+ static class ZipFolder extends ZipResource {
+
+ private HashMap fChildren= new HashMap(10);
+
+ ZipFolder(String name) {
+ super(name);
+ }
+
+ public String getType() {
+ return ITypedElement.FOLDER_TYPE;
+ }
+
+ public Object[] getChildren() {
+ Object[] children= new Object[fChildren.size()];
+ Iterator iter= fChildren.values().iterator();
+ for (int i= 0; iter.hasNext(); i++)
+ children[i]= iter.next();
+ return children;
+ }
+
+ ZipFile createContainer(String path) {
+ String entry= path;
+ int pos= path.indexOf('/');
+ if (pos < 0)
+ pos= path.indexOf('\\');
+ if (pos >= 0) {
+ entry= path.substring(0, pos);
+ path= path.substring(pos + 1);
+ } else if (entry.length() > 0) {
+ ZipFile ze= new ZipFile(entry);
+ fChildren.put(entry, ze);
+ return ze;
+ } else
+ return null;
+
+ ZipFolder folder= null;
+ if (fChildren != null) {
+ Object o= fChildren.get(entry);
+ if (o instanceof ZipFolder)
+ folder= (ZipFolder) o;
+ }
+
+ if (folder == null) {
+ folder= new ZipFolder(entry);
+ fChildren.put(entry, folder);
+ }
+
+ return folder.createContainer(path);
+ }
+ }
+
+ static class ZipFile extends ZipResource implements IStreamContentAccessor {
+
+ private byte[] fContents;
+
+ ZipFile(String name) {
+ super(name);
+ }
+
+ public String getType() {
+ String s= this.getName();
+ int pos= s.lastIndexOf('.');
+ if (pos >= 0)
+ return s.substring(pos + 1);
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ public Object[] getChildren() {
+ return null;
+ }
+
+ public InputStream getContents() {
+ if (fContents == null)
+ fContents= new byte[0];
+ return new ByteArrayInputStream(fContents);
+ }
+
+ byte[] getBytes() {
+ return fContents;
+ }
+
+ void setBytes(byte[] buffer) {
+ fContents= buffer;
+ }
+ }
+
+ private String fTitle;
+
+ public ZipStructureCreator() {
+ this("Zip Archive Compare");
+ }
+
+ public ZipStructureCreator(String title) {
+ fTitle= title;
+ }
+
+ public String getName() {
+ return fTitle;
+ }
+
+ public IStructureComparator getStructure(Object input) {
+
+ InputStream is= null;
+
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ try {
+ is= sca.getContents();
+ } catch (CoreException ex) {
+ }
+ }
+
+ if (is == null)
+ return null;
+
+ ZipInputStream zip= new ZipInputStream(is);
+ ZipFolder root= new ZipFolder("");
+ try {
+ for (;;) {
+ ZipEntry entry= zip.getNextEntry();
+ if (entry == null)
+ break;
+ //System.out.println(entry.getName() + ": " + entry.getSize() + " " + entry.getCompressedSize());
+
+ ZipFile ze= root.createContainer(entry.getName());
+ if (ze != null) {
+ int length= (int) entry.getSize();
+ if (length >= 0) {
+ byte[] buffer= new byte[length];
+ int offset= 0;
+
+ do {
+ int n= zip.read(buffer, offset, length);
+ offset += n;
+ length -= n;
+ } while (length > 0);
+
+ ze.setBytes(buffer);
+ }
+ }
+ zip.closeEntry();
+ }
+ } catch (IOException ex) {
+ return null;
+ } finally {
+ try {
+ zip.close();
+ } catch (IOException ex) {
+ }
+ }
+
+ if (root.fChildren.size() == 1) {
+ Iterator iter= root.fChildren.values().iterator();
+ return (IStructureComparator) iter.next();
+ }
+ return root;
+ }
+
+ public String getContents(Object o, boolean ignoreWhitespace) {
+ if (o instanceof ZipFile) {
+ byte[] bytes= ((ZipFile)o).getBytes();
+ if (bytes != null)
+ return new String(bytes);
+ return "";
+ }
+ return null;
+ }
+
+ /**
+ * Returns <code>false</code> since we cannot update a zip archive.
+ */
+ public boolean canSave() {
+ return false;
+ }
+
+ /**
+ * Throws <code>AssertionFailedException</code> since we cannot update a zip archive.
+ */
+ public void save(IStructureComparator structure, Object input) {
+ Assert.isTrue(false, "cannot update zip archive");
+ }
+
+ public IStructureComparator locate(Object path, Object source) {
+ return null;
+ }
+
+ public boolean canRewriteTree() {
+ return false;
+ }
+
+ public void rewriteTree(Differencer diff, IDiffContainer root) {
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/package.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/package.html
new file mode 100644
index 000000000..fb0ad6215
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/package.html
@@ -0,0 +1,50 @@
+<!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 performing structural and textual
+compare operations on arbitrary data and displaying the results.
+<h2>
+Package Specification</h2>
+The class <tt>CompareUI</tt> defines the entry point to initiate a configurable
+compare operation on arbitrary resources. The result of the compare is
+opened into a compare editor where the details can be browsed and edited
+in dynamically selected structure and content viewers.
+<p>A compare operation must be implemented as a subclass of <tt>CompareEditorInput</tt>.
+A <tt>CompareEditorInput</tt> runs a (potentially lengthy) compare operation
+under progress monitor control, creates a UI for drilling-down into the
+compare results, tracks the dirty state of the result in case of merge,
+and saves any changes that occured during a merge.
+<p>An instance of <tt>CompareConfiguration</tt> configures various UI aspects
+of compare/merge viewers like title labels and images, or whether a side
+of a merge viewer is editable. It is passed to the <tt>CompareEditorInput</tt>
+on creation.
+<p>When implementing a compare operation clients have to provide a tree
+of <tt>IStructureComparator</tt> (see package org.eclipse.compare.structuremergeviewer)
+and <tt>IStreamContentAccessor</tt> that can be passed as the input to
+the differencing engine (org.eclipse.compare.structuremergeviewer.Differencer).
+A <tt>ResourceNode</tt> is a convenience class that implements both interfaces
+for Eclipse workbench resources (org.eclipse.core.resources.IResource).
+It can be used without modification as the input to the differencing engine.
+<p>The <tt>EditionSelectionDialog</tt> is a simple selection dialog where
+one input element can be compared against a list of historic variants (<i>editions</i>)
+of the same input element. The dialog can be used to implement functions
+like <i>"Replace with Version"</i> or
+<br><i>"Replace with Edition"</i> on workbench resources.
+<p>In addition it is possible to specify a subsection of the input element
+(e.g. a method in a Java source file) by means of a <i>path</i>. In this
+case the dialog compares only the subsection (as specified by the path)
+with the corresponding subsection in the list of editions. This functionality
+can be used to implement <i>"Replace with Method Edition"</i> for the Java
+language.
+<p>The <tt>EditionSelectionDialog</tt> requires that the editions implement
+the <tt>IStreamContentAccessor</tt> and <tt>IModificationDate</tt> interfaces.
+The <tt>HistoryItem</tt> is a convenience class that implements these interfaces
+for <tt>IFileState</tt> objects.
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java
new file mode 100644
index 000000000..7ea31ef38
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/DifferencesIterator.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A custom iterator to iterate over a List of <code>RangeDifferences</code>.
+ * It is used internally by the <code>RangeDifferencer</code>.
+ */
+/* package */ class DifferencesIterator {
+
+ List fRange;
+ int fIndex;
+ RangeDifference[] fArray;
+ RangeDifference fDifference;
+
+ /**
+ * Creates a differences iterator on an array of <code>RangeDifference</code>s.
+ */
+ DifferencesIterator(RangeDifference[] differenceRanges) {
+
+ fArray= differenceRanges;
+ fIndex= 0;
+ fRange= new ArrayList();
+ if (fIndex < fArray.length)
+ fDifference= fArray[fIndex++];
+ else
+ fDifference= null;
+ }
+
+ /**
+ * Returns the number of RangeDifferences
+ */
+ int getCount() {
+ return fRange.size();
+ }
+
+ /**
+ * Appends the edit to its list and moves to the next <code>RangeDifference</code>.
+ */
+ void next() {
+ fRange.add(fDifference);
+ if (fDifference != null) {
+ if (fIndex < fArray.length)
+ fDifference= fArray[fIndex++];
+ else
+ fDifference= null;
+ }
+ }
+
+ /**
+ * Difference iterators are used in pairs.
+ * This method returns the other iterator.
+ */
+ DifferencesIterator other(DifferencesIterator right, DifferencesIterator left) {
+ if (this == right)
+ return left;
+ return right;
+ }
+
+ /**
+ * Removes all <code>RangeDifference</code>s
+ */
+ void removeAll() {
+ fRange.clear();
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java
new file mode 100644
index 000000000..2a7ca6643
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/IRangeComparator.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+/**
+ * For breaking an object to compare into a sequence of comparable entities.
+ * <p>
+ * It is used by <code>RangeDifferencer</code> to find longest sequences of
+ * matching and non-matching ranges.
+ * <p>
+ * For example, to compare two text documents and find longest common sequences
+ * of matching and non-matching lines, the implementation must break the document
+ * into lines. <code>getRangeCount</code> would return the number of lines in the
+ * document, and <code>rangesEqual</code> would compare a specified line given
+ * with one in another <code>IRangeComparator</code>.
+ * </p>
+ * <p>
+ * Clients should implement this interface; there is no standard implementation.
+ * </p>
+ */
+public interface IRangeComparator {
+
+ /**
+ * Returns the number of comparable entities.
+ *
+ * @return the number of comparable entities
+ */
+ int getRangeCount();
+
+ /**
+ * Returns whether the comparable entity given by the first index
+ * matches an entity specified by the other <code>IRangeComparator</code> and index.
+ *
+ * @param thisIndex the index of the comparable entity within this <code>IRangeComparator</code>
+ * @param other the IRangeComparator to compare this with
+ * @param otherIndex the index of the comparable entity within the other <code>IRangeComparator</code>
+ * @return <code>true</code> if the comparable entities are equal
+ */
+ boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex);
+
+ /**
+ * Returns whether a comparison should be skipped because it would be too costly (or lengthy).
+ *
+ * @param length a number on which to base the decision whether to return
+ * <code>true</code> or <code>false</code>
+ * @param maxLength another number on which to base the decision whether to return
+ * <code>true</code> or <code>false</code>
+ * @param other the other <code>IRangeComparator</code> to compare with
+ * @return <code>true</code> to avoid a too lengthy range comparison
+ */
+ boolean skipRangeComparison(int length, int maxLength, IRangeComparator other);
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java
new file mode 100644
index 000000000..f3ab74f91
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/LinkedRangeDifference.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+/* package */ class LinkedRangeDifference extends RangeDifference {
+
+ static final int INSERT= 0;
+ static final int DELETE= 1;
+ static final int CHANGE= 2;
+ static final int ERROR= 3;
+
+ LinkedRangeDifference fNext;
+
+ /**
+ * Creates a LinkedRangeDifference an initializes it to the error state
+ */
+ LinkedRangeDifference() {
+ super(ERROR);
+ fNext= null;
+ }
+
+ /**
+ * Constructs and links a LinkeRangeDifference to another LinkedRangeDifference
+ */
+ LinkedRangeDifference(LinkedRangeDifference next, int operation) {
+ super(operation);
+ fNext= next;
+ }
+
+ /**
+ * Follows the next link
+ */
+ LinkedRangeDifference getNext() {
+ return fNext;
+ }
+
+ boolean isDelete() {
+ return kind() == DELETE;
+ }
+
+ boolean isInsert() {
+ return kind() == INSERT;
+ }
+
+ /**
+ * Sets the next link of this LinkedRangeDifference
+ */
+ void setNext(LinkedRangeDifference next) {
+ fNext= next;
+ }
+}
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java
new file mode 100644
index 000000000..608ebc9e4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifference.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+import org.eclipse.jface.*;
+import org.eclipse.jface.util.Assert;
+
+/**
+ * Description of a change between two or three ranges of comparable entities.
+ * <p>
+ * <code>RangeDifference</code> objects are the elements of a compare result returned from
+ * the <code>RangeDifferencer</code> <code>find* </code> methods.
+ * Clients use these objects as they are returned from the differencer.
+ * This class is not intended to be instantiated or subclassed.
+ * <p>
+ * Note: A range in the <code>RangeDifference</code> object is given as a start index
+ * and length in terms of comparable entities. However, these entity indices and counts
+ * are not necessarily character positions. For example, if an entity represents a line
+ * in a document, the start index would be a line number and the count would be in lines.
+ * </p>
+ *
+ * @see RangeDifferencer
+ */
+public class RangeDifference {
+
+ /** Two-way change constant indicating no change. */
+ public final static int NOCHANGE= 0;
+ /** Two-way change constant indicating two-way change (same as <code>RIGHT</code>) */
+ public final static int CHANGE= 2;
+
+ /** Three-way change constant indicating a change in both right and left. */
+ public final static int CONFLICT= 1;
+ /** Three-way change constant indicating a change in right. */
+ public final static int RIGHT= 2;
+ /** Three-way change constant indicating a change in left. */
+ public final static int LEFT= 3;
+ /**
+ * Three-way change constant indicating the same change in both right and left,
+ * that is only the ancestor is different.
+ */
+ public final static int ANCESTOR= 4;
+
+ /** Constant indicating an unknown change kind. */
+ public final static int ERROR= 5;
+
+ /** the kind of change: NOCHANGE, CHANGE, LEFT, RIGHT, ANCESTOR, CONFLICT, ERROR */
+ int fKind;
+
+ int fLeftStart;
+ int fLeftLength;
+ int fRightStart;
+ int fRightLength;
+ int lAncestorStart;
+ int lAncestorLength;
+
+ /**
+ * Creates a new range difference with the given change kind.
+ *
+ * @param changeKind the kind of change
+ */
+ /* package */ RangeDifference(int changeKind) {
+ fKind= changeKind;
+ }
+
+ /**
+ * Creates a new <code>RangeDifference</code> with the given change kind
+ * and left and right ranges.
+ *
+ * @param changeKind the kind of change
+ * @param rightStart start index of entity on right side
+ * @param rightLength number of entities on right side
+ * @param leftStart start index of entity on left side
+ * @param leftLength number of entities on left side
+ */
+ /* package */ RangeDifference(int kind, int rightStart, int rightLength, int leftStart, int leftLength) {
+ fKind= kind;
+ fRightStart= rightStart;
+ fRightLength= rightLength;
+ fLeftStart= leftStart;
+ fLeftLength= leftLength;
+ }
+
+ /**
+ * Creates a new <code>RangeDifference</code> with the given change kind
+ * and left, right, and ancestor ranges.
+ *
+ * @param changeKind the kind of change
+ * @param rightStart start index of entity on right side
+ * @param rightLength number of entities on right side
+ * @param leftStart start index of entity on left side
+ * @param leftLength number of entities on left side
+ * @param ancestorStart start index of entity on ancestor side
+ * @param ancestorLength number of entities on ancestor side
+ */
+ /* package */ RangeDifference(int kind, int rightStart, int rightLength, int leftStart, int leftLength,
+ int ancestorStart, int ancestorLength) {
+ this(kind, rightStart, rightLength, leftStart, leftLength);
+ lAncestorStart= ancestorStart;
+ lAncestorLength= ancestorLength;
+ }
+
+ /**
+ * Returns the kind of difference.
+ *
+ * @return the kind of difference, one of
+ * <code>NOCHANGE</code>, <code>CHANGE</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>ANCESTOR</code>, <code>CONFLICT</code>, <code>ERROR</code>
+ */
+ public int kind() {
+ return fKind;
+ }
+
+ /**
+ * Returns the start index of the entity range on the ancestor side.
+ *
+ * @return the start index of the entity range on the ancestor side
+ */
+ public int ancestorStart() {
+ return lAncestorStart;
+ }
+
+ /**
+ * Returns the number of entities on the ancestor side.
+ *
+ * @return the number of entities on the ancestor side
+ */
+ public int ancestorLength() {
+ return lAncestorLength;
+ }
+
+ /**
+ * Returns the end index of the entity range on the ancestor side.
+ *
+ * @return the end index of the entity range on the ancestor side
+ */
+ public int ancestorEnd() {
+ return lAncestorStart + lAncestorLength;
+ }
+
+ /**
+ * Returns the start index of the entity range on the right side.
+ *
+ * @return the start index of the entity range on the right side
+ */
+ public int rightStart() {
+ return fRightStart;
+ }
+
+ /**
+ * Returns the number of entities on the right side.
+ *
+ * @return the number of entities on the right side
+ */
+ public int rightLength() {
+ return fRightLength;
+ }
+
+ /**
+ * Returns the end index of the entity range on the right side.
+ *
+ * @return the end index of the entity range on the right side
+ */
+ public int rightEnd() {
+ return fRightStart + fRightLength;
+ }
+
+ /**
+ * Returns the start index of the entity range on the left side.
+ *
+ * @return the start index of the entity range on the left side
+ */
+ public int leftStart() {
+ return fLeftStart;
+ }
+
+ /**
+ * Returns the number of entities on the left side.
+ *
+ * @return the number of entities on the left side
+ */
+ public int leftLength() {
+ return fLeftLength;
+ }
+
+ /**
+ * Returns the end index of the entity range on the left side.
+ *
+ * @return the end index of the entity range on the left side
+ */
+ public int leftEnd() {
+ return fLeftStart + fLeftLength;
+ }
+
+ /**
+ * Returns the maximum number of entities in the left, right, and ancestor sides of this range.
+ *
+ * @return the maximum number of entities in the left, right, and ancestor sides of this range
+ */
+ public int maxLength() {
+ return Math.max(fRightLength, Math.max(fLeftLength, lAncestorLength));
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java
new file mode 100644
index 000000000..9056493f6
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/RangeDifferencer.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.rangedifferencer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.util.Assert;
+
+/**
+ * For finding the differences between two or three <code>IRangeComparator</code>s.
+ * <p>
+ * To use the differencer, clients provide an <code>IRangeComparator</code>
+ * that breaks their input data into a sequence of comparable entities. The differencer
+ * returns the differences among these sequences as an array of <code>RangeDifference</code> objects
+ * (<code>findDifferences</code> methods).
+ * Every <code>RangeDifference</code> represents a single kind of difference
+ * and the corresponding ranges of the underlying comparable entities in the
+ * left, right, and optionally ancestor sides.
+ * <p>
+ * Alternatively, the <code>findRanges</code> methods not only return objects for
+ * the differing ranges but for non-differing ranges too.
+ * <p>
+ * The algorithm used is an objectified version of one described in:
+ * <it>A File Comparison Program,</it> by Webb Miller and Eugene W. Myers,
+ * Software Practice and Experience, Vol. 15, Nov. 1985.
+ *
+ * @see IRangeComparator
+ * @see RangeDifference
+ */
+public final class RangeDifferencer {
+
+ private static final RangeDifference[] EMPTY_RESULT= new RangeDifference[0];
+
+ /* (non Javadoc)
+ * Non instantiatiable!
+ */
+ private RangeDifferencer() {
+ }
+
+ /**
+ * Finds the differences between two <code>IRangeComparator</code>s.
+ * The differences are returned as a list of <code>RangeDifference</code>s.
+ * If no differences are detected an empty list is returned.
+ *
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences, or an empty array if no differences were found
+ */
+ public static RangeDifference[] findDifferences(IRangeComparator left, IRangeComparator right) {
+
+ // assert that both IRangeComparators are of the same class
+ Assert.isTrue(right.getClass().equals(left.getClass()));
+
+ int rightSize= right.getRangeCount();
+ int leftSize= left.getRangeCount();
+ //
+ // Differences matrix:
+ // only the last d of each diagonal is stored, i.e., lastDiagonal[k] = row of d
+ //
+ int diagLen= 2 * Math.max(rightSize, leftSize); // bound on the size of edit script
+ int maxDiagonal= diagLen;
+ int lastDiagonal[]= new int[diagLen + 1]; // the row containing the last d
+ // on diagonal k (lastDiagonal[k] = row)
+ int origin= diagLen / 2; // origin of diagonal 0
+
+ // script corresponding to d[k]
+ LinkedRangeDifference script[]= new LinkedRangeDifference[diagLen + 1];
+ int row, col;
+
+ // find common prefix
+ for (row= 0; row < rightSize && row < leftSize && rangesEqual(right, row, left, row) == true; ++row);
+
+ lastDiagonal[origin]= row;
+ script[origin]= null;
+ int lower= (row == rightSize) ? origin + 1 : origin - 1;
+ int upper= (row == leftSize) ? origin - 1 : origin + 1;
+
+ if (lower > upper)
+ return EMPTY_RESULT;
+
+ // for each value of the edit distance
+ for (int d= 1; d <= maxDiagonal; ++d) { // d is the current edit distance
+
+ if (right.skipRangeComparison(d, maxDiagonal, left))
+ return EMPTY_RESULT; // should be something we already found
+
+ // for each relevant diagonal (-d, -d+2 ..., d-2, d)
+ for (int k= lower; k <= upper; k += 2) { // k is the current diagonal
+ LinkedRangeDifference edit;
+
+ if (k == origin - d || k != origin + d && lastDiagonal[k + 1] >= lastDiagonal[k - 1]) {
+ //
+ // move down
+ //
+ row= lastDiagonal[k + 1] + 1;
+ edit= new LinkedRangeDifference(script[k + 1], LinkedRangeDifference.DELETE);
+ } else {
+ //
+ // move right
+ //
+ row= lastDiagonal[k - 1];
+ edit= new LinkedRangeDifference(script[k - 1], LinkedRangeDifference.INSERT);
+ }
+ col= row + k - origin;
+ edit.fRightStart= row;
+ edit.fLeftStart= col;
+ Assert.isTrue(k >= 0 && k <= maxDiagonal, "Indices out of range");
+ script[k]= edit;
+
+ // slide down the diagonal as far as possible
+ while (row < rightSize && col < leftSize && rangesEqual(right, row, left, col) == true) {
+ ++row;
+ ++col;
+ }
+
+ Assert.isTrue(k >= 0 && k <= maxDiagonal, "unreasonable value for diagonal index");
+ lastDiagonal[k]= row;
+
+ if (row == rightSize && col == leftSize) {
+ //showScript(script[k], right, left);
+ return createDifferencesRanges(script[k]);
+ }
+ if (row == rightSize)
+ lower= k + 2;
+ if (col == leftSize)
+ upper= k - 2;
+ }
+ --lower;
+ ++upper;
+ }
+ // too many differences
+ Assert.isTrue(false);
+ return null;
+ }
+
+ /**
+ * Finds the differences among three <code>IRangeComparator</code>s.
+ * The differences are returned as a list of <code>RangeDifference</code>s.
+ * If no differences are detected an empty list is returned.
+ * If the ancestor range comparator is <code>null</code>, a two-way
+ * comparison is performed.
+ *
+ * @param ancestor the ancestor range comparator or <code>null</code>
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences, or an empty array if no differences were found
+ */
+ public static RangeDifference[] findDifferences(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) {
+
+ if (ancestor == null)
+ return findDifferences(left, right);
+
+ RangeDifference[] leftAncestorScript= null;
+ RangeDifference[] rightAncestorScript= findDifferences(ancestor, right);
+ if (rightAncestorScript != null)
+ leftAncestorScript= findDifferences(ancestor, left);
+ if (rightAncestorScript == null || leftAncestorScript == null)
+ return null;
+
+ DifferencesIterator myIter= new DifferencesIterator(rightAncestorScript);
+ DifferencesIterator yourIter= new DifferencesIterator(leftAncestorScript);
+
+ List diff3= new ArrayList();
+ diff3.add(new RangeDifference(RangeDifference.ERROR)); // add a sentinel
+
+ int changeRangeStart= 0;
+ int changeRangeEnd= 0;
+ //
+ // Combine the two two-way edit scripts into one
+ //
+ while (myIter.fDifference != null || yourIter.fDifference != null) {
+
+ DifferencesIterator startThread;
+ myIter.removeAll();
+ yourIter.removeAll();
+ //
+ // take the next diff that is closer to the start
+ //
+ if (myIter.fDifference == null)
+ startThread= yourIter;
+ else if (yourIter.fDifference == null)
+ startThread= myIter;
+ else { // not at end of both scripts take the lowest range
+ if (myIter.fDifference.fLeftStart <= yourIter.fDifference.fLeftStart) // 2 -> common (Ancestor) change range
+ startThread= myIter;
+ else
+ startThread= yourIter;
+ }
+ changeRangeStart= startThread.fDifference.fLeftStart;
+ changeRangeEnd= startThread.fDifference.leftEnd();
+
+ startThread.next();
+ //
+ // check for overlapping changes with other thread
+ // merge overlapping changes with this range
+ //
+ DifferencesIterator other= startThread.other(myIter, yourIter);
+ while (other.fDifference != null && other.fDifference.fLeftStart <= changeRangeEnd) {
+ int newMax= other.fDifference.leftEnd();
+ other.next();
+ if (newMax >= changeRangeEnd) {
+ changeRangeEnd= newMax;
+ other= other.other(myIter, yourIter);
+ }
+ }
+ diff3.add(createRangeDifference3(myIter, yourIter, diff3, right, left, changeRangeStart, changeRangeEnd));
+ }
+
+ // remove sentinel
+ diff3.remove(0);
+ return (RangeDifference[]) diff3.toArray(EMPTY_RESULT);
+ }
+
+ /**
+ * Finds the differences among two <code>IRangeComparator</code>s.
+ * In contrast to <code>findDifferences</code>, the result
+ * contains <code>RangeDifference</code> elements for non-differing ranges too.
+ *
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences
+ */
+ public static RangeDifference[] findRanges(IRangeComparator left, IRangeComparator right) {
+ RangeDifference[] in= findDifferences(left, right);
+ List out= new ArrayList();
+
+ RangeDifference rd;
+
+ int mstart= 0;
+ int ystart= 0;
+
+ for (int i= 0; i < in.length; i++) {
+ RangeDifference es= in[i];
+
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, es.rightStart() - mstart, ystart, es.leftStart() - ystart);
+ if (rd.maxLength() != 0)
+ out.add(rd);
+
+ out.add(es);
+
+ mstart= es.rightEnd();
+ ystart= es.leftEnd();
+ }
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, right.getRangeCount() - mstart, ystart, left.getRangeCount() - ystart);
+ if (rd.maxLength() > 0)
+ out.add(rd);
+
+ return (RangeDifference[]) out.toArray(EMPTY_RESULT);
+ }
+
+ /**
+ * Finds the differences among three <code>IRangeComparator</code>s.
+ * In contrast to <code>findDifferences</code>, the result
+ * contains <code>RangeDifference</code> elements for non-differing ranges too.
+ * If the ancestor range comparator is <code>null</code>, a two-way
+ * comparison is performed.
+ *
+ * @param ancestor the ancestor range comparator or <code>null</code>
+ * @param left the left range comparator
+ * @param right the right range comparator
+ * @return an array of range differences
+ */
+ public static RangeDifference[] findRanges(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) {
+
+ if (ancestor == null)
+ return findRanges(left, right);
+
+ RangeDifference[] in= findDifferences(ancestor, left, right);
+ List out= new ArrayList();
+
+ RangeDifference rd;
+
+ int mstart= 0;
+ int ystart= 0;
+ int astart= 0;
+
+ for (int i= 0; i < in.length; i++) {
+ RangeDifference es= (RangeDifference) in[i];
+
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, es.rightStart() - mstart, ystart, es.leftStart() - ystart, astart, es.ancestorStart() - astart);
+ if (rd.maxLength() > 0)
+ out.add(rd);
+
+ out.add(es);
+
+ mstart= es.rightEnd();
+ ystart= es.leftEnd();
+ astart= es.ancestorEnd();
+ }
+ rd= new RangeDifference(RangeDifference.NOCHANGE, mstart, right.getRangeCount() - mstart, ystart, left.getRangeCount() - ystart, astart, ancestor.getRangeCount() - astart);
+ if (rd.maxLength() > 0)
+ out.add(rd);
+
+ return (RangeDifference[]) out.toArray(EMPTY_RESULT);
+ }
+
+ //---- private methods
+
+ /**
+ * Creates a Vector of DifferencesRanges out of the LinkedRangeDifference.
+ * It coalesces adjacent changes.
+ * In addition, indices are changed such that the ranges are 1) open, i.e,
+ * the end of the range is not included, and 2) are zero based.
+ */
+ private static RangeDifference[] createDifferencesRanges(LinkedRangeDifference start) {
+
+ LinkedRangeDifference ep= reverseDifferences(start);
+ ArrayList result= new ArrayList();
+ RangeDifference es= null;
+
+ while (ep != null) {
+ es= new RangeDifference(RangeDifference.CHANGE);
+
+ if (ep.isInsert()) {
+ es.fRightStart= ep.fRightStart + 1;
+ es.fLeftStart= ep.fLeftStart;
+ RangeDifference b= ep;
+ do {
+ ep= ep.getNext();
+ es.fLeftLength++;
+ } while (ep != null && ep.isInsert() && ep.fRightStart == b.fRightStart);
+ } else {
+ es.fRightStart= ep.fRightStart;
+ es.fLeftStart= ep.fLeftStart;
+
+ RangeDifference a= ep;
+ //
+ // deleted lines
+ //
+ do {
+ a= ep;
+ ep= ep.getNext();
+ es.fRightLength++;
+ } while (ep != null && ep.isDelete() && ep.fRightStart == a.fRightStart + 1);
+
+ boolean change= (ep != null && ep.isInsert() && ep.fRightStart == a.fRightStart);
+
+ if (change) {
+ RangeDifference b= ep;
+ //
+ // replacement lines
+ //
+ do {
+ ep= ep.getNext();
+ es.fLeftLength++;
+ } while (ep != null && ep.isInsert() && ep.fRightStart == b.fRightStart);
+ } else {
+ es.fLeftLength= 0;
+ }
+ es.fLeftStart++; // meaning of range changes from "insert after", to "replace with"
+
+ }
+ //
+ // the script commands are 1 based, subtract one to make them zero based
+ //
+ es.fRightStart--;
+ es.fLeftStart--;
+ result.add(es);
+ }
+ return (RangeDifference[]) result.toArray(EMPTY_RESULT);
+ }
+
+ /**
+ * Creates a <code>RangeDifference3</code> given the
+ * state of two DifferenceIterators.
+ */
+ private static RangeDifference createRangeDifference3(
+ DifferencesIterator myIter,
+ DifferencesIterator yourIter,
+ List diff3,
+ IRangeComparator right,
+ IRangeComparator left,
+ int changeRangeStart,
+ int changeRangeEnd) {
+
+ int rightStart, rightEnd;
+ int leftStart, leftEnd;
+ int kind= RangeDifference.ERROR;
+ RangeDifference last= (RangeDifference) diff3.get(diff3.size() - 1);
+
+ Assert.isTrue((myIter.getCount() != 0 || yourIter.getCount() != 0), "at least one range array must be non-empty");
+ //
+ // find corresponding lines to fChangeRangeStart/End in right and left
+ //
+ if (myIter.getCount() == 0) { // only left changed
+ rightStart= changeRangeStart - last.ancestorEnd() + last.rightEnd();
+ rightEnd= changeRangeEnd - last.ancestorEnd() + last.rightEnd();
+ kind= RangeDifference.LEFT;
+ } else {
+ RangeDifference f= (RangeDifference) myIter.fRange.get(0);
+ RangeDifference l= (RangeDifference) myIter.fRange.get(myIter.fRange.size() - 1);
+ rightStart= changeRangeStart - f.fLeftStart + f.fRightStart;
+ rightEnd= changeRangeEnd - l.leftEnd() + l.rightEnd();
+ }
+
+ if (yourIter.getCount() == 0) { // only right changed
+ leftStart= changeRangeStart - last.ancestorEnd() + last.leftEnd();
+ leftEnd= changeRangeEnd - last.ancestorEnd() + last.leftEnd();
+ kind= RangeDifference.RIGHT;
+ } else {
+ RangeDifference f= (RangeDifference) yourIter.fRange.get(0);
+ RangeDifference l= (RangeDifference) yourIter.fRange.get(yourIter.fRange.size() - 1);
+ leftStart= changeRangeStart - f.fLeftStart + f.fRightStart;
+ leftEnd= changeRangeEnd - l.leftEnd() + l.rightEnd();
+ }
+
+ if (kind == RangeDifference.ERROR) { // overlapping change (conflict) -> compare the changed ranges
+ if (rangeSpansEqual(right, rightStart, rightEnd - rightStart, left, leftStart, leftEnd - leftStart))
+ kind= RangeDifference.ANCESTOR;
+ else
+ kind= RangeDifference.CONFLICT;
+ }
+ return new RangeDifference(kind, rightStart, rightEnd - rightStart, leftStart, leftEnd - leftStart, changeRangeStart, changeRangeEnd - changeRangeStart);
+ }
+
+ /**
+ * Tests if two ranges are equal
+ */
+ private static boolean rangesEqual(IRangeComparator a, int ai, IRangeComparator b, int bi) {
+ return a.rangesEqual(ai, b, bi);
+ }
+
+ /**
+ * Tests whether <code>right</code> and <code>left</left> changed in the same way
+ */
+ private static boolean rangeSpansEqual(IRangeComparator right, int rightStart, int rightLen, IRangeComparator left, int leftStart, int leftLen) {
+ if (rightLen == leftLen) {
+ int i= 0;
+ for (i= 0; i < rightLen; i++) {
+ if (!rangesEqual(right, rightStart + i, left, leftStart + i))
+ break;
+ }
+ if (i == rightLen)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Reverses the range differences
+ */
+ private static LinkedRangeDifference reverseDifferences(LinkedRangeDifference start) {
+ LinkedRangeDifference ep, behind, ahead;
+
+ ahead= start;
+ ep= null;
+ while (ahead != null) {
+ behind= ep;
+ ep= ahead;
+ ahead= ahead.getNext();
+ ep.setNext(behind);
+ }
+ return ep;
+ }
+}
+
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html
new file mode 100644
index 000000000..2e4ffaf2b
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/rangedifferencer/package.html
@@ -0,0 +1,37 @@
+<!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 the differences between
+two or three sequences of comparable entities.
+<h2>
+Package Specification</h2>
+The class <tt>Differencer</tt> finds longest sequences of matching and
+non-matching comparable entities.
+<p>Clients must supply the input to the differencer as an implementation
+of the <tt>IRangeComparator</tt> interface.
+<br>An <tt>IRangeComparator</tt> breaks the input data into a sequence
+of entities and provides a method for comparing
+<br>one entity with the entity in another <tt>IRangeComparator</tt>.
+<br>For example, to compare two text documents and find longest common
+sequences
+<br>of matching and non-matching lines, the implementation of <tt>IRangeComparator
+</tt>must break the document
+<br>into lines and provide a method for testing whether two lines are considered
+equal.
+<p>The differencer returns the differences among these sequences as an
+array of <tt>RangeDifference</tt> objects.
+<br>Every single <tt>RangeDifference</tt> describes kind of difference
+(no change, change, addition, deletion)
+<br>and the corresponding ranges of the underlying comparable entities
+in the two or three inputs.
+<p>The algorithm used is an objectified version of one described in:
+<br><i>A File Comparison Program,</i> by Webb Miller and Eugene W. Myers,
+<br>Software Practice and Experience, Vol. 15, Nov. 1985.
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffContainer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffContainer.java
new file mode 100644
index 000000000..40f970c5e
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffElement.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffElement.java
new file mode 100644
index 000000000..645ae7a64
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffNode.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffNode.java
new file mode 100644
index 000000000..953e6c640
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java
new file mode 100644
index 000000000..bfbcbb742
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewerResources.properties b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewerResources.properties
new file mode 100644
index 000000000..50c6f69c4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java
new file mode 100644
index 000000000..01ea51cc9
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DocumentRangeNode.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DocumentRangeNode.java
new file mode 100644
index 000000000..6c69e1352
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInput.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInput.java
new file mode 100644
index 000000000..4a227242d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInputChangeListener.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/ICompareInputChangeListener.java
new file mode 100644
index 000000000..e182ad749
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffContainer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffContainer.java
new file mode 100644
index 000000000..376a18178
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffElement.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IDiffElement.java
new file mode 100644
index 000000000..8bff24bf4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureComparator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureComparator.java
new file mode 100644
index 000000000..185dc4d60
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureCreator.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/IStructureCreator.java
new file mode 100644
index 000000000..ccfd6ff2c
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java
new file mode 100644
index 000000000..07296cccf
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/package.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/package.html
new file mode 100644
index 000000000..e8567dda6
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/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>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/hglegal.htm b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/hglegal.htm
new file mode 100644
index 000000000..5ac1c6333
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/hglegal.htm
@@ -0,0 +1,19 @@
+<!doctype html public "-//w3c//dtd html 3.2 final//en">
+<html>
+
+<head>
+<title>Legal Notices</title>
+</head>
+
+<body>
+
+<h3><a name="Notices">Notices</a></h3>
+
+<p>Licensed Materials - Property of IBM<br>
+WebSphere Studio Workbench<br>
+(c) Copyright IBM Corp. 2000 All Rights Reserved.</p>
+
+<p>US Government Users Restricted Rights - Use, duplication or disclosure restricted by
+GSA ADP Schedule Contract with IBM Corp.</p>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/ngibmcpy.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/ngibmcpy.gif
new file mode 100644
index 000000000..e0d777bf5
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/ngibmcpy.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare.html
new file mode 100644
index 000000000..ce9339857
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare.html
@@ -0,0 +1,39 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Infrastructure Extension Points</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+IBM Eclipse Platform Compare</h1></center>
+This document lists all of the extension points that the compare plug-in
+makes available to tool developers.
+<p>All XML sub-elements defined in the individual extension point documents
+may appear more than once inside an extension element. For example, a <tt>org.eclipse.compare.contentMergeViewers</tt>
+extension may contain several instances of <tt>viewer</tt> elements. Although
+making one extension per sub-element is not technically incorrect, we recommend
+grouping for consistent style. It can also improve platform startup time
+because there will be fewer extensions to process.
+<br>
+<hr WIDTH="100%">
+<h1>
+Extension Points</h1>
+The following extension points can be used to extend the capabilities of
+the compare infrastructure:
+<ul>
+<li>
+<a href="org_eclipse_ui_compare_contentMergeViewers.html">org.eclipse.compare.contentMergeViewers</a></li>
+
+<li>
+<a href="org_eclipse_ui_compare_structureMergeViewers.html">org.eclipse.compare.structureMergeViewers</a></li>
+
+<li>
+<a href="org_eclipse_ui_compare_structureCreators.html">org.eclipse.compare.structureCreators</a></li>
+</ul>
+<a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html
new file mode 100644
index 000000000..cc382ae1d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_contentMergeViewers.html
@@ -0,0 +1,55 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Extension Points: contentMergeViewers</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+ContentMergeViewer</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.compare.contentMergeViewers
+<p><b><i>Description: </i></b>This extension point allows to plug in a
+compare/merge viewer for specific content types. The viewer must extend
+<tt>org.eclipse.jface.viewers.Viewer</tt>. However since viewers don't have
+a default constructor the extension point must implement a factory object
+for viewers: the interface <tt>org.eclipse.compare.IViewerCreator</tt>.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT viewer EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST viewer</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+<ul>
+<li>
+<b>id</b> - a unique identifier that can be used to reference the viewer</li>
+
+<li>
+<b>class</b> - name of a class that implements a factory for the content
+merge viewer and implements <tt>org.eclipse.compare.IViewerCreator</tt></li>
+
+<li>
+<b>extensions </b>- a comma separated list of file extensions e.g . "java,
+gif"</li>
+</ul>
+<b><i>Examples:</i></b>
+<br>The following is an example of compare/merge viewer for text files
+(extension "txt"):
+<p>&lt;extension point = "org.eclipse.compare.contentMergeViewers">
+<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;viewer
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="org.eclipse.compare.contentmergeviewer.TextMergeViewer"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.eclipse.compare.internal.TextMergeViewerCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions="txt"
+<br>&nbsp;&nbsp;&nbsp; />
+<br>&lt;/extension>
+<p><b><i>Supplied Implementation:</i></b>
+<br>The Compare UI plugin defines content viewers for text, binary contents,
+and images.
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html
new file mode 100644
index 000000000..6cef3b64c
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureCreators.html
@@ -0,0 +1,53 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Extension Points: structureCreators</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+StructureCreator</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.compare.structureCreators
+<p><b><i>Description: </i></b>This extension point allows to plug in an
+object that produces a tree structure of <tt>IStructureComparator</tt>
+for a given content.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT structureCreator EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST structureCreator</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+<ul>
+<li>
+<b>id</b> - a unique identifier that can be used to reference the structure
+creator</li>
+
+<li>
+<b>class</b> - name of a class that implements the strcuture creator and
+implements <tt>org.eclipse.compare.structuremergeviewer.IStructureComparator</tt></li>
+
+<li>
+<b>extensions </b>- a comma separated list of file extensions e.g . "java,
+properties"</li>
+</ul>
+<b><i>Examples:</i></b>
+<br>The following is an example of a structure creator for java files (extension
+"java"):
+<p>&lt;extension point = "org.eclipse.compare.structureCreators">
+<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;structureCreator
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="org.eclipse.compare.JavaStructureCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.eclipse.compare.JavaStructureCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions="java"
+<br>&nbsp;&nbsp;&nbsp; />
+<br>&lt;/extension>
+<p><b><i>Supplied Implementation:</i></b>
+<br>The Compare UI plugin defines a structure creator for zip archives.
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html
new file mode 100644
index 000000000..0de389610
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/doc/org_eclipse_compare_structureMergeViewers.html
@@ -0,0 +1,53 @@
+<!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="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
+ <title>Compare Extension Points: structureMergeViewers</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+StructureMergeViewer</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.compare.structureMergeViewers
+<p><b><i>Description: </i></b>This extension point allows to plug in a
+compare/merge viewer for structured content. The viewer must extend <tt>org.eclipse.jface.viewers.Viewer</tt>.
+However since viewers don't have a default constructor the extension point
+must implement a factory object for viewers: the interface <tt>org.eclipse.compare.IViewerCreator</tt>.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT viewer EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST viewer</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+<ul>
+<li>
+<b>id</b> - a unique identifier that can be used to reference the viewer</li>
+
+<li>
+<b>class</b> - name of a class that implements a factory for the strcuture
+merge viewer and implements <tt>org.eclipse.compare.IViewerCreator</tt></li>
+
+<li>
+<b>extensions </b>- a comma separated list of file extensions e.g . "zip,
+jar"</li>
+</ul>
+<b><i>Examples:</i></b>
+<br>The following is an example of compare/merge viewer for zip files (extension
+"zip"):
+<p>&lt;extension point = "org.eclipse.compare.structureMergeViewers">
+<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;viewer
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="org.eclipse.compare.ZipCompareViewer"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.eclipse.compare.ZipCompareViewerCreator"
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extensions="zip"
+<br>&nbsp;&nbsp;&nbsp; />
+<br>&lt;/extension>
+<p><b><i>Supplied Implementation:</i></b>
+<br>The Compare UI plugin defines a structure compare viewer for zip archives.
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corporation 2000" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/accept.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/accept.gif
new file mode 100644
index 000000000..10942a5cc
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/accept.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/both.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/both.gif
new file mode 100644
index 000000000..3bab8be02
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/both.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/compare.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/compare.gif
new file mode 100644
index 000000000..122f6816b
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/compare.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/ignorews.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/ignorews.gif
new file mode 100644
index 000000000..be6976d8f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/ignorews.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright.gif
new file mode 100644
index 000000000..ff4da3c8c
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gif
new file mode 100644
index 000000000..cd39a8b9d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/lefttoright2.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/next.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/next.gif
new file mode 100644
index 000000000..8b72b7f61
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/next.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/panes3.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/panes3.gif
new file mode 100644
index 000000000..06adb1dc4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/panes3.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/prev.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/prev.gif
new file mode 100644
index 000000000..c2258ef6a
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/prev.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/reject.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/reject.gif
new file mode 100644
index 000000000..df18820a1
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/reject.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft.gif
new file mode 100644
index 000000000..307d69a42
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gif
new file mode 100644
index 000000000..c8d98a4d4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/righttoleft2.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gif
new file mode 100644
index 000000000..4ecd4b29c
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/showpseudoconflicts.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/smart.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/smart.gif
new file mode 100644
index 000000000..213bc4687
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/smart.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/synchmode.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/synchmode.gif
new file mode 100644
index 000000000..a0831f04b
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/synchmode.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/toggle.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/toggle.gif
new file mode 100644
index 000000000..9db5644ab
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ctool16/toggle.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/compare.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/compare.gif
new file mode 100644
index 000000000..122f6816b
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/compare.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/date.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/date.gif
new file mode 100644
index 000000000..41744b7e4
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/date.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/time.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/time.gif
new file mode 100644
index 000000000..d52f5487f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/obj16/time.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/add.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/add.gif
new file mode 100644
index 000000000..edeb956cd
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_add.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_add.gif
new file mode 100644
index 000000000..96862a348
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_change.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_change.gif
new file mode 100644
index 000000000..c0761e473
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_change.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_del.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_del.gif
new file mode 100644
index 000000000..6e8d1f582
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/confl_del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/del.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/del.gif
new file mode 100644
index 000000000..0db04b13d
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_add.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_add.gif
new file mode 100644
index 000000000..9f17d268a
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_change.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_change.gif
new file mode 100644
index 000000000..83a065c6c
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_change.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_del.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_del.gif
new file mode 100644
index 000000000..a3310e837
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/in_del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_add.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_add.gif
new file mode 100644
index 000000000..a40943e87
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_add.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_change.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_change.gif
new file mode 100644
index 000000000..764fbf3bf
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_change.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_del.gif b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_del.gif
new file mode 100644
index 000000000..b8c6a1b87
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/icons/basic/ovr16/out_del.gif
Binary files differ
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.jars b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.jars
new file mode 100644
index 000000000..9bc88429f
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.jars
@@ -0,0 +1 @@
+compare.jar=Eclipse Compare \ No newline at end of file
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.properties b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.properties
new file mode 100644
index 000000000..565934f55
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.properties
@@ -0,0 +1,9 @@
+CompareWithMenu.label=Compare With
+
+CompareWithEachOtherAction.label=Each other
+CompareWithEachOtherAction.tooltip=Compare the selected resources
+
+ReplaceWithMenu.label=Replace With
+
+ReplaceFromHistoryAction.label=Edition from local History...
+ReplaceFromHistoryAction.tooltip=Replace selected resource with edition from history
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.xml b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.xml
new file mode 100644
index 000000000..389c27b9e
--- /dev/null
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/plugin.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0"?>
+
+<!-- ======================================================================= -->
+<!-- Compare UI Plugin -->
+<!-- ======================================================================= -->
+
+<plugin
+ name="Compare"
+ id="org.eclipse.compare"
+ version="1.0"
+ vendor-name="IBM"
+ class="org.eclipse.compare.internal.CompareUIPlugin">
+
+ <requires>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.core.resources"/>
+ </requires>
+
+ <runtime>
+ <library name="compare.jar" >
+ <export name="*"/>
+ </library>
+ </runtime>
+
+<!-- Compare extension point definitions -->
+
+ <extension-point
+ name="Structure Creator"
+ id="structureCreators"/>
+
+ <extension-point
+ name="Structure Merge Viewer"
+ id="structureMergeViewers"/>
+
+ <extension-point
+ name="Content Merge Viewer"
+ id="contentMergeViewers"/>
+
+ <extension-point
+ name="Content Viewer"
+ id="contentViewers"/>
+
+<!-- Extensions -->
+
+ <extension point="org.eclipse.ui.editors">
+ <editor id="org.eclipse.compare.CompareEditor"
+ name="Default Compare Editor"
+ icon="icons/basic/ctool16/compare.gif"
+ class="org.eclipse.compare.internal.CompareEditor"
+ contributorClass="org.eclipse.compare.internal.CompareEditorContributor">
+ </editor>
+ </extension>
+
+ <extension point="org.eclipse.ui.popupMenus">
+
+ <objectContribution
+ id="org.eclipse.compare.CompareAction"
+ objectClass="org.eclipse.core.resources.IResource">
+ <menu
+ id="replaceWithMenu"
+ path="additions"
+ label="%ReplaceWithMenu.label">
+ <separator name="replaceWithGroup"/>
+ </menu>
+ <menu
+ id="compareWithMenu"
+ path="additions"
+ label="%CompareWithMenu.label">
+ <separator name="compareWithGroup"/>
+ </menu>
+ <action
+ id="compareWithEachOther"
+ label="%CompareWithEachOtherAction.label"
+ tooltip="%CompareWithEachOtherAction.tooltip"
+ menubarPath="compareWithMenu/compareWithGroup"
+ enablesFor="2+"
+ class="org.eclipse.compare.internal.CompareAction">
+ </action>
+ </objectContribution>
+
+ <objectContribution
+ id="org.eclipse.compare.ReplaceWithEditionAction"
+ objectClass="org.eclipse.core.resources.IFile">
+ <action
+ id="replaceFromHistory"
+ label="%ReplaceFromHistoryAction.label"
+ tooltip="%ReplaceFromHistoryAction.tooltip"
+ menubarPath="replaceWithMenu/replaceWithGroup"
+ enablesFor="1"
+ class="org.eclipse.compare.internal.ReplaceWithEditionAction">
+ </action>
+ </objectContribution>
+
+ </extension>
+
+ <extension point="org.eclipse.compare.structureCreators">
+ <structureCreator
+ extensions="zip"
+ class="org.eclipse.compare.internal.ZipStructureCreator">
+ </structureCreator>
+ </extension>
+
+ <extension point="org.eclipse.compare.contentMergeViewers">
+ <viewer
+ extensions="binary"
+ class="org.eclipse.compare.internal.BinaryCompareViewerCreator">
+ </viewer>
+ <viewer
+ extensions="txt"
+ class="org.eclipse.compare.internal.TextMergeViewerCreator">
+ </viewer>
+ <viewer
+ extensions="gif,jpg"
+ class="org.eclipse.compare.internal.ImageMergeViewerCreator">
+ </viewer>
+ </extension>
+
+ <extension point="org.eclipse.compare.contentViewers">
+ <viewer
+ extensions="txt"
+ class="org.eclipse.compare.internal.TextViewerCreator">
+ </viewer>
+ </extension>
+
+</plugin>

Back to the top