/******************************************************************************* * Copyright (c) 2009, 2010 Fraunhofer IWU 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: * Fraunhofer IWU - initial API and implementation *******************************************************************************/ package net.enilink.komma.model.eclipse; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.content.IContentDescription; import net.enilink.komma.model.IModel; import net.enilink.komma.model.IURIConverter; import net.enilink.komma.model.ModelPlugin; import net.enilink.komma.model.base.URIHandler; import net.enilink.komma.core.URI; import net.enilink.komma.core.URIs; public class PlatformResourceURIHandler extends URIHandler { /** * The platform resource map. * * @see #getPlatformResourceMap */ private static Map<String, URI> platformResourceMap; /** * An output stream that transfers its contents to an {@link IFile} upon * closing. */ public static class PlatformResourceOutputStream extends ByteArrayOutputStream { protected IFile file; protected boolean force; protected boolean keepHistory; protected IProgressMonitor progressMonitor; protected boolean previouslyFlushed; public PlatformResourceOutputStream(IFile file, boolean force, boolean keepHistory, IProgressMonitor progressMonitor) { this.file = file; this.force = force; this.keepHistory = keepHistory; this.progressMonitor = progressMonitor; } protected void createContainer(IContainer container) throws IOException { if (!container.exists()) { if (container.getType() == IResource.FOLDER) { createContainer(container.getParent()); try { ((IFolder) container).create(force, keepHistory, progressMonitor); } catch (CoreException exception) { throw new IModel.IOWrappedException(exception); } } } } @Override public void close() throws IOException { flush(); super.close(); } @Override public void flush() throws IOException { super.flush(); if (previouslyFlushed) { if (count == 0) { return; } } else { createContainer(file.getParent()); } byte[] contents = toByteArray(); InputStream inputStream = new ByteArrayInputStream(contents, 0, contents.length); try { if (previouslyFlushed) { file.appendContents(inputStream, force, false, progressMonitor); } else if (!file.exists()) { file.create(inputStream, false, null); previouslyFlushed = true; } else { if (!file.isSynchronized(IResource.DEPTH_ONE)) { file.refreshLocal(IResource.DEPTH_ONE, progressMonitor); } file.setContents(inputStream, force, keepHistory, progressMonitor); previouslyFlushed = true; } reset(); } catch (CoreException exception) { throw new IModel.IOWrappedException(exception); } } } /** * Isolated Eclipse workbench utilities. */ public static class WorkbenchHelper { /** * Creates an output stream for the given {@link IFile} path. * <p> * This implementation uses a * {@link PlatformResourceURIHandler.PlatformResourceOutputStream}. * </p> * * @return an open output stream. * @exception IOException * if there is a problem obtaining an open output stream. * @see IWorkspaceRoot#getFile(org.eclipse.core.runtime.IPath) * @see PlatformResourceURIHandler.PlatformResourceOutputStream * @see IFile#setContents(InputStream, boolean, boolean, * IProgressMonitor) */ public static OutputStream createPlatformResourceOutputStream( String platformResourcePath, Map<?, ?> options) throws IOException { IFile file = getWorkspaceRoot().getFile( new Path(platformResourcePath)); @SuppressWarnings("unchecked") final Map<Object, Object> response = options == null ? null : (Map<Object, Object>) options .get(IURIConverter.OPTION_RESPONSE); return new PlatformResourceOutputStream(file, false, true, null) { @Override public void close() throws IOException { try { super.close(); } finally { if (response != null) { response.put( IURIConverter.RESPONSE_TIME_STAMP_PROPERTY, file.getLocalTimeStamp()); } } } }; } /** * Creates an input stream for the given {@link IFile} path. * <p> * This implementation uses {@link IFile#getContents() * IFile.getContents}. * </p> * * @return an open input stream. * @see IWorkspaceRoot#getFile(org.eclipse.core.runtime.IPath) * @see IFile#getContents() * @exception IOException * if there is a problem obtaining an open input stream. */ public static InputStream createPlatformResourceInputStream( String platformResourcePath, Map<?, ?> options) throws IOException { IFile file = getWorkspaceRoot().getFile( new Path(platformResourcePath)); try { if (!file.isSynchronized(IResource.DEPTH_ONE)) { file.refreshLocal(IResource.DEPTH_ONE, null); } InputStream result = file.getContents(); if (options != null) { @SuppressWarnings("unchecked") Map<Object, Object> response = (Map<Object, Object>) options .get(IURIConverter.OPTION_RESPONSE); if (response != null) { response.put( IURIConverter.RESPONSE_TIME_STAMP_PROPERTY, file.getLocalTimeStamp()); } } return result; } catch (CoreException exception) { throw new IModel.IOWrappedException(exception); } } public static void delete(String platformResourcePath, Map<?, ?> options) throws IOException { IFile file = getWorkspaceRoot().getFile( new Path(platformResourcePath)); try { file.delete(true, null); } catch (CoreException exception) { throw new IModel.IOWrappedException(exception); } } public static boolean exists(String platformResourcePath, Map<?, ?> options) { IResource resource = getWorkspaceRoot().findMember( new Path(platformResourcePath)); return resource != null; } public static Map<String, ?> attributes(String platformResourcePath, Map<?, ?> options) { IResource resource = getWorkspaceRoot().findMember( new Path(platformResourcePath)); Map<String, Object> result = new HashMap<String, Object>(); if (resource != null) { @SuppressWarnings("unchecked") Set<String> requestedAttributes = options == null ? null : (Set<String>) options .get(IURIConverter.OPTION_REQUESTED_ATTRIBUTES); if (requestedAttributes == null || requestedAttributes .contains(IURIConverter.ATTRIBUTE_TIME_STAMP)) { result.put(IURIConverter.ATTRIBUTE_TIME_STAMP, resource .getLocalTimeStamp()); } ResourceAttributes resourceAttributes = null; if (requestedAttributes == null || requestedAttributes .contains(IURIConverter.ATTRIBUTE_READ_ONLY)) { resourceAttributes = resource.getResourceAttributes(); result.put(IURIConverter.ATTRIBUTE_READ_ONLY, resourceAttributes.isReadOnly()); } if (requestedAttributes == null || requestedAttributes .contains(IURIConverter.ATTRIBUTE_ARCHIVE)) { if (resourceAttributes == null) { resourceAttributes = resource.getResourceAttributes(); } result.put(IURIConverter.ATTRIBUTE_ARCHIVE, resourceAttributes.isArchive()); } if (requestedAttributes == null || requestedAttributes .contains(IURIConverter.ATTRIBUTE_EXECUTABLE)) { if (resourceAttributes == null) { resourceAttributes = resource.getResourceAttributes(); } result.put(IURIConverter.ATTRIBUTE_EXECUTABLE, resourceAttributes.isExecutable()); } if (requestedAttributes == null || requestedAttributes .contains(IURIConverter.ATTRIBUTE_HIDDEN)) { if (resourceAttributes == null) { resourceAttributes = resource.getResourceAttributes(); } result.put(IURIConverter.ATTRIBUTE_HIDDEN, resourceAttributes.isHidden()); } if (requestedAttributes == null || requestedAttributes .contains(IURIConverter.ATTRIBUTE_DIRECTORY)) { if (resourceAttributes == null) { resourceAttributes = resource.getResourceAttributes(); } result.put(IURIConverter.ATTRIBUTE_DIRECTORY, resource instanceof IContainer); } } return result; } public static void updateAttributes(String platformResourcePath, Map<String, ?> attributes, Map<?, ?> options) throws IOException { IResource resource = getWorkspaceRoot().findMember( new Path(platformResourcePath)); if (resource == null) { throw new FileNotFoundException("The resource " + platformResourcePath + " does not exist"); } else { try { Long timeStamp = (Long) attributes .get(IURIConverter.ATTRIBUTE_TIME_STAMP); if (timeStamp != null) { resource.setLocalTimeStamp(timeStamp); } ResourceAttributes resourceAttributes = null; Boolean readOnly = (Boolean) attributes .get(IURIConverter.ATTRIBUTE_READ_ONLY); if (readOnly != null) { resourceAttributes = resource.getResourceAttributes(); resourceAttributes.setReadOnly(readOnly); } Boolean archive = (Boolean) attributes .get(IURIConverter.ATTRIBUTE_ARCHIVE); if (archive != null) { if (resourceAttributes == null) { resourceAttributes = resource .getResourceAttributes(); } resourceAttributes.setArchive(archive); } Boolean executable = (Boolean) attributes .get(IURIConverter.ATTRIBUTE_EXECUTABLE); if (executable != null) { if (resourceAttributes == null) { resourceAttributes = resource .getResourceAttributes(); } resourceAttributes.setExecutable(executable); } Boolean hidden = (Boolean) attributes .get(IURIConverter.ATTRIBUTE_HIDDEN); if (hidden != null) { if (resourceAttributes == null) { resourceAttributes = resource .getResourceAttributes(); } resourceAttributes.setHidden(hidden); } if (resourceAttributes != null) { resource.setResourceAttributes(resourceAttributes); } } catch (CoreException exception) { throw new IModel.IOWrappedException(exception); } } } public static IContentDescription getContentDescription( String platformResourcePath, Map<?, ?> options) throws IOException { IFile file = getWorkspaceRoot().getFile( new Path(platformResourcePath)); try { return file.getContentDescription(); } catch (CoreException exception) { throw new IModel.IOWrappedException(exception); } } public static IWorkspaceRoot getWorkspaceRoot() { return ModelPlugin.getWorkspaceRoot(); } } @Override public boolean canHandle(URI uri) { return uri.isPlatformResource(); } /** * Creates an output stream for the platform resource path and returns it. * <p> * This implementation does one of two things, depending on the runtime * environment. If there is an Eclipse workspace, it delegates to * {@link WorkbenchHelper#createPlatformResourceOutputStream * WorkbenchHelper.createPlatformResourceOutputStream}, which gives the * expected Eclipse behaviour. Otherwise, the * {@link EcorePlugin#resolvePlatformResourcePath resolved} URI is delegated * to {@link #createOutputStream createOutputStream} for recursive * processing. * * @return an open output stream. * @exception IOException * if there is a problem obtaining an open output stream or a * valid interpretation of the path. * @see EcorePlugin#resolvePlatformResourcePath(String) */ @Override public OutputStream createOutputStream(URI uri, Map<?, ?> options) throws IOException { String platformResourcePath = uri.toPlatformString(true); if (WorkbenchHelper.getWorkspaceRoot() != null) { return WorkbenchHelper.createPlatformResourceOutputStream( platformResourcePath, options); } else { URI resolvedLocation = resolvePlatformResourcePath(platformResourcePath); if (resolvedLocation != null) { return ((IURIConverter) options .get(IURIConverter.OPTION_URI_CONVERTER)) .createOutputStream(resolvedLocation, options); } throw new IOException("The path '" + platformResourcePath + "' is unmapped"); } } /** * Creates an input stream for the platform resource path and returns it. * <p> * This implementation does one of two things, depending on the runtime * environment. If there is an Eclipse workspace, it delegates to * {@link WorkbenchHelper#createPlatformResourceInputStream * WorkbenchHelper.createPlatformResourceInputStream}, which gives the * expected Eclipse behaviour. Otherwise, the * {@link EcorePlugin#resolvePlatformResourcePath resolved} URI is delegated * to {@link #createInputStream createInputStream} for recursive processing. * * @return an open input stream. * @exception IOException * if there is a problem obtaining an open input stream or a * valid interpretation of the path. * @see EcorePlugin#resolvePlatformResourcePath(String) */ @Override public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException { String platformResourcePath = uri.toPlatformString(true); if (WorkbenchHelper.getWorkspaceRoot() != null) { return WorkbenchHelper.createPlatformResourceInputStream( platformResourcePath, options); } else { URI resolvedLocation = resolvePlatformResourcePath(platformResourcePath); if (resolvedLocation != null) { return getURIConverter(options).createInputStream( resolvedLocation, options); } throw new IOException("The path '" + platformResourcePath + "' is unmapped"); } } @Override public boolean exists(URI uri, Map<?, ?> options) { String platformResourcePath = uri.toPlatformString(true); if (WorkbenchHelper.getWorkspaceRoot() != null) { return WorkbenchHelper.exists(platformResourcePath, options); } else { URI resolvedLocation = resolvePlatformResourcePath(platformResourcePath); return resolvedLocation != null && getURIConverter(options).exists(resolvedLocation, options); } } @Override public Map<String, ?> getAttributes(URI uri, Map<?, ?> options) { String platformResourcePath = uri.toPlatformString(true); if (WorkbenchHelper.getWorkspaceRoot() != null) { return WorkbenchHelper.attributes(platformResourcePath, options); } else { URI resolvedLocation = resolvePlatformResourcePath(platformResourcePath); return resolvedLocation == null ? Collections .<String, Object> emptyMap() : getURIConverter(options) .getAttributes(resolvedLocation, options); } } @Override public void setAttributes(URI uri, Map<String, ?> attributes, Map<?, ?> options) throws IOException { String platformResourcePath = uri.toPlatformString(true); if (WorkbenchHelper.getWorkspaceRoot() != null) { WorkbenchHelper.updateAttributes(platformResourcePath, attributes, options); } else { URI resolvedLocation = resolvePlatformResourcePath(platformResourcePath); if (resolvedLocation != null) { getURIConverter(options).setAttributes(resolvedLocation, attributes, options); } else { throw new IOException("The platform resource path '" + platformResourcePath + "' does not resolve"); } } } /** * Returns the platform resource map. * <p> * This map is from {@link String} to {@link URI}. It is the logical * equivalent of the map implied by an {@link IWorkspaceRoot}: I.e., each * entry in the map corresponds to an * {@link org.eclipse.core.resources.IProject} that has a * {@link org.eclipse.core.resources.IResource#getName name} and a location * {@link org.eclipse.core.resources.IResource#getLocation location}; the * name is the key and the location, interpreted as a * {@link URI#createFileURI file URI}, is the value. This map is used to * {@link #resolvePlatformResourcePath resolve} a platform resource path, * and thereby supports relocatable projects in a manner that is * transparently the same as an Eclipse workspace. * </p> * * @return the platform resource map. * @see #resolvePlatformResourcePath */ public static Map<String, URI> getPlatformResourceMap() { if (platformResourceMap == null) { platformResourceMap = new HashMap<String, URI>(); } return platformResourceMap; } /** * Resolves a platform resource path of the form * <code>"/project/path"</code> against the platform resource map. * <p> * The first segment of the path, i.e., the <em>project name</em>, is used * to get a URI from the {@link #getPlatformResourceMap() map}. If a URI * results, the remaining segments are {@link URI#resolve(URI) resolved} * against it and that is the result. Otherwise, the result is * <code>null</code>. For example, given this mapping * * <pre> * EcoreUtil.getPlatformResourceMap().put("project", * URI.createURI("file:///C:/location/")); * </pre> * * the following transformation would result: * * <pre> * /project/directory/file * -> * file:///C:/location/directory/file * </pre> * * </p> * * @return the resolved URI or <code>null</code>. */ public static URI resolvePlatformResourcePath(String platformResourcePath) { if (platformResourceMap != null) { int index = platformResourcePath.indexOf("/", 1); String rootContainerName = platformResourcePath.substring(1, index); String relativeName = platformResourcePath.substring(index + 1); URI rootContainerLocation = getPlatformResourceMap().get( rootContainerName); if (rootContainerLocation != null) { return URIs.createURI(relativeName).resolve( rootContainerLocation); } } return null; } }