/* * Copyright 2008 Fedora Commons * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mulgara.sparql.parser.cst; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.mulgara.query.rdf.Mulgara; import org.mulgara.sparql.parser.ParseException; import javax.xml.namespace.QName; /** * Represents IRI references in SPARQL. This is a superset of URI references. * For the moment, just wrap a URI, even though this doesn't meet the strict * definition of an IRI. * * @created Feb 8, 2008 * @author Paula Gearon * @copyright © 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a> * @licence <a href="{@docRoot}/../LICENCE.txt">Apache License, Version 2.0</a> */ public class IRIReference implements Node, PrimaryExpression, Verb, Cloneable { /** The RDF namespace */ private static final String RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; /** Constant IRI for RDF_TYPE */ public static final IRIReference RDF_TYPE = new IRIReference(URI.create(RDF_NS + "type")); /** Constant IRI for RDF_FIRST */ public static final IRIReference RDF_FIRST = new IRIReference(URI.create(RDF_NS + "first")); /** Constant IRI for RDF_REST */ public static final IRIReference RDF_REST = new IRIReference(URI.create(RDF_NS + "rest")); /** Constant IRI for RDF_NIL */ public static final IRIReference RDF_NIL = new IRIReference(URI.create(RDF_NS + "nil")); /** The made-up namespace for op: XPath functions */ public static final String OP_NS = "op"; /** The "op" prefix for XPath functions */ public static final String OP_PREFIX = OP_NS + ":"; /** The prefix for the sparql: namespace */ static final String SPARQL_PREFIX = "sparql"; /** The sparql: namespace */ static final URI SPARQL_NS; /** The prefix for the fn: namespace */ static final String FN_PREFIX = "fn"; /** The fn: namespace */ static final URI FN_NS; /** The internal URI value */ private URI uri; /** The original text of the URI */ private String text; /** The qname form, if available */ private QName qname; /** A modifier when this reference is used as a predicate */ private Modifier mod = Modifier.none; static { SPARQL_NS = URI.create("http://www.w3.org/2006/sparql-functions#"); FN_NS = URI.create("http://www.w3.org/2005/xpath-functions/#"); } /** * Create an IRI reference from a URI. * @param uri The URI referred to. */ public IRIReference(URI uri) { this.uri = uri; text = "<" + uri.toString() + ">"; qname = parseQName(uri.toString()); } /** * Create an IRI reference from a URI. * @param uri The URI referred to. * @param namespaces The environment's map of prefix to namespace URIs. */ public IRIReference(URI uri, Map<String,URI> namespaces) { this.uri = uri; text = "<" + uri.toString() + ">"; qname = parseQName(uri.toString(), namespaces); } /** * Create an IRI reference from a URI. * @param uri The URI referred to. * @param namespaces The environment's map of prefix to namespace URIs. */ public IRIReference(String uri, Map<String,URI> namespaces) throws ParseException { try { this.uri = new URI(uri); } catch (URISyntaxException e) { // Mulgara hack for handling the system graph if (!uri.endsWith("##")) throw new ParseException("Unable to create a URI from: " + uri); this.uri = URI.create("#"); } text = "<" + uri + ">"; qname = parseQName(uri.toString(), namespaces); } /** * Create an IRI reference from a URI with an abbreviated namespace, with a known namespace. * @param uri The URI referred to. * @param text The abbreviated form. */ public IRIReference(URI namespace, String prefix, String localPart) throws ParseException { try { this.uri = new URI(namespace + localPart); } catch (URISyntaxException e) { throw new ParseException("Unable to create URI: " + namespace + localPart); } if (prefix == null) { text = "<:" + localPart + ">"; qname = new QName(namespace.toString(), localPart); } else { text = prefix + ":" + localPart; qname = new QName(namespace.toString(), localPart, prefix); } } /** * @return the IRI value */ public URI getUri() { return uri; } /** * @see org.mulgara.sparql.parser.cst.Node#getImage() */ public String getImage() { return text; } /** * Retrieves the qname for this URI, if one is known. * @return The known QName, or <code>null</code> if none available. */ public QName getQName() { return qname; } /** * @see java.lang.Object#toString() */ public String toString() { return "<" + uri.toString() + ">"; } /** * Parse a URI, looking for a valid QName structure. * @param uri The URI to parse. * @return a new QName if one could be parsed, or <code>null</code> if one couldn't be found. * @param namespaces The environment's map of prefix to namespace URIs. */ private QName parseQName(String uri) { Map<String,URI> e = Collections.emptyMap(); return parseQName(uri, e); } /** * Parse a URI, looking for a valid QName structure. * @param uri The URI to parse. * @return a new QName if one could be parsed, or <code>null</code> if one couldn't be found. * @param namespaces The environment's map of prefix to namespace URIs. */ private QName parseQName(String uri, Map<String,URI> namespaces) { // the "op" prefix is special, in that it has no namespace // check if the URI starts with "op:" and is followed by a valid local name character if (uri.startsWith(OP_PREFIX) && localStartNameChar(uri.charAt(OP_PREFIX.length()))) { return new QName(OP_NS, uri.substring(OP_PREFIX.length()), OP_PREFIX); } // expand the namespaces to include some defaults, if missing namespaces = expandedNamespaces(namespaces); // search for a namespace we know about for (Map.Entry<String,URI> e: namespaces.entrySet()) { String namespaceUri = e.getValue().toString(); if (uri.startsWith(namespaceUri)) { return new QName(namespaceUri, uri.substring(namespaceUri.length()), e.getKey()); } } return null; } /** * Expand on a namespace map to include some useful defaults. * @param original The original namespace map of prefixes to namespace URIs. * @return A new namespace map with the added entries. */ private static Map<String,URI> expandedNamespaces(Map<String,URI> original) { Map<String,URI> result = new HashMap<String,URI>(original); if (!result.containsKey(SPARQL_PREFIX)) result.put(SPARQL_PREFIX, URI.create("http://www.w3.org/2006/sparql-functions#")); if (!result.containsKey(FN_PREFIX)) result.put(FN_PREFIX, URI.create("http://www.w3.org/2005/xpath-functions/#")); if (!result.containsKey(Mulgara.NS_PREFIX)) result.put(Mulgara.NS_PREFIX, Mulgara.NS_URI); return result; } /** * Indicates if the char is a valid XML name character, minus the colon character. * @see http://www.w3.org/TR/REC-xml/#NT-NameStartChar * @param c The character to test. * @return <code>true</code> if the character is a valid start to an XML name. */ @SuppressWarnings("unused") private static final boolean localNameChar(char c) { return localStartNameChar(c) || c == '-' || c == '.' || (c >= '0' && c <= '9') || c == 0xB7 || (c >= 0x0300 && c <= 0x036F) || (c >= 0x203F && c <= 0x2040); } /** * Indicates if the char is a valid start for an XML name character, minus the colon character. * @see http://www.w3.org/TR/REC-xml/#NT-NameStartChar * @param c The character to test. * @return <code>true</code> if the character is a valid start to an XML name. */ private static final boolean localStartNameChar(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || (c >= 0xC0 && c <= 0xD6) || (c >= 0xD8 && c <= 0xF6) || (c >= 0xF8 && c <= 0x2FF) || (c >= 0x370 && c <= 0x37D) || (c >= 0x37F && c <= 0x1FFF) || (c >= 0x200C && c <= 0x200D) || (c >= 0x2070 && c <= 0x218F) || (c >= 0x2C00 && c <= 0x2FEF) || (c >= 0x3001 && c <= 0xD7FF) || (c >= 0xF900 && c <= 0xFDCF) || (c >= 0xFDF0 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0xEFFFF); } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException("Unable to copy an IRIReference"); } } public void setModifier(Modifier m) { mod = m; } public Modifier getModifier() { return mod; } }