| /******************************************************************************* |
| * Copyright (c) 2013, 2017 Obeo and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Obeo - initial API and implementation |
| * Stefan Dirix - bug 466607 |
| * Philip Langer - add support for setting initial file URIs to scope |
| * Martin Fleck - bug 512562 |
| *******************************************************************************/ |
| package org.eclipse.emf.compare.ide.ui.internal.logical; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.common.collect.Collections2.transform; |
| import static org.eclipse.emf.compare.ide.ui.internal.util.PlatformElementUtil.findFile; |
| import static org.eclipse.emf.compare.ide.utils.ResourceUtil.createURIFor; |
| |
| import com.google.common.base.Function; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.Sets; |
| |
| import java.lang.reflect.Field; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| |
| import org.apache.log4j.Logger; |
| import org.eclipse.compare.ICompareContainer; |
| import org.eclipse.compare.IStreamContentAccessor; |
| import org.eclipse.compare.ITypedElement; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IStorage; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.emf.common.util.BasicDiagnostic; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.compare.DifferenceSource; |
| import org.eclipse.emf.compare.ide.EMFCompareIDEPlugin; |
| import org.eclipse.emf.compare.ide.internal.hook.ResourceSetHookRegistry; |
| import org.eclipse.emf.compare.ide.internal.utils.NotLoadingResourceSet; |
| import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages; |
| import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; |
| import org.eclipse.emf.compare.ide.ui.internal.util.PlatformElementUtil; |
| 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.IStorageProviderAccessor; |
| import org.eclipse.emf.compare.ide.ui.logical.SynchronizationModel; |
| import org.eclipse.emf.compare.ide.utils.ResourceUtil; |
| import org.eclipse.emf.compare.ide.utils.StorageTraversal; |
| import org.eclipse.emf.compare.scope.DefaultComparisonScope; |
| import org.eclipse.emf.compare.scope.FilterComparisonScope; |
| import org.eclipse.emf.compare.scope.IComparisonScope; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.resource.URIConverter; |
| import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.team.core.subscribers.Subscriber; |
| import org.eclipse.team.core.subscribers.SubscriberMergeContext; |
| import org.eclipse.team.ui.synchronize.ISynchronizeParticipant; |
| import org.eclipse.team.ui.synchronize.ModelSynchronizeParticipant; |
| |
| /** |
| * This will be used by EMF Compare in order to construct its comparison scope given a "starting point". |
| * <p> |
| * A single file is not always a single EMF model, nor is an EMF model always stored in a single file. This |
| * will be used to resolve all physical resources composing the logical model we are to compare, minimize this |
| * set to the potential changed candidates, and construct the actual comparison scope. |
| * </p> |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| public final class ComparisonScopeBuilder { |
| /** The instance that will be in charge of resolving our logical models. */ |
| private final IModelResolver resolver; |
| |
| /** The instance that will be in charge of reducing the comparison scope. */ |
| private final IModelMinimizer minimizer; |
| |
| /** The accessor that can be used to retrieve synchronization information between our resources. */ |
| private final IStorageProviderAccessor storageAccessor; |
| |
| /** The logger. */ |
| private static final Logger LOGGER = Logger.getLogger(ComparisonScopeBuilder.class); |
| |
| /** Function transforming an IResource into its URI. */ |
| private static final Function<IResource, URI> TO_FILE_URIS = new Function<IResource, URI>() { |
| public URI apply(IResource input) { |
| URI uri; |
| if (input instanceof IStorage) { |
| uri = ResourceUtil.asURI().apply((IStorage)input); |
| } else { |
| uri = URI.createPlatformResourceURI(input.getFullPath().toString(), true); |
| } |
| return uri; |
| } |
| }; |
| |
| /** |
| * Constructs a builder given its model resolver and minimizer. |
| * |
| * @param resolver |
| * The resolver we'll use to resolve the logical models of this scope. |
| * @param minimizer |
| * The minimizer that will be use to reduce the comparison scope before construction. |
| */ |
| public ComparisonScopeBuilder(IModelResolver resolver, IModelMinimizer minimizer) { |
| this(resolver, minimizer, null); |
| } |
| |
| /** |
| * Constructs a builder given its model resolver and minimizer, along with the storage accessor for remote |
| * data. |
| * |
| * @param resolver |
| * The resolver we'll use to resolve the logical models of this scope. |
| * @param minimizer |
| * The minimizer that will be use to reduce the comparison scope before construction. |
| * @param storageAccessor |
| * The storage accessor we'll use to access remote resource variants. May be <code>null</code>, |
| * in which case we'll only use local content. |
| */ |
| public ComparisonScopeBuilder(IModelResolver resolver, IModelMinimizer minimizer, |
| IStorageProviderAccessor storageAccessor) { |
| this.resolver = checkNotNull(resolver); |
| this.minimizer = checkNotNull(minimizer); |
| this.storageAccessor = storageAccessor; |
| } |
| |
| /** |
| * Builds a comparison scope from the given two starting elements. |
| * |
| * @param left |
| * The element that will be used as the starting point to resolve the left logical model. |
| * @param right |
| * Element that will be used as the starting point to resolve the left logical model. |
| * @param monitor |
| * The monitor on which to report progress information to the user. |
| * @return The newly created comparison scope. |
| */ |
| public IComparisonScope build(ITypedElement left, ITypedElement right, IProgressMonitor monitor) { |
| return build(left, right, null, monitor); |
| } |
| |
| /** |
| * Builds a comparison scope from the given starting elements. |
| * |
| * @param left |
| * The element that will be used as the starting point to resolve the left logical model. |
| * @param right |
| * Element that will be used as the starting point to resolve the left logical model. |
| * @param origin |
| * The origin resource, starting point of the logical model we are to resolve as the origin |
| * one. Can be <code>null</code>. |
| * @param monitor |
| * The monitor on which to report progress information to the user. |
| * @return The newly created comparison scope. |
| */ |
| public IComparisonScope build(ITypedElement left, ITypedElement right, ITypedElement origin, |
| IProgressMonitor monitor) { |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 100); |
| subMonitor.subTask(EMFCompareIDEUIMessages.getString("EMFSynchronizationModel.resolving")); //$NON-NLS-1$ |
| try { |
| final SynchronizationModel syncModel; |
| if (storageAccessor != null) { |
| syncModel = createSynchronizationModel(storageAccessor, left, right, origin, |
| subMonitor.newChild(60)); |
| } else { |
| syncModel = createSynchronizationModel(left, right, origin, subMonitor.newChild(60)); |
| } |
| |
| return createMinimizedScope(left, syncModel, subMonitor.newChild(40)); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| EmptyComparisonScope scope = new EmptyComparisonScope(); |
| scope.setDiagnostic(BasicDiagnostic.toDiagnostic(e)); |
| return scope; |
| } |
| } |
| |
| /** |
| * Resolves and minimizes the logical model for the given three typed element as would be done by |
| * {@link #build(ITypedElement, ITypedElement, ITypedElement, IProgressMonitor)}, but returns |
| * directly the SynchronizationModel DTO instead of the actual IComparisonScope. |
| * <p> |
| * This internal API is only intended for use by the resource mapping mergers. |
| * </p> |
| * |
| * @param left |
| * The element that will be used as the starting point to resolve the left logical |
| * model. |
| * @param right |
| * Element that will be used as the starting point to resolve the left logical model. |
| * @param origin |
| * The origin resource, starting point of the logical model we are to resolve as the |
| * origin one. Can be <code>null</code>. |
| * @param monitor |
| * The monitor on which to report progress information to the user. |
| * @return The newly created SynchronizationModel. |
| * @throws InterruptedException |
| * In case of user interruption. |
| */ |
| /* package */SynchronizationModel buildSynchronizationModel(ITypedElement left, |
| ITypedElement right, 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$ |
| |
| 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(PlatformElementUtil.findFile(left), syncModel, subMonitor.newChild(10)); |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug("buildSynchronizationModel - FINISH NORMALLY"); //$NON-NLS-1$ |
| } |
| return syncModel; |
| } |
| |
| /** |
| * Constructs the comparison scope corresponding to the given typed elements. |
| * |
| * @param left |
| * Left of the compared elements. |
| * @param right |
| * Right of the compared elements. |
| * @param origin |
| * Common ancestor of the <code>left</code> and <code>right</code> compared elements. |
| * @param monitor |
| * Monitor to report progress on. |
| * @return The created comparison scope. |
| */ |
| public static IComparisonScope create(ICompareContainer container, ITypedElement left, |
| ITypedElement right, ITypedElement origin, IProgressMonitor monitor) { |
| IStorageProviderAccessor storageAccessor = null; |
| Subscriber subscriber = getSubscriber(container); |
| if (subscriber != null) { |
| storageAccessor = new SubscriberStorageAccessor(subscriber); |
| } |
| IStorage leftStorage = PlatformElementUtil.findFile(left); |
| if (leftStorage == null) { |
| leftStorage = StreamAccessorStorage.fromTypedElement(left); |
| } |
| IModelResolver resolver = EMFCompareIDEUIPlugin.getDefault().getModelResolverRegistry() |
| .getBestResolverFor(leftStorage); |
| final ComparisonScopeBuilder scopeBuilder = new ComparisonScopeBuilder(resolver, |
| EMFCompareIDEUIPlugin.getDefault().getModelMinimizerRegistry().getCompoundMinimizer(), |
| storageAccessor); |
| return scopeBuilder.build(left, right, origin, monitor); |
| } |
| |
| /** |
| * Creates the comparison scope corresponding to the given synchronization model, with no further |
| * operation on it. |
| * <p> |
| * This internal API is only intended for use by the resource mapping mergers and is not meant to be |
| * referenced. |
| * </p> |
| * |
| * @param synchronizationModel |
| * The synchronization model describing the traversals for which a comparison scope is needed. |
| * @param monitor |
| * Monitor on which to report progress information to the user. |
| * @return The created comparison scope. |
| * @throws OperationCanceledException |
| * if the user cancels (or has already canceled) the operation through the given |
| * {@code monitor}. |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| public static IComparisonScope create(SynchronizationModel synchronizationModel, IProgressMonitor monitor) |
| throws OperationCanceledException { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| |
| return createScope(synchronizationModel, monitor); |
| } |
| |
| /** |
| * Team left us with absolutely no way to determine whether our supplied input is the result of a |
| * synchronization or not. |
| * <p> |
| * In order to properly resolve the logical model of the resource currently being compared we need to know |
| * what "other" resources were part of its logical model, and we need to know the revisions of these |
| * resources we are to load. All of this has already been computed by Team, but it would not let us know. |
| * This method uses discouraged means to get around this "black box" locking from Team. |
| * </p> |
| * <p> |
| * The basic need here is to retrieve the Subscriber from this point. We have a lot of accessible |
| * variables, the two most important being the CompareConfiguration and ICompareInput... I could find no |
| * way around the privileged access to the private ModelCompareEditorInput.participant field. There does |
| * not seem to be any adapter (or Platform.getAdapterManager().getAdapter(...)) that would allow for this, |
| * so I'm taking the long way 'round. |
| * </p> |
| * |
| * @return The subscriber used for this comparison if any could be found, <code>null</code> otherwise. |
| */ |
| @SuppressWarnings("restriction") |
| private static Subscriber getSubscriber(ICompareContainer container) { |
| if (container instanceof org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput) { |
| final org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput modelInput = (org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput)container; |
| ISynchronizeParticipant participant = null; |
| try { |
| final Field field = org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput.class |
| .getDeclaredField("participant"); //$NON-NLS-1$ |
| AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| field.setAccessible(true); |
| return null; |
| } |
| }); |
| participant = (ISynchronizeParticipant)field.get(modelInput); |
| } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { |
| // Swallow this, this private field was there at least from 3.5 to 4.3 |
| } |
| if (participant instanceof ModelSynchronizeParticipant |
| && ((ModelSynchronizeParticipant)participant) |
| .getContext() instanceof SubscriberMergeContext) { |
| return ((SubscriberMergeContext)((ModelSynchronizeParticipant)participant).getContext()) |
| .getSubscriber(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Creates the synchronization model for the given three elements (left, right, and the common ancestor of |
| * the two). Since this comparison may concern either local or remote resources, all I/O operations should |
| * go through the given storage accessor. |
| * |
| * @param accessor |
| * The accessor that can be used to retrieve synchronization information between our resources. |
| * @param left |
| * Typed element used as the left side of this comparison. |
| * @param right |
| * Typed element used as the right side of this comparison. |
| * @param origin |
| * Common ancestor of <code>left</code> and <code>right</code>. |
| * @param monitor |
| * Monitor to report progress information on. |
| * @return The created synchronization model. |
| */ |
| private SynchronizationModel createSynchronizationModel(IStorageProviderAccessor accessor, |
| ITypedElement left, ITypedElement right, ITypedElement origin, IProgressMonitor monitor) |
| throws InterruptedException { |
| |
| // Can we find a local file to associate a proper path to our storages? |
| final IFile localFile = findFile(left); |
| String path = null; |
| if (localFile != null) { |
| path = localFile.getFullPath().toString(); |
| } |
| |
| final IStorage leftStorage = StreamAccessorStorage.fromTypedElement(path, left); |
| final IStorage rightStorage; |
| if (right instanceof IStreamContentAccessor) { |
| rightStorage = StreamAccessorStorage.fromTypedElement(path, right); |
| } else { |
| rightStorage = null; |
| } |
| final IStorage originStorage; |
| if (origin instanceof IStreamContentAccessor) { |
| originStorage = StreamAccessorStorage.fromTypedElement(path, origin); |
| } else { |
| originStorage = null; |
| } |
| |
| return resolver.resolveModels(accessor, leftStorage, rightStorage, originStorage, monitor); |
| } |
| |
| /** |
| * Creates the synchronization model for the given three elements (left, right, and their common |
| * ancestor). Since we have no remote data available, we'll consider that the three given files are either |
| * locally available, or that they have no logical model. |
| * |
| * @param left |
| * Typed element used as the left side of this comparison. |
| * @param right |
| * Typed element used as the right side of this comparison. |
| * @param origin |
| * Common ancestor of <code>left</code> and <code>right</code>. |
| * @param monitor |
| * Monitor to report progress information on. |
| * @return The created synchronization model. |
| */ |
| private SynchronizationModel createSynchronizationModel(ITypedElement left, ITypedElement right, |
| ITypedElement origin, IProgressMonitor monitor) throws InterruptedException { |
| // Is this a local comparison? |
| final IFile leftFile = findFile(left); |
| final IFile rightFile = findFile(right); |
| if (leftFile != null && rightFile != null) { |
| // assume origin is local or null |
| return resolver.resolveLocalModels(leftFile, rightFile, findFile(origin), monitor); |
| } |
| |
| // This is not a local comparison, and we've got no info on how to load remote revisions. |
| final IStorage leftStorage = StreamAccessorStorage.fromTypedElement(left); |
| Assert.isNotNull(leftStorage); |
| final IStorage rightStorage; |
| if (right instanceof IStreamContentAccessor) { |
| rightStorage = StreamAccessorStorage.fromTypedElement(right); |
| } else { |
| rightStorage = null; |
| } |
| final IStorage originStorage; |
| if (origin instanceof IStreamContentAccessor) { |
| originStorage = StreamAccessorStorage.fromTypedElement(origin); |
| } else { |
| originStorage = null; |
| } |
| |
| return loadSingleResource(leftStorage, rightStorage, originStorage); |
| } |
| |
| /** |
| * We cannot resolve the logical model of these elements. Load them as single resources. |
| * |
| * @param left |
| * Storage to be loaded as left. |
| * @param right |
| * Storage to be loaded as right. |
| * @param origin |
| * Common ancestor of left and right, if any. |
| * @return The resolved synchronization model. |
| */ |
| private SynchronizationModel loadSingleResource(IStorage left, IStorage right, IStorage origin) { |
| final StorageTraversal leftTraversal = new StorageTraversal( |
| new LinkedHashSet<IStorage>(Arrays.asList(left))); |
| final StorageTraversal rightTraversal; |
| if (right != null) { |
| rightTraversal = new StorageTraversal(new LinkedHashSet<IStorage>(Arrays.asList(right))); |
| } else { |
| rightTraversal = new StorageTraversal(Sets.<IStorage> newLinkedHashSet()); |
| } |
| final StorageTraversal originTraversal; |
| if (origin != null) { |
| originTraversal = new StorageTraversal(new LinkedHashSet<IStorage>(Arrays.asList(origin))); |
| } else { |
| originTraversal = new StorageTraversal(Sets.<IStorage> newLinkedHashSet()); |
| } |
| |
| return new SynchronizationModel(leftTraversal, rightTraversal, originTraversal); |
| } |
| |
| /** |
| * Prepare the given synchronization model for use, then create the corresponding comparison scope. |
| * |
| * @param left |
| * The element that has been used as the starting point to resolve the left logical model. |
| * @param syncModel |
| * The synchronization model describing our resource traversals. |
| * @param monitor |
| * Monitor on which to report progress information to the user. |
| * @return The created comparison scope. |
| */ |
| private IComparisonScope createMinimizedScope(ITypedElement left, SynchronizationModel syncModel, |
| IProgressMonitor monitor) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 100); |
| // Minimize the traversals to non-read-only resources with no binary identical counterparts. |
| minimizer.minimize(PlatformElementUtil.findFile(left), syncModel, subMonitor.newChild(10)); |
| return createScope(syncModel, subMonitor.newChild(90)); |
| } |
| |
| /** |
| * Constructs the actual comparison scope given the storage traversals from which to load the resources. |
| * |
| * @param syncModel |
| * The synchronization model describing our resource traversals. |
| * @param monitor |
| * Monitor on which to report progress information to the user. |
| * @return The created comparison scope. |
| */ |
| private static IComparisonScope createScope(SynchronizationModel syncModel, IProgressMonitor monitor) { |
| SubMonitor progress = SubMonitor.convert(monitor, 3); |
| progress.subTask(EMFCompareIDEUIMessages.getString("EMFSynchronizationModel.creatingScope")); //$NON-NLS-1$ |
| |
| final StorageTraversal leftTraversal = syncModel.getLeftTraversal(); |
| final StorageTraversal rightTraversal = syncModel.getRightTraversal(); |
| final StorageTraversal originTraversal = syncModel.getOriginTraversal(); |
| |
| final ResourceSet originResourceSet; |
| ResourceSetHookRegistry resourceSetHookRegistry = EMFCompareIDEPlugin.getDefault() |
| .getResourceSetHookRegistry(); |
| if (originTraversal == null || originTraversal.getStorages().isEmpty()) { |
| originResourceSet = null; |
| progress.setWorkRemaining(2); |
| } else { |
| originResourceSet = NotLoadingResourceSet.create(originTraversal, progress.newChild(1), |
| resourceSetHookRegistry); |
| } |
| final ResourceSet leftResourceSet = NotLoadingResourceSet.create(leftTraversal, progress.newChild(1), |
| resourceSetHookRegistry); |
| final ResourceSet rightResourceSet = NotLoadingResourceSet.create(rightTraversal, |
| progress.newChild(1), resourceSetHookRegistry); |
| |
| final URIConverter converter = new ExtensibleURIConverterImpl(); |
| final Set<URI> urisInScope = Sets.newLinkedHashSet(); |
| for (IStorage left : leftTraversal.getStorages()) { |
| urisInScope.add(converter.normalize(createURIFor(left))); |
| } |
| for (IStorage right : rightTraversal.getStorages()) { |
| urisInScope.add(converter.normalize(createURIFor(right))); |
| } |
| if (originTraversal != null) { |
| for (IStorage origin : originTraversal.getStorages()) { |
| urisInScope.add(converter.normalize(createURIFor(origin))); |
| } |
| } |
| |
| final FilterComparisonScope scope = new DefaultComparisonScope(leftResourceSet, rightResourceSet, |
| originResourceSet); |
| scope.setResourceSetContentFilter(isInScope(urisInScope)); |
| |
| final Set<IResource> involvedResources = syncModel.getAllInvolvedResources(); |
| final Collection<URI> involvedResourceURIs = transform(involvedResources, TO_FILE_URIS); |
| scope.getAllInvolvedResourceURIs().addAll(involvedResourceURIs); |
| |
| Diagnostic syncModelDiagnostic = syncModel.getDiagnostic(); |
| Diagnostic scopeDiagnostic = computeDiagnostics(originResourceSet, leftResourceSet, rightResourceSet); |
| |
| BasicDiagnostic basicDiagnostic = new BasicDiagnostic(Diagnostic.OK, EMFCompareIDEUIPlugin.PLUGIN_ID, |
| 0, null, new Object[0]); |
| |
| basicDiagnostic.add(syncModelDiagnostic); |
| basicDiagnostic.add(scopeDiagnostic); |
| |
| scope.setDiagnostic(basicDiagnostic); |
| return scope; |
| } |
| |
| private static Diagnostic computeDiagnostics(final ResourceSet originResourceSet, |
| final ResourceSet leftResourceSet, final ResourceSet rightResourceSet) { |
| final BasicDiagnostic originDiagnostic; |
| if (originResourceSet != null) { |
| originDiagnostic = getResourceSetDiagnostic(originResourceSet, null, true); |
| } else { |
| originDiagnostic = null; |
| } |
| BasicDiagnostic leftDiagnostic = getResourceSetDiagnostic(leftResourceSet, DifferenceSource.LEFT, |
| true); |
| BasicDiagnostic rightDiagnostic = getResourceSetDiagnostic(rightResourceSet, DifferenceSource.RIGHT, |
| true); |
| |
| BasicDiagnostic diagnostic = new BasicDiagnostic(Diagnostic.OK, EMFCompareIDEUIPlugin.PLUGIN_ID, 0, |
| EMFCompareIDEUIMessages.getString("ComparisonScopeBuilder.comparisonScopeDiagnostic"), //$NON-NLS-1$ |
| new Object[0]); |
| |
| if (originDiagnostic != null) { |
| diagnostic.add(originDiagnostic); |
| } |
| diagnostic.add(leftDiagnostic); |
| diagnostic.add(rightDiagnostic); |
| |
| return diagnostic; |
| } |
| |
| /** |
| * Compute the diagnotic on each resource of the given resource set and return a diagnostic composed of |
| * |
| * @param resourceSet |
| * the resource set |
| * @param side |
| * the resource set's side. null means origin |
| * @param includeWarning |
| * @return the composite diagnostic |
| */ |
| private static BasicDiagnostic getResourceSetDiagnostic(final ResourceSet resourceSet, |
| DifferenceSource side, boolean includeWarning) { |
| final String sideStr; |
| if (side == DifferenceSource.LEFT) { |
| sideStr = EMFCompareIDEUIMessages.getString("ComparisonScopeBuilder.left"); //$NON-NLS-1$ |
| } else if (side == DifferenceSource.RIGHT) { |
| sideStr = EMFCompareIDEUIMessages.getString("ComparisonScopeBuilder.right"); //$NON-NLS-1$ |
| } else { |
| sideStr = EMFCompareIDEUIMessages.getString("ComparisonScopeBuilder.ancestor"); //$NON-NLS-1$ |
| } |
| BasicDiagnostic diagnostic = new BasicDiagnostic(EMFCompareIDEUIPlugin.PLUGIN_ID, 0, |
| EMFCompareIDEUIMessages.getString("ComparisonScopeBuilder.resourceSetDiagnostic", sideStr), //$NON-NLS-1$ |
| new Object[0]); |
| for (Resource resource : resourceSet.getResources()) { |
| Diagnostic resourceDiagnostic = EcoreUtil.computeDiagnostic(resource, includeWarning); |
| diagnostic.merge(resourceDiagnostic); |
| } |
| return diagnostic; |
| } |
| |
| /** |
| * Returns a predicate that can be applied to {@link Resource}s in order to check if their URI is |
| * contained in the given set. |
| * |
| * @param uris |
| * URIs that we consider to be in this scope. |
| * @return A useable Predicate. |
| */ |
| private static Predicate<Resource> isInScope(final Set<URI> uris) { |
| return new Predicate<Resource>() { |
| public boolean apply(Resource input) { |
| final boolean result; |
| if (input != null) { |
| URI inputUri = input.getURI(); |
| if (uris.contains(inputUri)) { |
| result = true; |
| } else if (input.getResourceSet() != null |
| && input.getResourceSet().getURIConverter() != null) { |
| // Tries to normalize the URI in case it is a pathmap uri that needs to be resolved |
| URI normalizedInputUri = input.getResourceSet().getURIConverter().normalize(inputUri); |
| result = uris.contains(normalizedInputUri); |
| } else { |
| result = false; |
| } |
| } else { |
| result = false; |
| } |
| return result; |
| } |
| }; |
| } |
| } |