/* * eXist Open Source Native XML Database * Copyright (C) 2006 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; er version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ */ package org.exist.xmldb; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.util.regex.Pattern; import org.apache.log4j.Logger; import org.exist.collections.CollectionConfigurationManager; import org.exist.storage.DBBroker; import org.exist.xquery.Constants; import org.exist.xquery.util.URIUtils; import org.exist.xquery.value.AnyURIValue; /** A utility class for xmldb URis. * Since, java.net.URI is <strong>final</strong> this class acts as a wrapper. * @author Pierrick Brihaye <pierrick.brihaye@free.fr> */ /* * This base class implementation only provides a path. FullXmldbURI provides * full uri support. The create method creates a minimal object to keep memory * usage low. */ public class XmldbURI implements Comparable { protected final static Logger LOG = Logger.getLogger(XmldbURI.class); public static final int NO_PORT = -1; //Should be provided by org.xmldb.api package !!! public static final String XMLDB_SCHEME = "xmldb"; public static final String XMLDB_URI_PREFIX = "xmldb:"; public static final String DEFAULT_INSTANCE_NAME = "exist"; public static final String EMBEDDED_SERVER_AUTHORITY = "embedded-eXist-server"; public static final String EMBEDDED_SERVER_URI_PREFIX = XMLDB_URI_PREFIX + DEFAULT_INSTANCE_NAME + "://"; public final static XmldbURI ROOT_COLLECTION_URI = create(DBBroker.ROOT_COLLECTION); public final static XmldbURI RELATIVE_ROOT_COLLECTION_URI = create(DBBroker.ROOT_COLLECTION_NAME); public final static XmldbURI SYSTEM_COLLECTION_URI = create(DBBroker.SYSTEM_COLLECTION); public final static XmldbURI CONFIG_COLLECTION_URI = create(CollectionConfigurationManager.CONFIG_COLLECTION); //TODO : create using resolve() public final static XmldbURI ROOT_COLLECTION_CONFIG_URI = create(CollectionConfigurationManager.CONFIG_COLLECTION + "/" + DBBroker.ROOT_COLLECTION_NAME); public final static XmldbURI METADATA_COLLECTION_URI = create(DBBroker.SYSTEM_COLLECTION + "/metadata"); public final static XmldbURI TEMP_COLLECTION_URI = create(DBBroker.TEMP_COLLECTION); public final static XmldbURI EMPTY_URI = createInternal(""); public static final XmldbURI EMBEDDED_SERVER_URI = XmldbURI.create(EMBEDDED_SERVER_URI_PREFIX + EMBEDDED_SERVER_AUTHORITY); private String encodedCollectionPath; //TODO : deprecate when we split at root collection public final static String API_XMLRPC = "xmlrpc"; public final static String API_WEBDAV = "webdav"; public final static String API_REST = "rest-style"; public final static String API_LOCAL = "local"; public static XmldbURI xmldbUriFor(URI uri) throws URISyntaxException { return getXmldbURI(uri); } public static XmldbURI xmldbUriFor(String xmldbURI) throws URISyntaxException { return xmldbUriFor(xmldbURI, true); } public static XmldbURI xmldbUriFor(String xmldbURI, boolean escape) throws URISyntaxException { if (xmldbURI == null) { return null; } URI uri = new URI(escape ? AnyURIValue.escape(xmldbURI) : xmldbURI); return getXmldbURI(uri); } public static XmldbURI xmldbUriFor(String accessURI, String collectionPath) throws URISyntaxException { if (collectionPath == null) { return null; } URI uri = new URI(accessURI + URIUtils.iriToURI(collectionPath)); return getXmldbURI(uri); } public static XmldbURI create(URI uri) { try { return xmldbUriFor(uri); } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid URI: " + e.getMessage()); } } public static XmldbURI create(String uri) { try { return xmldbUriFor(uri); } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid URI: " + e.getMessage()); } } public static XmldbURI create(String accessURI, String collectionPath) { try { return xmldbUriFor(accessURI, collectionPath); } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid URI: " + e.getMessage()); } } public static XmldbURI createInternal(String collectionPath) { return new XmldbURI(collectionPath); } private static XmldbURI getXmldbURI(URI uri) throws URISyntaxException { if (uri.getScheme() != null || uri.getFragment() != null || uri.getQuery() != null) { return new FullXmldbURI(uri); } return new XmldbURI(uri); /* //TODO : get rid of this and use a more robust approach (dedicated constructor ?) -pb //TODO : use named constants index = path.lastIndexOf("/xmlrpc"); if (index > lastIndex) { return false; } //TODO : use named constants index = path.lastIndexOf("/webdav"); if (index > lastIndex) { return false; } */ } /** * Contructs an XmldbURI from given URI. * The provided URI must have the XMLDB_SCHEME ("xmldb") * @param xmldbURI A string * @throws URISyntaxException If the given string is not a valid xmldb URI. */ protected XmldbURI(URI xmldbURI) throws URISyntaxException { boolean hadXmldbPrefix = false; if (xmldbURI.getScheme() != null) { if (!XMLDB_SCHEME.equals(xmldbURI.getScheme())) { throw new URISyntaxException(xmldbURI.toString(), "xmldb URI scheme does not start with " + XMLDB_URI_PREFIX); } xmldbURI = new URI(xmldbURI.toString().substring(XMLDB_URI_PREFIX.length())); hadXmldbPrefix = true; } parseURI(xmldbURI, hadXmldbPrefix); } protected XmldbURI(String collectionPath) { this.encodedCollectionPath = collectionPath; } /** Feeds private members. Receives a URI with the xmldb: scheme already stripped * @throws URISyntaxException */ protected void parseURI(URI xmldbURI, boolean hadXmldbPrefix) throws URISyntaxException { splitPath(xmldbURI.getRawPath()); } /** Given a java.net.URI.getPath(), <strong>tries</strong> to dispatch the host's context * from the collection path as smartly as possible. * One would probably prefer a split policy based on the presence of a well-known root collection. * @param path The java.net.URI.getPath() provided. * @throws URISyntaxException */ protected void splitPath(String path) throws URISyntaxException { encodedCollectionPath = path; if (encodedCollectionPath != null && encodedCollectionPath.length() > 1 && encodedCollectionPath.endsWith("/")) { encodedCollectionPath = encodedCollectionPath.substring(0, encodedCollectionPath.length() - 1); } //TODO : check that collectionPath starts with DBBroker.ROOT_COLLECTION ? } /** To be called before a context operation with another XmldbURI. * @param uri * @throws IllegalArgumentException */ protected void checkCompatibilityForContextOperation(XmldbURI uri) throws IllegalArgumentException { if (this.getInstanceName() != null && uri.getInstanceName() != null && !this.getInstanceName().equals(uri.getInstanceName())) { throw new IllegalArgumentException(this.getInstanceName() + " instance differs from " + uri.getInstanceName()); } //case insensitive comparison if (this.getHost() != null && uri.getHost() != null && !this.getHost().equalsIgnoreCase(uri.getHost())) { throw new IllegalArgumentException(this.getHost() + " host differs from " + uri.getHost()); } if (this.getPort() != NO_PORT && uri.getPort() != NO_PORT && this.getPort() != uri.getPort()) { throw new IllegalArgumentException(this.getPort() + " port differs from " + uri.getPort()); } if (this.getCollectionPath() != null && uri.getCollectionPath() != null && !this.getCollectionPath().equals(uri.getCollectionPath())) { throw new IllegalArgumentException(this.getCollectionPath() + " collection differs from " + uri.getCollectionPath()); } } /** To be called before a collection path operation with another XmldbURI. * @param uri * @throws IllegalArgumentException */ protected void checkCompatibilityForCollectionOperation(XmldbURI uri) throws IllegalArgumentException { if (this.getInstanceName() != null && uri.getInstanceName() != null && !this.getInstanceName().equals(uri.getInstanceName())) { throw new IllegalArgumentException(this.getInstanceName() + " instance differs from " + uri.getInstanceName()); } //case insensitive comparison if (this.getHost() != null && uri.getHost() != null && !this.getHost().equalsIgnoreCase(uri.getHost())) { throw new IllegalArgumentException(this.getHost() + " host differs from " + uri.getHost()); } if (this.getPort() != NO_PORT && uri.getPort() != NO_PORT && this.getPort() != uri.getPort()) { throw new IllegalArgumentException(this.getPort() + " port differs from " + uri.getPort()); } if (this.getContext() != null && uri.getContext() != null && !this.getContext().equals(uri.getContext())) { throw new IllegalArgumentException(this.getContext() + " context differs from " + uri.getContext()); } } /* * It is an error for any of the following private members to throw an exception. */ /* private void setInstanceName(String instanceName) { String oldInstanceName = this.instanceName; try { this.instanceName = instanceName; recomputeURI(); } catch (URISyntaxException e) { this.instanceName = oldInstanceName; throw new IllegalArgumentException("Invalid URI: "+e.getMessage()); } } private void setContext(String context) throws URISyntaxException { String oldContext = this.context; try { //trims any trailing slash if (context != null && context.endsWith("/")) { //include root slash if we have a host if (this.getHost() != null) context = context.substring(0, context.length() - 1); } this.context = "".equals(context) ? null : context; recomputeURI(); } catch (URISyntaxException e) { this.context = oldContext; throw e; } } private void setCollectionPath(String collectionPath) throws URISyntaxException { String oldCollectionPath = collectionPath; try { if (collectionPath == null) this.encodedCollectionPath = null; else { String escaped = URIUtils.escapeHtmlURI(collectionPath); this.encodedCollectionPath = escaped; } recomputeURI(); } catch (URISyntaxException e) { this.encodedCollectionPath = oldCollectionPath; throw e; } catch (UnsupportedEncodingException e) { wrappedURI = null; throw new URISyntaxException(this.toString(), e.getMessage()); } } */ /** * This returns a proper heirarchical URI - the xmldb scheme is trimmed * from the beginning. The scheme will be the instance name, and all * other fields will be populated as would be expected from a heirarchical * URI * * @see #getXmldbURI */ public URI getURI() { return URI.create(encodedCollectionPath); } /** * This returns an xmldb uri. This is the most generic sort of uri - the * only fields set in the uri are scheme and schemeSpecificPart */ public URI getXmldbURI() { return URI.create(encodedCollectionPath); } public String getInstanceName() { return null; } /** * Method to return the collection path with reserved characters * percent encoded * * @return Returns the encoded collection path */ public String getRawCollectionPath() { return encodedCollectionPath; } public String getCollectionPath() { if (encodedCollectionPath == null) { return null; } try { //TODO: we might want to cache this value return URLDecoder.decode(encodedCollectionPath, "UTF-8"); } catch (UnsupportedEncodingException e) { //Should never happen throw new IllegalArgumentException(encodedCollectionPath + " can not be properly escaped"); } } public XmldbURI toCollectionPathURI() { return (this instanceof FullXmldbURI) ? XmldbURI.create(getRawCollectionPath()) : this; } /** To be called each time a private member that interacts with the wrapped URI is modified. * @throws URISyntaxException */ protected void recomputeURI() throws URISyntaxException { } /** To be called each time a private member that interacts with the wrapped URI is modified. * @throws URISyntaxException */ protected void safeRecomputeURI() { try { recomputeURI(); } catch (URISyntaxException e) { } } /* * Must be encoded! */ private void setCollectionPath(String collectionPath) { String oldCollectionPath = encodedCollectionPath; try { encodedCollectionPath = "".equals(collectionPath) ? null : collectionPath; //include root slash if we have a context if (encodedCollectionPath != null && getContext() != null & encodedCollectionPath.charAt(0) != '/') { encodedCollectionPath = "/" + encodedCollectionPath; } recomputeURI(); } catch (URISyntaxException e) { encodedCollectionPath = oldCollectionPath; throw new IllegalArgumentException("Invalid URI: " + e.getMessage()); } } public String getApiName() { return null; } public String getContext() { return null; } public int compareTo(Object ob) throws ClassCastException { if (!(ob instanceof XmldbURI)) { throw new ClassCastException("The provided Object is not an XmldbURI"); } return getXmldbURI().compareTo(((XmldbURI) ob).getXmldbURI()); } /** * This function returns a relative XmldbURI with the value after the last * / in the collection path of the URI * * @return A relative XmldbURI containing the value after the last / * in the collection path */ public XmldbURI lastSegment() { String name = getRawCollectionPath(); int last; // No slash - give them the whole thing! if ((last = name.lastIndexOf('/')) == Constants.STRING_NOT_FOUND) { return this; } // Checks against a trailing slash // is this appropriate? if (last == name.length() - 1) { name = name.substring(0, last); last = name.lastIndexOf('/'); } return XmldbURI.create(name.substring(last + 1)); } /** * This function returns a relative XmldbURI with the value after the last * / in the collection path of the URI * * @return A relative XmldbURI containing the value after the last / * in the collection path */ public int numSegments() { String name = getRawCollectionPath(); if (name == null || "".equals(name)) { return 0; } String[] split = name.split("/"); return split.length; } /** * This function returns a relative XmldbURI with the value after the last * / in the collection path of the URI * * @return A relative XmldbURI containing the value after the last / * in the collection path */ public XmldbURI[] getPathSegments() { String name = getRawCollectionPath(); if (name == null || "".equals(name)) { return new XmldbURI[0]; } String[] split = name.split("/"); int fix = ("".equals(split[0])) ? 1 : 0; XmldbURI[] segments = new XmldbURI[split.length - fix]; for (int i = fix; i < split.length; i++) { segments[i - fix] = XmldbURI.create(split[i]); } return segments; } /** * This function returns a string with everything after the last / removed * * @return A relative XmldbURI containing the value after the last / * in the collection path */ public XmldbURI removeLastSegment() { String uri = toString(); int last; // No slash - return null! if ((last = uri.lastIndexOf('/')) == Constants.STRING_NOT_FOUND) { return XmldbURI.EMPTY_URI; } // Checks against a trailing slash // is this appropriate? if (last == uri.length() - 1) { uri = uri.substring(0, last); last = uri.lastIndexOf('/'); } return last <= 0 ? XmldbURI.EMPTY_URI : XmldbURI.create(uri.substring(0, last)); } public XmldbURI append(String uri) { return append(XmldbURI.create(uri)); } public XmldbURI append(XmldbURI uri) { String toAppend = uri.getRawCollectionPath(); String prepend = toString(); if ("".equals(toAppend)) { return this; } if ("".equals(prepend)) { return uri; } if (!(prepend.charAt(prepend.length() - 1) == '/') && !(toAppend.charAt(0) == '/')) { return XmldbURI.create(prepend + "/" + toAppend); } else { return XmldbURI.create(prepend + toAppend); } } public XmldbURI appendInternal(XmldbURI uri) { return XmldbURI.createInternal(getRawCollectionPath() + '/' + uri.getRawCollectionPath()); } /** Ugly workaround for non-URI compliant pathes * @param pseudoURI What is supposed to be a URI * @return an supposedly correctly escaped URI <strong>string representation</string> * @deprecated By definition, using this method is strongly discouraged */ public static String recoverPseudoURIs(String pseudoURI) throws URISyntaxException { Pattern p = Pattern.compile("/"); String[] parts = p.split(pseudoURI); StringBuilder newURIString = new StringBuilder(parts[0]); for (int i = 1; i < parts.length; i++) { newURIString.append("/"); if (!"".equals(parts[i])) { try { //Try to instantiate the parst as a URI new URI(newURIString + parts[i]); newURIString.append(parts[i]); } catch (URISyntaxException e) { LOG.info("Trying to escape : ''" + parts[i] + "' in '" + pseudoURI + "' !"); newURIString.append(URIUtils.encodeForURI(parts[i])); } } } return newURIString.toString(); } public boolean equals(Object ob) { if (ob instanceof XmldbURI) { return getXmldbURI().equals(((XmldbURI) ob).getXmldbURI()); } if (ob instanceof URI) { return getXmldbURI().equals(ob); } if (ob instanceof String) { try { return getXmldbURI().equals(new URI((String) ob)); } catch (URISyntaxException e) { return false; } } return false; } public boolean equalsInternal(XmldbURI other) { if (this == other) { return true; } return encodedCollectionPath.equals(other.encodedCollectionPath); } public boolean isAbsolute() { return isCollectionPathAbsolute(); } public boolean isContextAbsolute() { return false; } public XmldbURI normalizeContext() { return this; } public URI relativizeContext(URI uri) { return null; } public URI resolveContext(String str) throws NullPointerException, IllegalArgumentException { return null; } public URI resolveContext(URI uri) throws NullPointerException { return null; } public boolean isCollectionPathAbsolute() { return encodedCollectionPath != null && encodedCollectionPath.length() > 0 && encodedCollectionPath.charAt(0) == '/'; } public XmldbURI normalizeCollectionPath() { String collectionPath = this.encodedCollectionPath; if (collectionPath == null) { return this; } URI collectionPathURI = URI.create(collectionPath).normalize(); if (collectionPathURI.getPath().equals(collectionPath)) { return this; } XmldbURI uri = XmldbURI.create(getXmldbURI()); uri.setCollectionPath(collectionPathURI.toString()); return uri; } public URI relativizeCollectionPath(URI uri) { if (uri == null) { throw new NullPointerException("The provided URI is null"); } String collectionPath = this.encodedCollectionPath; if (collectionPath == null) { throw new NullPointerException("The current collection path is null"); } URI collectionPathURI; //Adds a final slash if necessary if (!collectionPath.endsWith("/")) { LOG.info("Added a final '/' to '" + collectionPath + "'"); collectionPathURI = URI.create(collectionPath + "/"); } else { collectionPathURI = URI.create(collectionPath); } return collectionPathURI.relativize(uri); } //TODO: unit test! public XmldbURI resolveCollectionPath(XmldbURI child) throws NullPointerException, IllegalArgumentException { if (child == null) { throw new NullPointerException("The provided child URI is null"); } // if (child.isAbsolute()) // return child; //Old method: /* String collectionPath = this.encodedCollectionPath; if (collectionPath == null) throw new NullPointerException("The current collection path is null"); URI collectionPathURI; //Adds a final slash if necessary if (!collectionPath.endsWith("/")) { LOG.info("Added a final '/' to '" + collectionPath + "'"); collectionPathURI = URI.create(collectionPath + "/"); } else collectionPathURI = URI.create(collectionPath); */ String collectionPath = toCollectionPathURI().toString(); URI newCollectionURI = null; if (!collectionPath.endsWith("/")) { newCollectionURI = URI.create(collectionPath + "/").resolve(child.toCollectionPathURI().getURI()); } else { newCollectionURI = getURI().resolve(child.toCollectionPathURI().getURI()); } XmldbURI newURI = XmldbURI.create(getXmldbURI()); String newCollectionPath = newCollectionURI.getRawPath(); if (newCollectionPath.endsWith("/")) { newCollectionPath = newCollectionPath.substring(0, newCollectionPath.length() - 1); } newURI.encodedCollectionPath = newCollectionPath; newURI.safeRecomputeURI(); return newURI; } public URI resolveCollectionPath(URI uri) throws NullPointerException { if (uri == null) { throw new NullPointerException("The provided URI is null"); } String collectionPath = this.encodedCollectionPath; if (collectionPath == null) { throw new NullPointerException("The current collection path is null"); } URI collectionPathURI; //Adds a final slash if necessary if (!collectionPath.endsWith("/")) { LOG.info("Added a final '/' to '" + collectionPath + "'"); collectionPathURI = URI.create(collectionPath + "/"); } else { collectionPathURI = URI.create(collectionPath); } return collectionPathURI.resolve(uri); } public String toASCIIString() { //TODO : trim trailing slash if necessary return getXmldbURI().toASCIIString(); } public URL toURL() throws IllegalArgumentException, MalformedURLException { return getXmldbURI().toURL(); } //TODO: add unit test for this //TODO : come on ! use a URI method name. //resolve() is a must here public boolean startsWith(XmldbURI xmldbUri) { return (xmldbUri == null) ? false : toString().startsWith(xmldbUri.toString()); } //TODO : come on ! use a URI method name. //resolve() is a must here public boolean startsWith(String string) throws URISyntaxException { return startsWith(XmldbURI.xmldbUriFor(string)); } //TODO: add unit test for this public boolean endsWith(XmldbURI xmldbUri) { return (xmldbUri == null) ? false : toString().endsWith(xmldbUri.toString()); } public boolean endsWith(String string) throws URISyntaxException { return endsWith(XmldbURI.xmldbUriFor(string)); } //TODO: add unit test for this public XmldbURI prepend(XmldbURI xmldbUri) { if (xmldbUri == null) { throw new NullPointerException(toString() + " cannot start with null!"); } //TODO : resolve URIs !!! xmldbUri.resolve(this) return xmldbUri.append(this); } //TODO: add unit test for this public XmldbURI trimFromBeginning(XmldbURI xmldbUri) { if (xmldbUri == null) { throw new NullPointerException(toString() + " cannot start with null!"); } if (!startsWith(xmldbUri)) { throw new IllegalArgumentException(toString() + " does not start with " + xmldbUri.toString()); } return XmldbURI.create(toString().substring(xmldbUri.toString().length())); } public XmldbURI trimFromBeginning(String string) throws URISyntaxException { return trimFromBeginning(XmldbURI.xmldbUriFor(string)); } public String toString() { return encodedCollectionPath; } public static String[] getPathComponents(String collectionPath) { Pattern p = Pattern.compile("/"); String[] split = p.split(collectionPath); String[] result = new String[split.length - 1]; System.arraycopy(split, 1, result, 0, split.length - 1); return result; } /* @deprecated Legacy method used here and there in the code * if the currentPath is null return the parentPath else * if the currentPath doesnt not start with "/db/" and is not equal to "/db" then adjust the path to start with the parentPath * * Fix to Jens collection/resource name problem by deliriumsky * * @deprecated Use {@link #resolveCollectionPath(String) resolveCollectionPath} instead */ public static String checkPath(String currentPath, String parentPath) { if (currentPath == null) { return parentPath; } //Absolute path if (DBBroker.ROOT_COLLECTION.equals(currentPath)) { return currentPath; } //Absolute path if (currentPath.startsWith(DBBroker.ROOT_COLLECTION + "/")) { return currentPath; } //Kind of relative path : against all conventions ! -pb if (currentPath.startsWith("/")) { LOG.warn("Initial '/' for relative path '" + currentPath + "'"); } //OK : let's process this so-called relative path if (currentPath.startsWith("/")) { if (parentPath.endsWith("/")) { return parentPath + currentPath.substring(1); } return parentPath + currentPath; } //True relative pathes if (parentPath.endsWith("/")) { return parentPath + currentPath; } return parentPath + "/" + currentPath; } /** @deprecated Legacy method used here and there in the code * @param fileName * @param parentPath */ public static String checkPath2(String fileName, String parentPath) { //if (!fileName.startsWith("/")) // fileName = "/" + fileName; /*if (!fileName.startsWith(ROOT_COLLECTION)) fileName = ROOT_COLLECTION + fileName;*/ return checkPath(fileName, parentPath); } /**@deprecated Legacy method used here and there in the code and copied as such * @param name */ //TODO : changes // into / */ public String makeAbsolute(String name) { StringBuilder out = new StringBuilder(); for (int i = 0; i < name.length(); i++) //TODO : use dedicated function in XmldbURI { if (name.charAt(i) == '/' && name.length() > i + 1 && name.charAt(i + 1) == '/') { i++; } else { out.append(name.charAt(i)); } } String name2 = out.toString(); if (name2.length() > 0 && name2.charAt(0) != '/') { name2 = "/" + name2; } if (!name2.startsWith(DBBroker.ROOT_COLLECTION)) { name2 = DBBroker.ROOT_COLLECTION + name2; } if (name2.endsWith("/") && name2.length() > 1) { name2 = name2.substring(0, name2.length() - 1); } return name2; } /**@deprecated Legacy method used here and there in the code and copied as such * @param name */ //TODO : changes // into / */ public final static String normalizeCollectionName(String name) { StringBuilder out = new StringBuilder(); for (int i = 0; i < name.length(); i++) //TODO : use dedicated function in XmldbURI { if (name.charAt(i) == '/' && name.length() > i + 1 && name.charAt(i + 1) == '/') { i++; } else { out.append(name.charAt(i)); } } String name2 = out.toString(); if (name2.length() > 0 && name2.charAt(0) != '/') { name2 = "/" + name2; } if (!name2.startsWith(DBBroker.ROOT_COLLECTION)) { name2 = DBBroker.ROOT_COLLECTION + name2; } if (name2.endsWith("/") && name2.length() > 1) { name2 = name2.substring(0, name2.length() - 1); } return name2; } /* (non-Javadoc) * @see java.net.URI#getAuthority() */ public String getAuthority() { return null; } /* (non-Javadoc) * @see java.net.URI#getFragment() */ public String getFragment() { return null; } /* (non-Javadoc) * @see java.net.URI#getPort() */ public int getPort() { return NO_PORT; } /* (non-Javadoc) * @see java.net.URI#getQuery() */ public String getQuery() { return null; } /* (non-Javadoc) * @see java.net.URI#getRawAuthority() */ public String getRawAuthority() { return null; } /* (non-Javadoc) * @see java.net.URI#getHost() */ public String getHost() { return null; } /* (non-Javadoc) * @see java.net.URI#getUserInfo() */ public String getUserInfo() { return null; } /* (non-Javadoc) * @see java.net.URI#getRawFragment() */ public String getRawFragment() { return null; } /* (non-Javadoc) * @see java.net.URI#getRawQuery() */ public String getRawQuery() { return null; } /* (non-Javadoc) * @see java.net.URI#getRawUserInfo() */ public String getRawUserInfo() { return null; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ public int hashCode() { return getXmldbURI().hashCode(); } // TODO : prefefined URIs as static classes... }