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.HashMap;
009: import java.util.HashSet;
010: import java.util.Map;
011: import java.util.Set;
012:
013: import org.openrdf.query.MalformedQueryException;
014: import org.openrdf.query.parser.sparql.ast.ASTBasicGraphPattern;
015: import org.openrdf.query.parser.sparql.ast.ASTBlankNode;
016: import org.openrdf.query.parser.sparql.ast.ASTBlankNodePropertyList;
017: import org.openrdf.query.parser.sparql.ast.ASTCollection;
018: import org.openrdf.query.parser.sparql.ast.ASTQueryContainer;
019: import org.openrdf.query.parser.sparql.ast.ASTVar;
020: import org.openrdf.query.parser.sparql.ast.SyntaxTreeBuilderTreeConstants;
021: import org.openrdf.query.parser.sparql.ast.VisitorException;
022:
023: /**
024: * Processes blank nodes in the query body, replacing them with variables while
025: * retaining scope.
026: *
027: * @author Arjohn Kampman
028: */
029: class BlankNodeVarProcessor extends ASTVisitorBase {
030:
031: public static void process(ASTQueryContainer qc)
032: throws MalformedQueryException {
033: try {
034: qc.jjtAccept(new BlankNodeToVarConverter(), null);
035: } catch (VisitorException e) {
036: throw new MalformedQueryException(e);
037: }
038: }
039:
040: /*-------------------------------------*
041: * Inner class BlankNodeToVarConverter *
042: *-------------------------------------*/
043:
044: private static class BlankNodeToVarConverter extends ASTVisitorBase {
045:
046: private int anonVarNo = 1;
047:
048: private Map<String, String> conversionMap = new HashMap<String, String>();
049:
050: private Set<String> usedBNodeIDs = new HashSet<String>();
051:
052: private String createAnonVarName() {
053: return "-anon-" + anonVarNo++;
054: }
055:
056: @Override
057: public Object visit(ASTBasicGraphPattern node, Object data)
058: throws VisitorException {
059: // The same Blank node ID cannot be used across Graph Patterns
060: usedBNodeIDs.addAll(conversionMap.keySet());
061:
062: // Blank nodes are scope to Basic Graph Patterns
063: conversionMap.clear();
064:
065: return super .visit(node, data);
066: }
067:
068: @Override
069: public Object visit(ASTBlankNode node, Object data)
070: throws VisitorException {
071: String bnodeID = node.getID();
072: String varName = findVarName(bnodeID);
073:
074: if (varName == null) {
075: varName = createAnonVarName();
076:
077: if (bnodeID != null) {
078: conversionMap.put(bnodeID, varName);
079: }
080: }
081:
082: ASTVar varNode = new ASTVar(
083: SyntaxTreeBuilderTreeConstants.JJTVAR);
084: varNode.setName(varName);
085: varNode.setAnonymous(true);
086:
087: node.jjtReplaceWith(varNode);
088:
089: return super .visit(node, data);
090: }
091:
092: private String findVarName(String bnodeID)
093: throws VisitorException {
094: if (bnodeID == null)
095: return null;
096: String varName = conversionMap.get(bnodeID);
097: if (varName == null && usedBNodeIDs.contains(bnodeID))
098: throw new VisitorException(
099: "BNodeID already used in another scope: "
100: + bnodeID);
101: return varName;
102: }
103:
104: @Override
105: public Object visit(ASTBlankNodePropertyList node, Object data)
106: throws VisitorException {
107: node.setVarName(createAnonVarName());
108: return super .visit(node, data);
109: }
110:
111: @Override
112: public Object visit(ASTCollection node, Object data)
113: throws VisitorException {
114: node.setVarName(createAnonVarName());
115: return super.visit(node, data);
116: }
117: }
118: }
|