/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2006.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.query.parser.sparql;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.parser.sparql.ast.ASTBasicGraphPattern;
import org.openrdf.query.parser.sparql.ast.ASTBlankNode;
import org.openrdf.query.parser.sparql.ast.ASTBlankNodePropertyList;
import org.openrdf.query.parser.sparql.ast.ASTCollection;
import org.openrdf.query.parser.sparql.ast.ASTQueryContainer;
import org.openrdf.query.parser.sparql.ast.ASTVar;
import org.openrdf.query.parser.sparql.ast.SyntaxTreeBuilderTreeConstants;
import org.openrdf.query.parser.sparql.ast.VisitorException;
/**
* Processes blank nodes in the query body, replacing them with variables while
* retaining scope.
*
* @author Arjohn Kampman
*/
class BlankNodeVarProcessor extends ASTVisitorBase {
public static void process(ASTQueryContainer qc)
throws MalformedQueryException
{
try {
qc.jjtAccept(new BlankNodeToVarConverter(), null);
}
catch (VisitorException e) {
throw new MalformedQueryException(e);
}
}
/*-------------------------------------*
* Inner class BlankNodeToVarConverter *
*-------------------------------------*/
private static class BlankNodeToVarConverter extends ASTVisitorBase {
private int anonVarNo = 1;
private Map<String, String> conversionMap = new HashMap<String, String>();
private Set<String> usedBNodeIDs = new HashSet<String>();
private String createAnonVarName() {
return "-anon-" + anonVarNo++;
}
@Override
public Object visit(ASTBasicGraphPattern node, Object data)
throws VisitorException
{
// The same Blank node ID cannot be used across Graph Patterns
usedBNodeIDs.addAll(conversionMap.keySet());
// Blank nodes are scope to Basic Graph Patterns
conversionMap.clear();
return super.visit(node, data);
}
@Override
public Object visit(ASTBlankNode node, Object data)
throws VisitorException
{
String bnodeID = node.getID();
String varName = findVarName(bnodeID);
if (varName == null) {
varName = createAnonVarName();
if (bnodeID != null) {
conversionMap.put(bnodeID, varName);
}
}
ASTVar varNode = new ASTVar(SyntaxTreeBuilderTreeConstants.JJTVAR);
varNode.setName(varName);
varNode.setAnonymous(true);
node.jjtReplaceWith(varNode);
return super.visit(node, data);
}
private String findVarName(String bnodeID) throws VisitorException {
if (bnodeID == null)
return null;
String varName = conversionMap.get(bnodeID);
if (varName == null && usedBNodeIDs.contains(bnodeID))
throw new VisitorException(
"BNodeID already used in another scope: " + bnodeID);
return varName;
}
@Override
public Object visit(ASTBlankNodePropertyList node, Object data)
throws VisitorException
{
node.setVarName(createAnonVarName());
return super.visit(node, data);
}
@Override
public Object visit(ASTCollection node, Object data)
throws VisitorException
{
node.setVarName(createAnonVarName());
return super.visit(node, data);
}
}
}