/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2006.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.query.parser.serql;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.openrdf.model.vocabulary.OWL;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.model.vocabulary.SESAME;
import org.openrdf.model.vocabulary.XMLSchema;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.parser.serql.ast.ASTNamespaceDecl;
import org.openrdf.query.parser.serql.ast.ASTQName;
import org.openrdf.query.parser.serql.ast.ASTQueryContainer;
import org.openrdf.query.parser.serql.ast.ASTURI;
import org.openrdf.query.parser.serql.ast.SyntaxTreeBuilderTreeConstants;
import org.openrdf.query.parser.serql.ast.VisitorException;
/**
* Processes the namespace declarations in a SeRQL query model.
*
* @author Arjohn Kampman
*/
class NamespaceDeclProcessor extends ASTVisitorBase {
/**
* Processes prefix declarations in queries. This method collects all
* prefixes that are declared in the supplied query, verifies that prefixes
* are not redefined and replaces any {@link ASTQName} nodes in the query
* with equivalent {@link ASTIRI} nodes.
*
* @param qc
* The query that needs to be processed.
* @return A map containing the prefixes that are declared in the query (key)
* and the namespace they map to (value).
* @throws MalformedQueryException
* If the query contains redefined prefixes or qnames that use
* undefined prefixes.
*/
public static Map<String, String> process(ASTQueryContainer qc)
throws MalformedQueryException
{
List<ASTNamespaceDecl> nsDeclList = qc.getNamespaceDeclList();
// Build a prefix --> URI map
Map<String, String> nsMap = new LinkedHashMap<String, String>();
for (ASTNamespaceDecl nsDecl : nsDeclList) {
String prefix = nsDecl.getPrefix();
String uri = nsDecl.getURI().getValue();
if (nsMap.containsKey(prefix)) {
// Prefix already defined
if (nsMap.get(prefix).equals(uri)) {
// duplicate, ignore
}
else {
throw new MalformedQueryException("Multiple namespace declarations for prefix '" + prefix
+ "'");
}
}
else {
nsMap.put(prefix, uri);
}
}
// Use default namespace prefixes when not defined explicitly
if (!nsMap.containsKey("rdf")) {
nsMap.put("rdf", RDF.NAMESPACE);
}
if (!nsMap.containsKey("rdfs")) {
nsMap.put("rdfs", RDFS.NAMESPACE);
}
if (!nsMap.containsKey("xsd")) {
nsMap.put("xsd", XMLSchema.NAMESPACE);
}
if (!nsMap.containsKey("owl")) {
nsMap.put("owl", OWL.NAMESPACE);
}
if (!nsMap.containsKey("sesame")) {
nsMap.put("sesame", SESAME.NAMESPACE);
}
// For backwards compatibility:
Map<String, String> extendedNsMap = new HashMap<String, String>(nsMap);
if (!extendedNsMap.containsKey("serql")) {
extendedNsMap.put("serql", SESAME.NAMESPACE);
}
// Replace all qnames with URIs
QNameProcessor visitor = new QNameProcessor(extendedNsMap);
try {
qc.jjtAccept(visitor, null);
}
catch (VisitorException e) {
throw new MalformedQueryException(e.getMessage(), e);
}
return nsMap;
}
private static class QNameProcessor extends ASTVisitorBase {
private Map<String, String> prefixMap;
public QNameProcessor(Map<String, String> prefixMap) {
this.prefixMap = prefixMap;
}
@Override
public Object visit(ASTQName qnameNode, Object data)
throws VisitorException
{
String qname = qnameNode.getValue();
int colonIdx = qname.indexOf(':');
assert colonIdx >= 0 : "colonIdx should be >= 0: " + colonIdx;
String prefix = qname.substring(0, colonIdx);
String localName = qname.substring(colonIdx + 1);
String namespace = prefixMap.get(prefix);
if (namespace == null) {
throw new VisitorException("QName '" + qname + "' uses an undefined prefix");
}
// Replace the qname node with a new IRI node in the parent node
ASTURI uriNode = new ASTURI(SyntaxTreeBuilderTreeConstants.JJTURI);
uriNode.setValue(namespace + localName);
qnameNode.jjtReplaceWith(uriNode);
return null;
}
}
}