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.sparql;
007:
008: import java.util.LinkedHashMap;
009: import java.util.List;
010: import java.util.Map;
011:
012: import org.openrdf.query.MalformedQueryException;
013: import org.openrdf.query.parser.sparql.ast.ASTIRI;
014: import org.openrdf.query.parser.sparql.ast.ASTPrefixDecl;
015: import org.openrdf.query.parser.sparql.ast.ASTQName;
016: import org.openrdf.query.parser.sparql.ast.ASTQueryContainer;
017: import org.openrdf.query.parser.sparql.ast.SyntaxTreeBuilderTreeConstants;
018: import org.openrdf.query.parser.sparql.ast.VisitorException;
019:
020: /**
021: * Processes the prefix declarations in a SPARQL query model.
022: *
023: * @author Arjohn Kampman
024: */
025: class PrefixDeclProcessor {
026:
027: /**
028: * Processes prefix declarations in queries. This method collects all
029: * prefixes that are declared in the supplied query, verifies that prefixes
030: * are not redefined and replaces any {@link ASTQName} nodes in the query
031: * with equivalent {@link ASTIRI} nodes.
032: *
033: * @param qc
034: * The query that needs to be processed.
035: * @return A map containing the prefixes that are declared in the query (key)
036: * and the namespace they map to (value).
037: * @throws MalformedQueryException
038: * If the query contains redefined prefixes or qnames that use
039: * undefined prefixes.
040: */
041: public static Map<String, String> process(ASTQueryContainer qc)
042: throws MalformedQueryException {
043: List<ASTPrefixDecl> prefixDeclList = qc.getPrefixDeclList();
044:
045: // Build a prefix --> IRI map
046: Map<String, String> prefixMap = new LinkedHashMap<String, String>();
047: for (ASTPrefixDecl prefixDecl : prefixDeclList) {
048: String prefix = prefixDecl.getPrefix();
049: String iri = prefixDecl.getIRI().getValue();
050:
051: if (prefixMap.containsKey(prefix)) {
052: throw new MalformedQueryException(
053: "Multiple prefix declarations for prefix '"
054: + prefix + "'");
055: }
056:
057: prefixMap.put(prefix, iri);
058: }
059:
060: QNameProcessor visitor = new QNameProcessor(prefixMap);
061: try {
062: qc.jjtAccept(visitor, null);
063: } catch (VisitorException e) {
064: throw new MalformedQueryException(e);
065: }
066:
067: return prefixMap;
068: }
069:
070: private static class QNameProcessor extends ASTVisitorBase {
071:
072: private Map<String, String> prefixMap;
073:
074: public QNameProcessor(Map<String, String> prefixMap) {
075: this .prefixMap = prefixMap;
076: }
077:
078: @Override
079: public Object visit(ASTQName qnameNode, Object data)
080: throws VisitorException {
081: String qname = qnameNode.getValue();
082:
083: int colonIdx = qname.indexOf(':');
084: assert colonIdx >= 0 : "colonIdx should be >= 0: "
085: + colonIdx;
086:
087: String prefix = qname.substring(0, colonIdx);
088: String localName = qname.substring(colonIdx + 1);
089:
090: String namespace = prefixMap.get(prefix);
091: if (namespace == null) {
092: throw new VisitorException("QName '" + qname
093: + "' uses an undefined prefix");
094: }
095:
096: // Replace the qname node with a new IRI node in the parent node
097: ASTIRI iriNode = new ASTIRI(
098: SyntaxTreeBuilderTreeConstants.JJTIRI);
099: iriNode.setValue(namespace + localName);
100: qnameNode.jjtReplaceWith(iriNode);
101:
102: return null;
103: }
104: }
105: }
|