Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip Langer2017-11-21 11:22:23 -0500
committerLaurent Goubet2018-05-01 06:36:20 -0400
commit3db650c128b5b9ff8d840b8f2a98fd679cecee39 (patch)
tree7880ad4adbcc767bd202137caf4e32bc21d85635
parente991effe0dcdf2ac503cc1c8afc9d58a76afec6e (diff)
downloadorg.eclipse.emf.compare-3db650c128b5b9ff8d840b8f2a98fd679cecee39.tar.gz
org.eclipse.emf.compare-3db650c128b5b9ff8d840b8f2a98fd679cecee39.tar.xz
org.eclipse.emf.compare-3db650c128b5b9ff8d840b8f2a98fd679cecee39.zip
[508526] Improve text compare
The viewer should show the text for the resources of the input (i.e., any resource of the logical model) rather than just the resources initially compared. It should also properly manage the dirty state of the overall editor, but also including showing which side is dirty. Ideally it should be able to show a preview of the resource as it would be if the user saved with the current state of the processed diffs. It's also ideal if the objects of the input are actually selected in the source viewers. Bug: 508526 Change-Id: Ib35cb9b23399cda672ec38220095d908eba0ce3e Signed-off-by: Philip Langer <planger@eclipsesource.com>
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/META-INF/MANIFEST.MF3
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/show_preview.gifbin0 -> 382 bytes
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/ForwardingCompareConfiguration.java12
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java1
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/TextFallbackCompareViewerCreator.java537
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInput.java212
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputData.java384
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputLabelProvider.java298
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewer.java727
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewerContentProvider.java172
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/ide_ui_messages.properties11
-rw-r--r--plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java14
-rw-r--r--plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java78
-rw-r--r--plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/contentmergeviewer/accessor/impl/ResourceStreamAccessorImpl.java5
14 files changed, 1908 insertions, 546 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare.ide.ui/META-INF/MANIFEST.MF
index acb87868a..d3d56c426 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.emf.compare.ide.ui/META-INF/MANIFEST.MF
@@ -20,7 +20,8 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.team.core;bundle-version="3.5.0",
org.eclipse.emf.ecore.xmi;bundle-version="2.5.0",
org.eclipse.team.ui;bundle-version="3.5.0",
- org.eclipse.emf.compare.rcp;bundle-version="2.1.0"
+ org.eclipse.emf.compare.rcp;bundle-version="2.1.0",
+ org.eclipse.ui.workbench.texteditor;bundle-version="3.5.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Bundle-Localization: plugin
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/show_preview.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/show_preview.gif
new file mode 100644
index 000000000..6c6504d5a
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/show_preview.gif
Binary files differ
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/ForwardingCompareConfiguration.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/ForwardingCompareConfiguration.java
index 66067976d..9375ac3ed 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/ForwardingCompareConfiguration.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/ForwardingCompareConfiguration.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013 Obeo.
+ * Copyright (c) 2013, 2018 Obeo 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
@@ -240,4 +240,14 @@ public abstract class ForwardingCompareConfiguration extends CompareConfiguratio
}
return isRightEditable();
}
+
+ /**
+ * {@inheritDoc} For backward compatibility, we override this to have the same implementation behavior as
+ * Eclipse Compare 3.7.
+ */
+ // @Override -- remove annotation for backwards-compatibility
+ public boolean isMirrored() {
+ Object property = getProperty("MIRRORED"); //$NON-NLS-1$
+ return property instanceof Boolean && ((Boolean)property).booleanValue();
+ }
}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java
index 92b9f7fb8..05f1824f2 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java
@@ -104,6 +104,7 @@ import org.eclipse.ui.views.properties.PropertySheet;
/**
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
*/
+@SuppressWarnings("restriction")
public abstract class EMFCompareContentMergeViewer extends ContentMergeViewer implements ISelectionChangedListener, ICompareColor.Provider, IAdaptable, CommandStackListener {
private static final String HANDLER_SERVICE = "fHandlerService"; //$NON-NLS-1$
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/TextFallbackCompareViewerCreator.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/TextFallbackCompareViewerCreator.java
index cdf14a20f..92586e9b6 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/TextFallbackCompareViewerCreator.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/TextFallbackCompareViewerCreator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014, 2017 Obeo.
+ * Copyright (c) 2014, 2017 Obeo 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
@@ -7,32 +7,17 @@
*
* Contributors:
* Obeo - initial API and implementation
- * Martin Fleck - bug 514079
+ * Philip Langer - bug 508526
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.IViewerCreator;
-import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
-import org.eclipse.compare.internal.MergeViewerContentProvider;
-import org.eclipse.compare.structuremergeviewer.ICompareInput;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.emf.common.notify.Notifier;
-import org.eclipse.emf.compare.Comparison;
-import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages;
-import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
-import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.CompareInputAdapter;
-import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.ForwardingCompareInput;
-import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.provider.TreeNodeCompareInput;
-import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.provider.TreeNodeCompareInputLabelProvider;
-import org.eclipse.emf.compare.internal.utils.ComparisonUtil;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.eclipse.emf.edit.tree.TreeNode;
+import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback.TextFallbackCompareInput;
+import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback.TextFallbackCompareInputLabelProvider;
+import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback.TextFallbackMergeViewer;
import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
/**
@@ -40,8 +25,6 @@ import org.eclipse.swt.widgets.Composite;
*/
public class TextFallbackCompareViewerCreator implements IViewerCreator {
- private static TextFallbackMergeViewerContentProvider fContentProvider;
-
/**
* {@inheritDoc}
*
@@ -50,512 +33,10 @@ public class TextFallbackCompareViewerCreator implements IViewerCreator {
*/
public Viewer createViewer(Composite parent, CompareConfiguration config) {
final EMFCompareConfiguration emfConfig = new EMFCompareConfiguration(config);
- emfConfig.setLabelProvider(TreeNodeCompareInput.class, new TreeNodeCompareInputLabelProvider());
- return new TextFallbackMergeViewer(parent, emfConfig);
- }
-
- private static ICompareInput getAdaptedCompareInput(CompareInputAdapter input) {
- final ICompareInput adaptedCompareInput;
- Notifier target = input.getTarget();
- if (target instanceof TreeNode) {
- TreeNode treeNode = (TreeNode)target;
- EObject data = treeNode.getData();
- Comparison comparison = ComparisonUtil.getComparison(data);
- if (comparison != null) {
- ICompareInput compareInput = (ICompareInput)EcoreUtil.getAdapter(comparison.eAdapters(),
- ICompareInput.class);
- if (compareInput instanceof ForwardingCompareInput) {
- adaptedCompareInput = ((ForwardingCompareInput)compareInput).delegate();
- } else {
- adaptedCompareInput = compareInput;
- }
- } else {
- adaptedCompareInput = null;
- EMFCompareIDEUIPlugin.getDefault().log(IStatus.ERROR,
- "Cannot find a comparison from input " + input); //$NON-NLS-1$
- }
- } else {
- adaptedCompareInput = null;
- }
- return adaptedCompareInput;
- }
-
- /**
- * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
- */
- private static final class TextFallbackMergeViewer extends TextMergeViewer {
- private Object originalInput;
-
- private Boolean fIsMirrored;
-
- /**
- * @param parent
- * @param configuration
- */
- private TextFallbackMergeViewer(Composite parent, CompareConfiguration configuration) {
- super(parent, configuration);
- fContentProvider = new TextFallbackMergeViewerContentProvider(configuration);
- setMirrored(MirrorUtil.isMirrored(getCompareConfiguration()));
- }
-
- @Override
- public void setInput(Object input) {
- originalInput = input;
- if (input instanceof CompareInputAdapter) {
- super.setInput(getAdaptedCompareInput((CompareInputAdapter)input));
- } else if (input instanceof ForwardingCompareInput) {
- super.setInput(((ForwardingCompareInput)input).delegate());
- } else {
- super.setInput(input);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.jface.viewers.ContentViewer#getInput()
- */
- @Override
- public Object getInput() {
- return originalInput;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.contentmergeviewer.TextMergeViewer#handleDispose(org.eclipse.swt.events.DisposeEvent)
- */
- @Override
- protected void handleDispose(DisposeEvent event) {
- super.handleDispose(event);
- originalInput = null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#getTitle()
- */
- @Override
- public String getTitle() {
- return EMFCompareIDEUIMessages.getString("TextFallbackCompareViewer.title"); //$NON-NLS-1$
- }
-
- /**
- * Sets the viewers {@link #isMirrored() mirrored} state and triggers an
- * {@link #updateMirrored(boolean) update}, if necessary.
- */
- protected void setMirrored(boolean isMirrored) {
- if (fIsMirrored == null || fIsMirrored.booleanValue() != isMirrored) {
- fIsMirrored = Boolean.valueOf(isMirrored);
- updateMirrored(isMirrored);
- }
- }
-
- /**
- * Updates the viewer based on its {@link #isMirrored() mirrored} state.
- */
- protected void updateMirrored(boolean isMirrored) {
- if (isMirrored) {
- setContentProvider(new MirroredTextFallbackMergeViewerContentProvider(
- getCompareConfiguration(), fContentProvider));
- } else {
- setContentProvider(fContentProvider);
- }
- }
- }
-
- /**
- * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
- */
- private static final class TextFallbackMergeViewerContentProvider extends MergeViewerContentProvider {
- /**
- * @param cc
- */
- private TextFallbackMergeViewerContentProvider(CompareConfiguration cc) {
- super(cc);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#isLeftEditable(java.lang.Object)
- */
- @Override
- public boolean isLeftEditable(Object element) {
- final boolean leftEditable;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- leftEditable = super.isLeftEditable(adaptedCompareInput);
- } else {
- leftEditable = super.isLeftEditable(element);
- }
- } else {
- leftEditable = super.isLeftEditable(element);
- }
- return leftEditable;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#isRightEditable(java.lang.Object)
- */
- @Override
- public boolean isRightEditable(Object element) {
- final boolean rightEditable;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- rightEditable = super.isRightEditable(adaptedCompareInput);
- } else {
- rightEditable = super.isRightEditable(element);
- }
- } else {
- rightEditable = super.isRightEditable(element);
- }
- return rightEditable;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#saveLeftContent(java.lang.Object,
- * byte[])
- */
- @Override
- public void saveLeftContent(Object element, byte[] bytes) {
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- super.saveLeftContent(adaptedCompareInput, bytes);
- } else {
- super.saveLeftContent(element, bytes);
- }
- } else {
- super.saveLeftContent(element, bytes);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#saveRightContent(java.lang.Object,
- * byte[])
- */
- @Override
- public void saveRightContent(Object element, byte[] bytes) {
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- super.saveRightContent(adaptedCompareInput, bytes);
- } else {
- super.saveRightContent(element, bytes);
- }
- } else {
- super.saveRightContent(element, bytes);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getAncestorLabel(java.lang.Object)
- */
- @Override
- public String getAncestorLabel(Object element) {
- final String ancestorLabel;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- ancestorLabel = super.getAncestorLabel(adaptedCompareInput);
- } else {
- ancestorLabel = super.getAncestorLabel(element);
- }
- } else {
- ancestorLabel = super.getAncestorLabel(element);
- }
- return ancestorLabel;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getAncestorImage(java.lang.Object)
- */
- @Override
- public Image getAncestorImage(Object element) {
- final Image ancestorImage;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- ancestorImage = super.getAncestorImage(adaptedCompareInput);
- } else {
- ancestorImage = super.getAncestorImage(element);
- }
- } else {
- ancestorImage = super.getAncestorImage(element);
- }
- return ancestorImage;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getAncestorContent(java.lang.Object)
- */
- @Override
- public Object getAncestorContent(Object element) {
- final Object ancestorContent;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- ancestorContent = super.getAncestorContent(adaptedCompareInput);
- } else {
- ancestorContent = super.getAncestorContent(element);
- }
- } else {
- ancestorContent = super.getAncestorContent(element);
- }
- return ancestorContent;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getLeftLabel(java.lang.Object)
- */
- @Override
- public String getLeftLabel(Object element) {
- final String leftLabel;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- leftLabel = super.getLeftLabel(adaptedCompareInput);
- } else {
- leftLabel = super.getLeftLabel(element);
- }
- } else {
- leftLabel = super.getLeftLabel(element);
- }
- return leftLabel;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getLeftImage(java.lang.Object)
- */
- @Override
- public Image getLeftImage(Object element) {
- final Image leftImage;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- leftImage = super.getLeftImage(adaptedCompareInput);
- } else {
- leftImage = super.getLeftImage(element);
- }
- } else {
- leftImage = super.getLeftImage(element);
- }
- return leftImage;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getLeftContent(java.lang.Object)
- */
- @Override
- public Object getLeftContent(Object element) {
- final Object leftContent;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- leftContent = super.getLeftContent(adaptedCompareInput);
- } else {
- leftContent = super.getLeftContent(element);
- }
- } else {
- leftContent = super.getLeftContent(element);
- }
- return leftContent;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getRightLabel(java.lang.Object)
- */
- @Override
- public String getRightLabel(Object element) {
- final String rightLabel;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- rightLabel = super.getRightLabel(adaptedCompareInput);
- } else {
- rightLabel = super.getRightLabel(element);
- }
- } else {
- rightLabel = super.getRightLabel(element);
- }
- return rightLabel;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getRightImage(java.lang.Object)
- */
- @Override
- public Image getRightImage(Object element) {
- final Image rightImage;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- rightImage = super.getRightImage(adaptedCompareInput);
- } else {
- rightImage = super.getRightImage(element);
- }
- } else {
- rightImage = super.getRightImage(element);
- }
- return rightImage;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.compare.internal.MergeViewerContentProvider#getRightContent(java.lang.Object)
- */
- @Override
- public Object getRightContent(Object element) {
- final Object rightContent;
- if (element instanceof CompareInputAdapter) {
- ICompareInput adaptedCompareInput = getAdaptedCompareInput((CompareInputAdapter)element);
- if (adaptedCompareInput != null) {
- rightContent = super.getRightContent(adaptedCompareInput);
- } else {
- rightContent = super.getRightContent(element);
- }
- } else {
- rightContent = super.getRightContent(element);
- }
- return rightContent;
- }
- }
-
- /**
- * Merge viewer content provider implementation that mirrors the left and right side. Swapping sides is
- * introduced in Eclipse Compare version 3.7, but due to backwards compatibility we cannot use their
- * implementation.
- *
- * @author Martin Fleck <mfleck@eclipsesource.com>
- */
- private static final class MirroredTextFallbackMergeViewerContentProvider extends MergeViewerContentProvider {
-
- private TextFallbackMergeViewerContentProvider delegate;
-
- public MirroredTextFallbackMergeViewerContentProvider(CompareConfiguration cc,
- TextFallbackMergeViewerContentProvider delegate) {
- super(cc);
- this.delegate = delegate;
- }
-
- @Override
- public void setLeftError(String errorMessage) {
- delegate.setRightError(errorMessage);
- }
-
- @Override
- public String getLeftLabel(Object element) {
- return delegate.getRightLabel(element);
- }
-
- @Override
- public Image getLeftImage(Object element) {
- return delegate.getRightImage(element);
- }
-
- @Override
- public Object getLeftContent(Object element) {
- return delegate.getRightContent(element);
- }
-
- @Override
- public boolean isLeftEditable(Object element) {
- return delegate.isRightEditable(element);
- }
-
- @Override
- public void saveLeftContent(Object element, byte[] bytes) {
- // The EMFCompareStructurededMergeViewer already "unswaps" the sides before saving-> keep sides
- delegate.saveLeftContent(element, bytes);
- }
-
- @Override
- public void setRightError(String errorMessage) {
- delegate.setLeftError(errorMessage);
- }
-
- @Override
- public String getRightLabel(Object element) {
- return delegate.getLeftLabel(element);
- }
-
- @Override
- public Image getRightImage(Object element) {
- return delegate.getLeftImage(element);
- }
-
- @Override
- public Object getRightContent(Object element) {
- return delegate.getLeftContent(element);
- }
-
- @Override
- public boolean isRightEditable(Object element) {
- return delegate.isLeftEditable(element);
- }
-
- @Override
- public void saveRightContent(Object element, byte[] bytes) {
- // The EMFCompareStructurededMergeViewer already "unswaps" the sides before saving-> keep sides
- delegate.saveRightContent(element, bytes);
- }
-
- @Override
- public void inputChanged(Viewer v, Object o1, Object o2) {
- delegate.inputChanged(v, o1, o2);
- }
-
- @Override
- public void setAncestorError(String errorMessage) {
- delegate.setAncestorError(errorMessage);
- }
-
- @Override
- public String getAncestorLabel(Object element) {
- return delegate.getAncestorLabel(element);
- }
-
- @Override
- public Image getAncestorImage(Object element) {
- return delegate.getAncestorImage(element);
- }
-
- @Override
- public Object getAncestorContent(Object element) {
- return delegate.getAncestorContent(element);
- }
-
- @Override
- public boolean showAncestor(Object element) {
- return delegate.showAncestor(element);
- }
+ TextFallbackMergeViewer textFallbackMergeViewer = new TextFallbackMergeViewer(parent, emfConfig);
+ emfConfig.setLabelProvider(TextFallbackCompareInput.class,
+ new TextFallbackCompareInputLabelProvider(textFallbackMergeViewer, emfConfig));
+ return textFallbackMergeViewer;
}
}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInput.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInput.java
new file mode 100644
index 000000000..c21453b0e
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInput.java
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 2018 EclipseSource Services GmbH 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:
+ * Philip Langer - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback;
+
+import com.google.common.collect.Lists;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.core.resources.IEncodedStorage;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
+import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.TextFallbackCompareViewerCreator;
+import org.eclipse.emf.compare.ide.ui.internal.logical.StorageTypedElement;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.eclipse.team.internal.ui.mapping.AbstractCompareInput;
+import org.eclipse.team.internal.ui.mapping.CompareInputChangeNotifier;
+import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement;
+
+/**
+ * A highly specialized implementation of a compare input that uses {@link TextFallbackCompareInputData text input
+ * data} to manage the information about the ancestor, left, and right.
+ */
+@SuppressWarnings("restriction")
+public final class TextFallbackCompareInput extends AbstractCompareInput {
+
+ /**
+ * The text input data representation the underlying sides of the input.
+ */
+ private final TextFallbackCompareInputData textInputData;
+
+ /**
+ * The compare input change notifier used by {@link #getChangeNotifier()}.
+ */
+ private final CompareInputChangeNotifier compareInputChangeNotifier = new CompareInputChangeNotifier() {
+ @Override
+ protected IResource[] getResources(ICompareInput input) {
+ return getResources();
+ }
+
+ /**
+ * Returns an array of resources containing each of{@link #originStorage}, {@link #leftStorage}, and
+ * {@link #rightStorage} that is an {@link IResource}.
+ *
+ * @return an array of the underlying resources of the sides.
+ * @see
+ */
+ public IResource[] getResources() {
+ List<IResource> resources = Lists.newArrayList();
+ if (textInputData.getOriginStorage() instanceof IResource) {
+ resources.add((IResource)textInputData.getOriginStorage());
+ }
+ if (textInputData.getLeftStorage() instanceof IResource) {
+ resources.add((IResource)textInputData.getLeftStorage());
+ }
+ if (textInputData.getRightStorage() instanceof IResource) {
+ resources.add((IResource)textInputData.getRightStorage());
+ }
+ return resources.toArray(new IResource[resources.size()]);
+ }
+
+ };
+
+ /**
+ * Creates an instance of the given kind, using the given text input data, and an indication of whether
+ * this input is for {@link TextFallbackCompareViewerCreator#SHOW_PREVIEW preview mode}.
+ *
+ * @param kind
+ * the kind of input.
+ * @param textInputData
+ * the text input data for this input.
+ * @param showPreview
+ * whether this is input for preview mode.
+ */
+ public TextFallbackCompareInput(int kind, TextFallbackCompareInputData textInputData, boolean showPreview) {
+ super(kind, getElement(textInputData.getOriginTypedElement(), textInputData.getOriginResource(), showPreview),
+ getElement(textInputData.getLeftTypedElement(), textInputData.getLeftResource(), showPreview),
+ getElement(textInputData.getRightTypedElement(), textInputData.getRightResource(), showPreview));
+ this.textInputData = textInputData;
+ }
+
+ /**
+ * Returns a transformed typed element appropriate for the given resource and the specified preview mode.
+ *
+ * @param typedElement
+ * the typed element to transform.
+ * @param resource
+ * the resource used during the transformation.
+ * @param showPreview
+ * whether the result should be used for preview mode.
+ * @return a transformed typed element, or the original.
+ */
+ private static ITypedElement getElement(ITypedElement typedElement, Resource resource,
+ boolean showPreview) {
+ // If we want preview mode and we have a local resource typed element, and the resource is an XML
+ // resource...
+ if (showPreview && typedElement instanceof LocalResourceTypedElement
+ && resource instanceof XMLResource) {
+ LocalResourceTypedElement localResourceTypedElement = (LocalResourceTypedElement)typedElement;
+ final IResource localResource = localResourceTypedElement.getResource();
+ final XMLResource xmlResource = (XMLResource)resource;
+
+ // Create storage that will use the local resource's path information, but fetches contents by
+ // serializing the XML resource.
+ IEncodedStorage storage = new EncodedStorage(localResource, xmlResource);
+ // Created a new storage typed element for the storage.
+ return new StorageTypedElement(storage, localResource.getFullPath().toString());
+ }
+ return typedElement;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation does nothing.
+ * </p>
+ *
+ * @see AbstractCompareInput#update()
+ */
+ @Override
+ public void update() {
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation always returns {@code false}.
+ * </p>
+ *
+ * @see AbstractCompareInput#needsUpdate()
+ */
+ @Override
+ public boolean needsUpdate() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation always returns {@link #compareInputChangeNotifier}.
+ * </p>
+ *
+ * @see AbstractCompareInput#getChangeNotifier()
+ */
+ @Override
+ protected CompareInputChangeNotifier getChangeNotifier() {
+ return compareInputChangeNotifier;
+ }
+
+ public TextFallbackCompareInputData getTextInputData() {
+ return textInputData;
+ }
+
+ private static final class EncodedStorage implements IEncodedStorage {
+
+ private final IResource localResource;
+
+ private final XMLResource xmlResource;
+
+ private EncodedStorage(IResource localResource, XMLResource xmlResource) {
+ this.localResource = localResource;
+ this.xmlResource = xmlResource;
+ }
+
+ // Don't use getAdapter(Class<T>) for compatibility with Luna
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ public String getName() {
+ return localResource.getName();
+ }
+
+ public IPath getFullPath() {
+ return localResource.getFullPath();
+ }
+
+ public InputStream getContents() throws CoreException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ try {
+ xmlResource.save(byteArrayOutputStream, null);
+ } catch (IOException e) {
+ EMFCompareIDEUIPlugin.getDefault().log(e);
+ }
+ return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+ }
+
+ public String getCharset() throws CoreException {
+ return xmlResource.getEncoding();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputData.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputData.java
new file mode 100644
index 000000000..d7bf2f65a
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputData.java
@@ -0,0 +1,384 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 2018 EclipseSource Services GmbH 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:
+ * Philip Langer - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback;
+
+import static com.google.common.collect.Iterables.getFirst;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Conflict;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.Equivalence;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.MatchResource;
+import org.eclipse.emf.compare.ReferenceChange;
+import org.eclipse.emf.compare.ide.ui.internal.logical.StorageTypedElement;
+import org.eclipse.emf.compare.ide.utils.ResourceUtil;
+import org.eclipse.emf.compare.internal.utils.ComparisonUtil;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement;
+
+/**
+ * An encapsulation of information about the three sides of a {@link TextFallbackCompareInput}.
+ *
+ * @see TextFallbackMergeViewer#getAdaptedCompareInput(CompareInputAdapter)}.
+ */
+@SuppressWarnings("restriction")
+public class TextFallbackCompareInputData {
+
+ /**
+ * The origin object.
+ */
+ private EObject origin;
+
+ /**
+ * The origin resource.
+ */
+ private Resource originResource;
+
+ /**
+ * The origin storage.
+ */
+ private IStorage originStorage;
+
+ /**
+ * The origin typed element.
+ */
+ private ITypedElement originTypedElement;
+
+ /**
+ * The left object.
+ */
+ private EObject left;
+
+ /**
+ * The left resource.
+ */
+ private Resource leftResource;
+
+ /**
+ * The left storage.
+ */
+ private IStorage leftStorage;
+
+ /**
+ * The left typed element.
+ */
+ private ITypedElement leftTypedElement;
+
+ /**
+ * The right object.
+ */
+ private EObject right;
+
+ /**
+ * The right resource.
+ */
+ private Resource rightResource;
+
+ /**
+ * The right storage.
+ */
+ private IStorage rightStorage;
+
+ /**
+ * The right typed element.
+ */
+ private ITypedElement rightTypedElement;
+
+ /**
+ * Creates an instance for the given object. It calls {@link #populate(EObject)} to populate the
+ * {@link #origin}, {@link #originResource}, {@link #left}, {@link #leftResource}, {@link #right}, and
+ * {@link #rightResource} followed by {@link #populateStorage()} to populate the {@link #originStorage},
+ * {@link #originTypedElement}, {@link #leftStorage}, {@link #leftTypedElement}, {@link #rightStorage},
+ * and {@link #rightTypedElement}.
+ *
+ * @param eObject
+ * the object for which to populate the sides of the text input data.
+ */
+ public TextFallbackCompareInputData(EObject eObject) {
+ populate(eObject);
+ populateStorage();
+ }
+
+ /**
+ * Returns {@code true}, when at least one of {@link #originTypedElement}, {@link #leftTypedElement}, or
+ * {@link #rightTypedElement} is not {@code null}.
+ *
+ * @return whether this text input data has a typed element for at least one of the sides.
+ */
+ public boolean hasTypedElement() {
+ return getOriginTypedElement() != null || getLeftTypedElement() != null
+ || getRightTypedElement() != null;
+ }
+
+ /**
+ * Populates the {@link #origin}, {@link #originResource}, {@link #left}, {@link #leftResource},
+ * {@link #right}, and {@link #rightResource}. Note that method calls itself recursively.
+ *
+ * @param eObject
+ * the object for which to populate the sides of the text input data.
+ */
+ private void populate(EObject eObject) {
+ if (eObject instanceof Match) {
+ populateFromMatch((Match)eObject);
+ } else if (eObject instanceof Diff) {
+ populateFromDiff((Diff)eObject);
+ } else if (eObject instanceof MatchResource) {
+ // If its a resource match, use the sides resources.
+ MatchResource matchResource = (MatchResource)eObject;
+ this.originResource = matchResource.getOrigin();
+ this.leftResource = matchResource.getLeft();
+ this.rightResource = matchResource.getRight();
+ } else if (eObject instanceof Equivalence) {
+ // If it's an equivalence, use the first diff.
+ Equivalence equivalence = (Equivalence)eObject;
+ EList<Diff> differences = equivalence.getDifferences();
+ Diff first = getFirst(differences, null);
+ populate(first);
+ } else if (eObject instanceof Conflict) {
+ // If it's a conflict, use the first diff.
+ Conflict conflict = (Conflict)eObject;
+ EList<Diff> differences = conflict.getDifferences();
+ Diff first = getFirst(differences, null);
+ populate(first);
+ }
+ }
+
+ private void populateFromMatch(Match match) {
+ this.origin = match.getOrigin();
+ this.left = match.getLeft();
+ this.right = match.getRight();
+
+ // If they're all null, and there is a containing match, populate using that instead.
+ if (getOrigin() == null && getLeft() == null && getRight() == null) {
+ EObject eContainer = match.eContainer();
+ if (eContainer instanceof Match) {
+ populateFromMatch((Match)eContainer);
+ }
+ } else {
+ // If there is a comparison...
+ Comparison comparison = match.getComparison();
+ if (comparison != null) {
+ // Try to find a resource match for the left, right, or origin.
+ MatchResource matchResource = getMatchResource(comparison, getSideResource(getLeft()));
+ if (matchResource == null) {
+ matchResource = getMatchResource(comparison, getSideResource(getRight()));
+ if (matchResource == null) {
+ matchResource = getMatchResource(comparison, getSideResource(getOrigin()));
+ }
+ }
+ // Use the sides from the resource match, or failing that the side resource of the side.
+ if (matchResource != null) {
+ this.originResource = matchResource.getOrigin();
+ if (getOriginResource() == null) {
+ this.originResource = getSideResource(getOrigin());
+ }
+ this.leftResource = matchResource.getLeft();
+ if (getLeftResource() == null) {
+ this.leftResource = getSideResource(getLeft());
+ }
+ this.rightResource = matchResource.getRight();
+ if (getRightResource() == null) {
+ this.rightResource = getSideResource(getRight());
+ }
+ }
+ } else {
+ // Otherwise we're forced to use only the side resources from the sides themselves.
+ this.originResource = getSideResource(getOrigin());
+ this.leftResource = getSideResource(getLeft());
+ this.rightResource = getSideResource(getRight());
+ }
+ }
+ }
+
+ private void populateFromDiff(Diff diff) {
+ for (Diff refinedDiff : diff.getRefinedBy()) {
+ if (areAllResourcesNull()) {
+ populateFromDiff(refinedDiff);
+ }
+ }
+
+ if (areAllResourcesNull()) {
+ // If the diff is a reference change of a containment reference and that change has a non-null
+ // value...
+ if (diff instanceof ReferenceChange) {
+ ReferenceChange referenceChange = (ReferenceChange)diff;
+ if (referenceChange.getReference().isContainment()) {
+ EObject value = referenceChange.getValue();
+ if (value != null) {
+ // Get the comparison for that value, and if it's not null, and there is a match
+ // for the value, populate using that match.
+ Comparison comparison = ComparisonUtil.getComparison(diff);
+ if (comparison != null) {
+ Match match = comparison.getMatch(value);
+ if (match != null) {
+ populateFromMatch(match);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (areAllResourcesNull()) {
+ // Failing those possibilities, populate using the containing match.
+ populateFromMatch(diff.getMatch());
+ }
+ }
+
+ /**
+ * Specifies whether all resources are <code>null</code>.
+ * <p>
+ * All resources are {@link #getOriginResource()}, {@link #getLeftResource()}, and
+ * {@link #getRightResource()}.
+ * <p>
+ *
+ * @return <code>true</code> if all resources are <code>null</code>, <code>false</code> otherwise.
+ */
+ private boolean areAllResourcesNull() {
+ return getOriginResource() == null && getLeftResource() == null && getRightResource() == null;
+ }
+
+ /**
+ * Returns the match resource of the comparison for the given resource.
+ *
+ * @param comparison
+ * the comparison in which to find a match resource.
+ * @param resource
+ * the resource for which to find a match resource.
+ * @return the match resource of the comparison for the given resource.
+ */
+ private MatchResource getMatchResource(Comparison comparison, Resource resource) {
+ if (resource != null) {
+ for (MatchResource matchResource : comparison.getMatchedResources()) {
+ if (matchResource.getLeft() == resource || matchResource.getRight() == resource
+ || matchResource.getOrigin() == resource) {
+ return matchResource;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link EObject#eResource() containing} resource of the object.
+ *
+ * @param eObject
+ * the object for which to get the containing resource, or {@code null}.
+ * @return the resource of the object, or {@code null}.
+ */
+ private Resource getSideResource(EObject eObject) {
+ if (eObject != null) {
+ return eObject.eResource();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Populates the {@link #originStorage}, {@link #originTypedElement}, {@link #leftStorage},
+ * {@link #leftTypedElement}, {@link #rightStorage}, and {@link #rightTypedElement}.
+ */
+ private void populateStorage() {
+ this.originStorage = getStorage(getOriginResource());
+ this.originTypedElement = getTypedElement(getOriginStorage());
+ this.leftStorage = getStorage(getLeftResource());
+ this.leftTypedElement = getTypedElement(getLeftStorage());
+ this.rightStorage = getStorage(getRightResource());
+ this.rightTypedElement = getTypedElement(getRightStorage());
+ }
+
+ /**
+ * Returns the {@link ResourceUtil#getAssociatedStorage(Resource) associated storage} of the resource.
+ *
+ * @param resource
+ * the resource for which to get associated storage.
+ * @return the associated storage of the resource
+ */
+ private IStorage getStorage(Resource resource) {
+ if (resource != null) {
+ return ResourceUtil.getAssociatedStorage(resource);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Creates an appropriate typed element for the given storage.
+ *
+ * @param storage
+ * the storage or {@code null}.
+ * @return a typed element for the storage or {@code null}.
+ */
+ private ITypedElement getTypedElement(IStorage storage) {
+ if (storage instanceof IResource) {
+ IResource resourceStorage = (IResource)storage;
+ return new LocalResourceTypedElement(resourceStorage);
+ } else if (storage != null) {
+ return new StorageTypedElement(storage, ResourceUtil.getFixedPath(storage).toString());
+ } else {
+ return null;
+ }
+ }
+
+ public EObject getOrigin() {
+ return origin;
+ }
+
+ public Resource getOriginResource() {
+ return originResource;
+ }
+
+ public IStorage getOriginStorage() {
+ return originStorage;
+ }
+
+ public ITypedElement getOriginTypedElement() {
+ return originTypedElement;
+ }
+
+ public EObject getLeft() {
+ return left;
+ }
+
+ public Resource getLeftResource() {
+ return leftResource;
+ }
+
+ public IStorage getLeftStorage() {
+ return leftStorage;
+ }
+
+ public ITypedElement getLeftTypedElement() {
+ return leftTypedElement;
+ }
+
+ public EObject getRight() {
+ return right;
+ }
+
+ public Resource getRightResource() {
+ return rightResource;
+ }
+
+ public IStorage getRightStorage() {
+ return rightStorage;
+ }
+
+ public ITypedElement getRightTypedElement() {
+ return rightTypedElement;
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputLabelProvider.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputLabelProvider.java
new file mode 100644
index 000000000..4a03bfa94
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackCompareInputLabelProvider.java
@@ -0,0 +1,298 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 2018 EclipseSource Services GmbH 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:
+ * Philip Langer - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback;
+
+import static org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback.TextFallbackMergeViewer.SHOW_PREVIEW;
+
+import java.util.Optional;
+
+import org.eclipse.compare.ICompareInputLabelProvider;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.emf.compare.ide.internal.utils.StoragePathAdapter;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages;
+import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
+import org.eclipse.emf.compare.provider.ExtendedAdapterFactoryItemDelegator;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A specialized implementation of a compare input label provider.
+ */
+public final class TextFallbackCompareInputLabelProvider implements ICompareInputLabelProvider {
+
+ /**
+ * The configuration used to provide labels.
+ *
+ * @see #isDirty(boolean)
+ * @see #getLabel(Resource, IStorage, boolean)
+ */
+ private final EMFCompareConfiguration configuration;
+
+ /**
+ * The item delegator used by {@link #getImage(Resource)} to provide resource images.
+ */
+ private final ExtendedAdapterFactoryItemDelegator itemDelegator;
+
+ /**
+ * The viewer for which labels are being provided so that {@link #isDirty(boolean) a dirty} indication is
+ * supported.
+ */
+ private final TextFallbackMergeViewer textFallbackMergeViewer;
+
+ /**
+ * Creates an instance for the given viewer and configuration.
+ *
+ * @param textFallbackMergeViewer
+ * the viewer for which labels are being provided
+ * @param configuration
+ * the configuration used to provide labels.
+ */
+ public TextFallbackCompareInputLabelProvider(TextFallbackMergeViewer textFallbackMergeViewer,
+ EMFCompareConfiguration configuration) {
+ this.textFallbackMergeViewer = textFallbackMergeViewer;
+ this.configuration = configuration;
+ itemDelegator = new ExtendedAdapterFactoryItemDelegator(configuration.getAdapterFactory());
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This is never called and always return {@code null}.
+ * <p>
+ *
+ * @see ILabelProvider#getImage(Object)
+ */
+ public Image getImage(Object element) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This is never called and always return {@code null}.
+ * <p>
+ *
+ * @see ILabelProvider#getText(Object)
+ */
+ public String getText(Object element) {
+ return null;
+ }
+
+ /**
+ * Returns an appropriate label for the given resource, storage, and dirty state.
+ *
+ * @param resource
+ * the resource for the label.
+ * @param storage
+ * the storage for the label, if the resource is {@code null}.
+ * @param dirty
+ * the dirty state of the resource/storage.
+ * @return an appropriate label for the given resource, storage, and dirty state.
+ */
+ private String getLabel(Resource resource, IStorage storage, boolean dirty) {
+ Optional<String> label = Optional.ofNullable(null);
+ if (resource != null) {
+ label = resource.eAdapters().stream().filter(StoragePathAdapter.class::isInstance)
+ .map(adapter -> getLabel(storage, dirty, (StoragePathAdapter)adapter)).findFirst();
+ }
+ return label.orElse(""); //$NON-NLS-1$
+ }
+
+ private String getLabel(IStorage storage, boolean dirty, StoragePathAdapter storagePathAdapter) {
+ String label = storagePathAdapter.getStoragePath();
+ if (storage instanceof IFile) {
+ if (configuration.getBooleanProperty(SHOW_PREVIEW, true)) {
+ label = EMFCompareIDEUIMessages.getString("TextFallbackCompareViewer.preview.title", label); //$NON-NLS-1$
+ } else {
+ label = EMFCompareIDEUIMessages.getString("TextFallbackCompareViewer.local.title", label); //$NON-NLS-1$
+ if (dirty) {
+ label = EMFCompareIDEUIMessages.getString("TextFallbackCompareViewer.dirty.title", label); //$NON-NLS-1$
+ }
+ }
+ }
+ return label;
+ }
+
+ private boolean isDirty(boolean left) {
+ if (left != configuration.isMirrored()) {
+ return textFallbackMergeViewer.isLeftDirty();
+ } else {
+ return textFallbackMergeViewer.isRightDirty();
+ }
+ }
+
+ /**
+ * Returns an image for the resource using the {@link #itemDelegator item delegator}.
+ *
+ * @param resource
+ * the resource for which we want an image.
+ * @return the image for the resource.
+ */
+ private Image getImage(Resource resource) {
+ if (resource != null) {
+ Object image = itemDelegator.getImage(resource);
+ return ExtendedImageRegistry.INSTANCE.getImage(image);
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation has no state so ignores listeners.
+ * </p>
+ *
+ * @see IBaseLabelProvider#addListener(ILabelProviderListener)
+ */
+ public void addListener(ILabelProviderListener listener) {
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation has no state so does nothing.
+ * </p>
+ *
+ * @see IBaseLabelProvider#dispose()
+ */
+ public void dispose() {
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This is never called and always returns {@code false}.
+ * </p>
+ *
+ * @see IBaseLabelProvider#isLabelProperty(Object, String)
+ */
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation has no state so ignores listeners.
+ * </p>
+ *
+ * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
+ */
+ public void removeListener(ILabelProviderListener listener) {
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation calls {@link #getLabel(Resource, IStorage, boolean)}.
+ * </p>
+ *
+ * @see ICompareInputLabelProvider#getAncestorLabel(Object)
+ */
+ public String getAncestorLabel(Object input) {
+ if (input instanceof TextFallbackCompareInput) {
+ TextFallbackCompareInputData textInputData = ((TextFallbackCompareInput)input).getTextInputData();
+ return getLabel(textInputData.getOriginResource(), textInputData.getOriginStorage(), false);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation calls {@link #getImage(Resource)}.
+ * </p>
+ *
+ * @see ICompareInputLabelProvider#getAncestorImage(Object)
+ */
+ public Image getAncestorImage(Object input) {
+ if (input instanceof TextFallbackCompareInput) {
+ TextFallbackCompareInputData textInputData = ((TextFallbackCompareInput)input).getTextInputData();
+ return getImage(textInputData.getOriginResource());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation calls {@link #getLabel(Resource, IStorage, boolean)}.
+ * </p>
+ *
+ * @see ICompareInputLabelProvider#getLeftLabel(Object)
+ */
+ public String getLeftLabel(Object input) {
+ if (input instanceof TextFallbackCompareInput) {
+ TextFallbackCompareInputData textInputData = ((TextFallbackCompareInput)input).getTextInputData();
+ return getLabel(textInputData.getLeftResource(), textInputData.getLeftStorage(), isDirty(true));
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation calls {@link #getImage(Resource)}.
+ * </p>
+ *
+ * @see ICompareInputLabelProvider#getLeftImage(Object)
+ */
+ public Image getLeftImage(Object input) {
+ if (input instanceof TextFallbackCompareInput) {
+ TextFallbackCompareInputData textInputData = ((TextFallbackCompareInput)input).getTextInputData();
+ return getImage(textInputData.getLeftResource());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation calls {@link #getLabel(Resource, IStorage, boolean)}.
+ * </p>
+ *
+ * @see ICompareInputLabelProvider#getRightLabel(Object)
+ */
+ public String getRightLabel(Object input) {
+ if (input instanceof TextFallbackCompareInput) {
+ TextFallbackCompareInputData textInputData = ((TextFallbackCompareInput)input).getTextInputData();
+ return getLabel(textInputData.getRightResource(), textInputData.getRightStorage(),
+ isDirty(false));
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation calls {@link #getImage(Resource)}.
+ * </p>
+ *
+ * @see ICompareInputLabelProvider#getRightImage(Object)
+ */
+ public Image getRightImage(Object input) {
+ if (input instanceof TextFallbackCompareInput) {
+ return getImage(((TextFallbackCompareInput)input).getTextInputData().getRightResource());
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewer.java
new file mode 100644
index 000000000..1b2916d5c
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewer.java
@@ -0,0 +1,727 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 2018 EclipseSource Services GmbH 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:
+ * Philip Langer - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback;
+
+import com.google.common.eventbus.Subscribe;
+
+import java.util.EventObject;
+
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareViewerSwitchingPane;
+import org.eclipse.compare.ISharedDocumentAdapter;
+import org.eclipse.compare.SharedDocumentAdapter;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.SharedDocumentAdapterWrapper;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.emf.common.command.CommandStackListener;
+import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.command.ICompareCommandStack;
+import org.eclipse.emf.compare.domain.ICompareEditingDomain;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
+import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
+import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.CompareInputAdapter;
+import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.ForwardingCompareInput;
+import org.eclipse.emf.compare.internal.utils.ComparisonUtil;
+import org.eclipse.emf.compare.rcp.ui.internal.configuration.ICompareEditingDomainChange;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.eclipse.emf.edit.tree.TreeNode;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+
+/**
+ * A highly specialized implementation of a text merge viewer.
+ *
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+@SuppressWarnings("restriction")
+public class TextFallbackMergeViewer extends TextMergeViewer {
+
+ static final String SHOW_PREVIEW = "SHOW_PREVIEW"; //$NON-NLS-1$
+
+ /**
+ * We subvert the base class from seeing or notifying listeners. That's because editing the text makes the
+ * text dirty, but the EMF compare's editor should always reflect the dirty state of the command stack.
+ *
+ * @see #addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ * @see #removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ * @see #updateDirtyState(ICompareCommandStack)
+ */
+ @SuppressWarnings("rawtypes")
+ private final ListenerList listenerList = new ListenerList();
+
+ /**
+ * The original input passed to {@link #setInput(Object)}.
+ *
+ * @see #getInput()
+ */
+ private Object originalInput;
+
+ /**
+ * The effective input computed during {@link #setInput(Object)} by
+ * {@link #getAdaptedCompareInput(CompareInputAdapter)}.
+ */
+ private Object effectiveInput;
+
+ /**
+ * The ancestor viewer in which to {@link #select(SourceViewer, EObject, Resource) select} objects.
+ *
+ * @see #createSourceViewer(Composite, int)
+ */
+ private SourceViewer ancesorViewer;
+
+ /**
+ * The left viewer in which to {@link #select(SourceViewer, EObject, Resource) select} objects.
+ *
+ * @see #createSourceViewer(Composite, int)
+ */
+ private SourceViewer leftViewer;
+
+ /**
+ * The right viewer in which to {@link #select(SourceViewer, EObject, Resource) select} objects.
+ *
+ * @see #createSourceViewer(Composite, int)
+ */
+ private SourceViewer rightViewer;
+
+ /**
+ * Controls whether {@link #setContentProvider(IContentProvider) content provider changes} are respected
+ * or ignored.
+ *
+ * @see #TextFallbackCompareViewerCreator(Composite, EMFCompareConfiguration)
+ * @see #handlePropertyChangeEvent(PropertyChangeEvent)
+ */
+ private boolean ignoreContentProvideChanges = true;
+
+ /**
+ * The item added by {@link #createToolItems(ToolBarManager)} and updated by {@link #updateToolItems()}.
+ * It's used to provide the ability to preview the contents of a resource as if it were saved.
+ */
+ private ActionContributionItem previewItem;
+
+ /**
+ * The command stack using during {@link #updateDirtyState(ICompareCommandStack) dirty state updates}.
+ */
+ private ICompareCommandStack commandStackForNotification;
+
+ /**
+ * A command stack listener that listens to the
+ * {@link #editingDomainChange(ICompareEditingDomain, ICompareEditingDomain) editing domain's command
+ * stack}. It {@link #updateDirtyState(ICompareCommandStack) updates the dirty state} and
+ * {@link #updateTitleImage() updates the title image}.
+ */
+ private final CommandStackListener commandStackListener = new CommandStackListener() {
+ public void commandStackChanged(EventObject event) {
+ Object commandStack = event.getSource();
+ if (commandStack instanceof ICompareCommandStack) {
+ ICompareCommandStack compareCommandStack = (ICompareCommandStack)commandStack;
+ updateDirtyState(compareCommandStack);
+ updateTitleImage();
+ }
+ setInput(getOriginalInput());
+ }
+ };
+
+ /**
+ * Creates an instance under the given parent using the given configuration.
+ *
+ * @param parent
+ * the parent composite under which to create this viewer.
+ * @param configuration
+ * the EMF compare configuration used by this viewer.
+ */
+ public TextFallbackMergeViewer(Composite parent, EMFCompareConfiguration configuration) {
+ super(parent, configuration);
+
+ // Register with the event bus.
+ configuration.getEventBus().register(this);
+
+ // Hook up the command stack listener to the editing domain's command stack.
+ editingDomainChange(null, getCompareConfiguration().getEditingDomain());
+
+ // Set our content provider, ensuring that it's not ignored during the update.
+ ignoreContentProvideChanges = false;
+ setContentProvider(new TextFallbackMergeViewerContentProvider(this));
+ ignoreContentProvideChanges = true;
+ }
+
+ /**
+ * Listens to editing domain changes on the {@link EMFCompareConfiguration#getEventBus() event bus}.
+ *
+ * @param event
+ * the editing domain change event.
+ */
+ @Subscribe
+ public void handleEditingDomainChange(ICompareEditingDomainChange event) {
+ editingDomainChange(event.getOldValue(), event.getNewValue());
+ }
+
+ /**
+ * Manages the {{@link #commandStackListener command stack listener} by removing it from the old editing
+ * domain's {@link ICompareEditingDomain#getCommandStack() command stack} and adding it to the new editing
+ * domain's command stack.
+ *
+ * @param oldValue
+ * the previous editing domain.
+ * @param newValue
+ * the new editing domain.
+ */
+ private void editingDomainChange(ICompareEditingDomain oldValue, ICompareEditingDomain newValue) {
+ if (newValue != oldValue) {
+ if (oldValue != null) {
+ oldValue.getCommandStack().removeCommandStackListener(commandStackListener);
+ }
+ if (newValue != null) {
+ newValue.getCommandStack().addCommandStackListener(commandStackListener);
+ updateDirtyState(newValue.getCommandStack());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation subverts calls to {@code super} so that nothing can actually listen to underlying
+ * state changes to the dirty state of this viewer's source viewers. It manages its own
+ * {@link #listenerList listeners} and {@link #updateDirtyState(ICompareCommandStack) informs listeners of
+ * the dirty state} based on changes to {@link #commandStackListener command stack state}.
+ * </p>
+ *
+ * @see ContentMergeViewer#removePropertyChangeListener(IPropertyChangeListener)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ listenerList.add(listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation subverts calls to {@code super} so that nothing can actually listen to underlying
+ * state changes to the dirty state of this viewer's source viewers. It manages its own
+ * {@link #listenerList listeners}.
+ * </p>
+ *
+ * @see ContentMergeViewer#removePropertyChangeListener(IPropertyChangeListener)
+ */
+ @Override
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ listenerList.remove(listener);
+ }
+
+ /**
+ * {@link Utilities#firePropertyChange(ListenerList, Object, String, Object, Object) Fires} a
+ * {@link CompareEditorInput#DIRTY_STATE dirty state} event to the {@link #listenerList listeners} of this
+ * viewer. It ensures that calls to the {link {@link #isLeftDirty()} and {@link #isRightDirty()} return
+ * the state of the {@link #commandStackForNotification command stack}.
+ *
+ * @param commandStack
+ * the command stack whose state should be used to update dirtiness.
+ */
+ @SuppressWarnings("unchecked")
+ private void updateDirtyState(ICompareCommandStack commandStack) {
+ // Don't change the dirty state of the part itself, because that's managed by actual changes to
+ // the source viewers.
+ this.commandStackForNotification = commandStack;
+ Utilities.firePropertyChange(listenerList, this, CompareEditorInput.DIRTY_STATE, null,
+ Boolean.valueOf(commandStack.isLeftSaveNeeded() || commandStack.isRightSaveNeeded()));
+ this.commandStackForNotification = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation returns the {@link ICompareCommandStack#isLeftSaveNeeded() state} of the
+ * {@link #commandStackForNotification command stack} when the command stack is
+ * {@link #updateDirtyState(ICompareCommandStack) updating the dirty state}.
+ * </p>
+ *
+ * @see ContentMergeViewer#isLeftDirty()
+ */
+ @Override
+ protected boolean isLeftDirty() {
+ if (commandStackForNotification == null) {
+ return super.isLeftDirty();
+ } else {
+ return commandStackForNotification.isLeftSaveNeeded();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation {@link #updateHeader() updates the header} because the label
+ * {@link TextFallbackCompareInputLabelProvider#getLabel(Resource, IStorage, boolean) includes a dirty
+ * state indication}.
+ * </p>
+ *
+ * @see ContentMergeViewer#setLeftDirty(boolean)
+ */
+ @Override
+ protected void setLeftDirty(boolean dirty) {
+ super.setLeftDirty(dirty);
+ updateHeader();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation returns the {@link ICompareCommandStack#isRightSaveNeeded() state} of the
+ * {@link #commandStackForNotification command stack} when the command stack is
+ * {@link #updateDirtyState(ICompareCommandStack) updating the dirty state}.
+ * </p>
+ *
+ * @see ContentMergeViewer#isRightDirty()
+ */
+ @Override
+ protected boolean isRightDirty() {
+ if (commandStackForNotification == null) {
+ return super.isRightDirty();
+ } else {
+ return commandStackForNotification.isRightSaveNeeded();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation {@link #updateHeader() updates the header} because the label
+ * {@link TextFallbackCompareInputLabelProvider#getLabel(Resource, IStorage, boolean) includes a dirty
+ * state indication}.
+ * </p>
+ *
+ * @see ContentMergeViewer#setRightDirty(boolean)
+ */
+ @Override
+ protected void setRightDirty(boolean dirty) {
+ super.setRightDirty(dirty);
+ updateHeader();
+ }
+
+ /**
+ * Updates the title image on the {@link CompareViewerSwitchingPane compare viewer switching pane}. This
+ * is needed when the {@link #commandStackListener command stack changes state} and when
+ * {@link #handlePropertyChangeEvent(PropertyChangeEvent) handling mirror direction changes}. The image
+ * often includes a directional indicator or a {@link Diff#getState() difference resolution state} that
+ * changes.
+ */
+ private void updateTitleImage() {
+ if (getInput() instanceof ICompareInput) {
+ Composite parent = getControl().getParent();
+ if (parent instanceof CompareViewerSwitchingPane) {
+ CompareViewerSwitchingPane switchingPane = (CompareViewerSwitchingPane)parent;
+ switchingPane.setImage(((ICompareInput)getInput()).getImage());
+ }
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation also creates a {@link #previewItem preview action} that's useful for showing the
+ * saved contents of resource in their current state of modification.
+ * </p>
+ *
+ * @see TextMergeViewer#createToolItems(ToolBarManager)
+ */
+ @Override
+ protected void createToolItems(ToolBarManager tbm) {
+ super.createToolItems(tbm);
+
+ Action previewAction = new Action() {
+ {
+ setChecked(getCompareConfiguration().getBooleanProperty(SHOW_PREVIEW, true));
+ setImageDescriptor(
+ EMFCompareIDEUIPlugin.getImageDescriptor("icons/full/toolb16/show_preview.gif")); //$NON-NLS-1$
+ updateToolTipText();
+ }
+
+ @Override
+ public void run() {
+ getCompareConfiguration().setProperty(SHOW_PREVIEW, Boolean.valueOf(isChecked()));
+ setInput(getOriginalInput());
+ updateToolTipText();
+ }
+
+ private void updateToolTipText() {
+ if (isChecked()) {
+ setToolTipText(
+ EMFCompareIDEUIMessages.getString("TextFallbackCompareViewer.hidePreviewLabel")); //$NON-NLS-1$
+ } else {
+ setToolTipText(
+ EMFCompareIDEUIMessages.getString("TextFallbackCompareViewer.showPreviewLabel")); //$NON-NLS-1$
+ }
+ }
+ };
+
+ tbm.appendToGroup("modes", previewAction); //$NON-NLS-1$
+ previewItem = new ActionContributionItem(previewAction);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation also updates the {@link #previewItem preview action}.
+ * </p>
+ *
+ * @see TextMergeViewer#updateToolItems()
+ */
+ @Override
+ protected void updateToolItems() {
+ previewItem.setVisible(getEffectiveInput() instanceof TextFallbackCompareInput);
+ super.updateToolItems();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation assumes the configuration must be an {@link EMFCompareConfiguration}, returning it
+ * as such.
+ * </p>
+ *
+ * @see ContentMergeViewer#getCompareConfiguration()
+ */
+ @Override
+ protected EMFCompareConfiguration getCompareConfiguration() {
+ return (EMFCompareConfiguration)super.getCompareConfiguration();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This specialized implementation {@link #ignoreContentProvideChanges ignores} content provider changes
+ * except when explicitly set in the
+ * {@link #TextFallbackCompareViewerCreator(Composite, EMFCompareConfiguration) constructor} and when this
+ * implementation is {@link #handlePropertyChangeEvent(PropertyChangeEvent) handling mirror changes}.
+ * </p>
+ *
+ * @see TextMergeViewer#setContentProvider(IContentProvider)
+ */
+ @Override
+ public void setContentProvider(IContentProvider contentProvider) {
+ if (!ignoreContentProvideChanges) {
+ super.setContentProvider(contentProvider);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation is specialized to {@link #getAdaptedCompareInput(CompareInputAdapter) adapt} the
+ * input and to {@link #select(SourceViewer, EObject, Resource) select} the objects of the input.
+ * </p>
+ *
+ * @see TextMergeViewer#setInput(Object)
+ */
+ @Override
+ public void setInput(Object input) {
+ Control control = getControl();
+ try {
+ // Disable painting to reduce flicker during the update process.
+ control.setRedraw(false);
+
+ // We set this to null while calling the super methods.
+ // This ensures that getInput() will return the actual old input during the input switching
+ // process until the super call has actually set the field and is returning the input we've
+ // just set.
+ // If we don't do this, any edits will not be saved when the user is prompted whether to save
+ // the changes or discard them because that relies on the actual old input.
+ setOriginalInput(null);
+ if (input instanceof CompareInputAdapter) {
+ setEffectiveInput(getAdaptedCompareInput((CompareInputAdapter)input));
+ } else if (input instanceof ForwardingCompareInput) {
+ setEffectiveInput(((ForwardingCompareInput)input).delegate());
+ } else {
+ setEffectiveInput(input);
+ }
+ super.setInput(getEffectiveInput());
+
+ // From this point forward, getInput() will return the original input.
+ // This ensures that when we switch to a different view that disposes this view,
+ // the input for that new view is this original input.
+ setOriginalInput(input);
+
+ // If we have a text compare input...
+ if (getEffectiveInput() instanceof TextFallbackCompareInput) {
+ TextFallbackCompareInput textCompareInput = (TextFallbackCompareInput)getEffectiveInput();
+ TextFallbackCompareInputData textInputData = textCompareInput.getTextInputData();
+ // Select the objects on each of the sides.
+ select(leftViewer, textInputData.getLeft(), textInputData.getLeftResource());
+ select(rightViewer, textInputData.getRight(), textInputData.getRightResource());
+ select(ancesorViewer, textInputData.getOrigin(), textInputData.getOriginResource());
+ }
+ } finally {
+ control.setRedraw(true);
+ }
+ }
+
+ /**
+ * Adapts the real input passed to {@link #setInput(Object)} to an input more appropriate for this viewer.
+ *
+ * @param input
+ * a compare input adapter.
+ * @return the input adapted to the appropriate input to use for this viewer.
+ * @see TextFallbackCompareInputData
+ * @see TextFallbackCompareInput
+ */
+ private ICompareInput getAdaptedCompareInput(CompareInputAdapter input) {
+ ICompareInput adaptedCompareInput;
+ Notifier target = input.getTarget();
+ // If this is an adapter for a tree node....
+ if (target instanceof TreeNode) {
+ // Get the tree node's data and determine its comparison; we generally expect that to never be
+ // null.
+ TreeNode treeNode = (TreeNode)target;
+ EObject data = treeNode.getData();
+ Comparison comparison = ComparisonUtil.getComparison(data);
+ if (comparison != null) {
+ // Adapt the input and if it's a forwarding compare input, unwrap the delegate.
+ ICompareInput compareInput = (ICompareInput)EcoreUtil.getAdapter(comparison.eAdapters(),
+ ICompareInput.class);
+ if (compareInput instanceof ForwardingCompareInput) {
+ adaptedCompareInput = ((ForwardingCompareInput)compareInput).delegate();
+ } else {
+ adaptedCompareInput = compareInput;
+ }
+ // Compute the most appropriate text input data from the tree node's data.
+ // Only use it as the adapted compare input if it succeeds to compute at least one typed
+ // element for ones of the sides.
+ TextFallbackCompareInputData textInputData = new TextFallbackCompareInputData(data);
+ if (textInputData.hasTypedElement()) {
+ adaptedCompareInput = new TextFallbackCompareInput(adaptedCompareInput.getKind(),
+ textInputData, getCompareConfiguration().getBooleanProperty(SHOW_PREVIEW, true));
+ }
+ } else {
+ adaptedCompareInput = null;
+ EMFCompareIDEUIPlugin.getDefault().log(IStatus.ERROR,
+ "Cannot find a comparison from input " + input); //$NON-NLS-1$
+ }
+ } else {
+ adaptedCompareInput = null;
+ }
+ return adaptedCompareInput;
+ }
+
+ /**
+ * Selects the the source viewer's text line closest to the given object of the given resource. If that
+ * line contains a difference, that difference is also selected.
+ *
+ * @param sourceViewer
+ * the source viewer on which to operate.
+ * @param eObject
+ * the object to be selected.
+ * @param resource
+ * the resource containing the object.
+ */
+ private void select(SourceViewer sourceViewer, EObject eObject, Resource resource) {
+ IDocument document = sourceViewer.getDocument();
+ int offset = getOffset(eObject, resource, document.get());
+ if (offset != -1) {
+ sourceViewer.setSelectedRange(offset, 0);
+ sourceViewer.setSelection(sourceViewer.getSelection(), true);
+
+ // The viewer listens for key events for keyboard navigation updates to the selected
+ // difference and this approach updates that selection based on the current selected range.
+ Event event = new Event();
+ StyledText textWidget = sourceViewer.getTextWidget();
+ textWidget.notifyListeners(SWT.KeyDown, event);
+ textWidget.notifyListeners(SWT.KeyUp, event);
+ }
+ }
+
+ /**
+ * Returns the offset of the start of the line containing then object of the given resource serialized for
+ * the given text.
+ *
+ * @param eObject
+ * the object to be selected.
+ * @param resource
+ * the resource containing the object.
+ * @param text
+ * the text of the serialized resource.
+ * @return the offset of the start of the line containing then object of the given resource serialized for
+ * the given text, or {@code -1} if the object can't be located.
+ */
+ private int getOffset(EObject eObject, Resource resource, String text) {
+ int offset = -1;
+ if (resource instanceof XMLResource && eObject != null) {
+ // This only works if there is an object and the resource is an XML Resource that has an
+ // extrinsic ID for the object.
+ XMLResource xmlResource = (XMLResource)resource;
+ String id = xmlResource.getID(eObject);
+ if (id != null) {
+ // Look for the expected form of the ID in the serialization.
+ offset = text.indexOf("xmi:id=\"" + id + '"'); //$NON-NLS-1$
+ if (offset != -1) {
+ // If we find it, iterate back to the start of the line.
+ while (offset > 0) {
+ char c = text.charAt(offset - 1);
+ if (c == '\n' || c == '\r') {
+ break;
+ }
+ --offset;
+ }
+ }
+ }
+ }
+ return offset;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation simply delegates to {@code super} but it is used to record the
+ * {@link #ancesorViewer}, {@link #leftViewer}, and {@link #rightViewer} which are needed in
+ * {@link #setInput(Object)} to {@link #select(SourceViewer, EObject, Resource) select} objects.
+ * </p>
+ *
+ * @see TextMergeViewer#createSourceViewer(Composite, int)
+ */
+ @Override
+ protected SourceViewer createSourceViewer(Composite parent, int textOrientation) {
+ SourceViewer sourceViewer = super.createSourceViewer(parent, textOrientation);
+ if (ancesorViewer == null) {
+ ancesorViewer = sourceViewer;
+ } else if (leftViewer == null) {
+ leftViewer = sourceViewer;
+ } else {
+ rightViewer = sourceViewer;
+ }
+ return sourceViewer;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This class' specialized {@link #setInput(Object) setInput} method transforms the real input, so it's
+ * important that we return the {@link #originalInput} when that isn't {@code null}.
+ * </p>
+ *
+ * @see #setInput(Object)
+ * @see org.eclipse.jface.viewers.ContentViewer#getInput()
+ */
+ @Override
+ public Object getInput() {
+ if (getOriginalInput() == null) {
+ return super.getInput();
+ } else {
+ return getOriginalInput();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.contentmergeviewer.TextMergeViewer#handleDispose(org.eclipse.swt.events.DisposeEvent)
+ */
+ @Override
+ protected void handleDispose(DisposeEvent event) {
+ super.handleDispose(event);
+
+ // Disconnect from the editing domain.
+ EMFCompareConfiguration configuration = getCompareConfiguration();
+ editingDomainChange(configuration.getEditingDomain(), null);
+
+ // Disconnect from the event bus.
+ configuration.getEventBus().unregister(this);
+
+ // Clean up the inputs.
+ setOriginalInput(null);
+ setEffectiveInput(null);
+ }
+
+ /**
+ * Opens a connection to the document provider and returns a runnable that will disconnect it.
+ *
+ * @param element
+ * the left or right element.
+ * @return a runnable to disconnect the connection, or null, if no connection was established.
+ */
+ private Runnable connectDocumentProvider(Object element) {
+ if (element != null) {
+ final ISharedDocumentAdapter sharedDocumentationAdapter = SharedDocumentAdapterWrapper
+ .getAdapter(element);
+ if (sharedDocumentationAdapter != null) {
+ final IEditorInput documentKey = sharedDocumentationAdapter.getDocumentKey(element);
+ if (documentKey != null) {
+ final IDocumentProvider documentProvider = SharedDocumentAdapter
+ .getDocumentProvider(documentKey);
+ if (documentProvider != null) {
+ try {
+ sharedDocumentationAdapter.connect(documentProvider, documentKey);
+ return new Runnable() {
+ public void run() {
+ sharedDocumentationAdapter.disconnect(documentProvider, documentKey);
+ }
+ };
+ } catch (CoreException e) {
+ EMFCompareIDEUIPlugin.getDefault().log(e);
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#getTitle()
+ */
+ @Override
+ public String getTitle() {
+ return EMFCompareIDEUIMessages.getString("TextFallbackCompareViewer.title"); //$NON-NLS-1$
+ }
+
+ public Object getOriginalInput() {
+ return originalInput;
+ }
+
+ private void setOriginalInput(Object originalInput) {
+ this.originalInput = originalInput;
+ }
+
+ public Object getEffectiveInput() {
+ return effectiveInput;
+ }
+
+ private void setEffectiveInput(Object effectiveInput) {
+ this.effectiveInput = effectiveInput;
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewerContentProvider.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewerContentProvider.java
new file mode 100644
index 000000000..ed72f9500
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/fallback/TextFallbackMergeViewerContentProvider.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2017, 2018 EclipseSource Services GmbH 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:
+ * Philip Langer - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.fallback;
+
+import org.eclipse.compare.internal.MergeViewerContentProvider;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+@SuppressWarnings("restriction")
+final class TextFallbackMergeViewerContentProvider extends MergeViewerContentProvider {
+
+ private TextFallbackMergeViewer textFallbackMergeViewer;
+
+ /**
+ * Creates a provider for the text fallback merge viewer.
+ *
+ * @param textFallbackMergeViewer
+ * the text fallback merge viewer.
+ */
+ public TextFallbackMergeViewerContentProvider(TextFallbackMergeViewer textFallbackMergeViewer) {
+ super(textFallbackMergeViewer.getCompareConfiguration());
+ this.textFallbackMergeViewer = textFallbackMergeViewer;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#isLeftEditable(java.lang.Object)
+ */
+ @Override
+ public boolean isLeftEditable(Object element) {
+ return super.isLeftEditable(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#isRightEditable(java.lang.Object)
+ */
+ @Override
+ public boolean isRightEditable(Object element) {
+ return super.isRightEditable(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#saveLeftContent(java.lang.Object, byte[])
+ */
+ @Override
+ public void saveLeftContent(Object element, byte[] bytes) {
+ super.saveLeftContent(getEffectiveElement(element), bytes);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#saveRightContent(java.lang.Object, byte[])
+ */
+ @Override
+ public void saveRightContent(Object element, byte[] bytes) {
+ super.saveRightContent(getEffectiveElement(element), bytes);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getAncestorLabel(java.lang.Object)
+ */
+ @Override
+ public String getAncestorLabel(Object element) {
+ return super.getAncestorLabel(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getAncestorImage(java.lang.Object)
+ */
+ @Override
+ public Image getAncestorImage(Object element) {
+ return super.getAncestorImage(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getAncestorContent(java.lang.Object)
+ */
+ @Override
+ public Object getAncestorContent(Object element) {
+ return super.getAncestorContent(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getLeftLabel(java.lang.Object)
+ */
+ @Override
+ public String getLeftLabel(Object element) {
+ return super.getLeftLabel(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getLeftImage(java.lang.Object)
+ */
+ @Override
+ public Image getLeftImage(Object element) {
+ return super.getLeftImage(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getLeftContent(java.lang.Object)
+ */
+ @Override
+ public Object getLeftContent(Object element) {
+ return super.getLeftContent(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getRightLabel(java.lang.Object)
+ */
+ @Override
+ public String getRightLabel(Object element) {
+ return super.getRightLabel(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getRightImage(java.lang.Object)
+ */
+ @Override
+ public Image getRightImage(Object element) {
+ return super.getRightImage(getEffectiveElement(element));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.compare.internal.MergeViewerContentProvider#getRightContent(java.lang.Object)
+ */
+ @Override
+ public Object getRightContent(Object element) {
+ return super.getRightContent(getEffectiveElement(element));
+ }
+
+ private Object getEffectiveElement(Object element) {
+ if (element == textFallbackMergeViewer.getOriginalInput()) {
+ return textFallbackMergeViewer.getEffectiveInput();
+ } else {
+ return element;
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/ide_ui_messages.properties b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/ide_ui_messages.properties
index a0a2cd098..161ea0178 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/ide_ui_messages.properties
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/ide_ui_messages.properties
@@ -7,7 +7,7 @@
#
# Contributors:
# Obeo - initial API and implementation
-# Philip Langer - log msgs for merge, bugs 462884, 508855, 521948, 522372
+# Philip Langer - log msgs, bugs 462884, 508855, 521948, 522372, 508526
# Stefan Dirix - bug 456699, 474723
# Michael Borkowski - bug 467191
################################################################################
@@ -158,3 +158,12 @@ LogicalModelView.errorDialog.message = Error while computing logical models. See
MergeAction.redoProblem.title = Merge Change
MergeAction.redoProblem.message = You will lose {0} manual changes. Do you wish to proceed?
+
+TextFallbackCompareViewer.showPreviewLabel = Show Preview of Saved Resource
+TextFallbackCompareViewer.hidePreviewLabel = Show Local Workspace Resource
+
+TextFallbackCompareViewer.local.title = Local: {0}
+TextFallbackCompareViewer.preview.title = Preview: {0}
+TextFallbackCompareViewer.dirty.title = * {0}
+
+
diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java
index 8f2f96772..514a9f254 100644
--- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java
+++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java
@@ -8,7 +8,7 @@
* Contributors:
* Obeo - initial API and implementation
* Stefan Dirix - bug 464904
- * Philip Langer - bug 516508
+ * Philip Langer - bug 516508, 508526
*******************************************************************************/
package org.eclipse.emf.compare.ide.internal.utils;
@@ -35,7 +35,6 @@ import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
-import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -53,7 +52,7 @@ import org.eclipse.emf.compare.ide.EMFCompareIDEPlugin;
import org.eclipse.emf.compare.ide.hook.IResourceSetHook;
import org.eclipse.emf.compare.ide.internal.EMFCompareIDEMessages;
import org.eclipse.emf.compare.ide.internal.hook.ResourceSetHookRegistry;
-import org.eclipse.emf.compare.ide.utils.IStoragePathAdapterProvider;
+import org.eclipse.emf.compare.ide.utils.ResourceUtil;
import org.eclipse.emf.compare.ide.utils.StorageTraversal;
import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
import org.eclipse.emf.compare.rcp.policy.ILoadOnDemandPolicy;
@@ -250,6 +249,7 @@ public final class NotLoadingResourceSet extends ResourceSetImpl implements Disp
* Exception raised if there is an error with the {@link IStorage}.
*/
private void loadFromStorage(Resource resource, IStorage storage) throws IOException {
+ ResourceUtil.setAssociatedStorage(resource, storage);
try (InputStream stream = storage.getContents()) {
resource.load(stream, getLoadOptions());
} catch (CoreException | WrappedException e) {
@@ -425,14 +425,6 @@ public final class NotLoadingResourceSet extends ResourceSetImpl implements Disp
getURIResourceMap().put(uri, loaded);
try {
loadFromStorage(loaded, storage);
- final String fullPath = storage.getFullPath().toString();
- boolean isLocal = storage instanceof IFile;
- if (storage instanceof IStoragePathAdapterProvider) {
- loaded.eAdapters().add(
- ((IStoragePathAdapterProvider)storage).createStoragePathAdapter(fullPath, isLocal));
- } else {
- loaded.eAdapters().add(new StoragePathAdapter(fullPath, isLocal));
- }
monitor.worked(1);
} catch (IOException e) {
logLoadingFromStorageFailed(loaded, storage, e);
diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java
index 7208991d3..06c5da046 100644
--- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java
+++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2016 Obeo and others.
+ * Copyright (c) 2012, 2017 Obeo 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
@@ -8,7 +8,7 @@
* Contributors:
* Obeo - initial API and implementation
* Michael Borkowski - bug 467677
- * Philip Langer - optimize use of StorageTraversal.getStorages()
+ * Philip Langer - optimize use of StorageTraversal.getStorages(), 508526
*******************************************************************************/
package org.eclipse.emf.compare.ide.utils;
@@ -42,9 +42,12 @@ import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
+import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.compare.ide.EMFCompareIDEPlugin;
+import org.eclipse.emf.compare.ide.internal.utils.StoragePathAdapter;
import org.eclipse.emf.compare.ide.internal.utils.URIStorage;
import org.eclipse.emf.compare.merge.ResourceChangeAdapter;
import org.eclipse.emf.ecore.resource.Resource;
@@ -108,6 +111,7 @@ public final class ResourceUtil {
final URI uri = createURIFor(storage);
try {
Resource resource = resourceSet.createResource(uri);
+ setAssociatedStorage(resource, storage);
try (InputStream stream = storage.getContents()) {
resource.load(stream, options);
}
@@ -119,6 +123,76 @@ public final class ResourceUtil {
}
/**
+ * Returns the storage {@link #setAssociatedStorage(Resource, IStorage) associated} with the resource.
+ *
+ * @param resource
+ * the resource.
+ * @return the associated storage or <code>null</code> if there isn't one.
+ * @see #setAssociatedStorage(Resource, IStorage)
+ */
+ public static IStorage getAssociatedStorage(Resource resource) {
+ StorageProvider storageProvider = (StorageProvider)EcoreUtil.getExistingAdapter(resource,
+ StorageProvider.class);
+ if (storageProvider != null) {
+ return storageProvider.getStorage();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Associates the storage with the resource such that {@link #getAssociatedStorage(Resource)} will return
+ * this storage for the resource.
+ *
+ * @param resource
+ * the resource.
+ * @param storage
+ * the associated storage.
+ */
+ public static void setAssociatedStorage(Resource resource, IStorage storage) {
+ final String fullPath = storage.getFullPath().toString();
+ boolean isLocal = storage instanceof IFile;
+ EList<Adapter> eAdapters = resource.eAdapters();
+ if (storage instanceof IStoragePathAdapterProvider) {
+ eAdapters.add(((IStoragePathAdapterProvider)storage).createStoragePathAdapter(fullPath, isLocal));
+ } else {
+ eAdapters.add(new StoragePathAdapter(fullPath, isLocal));
+ }
+ eAdapters.add(new StorageProvider(storage));
+ }
+
+ /**
+ * Used by {@link ResourceUtil#getAssociatedStorage(Resource)} and
+ * {@link ResourceUtil#setAssociatedStorage(Resource, IStorage)} to map a resource to its associated
+ * storage.
+ */
+ private static final class StorageProvider extends AdapterImpl {
+ /**
+ * The storage.
+ */
+ private final IStorage storage;
+
+ /**
+ * Creates an instance for the storage.
+ *
+ * @param storage
+ * the storage.
+ */
+ StorageProvider(IStorage storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public boolean isAdapterForType(Object type) {
+ return type == StorageProvider.class;
+ }
+
+ public IStorage getStorage() {
+ return storage;
+ }
+ }
+
+ /**
* Checks whether the two given storages point to binary identical data.
*
* @param left
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/contentmergeviewer/accessor/impl/ResourceStreamAccessorImpl.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/contentmergeviewer/accessor/impl/ResourceStreamAccessorImpl.java
index 02219f9c2..e0fa18f71 100644
--- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/contentmergeviewer/accessor/impl/ResourceStreamAccessorImpl.java
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/contentmergeviewer/accessor/impl/ResourceStreamAccessorImpl.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo.
+ * Copyright (c) 2012, 2017 Obeo 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
@@ -7,6 +7,7 @@
*
* Contributors:
* Obeo - initial API and implementation
+ * Philip Langer - bug 508526
*******************************************************************************/
package org.eclipse.emf.compare.rcp.ui.internal.contentmergeviewer.accessor.impl;
@@ -77,7 +78,7 @@ public class ResourceStreamAccessorImpl extends AbstractTypedElementAdapter impl
* @see org.eclipse.emf.compare.rcp.ui.contentmergeviewer.accessor.legacy.ITypedElement#getType()
*/
public String getType() {
- return TEXT_TYPE;
+ return "org.eclipse.emf.compare.rcp.ui.fallbackText"; //$NON-NLS-1$
}
/**

Back to the top