| /******************************************************************************* |
| * Copyright (c) 2012, 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 |
| * Michael Borkowski - bug 467677 |
| * Philip Langer - optimize use of StorageTraversal.getStorages(), 508526 |
| *******************************************************************************/ |
| package org.eclipse.emf.compare.ide.utils; |
| |
| import com.google.common.base.Function; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IStorage; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.content.IContentType; |
| import org.eclipse.core.runtime.content.IContentTypeManager; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.common.util.WrappedException; |
| import org.eclipse.emf.compare.ide.EMFCompareIDEPlugin; |
| import org.eclipse.emf.compare.ide.internal.utils.StoragePathAdapter; |
| import org.eclipse.emf.compare.ide.internal.utils.URIStorage; |
| import org.eclipse.emf.compare.merge.ResourceChangeAdapter; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| |
| /** |
| * This class will be used to provide various utilities aimed at IResource manipulation. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| */ |
| public final class ResourceUtil { |
| /** Content types of the files to consider as potential models. */ |
| private static final String[] MODEL_CONTENT_TYPES = new String[] {"org.eclipse.emf.compare.content.type", //$NON-NLS-1$ |
| "org.eclipse.emf.ecore", //$NON-NLS-1$ |
| "org.eclipse.emf.ecore.xmi", }; //$NON-NLS-1$ |
| |
| /** |
| * This can be used in order to convert an Iterable of IStorages to an Iterable over the storage's URIs. |
| */ |
| private static final Function<IStorage, URI> AS_URI = new Function<IStorage, URI>() { |
| public URI apply(IStorage input) { |
| if (input != null) { |
| return createURIFor(input); |
| } |
| return null; |
| } |
| }; |
| |
| /** |
| * This does not need to be instantiated. |
| */ |
| private ResourceUtil() { |
| // hides default constructor |
| } |
| |
| /** |
| * Provides a {@link Function} that converts an {@link IStorage} into a {@link URI}. |
| * |
| * @return A {@link Function} that converts an {@link IStorage} into a {@link URI}. This function |
| * transforms a {@code null} storage into a {@code null} URI. |
| * @since 3.2 |
| */ |
| public static Function<IStorage, URI> asURI() { |
| return AS_URI; |
| } |
| |
| /** |
| * This will try and load the given file as an EMF model, and return the corresponding {@link Resource} if |
| * at all possible. |
| * |
| * @param storage |
| * The file we need to try and load as a model. |
| * @param resourceSet |
| * The resource set in which to load this Resource. |
| * @param options |
| * The options to pass to {@link Resource#load(java.util.Map)}. |
| * @return The loaded EMF Resource if {@code file} was a model, {@code null} otherwise. |
| */ |
| public static Resource loadResource(IStorage storage, ResourceSet resourceSet, Map<?, ?> options) { |
| final URI uri = createURIFor(storage); |
| try { |
| Resource resource = resourceSet.createResource(uri); |
| setAssociatedStorage(resource, storage); |
| try (InputStream stream = storage.getContents()) { |
| resource.load(stream, options); |
| } |
| return resource; |
| } catch (IOException | CoreException | WrappedException e) { |
| // return null |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the storage {@link #setAssociatedStorage(Resource, IStorage) associated} with the resource. |
| * |
| * @param resource |
| * the resource. |
| * @return the associated storage or <code>null</code> if there isn't one. |
| * @see #setAssociatedStorage(Resource, IStorage) |
| */ |
| public static IStorage getAssociatedStorage(Resource resource) { |
| StorageProvider storageProvider = (StorageProvider)EcoreUtil.getExistingAdapter(resource, |
| StorageProvider.class); |
| if (storageProvider != null) { |
| return storageProvider.getStorage(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Associates the storage with the resource such that {@link #getAssociatedStorage(Resource)} will return |
| * this storage for the resource. |
| * |
| * @param resource |
| * the resource. |
| * @param storage |
| * the associated storage. |
| */ |
| public static void setAssociatedStorage(Resource resource, IStorage storage) { |
| final String fullPath = storage.getFullPath().toString(); |
| boolean isLocal = storage instanceof IFile; |
| EList<Adapter> eAdapters = resource.eAdapters(); |
| if (storage instanceof IStoragePathAdapterProvider) { |
| eAdapters.add(((IStoragePathAdapterProvider)storage).createStoragePathAdapter(fullPath, isLocal)); |
| } else { |
| eAdapters.add(new StoragePathAdapter(fullPath, isLocal)); |
| } |
| eAdapters.add(new StorageProvider(storage)); |
| } |
| |
| /** |
| * Used by {@link ResourceUtil#getAssociatedStorage(Resource)} and |
| * {@link ResourceUtil#setAssociatedStorage(Resource, IStorage)} to map a resource to its associated |
| * storage. |
| */ |
| private static final class StorageProvider extends AdapterImpl { |
| /** |
| * The storage. |
| */ |
| private final IStorage storage; |
| |
| /** |
| * Creates an instance for the storage. |
| * |
| * @param storage |
| * the storage. |
| */ |
| StorageProvider(IStorage storage) { |
| this.storage = storage; |
| } |
| |
| @Override |
| public boolean isAdapterForType(Object type) { |
| return type == StorageProvider.class; |
| } |
| |
| public IStorage getStorage() { |
| return storage; |
| } |
| } |
| |
| /** |
| * Checks whether the two given storages point to binary identical data. |
| * |
| * @param left |
| * First of the two storages which content we are testing. |
| * @param right |
| * Second of the two storages which content we are testing. |
| * @return <code>true</code> if {@code left} and {@code right} are binary identical. |
| */ |
| public static boolean binaryIdentical(IStorage left, IStorage right) { |
| final int maxBufferSize = 8192; |
| final byte[] buffer = new byte[maxBufferSize]; |
| try (BufferedInputStream leftStream = new BufferedInputStream(left.getContents(), maxBufferSize); |
| BufferedInputStream rightStream = new BufferedInputStream(right.getContents(), |
| maxBufferSize);) { |
| int readLeft; |
| boolean identical = true; |
| do { |
| readLeft = leftStream.read(buffer, 0, buffer.length); |
| if (readLeft == -1) { |
| // check if there is anything left to read on right |
| identical = rightStream.read() == -1; |
| break; |
| } |
| if (!verifyNextBytes(rightStream, buffer, 0, readLeft)) { |
| identical = false; |
| break; |
| } |
| } while (readLeft > 0); |
| |
| return identical; |
| } catch (CoreException | IOException e) { |
| logError(e); |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether the three given storages point to binary identical data. This could be done by calling |
| * {@link #binaryIdentical(IStorage, IStorage)} twice, though this implementation allows us to shortcut |
| * whenever one byte differs... and will read one less file from its input stream. |
| * |
| * @param left |
| * First of the three storages which content we are testing. |
| * @param right |
| * Second of the three storages which content we are testing. |
| * @param origin |
| * Third of the three storages which content we are testing. |
| * @return <code>true</code> if {@code left}, {@code right} and {@code origin} are binary identical. |
| */ |
| public static boolean binaryIdentical(IStorage left, IStorage right, IStorage origin) { |
| final int maxBufferSize = 8192; |
| final byte[] buffer = new byte[maxBufferSize]; |
| try (InputStream leftStream = new BufferedInputStream(left.getContents(), maxBufferSize); |
| InputStream rightStream = new BufferedInputStream(right.getContents(), maxBufferSize); |
| InputStream originStream = new BufferedInputStream(origin.getContents(), maxBufferSize);) { |
| int readLeft; |
| boolean identical = true; |
| do { |
| readLeft = leftStream.read(buffer, 0, buffer.length); |
| if (readLeft == -1) { |
| // check if there is anything left to read on right or origin |
| identical = rightStream.read() == -1 && originStream.read() == -1; |
| break; |
| } |
| if (!verifyNextBytes(rightStream, buffer, 0, readLeft) |
| || !verifyNextBytes(originStream, buffer, 0, readLeft)) { |
| identical = false; |
| break; |
| } |
| } while (readLeft > 0); |
| |
| return identical; |
| } catch (CoreException | IOException e) { |
| logError(e); |
| } |
| return false; |
| } |
| |
| /** |
| * Verifies whether the next <code>length</code> bytes coming from <code>stream</code> equal |
| * <code>bytes</code> at offset <code>offset</code>. |
| * |
| * @param stream |
| * The stream to read bytes from |
| * @param bytes |
| * The array of bytes to compare to (from offset of <code>offset</code> bytes) |
| * @param offset |
| * The offset in the byte array to use |
| * @param length |
| * The amount of bytes to verify |
| * @return <code>true</code> if there are at least <code>length</code> bytes in the stream and they equal |
| * the provided bytes |
| * @throws IOException |
| * If an I/O problem occurs |
| */ |
| private static boolean verifyNextBytes(InputStream stream, byte[] bytes, int offset, int length) |
| throws IOException { |
| int done = 0; |
| byte[] buffer = new byte[offset + length]; |
| while (done < length) { |
| int read = stream.read(buffer, offset + done, length - done); |
| if (read == -1 || !equalArrays(offset + done, read, bytes, buffer)) { |
| return false; |
| } |
| done += read; |
| } |
| return true; |
| } |
| |
| /** |
| * Create the URI with which we'll load the given IFile as an EMF resource. |
| * |
| * @param file |
| * The file for which we need an EMF URI. |
| * @return The created URI. |
| * @since 3.1 |
| */ |
| public static URI createURIFor(IFile file) { |
| // whether it exists or not (no longer), use platform:/resource |
| return URI.createPlatformResourceURI(file.getFullPath().toString(), true); |
| } |
| |
| /** |
| * Create the URI with which we'll load the given IStorage as an EMF resource. |
| * |
| * @param storage |
| * The storage for which we need an EMF URI. |
| * @return The created URI. |
| */ |
| public static URI createURIFor(IStorage storage) { |
| URI shortcut = null; |
| if (storage instanceof IFile) { |
| shortcut = createURIFor((IFile)storage); |
| } else if (storage instanceof URIStorage) { |
| shortcut = ((URIStorage)storage).getURI(); |
| } |
| if (shortcut != null) { |
| return shortcut; |
| } |
| |
| String path = getFixedPath(storage).toString(); |
| |
| // Given the two paths |
| // "g:/ws/project/test.ecore" |
| // "/project/test.ecore" |
| // We have no way to determine which is absolute and which should be platform:/resource |
| URI uri; |
| if (path.startsWith("platform:/plugin/")) { //$NON-NLS-1$ |
| uri = URI.createURI(path); |
| } else if (path.startsWith("file:/")) { //$NON-NLS-1$ |
| uri = URI.createURI(path); |
| } else if (hasStoragePathProvider(storage)) { |
| uri = URI.createPlatformResourceURI(path, true); |
| } else { |
| uri = URI.createURI(path, true); |
| } |
| |
| final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| final IPath iPath = new Path(path); |
| if (root != null && iPath.segmentCount() >= 2 && root.getFile(iPath).exists()) { |
| uri = URI.createPlatformResourceURI(path, true); |
| } |
| |
| return uri; |
| } |
| |
| /** |
| * Tries and retrieve the {@link IResource} associated with the given {@link URI}. This returns a file |
| * handle, which might point to a non-existing IResource. |
| * |
| * @param uri |
| * the URI for which we want the {@link IResource}. |
| * @return the {@link IResource} if found, null otherwise. |
| * @since 3.2 |
| */ |
| public static IResource getResourceFromURI(final URI uri) { |
| final IResource targetFile; |
| if (uri.isPlatform()) { |
| IPath platformString = new Path(uri.trimFragment().toPlatformString(true)); |
| targetFile = ResourcesPlugin.getWorkspace().getRoot().getFile(platformString); |
| } else { |
| /* |
| * FIXME Deresolve the URI against the workspace root, if it cannot be done, delegate to |
| * super.createInputStream() |
| */ |
| targetFile = ResourcesPlugin.getWorkspace().getRoot() |
| .getFile(new Path(uri.trimFragment().toString())); |
| } |
| return targetFile; |
| } |
| |
| /** |
| * Returns a path for this storage after fixing from an {@link IStoragePathProvider} if one exists. |
| * |
| * @param storage |
| * The storage for which we need a fixed full path. |
| * @return The full path to this storage, fixed if need be. |
| * @since 3.2 |
| */ |
| public static IPath getFixedPath(IStorage storage) { |
| final Object adapter = Platform.getAdapterManager().loadAdapter(storage, |
| IStoragePathProvider.class.getName()); |
| if (adapter instanceof IStoragePathProvider) { |
| return ((IStoragePathProvider)adapter).computeFixedPath(storage); |
| } |
| return storage.getFullPath(); |
| } |
| |
| /** |
| * Returns an absolute path for this storage if one exists. If the storage can be adapted to |
| * {@link IStoragePathProvider2}, it will call computeAbsolutePath from this interface. If the storage is |
| * a File, a {@link Path} will be created and then getAbsolutePath will be called. In other cases, the |
| * method will return the full path of the storage. |
| * |
| * @param storage |
| * The storage for which we need an absolute path. |
| * @return The absolute path to this storage. |
| * @since 3.3 |
| */ |
| public static IPath getAbsolutePath(IStorage storage) { |
| final IPath absolutePath; |
| final Object adapter = Platform.getAdapterManager().loadAdapter(storage, |
| IStoragePathProvider.class.getName()); |
| if (adapter instanceof IStoragePathProvider2) { |
| absolutePath = ((IStoragePathProvider2)adapter).computeAbsolutePath(storage); |
| } else if (storage instanceof File) { |
| absolutePath = new Path(((File)storage).getAbsolutePath()); |
| } else { |
| absolutePath = storage.getFullPath(); |
| } |
| return absolutePath; |
| } |
| |
| /** |
| * Checks if an {@link IStoragePathProvider} exists for the given storage. |
| * |
| * @param storage |
| * the given storage. |
| * @return true if exists, false otherwise. |
| */ |
| private static boolean hasStoragePathProvider(IStorage storage) { |
| final boolean hasProvider; |
| final Object adapter = Platform.getAdapterManager().loadAdapter(storage, |
| IStoragePathProvider.class.getName()); |
| if (adapter instanceof IStoragePathProvider) { |
| hasProvider = true; |
| } else { |
| hasProvider = false; |
| } |
| return hasProvider; |
| } |
| |
| /** |
| * This can be called to save all resources contained by the resource set. This will not try and save |
| * resources that do not support output. |
| * |
| * @param resourceSet |
| * The resource set to save. |
| * @param options |
| * The options we are to pass on to {@link Resource#save(Map)}. |
| */ |
| public static void saveAllResources(ResourceSet resourceSet, Map<?, ?> options) { |
| List<Resource> resources = Lists.newArrayList(resourceSet.getResources()); |
| for (Resource resource : resources) { |
| saveResource(resource, options); |
| } |
| } |
| |
| /** |
| * This can be called to save all resources contained by the resource set. This will not try and save |
| * resources that do not support output. |
| * |
| * @param resourceSet |
| * The resource set to save. |
| * @param options |
| * The options we are to pass on to {@link Resource#save(Map)}. |
| * @param leftTraversal |
| * The traversal corresponding to the left side. |
| * @param rightTraversal |
| * The traversal corresponding to the right side. |
| * @param originTraversal |
| * The traversal corresponding to the common ancestor of both other side. Can be |
| * <code>null</code>. |
| * @since 3.3 |
| */ |
| public static void saveAllResources(ResourceSet resourceSet, Map<?, ?> options, |
| StorageTraversal leftTraversal, StorageTraversal rightTraversal, |
| StorageTraversal originTraversal) { |
| |
| // filter out the resources that don't support output |
| List<Resource> resources = Lists |
| .newArrayList(Iterables.filter(resourceSet.getResources(), new Predicate<Resource>() { |
| public boolean apply(Resource input) { |
| return supportsOutput(input); |
| } |
| })); |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| |
| final Set<Resource> wsResources = Sets.newHashSet(); |
| final Set<Resource> nonWsResources = Sets.newHashSet(); |
| |
| for (Resource resource : resources) { |
| String projectName = new Path(resource.getURI().toPlatformString(true)).segment(0); |
| IProject project = root.getProject(projectName); |
| if (project != null && project.isAccessible()) { |
| wsResources.add(resource); |
| } else { |
| nonWsResources.add(resource); |
| } |
| } |
| |
| // Save workspace resources first |
| for (Resource resource : wsResources) { |
| saveResource(resource, options); |
| } |
| |
| // Delete workspace resources from ResourceSet |
| // Is it really useful ? |
| resources.removeAll(wsResources); |
| |
| // Change "platform:/resource/relativePath" URIs of non-workspace resources into "file:/absolutePath" |
| // URIs |
| final Set<? extends IStorage> leftStorages = leftTraversal.getStorages(); |
| final Set<? extends IStorage> rightStorages = rightTraversal.getStorages(); |
| final Set<? extends IStorage> originStorages; |
| if (originTraversal != null) { |
| originStorages = originTraversal.getStorages(); |
| } else { |
| originStorages = null; |
| } |
| for (Resource resource : nonWsResources) { |
| String absolutePath = getAbsolutePath(resource, leftStorages, rightStorages, originStorages); |
| URI fileURI = URI.createFileURI(absolutePath); |
| resource.setURI(fileURI); |
| } |
| |
| // Save non-workspace resources |
| for (Resource resource : nonWsResources) { |
| saveResource(resource, options); |
| } |
| } |
| |
| /** |
| * Get the absolute path of the given resource. |
| * |
| * @param resource |
| * The resource for which we seek an absolute path. |
| * @param leftStorages |
| * The storages of the left traversal. |
| * @param rightStorages |
| * The storages of the right traversal. |
| * @param originStorages |
| * The storages of the common ancestor traversal. Can be <code>null</code>. |
| * @return the absolute path of the given resource if found, null otherwise. |
| */ |
| private static String getAbsolutePath(Resource resource, Set<? extends IStorage> leftStorages, |
| Set<? extends IStorage> rightStorages, Set<? extends IStorage> originStorages) { |
| URI uri = resource.getURI(); |
| String absolutePath = getAbsolutePath(uri, leftStorages); |
| if (absolutePath == null) { |
| absolutePath = getAbsolutePath(uri, rightStorages); |
| } |
| if (absolutePath == null && originStorages != null) { |
| absolutePath = getAbsolutePath(uri, originStorages); |
| } |
| return absolutePath; |
| } |
| |
| /** |
| * Get the absolute path of the given URI that corresponds to one of the given storages. |
| * |
| * @param uri |
| * The URI for which we seek an absolute path. |
| * @param storages |
| * The given storages. |
| * @return the absolute path of the given URI if found, null otherwise. |
| */ |
| private static String getAbsolutePath(URI uri, Set<? extends IStorage> storages) { |
| for (IStorage storage : storages) { |
| IPath storagePath = getFixedPath(storage); |
| if (storagePath.makeAbsolute().toString().equals(uri.toPlatformString(true))) { |
| IPath absolutePath = getAbsolutePath(storage); |
| if (absolutePath != null) { |
| return absolutePath.toString(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * This can be called to save the given resource. This will not try and save a resource that does not |
| * support output. |
| * |
| * @param resource |
| * The resource to save. |
| * @param options |
| * The options we are to pass on to {@link Resource#save(Map)}. |
| * @since 3.1 |
| */ |
| public static void saveResource(Resource resource, Map<?, ?> options) { |
| if (supportsOutput(resource)) { |
| try { |
| if (mustDelete(resource)) { |
| deleteResource(resource); |
| } else { |
| resource.save(options); |
| } |
| } catch (IOException e) { |
| logError(e); |
| } |
| } |
| } |
| |
| /** |
| * Check if the given resource must be deleted. |
| * |
| * @param resource |
| * The resource to delete, must not be null. |
| * @return true if the given resource must be deleted, false otherwise. |
| * @since 3.4 |
| */ |
| protected static boolean mustDelete(Resource resource) { |
| Adapter adapter = EcoreUtil.getAdapter(resource.eAdapters(), ResourceChangeAdapter.class); |
| if (adapter instanceof ResourceChangeAdapter) { |
| return ((ResourceChangeAdapter)adapter).mustDelete(resource); |
| } |
| return false; |
| } |
| |
| /** |
| * Delete the given resource. |
| * |
| * @param resource |
| * The resource to delete, must not be null. |
| * @since 3.4 |
| */ |
| protected static void deleteResource(final Resource resource) { |
| try { |
| resource.delete(Collections.emptyMap()); |
| } catch (IOException e) { |
| logError(e); |
| } |
| } |
| |
| /** |
| * This will return <code>true</code> if the given <em>contentTypeId</em> represents a content-type |
| * contained in the given array. |
| * |
| * @param contentTypeId |
| * Fully qualified identifier of the content type we seek. |
| * @param contentTypes |
| * The array of content-types to compare against. |
| * @return <code>true</code> if the given array contains a content-type with this id. |
| * @since 3.1 |
| */ |
| public static boolean hasContentType(String contentTypeId, List<IContentType> contentTypes) { |
| IContentTypeManager ctManager = Platform.getContentTypeManager(); |
| IContentType expected = ctManager.getContentType(contentTypeId); |
| if (expected == null) { |
| return false; |
| } |
| |
| boolean hasContentType = false; |
| for (int i = 0; i < contentTypes.size() && !hasContentType; i++) { |
| if (contentTypes.get(i).isKindOf(expected)) { |
| hasContentType = true; |
| } |
| } |
| return hasContentType; |
| } |
| |
| /** |
| * Checks whether the given file has one of the content types described in {@link #MODEL_CONTENT_TYPES}. |
| * |
| * @param file |
| * The file which contents are to be checked. |
| * @return <code>true</code> if this file has one of the "model" content types. |
| * @since 3.1 |
| */ |
| public static boolean hasModelType(IFile file) { |
| boolean isModel = false; |
| // Try a first pass without the file contents, since some content type parsers can be very sluggish |
| // (EMF uses a sax parser to describe its content) |
| final IContentTypeManager ctManager = Platform.getContentTypeManager(); |
| final List<IContentType> fileNameTypes = Lists |
| .newArrayList(ctManager.findContentTypesFor(file.getName())); |
| for (int i = 0; i < MODEL_CONTENT_TYPES.length && !isModel; i++) { |
| isModel = hasContentType(MODEL_CONTENT_TYPES[i], fileNameTypes); |
| } |
| if (isModel) { |
| return true; |
| } |
| |
| // Fall back to the slower test |
| final List<IContentType> contentTypes = Lists.newArrayList(getContentTypes(file)); |
| contentTypes.removeAll(fileNameTypes); |
| for (int i = 0; i < MODEL_CONTENT_TYPES.length && !isModel; i++) { |
| isModel = hasContentType(MODEL_CONTENT_TYPES[i], contentTypes); |
| } |
| return isModel; |
| } |
| |
| /** |
| * Returns the whole list of content types of the given IFile, or an empty array if none. |
| * |
| * @param file |
| * The file we need the content types of. |
| * @return All content types associated with the given file, an empty array if none. |
| * @since 3.1 |
| */ |
| public static IContentType[] getContentTypes(IFile file) { |
| final IContentTypeManager ctManager = Platform.getContentTypeManager(); |
| IContentType[] contentTypes = new IContentType[0]; |
| try (InputStream resourceContent = file.getContents()) { |
| contentTypes = ctManager.findContentTypesFor(resourceContent, file.getName()); |
| } catch (CoreException | IOException e) { |
| ctManager.findContentTypesFor(file.getName()); |
| } |
| return contentTypes; |
| } |
| |
| /** |
| * Disable saving for resources that cannot support it. |
| * |
| * @param resource |
| * The resource we are to check. |
| * @return <code>true</code> if we can save this <code>resource</code>, <code>false</code> otherwise. |
| */ |
| private static boolean supportsOutput(Resource resource) { |
| final URI uri = resource.getURI(); |
| if (uri.isPlatformResource() || uri.isRelative() || uri.isFile()) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether the two arrays contain identical data in the {@code [0:length]} range. |
| * |
| * @param offset |
| * The offset at which to start comparing |
| * @param length |
| * Length of the data range to check within the arrays. |
| * @param array1 |
| * First of the two arrays which content we need to check. |
| * @param array2 |
| * Second of the two arrays which content we need to check. |
| * @return <code>true</code> if the two given arrays contain identical data in the |
| * {@code [offset..offset+length]} range. |
| */ |
| private static boolean equalArrays(int offset, int length, byte[] array1, byte[] array2) { |
| boolean result = true; |
| if (array1 == array2) { |
| result = true; |
| } else if (array1 == null || array2 == null) { |
| result = false; |
| } else { |
| for (int i = offset; result && i < offset + length; i++) { |
| result = array1[i] == array2[i]; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Logs the given exception as an error. |
| * |
| * @param e |
| * The exception we need to log. |
| */ |
| private static void logError(Exception e) { |
| final IStatus status = new Status(IStatus.ERROR, EMFCompareIDEPlugin.PLUGIN_ID, e.getMessage(), e); |
| EMFCompareIDEPlugin.getDefault().getLog().log(status); |
| } |
| } |