+ first SparqlToSparql transformation
authorEric Prud'hommeaux <eric@w3.org>
Sun, 31 Jan 2010 20:30:34 -0500
changeset 153 dae731526190
parent 152 bf251f29cfe0
child 154 87829cbc8e29
child 155 3dde18a5d198
+ first SparqlToSparql transformation
src/main/scala/SPARQL.scala
src/main/scala/SparqlToSparql.scala
src/test/scala/SparqlToSparqlTest.scala
--- a/src/main/scala/SPARQL.scala	Sun Jan 31 12:06:15 2010 -0500
+++ b/src/main/scala/SPARQL.scala	Sun Jan 31 20:30:34 2010 -0500
@@ -14,7 +14,7 @@
 import MyParsers._
 
 case class Select(attrs:SparqlAttributeList, gp:GraphPattern)
-case class Construct(pattern:TriplesBlock, gp:GraphPattern)
+case class Construct(head:TriplesBlock, gp:GraphPattern)
 case class SparqlAttributeList(attributelist:List[Var])
 
 sealed abstract class GraphPattern
--- a/src/main/scala/SparqlToSparql.scala	Sun Jan 31 12:06:15 2010 -0500
+++ b/src/main/scala/SparqlToSparql.scala	Sun Jan 31 20:30:34 2010 -0500
@@ -1,24 +1,105 @@
 /* SparqlToSparql: convert SPARQL queries to sound SQL queries.
  *
- * Please read from the bottom -- i.e. apply calls mapGraphPattern with the root
- * graph pattern. mapGraphPattern handles all the graph pattern types in SPARQL,
- * effectively peforming the Convert Graph Patterns step in SPARQL 1.0 12.2.1
- *   <http://www.w3.org/TR/rdf-sparql-query/#convertGraphPattern>
- * with the target semantics in SQL instead of SPARQL.
+
+ * 
  */
 
 package w3c.sw.sparql2sparql
 
 import scala.util.parsing.combinator._
-import java.net.URI
-import w3c.sw.sql
-import w3c.sw.sql.PrettySql.toPrettySql
 import w3c.sw.sparql
