001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2006.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.query.parser.serql;
007:
008: import java.util.HashMap;
009: import java.util.LinkedHashMap;
010: import java.util.List;
011: import java.util.Map;
012:
013: import org.openrdf.model.vocabulary.OWL;
014: import org.openrdf.model.vocabulary.RDF;
015: import org.openrdf.model.vocabulary.RDFS;
016: import org.openrdf.model.vocabulary.SESAME;
017: import org.openrdf.model.vocabulary.XMLSchema;
018: import org.openrdf.query.MalformedQueryException;
019: import org.openrdf.query.parser.serql.ast.ASTNamespaceDecl;
020: import org.openrdf.query.parser.serql.ast.ASTQName;
021: import org.openrdf.query.parser.serql.ast.ASTQueryContainer;
022: import org.openrdf.query.parser.serql.ast.ASTURI;
023: import org.openrdf.query.parser.serql.ast.SyntaxTreeBuilderTreeConstants;
024: import org.openrdf.query.parser.serql.ast.VisitorException;
025:
026: /**
027: * Processes the namespace declarations in a SeRQL query model.
028: *
029: * @author Arjohn Kampman
030: */
031: class NamespaceDeclProcessor extends ASTVisitorBase {
032:
033: /**
034: * Processes prefix declarations in queries. This method collects all
035: * prefixes that are declared in the supplied query, verifies that prefixes
036: * are not redefined and replaces any {@link ASTQName} nodes in the query
037: * with equivalent {@link ASTIRI} nodes.
038: *
039: * @param qc
040: * The query that needs to be processed.
041: * @return A map containing the prefixes that are declared in the query (key)
042: * and the namespace they map to (value).
043: * @throws MalformedQueryException
044: * If the query contains redefined prefixes or qnames that use
045: * undefined prefixes.
046: */
047: public static Map<String, String> process(ASTQueryContainer qc)
048: throws MalformedQueryException {
049: List<ASTNamespaceDecl> nsDeclList = qc.getNamespaceDeclList();
050:
051: // Build a prefix --> URI map
052: Map<String, String> nsMap = new LinkedHashMap<String, String>();
053: for (ASTNamespaceDecl nsDecl : nsDeclList) {
054: String prefix = nsDecl.getPrefix();
055: String uri = nsDecl.getURI().getValue();
056:
057: if (nsMap.containsKey(prefix)) {
058: // Prefix already defined
059: if (nsMap.get(prefix).equals(uri)) {
060: // duplicate, ignore
061: } else {
062: throw new MalformedQueryException(
063: "Multiple namespace declarations for prefix '"
064: + prefix + "'");
065: }
066: } else {
067: nsMap.put(prefix, uri);
068: }
069: }
070:
071: // Use default namespace prefixes when not defined explicitly
072: if (!nsMap.containsKey("rdf")) {
073: nsMap.put("rdf", RDF.NAMESPACE);
074: }
075: if (!nsMap.containsKey("rdfs")) {
076: nsMap.put("rdfs", RDFS.NAMESPACE);
077: }
078: if (!nsMap.containsKey("xsd")) {
079: nsMap.put("xsd", XMLSchema.NAMESPACE);
080: }
081: if (!nsMap.containsKey("owl")) {
082: nsMap.put("owl", OWL.NAMESPACE);
083: }
084: if (!nsMap.containsKey("sesame")) {
085: nsMap.put("sesame", SESAME.NAMESPACE);
086: }
087:
088: // For backwards compatibility:
089: Map<String, String> extendedNsMap = new HashMap<String, String>(
090: nsMap);
091: if (!extendedNsMap.containsKey("serql")) {
092: extendedNsMap.put("serql", SESAME.NAMESPACE);
093: }
094:
095: // Replace all qnames with URIs
096: QNameProcessor visitor = new QNameProcessor(extendedNsMap);
097: try {
098: qc.jjtAccept(visitor, null);
099: } catch (VisitorException e) {
100: throw new MalformedQueryException(e.getMessage(), e);
101: }
102:
103: return nsMap;
104: }
105:
106: private static class QNameProcessor extends ASTVisitorBase {
107:
108: private Map<String, String> prefixMap;
109:
110: public QNameProcessor(Map<String, String> prefixMap) {
111: this .prefixMap = prefixMap;
112: }
113:
114: @Override
115: public Object visit(ASTQName qnameNode, Object data)
116: throws VisitorException {
117: String qname = qnameNode.getValue();
118:
119: int colonIdx = qname.indexOf(':');
120: assert colonIdx >= 0 : "colonIdx should be >= 0: "
121: + colonIdx;
122:
123: String prefix = qname.substring(0, colonIdx);
124: String localName = qname.substring(colonIdx + 1);
125:
126: String namespace = prefixMap.get(prefix);
127: if (namespace == null) {
128: throw new VisitorException("QName '" + qname
129: + "' uses an undefined prefix");
130: }
131:
132: // Replace the qname node with a new IRI node in the parent node
133: ASTURI uriNode = new ASTURI(
134: SyntaxTreeBuilderTreeConstants.JJTURI);
135: uriNode.setValue(namespace + localName);
136: qnameNode.jjtReplaceWith(uriNode);
137:
138: return null;
139: }
140: }
141: }
|