/**
* Copyright (c) 2002-2012 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
*/
package org.eclipse.emf.ecore.resource.impl;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.Callback;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
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);
}
public void store(URI uri, byte[] bytes, Map<?, ?> options, Callback<Map<?, ?>> callback)
{
URI normalizedURI = normalize(uri);
getURIHandler(normalizedURI).store(normalizedURI, bytes, new OptionsMap(OPTION_URI_CONVERTER, this, options), callback);
}
public void delete(URI uri, Map<?, ?> options, Callback<Map<?, ?>> callback)
{
URI normalizedURI = normalize(uri);
getURIHandler(normalizedURI).delete(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options), callback);
}
static class OptionsMap implements Map<Object, Object>
{
protected Object key;
protected Object value;
protected Map<?, ?> options;
protected Map<Object, Object> mergedMap;
public OptionsMap(Object key, Object value, Map<?, ?> options)
{
this.options = options == null ? Collections.EMPTY_MAP : options;
this.key = key;
this.value = value;
}
protected Map<Object, Object> mergedMap()
{
if (mergedMap == null)
{
mergedMap = new LinkedHashMap<Object, Object>(options);
mergedMap.put(key, value);
}
return mergedMap;
}
public void clear()
{
throw new UnsupportedOperationException();
}
public boolean containsKey(Object key)
{
return this.key == key || this.key.equals(key) || options.containsKey(key);
}
public boolean containsValue(Object value)
{
return this.value == value || options.containsValue(value);
}
public Set<Map.Entry<Object, Object>> entrySet()
{
return mergedMap().entrySet();
}
public Object get(Object key)
{
return this.key == key || this.key.equals(key) ? value : options.get(key);
}
public boolean isEmpty()
{
return false;
}
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 createInputStream(URI uri, Map<?, ?> options, Callback<Map<?, ?>> callback)
{
URI normalizedURI = normalize(uri);
getURIHandler(normalizedURI).createInputStream(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options), callback);
}
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 void exists(URI uri, Map<?, ?> options, Callback<Boolean> callback)
{
URI normalizedURI = normalize(uri);
getURIHandler(normalizedURI).exists(normalizedURI, new OptionsMap(OPTION_URI_CONVERTER, this, options), callback);
}
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));
}
/**
* 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();
String query = uri.query();
URI result = getInternalURIMap().getURI(uri.trimFragment().trimQuery());
String scheme = result.scheme();
if (scheme == null)
{
if (result.hasAbsolutePath())
{
result = URI.createURI("file:" + result);
}
else
{
result = URI.createFileURI(result.toString());
}
}
if (fragment != null)
{
result = result.appendFragment(fragment);
}
if (query != null)
{
result = result.appendQuery(query);
}
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;
}
}