/******************************************************************************* * Copyright (c) 2015 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.ide.ui.internal.logical.resolver; import static org.eclipse.emf.compare.ide.utils.ResourceUtil.createURIFor; import static org.eclipse.emf.compare.ide.utils.ResourceUtil.hasModelType; import com.google.common.collect.ImmutableSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.runtime.CoreException; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; /** * This will listen to workspace changes and react to all changes on "model" resources as determined by * {@link ThreadedModelResolver#MODEL_CONTENT_TYPES}. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> * @see ThreadedModelResolver#hasModelType(IFile) */ public class ModelResourceListener implements IResourceChangeListener, IResourceDeltaVisitor { /** Keeps track of the URIs that need to be reparsed when next we need the dependencies graph . */ protected final Set<URI> changedURIs; /** Tracks the files that have been removed. */ protected final Set<URI> removedURIs; /** Prevents concurrent access to the two internal sets. */ protected final ReentrantLock internalLock; /** Initializes this listener. */ public ModelResourceListener() { this.changedURIs = new LinkedHashSet<URI>(); this.removedURIs = new LinkedHashSet<URI>(); this.internalLock = new ReentrantLock(); } /** {@inheritDoc} */ public void resourceChanged(IResourceChangeEvent event) { final IResourceDelta delta = event.getDelta(); if (delta == null) { return; } /* * We must block any and all threads from using the two internal sets through either popChangedURIs or * popRemovedURIs while we are parsing a resource delta. This particular locking is here to avoid such * misuses. */ internalLock.lock(); try { delta.accept(this); } catch (CoreException e) { EMFCompareIDEUIPlugin.getDefault().log(e); } finally { internalLock.unlock(); } } /** * Retrieves the set of all changed URIs since we last updated the dependencies graph, and clears it for * subsequent calls. * * @return The set of all changed URIs since we last updated the dependencies graph. */ public Set<URI> popChangedURIs() { final Set<URI> changed; internalLock.lock(); try { changed = ImmutableSet.copyOf(changedURIs); changedURIs.clear(); } finally { internalLock.unlock(); } return changed; } /** * Retrieves the set of all removed URIs since we last updated the dependencies graph, and clears it for * subsequent calls. * * @return The set of all removed URIs since we last updated the dependencies graph. */ public Set<URI> popRemovedURIs() { final Set<URI> removed; internalLock.lock(); try { removed = ImmutableSet.copyOf(removedURIs); removedURIs.clear(); } finally { internalLock.unlock(); } return removed; } /** * {@inheritDoc} * * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) */ public boolean visit(IResourceDelta delta) throws CoreException { // Note : the lock (#lock) must be acquired by the current thread _before_ calling #accept() on // this visitor. if (delta.getFlags() == IResourceDelta.MARKERS || delta.getResource().getType() != IResource.FILE) { return true; } final IFile file = (IFile)delta.getResource(); final URI fileURI = createURIFor(file); // We can't check the content type of a removed resource if (delta.getKind() == IResourceDelta.REMOVED) { removedURIs.add(fileURI); changedURIs.remove(fileURI); } else if (hasModelType(file)) { if ((delta.getKind() & IResourceDelta.CHANGED) != 0) { changedURIs.add(fileURI); // Probably can't happen, but let's stay on the safe side removedURIs.remove(fileURI); } else if ((delta.getKind() & IResourceDelta.ADDED) != 0) { // If a previously removed resource is changed, we can assume it's been re-added if (removedURIs.remove(fileURI)) { changedURIs.add(fileURI); } } } return true; } }