blob: 7026ddb375dfb81c77f68dd01002c931c31aa6a7 [file] [log] [blame]
/*******************************************************************************
* 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
* Alexandra Buzila - bug 469105
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.logical;
import static org.eclipse.emf.compare.ide.ui.internal.util.PlatformElementUtil.adaptAs;
import static org.eclipse.emf.compare.ide.ui.internal.util.PlatformElementUtil.findFile;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import org.eclipse.compare.ISharedDocumentAdapter;
import org.eclipse.compare.IStreamContentAccessor;
import org.eclipse.compare.ITypedElement;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.compare.ide.internal.utils.StoragePathAdapter;
import org.eclipse.emf.compare.ide.utils.IStoragePathAdapterProvider;
import org.eclipse.emf.compare.ide.utils.IStoragePathProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.core.variants.CachedResourceVariant;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.ui.IEditorInput;
/**
* This implementation of IStorage simply wraps a stream content accessor.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
public class StreamAccessorStorage implements IStorage, IStoragePathAdapterProvider {
/** The wrapped accessor. */
private final IStreamContentAccessor accessor;
/** Full path of the underlying content. */
private final String fullPath;
/**
* Wraps the given accessor.
*
* @param accessor
* The accessor to wrap as an IStorage.
* @param fullPath
* Full path to the underlying storage.
*/
public StreamAccessorStorage(IStreamContentAccessor accessor, String fullPath) {
this.accessor = accessor;
this.fullPath = fullPath;
}
/**
* This is a short-hand for {@link #fromTypedElement(String, ITypedElement)}. This second one should be
* preferred in case the given element is remote and we need a proper path for it.
*
* @param element
* The typed element for which we need to create a wrapper.
* @return The wrapped typed element.
* @throws IllegalArgumentException
* If the given element does not implement {@link IStreamContentAccessor}.
*/
public static StreamAccessorStorage fromTypedElement(ITypedElement element)
throws IllegalArgumentException {
return fromTypedElement(null, element);
}
/**
* Creates a StreamAccessorStorage given the input typed element. Note that the given typed element -must-
* implement {@link IStreamContentAccessor} as well.
*
* @param storagePath
* The full path to this storage, can be <code>null</code>.
* @param element
* The typed element for which we need to create a wrapper.
* @return The wrapped typed element.
* @throws IllegalArgumentException
* If the given element does not implement {@link IStreamContentAccessor}.
*/
public static StreamAccessorStorage fromTypedElement(String storagePath, ITypedElement element)
throws IllegalArgumentException {
if (!(element instanceof IStreamContentAccessor)) {
throw new IllegalArgumentException();
}
final String fullPath;
if (storagePath != null) {
fullPath = storagePath;
} else {
fullPath = findPath(element);
}
return new StreamAccessorStorage((IStreamContentAccessor)element, fullPath);
}
/**
* This will try to find a path for the given typed element. If that element can be adapted, it delegates
* to {@link #findPath(IAdaptable)}.
*
* @param element
* The element for which we need a path.
* @return A path for the given element.
*/
private static String findPath(ITypedElement element) {
final String fullPath;
final IFile file = findFile(element);
if (file != null) {
fullPath = file.getFullPath().toString();
} else {
final IFileRevision revision = findFileRevision(element);
String tmp = null;
if (revision != null) {
final URI uri = revision.getURI();
if (uri != null) {
tmp = org.eclipse.emf.common.util.URI.decode(uri.toString());
} else if (revision instanceof IAdaptable) {
tmp = findPath((IAdaptable)revision);
}
}
if (tmp != null) {
fullPath = tmp;
} else {
// We can't do much here...
fullPath = element.getName();
}
}
return fullPath;
}
/**
* Get the commit id for the given element
*
* @param element
* The element for which we want the commit id
* @return the commit id
*/
public static IFileRevision findCommitId(ITypedElement element) {
return findFileRevision(element);
}
/**
* {@inheritDoc}
*
* @see IStoragePathAdapterProvider#createStoragePathAdapter()
*/
public Adapter createStoragePathAdapter(String path, boolean isLocal) {
IFileRevision findFileRevision = findFileRevision((ITypedElement)accessor);
IFile file = findFile((ITypedElement)accessor);
// The parameter isLocal is overwritten because more informations are available here to determine
// whether the file is local or remote
boolean local = file != null;
if (!isLocal && findFileRevision != null) {
return new StoragePathAdapter(path, local, findFileRevision.getContentIdentifier(),
findFileRevision.getAuthor());
} else {
return new StoragePathAdapter(path, local);
}
}
/**
* If the {@code adaptable} can be adapted to an {@link IResourceVariant}, we'll try to retrieve the
* resource's path. It will use {@link IStoragePathProvider}s, if any are registered. May return null
*
* @param adaptable
* The {@link IAdaptable} for which we need a path
* @return A path for the given element.
*/
private static String findPath(IAdaptable adaptable) {
String result = null;
final IResourceVariant variant = (IResourceVariant)adaptable.getAdapter(IResourceVariant.class);
if (variant != null) {
try {
final IStorage storage = variant.getStorage(new NullProgressMonitor());
if (storage != null) {
final Object adapter = Platform.getAdapterManager().loadAdapter(storage,
IStoragePathProvider.class.getName());
if (adapter instanceof IStoragePathProvider) {
final IPath fixedPath = ((IStoragePathProvider)adapter).computeFixedPath(storage);
result = fixedPath.toString();
} else if (storage instanceof IFile) {
result = storage.getFullPath().toString();
}
} else if (variant instanceof CachedResourceVariant) {
result = ((CachedResourceVariant)variant).getDisplayPath().toString();
}
} catch (TeamException e) {
// Swallow, this was a best effort...
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
// suppressing since overriding.
@SuppressWarnings({"rawtypes", "unchecked" })
public Object getAdapter(Class adapter) {
Object adapted = null;
if (adapter.isInstance(this)) {
adapted = this;
} else if (adapter == IStreamContentAccessor.class) {
adapted = accessor;
} else if (accessor instanceof ITypedElement) {
if (adapter == ITypedElement.class) {
adapted = accessor;
} else if (adapter.isAssignableFrom(IFile.class)) {
adapted = findFile((ITypedElement)accessor);
}
}
return adapted;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.resources.IStorage#getContents()
*/
public InputStream getContents() throws CoreException {
return accessor.getContents();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.resources.IStorage#getFullPath()
*/
public IPath getFullPath() {
return new Path(fullPath);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.resources.IStorage#getName()
*/
public String getName() {
if (accessor instanceof ITypedElement) {
return ((ITypedElement)accessor).getName();
}
return getFullPath().lastSegment();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.resources.IStorage#isReadOnly()
*/
public boolean isReadOnly() {
if (accessor instanceof ITypedElement) {
final IFile file = findFile((ITypedElement)accessor);
if (file != null) {
return file.isReadOnly();
}
}
final File file = getFullPath().toFile();
return !file.exists() || !file.canWrite();
}
/**
* Try and determine the file revision of the given element.
*
* @param element
* The element for which we need an {@link IFileRevision}.
* @return The file revision of the given element if we could find one, <code>null</code> otherwise.
*/
private static IFileRevision findFileRevision(ITypedElement element) {
if (element == null) {
return null;
}
// Can we adapt it directly?
IFileRevision revision = adaptAs(element, IFileRevision.class);
if (revision == null) {
// Quite the workaround... but CVS does not offer us any other way.
// These few lines of code is what make us depend on org.eclipse.ui... Can we find another way?
final ISharedDocumentAdapter documentAdapter = adaptAs(element, ISharedDocumentAdapter.class);
if (documentAdapter != null) {
final IEditorInput editorInput = documentAdapter.getDocumentKey(element);
if (editorInput != null) {
revision = adaptAs(editorInput, IFileRevision.class);
}
}
}
if (revision == null) {
// Couldn't do it the API way ...
// At the time of writing, this was the case with EGit
try {
final Method method = element.getClass().getMethod("getFileRevision"); //$NON-NLS-1$
final Object value = method.invoke(element);
if (value instanceof IFileRevision) {
revision = (IFileRevision)value;
}
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// Ignore exceptions
}
}
return revision;
}
}