/**
* <copyright>
*
* Copyright (c) 2002-2007 IBM Corporation 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:
* IBM - Initial API and implementation
*
* </copyright>
*
* $Id: ExtensibleURIConverterImpl.java,v 1.7 2011/01/26 17:25:24 emerks Exp $
*/
package org.eclipse.emf.ecore.resource.impl;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
import org.eclipse.emf.ecore.resource.ContentHandler;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.URIHandler;
/**
* A highly functional and extensible URI converter implementation.
* <p>
* This implementation provides seamless transparent Eclipse integration
* by supporting the <code>platform:/resource</code> mechanism both inside of Eclipse and outside of Eclipse.
* Furthermore, although the implementation imports
* both {@link org.eclipse.core.runtime} and {@link org.eclipse.core.resources},
* and hence requires the Eclipse libraries at development time,
* the implementation does <b>not</b> require them at runtime.
* Clients of this implementation must be cautious if they wish to maintain this platform neutral behaviour.
* </p>
*/
public class ExtensibleURIConverterImpl implements URIConverter
{
/**
* A map that remaps URIs.
*/
public interface URIMap extends Map<URI, URI>
{
/**
* Returns the remapped URI, or the URI itself.
* @param uri the URI to remap.
* @return the remapped URI, or the URI itself.
*/
URI getURI(URI uri);
}
protected static class URIHandlerList extends BasicEList<URIHandler>
{
private static final long serialVersionUID = 1L;
public URIHandlerList()
{
super();
}
@Override
protected boolean canContainNull()
{
return false;
}
@Override
protected Object [] newData(int capacity)
{
return new URIHandler [capacity];
}
@Override
public URIHandler [] data()
{
return (URIHandler[])data;
}
}
protected URIHandlerList uriHandlers;
protected static class ContentHandlerList extends BasicEList<ContentHandler>
{
private static final long serialVersionUID = 1L;
public ContentHandlerList()
{
super();
}
@Override
protected boolean canContainNull()
{
return false;
}
@Override
protected Object [] newData(int capacity)
{
return new ContentHandler [capacity];
}
@Override
public ContentHandler [] data()
{
return (ContentHandler[])data;
}
}
protected ContentHandlerList contentHandlers;
/**
* The URI map.
*/
protected URIMap uriMap;
/**
* Creates an instance.
*/
public ExtensibleURIConverterImpl()
{
this(URIHandler.DEFAULT_HANDLERS, ContentHandler.Registry.INSTANCE.contentHandlers());
}
/**
* Creates an instance.
*/
public ExtensibleURIConverterImpl(Collection<URIHandler> uriHandlers, Collection<ContentHandler> contentHandlers)
{
getURIHandlers().addAll(uriHandlers);
getContentHandlers().addAll(contentHandlers);
}
public EList<URIHandler> getURIHandlers()
{
if (uriHandlers == null)
{
uriHandlers = new URIHandlerList();
}
return uriHandlers;
}
public URIHandler getURIHandler(URI uri)
{
int size = uriHandlers.size();
if (size > 0)
{
URIHandler[] data = uriHandlers.data();
for (int i = 0; i < size; ++i)
{
URIHandler uriHandler = data[i];
if (uriHandler.canHandle(uri))
{
return uriHandler;
}
}
}
throw new RuntimeException("There is no URIHandler to handle " + uri);
}
public EList<ContentHandler> getContentHandlers()
{
if (contentHandlers == null)
{
contentHandlers = new ContentHandlerList();
}
return contentHandlers;
}
public OutputStream createOutputStream(URI uri) throws IOException
{
return createOutputStream(uri, null);
}
static class OptionsMap implements Map<Object, Object>
{
private static final Object NO_KEY = new Object();
protected Object key;
protected Object value;
protected Map<?, ?> options;
protected Map<?, ?> defaultOptions;
protected Map<Object, Object> mergedMap;
public OptionsMap(Object key, Object value, Map<?, ?> options)
{
this(key, value, options, null);
}
public OptionsMap(Map<?, ?> options, Map<?, ?> defaultOptions)
{
this(NO_KEY, null, options, defaultOptions);
}
public OptionsMap(Object key, Object value, Map<?, ?> options, Map<?, ?> defaultOptions)
{
this.options = options;
this.defaultOptions = defaultOptions;
this.key = key;
this.value = value;
}
protected Map<Object, Object> mergedMap()
{
if (mergedMap == null)
{
mergedMap =
new LinkedHashMap<Object, Object>
((options == null ? 0 : options.size()) +
(defaultOptions == null ? 0 : defaultOptions.size()) +
(key == NO_KEY ? 0 : 1));
if (defaultOptions != null)
{
mergedMap.putAll(defaultOptions);
}
if (options != null)
{
mergedMap.putAll(options);
}
if (key != NO_KEY)
{
mergedMap.put(key, value);
}
}
return mergedMap;
}
public void clear()
{
throw new UnsupportedOperationException();
}
public boolean containsKey(Object key)
{
return
mergedMap != null ?
mergedMap.containsKey(key) :
this.key == key ||
this.key.equals(key) ||
options != null && options.containsKey(key) ||
defaultOptions != null && defaultOptions.containsKey(key);
}
public boolean containsValue(Object value)
{
return mergedMap().containsValue(value);
}
public Set<Map.Entry<Object, Object>> entrySet()
{
return mergedMap().entrySet();
}
public Object get(Object key)
{
return
mergedMap != null ?
mergedMap.get(key) :
this.key == key ||
this.key.equals(key) ?
value :
options != null && options.containsKey(key) ?
options.get(key) :
defaultOptions == null ?
null :
defaultOptions.get(key);
}
public boolean isEmpty()
{
return
mergedMap != null ?
mergedMap.isEmpty() :
key != NO_KEY ||
options != null && !options.isEmpty() ||
defaultOptions != null && !defaultOptions.isEmpty();
}
public Set<Object> keySet()
{
return mergedMap().keySet();
}
public Object put(Object key, Object value)
{
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends Object, ? extends Object> t)
{
throw new UnsupportedOperationException();
}
public Object remove(Object key)
{
throw new UnsupportedOperationException();
}
public int size()
{
return mergedMap().size();
}
public Collection<Object> values()
{
return mergedMap().values();
}
@Override
public int hashCode()
{
return mergedMap().hashCode();
}
@Override
public boolean equals(Object o)
{
return mergedMap().equals(o);
}
}
public OutputStream createOutputStream(URI uri, Map<?, ?> options) throws IOException
{
URI normalizedURI = normalize(uri);
return getURIHandler(normalizedURI).createOutputStream(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options));
}
public InputStream createInputStream(URI uri) throws IOException
{
return createInputStream(uri, null);
}
public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException
{
URI normalizedURI = normalize(uri);
return getURIHandler(normalizedURI).createInputStream(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options));
}
public void delete(URI uri, Map<?, ?> options) throws IOException
{
URI normalizedURI = normalize(uri);
getURIHandler(normalizedURI).delete(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options));
}
public Map<String, ?> contentDescription(URI uri, Map<?, ?> options) throws IOException
{
URI normalizedURI = normalize(uri);
return getURIHandler(normalizedURI).contentDescription(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options));
}
public boolean exists(URI uri, Map<?, ?> options)
{
URI normalizedURI = normalize(uri);
return getURIHandler(normalizedURI).exists(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options));
}
public Map<String, ?> getAttributes(URI uri, Map<?, ?> options)
{
URI normalizedURI = normalize(uri);
return getURIHandler(normalizedURI).getAttributes(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options));
}
public void setAttributes(URI uri, Map<String, ?> attributes, Map<?, ?> options) throws IOException
{
URI normalizedURI = normalize(uri);
getURIHandler(normalizedURI).setAttributes(normalizedURI, attributes, new OptionsMap(OPTION_URI_CONVERTER, this, options));
}
private static IWorkspaceRoot workspaceRoot = EcorePlugin.getWorkspaceRoot();
/**
* Returns the normalized form of the URI.
* <p>
* This implementation does precisely and only the {@link URIConverter#normalize typical} thing.
* It calls itself recursively so that mapped chains are followed.
* </p>
* @param uri the URI to normalize.
* @return the normalized form.
* @see org.eclipse.emf.ecore.plugin.EcorePlugin#getPlatformResourceMap
*/
public URI normalize(URI uri)
{
String fragment = uri.fragment();
URI result =
fragment == null ?
getInternalURIMap().getURI(uri) :
getInternalURIMap().getURI(uri.trimFragment()).appendFragment(fragment);
String scheme = result.scheme();
if (scheme == null)
{
if (workspaceRoot != null)
{
if (result.hasAbsolutePath())
{
result = URI.createPlatformResourceURI(result.trimFragment().toString(), false);
if (fragment != null)
{
result = result.appendFragment(fragment);
}
}
}
else
{
if (result.hasAbsolutePath())
{
result = URI.createURI("file:" + result);
}
else
{
result = URI.createFileURI(new File(result.trimFragment().toString()).getAbsolutePath());
if (fragment != null)
{
result = result.appendFragment(fragment);
}
}
}
}
if (result.equals(uri))
{
return uri;
}
else
{
return normalize(result);
}
}
/*
* Javadoc copied from interface.
*/
public Map<URI, URI> getURIMap()
{
return getInternalURIMap();
}
/**
* Returns the internal version of the URI map.
* @return the internal version of the URI map.
*/
protected URIMap getInternalURIMap()
{
if (uriMap == null)
{
URIMappingRegistryImpl mappingRegistryImpl =
new URIMappingRegistryImpl()
{
private static final long serialVersionUID = 1L;
@Override
protected URI delegatedGetURI(URI uri)
{
return URIMappingRegistryImpl.INSTANCE.getURI(uri);
}
};
uriMap = (URIMap)mappingRegistryImpl.map();
}
return uriMap;
}
}