From 5755d15895e8d471924809f783feb9a95718d240 Mon Sep 17 00:00:00 2001 From: Jeremy Flicker Date: Wed, 28 Nov 2012 12:03:52 +0100 Subject: Bug 382427 - Mechanism to inject line compare strategies into merge and structure viewers Change-Id: Iec597bd9582933b90f67de94547b4ccdaef9a319 --- bundles/org.eclipse.compare/META-INF/MANIFEST.MF | 2 +- .../org/eclipse/compare/ICompareFilter.java | 99 +++++ .../contentmergeviewer/TextMergeViewer.java | 156 +++++++- .../ChangeCompareFilterPropertyAction.java | 131 +++++++ .../compare/internal/CompareEditorContributor.java | 87 ++++- .../eclipse/compare/internal/CompareFilter.java | 397 -------------------- .../compare/internal/CompareFilterDescriptor.java | 110 ++++++ .../compare/internal/ComparePreferencePage.java | 4 +- .../compare/internal/CompareResourceFilter.java | 399 +++++++++++++++++++++ .../eclipse/compare/internal/CompareUIPlugin.java | 144 +++++++- .../compare/internal/DocLineComparator.java | 126 ++++++- .../compare/internal/IMergeViewerTestAdapter.java | 9 +- .../compare/internal/MergeSourceViewer.java | 6 +- .../org/eclipse/compare/internal/Utilities.java | 72 +++- .../compare/internal/merge/DocumentMerger.java | 35 +- .../compare/structuremergeviewer/Differencer.java | 44 ++- .../structuremergeviewer/StructureCreator.java | 93 ++++- .../structuremergeviewer/StructureDiffViewer.java | 35 +- bundles/org.eclipse.compare/plugin.properties | 1 + bundles/org.eclipse.compare/plugin.xml | 1 + .../org.eclipse.compare/schema/compareFilters.exsd | 283 +++++++++++++++ .../org.eclipse.compare.tests/META-INF/MANIFEST.MF | 2 +- .../src/org/eclipse/compare/tests/AllTests.java | 3 +- .../compare/tests/DocLineComparatorTest.java | 212 ++++++++++- .../src/org/eclipse/compare/tests/FilterTest.java | 14 +- .../compare/tests/StructureCreatorTest.java | 181 ++++++++++ .../eclipse/compare/tests/TextMergeViewerTest.java | 118 +++++- 27 files changed, 2286 insertions(+), 478 deletions(-) create mode 100644 bundles/org.eclipse.compare/compare/org/eclipse/compare/ICompareFilter.java create mode 100644 bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangeCompareFilterPropertyAction.java delete mode 100644 bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java create mode 100644 bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilterDescriptor.java create mode 100644 bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareResourceFilter.java create mode 100644 bundles/org.eclipse.compare/schema/compareFilters.exsd create mode 100644 tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/StructureCreatorTest.java diff --git a/bundles/org.eclipse.compare/META-INF/MANIFEST.MF b/bundles/org.eclipse.compare/META-INF/MANIFEST.MF index 016995ab0..de3405265 100644 --- a/bundles/org.eclipse.compare/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.compare/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.compare; singleton:=true -Bundle-Version: 3.5.400.qualifier +Bundle-Version: 3.6.0.qualifier Bundle-Activator: org.eclipse.compare.internal.CompareUIPlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/ICompareFilter.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/ICompareFilter.java new file mode 100644 index 000000000..1d740d354 --- /dev/null +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/ICompareFilter.java @@ -0,0 +1,99 @@ +package org.eclipse.compare; + +import java.util.HashMap; + +import org.eclipse.jface.text.IRegion; + +/******************************************************************************* + * Copyright (c) 2013 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +/** + * A filter that can be applied during the comparison of documents that can be + * used to customize the detection of text differences via the compareFilter + * extension point. Filters are exposed as toggle actions in the compare viewer. + * + * @noimplement This interface is not intended to be implemented by clients. + * This is an interface for org.eclipse.compare.compareFilters + * extension point which is internal. + * @since 3.6 + */ +public interface ICompareFilter { + + /** + * Key for the String of the line of text being compared. + */ + public static final String THIS_LINE = "THIS_LINE"; //$NON-NLS-1$ + + /** + * Key for the Character representing contributor of this line. + * Value is either 'A' for ancestor, 'L' for left, or 'R' for right. + */ + public static final String THIS_CONTRIBUTOR = "THIS_CONTRIBUTOR"; //$NON-NLS-1$ + + /** + * Key for the String of the line of text this line is being + * compared to. + */ + public static final String OTHER_LINE = "OTHER_LINE"; //$NON-NLS-1$ + + /** + * Key for the Character representing contributor of the other + * line. Value is either 'A' for ancestor, 'L' for left, or 'R' for right. + */ + public static final String OTHER_CONTRIBUTOR = "OTHER_CONTRIBUTOR"; //$NON-NLS-1$ + + /** + * Forwards the current input objects of the compare + * + * @param input + * the merge viewer input + * @param ancestor + * input into ancestor viewer + * @param left + * input into left viewer + * @param right + * input into right viewer + */ + public void setInput(Object input, Object ancestor, Object left, + Object right); + + /** + * Identifies the regions of a line of text in a comparison that should be + * ignored for comparison purposes. + * + * @param lineComparison + * contains values for the keys THIS_LINE, + * THIS_CONTRIBUTOR, OTHER_LINE and + * OTHER_CONTRIBUTOR + * @return Regions of THIS_LINE to be ignored for comparison + * purposes. + */ + public IRegion[] getFilteredRegions(HashMap lineComparison); + + /** + * Returns whether the filter should be enabled when first initialized + * + * @return default enablement + */ + public boolean isEnabledInitially(); + + /** + * Because the comparison routine may compare each line multiple times to + * other lines, the ignored regions may need to be calculated multiple times + * for the same line during a comparison. If the ignored regions for each + * line will be the same regardless of what line it is being compared to, + * returning true to this method will cause the ignored region + * calculations to be re-used and improve the performance of the comparison. + * + * @return ignored region results can be cached + */ + public boolean canCacheFilteredRegions(); +} 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 index 727a07943..fcdc3ed24 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java @@ -39,12 +39,15 @@ import org.eclipse.compare.IStreamContentAccessor; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.SharedDocumentAdapter; import org.eclipse.compare.internal.BufferedCanvas; +import org.eclipse.compare.internal.ChangeCompareFilterPropertyAction; import org.eclipse.compare.internal.ChangePropertyAction; import org.eclipse.compare.internal.CompareEditor; +import org.eclipse.compare.internal.CompareEditorContributor; import org.eclipse.compare.internal.CompareEditorSelectionProvider; import org.eclipse.compare.internal.CompareHandlerService; import org.eclipse.compare.internal.CompareMessages; import org.eclipse.compare.internal.ComparePreferencePage; +import org.eclipse.compare.internal.CompareFilterDescriptor; import org.eclipse.compare.internal.CompareUIPlugin; import org.eclipse.compare.internal.DocumentManager; import org.eclipse.compare.internal.ICompareContextIds; @@ -426,6 +429,7 @@ public class TextMergeViewer extends ContentMergeViewer implements IAdaptable { private TextEditorPropertyAction toggleLineNumbersAction; private IFindReplaceTarget fFindReplaceTarget; private ChangePropertyAction fIgnoreWhitespace; + private List fCompareFilterActions = new ArrayList(); private DocumentMerger fMerger; /** The current diff */ private Diff fCurrentDiff; @@ -1852,7 +1856,12 @@ public class TextMergeViewer extends ContentMergeViewer implements IAdaptable { if (fIgnoreWhitespace != null) fIgnoreWhitespace.dispose(); - + + getCompareConfiguration().setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS_INITIALIZING, + Boolean.TRUE); + disposeCompareFilterActions(false); + if (fSourceViewerDecorationSupport != null) { for (Iterator iterator = fSourceViewerDecorationSupport.iterator(); iterator.hasNext();) { ((SourceViewerDecorationSupport) iterator.next()).dispose(); @@ -2790,6 +2799,8 @@ public class TextMergeViewer extends ContentMergeViewer implements IAdaptable { Object input= getInput(); + configureCompareFilterActions(input, ancestor, left, right); + Position leftRange= null; Position rightRange= null; @@ -3711,6 +3722,140 @@ public class TextMergeViewer extends ContentMergeViewer implements IAdaptable { fHandlerService.registerAction(toggleLineNumbersAction, ITextEditorActionDefinitionIds.LINENUMBER_TOGGLE); } + private void configureCompareFilterActions(Object input, Object ancestor, + Object left, Object right) { + if (getCompareConfiguration() != null) { + CompareFilterDescriptor[] compareFilterDescriptors = CompareUIPlugin + .getDefault().findCompareFilters(input); + + Object current = getCompareConfiguration().getProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTER_ACTIONS); + boolean currentFiltersMatch = false; + if (current != null + && current instanceof List + && ((List) current).size() == compareFilterDescriptors.length) { + currentFiltersMatch = true; + List currentFilterActions = (List) current; + for (int i = 0; i < compareFilterDescriptors.length; i++) { + boolean match = false; + for (int j = 0; j < currentFilterActions.size(); j++) { + if (compareFilterDescriptors[i] + .getFilterId() + .equals(((ChangeCompareFilterPropertyAction) currentFilterActions + .get(j)).getFilterId())) { + match = true; + break; + } + } + if (!match) { + currentFiltersMatch = false; + break; + } + } + } + + if (!currentFiltersMatch) { + getCompareConfiguration() + .setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS_INITIALIZING, + Boolean.TRUE); + disposeCompareFilterActions(true); + fCompareFilterActions.clear(); + for (int i = 0; i < compareFilterDescriptors.length; i++) { + ChangeCompareFilterPropertyAction compareFilterAction = new ChangeCompareFilterPropertyAction( + compareFilterDescriptors[i], + getCompareConfiguration()); + compareFilterAction.setInput(input, ancestor, left, right); + fCompareFilterActions.add(compareFilterAction); + fLeft.addTextAction(compareFilterAction); + fRight.addTextAction(compareFilterAction); + fAncestor.addTextAction(compareFilterAction); + + if (getCompareConfiguration().getContainer() + .getActionBars() != null) { + getCompareConfiguration() + .getContainer() + .getActionBars() + .getToolBarManager() + .appendToGroup( + CompareEditorContributor.FILTER_SEPARATOR, + compareFilterAction); + if (compareFilterAction.getActionDefinitionId() != null) + getCompareConfiguration() + .getContainer() + .getActionBars() + .setGlobalActionHandler( + compareFilterAction + .getActionDefinitionId(), + compareFilterAction); + } + } + if (!fCompareFilterActions.isEmpty() + && getCompareConfiguration().getContainer() + .getActionBars() != null) { + getCompareConfiguration().getContainer().getActionBars() + .getToolBarManager().markDirty(); + getCompareConfiguration().getContainer().getActionBars() + .getToolBarManager().update(true); + getCompareConfiguration().getContainer().getActionBars() + .updateActionBars(); + } + getCompareConfiguration() + .setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTER_ACTIONS, + fCompareFilterActions); + getCompareConfiguration() + .setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS_INITIALIZING, + null); + } else { + for (int i = 0; i < fCompareFilterActions.size(); i++) { + ((ChangeCompareFilterPropertyAction) fCompareFilterActions + .get(i)).setInput(input, ancestor, left, right); + } + } + } + } + + private void disposeCompareFilterActions(boolean updateActionBars) { + Iterator compareFilterActionsIterator = fCompareFilterActions + .iterator(); + while (compareFilterActionsIterator.hasNext()) { + ChangeCompareFilterPropertyAction compareFilterAction = (ChangeCompareFilterPropertyAction) compareFilterActionsIterator + .next(); + fLeft.removeTextAction(compareFilterAction); + fRight.removeTextAction(compareFilterAction); + fAncestor.removeTextAction(compareFilterAction); + if (updateActionBars + && getCompareConfiguration().getContainer().getActionBars() != null) { + getCompareConfiguration().getContainer().getActionBars() + .getToolBarManager() + .remove(compareFilterAction.getId()); + if (compareFilterAction.getActionDefinitionId() != null) + getCompareConfiguration() + .getContainer() + .getActionBars() + .setGlobalActionHandler( + compareFilterAction.getActionDefinitionId(), + null); + } + compareFilterAction.dispose(); + } + if (updateActionBars + && !fCompareFilterActions.isEmpty() + && getCompareConfiguration().getContainer().getActionBars() != null) { + getCompareConfiguration().getContainer().getActionBars() + .getToolBarManager().markDirty(); + getCompareConfiguration().getContainer().getActionBars() + .getToolBarManager().update(true); + } + fCompareFilterActions.clear(); + getCompareConfiguration().setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS, null); + getCompareConfiguration().setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTER_ACTIONS, null); + } + /* (non-Javadoc) * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handlePropertyChangeEvent(org.eclipse.jface.util.PropertyChangeEvent) */ @@ -3718,7 +3863,10 @@ public class TextMergeViewer extends ContentMergeViewer implements IAdaptable { String key= event.getProperty(); if (key.equals(CompareConfiguration.IGNORE_WHITESPACE) - || key.equals(ComparePreferencePage.SHOW_PSEUDO_CONFLICTS)) { + || key.equals(ComparePreferencePage.SHOW_PSEUDO_CONFLICTS) + || (key.equals(ChangeCompareFilterPropertyAction.COMPARE_FILTERS) && getCompareConfiguration() + .getProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS_INITIALIZING) == null)) { fShowPseudoConflicts= fPreferenceStore.getBoolean(ComparePreferencePage.SHOW_PSEUDO_CONFLICTS); @@ -4969,6 +5117,10 @@ public class TextMergeViewer extends ContentMergeViewer implements IAdaptable { } return null; } + + public int getChangesCount() { + return fMerger.changesCount(); + } }; } if (adapter == OutlineViewerCreator.class) { diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangeCompareFilterPropertyAction.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangeCompareFilterPropertyAction.java new file mode 100644 index 000000000..609353517 --- /dev/null +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ChangeCompareFilterPropertyAction.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2000, 2013 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.ICompareFilter; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; + +/** + * Toggles the activation of a compare filter + */ +public class ChangeCompareFilterPropertyAction extends Action implements + IPropertyChangeListener, DisposeListener { + + public static final String COMPARE_FILTERS = "COMPARE_FILTERS"; //$NON-NLS-1$ + public static final String COMPARE_FILTER_ACTIONS = "COMPARE_FILTER_ACTIONS"; //$NON-NLS-1$ + public static final String COMPARE_FILTERS_INITIALIZING = "COMPARE_FILTERS_INITIALIZING"; //$NON-NLS-1$ + + private CompareConfiguration fCompareConfiguration; + private ResourceBundle fBundle; + private String fPrefix = "filter."; //$NON-NLS-1$ + private CompareFilterDescriptor fCompareFilterDescriptor; + private ICompareFilter fCompareFilter; + + public ChangeCompareFilterPropertyAction( + CompareFilterDescriptor compareFilterDescriptor, + CompareConfiguration compareConfiguration) { + this.fBundle = compareFilterDescriptor.getResourceBundle(); + this.fCompareFilterDescriptor = compareFilterDescriptor; + this.fCompareFilter = compareFilterDescriptor.createCompareFilter(); + Utilities.initAction(this, fBundle, fPrefix); + + // Utilities only loads images from compare plugin + setImageDescriptor(compareFilterDescriptor.getImageDescriptor()); + setId(compareFilterDescriptor.getFilterId()); + setActionDefinitionId(compareFilterDescriptor.getDefinitionId()); + setCompareConfiguration(compareConfiguration); + + if (fCompareFilter.isEnabledInitially()) { + setChecked(true); + setProperty(true); + } + } + + void setProperty(boolean state) { + if (fCompareConfiguration != null) { + Map filters = new HashMap(); + if (fCompareConfiguration.getProperty(COMPARE_FILTERS) != null) { + filters.putAll((Map) fCompareConfiguration + .getProperty(COMPARE_FILTERS)); + } + if (state) { + filters.put(fCompareFilterDescriptor.getFilterId(), + fCompareFilter); + } else { + filters.remove(fCompareFilterDescriptor.getFilterId()); + } + fCompareConfiguration.setProperty(COMPARE_FILTERS, filters); + } + } + + boolean getProperty() { + Map filters = (Map) fCompareConfiguration.getProperty(COMPARE_FILTERS); + if (filters == null) { + filters = new HashMap(); + } + return filters.containsKey(fCompareFilterDescriptor.getFilterId()); + } + + public void run() { + boolean b = !getProperty(); + setChecked(b); + setProperty(b); + } + + public void setChecked(boolean state) { + super.setChecked(state); + Utilities.initToggleAction(this, fBundle, fPrefix, state); + } + + public void setCompareConfiguration(CompareConfiguration cc) { + if (fCompareConfiguration != null) + fCompareConfiguration.removePropertyChangeListener(this); + fCompareConfiguration = cc; + if (fCompareConfiguration != null) + fCompareConfiguration.addPropertyChangeListener(this); + setChecked(getProperty()); + } + + public void propertyChange(PropertyChangeEvent event) { + if (event.getProperty().equals(COMPARE_FILTERS) + && event.getNewValue() instanceof Map) { + setChecked(((Map) event.getNewValue()) + .containsKey(fCompareFilterDescriptor.getFilterId())); + } + } + + public void dispose() { + if (fCompareConfiguration != null) + fCompareConfiguration.removePropertyChangeListener(this); + } + + public void widgetDisposed(DisposeEvent e) { + dispose(); + } + + public String getFilterId() { + return fCompareFilterDescriptor.getFilterId(); + } + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + fCompareFilter.setInput(input, ancestor, left, right); + } +} 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 index fc427fe23..10de7cef5 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareEditorContributor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2007 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,21 +10,34 @@ *******************************************************************************/ package org.eclipse.compare.internal; +import java.util.Iterator; +import java.util.List; import java.util.ResourceBundle; -import org.eclipse.jface.action.*; - -import org.eclipse.ui.*; +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.compare.CompareUI; +import org.eclipse.compare.NavigationAction; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.help.IWorkbenchHelpSystem; import org.eclipse.ui.part.EditorActionBarContributor; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; -import org.eclipse.compare.*; - public class CompareEditorContributor extends EditorActionBarContributor { + public final static String FILTER_SEPARATOR = "compare.filters"; //$NON-NLS-1$ + public final static String BUILTIN_SEPARATOR = "compare.builtin"; //$NON-NLS-1$ + private IEditorPart fActiveEditorPart= null; private ChangePropertyAction fIgnoreWhitespace; @@ -59,10 +72,11 @@ public class CompareEditorContributor extends EditorActionBarContributor { * @see EditorActionBarContributor#contributeToToolBar(IToolBarManager) */ public void contributeToToolBar(IToolBarManager tbm) { - tbm.add(new Separator()); - tbm.add(fIgnoreWhitespace); - tbm.add(fToolbarNext); - tbm.add(fToolbarPrevious); + tbm.add(new Separator(FILTER_SEPARATOR)); + tbm.add(new Separator(BUILTIN_SEPARATOR)); + tbm.appendToGroup(BUILTIN_SEPARATOR, fIgnoreWhitespace); + tbm.appendToGroup(BUILTIN_SEPARATOR, fToolbarNext); + tbm.appendToGroup(BUILTIN_SEPARATOR, fToolbarPrevious); } /* @@ -106,6 +120,59 @@ public class CompareEditorContributor extends EditorActionBarContributor { CompareConfiguration cc= editor.getCompareConfiguration(); fIgnoreWhitespace.setCompareConfiguration(cc); + + IContributionItem[] items = actionBars.getToolBarManager() + .getItems(); + boolean inFilters = false; + for (int i = 0; i < items.length; i++) { + if (items[i].getId().equals(FILTER_SEPARATOR)) { + inFilters = true; + } else if (items[i].getId().equals(BUILTIN_SEPARATOR)) { + break; + } else if (inFilters) { + if (items[i] instanceof ActionContributionItem) { + String definitionId = ((ActionContributionItem) items[i]) + .getAction().getActionDefinitionId(); + if (definitionId != null) { + actionBars.setGlobalActionHandler(definitionId, + null); + } + } + actionBars.getToolBarManager().remove(items[i]); + } + } + + IEditorInput input = editor.getEditorInput(); + if (input instanceof CompareEditorInput + && ((CompareEditorInput) input).getCompareConfiguration() != null) { + Object filterActions = ((CompareEditorInput) input) + .getCompareConfiguration() + .getProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTER_ACTIONS); + if (filterActions instanceof List + && !((List) filterActions).isEmpty()) { + Iterator i = ((List) filterActions).iterator(); + while (i.hasNext()) { + Object next = i.next(); + if (next instanceof ChangeCompareFilterPropertyAction) { + actionBars.getToolBarManager().appendToGroup( + FILTER_SEPARATOR, + (ChangeCompareFilterPropertyAction) next); + String definitionId = ((ChangeCompareFilterPropertyAction) next) + .getActionDefinitionId(); + if (definitionId != null) { + actionBars + .setGlobalActionHandler( + definitionId, + (ChangeCompareFilterPropertyAction) next); + } + } + } + actionBars.getToolBarManager().markDirty(); + actionBars.getToolBarManager().update(true); + actionBars.updateActionBars(); + } + } } else { IActionBars actionBars= getActionBars(); actionBars.setGlobalActionHandler(ActionFactory.NEXT.getId(), null); diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java deleted file mode 100644 index 3a17994be..000000000 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilter.java +++ /dev/null @@ -1,397 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.compare.internal; - -import com.ibm.icu.text.MessageFormat; -import java.util.StringTokenizer; - -import org.eclipse.core.resources.*; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.runtime.IStatus; - - -public class CompareFilter { - private static final char[][] NO_CHAR_CHAR= new char[0][]; - - private char[][] fExtraResourceFileFilters; - private String[] fExtraResourceFolderFilters; - - - public CompareFilter() { - // nothing to do - } - - /* - * Returns true if path matches filter, that is if path should be filtered. - */ - public boolean filter(String path0, boolean folder, boolean isArchive) { - if (!folder && fExtraResourceFileFilters != null) { - char[] name= path0.toCharArray(); - for (int i= 0, l= fExtraResourceFileFilters.length; i < l; i++) - if (match(fExtraResourceFileFilters[i], name, true)) - return true; - } - if (folder && fExtraResourceFolderFilters != null) { - for (int i= 0, l= fExtraResourceFolderFilters.length; i < l; i++) - if (fExtraResourceFolderFilters[i].equals(path0)) - return true; - } - return false; - } - - public static String validateResourceFilters(String text) { - IWorkspace workspace= ResourcesPlugin.getWorkspace(); - String[] filters= getTokens(text, ","); //$NON-NLS-1$ - for (int i= 0; i < filters.length; i++) { - String fileName= filters[i].replace('*', 'x'); - int resourceType= IResource.FILE; - int lastCharacter= fileName.length() - 1; - if (lastCharacter >= 0 && fileName.charAt(lastCharacter) == '/') { - fileName= fileName.substring(0, lastCharacter); - resourceType= IResource.FOLDER; - } - IStatus status= workspace.validateName(fileName, resourceType); - if (status.matches(IStatus.ERROR)) { - String format= Utilities.getString("ComparePreferencePage.filter.invalidsegment.error"); //$NON-NLS-1$ - return MessageFormat.format(format, new String[] { status.getMessage() } ); - } - } - return null; - } - - public void setFilters(String filterSequence) { - char[][] filters= filterSequence != null && filterSequence.length() > 0 - ? splitAndTrimOn(',', filterSequence.toCharArray()) - : null; - if (filters == null) { - fExtraResourceFileFilters= null; - fExtraResourceFolderFilters= null; - } else { - int fileCount= 0, folderCount= 0; - for (int i= 0, l= filters.length; i < l; i++) { - char[] f= filters[i]; - if (f.length == 0) - continue; - if (f[f.length - 1] == '/') - folderCount++; - else - fileCount++; - } - fExtraResourceFileFilters= new char[fileCount][]; - fExtraResourceFolderFilters= new String[folderCount]; - for (int i= 0, l= filters.length; i < l; i++) { - char[] f= filters[i]; - if (f.length == 0) - continue; - if (f[f.length - 1] == '/') - fExtraResourceFolderFilters[--folderCount]= new String(subarray(f, 0, f.length - 1)); - else - fExtraResourceFileFilters[--fileCount]= f; - } - } - } - - ///////// - - private static String[] getTokens(String text, String separator) { - StringTokenizer tok= new StringTokenizer(text, separator); - int nTokens= tok.countTokens(); - String[] res= new String[nTokens]; - for (int i= 0; i < res.length; i++) - res[i]= tok.nextToken().trim(); - return res; - } - - /** - * Answers true if the pattern matches the given name, false otherwise. - * This char[] pattern matching accepts wild-cards '*' and '?'. - * - * When not case sensitive, the pattern is assumed to already be - * lowercased, the name will be lowercased character per character as - * comparing. If name is null, the answer is false. If pattern is null, the - * answer is true if name is not null.

For example: - *
    - *
  1. - * - *
    -	 *  pattern = { '?', 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => true
    -	 * 
    - * - * - *
  2. - *
  3. - * - *
    -	 *  pattern = { '?', 'b', '?' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
    -	 * 
    - * - * - *
  4. - *
  5. - * - *
    -	 *  pattern = { 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
    -	 * 
    - * - * - *
  6. - *
- * - * @param pattern - * the given pattern - * @param name - * the given name - * @param isCaseSensitive - * flag to know whether or not the matching should be case - * sensitive - * @return true if the pattern matches the given name, false otherwise - */ - private boolean match(char[] pattern, char[] name, boolean isCaseSensitive) { - if (name == null) - return false; // null name cannot match - if (pattern == null) - return true; // null pattern is equivalent to '*' - return match(pattern, 0, pattern.length, name, 0, name.length, isCaseSensitive); - } - - /** - * Answers true if the a sub-pattern matches the subpart of the given name, - * false otherwise. char[] pattern matching, accepting wild-cards '*' and - * '?'. Can match only subset of name/pattern. end positions are - * non-inclusive. The subpattern is defined by the patternStart and - * pattternEnd positions. When not case sensitive, the pattern is assumed - * to already be lowercased, the name will be lowercased character per - * character as comparing.

For example: - *
    - *
  1. - * - *
    -	 *  pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 3 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 4 isCaseSensitive = true result => true
    -	 * 
    - * - * - *
  2. - *
  3. - * - *
    -	 *  pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 2 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 2 isCaseSensitive = true result => false
    -	 * 
    - * - * - *
  4. - *
- * - * @param pattern - * the given pattern - * @param patternStart - * the given pattern start - * @param patternEnd - * the given pattern end - * @param name - * the given name - * @param nameStart - * the given name start - * @param nameEnd - * the given name end - * @param isCaseSensitive - * flag to know if the matching should be case sensitive - * @return true if the a sub-pattern matches the subpart of the given name, - * false otherwise - */ - private boolean match(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd, - boolean isCaseSensitive) { - if (name == null) - return false; // null name cannot match - if (pattern == null) - return true; // null pattern is equivalent to '*' - int iPattern= patternStart; - int iName= nameStart; - if (patternEnd < 0) - patternEnd= pattern.length; - if (nameEnd < 0) - nameEnd= name.length; - /* check first segment */ - char patternChar= 0; - while ((iPattern < patternEnd) && (patternChar= pattern[iPattern]) != '*') { - if (iName == nameEnd) - return false; - if (patternChar != (isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) && patternChar != '?') { - return false; - } - iName++; - iPattern++; - } - /* check sequence of star+segment */ - int segmentStart; - if (patternChar == '*') { - segmentStart= ++iPattern; // skip star - } else { - segmentStart= 0; // force iName check - } - int prefixStart= iName; - checkSegment : while (iName < nameEnd) { - if (iPattern == patternEnd) { - iPattern= segmentStart; // mismatch - restart current segment - iName= ++prefixStart; - continue checkSegment; - } - /* segment is ending */ - if ((patternChar= pattern[iPattern]) == '*') { - segmentStart= ++iPattern; // skip start - if (segmentStart == patternEnd) { - return true; - } - prefixStart= iName; - continue checkSegment; - } - /* check current name character */ - if ((isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) != patternChar && patternChar != '?') { - iPattern= segmentStart; // mismatch - restart current segment - iName= ++prefixStart; - continue checkSegment; - } - iName++; - iPattern++; - } - return (segmentStart == patternEnd) || (iName == nameEnd && iPattern == patternEnd) - || (iPattern == patternEnd - 1 && pattern[iPattern] == '*'); - } - - /** - * Return a new array which is the split of the given array using the given - * divider and triming each subarray to remove whitespaces equals to ' '. - *

For example: - *
    - *
  1. - * - *
    -	 *  divider = 'b' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
    -	 * 
    - * - * - *
  2. - *
  3. - * - *
    -	 *  divider = 'c' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    -	 * 
    - * - * - *
  4. - *
  5. - * - *
    -	 *  divider = 'b' array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
    -	 * 
    - * - * - *
  6. - *
  7. - * - *
    -	 *  divider = 'c' array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    -	 * 
    - * - * - *
  8. - *
- * - * @param divider - * the given divider - * @param array - * the given array - * @return a new array which is the split of the given array using the - * given divider and triming each subarray to remove whitespaces - * equals to ' ' - */ - private char[][] splitAndTrimOn(char divider, char[] array) { - int length= array == null ? 0 : array.length; - if (length == 0) - return NO_CHAR_CHAR; - int wordCount= 1; - for (int i= 0; i < length; i++) - if (array[i] == divider) - wordCount++; - char[][] split= new char[wordCount][]; - int last= 0, currentWord= 0; - for (int i= 0; i < length; i++) { - if (array[i] == divider) { - int start= last, end= i - 1; - while (start < i && array[start] == ' ') - start++; - while (end > start && array[end] == ' ') - end--; - split[currentWord]= new char[end - start + 1]; - System.arraycopy(array, start, split[currentWord++], 0, end - start + 1); - last= i + 1; - } - } - int start= last, end= length - 1; - while (start < length && array[start] == ' ') - start++; - while (end > start && array[end] == ' ') - end--; - split[currentWord]= new char[end - start + 1]; - System.arraycopy(array, start, split[currentWord++], 0, end - start + 1); - return split; - } - - /** - * Answers a new array which is a copy of the given array starting at the - * given start and ending at the given end. The given start is inclusive - * and the given end is exclusive. Answers null if start is greater than - * end, if start is lower than 0 or if end is greater than the length of - * the given array. If end equals -1, it is converted to the array length. - *

For example: - *
    - *
  1. - * - *
    -	 *  array = { 'a' , 'b' } start = 0 end = 1 result => { 'a' }
    -	 * 
    - * - * - *
  2. - *
  3. - * - *
    -	 *  array = { 'a', 'b' } start = 0 end = -1 result => { 'a' , 'b' }
    -	 * 
    - * - * - *
  4. - *
- * - * @param array - * the given array - * @param start - * the given starting index - * @param end - * the given ending index - * @return a new array which is a copy of the given array starting at the - * given start and ending at the given end - * @exception NullPointerException - * if the given array is null - */ - private char[] subarray(char[] array, int start, int end) { - if (end == -1) - end= array.length; - if (start > end) - return null; - if (start < 0) - return null; - if (end > array.length) - return null; - char[] result= new char[end - start]; - System.arraycopy(array, start, result, 0, end - start); - return result; - } -} diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilterDescriptor.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilterDescriptor.java new file mode 100644 index 000000000..bfddb2e80 --- /dev/null +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareFilterDescriptor.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2013 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.net.URL; +import java.util.Enumeration; +import java.util.ResourceBundle; + +import org.eclipse.compare.ICompareFilter; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.resource.ImageDescriptor; +import org.osgi.framework.Bundle; + +/** + * Describes compare filter extension. + */ +public class CompareFilterDescriptor { + + private final static String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ + private final static String ID_ATTRIBUTE = "id"; //$NON-NLS-1$ + private final static String DEFINITION_ID_ATTRIBUTE = "definitionId"; //$NON-NLS-1$ + private final static String FILTER_IMAGE_ATTRIBUTE = "filter.image"; //$NON-NLS-1$ + + private IConfigurationElement fConfiguration; + private ResourceBundle fResourceBundle; + private ImageDescriptor fImageDescriptor; + + private class ConfigurationKeysEnumeration implements Enumeration { + + private String[] keySet; + private int cursor = 0; + + public ConfigurationKeysEnumeration(IConfigurationElement configuration) { + super(); + this.keySet = configuration.getAttributeNames(); + } + + public boolean hasMoreElements() { + return cursor >= keySet.length; + } + + public Object nextElement() { + return keySet[cursor++]; + } + + } + + public CompareFilterDescriptor(IConfigurationElement config) { + fConfiguration = config; + fResourceBundle = new ResourceBundle() { + protected Object handleGetObject(String key) { + return fConfiguration.getAttribute(key); + } + + public Enumeration getKeys() { + return new ConfigurationKeysEnumeration(fConfiguration); + } + }; + + URL url = null; + String pluginId = fConfiguration.getContributor().getName(); + Bundle bundle = Platform.getBundle(pluginId); + if (bundle != null) { + String path = Utilities.getString(fResourceBundle, + FILTER_IMAGE_ATTRIBUTE, FILTER_IMAGE_ATTRIBUTE); + if (path != null) + url = FileLocator.find(bundle, new Path(path), null); + } + fImageDescriptor = (url == null) ? null : ImageDescriptor + .createFromURL(url); + } + + public ICompareFilter createCompareFilter() { + try { + return (ICompareFilter) fConfiguration + .createExecutableExtension(CLASS_ATTRIBUTE); + } catch (CoreException e) { + CompareUIPlugin.log(e); + } + return null; + } + + public String getFilterId() { + return fConfiguration.getAttribute(ID_ATTRIBUTE); + } + + public String getDefinitionId() { + return fConfiguration.getAttribute(DEFINITION_ID_ATTRIBUTE); + } + + public ResourceBundle getResourceBundle() { + return fResourceBundle; + } + + public ImageDescriptor getImageDescriptor() { + return fImageDescriptor; + } +} diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java index f4fe72b73..2a5814923 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ComparePreferencePage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -327,7 +327,7 @@ public class ComparePreferencePage extends PreferencePage implements IWorkbenchP new ModifyListener() { public void modifyText(ModifyEvent e) { String filters= fFilters.getText(); - String message= CompareFilter.validateResourceFilters(filters); + String message= CompareResourceFilter.validateResourceFilters(filters); setValid(message == null); setMessage(null); setErrorMessage(message); diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareResourceFilter.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareResourceFilter.java new file mode 100644 index 000000000..0e7090296 --- /dev/null +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareResourceFilter.java @@ -0,0 +1,399 @@ +/******************************************************************************* + * Copyright (c) 2000, 2013 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.compare.internal; + +import java.util.StringTokenizer; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IStatus; + +import com.ibm.icu.text.MessageFormat; + + +public class CompareResourceFilter { + private static final char[][] NO_CHAR_CHAR= new char[0][]; + + private char[][] fExtraResourceFileFilters; + private String[] fExtraResourceFolderFilters; + + + public CompareResourceFilter() { + // nothing to do + } + + /* + * Returns true if path matches filter, that is if path should be filtered. + */ + public boolean filter(String path0, boolean folder, boolean isArchive) { + if (!folder && fExtraResourceFileFilters != null) { + char[] name= path0.toCharArray(); + for (int i= 0, l= fExtraResourceFileFilters.length; i < l; i++) + if (match(fExtraResourceFileFilters[i], name, true)) + return true; + } + if (folder && fExtraResourceFolderFilters != null) { + for (int i= 0, l= fExtraResourceFolderFilters.length; i < l; i++) + if (fExtraResourceFolderFilters[i].equals(path0)) + return true; + } + return false; + } + + public static String validateResourceFilters(String text) { + IWorkspace workspace= ResourcesPlugin.getWorkspace(); + String[] filters= getTokens(text, ","); //$NON-NLS-1$ + for (int i= 0; i < filters.length; i++) { + String fileName= filters[i].replace('*', 'x'); + int resourceType= IResource.FILE; + int lastCharacter= fileName.length() - 1; + if (lastCharacter >= 0 && fileName.charAt(lastCharacter) == '/') { + fileName= fileName.substring(0, lastCharacter); + resourceType= IResource.FOLDER; + } + IStatus status= workspace.validateName(fileName, resourceType); + if (status.matches(IStatus.ERROR)) { + String format= Utilities.getString("ComparePreferencePage.filter.invalidsegment.error"); //$NON-NLS-1$ + return MessageFormat.format(format, new String[] { status.getMessage() } ); + } + } + return null; + } + + public void setFilters(String filterSequence) { + char[][] filters= filterSequence != null && filterSequence.length() > 0 + ? splitAndTrimOn(',', filterSequence.toCharArray()) + : null; + if (filters == null) { + fExtraResourceFileFilters= null; + fExtraResourceFolderFilters= null; + } else { + int fileCount= 0, folderCount= 0; + for (int i= 0, l= filters.length; i < l; i++) { + char[] f= filters[i]; + if (f.length == 0) + continue; + if (f[f.length - 1] == '/') + folderCount++; + else + fileCount++; + } + fExtraResourceFileFilters= new char[fileCount][]; + fExtraResourceFolderFilters= new String[folderCount]; + for (int i= 0, l= filters.length; i < l; i++) { + char[] f= filters[i]; + if (f.length == 0) + continue; + if (f[f.length - 1] == '/') + fExtraResourceFolderFilters[--folderCount]= new String(subarray(f, 0, f.length - 1)); + else + fExtraResourceFileFilters[--fileCount]= f; + } + } + } + + ///////// + + private static String[] getTokens(String text, String separator) { + StringTokenizer tok= new StringTokenizer(text, separator); + int nTokens= tok.countTokens(); + String[] res= new String[nTokens]; + for (int i= 0; i < res.length; i++) + res[i]= tok.nextToken().trim(); + return res; + } + + /** + * Answers true if the pattern matches the given name, false otherwise. + * This char[] pattern matching accepts wild-cards '*' and '?'. + * + * When not case sensitive, the pattern is assumed to already be + * lowercased, the name will be lowercased character per character as + * comparing. If name is null, the answer is false. If pattern is null, the + * answer is true if name is not null.

For example: + *
    + *
  1. + * + *
    +	 *  pattern = { '?', 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => true
    +	 * 
    + * + * + *
  2. + *
  3. + * + *
    +	 *  pattern = { '?', 'b', '?' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
    +	 * 
    + * + * + *
  4. + *
  5. + * + *
    +	 *  pattern = { 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
    +	 * 
    + * + * + *
  6. + *
+ * + * @param pattern + * the given pattern + * @param name + * the given name + * @param isCaseSensitive + * flag to know whether or not the matching should be case + * sensitive + * @return true if the pattern matches the given name, false otherwise + */ + private boolean match(char[] pattern, char[] name, boolean isCaseSensitive) { + if (name == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + return match(pattern, 0, pattern.length, name, 0, name.length, isCaseSensitive); + } + + /** + * Answers true if the a sub-pattern matches the subpart of the given name, + * false otherwise. char[] pattern matching, accepting wild-cards '*' and + * '?'. Can match only subset of name/pattern. end positions are + * non-inclusive. The subpattern is defined by the patternStart and + * pattternEnd positions. When not case sensitive, the pattern is assumed + * to already be lowercased, the name will be lowercased character per + * character as comparing.

For example: + *
    + *
  1. + * + *
    +	 *  pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 3 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 4 isCaseSensitive = true result => true
    +	 * 
    + * + * + *
  2. + *
  3. + * + *
    +	 *  pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 2 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 2 isCaseSensitive = true result => false
    +	 * 
    + * + * + *
  4. + *
+ * + * @param pattern + * the given pattern + * @param patternStart + * the given pattern start + * @param patternEnd + * the given pattern end + * @param name + * the given name + * @param nameStart + * the given name start + * @param nameEnd + * the given name end + * @param isCaseSensitive + * flag to know if the matching should be case sensitive + * @return true if the a sub-pattern matches the subpart of the given name, + * false otherwise + */ + private boolean match(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd, + boolean isCaseSensitive) { + if (name == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + int iPattern= patternStart; + int iName= nameStart; + if (patternEnd < 0) + patternEnd= pattern.length; + if (nameEnd < 0) + nameEnd= name.length; + /* check first segment */ + char patternChar= 0; + while ((iPattern < patternEnd) && (patternChar= pattern[iPattern]) != '*') { + if (iName == nameEnd) + return false; + if (patternChar != (isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) && patternChar != '?') { + return false; + } + iName++; + iPattern++; + } + /* check sequence of star+segment */ + int segmentStart; + if (patternChar == '*') { + segmentStart= ++iPattern; // skip star + } else { + segmentStart= 0; // force iName check + } + int prefixStart= iName; + checkSegment : while (iName < nameEnd) { + if (iPattern == patternEnd) { + iPattern= segmentStart; // mismatch - restart current segment + iName= ++prefixStart; + continue checkSegment; + } + /* segment is ending */ + if ((patternChar= pattern[iPattern]) == '*') { + segmentStart= ++iPattern; // skip start + if (segmentStart == patternEnd) { + return true; + } + prefixStart= iName; + continue checkSegment; + } + /* check current name character */ + if ((isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) != patternChar && patternChar != '?') { + iPattern= segmentStart; // mismatch - restart current segment + iName= ++prefixStart; + continue checkSegment; + } + iName++; + iPattern++; + } + return (segmentStart == patternEnd) || (iName == nameEnd && iPattern == patternEnd) + || (iPattern == patternEnd - 1 && pattern[iPattern] == '*'); + } + + /** + * Return a new array which is the split of the given array using the given + * divider and triming each subarray to remove whitespaces equals to ' '. + *

For example: + *
    + *
  1. + * + *
    +	 *  divider = 'b' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
    +	 * 
    + * + * + *
  2. + *
  3. + * + *
    +	 *  divider = 'c' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + * + * + *
  4. + *
  5. + * + *
    +	 *  divider = 'b' array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
    +	 * 
    + * + * + *
  6. + *
  7. + * + *
    +	 *  divider = 'c' array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + * + * + *
  8. + *
+ * + * @param divider + * the given divider + * @param array + * the given array + * @return a new array which is the split of the given array using the + * given divider and triming each subarray to remove whitespaces + * equals to ' ' + */ + private char[][] splitAndTrimOn(char divider, char[] array) { + int length= array == null ? 0 : array.length; + if (length == 0) + return NO_CHAR_CHAR; + int wordCount= 1; + for (int i= 0; i < length; i++) + if (array[i] == divider) + wordCount++; + char[][] split= new char[wordCount][]; + int last= 0, currentWord= 0; + for (int i= 0; i < length; i++) { + if (array[i] == divider) { + int start= last, end= i - 1; + while (start < i && array[start] == ' ') + start++; + while (end > start && array[end] == ' ') + end--; + split[currentWord]= new char[end - start + 1]; + System.arraycopy(array, start, split[currentWord++], 0, end - start + 1); + last= i + 1; + } + } + int start= last, end= length - 1; + while (start < length && array[start] == ' ') + start++; + while (end > start && array[end] == ' ') + end--; + split[currentWord]= new char[end - start + 1]; + System.arraycopy(array, start, split[currentWord++], 0, end - start + 1); + return split; + } + + /** + * Answers a new array which is a copy of the given array starting at the + * given start and ending at the given end. The given start is inclusive + * and the given end is exclusive. Answers null if start is greater than + * end, if start is lower than 0 or if end is greater than the length of + * the given array. If end equals -1, it is converted to the array length. + *

For example: + *
    + *
  1. + * + *
    +	 *  array = { 'a' , 'b' } start = 0 end = 1 result => { 'a' }
    +	 * 
    + * + * + *
  2. + *
  3. + * + *
    +	 *  array = { 'a', 'b' } start = 0 end = -1 result => { 'a' , 'b' }
    +	 * 
    + * + * + *
  4. + *
+ * + * @param array + * the given array + * @param start + * the given starting index + * @param end + * the given ending index + * @return a new array which is a copy of the given array starting at the + * given start and ending at the given end + * @exception NullPointerException + * if the given array is null + */ + private char[] subarray(char[] array, int start, int end) { + if (end == -1) + end= array.length; + if (start > end) + return null; + if (start < 0) + return null; + if (end > array.length) + return null; + char[] result= new char[end - start]; + System.arraycopy(array, start, result, 0, end - start); + return result; + } +} diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java index e1d7e210b..7dc68f7a3 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/CompareUIPlugin.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,6 +16,9 @@ import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; @@ -195,9 +198,12 @@ public final class CompareUIPlugin extends AbstractUIPlugin { private static final String STRUCTURE_CREATOR_ID_ATTRIBUTE= "structureCreatorId"; //$NON-NLS-1$ private static final String VIEWER_TAG= "viewer"; //$NON-NLS-1$ + private static final String FILTER_TAG = "filter"; //$NON-NLS-1$ private static final String STRUCTURE_MERGE_VIEWER_EXTENSION_POINT= "structureMergeViewers"; //$NON-NLS-1$ private static final String STRUCTURE_MERGE_VIEWER_ID_ATTRIBUTE= "structureMergeViewerId"; //$NON-NLS-1$ private static final String CONTENT_MERGE_VIEWER_EXTENSION_POINT= "contentMergeViewers"; //$NON-NLS-1$ + private static final String COMPARE_FILTER_EXTENTION_POINT = "compareFilters"; //$NON-NLS-1$ + private static final String COMPARE_FILTER_ID_ATTRIBUTE = "filterId"; //$NON-NLS-1$ private static final String CONTENT_MERGE_VIEWER_ID_ATTRIBUTE= "contentMergeViewerId"; //$NON-NLS-1$ private static final String CONTENT_VIEWER_EXTENSION_POINT= "contentViewers"; //$NON-NLS-1$ private static final String CONTENT_VIEWER_ID_ATTRIBUTE= "contentViewerId"; //$NON-NLS-1$ @@ -236,9 +242,10 @@ public final class CompareUIPlugin extends AbstractUIPlugin { private CompareRegistry fStructureMergeViewers= new CompareRegistry(); private CompareRegistry fContentViewers= new CompareRegistry(); private CompareRegistry fContentMergeViewers= new CompareRegistry(); + private CompareRegistry fCompareFilters = new CompareRegistry(); private Map fStructureViewerAliases; - private CompareFilter fFilter; + private CompareResourceFilter fFilter; private IPropertyChangeListener fPropertyChangeListener; /** @@ -391,7 +398,28 @@ public final class CompareUIPlugin extends AbstractUIPlugin { if (CONTENT_TYPE_BINDING.equals(element.getName())) fContentMergeViewers.createBinding(element, CONTENT_MERGE_VIEWER_ID_ATTRIBUTE); } - + + // collect all extensions that define the compare filter extension point + elements = registry.getConfigurationElementsFor(PLUGIN_ID, + COMPARE_FILTER_EXTENTION_POINT); + for (int i = 0; i < elements.length; i++) { + IConfigurationElement element = elements[i]; + String name = element.getName(); + if (!CONTENT_TYPE_BINDING.equals(name)) { + if (!FILTER_TAG.equals(name)) + logErrorMessage(Utilities.getFormattedString( + "CompareUIPlugin.unexpectedTag", name, FILTER_TAG)); //$NON-NLS-1$ + fCompareFilters.register(element, new CompareFilterDescriptor( + element)); + } + } + for (int i = 0; i < elements.length; i++) { + IConfigurationElement element = elements[i]; + if (CONTENT_TYPE_BINDING.equals(element.getName())) + fCompareFilters.createBinding(element, + COMPARE_FILTER_ID_ATTRIBUTE); + } + // collect all viewers which define the content viewer extension point elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_VIEWER_EXTENSION_POINT); for (int i= 0; i < elements.length; i++) { @@ -850,6 +878,114 @@ public final class CompareUIPlugin extends AbstractUIPlugin { return getViewer(descriptors[0], oldViewer, parent, configuration); } + public CompareFilterDescriptor[] findCompareFilters(Object in) { + Collection contentTypes = getContentTypes(in); + if (contentTypes == null) { + return new CompareFilterDescriptor[0]; + } + Set result = new LinkedHashSet(); + Iterator ctIterator = contentTypes.iterator(); + while (ctIterator.hasNext()) { + Object ct = ctIterator.next(); + if (ct instanceof IContentType) { + List list = fCompareFilters.searchAll((IContentType) ct); + if (list != null) + result.addAll(list); + } else if (ct instanceof String) { + List list = fCompareFilters.searchAll((String) ct); + if (list != null) + result.addAll(list); + } + } + + ArrayList list = new ArrayList(result); + Collections.sort(list, new Comparator() { + public int compare(Object left, Object right) { + return ((CompareFilterDescriptor) left) + .getFilterId() + .compareTo( + ((CompareFilterDescriptor) right).getFilterId()); + } + }); + + return (CompareFilterDescriptor[]) result + .toArray(new CompareFilterDescriptor[result.size()]); + } + + private Collection getContentTypes(Object in) { + Set result = new LinkedHashSet(); + if (in instanceof IStreamContentAccessor) { + String type = ITypedElement.TEXT_TYPE; + + if (in instanceof ITypedElement) { + ITypedElement tin = (ITypedElement) in; + + IContentType ct = getContentType(tin); + if (ct != null) { + result.add(ct); + } + + String ty = tin.getType(); + if (ty != null) + type = ty; + result.add(type); + } + return result; + } + + if (!(in instanceof ICompareInput)) + return null; + + ICompareInput input = (ICompareInput) in; + + IContentType ctype = getCommonType(input); + if (ctype != null) { + result.add(ctype); + } + + String[] types = getTypes(input); + String type = null; + if (isHomogenous(types)) + type = types[0]; + + if (ITypedElement.FOLDER_TYPE.equals(type)) + return null; + + if (type == null) { + int n = 0; + for (int i = 0; i < types.length; i++) + if (!ITypedElement.UNKNOWN_TYPE.equals(types[i])) { + n++; + if (type == null) + type = types[i]; // remember the first known type + } + if (n > 1) // don't use the type if there were more than one + type = null; + } + + if (type != null) { + result.add(type); + } + + // fallback + String leftType = guessType(input.getLeft()); + String rightType = guessType(input.getRight()); + + if (leftType != null || rightType != null) { + boolean right_text = rightType != null + && ITypedElement.TEXT_TYPE.equals(rightType); + boolean left_text = leftType != null + && ITypedElement.TEXT_TYPE.equals(leftType); + if ((rightType != null && !right_text) + || (leftType != null && !left_text)) { + result.add(BINARY_TYPE); + } + result.add(ITypedElement.TEXT_TYPE); + + } + return result; + } + public ViewerDescriptor[] findContentViewerDescriptor(Viewer oldViewer, Object in, CompareConfiguration cc) { Set result = new LinkedHashSet(); if (in instanceof IStreamContentAccessor) { @@ -1232,7 +1368,7 @@ public final class CompareUIPlugin extends AbstractUIPlugin { public boolean filter(String name, boolean isFolder, boolean isArchive) { if (fFilter == null) { - fFilter= new CompareFilter(); + fFilter= new CompareResourceFilter(); final IPreferenceStore ps= getPreferenceStore(); fFilter.setFilters(ps.getString(ComparePreferencePage.PATH_FILTER)); fPropertyChangeListener= new IPropertyChangeListener() { 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 index 8686bbbb3..c0a4e1abd 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/DocLineComparator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,9 +10,13 @@ *******************************************************************************/ package org.eclipse.compare.internal; -import org.eclipse.jface.text.*; +import org.eclipse.compare.ICompareFilter; import org.eclipse.compare.contentmergeviewer.ITokenComparator; import org.eclipse.compare.rangedifferencer.IRangeComparator; +import org.eclipse.core.internal.expressions.util.LRUCache; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; /** * Implements the IRangeComparator interface for lines in a document. @@ -29,6 +33,9 @@ public class DocLineComparator implements ITokenComparator { private int fLineCount; private int fLength; private boolean fIgnoreWhiteSpace; + private ICompareFilter[] fCompareFilters; + private char fContributor; + private LRUCache fCompareFilterCache; /** * Creates a DocLineComparator for the given document range. @@ -41,8 +48,47 @@ public class DocLineComparator implements ITokenComparator { */ public DocLineComparator(IDocument document, IRegion region, boolean ignoreWhiteSpace) { + this(document, region, ignoreWhiteSpace, null, '?'); + } + + /** + * Creates a DocLineComparator for the given document range. + * ignoreWhiteSpace controls whether comparing lines (in method + * rangesEqual) should ignore whitespace. Compare filters may be used + * to affect the detection of line differences. + * + * @param document + * the document from which the lines are taken + * @param region + * if non-null only lines within this range are + * taken + * @param ignoreWhiteSpace + * if true white space is ignored when comparing + * lines + * @param compareFilters + * the active compare filters for the compare + * @param contributor + * contributor of document + */ + public DocLineComparator(IDocument document, IRegion region, + boolean ignoreWhiteSpace, ICompareFilter[] compareFilters, + char contributor) { fDocument = document; fIgnoreWhiteSpace = ignoreWhiteSpace; + fCompareFilters = compareFilters; + fContributor = contributor; + + boolean cacheFilteredLines = false; + if (compareFilters != null && compareFilters.length > 0) { + cacheFilteredLines = true; + for (int i = 0; i < compareFilters.length; i++) { + if (!compareFilters[i].canCacheFilteredRegions()) { + cacheFilteredLines = false; + break; + } + } + } + fCompareFilterCache = (cacheFilteredLines) ? new LRUCache(1024) : null; fLineOffset = 0; if (region != null) { @@ -116,18 +162,18 @@ public class DocLineComparator implements ITokenComparator { DocLineComparator other= (DocLineComparator) otherComparator; if (fIgnoreWhiteSpace) { - String s1= extract(thisIndex); - String s2= other.extract(otherIndex); - //return s1.trim().equals(s2.trim()); - return compare(s1, s2); + String[] linesToCompare = extract(thisIndex, otherIndex, other, false); + return compare(linesToCompare[0], linesToCompare[1]); } 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); + if (fCompareFilters != null && fCompareFilters.length > 0) { + String[] linesToCompare = extract(thisIndex, otherIndex, other, true); + return linesToCompare[0].equals(linesToCompare[1]); + } else if (tlen == olen) { + String[] linesToCompare = extract(thisIndex, otherIndex, other, false); + return linesToCompare[0].equals(linesToCompare[1]); } } return false; @@ -149,24 +195,74 @@ public class DocLineComparator implements ITokenComparator { //---- private methods + private String[] extract(int thisIndex, int otherIndex, + DocLineComparator other, boolean includeSeparator) { + + String[] extracts = new String[2]; + if (fCompareFilters != null && fCompareFilters.length > 0) { + if (fCompareFilterCache != null + && other.fCompareFilterCache != null) { + extracts[0] = (String) fCompareFilterCache.get(new Integer( + thisIndex)); + if (extracts[0] == null) { + extracts[0] = Utilities.applyCompareFilters( + extract(thisIndex, includeSeparator), fContributor, + other.extract(otherIndex, includeSeparator), other.fContributor, + fCompareFilters); + fCompareFilterCache + .put(new Integer(thisIndex), extracts[0]); + } + + extracts[1] = (String) other.fCompareFilterCache + .get(new Integer(otherIndex)); + if (extracts[1] == null) { + extracts[1] = Utilities.applyCompareFilters( + other.extract(otherIndex, includeSeparator), other.fContributor, + extract(thisIndex, includeSeparator), fContributor, fCompareFilters); + other.fCompareFilterCache.put(new Integer(otherIndex), + extracts[1]); + } + } else { + String thisLine = extract(thisIndex, includeSeparator); + String otherLine = other.extract(otherIndex, includeSeparator); + extracts = new String[] { + Utilities.applyCompareFilters(thisLine, fContributor, + otherLine, other.fContributor, fCompareFilters), + Utilities.applyCompareFilters(otherLine, + other.fContributor, thisLine, fContributor, + fCompareFilters) }; + } + } else { + extracts = new String[] { extract(thisIndex, includeSeparator), + other.extract(otherIndex, includeSeparator) }; + } + return extracts; + } + /** - * Extract a single line from the underlying document without the line separator. + * Extract a single line from the underlying document. * * @param line the number of the line to extract + * @param whether to include the line separator * @return the contents of the line as a String */ - private String extract(int line) { + private String extract(int line, boolean includeSeparator) { if (line < fLineCount) { try { - IRegion r= fDocument.getLineInformation(fLineOffset + line); + if (includeSeparator) + return fDocument.get(fDocument.getLineOffset(line), + fDocument.getLineLength(line)); + + IRegion r = fDocument.getLineInformation(fLineOffset + line); return fDocument.get(r.getOffset(), r.getLength()); - } catch(BadLocationException e) { + + } catch (BadLocationException e) { // silently ignored } } return ""; //$NON-NLS-1$ } - + private boolean compare(String s1, String s2) { int l1= s1.length(); int l2= s2.length(); diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java index a790eaae6..bccb14d82 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/IMergeViewerTestAdapter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. + * Copyright (c) 2006, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -24,4 +24,11 @@ public interface IMergeViewerTestAdapter { * @return the document for that leg of the comparison */ public IDocument getDocument(char leg); + + /** + * Returns the number of changes in merge viewer + * + * @return the number of changes + */ + public int getChangesCount(); } 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 index ec99bd716..a06dc4767 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/MergeSourceViewer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -991,6 +991,10 @@ public class MergeSourceViewer implements ISelectionChangedListener, textActions.add(textEditorPropertyAction); } + public boolean removeTextAction(IAction textEditorPropertyAction) { + return textActions.remove(textEditorPropertyAction); + } + public void addAction(String id, IAction action) { fActions.put(id, action); } 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 index fe48c7219..6283b3190 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/Utilities.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -31,6 +31,7 @@ import java.util.ResourceBundle; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareUI; +import org.eclipse.compare.ICompareFilter; import org.eclipse.compare.IEncodedStreamContentAccessor; import org.eclipse.compare.ISharedDocumentAdapter; import org.eclipse.compare.IStreamContentAccessor; @@ -71,6 +72,7 @@ import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; @@ -140,6 +142,25 @@ public class Utilities { return dflt; } + /** + * Returns the active compare filters for the compare configuration + * + * @param cc + * @return the active compare filters + */ + public static ICompareFilter[] getCompareFilters(CompareConfiguration cc) { + if (cc != null) { + Object value = cc + .getProperty(ChangeCompareFilterPropertyAction.COMPARE_FILTERS); + if (value instanceof Map) { + Map filtersMap = (Map) value; + return (ICompareFilter[]) filtersMap.values().toArray( + new ICompareFilter[filtersMap.size()]); + } + } + return new ICompareFilter[0]; + } + public static void firePropertyChange(ListenerList listenerList, Object source, String property, Object old, Object newValue) { PropertyChangeEvent event= new PropertyChangeEvent(source, property, old, newValue); firePropertyChange(listenerList, event); @@ -912,4 +933,53 @@ public class Utilities { }); return result[0]; } + + /** + * Applies the compare filters to the lines of text taken from the specified + * contributors + * + * @param thisLine + * @param thisContributor + * @param otherLine + * @param otherContributor + * @param filters + * may be null + * @return returns the result of applying the filters to the line from the + * contributor + */ + public static String applyCompareFilters(String thisLine, + char thisContributor, String otherLine, char otherContributor, + ICompareFilter[] filters) { + IRegion[][] ignoredRegions = new IRegion[filters.length][]; + + HashMap input = new HashMap(4); + input.put(ICompareFilter.THIS_LINE, thisLine); + input.put(ICompareFilter.THIS_CONTRIBUTOR, new Character( + thisContributor)); + input.put(ICompareFilter.OTHER_LINE, otherLine); + input.put(ICompareFilter.OTHER_CONTRIBUTOR, new Character( + otherContributor)); + for (int i = 0; i < filters.length; i++) { + ignoredRegions[i] = filters[i].getFilteredRegions(input); + } + + boolean[] ignored = new boolean[thisLine.length()]; + for (int j = 0; j < ignoredRegions.length; j++) { + if (ignoredRegions[j] != null) { + for (int k = 0; k < ignoredRegions[j].length; k++) { + if (ignoredRegions[j][k] != null) + for (int l = 0; l < ignoredRegions[j][k].getLength(); l++) { + ignored[ignoredRegions[j][k].getOffset() + l] = true; + } + } + } + } + StringBuffer buffer = new StringBuffer(thisLine.length()); + for (int i = 0; i < ignored.length; i++) { + if (!ignored[i]) { + buffer.append(thisLine.charAt(i)); + } + } + return buffer.toString(); + } } diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java index 81a28fe4d..c961c8028 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/merge/DocumentMerger.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2012 IBM Corporation and others. + * Copyright (c) 2007, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -37,6 +37,7 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.IProgressService; import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.ICompareFilter; import org.eclipse.compare.contentmergeviewer.ITokenComparator; import org.eclipse.compare.internal.CompareContentViewerSwitchingPane; import org.eclipse.compare.internal.CompareMessages; @@ -422,12 +423,19 @@ public class DocumentMerger { resetPositions(aDoc); boolean ignoreWhiteSpace= isIgnoreWhitespace(); - - DocLineComparator sright= new DocLineComparator(rDoc, toRegion(rRegion), ignoreWhiteSpace); - DocLineComparator sleft= new DocLineComparator(lDoc, toRegion(lRegion), ignoreWhiteSpace); - DocLineComparator sancestor= null; + ICompareFilter[] compareFilters = getCompareFilters(); + + DocLineComparator sright = new DocLineComparator(rDoc, + toRegion(rRegion), ignoreWhiteSpace, compareFilters, + MergeViewerContentProvider.RIGHT_CONTRIBUTOR); + DocLineComparator sleft = new DocLineComparator(lDoc, + toRegion(lRegion), ignoreWhiteSpace, compareFilters, + MergeViewerContentProvider.LEFT_CONTRIBUTOR); + DocLineComparator sancestor = null; if (aDoc != null) { - sancestor= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace); + sancestor = new DocLineComparator(aDoc, toRegion(aRegion), + ignoreWhiteSpace, compareFilters, + MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR); /*if (isPatchHunk()) { if (isHunkOnLeft()) { sright= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace); @@ -594,12 +602,13 @@ public class DocumentMerger { aDoc= getDocument(MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR); boolean ignoreWhiteSpace= isIgnoreWhitespace(); - - DocLineComparator sright= new DocLineComparator(rDoc, toRegion(rRegion), ignoreWhiteSpace); - DocLineComparator sleft= new DocLineComparator(lDoc, toRegion(lRegion), ignoreWhiteSpace); + ICompareFilter[] compareFilters = getCompareFilters(); + + DocLineComparator sright= new DocLineComparator(rDoc, toRegion(rRegion), ignoreWhiteSpace, compareFilters, MergeViewerContentProvider.RIGHT_CONTRIBUTOR); + DocLineComparator sleft= new DocLineComparator(lDoc, toRegion(lRegion), ignoreWhiteSpace, compareFilters, MergeViewerContentProvider.LEFT_CONTRIBUTOR); DocLineComparator sancestor= null; if (aDoc != null) - sancestor= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace); + sancestor= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace, compareFilters, MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR); final Object[] result= new Object[1]; final DocLineComparator sa= sancestor, sl= sleft, sr= sright; @@ -681,7 +690,11 @@ public class DocumentMerger { private boolean isIgnoreWhitespace() { return Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false); } - + + private ICompareFilter[] getCompareFilters() { + return Utilities.getCompareFilters(getCompareConfiguration()); + } + private boolean isCappingDisabled() { return CompareUIPlugin.getDefault().getPreferenceStore().getBoolean(ComparePreferencePage.CAPPING_DISABLED); } 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 index 60fa61b97..3339b3425 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/Differencer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -22,6 +22,7 @@ import java.util.Set; import org.eclipse.compare.IStreamContentAccessor; import org.eclipse.compare.ITypedElement; +import org.eclipse.compare.internal.MergeViewerContentProvider; import org.eclipse.compare.internal.Utilities; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; @@ -360,7 +361,7 @@ public class Differencer { description= LEFT | ADDITION; } else { description= CONFLICTING | ADDITION; - if (contentsEqual(left, right)) + if (contentsEqual(left, MergeViewerContentProvider.LEFT_CONTRIBUTOR, right, MergeViewerContentProvider.RIGHT_CONTRIBUTOR)) description|= PSEUDO_CONFLICT; } } @@ -369,20 +370,20 @@ public class Differencer { if (right == null) { description= CONFLICTING | DELETION | PSEUDO_CONFLICT; } else { - if (contentsEqual(ancestor, right)) + if (contentsEqual(ancestor, MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR, right, MergeViewerContentProvider.RIGHT_CONTRIBUTOR)) description= LEFT | DELETION; else description= CONFLICTING | CHANGE; } } else { if (right == null) { - if (contentsEqual(ancestor, left)) + if (contentsEqual(ancestor, MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR, left, MergeViewerContentProvider.LEFT_CONTRIBUTOR)) description= RIGHT | DELETION; else description= CONFLICTING | CHANGE; } else { - boolean ay= contentsEqual(ancestor, left); - boolean am= contentsEqual(ancestor, right); + boolean ay= contentsEqual(ancestor, MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR, left, MergeViewerContentProvider.LEFT_CONTRIBUTOR); + boolean am= contentsEqual(ancestor, MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR, right, MergeViewerContentProvider.RIGHT_CONTRIBUTOR); if (ay && am) { // empty @@ -392,7 +393,7 @@ public class Differencer { description= LEFT | CHANGE; } else { description= CONFLICTING | CHANGE; - if (contentsEqual(left, right)) + if (contentsEqual(left, MergeViewerContentProvider.LEFT_CONTRIBUTOR, right, MergeViewerContentProvider.RIGHT_CONTRIBUTOR)) description|= PSEUDO_CONFLICT; } } @@ -410,7 +411,7 @@ public class Differencer { if (right == null) { description= DELETION; } else { - if (! contentsEqual(left, right)) + if (! contentsEqual(left, MergeViewerContentProvider.LEFT_CONTRIBUTOR, right, MergeViewerContentProvider.RIGHT_CONTRIBUTOR)) description= CHANGE; } } @@ -418,7 +419,32 @@ public class Differencer { return description; } - + + /** + * Performs a content compare on the two given inputs. + *

+ * The Differencer implementation returns + * contentsEqual(Object input1, Object input2). Subclasses may + * override to implement a different content compare on the given inputs. + *

+ * + * @param input1 + * first input to contents compare + * @param contributor1 + * the contributor of input1 + * @param input2 + * second input to contents compare + * @param contributor2 + * the contributor of input2 + * @return true if content is equal + * @noreference This method is not intended to be referenced by clients. + * @since 3.6 + */ + protected boolean contentsEqual(Object input1, char contributor1, + Object input2, char contributor2) { + return contentsEqual(input1, input2); + } + /** * Performs a content compare on the two given inputs. *

diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java index 799afba98..f978ef0a2 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2009 IBM Corporation and others. + * Copyright (c) 2006, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,9 +10,13 @@ *******************************************************************************/ package org.eclipse.compare.structuremergeviewer; +import java.io.BufferedReader; +import java.io.StringReader; import java.io.UnsupportedEncodingException; +import java.util.List; import org.eclipse.compare.CompareUI; +import org.eclipse.compare.ICompareFilter; import org.eclipse.compare.IEditableContent; import org.eclipse.compare.IEncodedStreamContentAccessor; import org.eclipse.compare.ISharedDocumentAdapter; @@ -22,6 +26,7 @@ import org.eclipse.compare.SharedDocumentAdapter; import org.eclipse.compare.contentmergeviewer.IDocumentRange; import org.eclipse.compare.internal.CompareUIPlugin; import org.eclipse.compare.internal.Utilities; +import org.eclipse.compare.internal.patch.LineReader; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -430,4 +435,90 @@ public abstract class StructureCreator implements IStructureCreator2 { } return null; } + + /** + * Returns true if the two nodes are equal for comparison purposes. If + * compareFilters is not empty, the filters are applied to each + * line of each node's text representation. + * + * @param node1 + * @param contributor1 + * either 'A', 'L', or 'R' for ancestor, left or right + * contributor + * @param node2 + * @param contributor2 + * either 'A', 'L', or 'R' for ancestor, left or right + * contributor + * @param ignoreWhitespace + * if true whitespace characters will be ignored + * when determining equality. Note: Will bypass any custom ignore + * whitespace behaviors contributed through implementations of + * org.eclipse.compare.structuremergeviewer.IStructureCreator.getContents() + * @param compareFilters + * the filters used to customize the comparison of lines of text. + * @return whether the two nodes are equal for comparison purposes + * @noreference This method is not intended to be referenced by clients. + * @since 3.6 + */ + public boolean contentsEquals(Object node1, char contributor1, + Object node2, char contributor2, boolean ignoreWhitespace, + ICompareFilter[] compareFilters) { + + List lines1 = LineReader.readLines(new BufferedReader(new StringReader( + getContents(node1, false)))); + List lines2 = LineReader.readLines(new BufferedReader(new StringReader( + getContents(node2, false)))); + + StringBuffer buffer1 = new StringBuffer(); + StringBuffer buffer2 = new StringBuffer(); + + int maxLines = Math.max(lines1.size(), lines2.size()); + for (int i = 0; i < maxLines; i++) { + String s1 = lines1.size() > i ? (String) lines1.get(i) : ""; //$NON-NLS-1$ + String s2 = lines2.size() > i ? (String) lines2.get(i) : ""; //$NON-NLS-1$ + + if (compareFilters != null && compareFilters.length > 0) { + s1 = Utilities.applyCompareFilters(s1, contributor1, s2, + contributor2, compareFilters); + s2 = Utilities.applyCompareFilters(s2, contributor2, s1, + contributor1, compareFilters); + } + buffer1.append(s1); + buffer2.append(s2); + } + if (ignoreWhitespace) { + int l1 = buffer1.length(); + int l2 = buffer2.length(); + int c1 = 0, c2 = 0; + int i1 = 0, i2 = 0; + + while (c1 != -1) { + + c1 = -1; + while (i1 < l1) { + char c = buffer1.charAt(i1++); + if (!Character.isWhitespace(c)) { + c1 = c; + break; + } + } + + c2 = -1; + while (i2 < l2) { + char c = buffer2.charAt(i2++); + if (!Character.isWhitespace(c)) { + c2 = c; + break; + } + } + + if (c1 != c2) + return false; + } + } else if (!buffer1.toString().equals(buffer2.toString())) { + return false; + } + + return true; + } } 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 index 4b8700659..b51e56254 100644 --- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java +++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureDiffViewer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -475,8 +475,10 @@ public class StructureDiffViewer extends DiffTreeViewer { if (fDifferencer == null) fDifferencer= new Differencer() { - protected boolean contentsEqual(Object o1, Object o2) { - return StructureDiffViewer.this.contentsEqual(o1, o2); + protected boolean contentsEqual(Object o1, + char contributor1, Object o2, char contributor2) { + return StructureDiffViewer.this.contentsEqual(o1, + contributor1, o2, contributor2); } protected Object visit(Object data, int result, Object ancestor, Object left, Object right) { Object o= super.visit(data, result, ancestor, left, right); @@ -608,11 +610,24 @@ public class StructureDiffViewer extends DiffTreeViewer { * Called from the difference engine. * Returns null if no structure creator has been set. */ - private boolean contentsEqual(Object o1, Object o2) { + private boolean contentsEqual(Object o1, char contributor1, Object o2, + char contributor2) { if (fStructureCreator != null) { - boolean ignoreWhiteSpace= Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false); - String s1= fStructureCreator.getContents(o1, ignoreWhiteSpace); - String s2= fStructureCreator.getContents(o2, ignoreWhiteSpace); + boolean ignoreWhiteSpace = Utilities.getBoolean( + getCompareConfiguration(), + CompareConfiguration.IGNORE_WHITESPACE, false); + ICompareFilter[] compareFilters = Utilities + .getCompareFilters(getCompareConfiguration()); + String s1, s2; + if (compareFilters != null && compareFilters.length > 0 + && fStructureCreator instanceof StructureCreator) { + return ((StructureCreator) fStructureCreator).contentsEquals( + o1, contributor1, o2, contributor2, ignoreWhiteSpace, + compareFilters); + } else { + s1 = fStructureCreator.getContents(o1, ignoreWhiteSpace); + s2 = fStructureCreator.getContents(o2, ignoreWhiteSpace); + } if (s1 == null || s2 == null) return false; return s1.equals(s2); @@ -630,6 +645,12 @@ public class StructureDiffViewer extends DiffTreeViewer { String key= event.getProperty(); if (key.equals(CompareConfiguration.IGNORE_WHITESPACE)) { diff(); + } else if (key + .equals(ChangeCompareFilterPropertyAction.COMPARE_FILTERS) + && getCompareConfiguration() + .getProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS_INITIALIZING) == null) { + diff(); } else if (key.equals("ANCESTOR_STRUCTURE_REFRESH")) { //$NON-NLS-1$ fAncestorStructure.refresh(new NullProgressMonitor()); diff(); diff --git a/bundles/org.eclipse.compare/plugin.properties b/bundles/org.eclipse.compare/plugin.properties index 11d1bdd2d..2cd5f8c39 100644 --- a/bundles/org.eclipse.compare/plugin.properties +++ b/bundles/org.eclipse.compare/plugin.properties @@ -22,6 +22,7 @@ structureCreators= Structure Creator structureMergeViewers= Structure Merge Viewer contentMergeViewers= Content Merge Viewer contentViewers= Content Viewer +compareFilters= Compare Filters # # CompareUIPlugin diff --git a/bundles/org.eclipse.compare/plugin.xml b/bundles/org.eclipse.compare/plugin.xml index 751f46aef..f3acf4641 100644 --- a/bundles/org.eclipse.compare/plugin.xml +++ b/bundles/org.eclipse.compare/plugin.xml @@ -19,6 +19,7 @@ + + + + + + + + + This extension point is for internal use only.<br/> +This extension point allows a plug-in to register a compare filter +for specific content types. The filter will be exposed as a +toggle action in the compare viewer and can be used to customize how +differences are calculated when comparing documents as well as affect the +calculation of structural differences. The extension point must +implement the interface <samp>org.eclipse.compare.ICompareFilter</samp>. +For the filter to affect the calculation of structural differences, the structure creator must extend +<samp>org.eclipse.compare.structuremergeviewer.StructureCreator</samp>. + + + + + + + + + + Internal use only + + + + + + + + + + + a fully qualified identifier of the target extension point + + + + + + + an optional identifier of the extension instance + + + + + + + an optional name of the extension instance + + + + + + + + + + + + + Internal use only + + + + + + + A unique identifier that can be used to reference the filter. It will also be used as the action id for the toggle action added to the compare viewer + + + + + + + The command id of the toggle action added to the compare viewer + + + + + + + + + + a comma separated list of file extensions e.g. "java, txt" + + + + + + + Implementation of ICompareFilter. + + + + + + + + + + A translatable label that will be used in the UI for this filter. + + + + + + + + + + A translatable label that will be used in the UI for this filter. + + + + + + + + + + A translatable label that will be used in the UI for this filter. + + + + + + + + + + A translatable label that will be used in the UI for this filter. + + + + + + + + + + A translatable label that will be used in the UI for this filter. + + + + + + + + + + A translatable label that will be used in the UI for this filter. + + + + + + + + + + A translatable label that will be used in the UI for this filter. + + + + + + + + + + An image that will be used in the UI for this filter. + + + + + + + + + + Internal use only.<br/> +A <code>contentTypeBinding</code> binds a compare filter to a content type. + + + + + + + The id of a content type defined using the <code>org.eclipse.core.contenttype.contentTypes</code> extension point. + + + + + + + + + + The id of a filter defined using the <code>filter</code> element of this extension point (i.e. <code>org.eclipse.compare.compareFilters</code>) + + + + + + + + + + + + + + + [Enter the first release in which this extension point appears.] + + + + + + + + + The following is an example of a compare filter +for example files (extensions ".example" and ".example2") and +the text content type: +<p> +<pre> +<extension + point="org.eclipse.compare.compareFilters"> + <filter + id="com.example.IgnorePrefixId" + class="com.example.IgnorePrefix" + extensions="example,example2" + definitionId="com.example.IgnorePrefixCommand" + filter.description="Ignore Columns 1-6" + filter.label="Ignore Prefix" + filter.image="/icons/ignorePrefix.gif" + filter.tooltip="Ignore Prefix"> + </filter> + <contentTypeBinding + contentTypeId="org.eclipse.core.runtime.text" + filterId="com.example.IgnorePrefixId"> + </contentTypeBinding> + </extension> +</pre> +</p> + + + + + + + + + The contributed class must implement <samp>org.eclipse.compare.ICompareFilter</samp>. For the filter to affect the calculation of structural differences, the structure creator must extend <samp>org.eclipse.compare.structuremergeviewer.StructureCreator</samp>. + + + + + + + + + [Enter information about supplied implementation of this extension point.] + + + + + + + + + Copyright (c) 2013 IBM Corporation and others.<br> +All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + + + + diff --git a/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF index 15b8c032a..e7a39c5a9 100644 --- a/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.compare.tests;singleton:=true -Bundle-Version: 3.5.400.qualifier +Bundle-Version: 3.6.0.qualifier Bundle-ClassPath: comparetests.jar Require-Bundle: org.junit, org.eclipse.compare, diff --git a/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/AllTests.java b/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/AllTests.java index a47c61fc2..061de77e5 100644 --- a/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/AllTests.java +++ b/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/AllTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -36,6 +36,7 @@ public class AllTests { suite.addTestSuite(PatchUITest.class); suite.addTestSuite(RangeDifferencerThreeWayDiffTest.class); suite.addTestSuite(CompareUIPluginTest.class); + suite.addTestSuite(StructureCreatorTest.class); // $JUnit-END$ return suite; } diff --git a/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/DocLineComparatorTest.java b/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/DocLineComparatorTest.java index cf963c76b..5c8b7c6f7 100644 --- a/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/DocLineComparatorTest.java +++ b/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/DocLineComparatorTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,13 +10,17 @@ *******************************************************************************/ package org.eclipse.compare.tests; +import java.util.HashMap; + import junit.framework.Assert; import junit.framework.TestCase; +import org.eclipse.compare.ICompareFilter; import org.eclipse.compare.internal.DocLineComparator; import org.eclipse.compare.rangedifferencer.IRangeComparator; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; public class DocLineComparatorTest extends TestCase { @@ -59,6 +63,212 @@ public class DocLineComparatorTest extends TestCase { Assert.assertTrue(comp1.rangesEqual(0, comp2, 0)); } + public void testOneCompareFilter() { + IDocument doc1 = new Document(); + doc1.set("if (s.strip))"); //$NON-NLS-1$ + + IDocument doc2 = new Document(); + doc2.set("IF (S.stRIp))"); //$NON-NLS-1$ + + IDocument doc3 = new Document(); + doc3.set("IF (S.stRIp)) "); //$NON-NLS-1$ + + ICompareFilter filter = new ICompareFilter() { + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + // EMPTY + } + + public IRegion[] getFilteredRegions(HashMap lineComparison) { + return new IRegion[] { new Region(0, 2), new Region(4, 1), + new Region(8, 2) }; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return false; + } + }; + + IRangeComparator comp1 = new DocLineComparator(doc1, null, false, + new ICompareFilter[] { filter }, 'L'); + IRangeComparator comp2 = new DocLineComparator(doc2, null, false, + new ICompareFilter[] { filter }, 'R'); + Assert.assertTrue(comp1.rangesEqual(0, comp2, 0)); + + IRangeComparator comp3 = new DocLineComparator(doc1, null, true, + new ICompareFilter[] { filter }, 'L'); + IRangeComparator comp4 = new DocLineComparator(doc3, null, true, + new ICompareFilter[] { filter }, 'R'); + Assert.assertTrue(comp3.rangesEqual(0, comp4, 0)); + + IRangeComparator comp5 = new DocLineComparator(doc1, null, false, + new ICompareFilter[] { filter }, 'L'); + IRangeComparator comp6 = new DocLineComparator(doc3, null, false, + new ICompareFilter[] { filter }, 'R'); + Assert.assertFalse(comp5.rangesEqual(0, comp6, 0)); + } + + public void testMultipleCompareFilters() { + IDocument doc1 = new Document(); + doc1.set("if (s.strip))"); //$NON-NLS-1$ + + IDocument doc2 = new Document(); + doc2.set("IF (S.stRIp))"); //$NON-NLS-1$ + + ICompareFilter filter1 = new ICompareFilter() { + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + // EMPTY + } + + public IRegion[] getFilteredRegions(HashMap lineComparison) { + return new IRegion[] { new Region(0, 2) }; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return false; + } + }; + + ICompareFilter filter2 = new ICompareFilter() { + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + // EMPTY + } + + public IRegion[] getFilteredRegions(HashMap lineComparison) { + return new IRegion[] { new Region(4, 1) }; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return false; + } + }; + + ICompareFilter filter3 = new ICompareFilter() { + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + // EMPTY + } + + public IRegion[] getFilteredRegions(HashMap lineComparison) { + return new IRegion[] { new Region(8, 2) }; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return false; + } + }; + + IRangeComparator comp1 = new DocLineComparator(doc1, null, false, + new ICompareFilter[] { filter1, filter2, filter3 }, 'L'); + IRangeComparator comp2 = new DocLineComparator(doc2, null, false, + new ICompareFilter[] { filter1, filter2, filter3 }, 'R'); + Assert.assertTrue(comp1.rangesEqual(0, comp2, 0)); + + IRangeComparator comp3 = new DocLineComparator(doc1, null, false, + new ICompareFilter[] { filter2, filter3 }, 'L'); + IRangeComparator comp4 = new DocLineComparator(doc2, null, false, + new ICompareFilter[] { filter2, filter3 }, 'R'); + Assert.assertFalse(comp3.rangesEqual(0, comp4, 0)); + } + + public void testWhitespace() { + IDocument[] docs = new IDocument[6]; + docs[0] = new Document(); + docs[1] = new Document(); + docs[2] = new Document(); + docs[3] = new Document(); + docs[4] = new Document(); + docs[5] = new Document(); + + docs[0].set("if (s.strip))\r\n");//$NON-NLS-1$ + docs[1].set("if (s.strip))\n"); //$NON-NLS-1$ + docs[2].set("if (s .strip))\n"); //$NON-NLS-1$ + docs[3].set("if (s.str ip))\r"); //$NON-NLS-1$ + docs[4].set("if (s.strip))"); //$NON-NLS-1$ + docs[5].set("if (s.stri p))"); //$NON-NLS-1$ + + ICompareFilter[][] filters = new ICompareFilter[3][]; + filters[0] = null; + filters[1] = new ICompareFilter[]{ + new ICompareFilter() { + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + // EMPTY + } + + public IRegion[] getFilteredRegions(HashMap lineComparison) { + return new IRegion[] { new Region(0, 2) }; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return true; // cache-able + } + } + }; + + filters[2] = new ICompareFilter[]{ + new ICompareFilter() { + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + // EMPTY + } + + public IRegion[] getFilteredRegions(HashMap lineComparison) { + return new IRegion[] { new Region(0, 2) }; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return false; // not cache-able + } + } + }; + + IRangeComparator l, r; + for (int i=0;i -1) + return new IRegion[] { new Region(index, 1) }; + return null; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return true; // cache-able + } + } }; + + filters[2] = new ICompareFilter[] { new ICompareFilter() { + + public void setInput(Object input, Object ancestor, Object left, + Object right) { + // EMPTY + } + + public IRegion[] getFilteredRegions(HashMap lineComparison) { + String line = lineComparison.get(ICompareFilter.THIS_LINE) + .toString(); + int index = line.indexOf("E"); + if (index > -1) + return new IRegion[] { new Region(index, 1) }; + return null; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return false; // not cache-able + } + } }; + + StructureCreator creator = new StructureCreator() { + private Pattern whitespace = Pattern.compile("\\s+"); + private Matcher matcher = null; + + public String getName() { + return "NAME"; + } + + public String getContents(Object node, boolean ignoreWhitespace) { + DocumentRangeNode drn = (DocumentRangeNode) node; + String retval = null; + try { + retval = drn.getDocument().get(drn.getRange().getOffset(), + drn.getRange().getLength()); + if (ignoreWhitespace) { + if (matcher == null) + matcher = whitespace.matcher(retval); + else + matcher.reset(retval); + retval = matcher.replaceAll(""); + } + } catch (BadLocationException ble) { + assertNull(ble); + } + return retval; + } + + protected IStructureComparator createStructureComparator( + Object element, IDocument document, + ISharedDocumentAdapter sharedDocumentAdapter, + IProgressMonitor monitor) throws CoreException { + return new DocumentRangeNode(1, "ID", document, 0, + document.getLength()); + } + + }; + DocumentRangeNode l, r; + for (int i = 0; i < docs.length; i++) + for (int j = i + 1; j < docs.length; j++) + for (int k = 0; k < filters.length; k++) { + + l = new DocumentRangeNode(1, "ID", docs[i], 0, + docs[i].getLength()); + r = new DocumentRangeNode(1, "ID", docs[j], 0, + docs[j].getLength()); + creator.contentsEquals(l, 'L', r, 'R', true, filters[k]); + Assert.assertFalse(creator.contentsEquals(l, 'L', r, 'R', + false, filters[k])); + Assert.assertTrue(creator.contentsEquals(l, 'L', r, 'R', + true, filters[k])); + } + } +} \ No newline at end of file diff --git a/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/TextMergeViewerTest.java b/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/TextMergeViewerTest.java index 608e44743..171d4ac68 100644 --- a/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/TextMergeViewerTest.java +++ b/tests/org.eclipse.compare.tests/src/org/eclipse/compare/tests/TextMergeViewerTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. + * Copyright (c) 2006, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,21 +10,36 @@ *******************************************************************************/ package org.eclipse.compare.tests; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; import junit.framework.TestCase; -import org.eclipse.compare.*; +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.ICompareFilter; +import org.eclipse.compare.IEditableContent; +import org.eclipse.compare.IStreamContentAccessor; +import org.eclipse.compare.ITypedElement; import org.eclipse.compare.contentmergeviewer.TextMergeViewer; -import org.eclipse.compare.internal.*; +import org.eclipse.compare.internal.ChangeCompareFilterPropertyAction; +import org.eclipse.compare.internal.IMergeViewerTestAdapter; +import org.eclipse.compare.internal.MergeViewerContentProvider; +import org.eclipse.compare.internal.Utilities; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.compare.structuremergeviewer.Differencer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.*; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; public class TextMergeViewerTest extends TestCase { @@ -157,6 +172,11 @@ public class TextMergeViewerTest extends TestCase { public TestMergeViewer(Composite parent) { super(parent, new CompareConfiguration()); } + + public TestMergeViewer(Composite parent, CompareConfiguration cc) { + super(parent, cc); + } + public void copy(boolean leftToRight) { super.copy(leftToRight); } @@ -185,11 +205,16 @@ public class TextMergeViewerTest extends TestCase { } private void runInDialog(Object input, Runnable runnable) throws Exception { + runInDialog(input, runnable, new CompareConfiguration()); + } + + private void runInDialog(Object input, Runnable runnable, + final CompareConfiguration cc) throws Exception { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); Dialog dialog = new Dialog(shell) { protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); - viewer = new TestMergeViewer(composite); + viewer = new TestMergeViewer(composite, cc); return composite; } }; @@ -354,5 +379,86 @@ public class TextMergeViewerTest extends TestCase { assertEquals(newText, ((EditableTestElement)testNode.getRight()).getContentsAsString()); } + public void testCompareFilter() throws Exception { + DiffNode parentNode = new DiffNode(new ParentTestElement(), + new ParentTestElement()); + + final String leftString = "HI there"; + final String rightString = "hi there"; + final EditableTestElement leftElement = new EditableTestElement( + leftString.getBytes()); + final EditableTestElement rightElement = new EditableTestElement( + rightString.getBytes()); + DiffNode testNode = new DiffNode(parentNode, Differencer.CHANGE, null, + leftElement, rightElement); + final CompareConfiguration cc = new CompareConfiguration(); + runInDialog(testNode, new Runnable() { + public void run() { + Object adapter = viewer + .getAdapter(IMergeViewerTestAdapter.class); + if (adapter instanceof IMergeViewerTestAdapter) { + IMergeViewerTestAdapter ta = (IMergeViewerTestAdapter) adapter; + assertEquals(ta.getChangesCount(), 1); + + Map filters = new HashMap(); + filters.put("filter.id", new ICompareFilter() { + public void setInput(Object input, Object ancestor, + Object left, Object right) { + assertTrue(leftElement == left); + assertTrue(rightElement == right); + } + + public IRegion[] getFilteredRegions( + HashMap lineComparison) { + Object thisLine = lineComparison.get(THIS_LINE); + Object thisContributor = lineComparison + .get(THIS_CONTRIBUTOR); + Object otherLine = lineComparison.get(OTHER_LINE); + Object otherContributor = lineComparison + .get(OTHER_CONTRIBUTOR); + if (thisContributor.equals(new Character('L'))) { + assertEquals(thisLine, leftString); + assertEquals(otherContributor, new Character( + 'R')); + assertEquals(otherLine, rightString); + } else { + assertEquals(thisContributor, + new Character('R')); + assertEquals(thisLine, rightString); + assertEquals(otherContributor, new Character( + 'L')); + assertEquals(otherLine, leftString); + } + + if (thisContributor.equals(new Character('L'))) + return new IRegion[] { new Region(0, 1), + new Region(1, 1) }; + + return new IRegion[] { new Region(0, 2) }; + } + + public boolean isEnabledInitially() { + return false; + } + + public boolean canCacheFilteredRegions() { + return true; + } + + }); + + cc.setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS, + filters); + assertEquals(ta.getChangesCount(), 0); + + cc.setProperty( + ChangeCompareFilterPropertyAction.COMPARE_FILTERS, + null); + assertEquals(ta.getChangesCount(), 1); + } + } + }, cc); + } } -- cgit v1.2.3