/*******************************************************************************
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
* Philip Langer - bug 470268
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.logical;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.emf.compare.ide.ui.logical.IStorageProvider;
import org.eclipse.emf.compare.ide.ui.logical.IStorageProviderAccessor;
import org.eclipse.team.core.diff.IDiff;
import org.eclipse.team.core.diff.IThreeWayDiff;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.core.mapping.provider.ResourceDiff;
import org.eclipse.team.core.subscribers.Subscriber;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.core.variants.IResourceVariantTree;
import org.eclipse.team.core.variants.ResourceVariantTreeSubscriber;
/**
* This is used by our synchronization model to access the innards of Team's {@link Subscriber}s.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
public final class SubscriberStorageAccessor implements IStorageProviderAccessor {
/** Our underlying subscriber. */
private final Subscriber subscriber;
/** The resource variant tree holding data for the common ancestor of the underlying subscriber. */
private final IResourceVariantTree originTree;
/** The resource variant tree holding data for the remote side of the underlying subscriber. */
private final IResourceVariantTree remoteTree;
/** The resource variant tree holding data for the source side of the underlying subscriber. */
private final IResourceVariantTree sourceTree;
/** The detector for determining whether a resource has been renamed based on the subscriber's diffs. */
private final RenameDetector renameDetector;
/**
* Wraps the given subscriber within this accessor.
*
* @param subscriber
* The wrapped subscriber.
*/
public SubscriberStorageAccessor(Subscriber subscriber) {
this.subscriber = subscriber;
originTree = initTree(subscriber, "getBaseTree"); //$NON-NLS-1$
remoteTree = initTree(subscriber, "getRemoteTree"); //$NON-NLS-1$
sourceTree = initTree(subscriber, "getSourceTree"); //$NON-NLS-1$
renameDetector = new RenameDetector(subscriber, this);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.ide.ui.logical.IStorageProviderAccessor#getStorageProvider(org.eclipse.core.resources.IResource,
* org.eclipse.emf.compare.ide.ui.logical.IStorageProviderAccessor.DiffSide)
*/
public IStorageProvider getStorageProvider(IResource resource, DiffSide side) throws CoreException {
final IStorageProvider provider;
switch (side) {
case SOURCE:
provider = getSourceVariant(resource);
break;
case REMOTE:
provider = getRemoteVariant(resource);
break;
case ORIGIN:
provider = getOriginVariant(resource);
break;
default:
provider = null;
break;
}
return provider;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.ide.ui.logical.IStorageProviderAccessor#isInSync(org.eclipse.core.resources.IResource)
*/
public boolean isInSync(IResource resource) throws CoreException {
return subscriber.getDiff(resource) == null;
}
/**
* Finds and returns the "origin" variant of the given IResource as provided by the underlying subscriber.
*
* @param resource
* The resource for which we need a variant.
* @return The "origin" variant of the given IResource.
* @throws CoreException
*/
private IStorageProvider getOriginVariant(IResource resource) throws CoreException {
if (originTree != null) {
return wrapStorageProvider(originTree.getResourceVariant(resource));
}
final IDiff diff = subscriber.getDiff(resource);
return wrapStorageProvider(getOrigin(diff));
}
/**
* Finds and returns the "source" variant of the given IResource as provided by the underlying subscriber.
*
* @param resource
* The resource for which we need a variant.
* @return The "source" variant of the given IResource.
* @throws CoreException
*/
private IStorageProvider getSourceVariant(IResource resource) throws CoreException {
if (sourceTree != null) {
return wrapStorageProvider(sourceTree.getResourceVariant(resource));
}
final IDiff diff = subscriber.getDiff(resource);
return wrapStorageProvider(getSource(diff));
}
/**
* Finds and returns the "remote" variant of the given IResource as provided by the underlying subscriber.
*
* @param resource
* The resource for which we need a variant.
* @return The "remote" variant of the given IResource.
* @throws CoreException
*/
private IStorageProvider getRemoteVariant(IResource resource) throws CoreException {
if (remoteTree != null) {
return wrapStorageProvider(remoteTree.getResourceVariant(resource));
}
final IDiff diff = subscriber.getDiff(resource);
return wrapStorageProvider(getRemote(diff));
}
/**
* Wraps the given file revision as an {@link IStorageProvider}.
*
* @param revision
* The wrapped revision.
* @return The wrapping storage provider.
*/
private static IStorageProvider wrapStorageProvider(IFileRevision revision) {
if (revision != null) {
return new FileRevisionStorageProvider(revision);
}
return null;
}
/**
* Wraps the given resource variant as an {@link IStorageProvider}.
*
* @param variant
* The wrapped resource variant.
* @return The wrapping storage provider.
*/
private static IStorageProvider wrapStorageProvider(IResourceVariant variant) {
if (variant != null) {
return new ResourceVariantStorageProvider(variant);
}
return null;
}
/**
* Try and locate the revision that was used as a common ancestor by the given diff.
* <p>
* Will always return <code>null</code> in the case of two-way comparisons.
* </p>
*
* @param diff
* Diff for which we're searching the common ancestor.
* @return the revision that was used as a common ancestor by the given diff.
*/
private static IFileRevision getOrigin(IDiff diff) {
IFileRevision revision = null;
if (diff instanceof IThreeWayDiff) {
final IDiff localChange = ((IThreeWayDiff)diff).getLocalChange();
final IDiff remoteChange = ((IThreeWayDiff)diff).getRemoteChange();
if (localChange instanceof ResourceDiff) {
revision = ((ResourceDiff)localChange).getBeforeState();
} else if (remoteChange instanceof ResourceDiff) {
revision = ((ResourceDiff)remoteChange).getBeforeState();
}
}
return revision;
}
/**
* Try and locate the revision that was used as a source by the given diff.
* <p>
* Note that this could be either a local or remote resource. "source" can also be referred as the "left"
* side of a comparison.
* </p>
*
* @param diff
* Diff for which we're searching the source side.
* @return the revision that was used as the source side by the given diff.
*/
private static IFileRevision getSource(IDiff diff) {
IFileRevision revision = null;
if (diff instanceof IThreeWayDiff) {
final IDiff localChange = ((IThreeWayDiff)diff).getLocalChange();
if (localChange instanceof ResourceDiff) {
revision = ((ResourceDiff)localChange).getAfterState();
}
} else if (diff instanceof ResourceDiff) {
revision = ((ResourceDiff)diff).getAfterState();
}
return revision;
}
/**
* Try and locate the revision that was used as a remote by the given diff.
* <p>
* Note that despite its name, this could also be a local resource in case of local comparisons. "remote"
* can also be referred to as the "right" or "reference" side of a comparison.
* </p>
*
* @param diff
* Diff for which we're searching the remote side.
* @return the revision that was used as the remote side by the given diff.
*/
private static IFileRevision getRemote(IDiff diff) {
IFileRevision revision = null;
if (diff instanceof IThreeWayDiff) {
final IDiff remoteChange = ((IThreeWayDiff)diff).getRemoteChange();
if (remoteChange instanceof ResourceDiff) {
revision = ((ResourceDiff)remoteChange).getAfterState();
}
} else if (diff instanceof ResourceDiff) {
revision = ((ResourceDiff)diff).getBeforeState();
}
return revision;
}
private static IResourceVariantTree initTree(final Subscriber teamSubscriber, final String methodName) {
if (teamSubscriber instanceof ResourceVariantTreeSubscriber) {
final Object tree = AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
final Method method;
if (methodName.contains("Source")) { //$NON-NLS-1$
// Only available on git, only from 3.0
method = teamSubscriber.getClass().getDeclaredMethod(methodName);
} else {
method = ResourceVariantTreeSubscriber.class.getDeclaredMethod(methodName);
}
method.setAccessible(true);
return method.invoke(teamSubscriber);
} catch (SecurityException | NoSuchMethodException | IllegalArgumentException
| IllegalAccessException | InvocationTargetException e) {
// Swallow all this, don't use the variant tree.
}
return null;
}
});
if (tree instanceof IResourceVariantTree) {
return (IResourceVariantTree)tree;
}
}
return null;
}
/** {@inheritDoc} */
public IFile getFileBeforeRename(IFile sourceOrRemoteFile, DiffSide side) {
return renameDetector.getFileBeforeRename(sourceOrRemoteFile, side).orNull();
}
/** {@inheritDoc} */
public IFile getFileAfterRename(IFile originFile, DiffSide side) {
return renameDetector.getFileAfterRename(originFile, side).orNull();
}
}