Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Dirix2015-05-13 09:27:15 +0000
committerStefan Dirix2015-05-21 13:52:00 +0000
commitd8b87cecb1a4c85496d780c2b350e0ec938488c2 (patch)
tree72949695aefaccf40060b2e4dc5c5b2f3fdc0fe5 /plugins
parent53aaaadcc4b23a5520ff32c3a6627be4ea726ed8 (diff)
downloadorg.eclipse.emf.compare-d8b87cecb1a4c85496d780c2b350e0ec938488c2.tar.gz
org.eclipse.emf.compare-d8b87cecb1a4c85496d780c2b350e0ec938488c2.tar.xz
org.eclipse.emf.compare-d8b87cecb1a4c85496d780c2b350e0ec938488c2.zip
[466607] Enhance EMFModelProvider to consider multiple resources at once
The EMFModelProvider now overrides the getMappings(Resource[] ...) method to allow for optimized results. Depending on the EMF Compare resolution settings the logical model can not be computed as a whole when only looking at single resources. The getMappings(Resource[] ...) method now uses the already available multi-resource input to combine overlapping logical models to a single model. Since the EMFModelProvider is for example used during merging, the optimized results avoid redundant comparisons and mergings and therefore save time and computation power. Includes testcases. Bug: 466607 Signed-off-by: Stefan Dirix <sdirix@eclipsesource.com> Change-Id: Id928bc1a05e353bf2b721243c264d09de07eebc3
Diffstat (limited to 'plugins')
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/EMFModelProviderTest.java523
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file1.ecore5
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file2.ecore5
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file3.ecore4
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file1.ecore5
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file2.ecore5
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file3.ecore4
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file1.ecore5
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file2.ecore5
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file3.ecore4
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/suite/AllTests.java21
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java52
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFModelProvider.java221
13 files changed, 815 insertions, 44 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/EMFModelProviderTest.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/EMFModelProviderTest.java
new file mode 100644
index 000000000..ae9d08354
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/EMFModelProviderTest.java
@@ -0,0 +1,523 @@
+/*******************************************************************************
+ * Copyright (c) 2015 EclipseSource Muenchen GmbH and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Dirix - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.tests.logical.modelprovider;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.mapping.IModelProviderDescriptor;
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.resources.mapping.RemoteResourceMappingContext;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
+import org.eclipse.emf.compare.ide.ui.internal.logical.EMFModelProvider;
+import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.CrossReferenceResolutionScope;
+import org.eclipse.emf.compare.ide.ui.internal.preferences.EMFCompareUIPreferences;
+import org.eclipse.emf.compare.ide.ui.tests.CompareTestCase;
+import org.eclipse.emf.compare.ide.ui.tests.workspace.TestProject;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+
+/**
+ * Tests the EMFModelProvider to return a minimum number of {@link ResourceMapping}s in cases where it is not
+ * straightforward (e.g. resolution scope set to "workspace") but still doable since it has all required input
+ * resources.
+ *
+ * @author Stefan Dirix <sdirix@eclipsesource.com>
+ */
+@SuppressWarnings({"nls", "restriction" })
+public class EMFModelProviderTest extends CompareTestCase {
+
+ /**
+ * Path to the test data.
+ */
+ private static final String TEST_DATA_PATH = "src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/";
+
+ /**
+ * The bundle containing this test.
+ */
+ private static final String TEST_BUNDLE = "org.eclipse.emf.compare.ide.ui.tests";
+
+ /**
+ * Name of test project 1.
+ */
+ private static final String PROJECT_NAME_1 = "project1";
+
+ /**
+ * Name of test project 2.
+ */
+ private static final String PROJECT_NAME_2 = "project2";
+
+ /**
+ * Name of test project 3.
+ */
+ private static final String PROJECT_NAME_3 = "project3";
+
+ /**
+ * Reset the resolution scope preference.
+ */
+ @AfterClass
+ public static void setDefaultResolutionScope() {
+ final IPreferenceStore store = EMFCompareIDEUIPlugin.getDefault().getPreferenceStore();
+ store.setToDefault(EMFCompareUIPreferences.RESOLUTION_SCOPE_PREFERENCE);
+ }
+
+ /**
+ * Delete created projects.
+ */
+ @AfterClass
+ public static void deleteProjects() throws CoreException {
+ deleteProject(PROJECT_NAME_1);
+ deleteProject(PROJECT_NAME_2);
+ deleteProject(PROJECT_NAME_3);
+ }
+
+ /**
+ * Deletes the project wit the given {@code name}.
+ *
+ * @param name
+ * The name of the project which shall be deleted.
+ */
+ private static void deleteProject(String name) throws CoreException {
+ final IProject project = new TestProject(name).getProject();
+ project.delete(true, true, new NullProgressMonitor());
+ }
+
+ /**
+ * Sets the resolution scope to the given preference.
+ *
+ * @param preference
+ * The value to which the resolution scope preference is set.
+ */
+ private void setResolutionScope(String preference) {
+ final IPreferenceStore store = EMFCompareIDEUIPlugin.getDefault().getPreferenceStore();
+ store.setValue(EMFCompareUIPreferences.RESOLUTION_SCOPE_PREFERENCE, preference);
+ }
+
+ /**
+ * Creates the projects and test files for case1.
+ *
+ * @return The created test files.
+ */
+ private IFile[] createProjectFilesCase1() throws CoreException, IOException, URISyntaxException {
+ IProject project1 = new TestProject(PROJECT_NAME_1).getProject();
+ IProject project2 = new TestProject(PROJECT_NAME_2).getProject();
+ IProject project3 = new TestProject(PROJECT_NAME_3).getProject();
+ IFile file1 = addToProject(project1, "case1/file1.ecore", "");
+ IFile file2 = addToProject(project2, "case1/file2.ecore", "");
+ IFile file3 = addToProject(project3, "case1/file3.ecore", "");
+ return new IFile[] {file1, file2, file3 };
+ }
+
+ /**
+ * Creates the projects and test files for case2.
+ *
+ * @return The created test files.
+ */
+ private IFile[] createProjectFilesCase2() throws CoreException, IOException, URISyntaxException {
+ IProject project1 = new TestProject(PROJECT_NAME_1).getProject();
+ IProject project2 = new TestProject(PROJECT_NAME_2).getProject();
+ IProject project3 = new TestProject(PROJECT_NAME_3).getProject();
+ IFile file1 = addToProject(project1, "case2/file1.ecore", "");
+ IFile file2 = addToProject(project2, "case2/file2.ecore", "");
+ IFile file3 = addToProject(project3, "case2/file3.ecore", "");
+ return new IFile[] {file1, file2, file3 };
+ }
+
+ /**
+ * Creates the projects and test files for case3.
+ *
+ * @return The created test files.
+ */
+ private IFile[] createProjectFilesCase3() throws CoreException, IOException, URISyntaxException {
+ IProject project1 = new TestProject(PROJECT_NAME_1).getProject();
+ IFile file1 = addToProject(project1, "case3/file1.ecore", "");
+ IFile file2 = addToProject(project1, "case3/file2.ecore", "/a/");
+ IFile file3 = addToProject(project1, "case3/file3.ecore", "/a/");
+ return new IFile[] {file1, file2, file3 };
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row:
+ * {@code project1/file1 -> project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#OUTGOING}.
+ */
+ @Test
+ public void testResourceMappingCase1_Outgoing() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.OUTGOING.toString());
+ final IFile[] files = createProjectFilesCase1();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1,file2,file3}, {file2,file3}, {file3}
+ assertEquals(3, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can optimize to {file1,file2,file3}
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row:
+ * {@code project1/file1 -> project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#CONTAINER}.
+ */
+ @Test
+ public void testResourceMappingCase1_Container() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.CONTAINER.toString());
+ final IFile[] files = createProjectFilesCase1();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1}, {file2}, {file3}
+ assertEquals(3, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can not optimize and returns the same
+ assertEquals(3, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row:
+ * {@code project1/file1 -> project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#PROJECT}.
+ */
+ @Test
+ public void testResourceMappingCase1_Project() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.PROJECT.toString());
+ final IFile[] files = createProjectFilesCase1();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1}, {file2}, {file3}
+ assertEquals(3, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can not optimize and returns the same
+ assertEquals(3, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row:
+ * {@code project1/file1 -> project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#WORKSPACE}.
+ */
+ @Test
+ public void testResourceMappingCase1_Workspace() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.WORKSPACE.toString());
+ final IFile[] files = createProjectFilesCase1();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1,file2,file3}
+ assertEquals(1, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // The mapping can not be optimized further
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements fork: {@code project1/file1 -> project3/file3} &
+ * {@code project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#OUTGOING}.
+ */
+ @Test
+ public void testResourceMappingCase2_Outgoing() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.OUTGOING.toString());
+ final IFile[] files = createProjectFilesCase2();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1,file2,file3}, {file3}
+ assertEquals(2, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can optimize to {file1,file2,file3}
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements fork: {@code project1/file1 -> project3/file3} &
+ * {@code project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#CONTAINER}.
+ */
+ @Test
+ public void testResourceMappingCase2_Container() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.CONTAINER.toString());
+ final IFile[] files = createProjectFilesCase2();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1}, {file2}, {file3}
+ assertEquals(3, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can not optimize and returns the same
+ assertEquals(3, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements fork: {@code project1/file1 -> project3/file3} &
+ * {@code project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#PROJECT}.
+ */
+ @Test
+ public void testResourceMappingCase2_Project() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.PROJECT.toString());
+ final IFile[] files = createProjectFilesCase2();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1}, {file2}, {file3}
+ assertEquals(3, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can not optimize and returns the same
+ assertEquals(3, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements fork: {@code project1/file1 -> project3/file3} &
+ * {@code project2/file2 -> project3/file3} and resolution scope
+ * {@link CrossReferenceResolutionScope#WORKSPACE}.
+ */
+ @Test
+ public void testResourceMappingCase2_Workspace() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.WORKSPACE.toString());
+ final IFile[] files = createProjectFilesCase2();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1,file2,file3}
+ assertEquals(1, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // The mapping can not be optimized further
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row: {@code file1 -> a/file2 -> a/file3} and resolution
+ * scope {@link CrossReferenceResolutionScope#OUTGOING}.
+ */
+ @Test
+ public void testResourceMappingCase3_Outgoing() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.OUTGOING.toString());
+ final IFile[] files = createProjectFilesCase3();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1,file2,file3}, {file2,file3}, {file3}
+ assertEquals(3, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can optimize to {file1,file2,file3}
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row: {@code file1 -> a/file2 -> a/file3} and resolution
+ * scope {@link CrossReferenceResolutionScope#CONTAINER}.
+ */
+ @Test
+ public void testResourceMappingCase3_Container() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.CONTAINER.toString());
+ final IFile[] files = createProjectFilesCase3();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1}, {file2,file3}
+ assertEquals(2, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // EMFModelProvider can optimize to {file1,file2,file3}
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row: {@code file1 -> a/file2 -> a/file3} and resolution
+ * scope {@link CrossReferenceResolutionScope#PROJECT}.
+ */
+ @Test
+ public void testResourceMappingCase3_Project() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.PROJECT.toString());
+ final IFile[] files = createProjectFilesCase3();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1,file2,file3}
+ assertEquals(1, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // The mapping can not be optimized further
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Tests the EMFModelProvider with a requirements row: {@code file1 -> a/file2 -> a/file3} and resolution
+ * scope {@link CrossReferenceResolutionScope#WORKSPACE}.
+ */
+ @Test
+ public void testResourceMappingCase3_Workspace() throws CoreException, IOException, URISyntaxException {
+ setResolutionScope(CrossReferenceResolutionScope.WORKSPACE.toString());
+ final IFile[] files = createProjectFilesCase3();
+
+ final ResourceMapping[] singleResourceMappings = collectSingleResourceMappings(files);
+ // {file1,file2,file3}
+ assertEquals(1, singleResourceMappings.length);
+
+ final ResourceMapping[] combinedResourceMappings = getCombinedResourceMappings(files);
+ // The mapping can not be optimized further
+ assertEquals(1, combinedResourceMappings.length);
+ }
+
+ /**
+ * Collects all resource mappings of the given files by calling
+ * {@link EMFModelProvider#getMappings(IResource, org.eclipse.core.resources.mapping.ResourceMappingContext, IProgressMonitor)}
+ * on each of them and combining the results. This is how the default implementation within the
+ * ModelProvider class is implemented.
+ *
+ * @param files
+ * The files for which the resource mappings shall be calculated.
+ * @return The combined result of determining the resource mapping for each given file separately.
+ */
+ private ResourceMapping[] collectSingleResourceMappings(IFile... files) throws CoreException {
+ final EMFModelProvider emfModelProvider = getEMFModelProvider();
+ Set<ResourceMapping> mappings = new HashSet<ResourceMapping>();
+ for (int i = 0; i < files.length; i++) {
+ IResource resource = files[i];
+ ResourceMapping[] resourceMappings = emfModelProvider.getMappings(resource, new StubContext(),
+ new NullProgressMonitor());
+ if (resourceMappings.length > 0) {
+ mappings.addAll(Arrays.asList(resourceMappings));
+ }
+ }
+ return mappings.toArray(new ResourceMapping[mappings.size()]);
+ }
+
+ /**
+ * Returns the result of calling
+ * {@link EMFModelProvider#getTraversals(ResourceMapping[], org.eclipse.core.resources.mapping.ResourceMappingContext, IProgressMonitor)}
+ * to determine the ResourceMappings.
+ *
+ * @param files
+ * The files for which the resource mappings shall be determined.
+ * @return The resource mappings for the given file when {@link EMFModelProvider} is given a chance to
+ * optimize.
+ */
+ private ResourceMapping[] getCombinedResourceMappings(IFile... files) throws CoreException {
+ final EMFModelProvider emfModelProvider = getEMFModelProvider();
+ return emfModelProvider.getMappings(files, new StubContext(), new NullProgressMonitor());
+ }
+
+ private EMFModelProvider getEMFModelProvider() throws CoreException {
+ final IModelProviderDescriptor[] descriptors = ModelProvider.getModelProviderDescriptors();
+ for (IModelProviderDescriptor descriptor : descriptors) {
+ if (descriptor.getModelProvider() instanceof EMFModelProvider) {
+ return (EMFModelProvider)descriptor.getModelProvider();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Copies the file located in {@link #TEST_DATA_PATH} + {@code filePath} to the given
+ * {@code destinationPath} in {@code iProject}.
+ *
+ * @param iProject
+ * The {@link IProject} to which the file is added.
+ * @param filePath
+ * The path relative to {@link #TEST_DATA_PATH} where the file is originally located.
+ * @param destinationPath
+ * The path in the {@code iProject} to which the file will be copied.
+ * @return The newly created {@link IFile}.
+ */
+ private IFile addToProject(IProject iProject, String filePath, String destinationPath)
+ throws IOException, URISyntaxException, CoreException {
+ final Bundle bundle = Platform.getBundle(TEST_BUNDLE);
+ final URI fileUri = getFileUri(bundle.getEntry(TEST_DATA_PATH + filePath));
+
+ final File file = project.getOrCreateFile(iProject, destinationPath + fileUri.lastSegment());
+
+ copyFile(toFile(fileUri), file);
+
+ return project.getIFile(iProject, file);
+ }
+
+ private URI getFileUri(final URL bundleUrl) throws IOException {
+ URL fileLocation = FileLocator.toFileURL(bundleUrl);
+ return URI.createFileURI(fileLocation.getPath());
+ }
+
+ private File toFile(final URI fileUri) throws URISyntaxException {
+ return new File(fileUri.toFileString());
+ }
+
+ /**
+ * Stub for a {@link RemoteResourceMappingContext}. The class is used to trigger the creation of a proper
+ * comparison scope within the {@link EMFModelProvider}.
+ */
+ private class StubContext extends RemoteResourceMappingContext {
+
+ @Override
+ public IStorage fetchBaseContents(IFile file, IProgressMonitor monitor) throws CoreException {
+ return null;
+ }
+
+ @Override
+ public IResource[] fetchMembers(IContainer container, IProgressMonitor monitor) throws CoreException {
+ return new IResource[0];
+ }
+
+ @Override
+ public IStorage fetchRemoteContents(IFile file, IProgressMonitor monitor) throws CoreException {
+ return null;
+ }
+
+ @Override
+ public IProject[] getProjects() {
+ return ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ }
+
+ @Override
+ public boolean hasLocalChange(IResource resource, IProgressMonitor monitor) throws CoreException {
+ return true;
+ }
+
+ @Override
+ public boolean hasRemoteChange(IResource resource, IProgressMonitor monitor) throws CoreException {
+ return true;
+ }
+
+ @Override
+ public boolean isThreeWay() {
+ return false;
+ }
+
+ @Override
+ public void refresh(ResourceTraversal[] traversals, int flags, IProgressMonitor monitor)
+ throws CoreException {
+ }
+
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file1.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file1.ecore
new file mode 100644
index 000000000..fdc0ea7cb
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file1.ecore
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+ xmi:id="_A" name="File1_A">
+ <eSubpackages href="../project2/file2.ecore#_B"/>
+</ecore:EPackage> \ No newline at end of file
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file2.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file2.ecore
new file mode 100644
index 000000000..537d7623e
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file2.ecore
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+ xmi:id="_B" name="File2_B">
+ <eSubpackages href="../project3/file3.ecore#_C"/>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file3.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file3.ecore
new file mode 100644
index 000000000..07e473eeb
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case1/file3.ecore
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmi:id="_C" name="File3_C">
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file1.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file1.ecore
new file mode 100644
index 000000000..373b2df6e
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file1.ecore
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+ xmi:id="_A" name="File1_A">
+ <eSubpackages href="../project3/file3.ecore#_C"/>
+</ecore:EPackage> \ No newline at end of file
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file2.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file2.ecore
new file mode 100644
index 000000000..537d7623e
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file2.ecore
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+ xmi:id="_B" name="File2_B">
+ <eSubpackages href="../project3/file3.ecore#_C"/>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file3.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file3.ecore
new file mode 100644
index 000000000..07e473eeb
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case2/file3.ecore
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmi:id="_C" name="File3_C">
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file1.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file1.ecore
new file mode 100644
index 000000000..a21e37c62
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file1.ecore
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+ xmi:id="_A" name="File1_A">
+ <eSubpackages href="a/file2.ecore#_B"/>
+</ecore:EPackage> \ No newline at end of file
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file2.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file2.ecore
new file mode 100644
index 000000000..531c77313
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file2.ecore
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+ xmi:id="_B" name="File2_B">
+ <eSubpackages href="file3.ecore#_C"/>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file3.ecore b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file3.ecore
new file mode 100644
index 000000000..07e473eeb
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/logical/modelprovider/data/case3/file3.ecore
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmi:id="_C" name="File3_C">
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/suite/AllTests.java
index 81deaa288..d8c01aea8 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/suite/AllTests.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/suite/AllTests.java
@@ -8,6 +8,7 @@
* Contributors:
* Obeo - initial API and implementation
* Philip Langer - adds test classes
+ * Stefan Dirix - add EMFModelProviderTest
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.tests.suite;
@@ -18,6 +19,7 @@ import junit.textui.TestRunner;
import org.eclipse.emf.compare.ComparePackage;
import org.eclipse.emf.compare.ide.ui.tests.compareconfiguration.EMFCompareConfigurationTest;
import org.eclipse.emf.compare.ide.ui.tests.contentmergeviewer.notloadedfragment.NotLoadedFragmentItemTest;
+import org.eclipse.emf.compare.ide.ui.tests.logical.modelprovider.EMFModelProviderTest;
import org.eclipse.emf.compare.ide.ui.tests.logical.resolver.DependencyGraphUpdaterTest;
import org.eclipse.emf.compare.ide.ui.tests.logical.resolver.GraphResolutionTest;
import org.eclipse.emf.compare.ide.ui.tests.logical.resolver.ResolutionEventsTest;
@@ -39,19 +41,12 @@ import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
-@SuiteClasses({EMFCompareConfigurationTest.class,
- DependenciesTest.class,
- MergeActionTest.class,
- PseudoConflictsMergeActionTest.class,
- BugsTestSuite.class,
- NavigatableTest.class,
- NotLoadedFragmentNodeTest.class,
- NotLoadedFragmentItemTest.class,
- ResolutionEventsTest.class,
- ResourceComputationSchedulerTest.class,
- ThreadedModelResolverGraphTest.class,
- ThreadedModelResolverWithCustomDependencyProviderTest.class,
- DependencyGraphUpdaterTest.class, GraphResolutionTest.class })
+@SuiteClasses({EMFCompareConfigurationTest.class, DependenciesTest.class, MergeActionTest.class,
+ PseudoConflictsMergeActionTest.class, BugsTestSuite.class, NavigatableTest.class,
+ NotLoadedFragmentNodeTest.class, NotLoadedFragmentItemTest.class, ResolutionEventsTest.class,
+ ResourceComputationSchedulerTest.class, ThreadedModelResolverGraphTest.class,
+ ThreadedModelResolverWithCustomDependencyProviderTest.class, DependencyGraphUpdaterTest.class,
+ GraphResolutionTest.class, EMFModelProviderTest.class })
public class AllTests {
/**
* Launches the test with the given arguments.
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java
index bb2850367..0bad985bb 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2015 Obeo.
+ * Copyright (c) 2013, 2015 Obeo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* Obeo - initial API and implementation
+ * Stefan Dirix - bug 466607
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.logical;
@@ -184,41 +185,36 @@ public final class ComparisonScopeBuilder {
* one. Can be <code>null</code>.
* @param monitor
* The monitor on which to report progress information to the user.
- * @return The newly created SynchronizationModel, <code>null</code> in case of user interruption.
+ * @return The newly created SynchronizationModel.
+ * @throws InterruptedException
+ * In case of user interruption.
*/
/* package */SynchronizationModel buildSynchronizationModel(ITypedElement left, ITypedElement right,
- ITypedElement origin, IProgressMonitor monitor) {
+ ITypedElement origin, IProgressMonitor monitor) throws InterruptedException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("buildSynchronizationModel - START"); //$NON-NLS-1$
}
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
subMonitor.subTask(EMFCompareIDEUIMessages.getString("EMFSynchronizationModel.resolving")); //$NON-NLS-1$
- try {
- final SynchronizationModel syncModel;
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("buildSynchronizationModel - Creating sync model"); //$NON-NLS-1$
- }
- if (storageAccessor != null) {
- syncModel = createSynchronizationModel(storageAccessor, left, right, origin, subMonitor
- .newChild(90));
- } else {
- syncModel = createSynchronizationModel(left, right, origin, subMonitor.newChild(90));
- }
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("buildSynchronizationModel - Minimizing model"); //$NON-NLS-1$
- }
- minimizer.minimize(syncModel, subMonitor.newChild(10));
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("buildSynchronizationModel - FINISH NORMALLY"); //$NON-NLS-1$
- }
- return syncModel;
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("buildSynchronizationModel - FINISH ABNORMALLY"); //$NON-NLS-1$
- }
- return null;
+
+ final SynchronizationModel syncModel;
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("buildSynchronizationModel - Creating sync model"); //$NON-NLS-1$
+ }
+ if (storageAccessor != null) {
+ syncModel = createSynchronizationModel(storageAccessor, left, right, origin, subMonitor
+ .newChild(90));
+ } else {
+ syncModel = createSynchronizationModel(left, right, origin, subMonitor.newChild(90));
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("buildSynchronizationModel - Minimizing model"); //$NON-NLS-1$
+ }
+ minimizer.minimize(syncModel, subMonitor.newChild(10));
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("buildSynchronizationModel - FINISH NORMALLY"); //$NON-NLS-1$
}
+ return syncModel;
}
/**
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFModelProvider.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFModelProvider.java
index bcde040fd..441098895 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFModelProvider.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFModelProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2015 Obeo.
+ * Copyright (c) 2012, 2015 Obeo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,15 +7,27 @@
*
* Contributors:
* Obeo - initial API and implementation
+ * Stefan Dirix - bug 466607
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.logical;
+import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
@@ -32,6 +44,8 @@ import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
+import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.CrossReferenceResolutionScope;
+import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.ResolutionUtil;
import org.eclipse.emf.compare.ide.ui.logical.IModelMinimizer;
import org.eclipse.emf.compare.ide.ui.logical.IModelResolver;
import org.eclipse.emf.compare.ide.ui.logical.IStorageProvider;
@@ -132,6 +146,62 @@ public class EMFModelProvider extends ModelProvider {
return super.getMappings(resource, context, monitor);
}
+ @Override
+ public ResourceMapping[] getMappings(IResource[] resources, ResourceMappingContext context,
+ IProgressMonitor monitor) throws CoreException {
+ if (LOGGER.isInfoEnabled()) {
+ final Joiner joiner = Joiner.on(",").skipNulls(); //$NON-NLS-1$
+ final String resourceList = joiner.join(resources);
+ LOGGER.info("getMappings() - START for " + resourceList); //$NON-NLS-1$
+ }
+
+ final List<ResourceMapping> mappings = new ArrayList<ResourceMapping>();
+
+ // collect all IFiles
+ final List<IFile> files = new ArrayList<IFile>();
+ final List<IResource> remainingResources = new ArrayList<IResource>();
+
+ for (IResource resource : resources) {
+ if (resource instanceof IFile) {
+ files.add((IFile)resource);
+ } else {
+ remainingResources.add(resource);
+ }
+ }
+
+ try {
+ final Map<SynchronizationModel, IFile> syncModels = computeLogicalModels(files, context, monitor);
+ for (Map.Entry<SynchronizationModel, IFile> entry : syncModels.entrySet()) {
+ final ResourceMapping mapping = new EMFResourceMapping(entry.getValue(), context, entry
+ .getKey(), PROVIDER_ID);
+ mappings.add(mapping);
+ }
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("getMappings() - interrupt exception: fallback to super."); //$NON-NLS-1$
+ }
+ return super.getMappings(resources, context, monitor);
+ }
+
+ if (!remainingResources.isEmpty()) {
+ if (LOGGER.isInfoEnabled()) {
+ final Joiner joiner = Joiner.on(",").skipNulls(); //$NON-NLS-1$
+ final String resourceList = joiner.join(remainingResources);
+ LOGGER.info("getMappings() - not all resources were handled. fallback to super for: " + resourceList); //$NON-NLS-1$
+ }
+ mappings.addAll(Arrays.asList(super.getMappings(remainingResources
+ .toArray(new IResource[remainingResources.size()]), context, monitor)));
+ } else {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("getMappings() - FINISH NORMALLY"); //$NON-NLS-1$
+ }
+ }
+
+ return mappings.toArray(new ResourceMapping[mappings.size()]);
+ }
+
/**
* Clears the caches of this provider.
*/
@@ -182,8 +252,13 @@ public class EMFModelProvider extends ModelProvider {
}
syncModel = computeLogicalModel(file, context, monitor);
if (syncModel != null) {
- for (IResource res : syncModel.getResources()) {
- resourceMappingCache.put(res, syncModel);
+ if (ResolutionUtil.getResolutionScope().equals(CrossReferenceResolutionScope.WORKSPACE)) {
+ // logical model will be the same for all resources contained in the model
+ for (IResource res : syncModel.getResources()) {
+ resourceMappingCache.put(res, syncModel);
+ }
+ } else {
+ resourceMappingCache.put(file, syncModel);
}
}
} else if (LOGGER.isDebugEnabled()) {
@@ -274,6 +349,146 @@ public class EMFModelProvider extends ModelProvider {
}
/**
+ * Resolve the logical model(s) of the given files.
+ * <p>
+ * When model resolution setting is not set to "workspace" the
+ * {@link #computeLogicalModel(IFile, ResourceMappingContext, IProgressMonitor)} method can not guarantee
+ * to compute the whole logical model for the given file. But there are actually use cases in which the
+ * client already knows which files constitute the logical model, see for example
+ * {@link #getMappings(IResource[], ResourceMappingContext, IProgressMonitor)}.
+ * </p>
+ * <p>
+ * This method tries to combine the logical models of the given {@code files} if applicable. Depending of
+ * the given {@code files} the whole logical model may be resolved independent of the workspace resolution
+ * settings.
+ * </p>
+ *
+ * @param files
+ * The {@link IFile}s for which the logical model(s) is to be computed.
+ * @param context
+ * The resource mapping context.
+ * @param monitor
+ * Used to display progress information to the user.
+ * @return The computed logical models each mapped to the first file from which they were resolved. The
+ * logical models are disjoint.
+ * @throws CoreException
+ * If we cannot retrieve the content of a resource for some reason.
+ * @throws InterruptedException
+ * If the user interrupts the resolving.
+ */
+ Map<SynchronizationModel, IFile> computeLogicalModels(Collection<IFile> files,
+ ResourceMappingContext context, IProgressMonitor monitor) throws CoreException,
+ InterruptedException {
+ if (LOGGER.isDebugEnabled()) {
+ final Joiner joiner = Joiner.on(",").skipNulls(); //$NON-NLS-1$
+ final String fileList = joiner.join(files);
+ LOGGER.debug("computeLogicalModels() - START with " + fileList); //$NON-NLS-1$
+ }
+ final Map<SynchronizationModel, IFile> syncModels = new LinkedHashMap<SynchronizationModel, IFile>();
+
+ for (IFile file : files) {
+ final SynchronizationModel currentSyncModel = getOrComputeLogicalModel(file, context, monitor);
+
+ if (currentSyncModel == null) {
+ // skip file
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("computeLogicalModels() - Could not determine logical model for \"" + file + "\". SKIP file."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ continue;
+ }
+
+ // subsets can be shortcutted
+ boolean combineModels = false;
+
+ final List<SynchronizationModel> toCombine = new LinkedList<SynchronizationModel>();
+
+ for (SynchronizationModel syncModel : syncModels.keySet()) {
+
+ if (!Collections.disjoint(syncModel.getResources(), currentSyncModel.getResources())) {
+ toCombine.add(syncModel);
+
+ if (!syncModel.getResources().containsAll(currentSyncModel.getResources())) {
+ // the model is not a subset -> deactivate shortcut
+ combineModels = true;
+ }
+ }
+ }
+
+ // if there are no models to combine, add this model to the result set
+ if (toCombine.isEmpty()) {
+ syncModels.put(currentSyncModel, file);
+ }
+
+ // when a model can be added to multiple other models, all these models must be combined
+ if (combineModels) {
+ final Iterator<SynchronizationModel> it = toCombine.iterator();
+ final SynchronizationModel firstToCombine = it.next();
+ final IFile value = syncModels.get(firstToCombine);
+
+ SynchronizationModel combinedModel = combineModels(currentSyncModel, firstToCombine);
+ syncModels.remove(firstToCombine);
+
+ while (it.hasNext()) {
+ final SynchronizationModel currentModel = it.next();
+ combinedModel = combineModels(combinedModel, currentModel);
+ syncModels.remove(currentModel);
+ }
+
+ syncModels.put(combinedModel, value);
+ }
+ }
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("computeLogicalModels() - FINISH with " + syncModels.size() + " models"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return syncModels;
+ }
+
+ /**
+ * Combines the given {@link SynchronizationModel}s to a new model.
+ *
+ * @param modelA
+ * The model to combine.
+ * @param modelB
+ * The model to combine.
+ * @return A new {@link SynchronizationModel} which contains the combination of {@link StorageTraversal}s
+ * of the given models.
+ */
+ private SynchronizationModel combineModels(SynchronizationModel modelA, SynchronizationModel modelB) {
+ Set<IStorage> left = new HashSet<IStorage>();
+ Set<IStorage> right = new HashSet<IStorage>();
+ Set<IStorage> origin = new HashSet<IStorage>();
+
+ StorageTraversal leftTraversal = null;
+ StorageTraversal rightTraversal = null;
+ StorageTraversal originTraversal = null;
+
+ if (modelA.getLeftTraversal() != null) {
+ left.addAll(modelA.getLeftTraversal().getStorages());
+ if (modelB != null && modelB.getLeftTraversal() != null) {
+ left.addAll(modelB.getLeftTraversal().getStorages());
+ }
+ leftTraversal = new StorageTraversal(left);
+ }
+ if (modelA.getRightTraversal() != null) {
+ right.addAll(modelA.getRightTraversal().getStorages());
+ if (modelB != null && modelB.getRightTraversal() != null) {
+ right.addAll(modelB.getRightTraversal().getStorages());
+ }
+ rightTraversal = new StorageTraversal(right);
+ }
+ if (modelA.getOriginTraversal() != null) {
+ origin.addAll(modelA.getOriginTraversal().getStorages());
+ if (modelB != null && modelB.getOriginTraversal() != null) {
+ right.addAll(modelB.getOriginTraversal().getStorages());
+ }
+ originTraversal = new StorageTraversal(origin);
+ }
+
+ return new SynchronizationModel(leftTraversal, rightTraversal, originTraversal);
+ }
+
+ /**
* Browses the given traversal in order to create a typed element for the given side of the comparison.
*
* @param traversal

Back to the top