/******************************************************************************* * Copyright (c) 2014, 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Obeo - initial API and implementation * Alexandra Buzila - Fixes for Bug 462938 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.logical.resolver; import com.google.common.eventbus.EventBus; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.graph.IGraph; import org.eclipse.emf.compare.graph.IGraphView; import org.eclipse.emf.compare.ide.ui.logical.AbstractModelResolver; 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.StorageTraversal; import org.eclipse.emf.compare.internal.utils.ReadOnlyGraph; import org.eclipse.emf.compare.rcp.graph.IGraphConsumer; import org.eclipse.emf.compare.rcp.ui.internal.util.ResourceUIUtil; /** * This implementation of an {@link IModelResolver} will look up all of the models located in a set container * level of the "starting point" (by default, the containing project) to construct the graph of dependencies * between these models. * <p> * Once this graph is created for the "local" resource, the right and origin (if any) resources will be * inferred from the same traversal of resources, though this time expanded with a "top-down" approach : load * all models of the traversal from the remote side, then resolve their containment tree to check whether * there are other remote resources in the logical model that do not (or "that no longer) exist locally and * thus couldn't be discovered in the first resolution phase. <b>Note</b> that this will be looped in order to * determine whether the resource is really inexistent locally, or if on the contrary, it is a new dependency * that's been added remotely; in which case we need to start from the local resolution again : the local * resource may have changed locally and depend on other again. * </p> * <p> * All model loading will happen concurrently. At first, a distinct thread will be launched to resolve every * model discovered in the container we're browsing. Then, each thread can and will launch separate threads to * resolve the set of dependencies discovered "under" the model they are in charge of resolving. * </p> * <p> * No model will be loaded twice, since this will be aware of what models have already been resolved, thus * ignoring duplicate resolving demands. * </p> * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ public class ThreadedModelResolver extends AbstractModelResolver implements IGraphConsumer { private IResolutionContext context; /** * The URI Graph instance. */ private IGraph<URI> graph; /** * Convert the dependency graph to its read-only version. * * @return a read-only version of the dependency graph associated to this model resolver. */ public IGraphView<URI> getGraphView() { return ReadOnlyGraph.toReadOnlyGraph(graph); } /** * {@inheritDoc} When initialized, the ThreadedModelResolver will: * <ol> * <li>install a listener on the workspace to keep track of modified resources</li> * <li>Register its {@link #graphUpdater} to its {@link #eventBus}</li> * <li>initialize its {@link #scheduler}</li> * </ol> */ @Override public void initialize() { super.initialize(); if (graph == null) { throw new IllegalStateException(); } EventBus eventBus = new EventBus(); this.context = createContext(eventBus, graph); context.initialize(); } /** {@inheritDoc} */ @Override public void dispose() { context.dispose(); super.dispose(); } /** * For testing purposes, this method is protected. * * @param eventBus * @param aGraph * @return The resolution context to use. */ protected DefaultResolutionContext createContext(EventBus eventBus, IGraph<URI> aGraph) { return new DefaultResolutionContext(eventBus, aGraph, new DependencyGraphUpdater<URI>(aGraph, eventBus), new ResourceComputationScheduler<URI>(), new ModelResourceListener()); } /** {@inheritDoc} */ public boolean canResolve(IStorage sourceStorage) { return true; } /** * {@inheritDoc} * <p> * Note that no two threads will be able to resolve models at once : all three "resolve*" methods will * lock internally to prevent multiple resolutions at once. Though this shouldn't happen unless the user * calls multiple comparisons one after the other in quick succession, we use this locking to prevent * potential unforeseen interactions. * </p> */ public StorageTraversal resolveLocalModel(final IResource start, final IProgressMonitor monitor) throws InterruptedException { LocalModelResolution comp = new LocalModelResolution(context, monitor); return comp.run(start); } /** * {@inheritDoc} * <p> * Note that no two threads will be able to resolve models at once : all three "resolve*" methods will * lock internally to prevent multiple resolutions at once. Though this shouldn't happen unless the user * calls multiple comparisons one after the other in quick succession, we use this locking to prevent * potential unforeseen interactions. * </p> */ public SynchronizationModel resolveLocalModels(IResource left, IResource right, IResource origin, IProgressMonitor monitor) throws InterruptedException { LocalModelsResolution comp = new LocalModelsResolution(context, left, right, origin, monitor); return comp.run(); } /** * {@inheritDoc} * <p> * Note that no two threads will be able to resolve models at once : all three "resolve*" methods will * lock internally to prevent multiple resolutions at once. Though this shouldn't happen unless the user * calls multiple comparisons one after the other in quick succession, we use this locking to prevent * potential unforeseen interactions. * </p> */ public SynchronizationModel resolveModels(final IStorageProviderAccessor storageAccessor, final IStorage left, final IStorage right, final IStorage origin, final IProgressMonitor monitor) throws InterruptedException { ModelsResolution comp = new ModelsResolution(context, monitor, storageAccessor, left, right, origin); return comp.run(); } /** * Getter for the ID of the Resource Graph. */ public String getId() { return ResourceUIUtil.RESOURCES_GRAPH_ID; } /** * Setter for the URI Graph istance. * * @param graph * The URI Graph */ public void setGraph(IGraph<URI> graph) { this.graph = graph; } }