Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Plouhinec2020-03-11 15:20:23 +0000
committerGlenn Plouhinec2020-03-17 15:17:27 +0000
commit797551cdf22e95efb5688a58d2b70f3cb676b83a (patch)
tree0b0521c9745d2ce150fe38d7919f7c78e2c1675b
parent258af3887e37e18f699d81866664e305ad3981ef (diff)
downloadorg.eclipse.emf.compare-797551cdf22e95efb5688a58d2b70f3cb676b83a.tar.gz
org.eclipse.emf.compare-797551cdf22e95efb5688a58d2b70f3cb676b83a.tar.xz
org.eclipse.emf.compare-797551cdf22e95efb5688a58d2b70f3cb676b83a.zip
[560861] Swapping content merge viewer don't swap phantoms
The target side when revealing phantom decorators don't return the correct MergeViewerSide when the compare configuration is mirrored. - Fix the method PhantomManager#getTargetSide(Match, View) - Refactor DiagramContentMergeViewer class - Creating Unit tests Bug: 560861 Change-Id: I437293113ad2e5fe91ffb4977aaa52c975167d73 Signed-off-by: Glenn Plouhinec <glenn.plouhinec@obeo.fr>
-rw-r--r--org.eclipse.emf.compare-parent/pom.xml1
-rw-r--r--packaging/org.eclipse.emf.compare.tests-feature/feature.xml7
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.checkstyle10
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.classpath11
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.project28
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/META-INF/MANIFEST.MF19
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/about.html106
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/build.properties7
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/plugin.properties12
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/pom.xml30
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/TestBug560861.java130
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/left.nodes4
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/right.nodes2
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/AllTests.java36
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/BugsTestSuite.java21
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java796
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java99
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java1784
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java56
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java282
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java824
22 files changed, 2504 insertions, 1768 deletions
diff --git a/org.eclipse.emf.compare-parent/pom.xml b/org.eclipse.emf.compare-parent/pom.xml
index 62d805f9e..2e95ea6a3 100644
--- a/org.eclipse.emf.compare-parent/pom.xml
+++ b/org.eclipse.emf.compare-parent/pom.xml
@@ -120,6 +120,7 @@
<module>../plugins/org.eclipse.emf.compare.diagram</module>
<module>../plugins/org.eclipse.emf.compare.diagram.edit</module>
<module>../plugins/org.eclipse.emf.compare.diagram.ide.ui</module>
+ <module>../plugins/org.eclipse.emf.compare.diagram.ide.ui.tests</module>
<module>../packaging/org.eclipse.emf.compare.tests-feature</module>
</modules>
diff --git a/packaging/org.eclipse.emf.compare.tests-feature/feature.xml b/packaging/org.eclipse.emf.compare.tests-feature/feature.xml
index 44f8c1435..b33eeb74b 100644
--- a/packaging/org.eclipse.emf.compare.tests-feature/feature.xml
+++ b/packaging/org.eclipse.emf.compare.tests-feature/feature.xml
@@ -113,4 +113,11 @@
version="0.0.0"
unpack="false"/>
+ <plugin
+ id="org.eclipse.emf.compare.diagram.ide.ui.tests"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
</feature>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.checkstyle b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.checkstyle
new file mode 100644
index 000000000..2684ff12f
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+ <local-check-config name="EMF Compare" location="/org.eclipse.emf.compare-parent/codeStyle/EMFCompareCheckstyle6Configuration.xml" type="project" description="">
+ <additional-data name="protect-config-file" value="false"/>
+ </local-check-config>
+ <fileset name="all" enabled="true" check-config-name="EMF Compare" local="true">
+ <file-match-pattern match-pattern="." include-pattern="true"/>
+ </fileset>
+</fileset-config>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.classpath b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.classpath
new file mode 100644
index 000000000..3e5654f17
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.project b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.project
new file mode 100644
index 000000000..4a23ac7d3
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.emf.compare.diagram.ide.ui.tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.settings/org.eclipse.jdt.core.prefs b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..0c68a61dc
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..ddf357950
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.emf.compare.diagram.ide.ui.tests
+Bundle-Version: 3.4.3.qualifier
+Bundle-Vendor: %providerName
+Automatic-Module-Name: org.eclipse.emf.compare.diagram.ide.ui.tests
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-Localization: plugin
+Require-Bundle: org.junit,
+ org.eclipse.compare,
+ org.mockito,
+ org.hamcrest,
+ org.eclipse.emf.compare,
+ org.eclipse.emf.compare.diagram.ide.ui,
+ org.eclipse.emf.compare.ide.ui,
+ org.eclipse.emf.compare.tests,
+ org.eclipse.emf.compare.rcp.ui,
+ org.eclipse.gmf.runtime.notation
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/about.html b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/about.html
new file mode 100644
index 000000000..670d10856
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/about.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Foundation Software User Agreement</title>
+</head>
+
+<body lang="EN-US">
+<h2>Eclipse Foundation Software User Agreement</h2>
+<p>April 14, 2010</p>
+
+<h3>Usage Of Content</h3>
+
+<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
+ (COLLECTIVELY &quot;CONTENT&quot;). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
+ CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
+ OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
+ NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
+ CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
+
+<h3>Applicable Licenses</h3>
+
+<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
+ (&quot;EPL&quot;). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+ For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
+ repository (&quot;Repository&quot;) in software modules (&quot;Modules&quot;) and made available as downloadable archives (&quot;Downloads&quot;).</p>
+
+<ul>
+ <li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins (&quot;Plug-ins&quot;), plug-in fragments (&quot;Fragments&quot;), and features (&quot;Features&quot;).</li>
+ <li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java&trade; ARchive) in a directory named &quot;plugins&quot;.</li>
+ <li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named &quot;features&quot;. Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of the Plug-ins
+ and/or Fragments associated with that Feature.</li>
+ <li>Features may also include other Features (&quot;Included Features&quot;). Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of Included Features.</li>
+</ul>
+
+<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named &quot;about.html&quot; (&quot;Abouts&quot;). The terms and conditions governing Features and
+Included Features should be contained in files named &quot;license.html&quot; (&quot;Feature Licenses&quot;). Abouts and Feature Licenses may be located in any directory of a Download or Module
+including, but not limited to the following locations:</p>
+
+<ul>
+ <li>The top-level (root) directory</li>
+ <li>Plug-in and Fragment directories</li>
+ <li>Inside Plug-ins and Fragments packaged as JARs</li>
+ <li>Sub-directories of the directory named &quot;src&quot; of certain Plug-ins</li>
+ <li>Feature directories</li>
+</ul>
+
+<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license (&quot;Feature Update License&quot;) during the
+installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
+inform you where you can locate them. Feature Update Licenses may be found in the &quot;license&quot; property of files named &quot;feature.properties&quot; found within a Feature.
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
+that directory.</p>
+
+<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
+
+<ul>
+ <li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
+ <li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
+ <li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
+ <li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li>
+ <li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
+</ul>
+
+<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
+contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
+
+
+<h3>Use of Provisioning Technology</h3>
+
+<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
+ Update Manager (&quot;Provisioning Technology&quot;) for the purpose of allowing users to install software, documentation, information and/or
+ other materials (collectively &quot;Installable Software&quot;). This capability is provided with the intent of allowing such users to
+ install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a
+ href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
+ (&quot;Specification&quot;).</p>
+
+<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
+ applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
+ in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
+ Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p>
+
+<ol>
+ <li>A series of actions may occur (&quot;Provisioning Process&quot;) in which a user may execute the Provisioning Technology
+ on a machine (&quot;Target Machine&quot;) with the intent of installing, extending or updating the functionality of an Eclipse-based
+ product.</li>
+ <li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
+ accessed and copied to the Target Machine.</li>
+ <li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
+ Software (&quot;Installable Software Agreement&quot;) and such Installable Software Agreement shall be accessed from the Target
+ Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
+ the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
+ indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li>
+</ol>
+
+<h3>Cryptography</h3>
+
+<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
+ another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
+ possession, or use, and re-export of encryption software, to see if this is permitted.</p>
+
+<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/build.properties b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/build.properties
new file mode 100644
index 000000000..0fb7ee670
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = target/
+bin.includes = META-INF/,\
+ .,\
+ about.html,\
+ plugin.properties
+src.includes = about.html
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/plugin.properties b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/plugin.properties
new file mode 100644
index 000000000..0d9f2c42d
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/plugin.properties
@@ -0,0 +1,12 @@
+################################################################################
+# Copyright (c) 2020 Obeo.
+# 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:
+# Obeo - initial API and implementation
+################################################################################
+pluginName = EMF Compare IDE UI Diagram integration Tests
+providerName = Eclipse Modeling Project
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/pom.xml b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/pom.xml
new file mode 100644
index 000000000..2199fe87d
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>emf.compare-test-parent</artifactId>
+ <groupId>org.eclipse.emf.compare</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../org.eclipse.emf.compare-parent/test-parent</relativePath>
+ </parent>
+ <groupId>org.eclipse.emf.compare</groupId>
+ <artifactId>org.eclipse.emf.compare.diagram.ide.ui.tests</artifactId>
+ <version>3.4.3-SNAPSHOT</version>
+ <packaging>eclipse-test-plugin</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-surefire-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>org/eclipse/emf/compare/diagram/ide/ui/tests/suite/AllTests.class</include>
+ </includes>
+ <useUIHarness>true</useUIHarness>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/TestBug560861.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/TestBug560861.java
new file mode 100644
index 000000000..46bf09947
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/TestBug560861.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.EMFCompare;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide;
+import org.eclipse.emf.compare.scope.DefaultComparisonScope;
+import org.eclipse.emf.compare.tests.framework.AbstractInputData;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * This test is related to the bug <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=560861">560861</a>
+ *
+ * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
+ */
+@SuppressWarnings("restriction")
+public class TestBug560861 {
+
+ private Comparison comparison;
+
+ private CompareConfiguration cc;
+
+ private EMFCompareConfiguration emfcc;
+
+ private PhantomManager phantomManager;
+
+ private boolean mirrored;
+
+ /**
+ * Set up the two test models.
+ * <p>
+ * <ul>
+ * <li>left.nodes: In this model we have a tree nodes structured like this:
+ *
+ * <pre>
+ * Root - DeletedNode
+ * </pre>
+ *
+ * </li>
+ * <li>right.nodes: In this model "DeletedNode" has been deleted.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * A 2-way comparison between left.nodes (left input) and right.nodes (right input) gives the resulting
+ * comparison model:
+ * <li>A difference for the deletion of "DeletedNode".</li>
+ * </p>
+ *
+ * @throws IOException
+ */
+ @Before
+ public void setUp() throws IOException {
+ Bug560861InputData inputData = new Bug560861InputData();
+ Resource left = inputData.getResource("left.nodes"); //$NON-NLS-1$
+ Resource right = inputData.getResource("right.nodes"); //$NON-NLS-1$
+ Resource origin = null;
+
+ DefaultComparisonScope scope = new DefaultComparisonScope(left, right, origin);
+ comparison = EMFCompare.builder().build().compare(scope);
+ cc = new CompareConfiguration();
+ emfcc = new EMFCompareConfiguration(cc);
+ }
+
+ /**
+ * Tests that the side returned by the getTargetSide() method is correct before swapping.
+ */
+ @Test
+ public void testSideBeforeSwappingLeftAndRight() {
+ mirrored = false;
+ cc.setProperty(EMFCompareConfiguration.MIRRORED, mirrored);
+ Match match = comparison.getMatches().get(0).getSubmatches().get(0);
+ phantomManager = new PhantomManager(emfcc, null, null, null, null);
+ MergeViewerSide phantomSide = phantomManager.getTargetSide(match, null);
+
+ assertFalse(emfcc.isMirrored());
+ assertEquals(phantomSide, MergeViewerSide.RIGHT);
+ }
+
+ /**
+ * Tests that the side returned by the getTargetSide() method is correct after swapping.
+ */
+ @Test
+ public void testSideAfterSwappingLeftAndRight() {
+ mirrored = true;
+ cc.setProperty(EMFCompareConfiguration.MIRRORED, mirrored);
+ Match match = comparison.getMatches().get(0).getSubmatches().get(0);
+ phantomManager = new PhantomManager(emfcc, null, null, null, null);
+ MergeViewerSide phantomSide = phantomManager.getTargetSide(match, null);
+
+ assertTrue(emfcc.isMirrored());
+ assertEquals(phantomSide, MergeViewerSide.LEFT);
+ }
+
+ /**
+ * Input data for this bug.
+ *
+ * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
+ */
+ public class Bug560861InputData extends AbstractInputData {
+
+ private static final String PATH_PREFIX = "data/_560861/"; //$NON-NLS-1$
+
+ public Resource getResource(String resourceName) throws IOException {
+ StringBuilder resourceURL = new StringBuilder(PATH_PREFIX);
+ resourceURL.append(resourceName);
+ return loadFromClassLoader(resourceURL.toString());
+ }
+ }
+
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/left.nodes b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/left.nodes
new file mode 100644
index 000000000..b26ea1349
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/left.nodes
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodes:Node xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:nodes="http://www.eclipse.org/emf/compare/tests/nodes" xmi:id="_yWc_0JUeEeGiestbncRZoQ" name="Root">
+ <containmentRef1 xmi:id="_MPLmoN82EeO5ttKSQEjHCQ" name="DeletedNode"/>
+</nodes:Node>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/right.nodes b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/right.nodes
new file mode 100644
index 000000000..f249db886
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/data/_560861/right.nodes
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodes:Node xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:nodes="http://www.eclipse.org/emf/compare/tests/nodes" xmi:id="_yWc_0JUeEeGiestbncRZoQ" name="Root"/>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/AllTests.java
new file mode 100644
index 000000000..9401ad416
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/AllTests.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.tests.suite;
+
+import org.eclipse.emf.compare.ComparePackage;
+import org.eclipse.emf.compare.diagram.ide.ui.tests.suite.BugsTestSuite;
+import org.eclipse.emf.compare.tests.nodes.NodesPackage;
+import org.eclipse.emf.compare.tests.nodes.util.NodesResourceFactoryImpl;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({BugsTestSuite.class })
+public class AllTests {
+
+ @BeforeClass
+ public static void fillEMFRegistries() {
+ EPackage.Registry.INSTANCE.put(ComparePackage.eNS_URI, ComparePackage.eINSTANCE);
+ EPackage.Registry.INSTANCE.put(NodesPackage.eNS_URI, NodesPackage.eINSTANCE);
+
+ Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("nodes", //$NON-NLS-1$
+ new NodesResourceFactoryImpl());
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/BugsTestSuite.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/BugsTestSuite.java
new file mode 100644
index 000000000..69ac11564
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.tests/src/org/eclipse/emf/compare/diagram/ide/ui/tests/suite/BugsTestSuite.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.tests.suite;
+
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.TestBug560861;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({TestBug560861.class })
+public class BugsTestSuite {
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java
new file mode 100644
index 000000000..1043020ca
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java
@@ -0,0 +1,796 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram;
+
+import com.google.common.base.Predicate;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.DecoratorFigure;
+import org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff;
+import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.ICompareColor;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.LayerConstants;
+import org.eclipse.gef.editparts.LayerManager;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.ListItemEditPart;
+import org.eclipse.gmf.runtime.notation.Diagram;
+import org.eclipse.gmf.runtime.notation.Edge;
+import org.eclipse.gmf.runtime.notation.View;
+
+/**
+ * Decorator manager to create, hide or reveal decorator figures related to deleted or added graphical
+ * objects.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+public abstract class AbstractDecoratorManager implements IDecoratorManager {
+
+ /**
+ * Decorator represented by a <code>figure</code> on a <code>layer</code>, from the given
+ * <code>side</code> of the merge viewer. An edit part may be linked to the <code>figure</code> in some
+ * cases.<br>
+ * The decorator is related to a <code>difference</code> and it is binded with the reference view and
+ * figure.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+ protected abstract class AbstractDecorator {
+
+ /** The reference <code>View</code> for this decorator. */
+ protected View fOriginView;
+
+ /** The reference <code>IFigure</code> for this decorator. */
+ protected IFigure fOriginFigure;
+
+ /** The <code>IFigure</code> representing this decorator. */
+ protected IFigure fFigure;
+
+ /**
+ * The <code>DecoratorFigure</code> representing this decorator. It references
+ * {@link AbstractDecorator#fFigure} through the accessor {@link DecoratorFigure#getMainFigure()}.
+ */
+ protected DecoratorFigure fDecoratorFigure;
+
+ /** The layer on which the <code>figure</code> has to be drawn. */
+ protected IFigure fLayer;
+
+ /** The side of the merge viewer on which the <code>figure</code> has to be drawn. */
+ protected MergeViewerSide fSide;
+
+ /** The difference related to this phantom. */
+ protected Diff fDifference;
+
+ /** The edit part of the figure representing this phantom. May be null. */
+ protected EditPart fEditPart;
+
+ /**
+ * Getter.
+ *
+ * @return the originView {@link AbstractDecorator#fOriginView}.
+ */
+ public View getOriginView() {
+ return fOriginView;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param originView
+ * {@link AbstractDecorator#fOriginView}.
+ */
+ public void setOriginView(View originView) {
+ this.fOriginView = originView;
+ }
+
+ /**
+ * Getter.
+ *
+ * @return the originFigure {@link AbstractDecorator#fOriginFigure}.
+ */
+ public IFigure getOriginFigure() {
+ return fOriginFigure;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param originFigure
+ * {@link AbstractDecorator#fOriginFigure}.
+ */
+ public void setOriginFigure(IFigure originFigure) {
+ this.fOriginFigure = originFigure;
+ }
+
+ /**
+ * Getter.
+ *
+ * @return the figure {@link AbstractDecorator#fFigure}.
+ */
+ public IFigure getFigure() {
+ return fFigure;
+ }
+
+ /**
+ * Getter.
+ *
+ * @return the decorator figure {@link AbstractDecorator#fDecoratorFigure}.
+ */
+ public DecoratorFigure getDecoratorFigure() {
+ return fDecoratorFigure;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param figure
+ * {@link AbstractDecorator#fFigure}.
+ */
+ public void setFigure(IFigure figure) {
+ this.fFigure = figure;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param figure
+ * {@link AbstractDecorator#fFigure}.
+ */
+ public void setDecoratorFigure(DecoratorFigure figure) {
+ this.fDecoratorFigure = figure;
+ setFigure(figure.getMainFigure());
+ }
+
+ /**
+ * Getter.
+ *
+ * @return the layer {@link AbstractDecorator#fLayer}.
+ */
+ public IFigure getLayer() {
+ return fLayer;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param layer
+ * {@link AbstractDecorator#fLayer}.
+ */
+ public void setLayer(IFigure layer) {
+ this.fLayer = layer;
+ }
+
+ /**
+ * Getter.
+ *
+ * @return the side {@link AbstractDecorator#fSide}.
+ */
+ public MergeViewerSide getSide() {
+ return fSide;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param side
+ * {@link AbstractDecorator#fSide}.
+ */
+ public void setSide(MergeViewerSide side) {
+ this.fSide = side;
+ }
+
+ /**
+ * Getter.
+ *
+ * @return the difference {@link AbstractDecorator#fDifference}.
+ */
+ public Diff getDifference() {
+ return fDifference;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param difference
+ * {@link AbstractDecorator#fDifference}.
+ */
+ public void setDifference(Diff difference) {
+ this.fDifference = difference;
+ }
+
+ /**
+ * Getter.
+ *
+ * @return the editPart {@link AbstractDecorator#fEditPart}.
+ */
+ public EditPart getEditPart() {
+ return fEditPart;
+ }
+
+ /**
+ * Setter.
+ *
+ * @param editPart
+ * {@link AbstractDecorator#fEditPart}.
+ */
+ public void setEditPart(EditPart editPart) {
+ this.fEditPart = editPart;
+ }
+
+ }
+
+ /**
+ * The compare configuration of the viewer.
+ */
+ private EMFCompareConfiguration fCompareConfiguration;
+
+ /**
+ * The left area of the viewer.
+ */
+ private DiagramMergeViewer fLeft;
+
+ /**
+ * The right area of the viewer.
+ */
+ private DiagramMergeViewer fRight;
+
+ /**
+ * The ancestor area of the viewer.
+ */
+ private DiagramMergeViewer fAncestor;
+
+ /**
+ * The color of the difference.
+ */
+ private ICompareColor fColor;
+
+ /**
+ * Constructor.
+ *
+ * @param compareConfiguration
+ * The compare configuration of the viewer.
+ * @param left
+ * The left area of the viewer.
+ * @param right
+ * The right area of the viewer.
+ * @param ancestor
+ * The ancestor area of the viewer.
+ * @param color
+ * The color of the difference.
+ */
+ protected AbstractDecoratorManager(EMFCompareConfiguration compareConfiguration, DiagramMergeViewer left,
+ DiagramMergeViewer right, DiagramMergeViewer ancestor, ICompareColor color) {
+ fCompareConfiguration = compareConfiguration;
+ fLeft = left;
+ fRight = right;
+ fAncestor = ancestor;
+ fColor = color;
+ }
+
+ /**
+ * Returns the compare configuration of this viewer.
+ *
+ * @return the compare configuration, never <code>null</code>
+ */
+ protected EMFCompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * Returns the left area of the viewer.
+ *
+ * @return the fLeft
+ */
+ protected DiagramMergeViewer getLeftMergeViewer() {
+ return fLeft;
+ }
+
+ /**
+ * Returns the right area of the viewer.
+ *
+ * @return the fRight
+ */
+ protected DiagramMergeViewer getRightMergeViewer() {
+ return fRight;
+ }
+
+ /**
+ * Returns the ancestor area of the viewer.
+ *
+ * @return the fAncestor
+ */
+ protected DiagramMergeViewer getAncestorMergeViewer() {
+ return fAncestor;
+ }
+
+ /**
+ * Return whether the input is a three-way comparison.
+ *
+ * @return whether the input is a three-way comparison
+ */
+ protected boolean isThreeWay() {
+ return getCompareConfiguration().getComparison().isThreeWay();
+ }
+
+ /**
+ * Returns the ICompareColor.
+ *
+ * @return the ICompareColor.
+ */
+ public ICompareColor getCompareColor() {
+ return fColor;
+ }
+
+ /**
+ * From a given difference, it hides the related decorators.
+ *
+ * @param difference
+ * The difference.
+ */
+ public void hideDecorators(Diff difference) {
+ Collection<? extends AbstractDecorator> oldDecorators = getDecorators(difference);
+ if (oldDecorators != null && !oldDecorators.isEmpty()
+ && getCompareConfiguration().getComparison() != null) {
+ handleDecorators(oldDecorators, false, true);
+ }
+ }
+
+ /**
+ * From a given difference, it reveals the related decorators.
+ *
+ * @param difference
+ * The difference.
+ */
+ public void revealDecorators(Diff difference) {
+
+ Collection<? super AbstractDecorator> decorators = (Collection<? super AbstractDecorator>)getDecorators(
+ difference);
+
+ // Create decorators only if they do not already exist and if the selected difference is a good
+ // candidate for that.
+ if ((decorators == null || decorators.isEmpty()) && isGoodCandidate(difference)) {
+
+ DiagramDiff diagramDiff = (DiagramDiff)difference;
+
+ List<View> referenveViews = getReferenceViews(diagramDiff);
+
+ for (View referenceView : referenveViews) {
+ IFigure referenceFigure = getFigure(referenceView);
+
+ if (referenceFigure != null) {
+ MergeViewerSide targetSide = getTargetSide(
+ getCompareConfiguration().getComparison().getMatch(referenceView), referenceView);
+
+ if (decorators == null) {
+ decorators = new ArrayList<AbstractDecorator>();
+ }
+
+ AbstractDecorator decorator = createAndRegisterDecorator(difference, referenceView,
+ referenceFigure, targetSide);
+ if (decorator != null) {
+ decorators.add(decorator);
+ }
+ }
+
+ }
+
+ }
+
+ // The selected difference is a good candidate and decorators exist for it
+ if (decorators != null && !decorators.isEmpty()) {
+ revealDecorators((Collection<? extends AbstractDecorator>)decorators);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
+ */
+ public abstract void removeDecorators(Diff difference);
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#removeAll()
+ */
+ public abstract void removeAll();
+
+ /**
+ * It reveals the given decorators.
+ *
+ * @param decorators
+ * The main decorators.
+ */
+ protected void revealDecorators(Collection<? extends AbstractDecorator> decorators) {
+ handleDecorators(decorators, true, true);
+ }
+
+ /**
+ * Get the figure related to the given view.
+ *
+ * @param view
+ * The view.
+ * @return the figure.
+ */
+ protected IFigure getFigure(View view) {
+ MergeViewerSide side = getSide(view);
+ GraphicalEditPart originEditPart = (GraphicalEditPart)getViewer(side).getEditPart(view);
+ if (originEditPart != null) {
+ return originEditPart.getFigure();
+ }
+ return null;
+ }
+
+ /**
+ * It manages the display of the given decorators.<br>
+ *
+ * @param decorators
+ * The decorators to handle.
+ * @param isAdd
+ * True if it has to be revealed, False otherwise.
+ * @param areMain
+ * It indicates if the given decorators to handle are considered as the main ones (the ones
+ * directly linked to the selected difference).
+ */
+ protected void handleDecorators(Collection<? extends AbstractDecorator> decorators, boolean isAdd,
+ boolean areMain) {
+ for (AbstractDecorator decorator : decorators) {
+ handleDecorator(decorator, isAdd, areMain);
+ }
+ }
+
+ /**
+ * It manages the display of the given decorator.
+ *
+ * @param decorator
+ * The decorator to handle.
+ * @param isAdd
+ * True if it has to be revealed, False otherwise.
+ * @param isMain
+ * It indicates if the given decorator to handle is considered as the main one (the one
+ * directly linked to the selected difference).
+ */
+ protected void handleDecorator(AbstractDecorator decorator, boolean isAdd, boolean isMain) {
+ IFigure layer = decorator.getLayer();
+ IFigure figure = decorator.getFigure();
+ if (isAdd) {
+ handleAddDecorator(decorator, layer, figure, isMain);
+ } else if (layer.getChildren().contains(figure)) {
+ handleDeleteDecorator(decorator, layer, figure);
+ }
+ }
+
+ /**
+ * It manages the reveal of the given decorator.
+ *
+ * @param decorator
+ * The decorator.
+ * @param parent
+ * The parent figure which has to get the figure to reveal (<code>toAdd</code>)
+ * @param toAdd
+ * The figure to reveal.
+ * @param isMain
+ * It indicates if the given decorator to reveal is considered as the main one (the one
+ * directly linked to the selected difference).
+ */
+ protected void handleAddDecorator(AbstractDecorator decorator, IFigure parent, IFigure toAdd,
+ boolean isMain) {
+ if (decorator.getEditPart() != null) {
+ decorator.getEditPart().activate();
+ }
+ if (!parent.getChildren().contains(toAdd)) {
+ parent.add(toAdd);
+ }
+ }
+
+ /**
+ * It manages the hiding of the given decorator.
+ *
+ * @param decorator
+ * The decorator.
+ * @param parent
+ * The parent figure which has to get the figure to hide (<code>toDelete</code>)
+ * @param toDelete
+ * The figure to hide.
+ */
+ protected void handleDeleteDecorator(AbstractDecorator decorator, IFigure parent, IFigure toDelete) {
+ if (decorator.getEditPart() != null) {
+ decorator.getEditPart().deactivate();
+ }
+ if (parent.getChildren().contains(toDelete)) {
+ parent.remove(toDelete);
+ }
+ }
+
+ /**
+ * It checks if the given difference is a good candidate to manage decorators.<br>
+ *
+ * @see {@link PhantomManager#goodCandidate()}.
+ * @param difference
+ * The difference.
+ * @return True if it is a good candidate, False otherwise.
+ */
+ private boolean isGoodCandidate(Diff difference) {
+ return goodCandidate().apply(difference);
+ }
+
+ /**
+ * Get the layer on the given side, from the reference view.<br>
+ *
+ * @see @ link {@link PhantomManager#getIDLayer(View)}.
+ * @param referenceView
+ * The reference view.
+ * @param side
+ * The side where the layer has to be found.
+ * @return The layer figure or null if the edit part of the diagram is not found.
+ */
+ protected IFigure getLayer(View referenceView, MergeViewerSide side) {
+ Diagram referenceDiagram = referenceView.getDiagram();
+ Diagram targetDiagram = (Diagram)getMatchView(referenceDiagram, getEffectiveSide(side));
+ DiagramMergeViewer targetViewer = getViewer(side);
+ EditPart editPart = targetViewer.getEditPart(targetDiagram);
+ if (editPart != null) {
+ return LayerManager.Helper.find(editPart).getLayer(getIDLayer(referenceView));
+ }
+ return null;
+ }
+
+ /**
+ * Get the layer ID to use from the reference view.<br>
+ * If the reference view is an edge, it is the {@link LayerConstants.CONNECTION_LAYER} which is used,
+ * {@link LayerConstants.SCALABLE_LAYERS} otherwise.
+ *
+ * @param referenceView
+ * The reference view.
+ * @return The ID of the layer.
+ */
+ protected Object getIDLayer(View referenceView) {
+ if (referenceView instanceof Edge) {
+ return LayerConstants.CONNECTION_LAYER;
+ } else {
+ return LayerConstants.SCALABLE_LAYERS;
+ }
+ }
+
+ /**
+ * It translates the coordinates of the given bounds, from the reference figure and the root of this one,
+ * to absolute coordinates.
+ *
+ * @param referenceFigure
+ * The reference figure.
+ * @param rootReferenceFigure
+ * The root of the reference figure.
+ * @param boundsToTranslate
+ * The bounds to translate.
+ */
+ protected void translateCoordinates(IFigure referenceFigure, IFigure rootReferenceFigure,
+ Rectangle boundsToTranslate) {
+ IFigure referenceParentFigure = referenceFigure.getParent();
+ if (referenceParentFigure != null && referenceFigure != rootReferenceFigure) {
+ // rootReferenceFigure may be located to (-x,0)... We consider that the root reference is
+ // always (0,0)
+ if (referenceParentFigure.isCoordinateSystem() && referenceParentFigure != rootReferenceFigure) {
+ boundsToTranslate.x += referenceParentFigure.getBounds().x;
+ boundsToTranslate.y += referenceParentFigure.getBounds().y;
+ }
+ translateCoordinates(referenceParentFigure, rootReferenceFigure, boundsToTranslate);
+ }
+ }
+
+ /**
+ * It checks that the given view represents an element of a list.
+ *
+ * @param view
+ * The view.
+ * @return True it it is an element of a list.
+ */
+ protected boolean isNodeList(View view) {
+ DiagramMergeViewer viewer = getViewer(getSide(view));
+ EditPart part = viewer.getEditPart(view);
+ return isNodeList(part);
+ }
+
+ /**
+ * It checks that the given part represents an element of a list.
+ *
+ * @param part
+ * The part.
+ * @return True it it represents an element of a list.
+ */
+ private boolean isNodeList(EditPart part) {
+ return part instanceof ListItemEditPart || isInListContainer(part);
+
+ }
+
+ /**
+ * It checks that the given part is in one representing a list container.
+ *
+ * @param part
+ * The part.
+ * @return True it it is in a part representing a list container.
+ */
+ private boolean isInListContainer(EditPart part) {
+ EditPart parent = part.getParent();
+ while (parent != null) {
+ if (parent instanceof ListCompartmentEditPart) {
+ return true;
+ }
+ parent = parent.getParent();
+ }
+ return false;
+ }
+
+ /**
+ * Utility method to retrieve the {@link DiagramMergeViewer} from the given side.
+ *
+ * @param side
+ * The side to focus.
+ * @return The viewer.
+ */
+ public DiagramMergeViewer getViewer(MergeViewerSide side) {
+ DiagramMergeViewer result = null;
+ switch (side) {
+ case LEFT:
+ result = getLeftMergeViewer();
+ break;
+ case RIGHT:
+ result = getRightMergeViewer();
+ break;
+ case ANCESTOR:
+ result = getAncestorMergeViewer();
+ break;
+ default:
+ }
+ return result;
+ }
+
+ /**
+ * Utility method to know the side where is located the given view.
+ *
+ * @param view
+ * The view.
+ * @return The side of the view.
+ */
+ public MergeViewerSide getSide(View view) {
+ MergeViewerSide result = null;
+ Match match = getCompareConfiguration().getComparison().getMatch(view);
+ if (match.getLeft() == view) {
+ result = MergeViewerSide.LEFT;
+ } else if (match.getRight() == view) {
+ result = MergeViewerSide.RIGHT;
+ } else if (match.getOrigin() == view) {
+ result = MergeViewerSide.ANCESTOR;
+ }
+ return getEffectiveSide(result);
+ }
+
+ /**
+ * Returns the effective side taking into account {@link CompareConfiguration#isMirrored()} to switch left
+ * and right.
+ *
+ * @param side
+ * The side where the potential matching object has to be retrieved.
+ * @return the effective side with respect to mirroring.
+ */
+ protected MergeViewerSide getEffectiveSide(MergeViewerSide side) {
+ if (side != null && getCompareConfiguration().isMirrored()) {
+ return side.opposite();
+ }
+ return side;
+ }
+
+ /**
+ * Utility method to get the object matching with the given one, to the given side.
+ *
+ * @param object
+ * The object as base of the lookup.
+ * @param side
+ * The side where the potential matching object has to be retrieved.
+ * @return The matching object.
+ */
+ public EObject getMatchView(EObject object, MergeViewerSide side) {
+ Match match = getCompareConfiguration().getComparison().getMatch(object);
+ return getMatchView(match, side);
+ }
+
+ /**
+ * Utility method to get the object in the given side from the given match.
+ *
+ * @param match
+ * The match.
+ * @param side
+ * The side where the potential matching object has to be retrieved.
+ * @return The matching object.
+ */
+ private EObject getMatchView(Match match, MergeViewerSide side) {
+ EObject result = null;
+ switch (side) {
+ case LEFT:
+ result = match.getLeft();
+ break;
+ case RIGHT:
+ result = match.getRight();
+ break;
+ case ANCESTOR:
+ result = match.getOrigin();
+ break;
+ default:
+ }
+ return result;
+ }
+
+ /**
+ * Get the predicate to know the differences concerned by the display of decorators.
+ *
+ * @return The predicate.
+ */
+ protected abstract Predicate<Diff> goodCandidate();
+
+ /**
+ * Get the views which have to be used as reference to build the related decorators from the given
+ * difference of the value concerned by the difference.<br>
+ *
+ * @param difference
+ * The difference.
+ * @return The list of reference views.
+ */
+ protected abstract List<View> getReferenceViews(DiagramDiff difference);
+
+ /**
+ * Get the side where decorators have to be drawn, according to the given reference view and its
+ * match.<br>
+ *
+ * @param match
+ * The match of the reference view.
+ * @param referenceView
+ * The reference view.
+ * @return The side for phantoms.
+ */
+ protected abstract MergeViewerSide getTargetSide(Match match, View referenceView);
+
+ /**
+ * It creates new decorators and registers them.
+ *
+ * @param diff
+ * The related difference used as index for the main decorator.
+ * @param referenceView
+ * The reference view as base for creation of the decorator.
+ * @param referenceFigure
+ * The reference figure as base for creation of the decorator.
+ * @param targetSide
+ * The side where the decorator has to be created.
+ * @return The list of main decorators.
+ */
+ protected abstract AbstractDecorator createAndRegisterDecorator(Diff diff, View referenceView,
+ IFigure referenceFigure, MergeViewerSide targetSide);
+
+ /**
+ * Get the main decorators related to the given difference.
+ *
+ * @param difference
+ * The difference.
+ * @return The list of main decorators.
+ */
+ protected abstract Collection<? extends AbstractDecorator> getDecorators(Diff difference);
+
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java
new file mode 100644
index 000000000..7b8ebb381
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.ICompareColor;
+
+/**
+ * Decorator manager to create, hide or reveal all decorator figures related to graphical changes.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+public class DecoratorsManager implements IDecoratorManager {
+ /** Phantoms manager. */
+ private IDecoratorManager fPhantomManager;
+
+ /** Markers manager. */
+ private IDecoratorManager fMarkerManager;
+
+ /**
+ * Constructor.
+ *
+ * @param compareConfiguration
+ * The compare configuration of the viewer.
+ * @param left
+ * The left area of the viewer.
+ * @param right
+ * The right area of the viewer.
+ * @param ancestor
+ * The ancestor area of the viewer.
+ * @param color
+ * The color of the difference.
+ */
+ public DecoratorsManager(EMFCompareConfiguration compareConfiguration, DiagramMergeViewer left,
+ DiagramMergeViewer right, DiagramMergeViewer ancestor, ICompareColor color) {
+ fPhantomManager = new PhantomManager(compareConfiguration, left, right, ancestor, color);
+ fMarkerManager = new MarkerManager(compareConfiguration, left, right, ancestor, color);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#hideDecorators(org.eclipse.emf.compare.Diff)
+ */
+ public void hideDecorators(Diff difference) {
+ fMarkerManager.hideDecorators(difference);
+ fPhantomManager.hideDecorators(difference);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#revealDecorators(org.eclipse.emf.compare.Diff)
+ */
+ public void revealDecorators(Diff difference) {
+ fMarkerManager.revealDecorators(difference);
+ fPhantomManager.revealDecorators(difference);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#hideAll()
+ */
+ public void hideAll() {
+ fMarkerManager.hideAll();
+ fPhantomManager.hideAll();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
+ */
+ public void removeDecorators(Diff difference) {
+ fMarkerManager.removeDecorators(difference);
+ fPhantomManager.removeDecorators(difference);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#removeAll()
+ */
+ public void removeAll() {
+ fMarkerManager.removeAll();
+ fPhantomManager.removeAll();
+ }
+
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java
index a8a49e858..728067717 100644
--- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2018 Obeo and others.
+ * Copyright (c) 2013, 2020 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
@@ -13,80 +13,32 @@
*******************************************************************************/
package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram;
-import static com.google.common.base.Predicates.and;
-import static com.google.common.base.Predicates.instanceOf;
-import static com.google.common.base.Predicates.or;
import static org.eclipse.emf.compare.merge.AbstractMerger.isInTerminalState;
-import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind;
-import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature;
-import static org.eclipse.emf.compare.utils.EMFComparePredicates.valueIs;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.EventObject;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
import java.util.ResourceBundle;
-import org.eclipse.draw2d.IFigure;
-import org.eclipse.draw2d.Polyline;
-import org.eclipse.draw2d.PolylineConnection;
-import org.eclipse.draw2d.RectangleFigure;
-import org.eclipse.draw2d.geometry.Point;
-import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CommandStack;
import org.eclipse.emf.compare.Diff;
-import org.eclipse.emf.compare.DifferenceKind;
-import org.eclipse.emf.compare.DifferenceState;
-import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.command.impl.CopyCommand;
import org.eclipse.emf.compare.diagram.ide.ui.internal.accessor.IDiagramDiffAccessor;
import org.eclipse.emf.compare.diagram.ide.ui.internal.accessor.IDiagramNodeAccessor;
-import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.DecoratorFigure;
-import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.EdgeFigure;
-import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.NodeFigure;
-import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.NodeListFigure;
-import org.eclipse.emf.compare.diagram.internal.extensions.CoordinatesChange;
import org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff;
-import org.eclipse.emf.compare.diagram.internal.extensions.Hide;
-import org.eclipse.emf.compare.diagram.internal.extensions.Show;
import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer;
import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.tree.TreeContentMergeViewerContentProvider;
-import org.eclipse.emf.compare.internal.utils.DiffUtil;
import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer;
import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide;
-import org.eclipse.emf.compare.utils.ReferenceUtil;
import org.eclipse.emf.ecore.EObject;
-import org.eclipse.gef.ConnectionEditPart;
-import org.eclipse.gef.EditPart;
-import org.eclipse.gef.GraphicalEditPart;
-import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.RootEditPart;
-import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
-import org.eclipse.gef.editparts.LayerManager;
import org.eclipse.gef.editparts.ZoomListener;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.ui.actions.ZoomComboContributionItem;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart;
-import org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart;
-import org.eclipse.gmf.runtime.diagram.ui.editparts.ListItemEditPart;
-import org.eclipse.gmf.runtime.notation.Diagram;
-import org.eclipse.gmf.runtime.notation.Edge;
-import org.eclipse.gmf.runtime.notation.NotationPackage;
-import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
@@ -104,1642 +56,12 @@ import org.eclipse.ui.PlatformUI;
public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer implements ZoomListener {
/**
- * Interface for the management of decorators.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- private interface IDecoratorManager {
-
- /**
- * It hides the revealed decorators.
- */
- void hideAll();
-
- /**
- * From a given difference, it hides the related decorators.
- *
- * @param difference
- * The difference.
- */
- void hideDecorators(Diff difference);
-
- /**
- * From a given difference, it reveals the related decorators.
- *
- * @param difference
- * The difference.
- */
- void revealDecorators(Diff difference);
-
- /**
- * From a given difference, it removes the related decorators from cash.
- *
- * @param difference
- * The difference.
- */
- void removeDecorators(Diff difference);
-
- /**
- * It removes all the displayed decorators from cache.
- */
- void removeAll();
- }
-
- /**
- * Decorator manager to create, hide or reveal decorator figures related to deleted or added graphical
- * objects.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- private abstract class AbstractDecoratorManager implements IDecoratorManager {
-
- /**
- * Decorator represented by a <code>figure</code> on a <code>layer</code>, from the given
- * <code>side</code> of the merge viewer. An edit part may be linked to the <code>figure</code> in
- * some cases.<br>
- * The decorator is related to a <code>difference</code> and it is binded with the reference view and
- * figure.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- protected abstract class AbstractDecorator {
-
- /** The reference <code>View</code> for this decorator. */
- protected View fOriginView;
-
- /** The reference <code>IFigure</code> for this decorator. */
- protected IFigure fOriginFigure;
-
- /** The <code>IFigure</code> representing this decorator. */
- protected IFigure fFigure;
-
- /**
- * The <code>DecoratorFigure</code> representing this decorator. It references
- * {@link AbstractDecorator#fFigure} through the accessor {@link DecoratorFigure#getMainFigure()}.
- */
- protected DecoratorFigure fDecoratorFigure;
-
- /** The layer on which the <code>figure</code> has to be drawn. */
- protected IFigure fLayer;
-
- /** The side of the merge viewer on which the <code>figure</code> has to be drawn. */
- protected MergeViewerSide fSide;
-
- /** The difference related to this phantom. */
- protected Diff fDifference;
-
- /** The edit part of the figure representing this phantom. May be null. */
- protected EditPart fEditPart;
-
- /**
- * Getter.
- *
- * @return the originView {@link AbstractDecorator#fOriginView}.
- */
- public View getOriginView() {
- return fOriginView;
- }
-
- /**
- * Setter.
- *
- * @param originView
- * {@link AbstractDecorator#fOriginView}.
- */
- public void setOriginView(View originView) {
- this.fOriginView = originView;
- }
-
- /**
- * Getter.
- *
- * @return the originFigure {@link AbstractDecorator#fOriginFigure}.
- */
- public IFigure getOriginFigure() {
- return fOriginFigure;
- }
-
- /**
- * Setter.
- *
- * @param originFigure
- * {@link AbstractDecorator#fOriginFigure}.
- */
- public void setOriginFigure(IFigure originFigure) {
- this.fOriginFigure = originFigure;
- }
-
- /**
- * Getter.
- *
- * @return the figure {@link AbstractDecorator#fFigure}.
- */
- public IFigure getFigure() {
- return fFigure;
- }
-
- /**
- * Getter.
- *
- * @return the decorator figure {@link AbstractDecorator#fDecoratorFigure}.
- */
- public DecoratorFigure getDecoratorFigure() {
- return fDecoratorFigure;
- }
-
- /**
- * Setter.
- *
- * @param figure
- * {@link AbstractDecorator#fFigure}.
- */
- public void setFigure(IFigure figure) {
- this.fFigure = figure;
- }
-
- /**
- * Setter.
- *
- * @param figure
- * {@link AbstractDecorator#fFigure}.
- */
- public void setDecoratorFigure(DecoratorFigure figure) {
- this.fDecoratorFigure = figure;
- setFigure(figure.getMainFigure());
- }
-
- /**
- * Getter.
- *
- * @return the layer {@link AbstractDecorator#fLayer}.
- */
- public IFigure getLayer() {
- return fLayer;
- }
-
- /**
- * Setter.
- *
- * @param layer
- * {@link AbstractDecorator#fLayer}.
- */
- public void setLayer(IFigure layer) {
- this.fLayer = layer;
- }
-
- /**
- * Getter.
- *
- * @return the side {@link AbstractDecorator#fSide}.
- */
- public MergeViewerSide getSide() {
- return fSide;
- }
-
- /**
- * Setter.
- *
- * @param side
- * {@link AbstractDecorator#fSide}.
- */
- public void setSide(MergeViewerSide side) {
- this.fSide = side;
- }
-
- /**
- * Getter.
- *
- * @return the difference {@link AbstractDecorator#fDifference}.
- */
- public Diff getDifference() {
- return fDifference;
- }
-
- /**
- * Setter.
- *
- * @param difference
- * {@link AbstractDecorator#fDifference}.
- */
- public void setDifference(Diff difference) {
- this.fDifference = difference;
- }
-
- /**
- * Getter.
- *
- * @return the editPart {@link AbstractDecorator#fEditPart}.
- */
- public EditPart getEditPart() {
- return fEditPart;
- }
-
- /**
- * Setter.
- *
- * @param editPart
- * {@link AbstractDecorator#fEditPart}.
- */
- public void setEditPart(EditPart editPart) {
- this.fEditPart = editPart;
- }
-
- }
-
- /**
- * From a given difference, it hides the related decorators.
- *
- * @param difference
- * The difference.
- */
- public void hideDecorators(Diff difference) {
- Collection<? extends AbstractDecorator> oldDecorators = getDecorators(difference);
- if (oldDecorators != null && !oldDecorators.isEmpty()
- && getCompareConfiguration().getComparison() != null) {
- handleDecorators(oldDecorators, false, true);
- }
- }
-
- /**
- * From a given difference, it reveals the related decorators.
- *
- * @param difference
- * The difference.
- */
- public void revealDecorators(Diff difference) {
-
- Collection<? super AbstractDecorator> decorators = (Collection<? super AbstractDecorator>)getDecorators(
- difference);
-
- // Create decorators only if they do not already exist and if the selected difference is a good
- // candidate for that.
- if ((decorators == null || decorators.isEmpty()) && isGoodCandidate(difference)) {
-
- DiagramDiff diagramDiff = (DiagramDiff)difference;
-
- List<View> referenveViews = getReferenceViews(diagramDiff);
-
- for (View referenceView : referenveViews) {
- IFigure referenceFigure = getFigure(referenceView);
-
- if (referenceFigure != null) {
- MergeViewerSide targetSide = getTargetSide(
- getCompareConfiguration().getComparison().getMatch(referenceView),
- referenceView);
-
- if (decorators == null) {
- decorators = new ArrayList<AbstractDecorator>();
- }
-
- AbstractDecorator decorator = createAndRegisterDecorator(difference, referenceView,
- referenceFigure, targetSide);
- if (decorator != null) {
- decorators.add(decorator);
- }
- }
-
- }
-
- }
-
- // The selected difference is a good candidate and decorators exist for it
- if (decorators != null && !decorators.isEmpty()) {
- revealDecorators((Collection<? extends AbstractDecorator>)decorators);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
- */
- public abstract void removeDecorators(Diff difference);
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#removeAll()
- */
- public abstract void removeAll();
-
- /**
- * It reveals the given decorators.
- *
- * @param decorators
- * The main decorators.
- */
- protected void revealDecorators(Collection<? extends AbstractDecorator> decorators) {
- handleDecorators(decorators, true, true);
- }
-
- /**
- * Get the figure related to the given view.
- *
- * @param view
- * The view.
- * @return the figure.
- */
- protected IFigure getFigure(View view) {
- MergeViewerSide side = getSide(view);
- GraphicalEditPart originEditPart = (GraphicalEditPart)getViewer(side).getEditPart(view);
- if (originEditPart != null) {
- return originEditPart.getFigure();
- }
- return null;
- }
-
- /**
- * It manages the display of the given decorators.<br>
- *
- * @param decorators
- * The decorators to handle.
- * @param isAdd
- * True if it has to be revealed, False otherwise.
- * @param areMain
- * It indicates if the given decorators to handle are considered as the main ones (the ones
- * directly linked to the selected difference).
- */
- protected void handleDecorators(Collection<? extends AbstractDecorator> decorators, boolean isAdd,
- boolean areMain) {
- for (AbstractDecorator decorator : decorators) {
- handleDecorator(decorator, isAdd, areMain);
- }
- }
-
- /**
- * It manages the display of the given decorator.
- *
- * @param decorator
- * The decorator to handle.
- * @param isAdd
- * True if it has to be revealed, False otherwise.
- * @param isMain
- * It indicates if the given decorator to handle is considered as the main one (the one
- * directly linked to the selected difference).
- */
- protected void handleDecorator(AbstractDecorator decorator, boolean isAdd, boolean isMain) {
- IFigure layer = decorator.getLayer();
- IFigure figure = decorator.getFigure();
- if (isAdd) {
- handleAddDecorator(decorator, layer, figure, isMain);
- } else if (layer.getChildren().contains(figure)) {
- handleDeleteDecorator(decorator, layer, figure);
- }
- }
-
- /**
- * It manages the reveal of the given decorator.
- *
- * @param decorator
- * The decorator.
- * @param parent
- * The parent figure which has to get the figure to reveal (<code>toAdd</code>)
- * @param toAdd
- * The figure to reveal.
- * @param isMain
- * It indicates if the given decorator to reveal is considered as the main one (the one
- * directly linked to the selected difference).
- */
- protected void handleAddDecorator(AbstractDecorator decorator, IFigure parent, IFigure toAdd,
- boolean isMain) {
- if (decorator.getEditPart() != null) {
- decorator.getEditPart().activate();
- }
- if (!parent.getChildren().contains(toAdd)) {
- parent.add(toAdd);
- }
- }
-
- /**
- * It manages the hiding of the given decorator.
- *
- * @param decorator
- * The decorator.
- * @param parent
- * The parent figure which has to get the figure to hide (<code>toDelete</code>)
- * @param toDelete
- * The figure to hide.
- */
- protected void handleDeleteDecorator(AbstractDecorator decorator, IFigure parent, IFigure toDelete) {
- if (decorator.getEditPart() != null) {
- decorator.getEditPart().deactivate();
- }
- if (parent.getChildren().contains(toDelete)) {
- parent.remove(toDelete);
- }
- }
-
- /**
- * It checks if the given difference is a good candidate to manage decorators.<br>
- *
- * @see {@link PhantomManager#goodCandidate()}.
- * @param difference
- * The difference.
- * @return True if it is a good candidate, False otherwise.
- */
- private boolean isGoodCandidate(Diff difference) {
- return goodCandidate().apply(difference);
- }
-
- /**
- * Get the layer on the given side, from the reference view.<br>
- *
- * @see @ link PhantomManager#getIDLayer(View)} .
- * @param referenceView
- * The reference view.
- * @param side
- * The side where the layer has to be found.
- * @return The layer figure or null if the edit part of the diagram is not found.
- */
- protected IFigure getLayer(View referenceView, MergeViewerSide side) {
- Diagram referenceDiagram = referenceView.getDiagram();
- Diagram targetDiagram = (Diagram)getMatchView(referenceDiagram, getEffectiveSide(side));
- DiagramMergeViewer targetViewer = getViewer(side);
- EditPart editPart = targetViewer.getEditPart(targetDiagram);
- if (editPart != null) {
- return LayerManager.Helper.find(editPart).getLayer(getIDLayer(referenceView));
- }
- return null;
- }
-
- /**
- * Get the layer ID to use from the reference view.<br>
- * If the reference view is an edge, it is the {@link LayerConstants.CONNECTION_LAYER} which is used,
- * {@link LayerConstants.SCALABLE_LAYERS} otherwise.
- *
- * @param referenceView
- * The reference view.
- * @return The ID of the layer.
- */
- protected Object getIDLayer(View referenceView) {
- if (referenceView instanceof Edge) {
- return LayerConstants.CONNECTION_LAYER;
- } else {
- return LayerConstants.SCALABLE_LAYERS;
- }
- }
-
- /**
- * It translates the coordinates of the given bounds, from the reference figure and the root of this
- * one, to absolute coordinates.
- *
- * @param referenceFigure
- * The reference figure.
- * @param rootReferenceFigure
- * The root of the reference figure.
- * @param boundsToTranslate
- * The bounds to translate.
- */
- protected void translateCoordinates(IFigure referenceFigure, IFigure rootReferenceFigure,
- Rectangle boundsToTranslate) {
- IFigure referenceParentFigure = referenceFigure.getParent();
- if (referenceParentFigure != null && referenceFigure != rootReferenceFigure) {
- // rootReferenceFigure may be located to (-x,0)... We consider that the root reference is
- // always (0,0)
- if (referenceParentFigure.isCoordinateSystem()
- && referenceParentFigure != rootReferenceFigure) {
- boundsToTranslate.x += referenceParentFigure.getBounds().x;
- boundsToTranslate.y += referenceParentFigure.getBounds().y;
- }
- translateCoordinates(referenceParentFigure, rootReferenceFigure, boundsToTranslate);
- }
- }
-
- /**
- * It checks that the given view represents an element of a list.
- *
- * @param view
- * The view.
- * @return True it it is an element of a list.
- */
- protected boolean isNodeList(View view) {
- DiagramMergeViewer viewer = getViewer(getSide(view));
- EditPart part = viewer.getEditPart(view);
- return isNodeList(part);
- }
-
- /**
- * It checks that the given part represents an element of a list.
- *
- * @param part
- * The part.
- * @return True it it represents an element of a list.
- */
- private boolean isNodeList(EditPart part) {
- return part instanceof ListItemEditPart || isInListContainer(part);
-
- }
-
- /**
- * It checks that the given part is in one representing a list container.
- *
- * @param part
- * The part.
- * @return True it it is in a part representing a list container.
- */
- private boolean isInListContainer(EditPart part) {
- EditPart parent = part.getParent();
- while (parent != null) {
- if (parent instanceof ListCompartmentEditPart) {
- return true;
- }
- parent = parent.getParent();
- }
- return false;
- }
-
- /**
- * Get the predicate to know the differences concerned by the display of decorators.
- *
- * @return The predicate.
- */
- protected abstract Predicate<Diff> goodCandidate();
-
- /**
- * Get the views which have to be used as reference to build the related decorators from the given
- * difference of the value concerned by the difference.<br>
- *
- * @param difference
- * The difference.
- * @return The list of reference views.
- */
- protected abstract List<View> getReferenceViews(DiagramDiff difference);
-
- /**
- * Get the side where decorators have to be drawn, according to the given reference view and its
- * match.<br>
- *
- * @param match
- * The match of the reference view.
- * @param referenceView
- * The reference view.
- * @return The side for phantoms.
- */
- protected abstract MergeViewerSide getTargetSide(Match match, View referenceView);
-
- /**
- * It creates new decorators and registers them.
- *
- * @param diff
- * The related difference used as index for the main decorator.
- * @param referenceView
- * The reference view as base for creation of the decorator.
- * @param referenceFigure
- * The reference figure as base for creation of the decorator.
- * @param targetSide
- * The side where the decorator has to be created.
- * @return The list of main decorators.
- */
- protected abstract AbstractDecorator createAndRegisterDecorator(Diff diff, View referenceView,
- IFigure referenceFigure, MergeViewerSide targetSide);
-
- /**
- * Get the main decorators related to the given difference.
- *
- * @param difference
- * The difference.
- * @return The list of main decorators.
- */
- protected abstract Collection<? extends AbstractDecorator> getDecorators(Diff difference);
-
- }
-
- /**
- * Phantom manager to create, hide or reveal phantom figures related to deleted or added graphical
- * objects.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- private class PhantomManager extends AbstractDecoratorManager {
-
- /**
- * Phantom represented by a <code>figure</code> on a <code>layer</code>, from the given
- * <code>side</code> of the merge viewer. An edit part may be linked to the <code>figure</code> in
- * some cases.<br>
- * The phantom is related to a <code>difference</code> and it is binded with the reference view and
- * figure.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- private class Phantom extends AbstractDecorator {
-
- /**
- * Constructor.
- *
- * @param layer
- * {@link Phantom#fLayer}.
- * @param side
- * {@link Phantom#fSide}.
- * @param originView
- * {@link Phantom#fOriginView}.
- * @param originFigure
- * {@link Phantom#fOriginFigure}.
- * @param diff
- * {@link Phantom#fDifference}.
- */
- Phantom(IFigure layer, MergeViewerSide side, View originView, IFigure originFigure, Diff diff) {
- setLayer(layer);
- setSide(side);
- setOriginView(originView);
- setOriginFigure(originFigure);
- setDifference(diff);
- }
-
- /**
- * Get the decorator dependencies of this one. The dependencies are the decorator ancestors plus
- * the extremities of an edge decorator.<br>
- * DO NOT CALL in an iterate of {@link PhantomManager#fPhantomRegistry}
- *
- * @return The list of found decorators.
- */
- public List<? extends AbstractDecorator> getDependencies() {
- List<AbstractDecorator> result = new ArrayList<AbstractDecorator>();
- result.addAll(getAncestors());
- if (fOriginView instanceof Edge) {
- View source = ((Edge)fOriginView).getSource();
- View target = ((Edge)fOriginView).getTarget();
- result.addAll(getOrCreateRelatedPhantoms(source, fSide));
- result.addAll(getOrCreateRelatedPhantoms(target, fSide));
- }
- return result;
- }
-
- /**
- * Get the ancestor decorators of this one.
- *
- * @return The list of the ancestors.
- */
- private List<? extends AbstractDecorator> getAncestors() {
- List<AbstractDecorator> result = new ArrayList<AbstractDecorator>();
- EObject parentOriginView = fOriginView.eContainer();
- while (parentOriginView != null) {
- result.addAll(getOrCreateRelatedPhantoms(parentOriginView, fSide));
- parentOriginView = parentOriginView.eContainer();
- }
- return result;
- }
- }
-
- /** Registry of created phantoms, indexed by difference. */
- private final Map<Diff, Phantom> fPhantomRegistry = new HashMap<Diff, Phantom>();
-
- /** Predicate witch checks that the given difference is an ADD or DELETE of a graphical object. */
- private Predicate<Diff> isAddOrDelete = and(instanceOf(DiagramDiff.class),
- or(ofKind(DifferenceKind.ADD), ofKind(DifferenceKind.DELETE)));
-
- /** Predicate witch checks that the given difference is a HIDE or REVEAL of a graphical object. */
- private Predicate<Diff> isHideOrReveal = or(instanceOf(Show.class), instanceOf(Hide.class));
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.
- * DiagramContentMergeViewer.AbstractDecoratorManager#goodCandidate()<br>
- * Only the diagram differences ADD/REVEAL or DELETE/HIDE are concerned by this display.
- */
- @Override
- protected Predicate<Diff> goodCandidate() {
- return new Predicate<Diff>() {
- public boolean apply(Diff difference) {
- return Predicates.or(isAddOrDelete, isHideOrReveal).apply(difference)
- && difference.getState() == DifferenceState.UNRESOLVED;
- }
- };
- }
-
- /**
- * From the given view, get or create the related phantoms in the given side.
- *
- * @param referenceView
- * The given view.
- * @param side
- * The given side.
- * @return The list of phantoms.
- */
- private List<Phantom> getOrCreateRelatedPhantoms(EObject referenceView, MergeViewerSide side) {
- List<Phantom> result = new ArrayList<Phantom>();
- Collection<Diff> changes = Collections2.filter(
- getCompareConfiguration().getComparison().getDifferences(referenceView), goodCandidate());
- for (Diff change : changes) {
- Phantom phantom = fPhantomRegistry.get(change);
- if (phantom == null) {
- IFigure referenceFigure = PhantomManager.this.getFigure((View)referenceView);
- if (referenceFigure != null) {
- phantom = createAndRegisterDecorator(change, (View)referenceView, referenceFigure,
- side);
- }
- }
- if (phantom != null) {
- result.add(phantom);
- }
- }
- return result;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#getReferenceViews(org.eclipse.emf.compare.diagram.DiagramDiff)
- */
- @Override
- protected List<View> getReferenceViews(DiagramDiff difference) {
- List<View> result = new ArrayList<View>();
-
- Match match = getCompareConfiguration().getComparison().getMatch(difference.getView());
-
- EObject originObj = match.getOrigin();
- EObject leftObj = match.getLeft();
- EObject rightObj = match.getRight();
-
- if (leftObj instanceof View || rightObj instanceof View) {
- View referenceView = getReferenceView((View)originObj, (View)leftObj, (View)rightObj);
- // It may be null if it is hidden
- if (referenceView != null) {
- result.add(referenceView);
- }
- }
-
- return result;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.
- * DiagramContentMergeViewer.AbstractDecoratorManager#getTargetSide(org.eclipse.emf.compare.
- * Match, org.eclipse.gmf.runtime.notation.View)<br>
- * If the left object is null, a phantom should be drawn instead. Else, it means that the right
- * object is null and a phantom should be displayed on the right side.
- */
- @Override
- protected MergeViewerSide getTargetSide(Match match, View referenceView) {
- MergeViewerSide targetSide = null;
- if (!isFigureExist((View)match.getLeft())) {
- targetSide = MergeViewerSide.LEFT;
- } else {
- targetSide = MergeViewerSide.RIGHT;
- }
- return targetSide;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#createAndRegisterDecorator(org.eclipse.emf.compare.Diff,
- * org.eclipse.gmf.runtime.notation.View, org.eclipse.draw2d.IFigure,
- * org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide)
- */
- @Override
- protected Phantom createAndRegisterDecorator(Diff diff, View referenceView, IFigure referenceFigure,
- MergeViewerSide targetSide) {
- Phantom phantom = createPhantom(diff, referenceView, referenceFigure, targetSide);
- if (phantom != null) {
- fPhantomRegistry.put(diff, phantom);
- }
- return phantom;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
- */
- @Override
- public void removeDecorators(Diff difference) {
- fPhantomRegistry.remove(difference);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#removeAll()
- */
- @Override
- public void removeAll() {
- fPhantomRegistry.clear();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#getDecorators(org.eclipse.emf.compare.Diff)
- */
- @Override
- protected List<Phantom> getDecorators(Diff difference) {
- List<Phantom> result = new ArrayList<DiagramContentMergeViewer.PhantomManager.Phantom>();
- Phantom phantom = fPhantomRegistry.get(difference);
- if (phantom != null) {
- result.add(phantom);
- }
- return result;
- }
-
- /**
- * {@inheritDoc}.<br>
- * DO NOT CALL on a phantom within an iteration on the phantom registry.
- * {@link PhantomManager#fPhantomRegistry}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#handleDecorator(org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager.AbstractDecorator,
- * boolean)
- */
- @Override
- protected void handleDecorator(AbstractDecorator decorator, boolean isAdd, boolean isMain) {
- super.handleDecorator(decorator, isAdd, isMain);
- // Display the dependencies (context) of this decorator
- for (AbstractDecorator ancestor : ((Phantom)decorator).getDependencies()) {
- super.handleDecorator(ancestor, isAdd, false);
- }
- }
-
- @Override
- protected void handleAddDecorator(AbstractDecorator decorator, IFigure parent, IFigure toAdd,
- boolean isMain) {
- super.handleAddDecorator(decorator, parent, toAdd, isMain);
- // Set the highlight of the figure
- if (isMain) {
- decorator.getDecoratorFigure().highlight();
- getViewer(decorator.getSide()).getGraphicalViewer().reveal(toAdd);
- } else {
- decorator.getDecoratorFigure().unhighlight();
- }
- }
-
- /**
- * It checks that the given view graphically exists.
- *
- * @param view
- * The view.
- * @return True if it exists.
- */
- private boolean isFigureExist(View view) {
- return view != null && view.isVisible();
- }
-
- /**
- * Get the view which has to be used as reference to build a phantom.<br>
- * The reference is the non null object among the given objects. In case of delete object, in the
- * context of three-way comparison, the reference will be the ancestor one (<code>originObj</code>).
- *
- * @param originObj
- * The ancestor object.
- * @param leftView
- * The left object.
- * @param rightView
- * The right object.
- * @return The reference object.
- */
- private View getReferenceView(View originObj, View leftView, View rightView) {
- View referenceView;
- if (isFigureExist(originObj)) {
- referenceView = originObj;
- } else if (isFigureExist(leftView)) {
- referenceView = leftView;
- } else {
- referenceView = rightView;
- }
- return referenceView;
- }
-
- /**
- * It creates a new phantom from the given difference, view and figure.
- *
- * @param diff
- * The related difference used as index for the main phantom.
- * @param referenceView
- * The reference view as base for creation of the phantom.
- * @param referenceFigure
- * The reference figure as base for creation of the phantom.
- * @param side
- * The side where the phantom has to be created.
- * @return The phantom or null if the target layer is not found.
- */
- private Phantom createPhantom(Diff diff, View referenceView, IFigure referenceFigure,
- MergeViewerSide side) {
- IFigure targetLayer = getLayer(referenceView, side);
- if (targetLayer != null) {
- MergeViewerSide referenceSide = getSide(referenceView);
-
- Rectangle rect = referenceFigure.getBounds().getCopy();
-
- IFigure referenceLayer = getLayer(referenceView, referenceSide);
- translateCoordinates(referenceFigure, referenceLayer, rect);
-
- DecoratorFigure ghost = null;
-
- Phantom phantom = new Phantom(targetLayer, side, referenceView, referenceFigure, diff);
-
- // Container "list" case
- if (isNodeList(referenceView)) {
-
- int index = getIndex(diff, referenceView, side);
-
- IFigure referenceParentFigure = referenceFigure.getParent();
- Rectangle referenceParentBounds = referenceParentFigure.getBounds().getCopy();
- translateCoordinates(referenceParentFigure, referenceLayer, referenceParentBounds);
-
- View parentView = (View)getMatchView(referenceView.eContainer(), side);
- if (parentView != null) {
- int nbElements = getVisibleViews(parentView).size();
- // CHECKSTYLE:OFF
- if (index > nbElements) {
- // CHECKSTYLE:ON
- index = nbElements;
- }
- }
-
- // FIXME: The add of decorators modifies the physical coordinates of elements
- // FIXME: Compute position from the y position of the first child + sum of height of the
- // children.
- int pos = rect.height * index + referenceParentBounds.y + 1;
- Map<String, Object> parameters = new HashMap<String, Object>();
- parameters.put(NodeListFigure.PARAM_Y_POS, Integer.valueOf(pos));
-
- ghost = new NodeListFigure(diff, isThreeWay(), getCompareColor(), referenceFigure, rect,
- true, parameters);
-
- // Edge case
- } else if (referenceView instanceof Edge) {
- // If the edge phantom ties shapes where their coordinates changed
- if (hasAnExtremityChange((Edge)referenceView, side)) {
- EditPart edgeEditPart = createEdgeEditPart((Edge)referenceView, referenceSide, side);
- // CHECKSTYLE:OFF
- if (edgeEditPart instanceof GraphicalEditPart) {
- // CHECKSTYLE:ON
- phantom.setEditPart(edgeEditPart);
-
- IFigure fig = ((GraphicalEditPart)edgeEditPart).getFigure();
- fig.getChildren().clear();
- ghost = new DecoratorFigure(diff, isThreeWay(), getCompareColor(),
- referenceFigure, fig, true);
-
- }
- // Else, it creates only a polyline connection figure with the same properties as the
- // reference
- } else {
- if (referenceFigure instanceof PolylineConnection) {
- ghost = new EdgeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
- rect, true);
- }
- }
- }
-
- // Default case: Nodes
- if (ghost == null) {
- ghost = new NodeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure, rect,
- true);
- }
-
- phantom.setDecoratorFigure(ghost);
-
- translateWhenInsideContainerChange(phantom);
-
- return phantom;
- }
-
- return null;
-
- }
-
- /**
- * Get the index of the phantom to draw.
- *
- * @param diff
- * The related difference used as index for the main phantom.
- * @param referenceView
- * The reference view to compute the phantom.
- * @param side
- * The side where the phantom has to be drawn.
- * @return The index in the list where the phantom has to be drawn.
- */
- private int getIndex(Diff diff, View referenceView, MergeViewerSide side) {
- // Case for invisible objects
- if (diff instanceof Hide || diff instanceof Show) {
- List<Object> source = null;
- List<Object> target = null;
- Object newElement = null;
- Match match = diff.getMatch();
- List<Object> leftList = ReferenceUtil.getAsList(match.getLeft().eContainer(),
- NotationPackage.Literals.VIEW__PERSISTED_CHILDREN);
- List<Object> rightList = ReferenceUtil.getAsList(match.getRight().eContainer(),
- NotationPackage.Literals.VIEW__PERSISTED_CHILDREN);
- if (diff instanceof Hide) {
- source = rightList;
- target = leftList;
- newElement = diff.getMatch().getRight();
- } else {
- source = leftList;
- target = rightList;
- newElement = diff.getMatch().getLeft();
- }
- Iterable<Object> ignoredElements = Iterables.filter(target, new Predicate() {
- public boolean apply(Object input) {
- return input instanceof View && !((View)input).isVisible();
- }
- });
- return DiffUtil.findInsertionIndex(getCompareConfiguration().getComparison(), ignoredElements,
- source, target, newElement);
- }
- // Case for deleted objects
- Diff refiningDiff = Iterators.find(diff.getRefinedBy().iterator(), and(valueIs(referenceView),
- onFeature(NotationPackage.Literals.VIEW__PERSISTED_CHILDREN.getName())));
-
- return DiffUtil.findInsertionIndex(getCompareConfiguration().getComparison(), refiningDiff,
- side == MergeViewerSide.LEFT);
- }
-
- /**
- * Get the visible view under the given parent view.
- *
- * @param parent
- * The parent view.
- * @return The list of views.
- */
- private List<View> getVisibleViews(View parent) {
- return (List<View>)Lists
- .newArrayList(Iterators.filter(parent.getChildren().iterator(), new Predicate<Object>() {
- public boolean apply(Object input) {
- return input instanceof View && ((View)input).isVisible();
- }
- }));
- }
-
- /**
- * It translates and resizes the figure of the given phantom when this one is nested in a container
- * which is subjected to a coordinates change.
- *
- * @param phantom
- * The phantom.
- */
- private void translateWhenInsideContainerChange(Phantom phantom) {
- boolean isCandidate = false;
- Diff diff = phantom.getDifference();
- if (diff instanceof DiagramDiff) {
- EObject parent = ((DiagramDiff)diff).getView().eContainer();
- while (parent instanceof View && !isCandidate) {
- isCandidate = Iterables.any(
- getCompareConfiguration().getComparison().getDifferences(parent),
- instanceOf(CoordinatesChange.class));
- parent = parent.eContainer();
- }
- }
- if (isCandidate) {
- View referenceView = phantom.getOriginView();
- View parentReferenceView = (View)referenceView.eContainer();
- if (parentReferenceView != null) {
- View parentView = (View)getMatchView(parentReferenceView, phantom.getSide());
- IFigure parentFigure = getFigure(parentView);
- if (parentFigure != null) {
- Rectangle parentRect = parentFigure.getBounds().getCopy();
- translateCoordinates(parentFigure, getLayer(parentReferenceView, getSide(parentView)),
- parentRect);
-
- IFigure parentReferenceFigure = getFigure(parentReferenceView);
- // CHECKSTYLE:OFF
- if (parentReferenceFigure != null) {
- Rectangle parentReferenceRect = parentReferenceFigure.getBounds().getCopy();
- translateCoordinates(parentReferenceFigure,
- getLayer(parentReferenceView, getSide(parentReferenceView)),
- parentReferenceRect);
-
- int deltaX = parentRect.x - parentReferenceRect.x;
- int deltaY = parentRect.y - parentReferenceRect.y;
- int deltaWidth = parentRect.width - parentReferenceRect.width;
- int deltaHeight = parentRect.height - parentReferenceRect.height;
-
- IFigure figure = phantom.getFigure();
-
- Rectangle rect = figure.getBounds().getCopy();
- rect.x += deltaX;
- rect.y += deltaY;
- rect.width += deltaWidth;
- if (!(figure instanceof Polyline)) {
- rect.height += deltaHeight;
- }
- figure.setBounds(rect);
-
- if (figure instanceof Polyline) {
-
- Point firstPoint = ((Polyline)figure).getPoints().getFirstPoint().getCopy();
- Point lastPoint = ((Polyline)figure).getPoints().getLastPoint().getCopy();
-
- firstPoint.x += deltaX;
- firstPoint.y += deltaY;
-
- lastPoint.x += deltaX + deltaWidth;
- lastPoint.y += deltaY;
-
- ((Polyline)figure).setEndpoints(firstPoint, lastPoint);
-
- }
- }
- // CHECKSTYLE:ON
- }
- }
- }
- }
-
- /**
- * It checks that the given edge is linked to graphical objects subjected to coordinate changes, on
- * the given side.
- *
- * @param edge
- * The edge to check.
- * @param targetSide
- * The side to check extremities (side of the phantom).
- * @return True if an extremity at least changed its location, False otherwise.
- */
- private boolean hasAnExtremityChange(Edge edge, MergeViewerSide targetSide) {
- View referenceSource = edge.getSource();
- View referenceTarget = edge.getTarget();
- return hasChange(referenceSource, targetSide) || hasChange(referenceTarget, targetSide);
- }
-
- /**
- * It checks that the coordinates of the given view changed between left and right, from the given
- * side.
- *
- * @param referenceView
- * The view to check.
- * @param targetSide
- * The side to focus.
- * @return True if the view changed its location, False otherwise.
- */
- private boolean hasChange(View referenceView, MergeViewerSide targetSide) {
- View extremity = (View)getMatchView(referenceView, targetSide);
- // Look for a related change coordinates on the extremity of the edge reference.
- Collection<Diff> diffs = Collections2.filter(
- getCompareConfiguration().getComparison().getDifferences(referenceView),
- instanceOf(CoordinatesChange.class));
- if (diffs.isEmpty()) {
- // Look for a related change coordinates on the matching extremity (other side) of the edge
- // reference.
- diffs = Collections2.filter(
- getCompareConfiguration().getComparison().getDifferences(extremity),
- instanceOf(CoordinatesChange.class));
- }
- return !diffs.isEmpty();
- }
-
- /**
- * It creates and returns a new edit part from the given edge. This edit part listens to the reference
- * edge but is attached to the controllers of the target (phantom) side.
- *
- * @param referenceEdge
- * The edge as base of the edit part.
- * @param referenceSide
- * The side of this edge.
- * @param targetSide
- * The side where the edit part has to be created to draw the related phantom.
- * @return The new edit part.
- */
- private EditPart createEdgeEditPart(Edge referenceEdge, MergeViewerSide referenceSide,
- MergeViewerSide targetSide) {
- EditPart edgeEditPartReference = getViewer(referenceSide).getEditPart(referenceEdge);
- EditPart edgeEditPart = null;
- if (edgeEditPartReference instanceof ConnectionEditPart) {
-
- edgeEditPart = getOrCreatePhantomEditPart(referenceEdge, referenceSide, targetSide);
-
- if (edgeEditPart instanceof ConnectionEditPart) {
- EditPart edgeSourceEp = getOrCreateExtremityPhantomEditPart(
- ((ConnectionEditPart)edgeEditPartReference).getSource(), referenceSide,
- targetSide);
-
- ((ConnectionEditPart)edgeEditPart).setSource(edgeSourceEp);
-
- EditPart edgeTargetEp = getOrCreateExtremityPhantomEditPart(
- ((ConnectionEditPart)edgeEditPartReference).getTarget(), referenceSide,
- targetSide);
-
- ((ConnectionEditPart)edgeEditPart).setTarget(edgeTargetEp);
- }
- }
- return edgeEditPart;
- }
-
- /**
- * From the given edit part, it retrieves the matched one, from the given target side. If the
- * retrieved edit part is not linked to a GMF object, in the target side, a phantom GEF edit part is
- * returned which will locate a rectangle invisible figure in the same location as the related
- * phantom.
- *
- * @param referenceEdgeExtremityEp
- * The reference edit part for one of the extremities of an edge.
- * @param referenceSide
- * The side of the reference.
- * @param targetSide
- * The other side, where the phantom has to be drawn.
- * @return The phantom edit part used to attach the extremity of an edge phantom.
- */
- private EditPart getOrCreateExtremityPhantomEditPart(EditPart referenceEdgeExtremityEp,
- MergeViewerSide referenceSide, MergeViewerSide targetSide) {
- View referenceExtremityView = (View)referenceEdgeExtremityEp.getModel();
-
- EditPart edgeExtremityEp = getOrCreatePhantomEditPart(referenceExtremityView, referenceSide,
- targetSide);
-
- if (isPhantomEditPart((AbstractGraphicalEditPart)edgeExtremityEp)) {
-
- final AbstractGraphicalEditPart edgeExtremityEpParent = (AbstractGraphicalEditPart)edgeExtremityEp
- .getParent();
-
- List<Phantom> phantoms = getOrCreateRelatedPhantoms(referenceExtremityView, targetSide);
- if (!phantoms.isEmpty()) {
- Phantom phantomToTarget = phantoms.get(0);
- final IFigure figureToTarget = phantomToTarget.getFigure();
-
- edgeExtremityEp = new org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart(
- referenceExtremityView) {
- @Override
- protected void createDefaultEditPolicies() {
- }
-
- @Override
- protected IFigure createFigure() {
- RectangleFigure fig = new RectangleFigure();
- fig.setBounds(figureToTarget.getBounds());
- fig.setParent(edgeExtremityEpParent.getFigure());
- return fig;
- }
- };
-
- edgeExtremityEp.setParent(edgeExtremityEpParent);
- }
-
- ((AbstractGraphicalEditPart)edgeExtremityEp).activate();
- ((AbstractGraphicalEditPart)edgeExtremityEp).getFigure();
- }
-
- return edgeExtremityEp;
- }
-
- /**
- * It checks if the given edit part is related to a phantom edit part (created for nothing, without
- * link to a GMF object in the target side).
- *
- * @param editPart
- * The edit part to check.
- * @return True if it is a phantom edit part, false otherwise.
- */
- private boolean isPhantomEditPart(AbstractGraphicalEditPart editPart) {
- Rectangle targetBounds = editPart.getFigure().getBounds();
- return targetBounds.x == 0 && targetBounds.y == 0 && targetBounds.width == 0
- && targetBounds.height == 0;
- }
-
- /**
- * It creates and returns a new edit part from the given view. This edit part listens the reference
- * view but is attached to the controllers of the target (phantom) side.
- *
- * @param referenceView
- * The view as base of the edit part.
- * @param referenceSide
- * The side of this view.
- * @param targetSide
- * The side where the edit part has to be created to draw the related phantom.
- * @return The new edit part.
- */
- private EditPart getOrCreatePhantomEditPart(EObject referenceView, MergeViewerSide referenceSide,
- MergeViewerSide targetSide) {
- EditPart editPartParent = null;
- EditPart editPart = null;
- EditPart editPartReference = getViewer(referenceSide).getEditPart(referenceView);
- EditPart editPartReferenceParent = editPartReference.getParent();
- Object referenceViewParent = editPartReferenceParent.getModel();
- if (!(referenceViewParent instanceof EObject)) {
- referenceViewParent = referenceView.eContainer();
- }
- View viewParent = (View)getMatchView((EObject)referenceViewParent, targetSide);
- if (viewParent != null) {
- editPartParent = getViewer(targetSide).getEditPart(viewParent);
- }
- if (editPartParent == null) {
- editPartParent = getOrCreatePhantomEditPart((EObject)referenceViewParent, referenceSide,
- targetSide);
-
- }
- if (editPartParent != null) {
- View view = (View)getMatchView(referenceView, targetSide);
- if (view != null) {
- editPart = getViewer(targetSide).getEditPart(view);
- }
- if (editPart == null) {
-
- editPart = getViewer(targetSide).getGraphicalViewer().getEditPartFactory()
- .createEditPart(editPartParent, referenceView);
- editPart.setParent(editPartParent);
- getViewer(targetSide).getGraphicalViewer().getEditPartRegistry().put(referenceView,
- editPart);
-
- }
-
- }
- return editPart;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#hideAll()
- */
- public void hideAll() {
- for (Phantom phantom : fPhantomRegistry.values()) {
- handleDeleteDecorator(phantom, phantom.getLayer(), phantom.getFigure());
- }
- }
- }
-
- /**
- * Marker manager to create, hide or reveal marker figures related to deleted or added graphical objects.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- private class MarkerManager extends AbstractDecoratorManager {
-
- /**
- * Marker represented by a <code>figure</code> on a <code>layer</code>, from the given
- * <code>side</code> of the merge viewer. An edit part may be linked to the <code>figure</code> in
- * some cases.<br>
- * The marker is related to a <code>difference</code> and it is binded with the reference view and
- * figure.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- private class Marker extends AbstractDecorator {
-
- /**
- * Constructor.
- *
- * @param layer
- * {@link Marker#fLayer}.
- * @param side
- * {@link Marker#fSide}.
- * @param originView
- * {@link Marker#fOriginView}.
- * @param originFigure
- * {@link Marker#fOriginFigure}.
- * @param diff
- * {@link Marker#fDifference}.
- */
- Marker(IFigure layer, MergeViewerSide side, View originView, IFigure originFigure, Diff diff) {
- setLayer(layer);
- setSide(side);
- setOriginView(originView);
- setOriginFigure(originFigure);
- setDifference(diff);
- }
- }
-
- /** Registry of created markers, indexed by difference. */
- private Multimap<Diff, Marker> fMarkerRegistry = HashMultimap.create();
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#getReferenceViews(org.eclipse.emf.compare.diagram.DiagramDiff)
- */
- @Override
- protected List<View> getReferenceViews(DiagramDiff difference) {
- List<View> result = new ArrayList<View>();
- Match matchValue = getCompareConfiguration().getComparison().getMatch(difference.getView());
- if (matchValue != null) {
- if (matchValue.getLeft() != null) {
- result.add((View)matchValue.getLeft());
- }
- if (matchValue.getRight() != null) {
- result.add((View)matchValue.getRight());
- }
- if (getCompareConfiguration().getComparison().isThreeWay()) {
- switch (difference.getKind()) {
- case DELETE:
- case CHANGE:
- case MOVE:
- result.add((View)matchValue.getOrigin());
- break;
- default:
- break;
- }
- }
- }
- return result;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#getTargetSide(org.eclipse.emf.compare.Match,
- * org.eclipse.gmf.runtime.notation.View)
- */
- @Override
- protected MergeViewerSide getTargetSide(Match match, View referenceView) {
- return getSide(referenceView);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#createAndRegisterDecorator(org.eclipse.emf.compare.Diff,
- * org.eclipse.gmf.runtime.notation.View, org.eclipse.draw2d.IFigure,
- * org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide)
- */
- @Override
- protected Marker createAndRegisterDecorator(Diff diff, View referenceView, IFigure referenceFigure,
- MergeViewerSide targetSide) {
- Marker marker = createMarker(diff, referenceView, referenceFigure, targetSide);
- if (marker != null) {
- fMarkerRegistry.put(diff, marker);
- }
- return marker;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
- */
- @Override
- public void removeDecorators(Diff difference) {
- fMarkerRegistry.removeAll(difference);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#removeAll()
- */
- @Override
- public void removeAll() {
- fMarkerRegistry.clear();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.AbstractDecoratorManager#getDecorators(org.eclipse.emf.compare.Diff)
- */
- @Override
- protected Collection<Marker> getDecorators(Diff difference) {
- return fMarkerRegistry.get(difference);
- }
-
- @Override
- protected void handleAddDecorator(AbstractDecorator decorator, IFigure parent, IFigure toAdd,
- boolean isMain) {
- super.handleAddDecorator(decorator, parent, toAdd, isMain);
- DiagramMergeViewer viewer = getViewer(decorator.getSide());
- EditPart editPart = viewer.getEditPart(decorator.getOriginView());
- if (editPart != null) {
- viewer.getGraphicalViewer().reveal(editPart);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.
- * DiagramContentMergeViewer.AbstractDecoratorManager#goodCandidate()<br>
- * All graphical differences are concerned.
- */
- @Override
- protected Predicate<Diff> goodCandidate() {
- return new Predicate<Diff>() {
- public boolean apply(Diff difference) {
- return instanceOf(DiagramDiff.class).apply(difference);
- }
- };
- }
-
- /**
- * It creates a new marker from the given difference, view and figure.
- *
- * @param diff
- * The related difference used as index for the main marker.
- * @param referenceView
- * The reference view as base for creation of the marker.
- * @param referenceFigure
- * The reference figure as base for creation of the marker.
- * @param side
- * The side where the marker has to be created.
- * @return The phantom or null if the target layer is not found.
- */
- private Marker createMarker(Diff diff, View referenceView, IFigure referenceFigure,
- MergeViewerSide side) {
-
- IFigure referenceLayer = getLayer(referenceView, side);
- if (referenceLayer != null) {
- Rectangle referenceBounds = referenceFigure.getBounds().getCopy();
- translateCoordinates(referenceFigure, referenceLayer, referenceBounds);
-
- DecoratorFigure markerFigure = null;
-
- Marker marker = new Marker(referenceLayer, side, referenceView, referenceFigure, diff);
-
- if (isNodeList(referenceView)) {
- markerFigure = new NodeListFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
- referenceBounds, false);
- } else if (referenceView instanceof Edge && referenceFigure instanceof PolylineConnection) {
- markerFigure = new EdgeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
- referenceBounds, false);
- }
-
- // Default case: Nodes
- if (markerFigure == null) {
- markerFigure = new NodeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
- referenceBounds, false);
- }
-
- marker.setDecoratorFigure(markerFigure);
- return marker;
- }
-
- return null;
-
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#hideAll()
- */
- public void hideAll() {
- for (Marker marker : fMarkerRegistry.values()) {
- handleDeleteDecorator(marker, marker.getLayer(), marker.getFigure());
- }
- }
-
- }
-
- /**
- * Decorator manager to create, hide or reveal all decorator figures related to graphical changes.
- *
- * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
- */
- private class DecoratorsManager implements IDecoratorManager {
- /** Phantoms manager. */
- private IDecoratorManager fPhantomManager = new PhantomManager();
-
- /** Markers manager. */
- private IDecoratorManager fMarkerManager = new MarkerManager();
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#hideDecorators(org.eclipse.emf.compare.Diff)
- */
- public void hideDecorators(Diff difference) {
- fMarkerManager.hideDecorators(difference);
- fPhantomManager.hideDecorators(difference);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#revealDecorators(org.eclipse.emf.compare.Diff)
- */
- public void revealDecorators(Diff difference) {
- fMarkerManager.revealDecorators(difference);
- fPhantomManager.revealDecorators(difference);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#hideAll()
- */
- public void hideAll() {
- fMarkerManager.hideAll();
- fPhantomManager.hideAll();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
- */
- public void removeDecorators(Diff difference) {
- fMarkerManager.removeDecorators(difference);
- fPhantomManager.removeDecorators(difference);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer.IDecoratorManager#removeAll()
- */
- public void removeAll() {
- fMarkerManager.removeAll();
- fPhantomManager.removeAll();
- }
-
- }
-
- /**
* Bundle name of the property file containing all displayed strings.
*/
private static final String BUNDLE_NAME = DiagramContentMergeViewer.class.getName();
/** The phantom manager to use in the context of this viewer. */
- private final DecoratorsManager fDecoratorsManager = new DecoratorsManager();
+ private DecoratorsManager fDecoratorsManager;
/** The current "opened" difference. */
private Diff fCurrentSelectedDiff;
@@ -1819,7 +141,7 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer impl
/**
* {@inheritDoc}
*
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.DiagramCompareContentMergeViewer#createMergeViewer(org.eclipse.swt.widgets.Composite,
+ * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#createMergeViewer(org.eclipse.swt.widgets.Composite,
* org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide)
*/
@Override
@@ -1832,7 +154,7 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer impl
/**
* {@inheritDoc}
*
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.DiagramCompareContentMergeViewer#paintCenter(org.eclipse.swt.graphics.GC)
+ * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#paintCenter(org.eclipse.swt.graphics.GC)
*/
@Override
protected void paintCenter(GC g) {
@@ -1842,12 +164,15 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer impl
/**
* {@inheritDoc}
*
- * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.DiagramCompareContentMergeViewer#updateContent(java.lang.Object,
+ * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#updateContent(java.lang.Object,
* java.lang.Object, java.lang.Object)
*/
@Override
protected void updateContent(Object ancestor, Object left, Object right) {
+ fDecoratorsManager = new DecoratorsManager(getCompareConfiguration(), getLeftMergeViewer(),
+ getRightMergeViewer(), getAncestorMergeViewer(), getCompareColor());
+
// Delete decorators at each selection of a difference (force the computation)
fDecoratorsManager.hideAll();
fDecoratorsManager.removeAll();
@@ -1933,6 +258,11 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer impl
return null;
}
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#createToolItems(org.eclipse.jface.action.ToolBarManager)
+ */
@Override
protected void createToolItems(ToolBarManager toolBarManager) {
super.createToolItems(toolBarManager);
@@ -1943,6 +273,8 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer impl
/**
* {@inheritDoc} When the zoom on any of the sides changes, the same zoom level is applied to other sides.
+ *
+ * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double)
*/
@Override
public void zoomChanged(double zoom) {
@@ -1970,7 +302,7 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer impl
// Delete decorators at each change of the input models (after merging or CTRL-Z, CTRL-Y)
Object source = event.getSource();
- if (source instanceof CommandStack) {
+ if (source instanceof CommandStack && fDecoratorsManager != null) {
Command command = ((CommandStack)source).getMostRecentCommand();
if (command instanceof CopyCommand) {
Iterator<DiagramDiff> diffs = Iterators.filter(command.getAffectedObjects().iterator(),
@@ -2007,88 +339,4 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer impl
getRightMergeViewer().removeSelectionChangedListener(this);
}
- /**
- * Utility method to retrieve the {@link DiagramMergeViewer} from the given side.
- *
- * @param side
- * The side to focus.
- * @return The viewer.
- */
- private DiagramMergeViewer getViewer(MergeViewerSide side) {
- DiagramMergeViewer result = null;
- switch (side) {
- case LEFT:
- result = getLeftMergeViewer();
- break;
- case RIGHT:
- result = getRightMergeViewer();
- break;
- case ANCESTOR:
- result = getAncestorMergeViewer();
- break;
- default:
- }
- return result;
- }
-
- /**
- * Utility method to know the side where is located the given view.
- *
- * @param view
- * The view.
- * @return The side of the view.
- */
- private MergeViewerSide getSide(View view) {
- MergeViewerSide result = null;
- Match match = getCompareConfiguration().getComparison().getMatch(view);
- if (match.getLeft() == view) {
- result = MergeViewerSide.LEFT;
- } else if (match.getRight() == view) {
- result = MergeViewerSide.RIGHT;
- } else if (match.getOrigin() == view) {
- result = MergeViewerSide.ANCESTOR;
- }
- return getEffectiveSide(result);
- }
-
- /**
- * Utility method to get the object matching with the given one, to the given side.
- *
- * @param object
- * The object as base of the lookup.
- * @param side
- * The side where the potential matching object has to be retrieved.
- * @return The matching object.
- */
- private EObject getMatchView(EObject object, MergeViewerSide side) {
- Match match = getCompareConfiguration().getComparison().getMatch(object);
- return getMatchView(match, side);
- }
-
- /**
- * Utility method to get the object in the given side from the given match.
- *
- * @param match
- * The match.
- * @param side
- * The side where the potential matching object has to be retrieved.
- * @return The matching object.
- */
- private EObject getMatchView(Match match, MergeViewerSide side) {
- EObject result = null;
- switch (side) {
- case LEFT:
- result = match.getLeft();
- break;
- case RIGHT:
- result = match.getRight();
- break;
- case ANCESTOR:
- result = match.getOrigin();
- break;
- default:
- }
- return result;
- }
-
}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java
new file mode 100644
index 000000000..f5e3a5b40
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram;
+
+import org.eclipse.emf.compare.Diff;
+
+/**
+ * Interface for the management of decorators.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+public interface IDecoratorManager {
+
+ /**
+ * It hides the revealed decorators.
+ */
+ void hideAll();
+
+ /**
+ * From a given difference, it hides the related decorators.
+ *
+ * @param difference
+ * The difference.
+ */
+ void hideDecorators(Diff difference);
+
+ /**
+ * From a given difference, it reveals the related decorators.
+ *
+ * @param difference
+ * The difference.
+ */
+ void revealDecorators(Diff difference);
+
+ /**
+ * From a given difference, it removes the related decorators from cash.
+ *
+ * @param difference
+ * The difference.
+ */
+ void removeDecorators(Diff difference);
+
+ /**
+ * It removes all the displayed decorators from cache.
+ */
+ void removeAll();
+
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java
new file mode 100644
index 000000000..0075fe6ac
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram;
+
+import static com.google.common.base.Predicates.instanceOf;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.PolylineConnection;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.DecoratorFigure;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.EdgeFigure;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.NodeFigure;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.NodeListFigure;
+import org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff;
+import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.ICompareColor;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gmf.runtime.notation.Edge;
+import org.eclipse.gmf.runtime.notation.View;
+
+/**
+ * Marker manager to create, hide or reveal marker figures related to deleted or added graphical objects.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+public class MarkerManager extends AbstractDecoratorManager {
+
+ /**
+ * Marker represented by a <code>figure</code> on a <code>layer</code>, from the given <code>side</code>
+ * of the merge viewer. An edit part may be linked to the <code>figure</code> in some cases.<br>
+ * The marker is related to a <code>difference</code> and it is binded with the reference view and figure.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+ private class Marker extends AbstractDecorator {
+
+ /**
+ * Constructor.
+ *
+ * @param layer
+ * {@link Marker#fLayer}.
+ * @param side
+ * {@link Marker#fSide}.
+ * @param originView
+ * {@link Marker#fOriginView}.
+ * @param originFigure
+ * {@link Marker#fOriginFigure}.
+ * @param diff
+ * {@link Marker#fDifference}.
+ */
+ Marker(IFigure layer, MergeViewerSide side, View originView, IFigure originFigure, Diff diff) {
+ setLayer(layer);
+ setSide(side);
+ setOriginView(originView);
+ setOriginFigure(originFigure);
+ setDifference(diff);
+ }
+ }
+
+ /** Registry of created markers, indexed by difference. */
+ private Multimap<Diff, Marker> fMarkerRegistry = HashMultimap.create();
+
+ /**
+ * Constructor.
+ *
+ * @param compareConfiguration
+ * The compare configuration of the viewer.
+ * @param left
+ * The left area of the viewer.
+ * @param right
+ * The right area of the viewer.
+ * @param ancestor
+ * The ancestor area of the viewer.
+ * @param color
+ * The color of the difference.
+ */
+ public MarkerManager(EMFCompareConfiguration compareConfiguration, DiagramMergeViewer left,
+ DiagramMergeViewer right, DiagramMergeViewer ancestor, ICompareColor color) {
+ super(compareConfiguration, left, right, ancestor, color);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#getReferenceViews(
+ * org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff)
+ */
+ @Override
+ protected List<View> getReferenceViews(DiagramDiff difference) {
+ List<View> result = new ArrayList<View>();
+ Match matchValue = getCompareConfiguration().getComparison().getMatch(difference.getView());
+ if (matchValue != null) {
+ if (matchValue.getLeft() != null) {
+ result.add((View)matchValue.getLeft());
+ }
+ if (matchValue.getRight() != null) {
+ result.add((View)matchValue.getRight());
+ }
+ if (getCompareConfiguration().getComparison().isThreeWay()) {
+ switch (difference.getKind()) {
+ case DELETE:
+ case CHANGE:
+ case MOVE:
+ result.add((View)matchValue.getOrigin());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#getTargetSide(org.eclipse.emf.compare.Match,
+ * org.eclipse.gmf.runtime.notation.View)
+ */
+ @Override
+ public MergeViewerSide getTargetSide(Match match, View referenceView) {
+ return getSide(referenceView);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#createAndRegisterDecorator(org.eclipse.emf.compare.Diff,
+ * org.eclipse.gmf.runtime.notation.View, org.eclipse.draw2d.IFigure,
+ * org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide)
+ */
+ @Override
+ protected Marker createAndRegisterDecorator(Diff diff, View referenceView, IFigure referenceFigure,
+ MergeViewerSide targetSide) {
+ Marker marker = createMarker(diff, referenceView, referenceFigure, targetSide);
+ if (marker != null) {
+ fMarkerRegistry.put(diff, marker);
+ }
+ return marker;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
+ */
+ @Override
+ public void removeDecorators(Diff difference) {
+ fMarkerRegistry.removeAll(difference);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#removeAll()
+ */
+ @Override
+ public void removeAll() {
+ fMarkerRegistry.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#getDecorators(org.eclipse.emf.compare.Diff)
+ */
+ @Override
+ protected Collection<Marker> getDecorators(Diff difference) {
+ return fMarkerRegistry.get(difference);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#handleAddDecorator(org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator,
+ * org.eclipse.draw2d.IFigure, org.eclipse.draw2d.IFigure, boolean)
+ */
+ @Override
+ protected void handleAddDecorator(AbstractDecorator decorator, IFigure parent, IFigure toAdd,
+ boolean isMain) {
+ super.handleAddDecorator(decorator, parent, toAdd, isMain);
+ DiagramMergeViewer viewer = getViewer(decorator.getSide());
+ EditPart editPart = viewer.getEditPart(decorator.getOriginView());
+ if (editPart != null) {
+ viewer.getGraphicalViewer().reveal(editPart);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#goodCandidate()
+ * <br>
+ * All graphical differences are concerned.
+ */
+ @Override
+ protected Predicate<Diff> goodCandidate() {
+ return new Predicate<Diff>() {
+ public boolean apply(Diff difference) {
+ return instanceOf(DiagramDiff.class).apply(difference);
+ }
+ };
+ }
+
+ /**
+ * It creates a new marker from the given difference, view and figure.
+ *
+ * @param diff
+ * The related difference used as index for the main marker.
+ * @param referenceView
+ * The reference view as base for creation of the marker.
+ * @param referenceFigure
+ * The reference figure as base for creation of the marker.
+ * @param side
+ * The side where the marker has to be created.
+ * @return The phantom or null if the target layer is not found.
+ */
+ private Marker createMarker(Diff diff, View referenceView, IFigure referenceFigure,
+ MergeViewerSide side) {
+
+ IFigure referenceLayer = getLayer(referenceView, side);
+ if (referenceLayer != null) {
+ Rectangle referenceBounds = referenceFigure.getBounds().getCopy();
+ translateCoordinates(referenceFigure, referenceLayer, referenceBounds);
+
+ DecoratorFigure markerFigure = null;
+
+ Marker marker = new Marker(referenceLayer, side, referenceView, referenceFigure, diff);
+
+ if (isNodeList(referenceView)) {
+ markerFigure = new NodeListFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
+ referenceBounds, false);
+ } else if (referenceView instanceof Edge && referenceFigure instanceof PolylineConnection) {
+ markerFigure = new EdgeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
+ referenceBounds, false);
+ }
+
+ // Default case: Nodes
+ if (markerFigure == null) {
+ markerFigure = new NodeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
+ referenceBounds, false);
+ }
+
+ marker.setDecoratorFigure(markerFigure);
+ return marker;
+ }
+
+ return null;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#hideAll()
+ */
+ public void hideAll() {
+ for (Marker marker : fMarkerRegistry.values()) {
+ handleDeleteDecorator(marker, marker.getLayer(), marker.getFigure());
+ }
+ }
+
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java
new file mode 100644
index 000000000..e72faa77f
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java
@@ -0,0 +1,824 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram;
+
+import static com.google.common.base.Predicates.and;
+import static com.google.common.base.Predicates.instanceOf;
+import static com.google.common.base.Predicates.or;
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind;
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature;
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.valueIs;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Polyline;
+import org.eclipse.draw2d.PolylineConnection;
+import org.eclipse.draw2d.RectangleFigure;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceKind;
+import org.eclipse.emf.compare.DifferenceState;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.DecoratorFigure;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.EdgeFigure;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.NodeFigure;
+import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.figures.NodeListFigure;
+import org.eclipse.emf.compare.diagram.internal.extensions.CoordinatesChange;
+import org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff;
+import org.eclipse.emf.compare.diagram.internal.extensions.Hide;
+import org.eclipse.emf.compare.diagram.internal.extensions.Show;
+import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
+import org.eclipse.emf.compare.internal.utils.DiffUtil;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.ICompareColor;
+import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide;
+import org.eclipse.emf.compare.utils.ReferenceUtil;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.gef.ConnectionEditPart;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
+import org.eclipse.gmf.runtime.notation.Edge;
+import org.eclipse.gmf.runtime.notation.NotationPackage;
+import org.eclipse.gmf.runtime.notation.View;
+
+/**
+ * Phantom manager to create, hide or reveal phantom figures related to deleted or added graphical objects.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+public class PhantomManager extends AbstractDecoratorManager {
+
+ /**
+ * Phantom represented by a <code>figure</code> on a <code>layer</code>, from the given <code>side</code>
+ * of the merge viewer. An edit part may be linked to the <code>figure</code> in some cases.<br>
+ * The phantom is related to a <code>difference</code> and it is binded with the reference view and
+ * figure.
+ *
+ * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
+ */
+ private class Phantom extends AbstractDecorator {
+
+ /**
+ * Constructor.
+ *
+ * @param layer
+ * {@link Phantom#fLayer}.
+ * @param side
+ * {@link Phantom#fSide}.
+ * @param originView
+ * {@link Phantom#fOriginView}.
+ * @param originFigure
+ * {@link Phantom#fOriginFigure}.
+ * @param diff
+ * {@link Phantom#fDifference}.
+ */
+ Phantom(IFigure layer, MergeViewerSide side, View originView, IFigure originFigure, Diff diff) {
+ setLayer(layer);
+ setSide(side);
+ setOriginView(originView);
+ setOriginFigure(originFigure);
+ setDifference(diff);
+ }
+
+ /**
+ * Get the decorator dependencies of this one. The dependencies are the decorator ancestors plus the
+ * extremities of an edge decorator.<br>
+ * DO NOT CALL in an iterate of {@link PhantomManager#fPhantomRegistry}
+ *
+ * @return The list of found decorators.
+ */
+ public List<? extends AbstractDecorator> getDependencies() {
+ List<AbstractDecorator> result = new ArrayList<AbstractDecorator>();
+ result.addAll(getAncestors());
+ if (fOriginView instanceof Edge) {
+ View source = ((Edge)fOriginView).getSource();
+ View target = ((Edge)fOriginView).getTarget();
+ result.addAll(getOrCreateRelatedPhantoms(source, fSide));
+ result.addAll(getOrCreateRelatedPhantoms(target, fSide));
+ }
+ return result;
+ }
+
+ /**
+ * Get the ancestor decorators of this one.
+ *
+ * @return The list of the ancestors.
+ */
+ private List<? extends AbstractDecorator> getAncestors() {
+ List<AbstractDecorator> result = new ArrayList<AbstractDecorator>();
+ EObject parentOriginView = fOriginView.eContainer();
+ while (parentOriginView != null) {
+ result.addAll(getOrCreateRelatedPhantoms(parentOriginView, fSide));
+ parentOriginView = parentOriginView.eContainer();
+ }
+ return result;
+ }
+ }
+
+ /** Registry of created phantoms, indexed by difference. */
+ private final Map<Diff, Phantom> fPhantomRegistry = new HashMap<Diff, Phantom>();
+
+ /** Predicate witch checks that the given difference is an ADD or DELETE of a graphical object. */
+ private Predicate<Diff> isAddOrDelete = and(instanceOf(DiagramDiff.class),
+ or(ofKind(DifferenceKind.ADD), ofKind(DifferenceKind.DELETE)));
+
+ /** Predicate witch checks that the given difference is a HIDE or REVEAL of a graphical object. */
+ private Predicate<Diff> isHideOrReveal = or(instanceOf(Show.class), instanceOf(Hide.class));
+
+ /**
+ * Constructor.
+ *
+ * @param compareConfiguration
+ * The compare configuration of the viewer.
+ * @param left
+ * The left area of the viewer.
+ * @param right
+ * The right area of the viewer.
+ * @param ancestor
+ * The ancestor area of the viewer.
+ * @param color
+ * The color of the difference.
+ */
+ public PhantomManager(EMFCompareConfiguration compareConfiguration, DiagramMergeViewer left,
+ DiagramMergeViewer right, DiagramMergeViewer ancestor, ICompareColor color) {
+ super(compareConfiguration, left, right, ancestor, color);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#goodCandidate()
+ * <br>
+ * Only the diagram differences ADD/REVEAL or DELETE/HIDE are concerned by this display.
+ */
+ @Override
+ protected Predicate<Diff> goodCandidate() {
+ return new Predicate<Diff>() {
+ public boolean apply(Diff difference) {
+ return Predicates.or(isAddOrDelete, isHideOrReveal).apply(difference)
+ && difference.getState() == DifferenceState.UNRESOLVED;
+ }
+ };
+ }
+
+ /**
+ * From the given view, get or create the related phantoms in the given side.
+ *
+ * @param referenceView
+ * The given view.
+ * @param side
+ * The given side.
+ * @return The list of phantoms.
+ */
+ private List<Phantom> getOrCreateRelatedPhantoms(EObject referenceView, MergeViewerSide side) {
+ List<Phantom> result = new ArrayList<Phantom>();
+ Collection<Diff> changes = Collections2.filter(
+ getCompareConfiguration().getComparison().getDifferences(referenceView), goodCandidate());
+ for (Diff change : changes) {
+ Phantom phantom = fPhantomRegistry.get(change);
+ if (phantom == null) {
+ IFigure referenceFigure = PhantomManager.this.getFigure((View)referenceView);
+ if (referenceFigure != null) {
+ phantom = createAndRegisterDecorator(change, (View)referenceView, referenceFigure, side);
+ }
+ }
+ if (phantom != null) {
+ result.add(phantom);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#getReferenceViews(
+ * org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff)
+ */
+ @Override
+ protected List<View> getReferenceViews(DiagramDiff difference) {
+ List<View> result = new ArrayList<View>();
+
+ Match match = getCompareConfiguration().getComparison().getMatch(difference.getView());
+
+ EObject originObj = match.getOrigin();
+ EObject leftObj = match.getLeft();
+ EObject rightObj = match.getRight();
+
+ if (leftObj instanceof View || rightObj instanceof View) {
+ View referenceView = getReferenceView((View)originObj, (View)leftObj, (View)rightObj);
+ // It may be null if it is hidden
+ if (referenceView != null) {
+ result.add(referenceView);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#getTargetSide(org.eclipse.emf.compare.
+ * Match, org.eclipse.gmf.runtime.notation.View) <br>
+ * If the left object is null, a phantom should be drawn instead. Else, it means that the right
+ * object is null and a phantom should be displayed on the right side.
+ */
+ @Override
+ public MergeViewerSide getTargetSide(Match match, View referenceView) {
+ MergeViewerSide targetSide = null;
+ EObject leftMatch = match.getLeft();
+
+ if (leftMatch == null || (leftMatch instanceof View && !isFigureExist((View)leftMatch))) {
+ targetSide = MergeViewerSide.LEFT;
+ } else {
+ targetSide = MergeViewerSide.RIGHT;
+ }
+ return getEffectiveSide(targetSide);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#createAndRegisterDecorator(org.eclipse.emf.compare.Diff,
+ * org.eclipse.gmf.runtime.notation.View, org.eclipse.draw2d.IFigure,
+ * org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide)
+ */
+ @Override
+ protected Phantom createAndRegisterDecorator(Diff diff, View referenceView, IFigure referenceFigure,
+ MergeViewerSide targetSide) {
+ Phantom phantom = createPhantom(diff, referenceView, referenceFigure, targetSide);
+ if (phantom != null) {
+ fPhantomRegistry.put(diff, phantom);
+ }
+ return phantom;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#removeDecorators(org.eclipse.emf.compare.Diff)
+ */
+ @Override
+ public void removeDecorators(Diff difference) {
+ fPhantomRegistry.remove(difference);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#removeAll()
+ */
+ @Override
+ public void removeAll() {
+ fPhantomRegistry.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#getDecorators(org.eclipse.emf.compare.Diff)
+ */
+ @Override
+ protected List<Phantom> getDecorators(Diff difference) {
+ List<Phantom> result = new ArrayList<PhantomManager.Phantom>();
+ Phantom phantom = fPhantomRegistry.get(difference);
+ if (phantom != null) {
+ result.add(phantom);
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.<br>
+ * DO NOT CALL on a phantom within an iteration on the phantom registry.
+ * {@link PhantomManager#fPhantomRegistry}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#handleDecorator(org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator,
+ * boolean, boolean)
+ */
+ @Override
+ protected void handleDecorator(AbstractDecorator decorator, boolean isAdd, boolean isMain) {
+ super.handleDecorator(decorator, isAdd, isMain);
+ // Display the dependencies (context) of this decorator
+ for (AbstractDecorator ancestor : ((Phantom)decorator).getDependencies()) {
+ super.handleDecorator(ancestor, isAdd, false);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager#handleAddDecorator(org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator,
+ * org.eclipse.draw2d.IFigure, org.eclipse.draw2d.IFigure, boolean)
+ */
+ @Override
+ protected void handleAddDecorator(AbstractDecorator decorator, IFigure parent, IFigure toAdd,
+ boolean isMain) {
+ super.handleAddDecorator(decorator, parent, toAdd, isMain);
+ // Set the highlight of the figure
+ if (isMain) {
+ decorator.getDecoratorFigure().highlight();
+ getViewer(decorator.getSide()).getGraphicalViewer().reveal(toAdd);
+ } else {
+ decorator.getDecoratorFigure().unhighlight();
+ }
+ }
+
+ /**
+ * It checks that the given view graphically exists.
+ *
+ * @param view
+ * The view.
+ * @return True if it exists.
+ */
+ private boolean isFigureExist(View view) {
+ return view != null && view.isVisible();
+ }
+
+ /**
+ * Get the view which has to be used as reference to build a phantom.<br>
+ * The reference is the non null object among the given objects. In case of delete object, in the context
+ * of three-way comparison, the reference will be the ancestor one (<code>originObj</code>).
+ *
+ * @param originObj
+ * The ancestor object.
+ * @param leftView
+ * The left object.
+ * @param rightView
+ * The right object.
+ * @return The reference object.
+ */
+ private View getReferenceView(View originObj, View leftView, View rightView) {
+ View referenceView;
+ if (isFigureExist(originObj)) {
+ referenceView = originObj;
+ } else if (isFigureExist(leftView)) {
+ referenceView = leftView;
+ } else {
+ referenceView = rightView;
+ }
+ return referenceView;
+ }
+
+ /**
+ * It creates a new phantom from the given difference, view and figure.
+ *
+ * @param diff
+ * The related difference used as index for the main phantom.
+ * @param referenceView
+ * The reference view as base for creation of the phantom.
+ * @param referenceFigure
+ * The reference figure as base for creation of the phantom.
+ * @param side
+ * The side where the phantom has to be created.
+ * @return The phantom or null if the target layer is not found.
+ */
+ private Phantom createPhantom(Diff diff, View referenceView, IFigure referenceFigure,
+ MergeViewerSide side) {
+ IFigure targetLayer = getLayer(referenceView, side);
+ if (targetLayer != null) {
+ MergeViewerSide referenceSide = getSide(referenceView);
+
+ Rectangle rect = referenceFigure.getBounds().getCopy();
+
+ IFigure referenceLayer = getLayer(referenceView, referenceSide);
+ translateCoordinates(referenceFigure, referenceLayer, rect);
+
+ DecoratorFigure ghost = null;
+
+ Phantom phantom = new Phantom(targetLayer, side, referenceView, referenceFigure, diff);
+
+ // Container "list" case
+ if (isNodeList(referenceView)) {
+
+ int index = getIndex(diff, referenceView, side);
+
+ IFigure referenceParentFigure = referenceFigure.getParent();
+ Rectangle referenceParentBounds = referenceParentFigure.getBounds().getCopy();
+ translateCoordinates(referenceParentFigure, referenceLayer, referenceParentBounds);
+
+ View parentView = (View)getMatchView(referenceView.eContainer(), side);
+ if (parentView != null) {
+ int nbElements = getVisibleViews(parentView).size();
+ // CHECKSTYLE:OFF
+ if (index > nbElements) {
+ // CHECKSTYLE:ON
+ index = nbElements;
+ }
+ }
+
+ // FIXME: The add of decorators modifies the physical coordinates of elements
+ // FIXME: Compute position from the y position of the first child + sum of height of the
+ // children.
+ int pos = rect.height * index + referenceParentBounds.y + 1;
+ Map<String, Object> parameters = new HashMap<String, Object>();
+ parameters.put(NodeListFigure.PARAM_Y_POS, Integer.valueOf(pos));
+
+ ghost = new NodeListFigure(diff, isThreeWay(), getCompareColor(), referenceFigure, rect, true,
+ parameters);
+
+ // Edge case
+ } else if (referenceView instanceof Edge) {
+ // If the edge phantom ties shapes where their coordinates changed
+ if (hasAnExtremityChange((Edge)referenceView, side)) {
+ EditPart edgeEditPart = createEdgeEditPart((Edge)referenceView, referenceSide, side);
+ // CHECKSTYLE:OFF
+ if (edgeEditPart instanceof GraphicalEditPart) {
+ // CHECKSTYLE:ON
+ phantom.setEditPart(edgeEditPart);
+
+ IFigure fig = ((GraphicalEditPart)edgeEditPart).getFigure();
+ fig.getChildren().clear();
+ ghost = new DecoratorFigure(diff, isThreeWay(), getCompareColor(), referenceFigure,
+ fig, true);
+
+ }
+ // Else, it creates only a polyline connection figure with the same properties as the
+ // reference
+ } else {
+ if (referenceFigure instanceof PolylineConnection) {
+ ghost = new EdgeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure, rect,
+ true);
+ }
+ }
+ }
+
+ // Default case: Nodes
+ if (ghost == null) {
+ ghost = new NodeFigure(diff, isThreeWay(), getCompareColor(), referenceFigure, rect, true);
+ }
+
+ phantom.setDecoratorFigure(ghost);
+
+ translateWhenInsideContainerChange(phantom);
+
+ return phantom;
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Get the index of the phantom to draw.
+ *
+ * @param diff
+ * The related difference used as index for the main phantom.
+ * @param referenceView
+ * The reference view to compute the phantom.
+ * @param side
+ * The side where the phantom has to be drawn.
+ * @return The index in the list where the phantom has to be drawn.
+ */
+ private int getIndex(Diff diff, View referenceView, MergeViewerSide side) {
+ // Case for invisible objects
+ if (diff instanceof Hide || diff instanceof Show) {
+ List<Object> source = null;
+ List<Object> target = null;
+ Object newElement = null;
+ Match match = diff.getMatch();
+ List<Object> leftList = ReferenceUtil.getAsList(match.getLeft().eContainer(),
+ NotationPackage.Literals.VIEW__PERSISTED_CHILDREN);
+ List<Object> rightList = ReferenceUtil.getAsList(match.getRight().eContainer(),
+ NotationPackage.Literals.VIEW__PERSISTED_CHILDREN);
+ if (diff instanceof Hide) {
+ source = rightList;
+ target = leftList;
+ newElement = diff.getMatch().getRight();
+ } else {
+ source = leftList;
+ target = rightList;
+ newElement = diff.getMatch().getLeft();
+ }
+ Iterable<Object> ignoredElements = Iterables.filter(target, new Predicate() {
+ public boolean apply(Object input) {
+ return input instanceof View && !((View)input).isVisible();
+ }
+ });
+ return DiffUtil.findInsertionIndex(getCompareConfiguration().getComparison(), ignoredElements,
+ source, target, newElement);
+ }
+ // Case for deleted objects
+ Diff refiningDiff = Iterators.find(diff.getRefinedBy().iterator(), and(valueIs(referenceView),
+ onFeature(NotationPackage.Literals.VIEW__PERSISTED_CHILDREN.getName())));
+
+ return DiffUtil.findInsertionIndex(getCompareConfiguration().getComparison(), refiningDiff,
+ side == MergeViewerSide.LEFT);
+ }
+
+ /**
+ * Get the visible view under the given parent view.
+ *
+ * @param parent
+ * The parent view.
+ * @return The list of views.
+ */
+ private List<View> getVisibleViews(View parent) {
+ return (List<View>)Lists
+ .newArrayList(Iterators.filter(parent.getChildren().iterator(), new Predicate<Object>() {
+ public boolean apply(Object input) {
+ return input instanceof View && ((View)input).isVisible();
+ }
+ }));
+ }
+
+ /**
+ * It translates and resizes the figure of the given phantom when this one is nested in a container which
+ * is subjected to a coordinates change.
+ *
+ * @param phantom
+ * The phantom.
+ */
+ private void translateWhenInsideContainerChange(Phantom phantom) {
+ boolean isCandidate = false;
+ Diff diff = phantom.getDifference();
+ if (diff instanceof DiagramDiff) {
+ EObject parent = ((DiagramDiff)diff).getView().eContainer();
+ while (parent instanceof View && !isCandidate) {
+ isCandidate = Iterables.any(getCompareConfiguration().getComparison().getDifferences(parent),
+ instanceOf(CoordinatesChange.class));
+ parent = parent.eContainer();
+ }
+ }
+ if (isCandidate) {
+ View referenceView = phantom.getOriginView();
+ View parentReferenceView = (View)referenceView.eContainer();
+ if (parentReferenceView != null) {
+ View parentView = (View)getMatchView(parentReferenceView, phantom.getSide());
+ IFigure parentFigure = getFigure(parentView);
+ if (parentFigure != null) {
+ Rectangle parentRect = parentFigure.getBounds().getCopy();
+ translateCoordinates(parentFigure, getLayer(parentReferenceView, getSide(parentView)),
+ parentRect);
+
+ IFigure parentReferenceFigure = getFigure(parentReferenceView);
+ // CHECKSTYLE:OFF
+ if (parentReferenceFigure != null) {
+ Rectangle parentReferenceRect = parentReferenceFigure.getBounds().getCopy();
+ translateCoordinates(parentReferenceFigure,
+ getLayer(parentReferenceView, getSide(parentReferenceView)),
+ parentReferenceRect);
+
+ int deltaX = parentRect.x - parentReferenceRect.x;
+ int deltaY = parentRect.y - parentReferenceRect.y;
+ int deltaWidth = parentRect.width - parentReferenceRect.width;
+ int deltaHeight = parentRect.height - parentReferenceRect.height;
+
+ IFigure figure = phantom.getFigure();
+
+ Rectangle rect = figure.getBounds().getCopy();
+ rect.x += deltaX;
+ rect.y += deltaY;
+ rect.width += deltaWidth;
+ if (!(figure instanceof Polyline)) {
+ rect.height += deltaHeight;
+ }
+ figure.setBounds(rect);
+
+ if (figure instanceof Polyline) {
+
+ Point firstPoint = ((Polyline)figure).getPoints().getFirstPoint().getCopy();
+ Point lastPoint = ((Polyline)figure).getPoints().getLastPoint().getCopy();
+
+ firstPoint.x += deltaX;
+ firstPoint.y += deltaY;
+
+ lastPoint.x += deltaX + deltaWidth;
+ lastPoint.y += deltaY;
+
+ ((Polyline)figure).setEndpoints(firstPoint, lastPoint);
+
+ }
+ }
+ // CHECKSTYLE:ON
+ }
+ }
+ }
+ }
+
+ /**
+ * It checks that the given edge is linked to graphical objects subjected to coordinate changes, on the
+ * given side.
+ *
+ * @param edge
+ * The edge to check.
+ * @param targetSide
+ * The side to check extremities (side of the phantom).
+ * @return True if an extremity at least changed its location, False otherwise.
+ */
+ private boolean hasAnExtremityChange(Edge edge, MergeViewerSide targetSide) {
+ View referenceSource = edge.getSource();
+ View referenceTarget = edge.getTarget();
+ return hasChange(referenceSource, targetSide) || hasChange(referenceTarget, targetSide);
+ }
+
+ /**
+ * It checks that the coordinates of the given view changed between left and right, from the given side.
+ *
+ * @param referenceView
+ * The view to check.
+ * @param targetSide
+ * The side to focus.
+ * @return True if the view changed its location, False otherwise.
+ */
+ private boolean hasChange(View referenceView, MergeViewerSide targetSide) {
+ View extremity = (View)getMatchView(referenceView, targetSide);
+ // Look for a related change coordinates on the extremity of the edge reference.
+ Collection<Diff> diffs = Collections2.filter(
+ getCompareConfiguration().getComparison().getDifferences(referenceView),
+ instanceOf(CoordinatesChange.class));
+ if (diffs.isEmpty()) {
+ // Look for a related change coordinates on the matching extremity (other side) of the edge
+ // reference.
+ diffs = Collections2.filter(getCompareConfiguration().getComparison().getDifferences(extremity),
+ instanceOf(CoordinatesChange.class));
+ }
+ return !diffs.isEmpty();
+ }
+
+ /**
+ * It creates and returns a new edit part from the given edge. This edit part listens to the reference
+ * edge but is attached to the controllers of the target (phantom) side.
+ *
+ * @param referenceEdge
+ * The edge as base of the edit part.
+ * @param referenceSide
+ * The side of this edge.
+ * @param targetSide
+ * The side where the edit part has to be created to draw the related phantom.
+ * @return The new edit part.
+ */
+ private EditPart createEdgeEditPart(Edge referenceEdge, MergeViewerSide referenceSide,
+ MergeViewerSide targetSide) {
+ EditPart edgeEditPartReference = getViewer(referenceSide).getEditPart(referenceEdge);
+ EditPart edgeEditPart = null;
+ if (edgeEditPartReference instanceof ConnectionEditPart) {
+
+ edgeEditPart = getOrCreatePhantomEditPart(referenceEdge, referenceSide, targetSide);
+
+ if (edgeEditPart instanceof ConnectionEditPart) {
+ EditPart edgeSourceEp = getOrCreateExtremityPhantomEditPart(
+ ((ConnectionEditPart)edgeEditPartReference).getSource(), referenceSide, targetSide);
+
+ ((ConnectionEditPart)edgeEditPart).setSource(edgeSourceEp);
+
+ EditPart edgeTargetEp = getOrCreateExtremityPhantomEditPart(
+ ((ConnectionEditPart)edgeEditPartReference).getTarget(), referenceSide, targetSide);
+
+ ((ConnectionEditPart)edgeEditPart).setTarget(edgeTargetEp);
+ }
+ }
+ return edgeEditPart;
+ }
+
+ /**
+ * From the given edit part, it retrieves the matched one, from the given target side. If the retrieved
+ * edit part is not linked to a GMF object, in the target side, a phantom GEF edit part is returned which
+ * will locate a rectangle invisible figure in the same location as the related phantom.
+ *
+ * @param referenceEdgeExtremityEp
+ * The reference edit part for one of the extremities of an edge.
+ * @param referenceSide
+ * The side of the reference.
+ * @param targetSide
+ * The other side, where the phantom has to be drawn.
+ * @return The phantom edit part used to attach the extremity of an edge phantom.
+ */
+ private EditPart getOrCreateExtremityPhantomEditPart(EditPart referenceEdgeExtremityEp,
+ MergeViewerSide referenceSide, MergeViewerSide targetSide) {
+ View referenceExtremityView = (View)referenceEdgeExtremityEp.getModel();
+
+ EditPart edgeExtremityEp = getOrCreatePhantomEditPart(referenceExtremityView, referenceSide,
+ targetSide);
+
+ if (isPhantomEditPart((AbstractGraphicalEditPart)edgeExtremityEp)) {
+
+ final AbstractGraphicalEditPart edgeExtremityEpParent = (AbstractGraphicalEditPart)edgeExtremityEp
+ .getParent();
+
+ List<Phantom> phantoms = getOrCreateRelatedPhantoms(referenceExtremityView, targetSide);
+ if (!phantoms.isEmpty()) {
+ Phantom phantomToTarget = phantoms.get(0);
+ final IFigure figureToTarget = phantomToTarget.getFigure();
+
+ edgeExtremityEp = new org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart(
+ referenceExtremityView) {
+ @Override
+ protected void createDefaultEditPolicies() {
+ }
+
+ @Override
+ protected IFigure createFigure() {
+ RectangleFigure fig = new RectangleFigure();
+ fig.setBounds(figureToTarget.getBounds());
+ fig.setParent(edgeExtremityEpParent.getFigure());
+ return fig;
+ }
+ };
+
+ edgeExtremityEp.setParent(edgeExtremityEpParent);
+ }
+
+ ((AbstractGraphicalEditPart)edgeExtremityEp).activate();
+ ((AbstractGraphicalEditPart)edgeExtremityEp).getFigure();
+ }
+
+ return edgeExtremityEp;
+ }
+
+ /**
+ * It checks if the given edit part is related to a phantom edit part (created for nothing, without link
+ * to a GMF object in the target side).
+ *
+ * @param editPart
+ * The edit part to check.
+ * @return True if it is a phantom edit part, false otherwise.
+ */
+ private boolean isPhantomEditPart(AbstractGraphicalEditPart editPart) {
+ Rectangle targetBounds = editPart.getFigure().getBounds();
+ return targetBounds.x == 0 && targetBounds.y == 0 && targetBounds.width == 0
+ && targetBounds.height == 0;
+ }
+
+ /**
+ * It creates and returns a new edit part from the given view. This edit part listens the reference view
+ * but is attached to the controllers of the target (phantom) side.
+ *
+ * @param referenceView
+ * The view as base of the edit part.
+ * @param referenceSide
+ * The side of this view.
+ * @param targetSide
+ * The side where the edit part has to be created to draw the related phantom.
+ * @return The new edit part.
+ */
+ private EditPart getOrCreatePhantomEditPart(EObject referenceView, MergeViewerSide referenceSide,
+ MergeViewerSide targetSide) {
+ EditPart editPartParent = null;
+ EditPart editPart = null;
+ EditPart editPartReference = getViewer(referenceSide).getEditPart(referenceView);
+ EditPart editPartReferenceParent = editPartReference.getParent();
+ Object referenceViewParent = editPartReferenceParent.getModel();
+ if (!(referenceViewParent instanceof EObject)) {
+ referenceViewParent = referenceView.eContainer();
+ }
+ View viewParent = (View)getMatchView((EObject)referenceViewParent, targetSide);
+ if (viewParent != null) {
+ editPartParent = getViewer(targetSide).getEditPart(viewParent);
+ }
+ if (editPartParent == null) {
+ editPartParent = getOrCreatePhantomEditPart((EObject)referenceViewParent, referenceSide,
+ targetSide);
+
+ }
+ if (editPartParent != null) {
+ View view = (View)getMatchView(referenceView, targetSide);
+ if (view != null) {
+ editPart = getViewer(targetSide).getEditPart(view);
+ }
+ if (editPart == null) {
+
+ editPart = getViewer(targetSide).getGraphicalViewer().getEditPartFactory()
+ .createEditPart(editPartParent, referenceView);
+ editPart.setParent(editPartParent);
+ getViewer(targetSide).getGraphicalViewer().getEditPartRegistry().put(referenceView, editPart);
+
+ }
+
+ }
+ return editPart;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#hideAll()
+ */
+ public void hideAll() {
+ for (Phantom phantom : fPhantomRegistry.values()) {
+ handleDeleteDecorator(phantom, phantom.getLayer(), phantom.getFigure());
+ }
+ }
+}

Back to the top