-import w3c.sw.util
 
 object SparqlToSparql {
+  def substituteTerm (changeMe:sparql.S, from:sparql.Term, to:sparql.Term):sparql.S = {
+    if (toTerm(changeMe) == from) to match {
+      case sparql.TermUri(u) => sparql.SUri(u)
+      case sparql.TermVar(v) => sparql.SVar(v)
+      case sparql.TermLit(l) => error("literal subject \"" + l + "\" not allowed in SPARQL")
+    }
+    else changeMe
+  }
+  def substituteTerm (changeMe:sparql.O, from:sparql.Term, to:sparql.Term):sparql.O = {
+    if (toTerm(changeMe) == from) to match {
+      case sparql.TermUri(u) => sparql.OUri(u)
+      case sparql.TermVar(v) => sparql.OVar(v)
+      case sparql.TermLit(l) => sparql.OLit(l)
+    }
+    else changeMe
+  }
+  def toTerm (term:sparql.S):sparql.Term = {
+    term match {
+      case sparql.SUri(u) => sparql.TermUri(u)
+      case sparql.SVar(v) => sparql.TermVar(v)
+    }
+  }
+  def toTerm (term:sparql.O):sparql.Term = {
+    term match {
+      case sparql.OUri(u) => sparql.TermUri(u)
+      case sparql.OVar(v) => sparql.TermVar(v)
+      case sparql.OLit(l) => sparql.TermLit(l)
+    }
+  }
+  def substitute (gp:sparql.GraphPattern, from:sparql.Term, to:sparql.Term) = {
+    gp match {
+      case sparql.TriplesBlock(triplepatterns) => sparql.TriplesBlock(
+	triplepatterns.map((tp) => {
+	  // println("sub(" + tp.o + ", " + from + ", " + to + ") => ")
+	  // println(substituteTerm(toTerm(tp.o), tp.o, from, to))
+	  sparql.TriplePattern(substituteTerm(tp.s, from, to),
+			       tp.p,
+			       substituteTerm(tp.o, from, to))
+	}
+      ))
+      case _ => error("not implemented" + gp)
+    }
+  }
+  case class HornRule (head:sparql.TriplePattern, gp:sparql.GraphPattern) {
+    override def toString = "{" + head + "} => {" + gp + "}"
+    def transform (tp:sparql.TriplePattern):sparql.GraphPattern = {
+      substitute(substitute(gp, toTerm(head.s), toTerm(tp.s)), toTerm(head.o), toTerm(tp.o))
+    }
+  }
+  case class RuleMap (rules:Map[sparql.Uri, HornRule]) {
+    def transform (tp:sparql.TriplePattern):sparql.GraphPattern = {
+      tp.p match {
+	case sparql.PUri(u) =>
+	  try { rules(u).transform(tp) } catch {
+	    case e:java.util.NoSuchElementException => sparql.TriplesBlock(List(tp))
+	  }
+	case _ => error("not implemented: " + tp.p)
+      }
+    }
+  }
+
+  def mapGraphPattern (gp:sparql.GraphPattern, ruleMap:RuleMap):sparql.GraphPattern = {
+    gp match {
+      case sparql.TriplesBlock(tps) => {
+	val l = tps.foldLeft(List[sparql.GraphPattern]())((s, tp) => s ++ Set(ruleMap.transform(tp)))
+	if (l.size > 1)
+	  sparql.TableConjunction(l)
+	else
+	  l(0)
+      }
+      case _ => error("not implemented: " + gp);
+    }
+  }
+
   def apply (query:sparql.Select, constructs:List[sparql.Construct]) : sparql.Select = {
-    query
+    val ruleMap = RuleMap({
+      constructs.foldLeft(Map[sparql.Uri, HornRule]())((m, rule) => {
+	rule.head.triplepatterns.foldLeft(m)((m, tp) => m + ({
+	  tp.p match {
+	    case sparql.PUri(u) => u -> HornRule(tp, rule.gp)
+	    case _ => error("not implemented: " + tp.p)
+	  }
+	}))
+      })
+    })
+    // println("ruleMap: " + ruleMap)
+    sparql.Select(
+      query.attrs,
+      mapGraphPattern(query.gp, ruleMap)
+    )
   }
 }
 
--- a/src/test/scala/SparqlToSparqlTest.scala	Sun Jan 31 12:06:15 2010 -0500
+++ b/src/test/scala/SparqlToSparqlTest.scala	Sun Jan 31 20:30:34 2010 -0500
@@ -14,7 +14,6 @@
  */
 class SparqlToSparqlTest extends FunSuite {
 
-  /* Disable turtle string-izing (SparqlToSparql parm 5) and return native format: */
   test("cat") {
     val sparqlParser = Sparql()
     val query = sparqlParser.parseAll(sparqlParser.select, """
@@ -33,4 +32,33 @@
     assert(transformed === expected)
   }
 
+  test("foaf:last_name") {
+    val sparqlParser = Sparql()
+    val query = sparqlParser.parseAll(sparqlParser.select, """
+PREFIX foaf : <http://xmlns.com/foaf/0.1/>
+PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
+SELECT ?emp {
+?emp  foaf:last_name    "Smith"^^xsd:string
 }
+""").get
+    val rule1 = sparqlParser.parseAll(sparqlParser.construct, """
+PREFIX foaf : <http://xmlns.com/foaf/0.1/>
+PREFIX empP : <http://hr.example/DB/Employee#>
+CONSTRUCT { ?who foaf:first_name ?fname .
+            ?who foaf:last_name  ?lname }
+    WHERE { ?who empP:firstName  ?fname .
+            ?who empP:lastName   ?lname }
+""").get
+    val transformed = SparqlToSparql(query, List(rule1))
+    val expected = sparqlParser.parseAll(sparqlParser.select, """
+PREFIX empP : <http://hr.example/DB/Employee#>
+PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
+SELECT ?emp {
+?emp  empP:firstName    ?fname .
+?emp  empP:lastName    "Smith"^^xsd:string
+}
+""").get
+    assert(transformed === expected)
+  }
+
+}