package org.xenei.jdbc4sparql.sparql; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import com.hp.hpl.jena.graph.Node; import com.hp.hpl.jena.graph.NodeFactory; import com.hp.hpl.jena.graph.Triple; import com.hp.hpl.jena.rdf.model.AnonId; import com.hp.hpl.jena.sparql.core.PathBlock; import com.hp.hpl.jena.sparql.core.TriplePath; import com.hp.hpl.jena.sparql.core.Var; import com.hp.hpl.jena.sparql.expr.Expr; import com.hp.hpl.jena.sparql.expr.ExprVar; import com.hp.hpl.jena.sparql.syntax.Element; import com.hp.hpl.jena.sparql.syntax.ElementAssign; import com.hp.hpl.jena.sparql.syntax.ElementBind; import com.hp.hpl.jena.sparql.syntax.ElementData; import com.hp.hpl.jena.sparql.syntax.ElementDataset; import com.hp.hpl.jena.sparql.syntax.ElementExists; import com.hp.hpl.jena.sparql.syntax.ElementFilter; import com.hp.hpl.jena.sparql.syntax.ElementGroup; import com.hp.hpl.jena.sparql.syntax.ElementMinus; import com.hp.hpl.jena.sparql.syntax.ElementNamedGraph; import com.hp.hpl.jena.sparql.syntax.ElementNotExists; import com.hp.hpl.jena.sparql.syntax.ElementOptional; import com.hp.hpl.jena.sparql.syntax.ElementPathBlock; import com.hp.hpl.jena.sparql.syntax.ElementService; import com.hp.hpl.jena.sparql.syntax.ElementSubQuery; import com.hp.hpl.jena.sparql.syntax.ElementTriplesBlock; import com.hp.hpl.jena.sparql.syntax.ElementUnion; import com.hp.hpl.jena.sparql.syntax.ElementVisitor; /** * Class to renumber bNodes to be unique within the query. */ public class BnodeRenumber { private class ElementHandler implements ElementVisitor { BnodeRenumber.EType type; public BnodeRenumber.EType getType() { return type; } @Override public void visit(final ElementAssign el) { type = EType.Assign; } @Override public void visit(final ElementBind el) { type = EType.Bind; } @Override public void visit(final ElementData el) { type = EType.Data; } @Override public void visit(final ElementDataset el) { type = EType.Dataset; } @Override public void visit(final ElementExists el) { type = EType.Exists; } @Override public void visit(final ElementFilter el) { type = EType.Filter; } @Override public void visit(final ElementGroup el) { type = EType.Group; } @Override public void visit(final ElementMinus el) { type = EType.Minus; } @Override public void visit(final ElementNamedGraph el) { type = EType.NamedGraph; } @Override public void visit(final ElementNotExists el) { type = EType.NotExists; } @Override public void visit(final ElementOptional el) { type = EType.Optional; } @Override public void visit(final ElementPathBlock el) { type = EType.PathBlock; } @Override public void visit(final ElementService el) { type = EType.Service; } @Override public void visit(final ElementSubQuery el) { type = EType.SubQuery; } @Override public void visit(final ElementTriplesBlock el) { type = EType.TriplesBlock; } @Override public void visit(final ElementUnion el) { type = EType.Union; } } enum EType { Assign, Bind, Data, Dataset, Exists, Filter, Group, Minus, NamedGraph, NotExists, Optional, PathBlock, Service, SubQuery, TriplesBlock, Union } private int bnodeCount = 0; private final ElementHandler handler = new ElementHandler();; private Map<String, Node> renumberMap = new HashMap<String, Node>(); private final Stack<Map<String, Node>> renumberMapStack = new Stack<Map<String, Node>>(); private Node nextAnon() { final AnonId id = new AnonId("?" + bnodeCount); bnodeCount++; return NodeFactory.createAnon(id); } private Expr processExpr(final Expr expr) { if (expr.isVariable()) { final ExprVar ev = expr.getExprVar(); if (ev.asVar().isBlankNodeVar()) { return new ExprVar(nextAnon()); } } return expr; } private Node processNode(Node n) { if (n.isBlank()) { Node retval = renumberMap.get(n.getBlankNodeLabel()); if (retval == null) { retval = nextAnon(); renumberMap.put(n.getBlankNodeLabel(), retval); } return retval; } if (n.isVariable()) { n = processVar((Var) n); } return n; } private Var processVar(final Var var) { Var v = var; if (v.isBlankNodeVar()) { final String s = v.getVarName(); final AnonId id = new AnonId(s); Node n = renumberMap.get(id.getLabelString()); if (n == null) { n = nextAnon(); renumberMap.put(id.getLabelString(), n); } v = Var.alloc(n.getBlankNodeId().getLabelString()); } return v; } public Element renumber(final Element e) { Element retval = e; e.visit(handler); switch (handler.getType()) { case Assign: retval = renumberAssign((ElementAssign) e); break; case Bind: retval = renumberBind((ElementBind) e); break; case Data: retval = renumberData((ElementData) e); break; case Dataset: throw new IllegalArgumentException( "Dataset should not be used in parser"); case Exists: retval = new ElementExists( renumber(((ElementExists) e).getElement())); break; case Filter: retval = new ElementFilter( processExpr(((ElementFilter) e).getExpr())); break; case Group: retval = renumberGroup((ElementGroup) e); break; case Minus: retval = new ElementMinus( renumber(((ElementMinus) e).getMinusElement())); break; case NamedGraph: retval = renumberNamedGraph((ElementNamedGraph) e); break; case NotExists: retval = new ElementNotExists( renumber(((ElementNotExists) e).getElement())); break; case Optional: retval = new ElementOptional( renumber(((ElementOptional) e).getOptionalElement())); break; case PathBlock: retval = renumberPathBlock((ElementPathBlock) e); break; case Service: // default to returning e break; case SubQuery: // default to returning e break; case TriplesBlock: retval = renumberTriplesBlock((ElementTriplesBlock) e); break; case Union: retval = renumberUnion((ElementUnion) e); break; } return retval; } private ElementAssign renumberAssign(final ElementAssign e) { return new ElementAssign(processVar(e.getVar()), processExpr(e.getExpr())); } private ElementBind renumberBind(final ElementBind e) { return new ElementBind(processVar(e.getVar()), processExpr(e.getExpr())); } private ElementData renumberData(final ElementData e) { final List<Var> vars = e.getVars(); boolean foundBlank = false; for (int i = 0; i < vars.size(); i++) { if (vars.get(i).isBlankNodeVar()) { foundBlank = true; vars.set(i, processVar(vars.get(i))); } } ElementData retval = e; if (foundBlank) { retval = new ElementData(); for (final Var v : vars) { retval.add(v); } } return retval; } private ElementGroup renumberGroup(final ElementGroup e) { renumberMapStack.push(renumberMap); renumberMap = new HashMap<String, Node>(); final ElementGroup retval = new ElementGroup(); for (final Element el : e.getElements()) { retval.addElement(renumber(el)); } renumberMap = renumberMapStack.pop(); return retval; } private ElementNamedGraph renumberNamedGraph(final ElementNamedGraph e) { final Node n = e.getGraphNameNode(); if (n != null) { return new ElementNamedGraph(processNode(n), renumber(e.getElement())); } else { return new ElementNamedGraph(renumber(e.getElement())); } } private ElementPathBlock renumberPathBlock(final ElementPathBlock e) { final ElementPathBlock retval = new ElementPathBlock(); final PathBlock pb = e.getPattern(); for (final TriplePath tp : pb.getList()) { if (tp.isTriple()) { final Triple t = new Triple(processNode(tp.getSubject()), tp.getPredicate(), processNode(tp.getObject())); retval.addTriple(t); } else { final TriplePath tp2 = new TriplePath( processNode(tp.getSubject()), tp.getPath(), processNode(tp.getObject())); retval.addTriple(tp2); } } return retval; } private ElementTriplesBlock renumberTriplesBlock(final ElementTriplesBlock e) { final ElementTriplesBlock retval = new ElementTriplesBlock(); final Iterator<Triple> iter = e.patternElts(); while (iter.hasNext()) { final Triple tp = iter.next(); retval.addTriple(new Triple(processNode(tp.getSubject()), tp .getPredicate(), processNode(tp.getObject()))); } return retval; } private ElementUnion renumberUnion(final ElementUnion e) { final ElementUnion retval = new ElementUnion(); for (final Element el : e.getElements()) { retval.addElement(renumber(el)); } return retval; } }