This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/master by this push:
     new e87a872  JENA-1881: Support RDF* triple terms in SPARQL XML results
     new 7e71664  Merge pull request #742 from afs/jena-1881_sparql_xml
e87a872 is described below

commit e87a8729f7416f183f0cd82819ab2ec0b5ed9d56
Author: Andy Seaborne <[email protected]>
AuthorDate: Mon May 11 18:58:40 2020 +0100

    JENA-1881: Support RDF* triple terms in SPARQL XML results
---
 .../riot/resultset/rw/ResultSetWriterJSON.java     |   6 +-
 .../jena/riot/resultset/rw/ResultSetWriterXML.java | 135 ++++++++++++-----
 .../apache/jena/riot/resultset/rw/ResultsStAX.java | 168 +++++++++++++++------
 .../apache/jena/riot/resultset/rw/XMLResults.java  |  10 ++
 .../org/apache/jena/sparql/util/ModelUtils.java    |   3 +
 .../testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl  |   2 +-
 .../RDF-Star/SPARQL-star/sparql-star-basic-1.srj   |  25 ---
 .../RDF-Star/SPARQL-star/sparql-star-basic-1.srx   |  42 ++++++
 8 files changed, 275 insertions(+), 116 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
 
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
index 508df6e..e5f74e9 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
@@ -346,17 +346,17 @@ public class ResultSetWriterJSON implements 
ResultSetWriter {
 
             // ---
             print(out, quoteName(kSubject), ": ");
-            print(out, "  ");
+            print(out, " ");
 
             writeValue(out, triple.getSubject(), multiLineInnerValue);
             println(out, " ,");
 
-            print(out, quoteName(kPredicate), ": ");
+            print(out, quoteName(kProperty), ": ");
             writeValue(out, triple.getPredicate(), multiLineInnerValue);
             println(out, " ,");
 
             print(out, quoteName(kObject), ": ");
-            print(out, "   ");
+            print(out, "  ");
             writeValue(out, triple.getObject(), multiLineInnerValue);
             // End of triple object.
             decIndent(out);
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
 
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
index 92fd5c4..8dead39 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
@@ -24,12 +24,13 @@ import java.util.Objects;
 
 import org.apache.jena.atlas.io.IndentedWriter;
 import org.apache.jena.atlas.logging.Log;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Node_Triple;
+import org.apache.jena.graph.Triple;
 import org.apache.jena.query.ARQ;
 import org.apache.jena.query.QuerySolution;
 import org.apache.jena.query.ResultSet;
-import org.apache.jena.rdf.model.Literal;
 import org.apache.jena.rdf.model.RDFNode;
-import org.apache.jena.rdf.model.Resource;
 import org.apache.jena.rdf.model.impl.Util;
 import org.apache.jena.riot.out.NodeToLabel;
 import org.apache.jena.riot.resultset.ResultSetLang;
@@ -243,8 +244,8 @@ public class ResultSetWriterXML implements ResultSetWriter {
         }
 
         @Override
-        public void binding(String varName, RDFNode node) {
-            if ( node == null && !outputExplicitUnbound )
+        public void binding(String varName, RDFNode rdfNode) {
+            if ( rdfNode == null && !outputExplicitUnbound )
                 return;
 
             out.print("<");
@@ -253,15 +254,15 @@ public class ResultSetWriterXML implements 
ResultSetWriter {
             out.print(varName);
             out.println("\">");
             out.incIndent(INDENT);
-            printBindingValue(node);
+            printBindingValue(rdfNode);
             out.decIndent(INDENT);
             out.print("</");
             out.print(dfBinding);
             out.println(">");
         }
 
-        void printBindingValue(RDFNode node) {
-            if ( node == null ) {
+        private void printBindingValue(RDFNode rdfNode) {
+            if ( rdfNode == null ) {
                 // Unbound
                 out.print("<");
                 out.print(dfUnbound);
@@ -269,68 +270,122 @@ public class ResultSetWriterXML implements 
ResultSetWriter {
                 return;
             }
 
-            if ( node instanceof Literal ) {
-                printLiteral((Literal)node);
+            Node node = rdfNode.asNode();
+            printBindingValue(node);
+        }
+        
+        private void printBindingValue(Node node) {
+            if ( node == null )
+                return;
+        
+            if ( node.isLiteral() ) {
+                printLiteral(node);
                 return;
             }
 
-            if ( node instanceof Resource ) {
-                printResource((Resource)node);
+            if ( node.isURI() ) {
+                printURI(node);
+                return;
+            }
+            
+            if ( node.isBlank() ) {
+                printBlankNode(node);
+                return;
+            }
+            if ( node.isNodeTriple() ) {
+                printTripleTerm(node);
                 return;
             }
+            
+            if ( node.isNodeGraph() )
+                throw new UnsupportedOperationException("Graph terms");
 
-            Log.warn(this, "Unknown RDFNode type in result set: " + 
node.getClass());
+            Log.warn(this, "Unknown RDFNode type in result set: " + node);
         }
 
-        void printLiteral(Literal literal) {
+        private void printURI(Node nodeURI) {
+            String uri = nodeURI.getURI();
             out.print("<");
-            out.print(dfLiteral);
+            out.print(dfURI);
+            out.print(">");
+            out.print(xml_escape(uri));
+            out.print("</");
+            out.print(dfURI);
+            out.println(">");
+        }            
+            
+        private void printBlankNode(Node node) {
+            String label = bNodeMap.get(null, node);
+            // Comes with leading "_:"
+            label = label.substring(2);
+            out.print("<");
+            out.print(dfBNode);
+            out.print(">");
+            out.print(xml_escape(label));
+            out.print("</");
+            out.print(dfBNode);
+            out.println(">");
+        }
 
+        private void printLiteral(Node literal) {
+            out.print("<");
+            out.print(dfLiteral);
+        
             if ( Util.isLangString(literal) ) {
-                String lang = literal.getLanguage();
+                String lang = literal.getLiteralLanguage();
                 out.print(" xml:lang=\"");
-                out.print(literal.getLanguage());
+                out.print(lang);
                 out.print("\"");
             } else if ( !Util.isSimpleString(literal) ) {
                 // Datatype
                 // (RDF 1.1) not xsd:string nor rdf:langString.
                 // (RDF 1.0) any datatype.
-                String datatype = literal.getDatatypeURI();
+                String datatype = literal.getLiteralDatatypeURI();
                 out.print(" ");
                 out.print(dfAttrDatatype);
                 out.print("=\"");
                 out.print(datatype);
                 out.print("\"");
             }
-
+        
             out.print(">");
-            out.print(xml_escape(literal.getLexicalForm()));
+            out.print(xml_escape(literal.getLiteralLexicalForm()));
             out.print("</");
             out.print(dfLiteral);
             out.println(">");
         }
 
-        void printResource(Resource r) {
-            if ( r.isAnon() ) {
-                String label = bNodeMap.get(null, r.asNode());
-                // Comes with leading "_:"
-                label = label.substring(2);
-                out.print("<");
-                out.print(dfBNode);
-                out.print(">");
-                out.print(xml_escape(label));
-                out.print("</");
-                out.print(dfBNode);
-                out.println(">");
-            } else {
-                out.print("<");
-                out.print(dfURI);
-                out.print(">");
-                out.print(xml_escape(r.getURI()));
-                out.print("</");
-                out.print(dfURI);
-                out.println(">");
-            }
+        private void printTripleTerm(Node node) {
+            Triple triple = Node_Triple.triple(node);
+            openTag(dfTriple);
+            
+            // Subject
+            openTag(dfSubject);
+            printBindingValue(triple.getSubject());
+            closeTag(dfSubject);
+            // Property
+            openTag(dfProperty);
+            printBindingValue(triple.getPredicate());
+            closeTag(dfProperty);
+            // Object
+            openTag(dfObject);
+            printBindingValue(triple.getObject());
+            closeTag(dfObject);
+            
+            closeTag(dfTriple);
+        }
+        
+        private void openTag(String name) {
+            out.print("<");
+            out.print(name);
+            out.println(">");
+            out.incIndent();
+        }
+        private void closeTag(String name) {
+            out.decIndent();
+            out.print("</");
+            out.print(name);
+            out.println(">");
         }
 
         private static String xml_escape(String string) {
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java 
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java
index cb1949d..6407f53 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java
@@ -23,6 +23,7 @@ import java.io.Reader;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 import javax.xml.namespace.QName;
 import javax.xml.stream.XMLInputFactory;
@@ -53,7 +54,7 @@ import org.apache.jena.sparql.resultset.ResultSetException;
 import org.apache.jena.sparql.resultset.SPARQLResult;
 import org.apache.jena.sparql.util.Context;
 
-/** Public only for use by XMLOuptu (legacy) */
+/** Public only for use by XMLOutput (legacy) */
 public class ResultsStAX implements ResultSet, Closeable {
     public static SPARQLResult read(InputStream in, Model model, Context 
context) {
         XMLInputFactory xf = XMLInputFactory.newInstance() ;
@@ -90,7 +91,8 @@ public class ResultsStAX implements ResultSet, Closeable {
     private QuerySolution   current          = null;
     private XMLStreamReader parser           = null;
     private List<String>    variables        = new ArrayList<>();
-    private Binding         binding          = null;                           
                                       // Current
+    // Current binding/query solution.
+    private Binding         binding          = null;
     private boolean         inputGraphLabels = 
ARQ.isTrue(ARQ.inputGraphBNodeLabels);
 
     private final LabelToNode  bNodes;
@@ -386,53 +388,13 @@ public class ResultsStAX implements ResultSet, Closeable {
                         varName = parser.getAttributeValue(null, 
XMLResults.dfAttrVarName) ;
                         break ;
                     }
-                    // URI, literal, bNode, unbound.
-                    if ( isTag(tag, XMLResults.dfBNode) ) {
-                        String label = parser.getElementText() ;
-                        Node node = null ;
-                        // if ( inputGraphLabels.getValue() )
-                        if ( inputGraphLabels )
-                            node = NodeFactory.createBlankNode(label) ;
-                        else
-                            node = bNodes.get(null, label);
-                        addBinding(binding, Var.alloc(varName), node) ;
-                        break ;
-                    }
-
-                    if ( isTag(tag, XMLResults.dfLiteral) ) {
-                        String datatype = parser.getAttributeValue(null, 
XMLResults.dfAttrDatatype) ;
-
-                        // String langTag = parser.getAttributeValue(null,
-                        // "lang") ;
-
-                        // Woodstox needs XML_NS despite the javadoc of StAX
-                        // "If the namespaceURI is null the namespace is not 
checked for equality"
-                        // StAX(.codehaus.org) copes both ways round
-                        String langTag = parser.getAttributeValue(XML_NS, 
"lang") ;
-
-                        // Works for XML literals (returning them as a
-                        // string)
-                        String text = parser.getElementText() ;
-
-                        RDFDatatype dType = null ;
-                        if ( datatype != null )
-                            dType = 
TypeMapper.getInstance().getSafeTypeByName(datatype) ;
-
-                        Node n = NodeFactory.createLiteral(text, langTag, 
dType) ;
+                    
+                    Node value = parseOneTerm(tag);
+                    if ( value != null ) {
                         if ( varName == null )
                             throw new ResultSetException("No name for 
variable") ;
-                        addBinding(binding, Var.alloc(varName), n) ;
-                        break ;
-                    }
-
-                    if ( isTag(tag, XMLResults.dfUnbound) ) {
-                        break ;
-                    }
-                    if ( isTag(tag, XMLResults.dfURI) ) {
-                        String uri = parser.getElementText() ;
-                        Node node = NodeFactory.createURI(uri) ;
-                        addBinding(binding, Var.alloc(varName), node) ;
-                        break ;
+                        addBinding(binding, Var.alloc(varName), value) ;
+                        break;
                     }
                     break ;
                 default :
@@ -442,6 +404,118 @@ public class ResultsStAX implements ResultSet, Closeable {
         return null ;
     }
 
+    private Node parseOneTerm(String tag) throws XMLStreamException {
+      // URI, literal, bNode, unbound, triple
+      if ( isTag(tag, XMLResults.dfBNode) ) {
+          String label = parser.getElementText() ;
+          Node node = null ;
+          // if ( inputGraphLabels.getValue() )
+          if ( inputGraphLabels )
+              node = NodeFactory.createBlankNode(label) ;
+          else
+              node = bNodes.get(null, label);
+          return node;
+      }
+
+      if ( isTag(tag, XMLResults.dfLiteral) ) {
+          String datatype = parser.getAttributeValue(null, 
XMLResults.dfAttrDatatype) ;
+
+          // String langTag = parser.getAttributeValue(null, "lang") ;
+          // Woodstox needs XML_NS despite the javadoc of StAX
+          // "If the namespaceURI is null the namespace is not checked for 
equality"
+          // StAX(.codehaus.org) copes both ways round
+          String langTag = parser.getAttributeValue(XML_NS, "lang") ;
+
+          // Works for XML literals (returning them as a string)
+          String text = parser.getElementText() ;
+
+          RDFDatatype dType = null ;
+          if ( datatype != null )
+              dType = TypeMapper.getInstance().getSafeTypeByName(datatype) ;
+
+          Node n = NodeFactory.createLiteral(text, langTag, dType) ;
+          return n ;
+      }
+
+      if ( isTag(tag, XMLResults.dfUnbound) ) {
+          return null;
+      }
+      
+      if ( isTag(tag, XMLResults.dfURI) ) {
+          String uri = parser.getElementText() ;
+          Node node = NodeFactory.createURI(uri) ;
+          return node;
+      }
+      
+      if ( isTag(tag, XMLResults.dfTriple) ) {
+          // <triple>
+          Node s = null;
+          Node p = null;
+          Node o = null;
+          while (parser.hasNext()) {
+              // Skip CHARACTERS
+              int event2 = parser.nextTag() ;
+              if ( event2 == XMLStreamConstants.END_ELEMENT ) {
+                  // Early end.
+                  String tagx = parser.getLocalName() ;
+                  if ( tagx.equals(XMLResults.dfTriple) )
+                      staxError("Incomplete triple term");
+                  else
+                      staxError("Mismatched tag: "+tagx);
+              }
+              //XMLStreamConstants.START_ELEMENT
+                  
+              String tag2 = parser.getLocalName() ;
+              // <subject> <property> <object>
+              // One of subject, property, object (s,p,o)
+              
+              if ( ! isOneOf(tag2, XMLResults.dfSubject, 
XMLResults.dfProperty, XMLResults.dfPredicate, XMLResults.dfObject,
+                                   XMLResults.dfSubjectAlt, 
XMLResults.dfPropertyAlt, XMLResults.dfObjectAlt) )
+                  staxError("Unexpected tag in triple term: "+tag2);
+              
+              int event3 = parser.nextTag() ;
+              String tag3 = parser.getLocalName() ;
+              Node x = parseOneTerm(tag3);
+              
+              // Read end </subject> etc.
+              parser.nextTag() ;
+
+              // Check for double assignment.
+              if ( isOneOf(tag2, XMLResults.dfSubject, 
XMLResults.dfSubjectAlt) ) 
+                  s = x ;
+              else if ( isOneOf(tag2, XMLResults.dfProperty, 
XMLResults.dfPredicate, XMLResults.dfPropertyAlt) )
+                  p = x ;
+              else if ( isOneOf(tag2, XMLResults.dfObject, 
XMLResults.dfObjectAlt) )
+                  o = x ;
+              if ( s != null && p != null && o != null ) {
+                  int event4 = parser.nextTag() ;
+                  String tagx = parser.getLocalName() ;
+                  if ( event4 == XMLStreamConstants.START_ELEMENT )
+                      staxError("Too many terms for a triple");
+                  // XMLStreamConstants.END_ELEMENT )
+                  if ( ! tagx.equals(XMLResults.dfTriple) )
+                      staxError("Expecting </triple>: "+tagx);
+                  // </triple>
+                  break;
+              }
+          }
+          
+          if ( s == null || p == null || o == null )
+              staxError("Bad <triple> term");
+          Node node = NodeFactory.createTripleNode(s, p, o);
+          return node;
+      }
+        return null;
+    }
+
+    static private boolean isOneOf (String string, String...values) {
+        for ( String v : values ) {
+            if ( Objects.equals(string, v) )
+                return true;
+        }
+        return false;
+    }
+    
     static protected void addBinding(BindingMap binding, Var var, Node value) {
         Node n = binding.get(var);
         if ( n != null ) {
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java 
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java
index ae0c423..ff7565f 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java
@@ -43,6 +43,16 @@ public interface XMLResults
     public static final String dfBNode      = "bnode" ;
     public static final String dfURI        = "uri" ;
     public static final String dfLiteral    = "literal" ;
+    public static final String dfTriple     = "triple" ;
+    public static final String dfSubject    = "subject" ;
+    public static final String dfProperty   = "property" ;
+    public static final String dfObject     = "object" ;
+
+    // Alternatives
+    public static final String dfPredicate   = "predicate" ;
+    public static final String dfSubjectAlt  = "s" ;
+    public static final String dfPropertyAlt = "p" ;
+    public static final String dfObjectAlt   = "o" ;
     
     public static final String dfUnbound    = "unbound" ;
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java
index 9c4b0c5..8e351c2 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java
@@ -60,6 +60,9 @@ public class ModelUtils
                 
         if ( node.isURI() || node.isBlank() )
             return new ResourceImpl(node, null) ;
+
+        if ( node.isNodeTriple() )
+            return new ResourceImpl(node, null) ;
         
         throw new ARQInternalErrorException("Unknown node type for node: 
"+node) ;
     }
diff --git a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl 
b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl
index 04363de..55703b1 100644
--- a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl
+++ b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl
@@ -113,7 +113,7 @@ PREFIX :       <#>
     mf:action
         [ qt:query  <sparql-star-basic-1.rq> ;
           qt:data   <data1.ttl> ] ;
-    mf:result  <sparql-star-basic-1.srj>
+    mf:result  <sparql-star-basic-1.srx>
 .
 
 :sparql-star-basic-2
diff --git a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srj 
b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srj
deleted file mode 100644
index 067edb2..0000000
--- a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srj
+++ /dev/null
@@ -1,25 +0,0 @@
-{ "head": {
-    "vars": [ "s" , "p" , "o" ]
-  } ,
-  "results": {
-    "bindings": [
-      { 
-        "s": {
-          "type": "triple" , 
-          "value": {
-            "subject":   { "type": "uri" , "value": "http://example/s"; } ,
-            "predicate": { "type": "uri" , "value": "http://example/p"; } ,
-            "object":    { "type": "uri" , "value": "http://example/o"; }
-          }
-        } ,
-        "p": { "type": "uri" , "value": "http://example/q"; } ,
-        "o": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#integer"; , "value": "123" }
-      } ,
-      { 
-        "s": { "type": "uri" , "value": "http://example/s"; } ,
-        "p": { "type": "uri" , "value": "http://example/p"; } ,
-        "o": { "type": "uri" , "value": "http://example/o"; }
-      }
-    ]
-  }
-}
diff --git a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srx 
b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srx
new file mode 100644
index 0000000..d52ff7d
--- /dev/null
+++ b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srx
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#";>
+  <head>
+    <variable name="s"/>
+    <variable name="p"/>
+    <variable name="o"/>
+  </head>
+  <results>
+    <result>
+      <binding name="s">
+        <triple>
+          <subject>
+            <uri>http://example/s</uri>
+          </subject>
+          <property>
+            <uri>http://example/p</uri>
+          </property>
+          <object>
+            <uri>http://example/o</uri>
+          </object>
+        </triple>
+      </binding>
+      <binding name="p">
+        <uri>http://example/q</uri>
+      </binding>
+      <binding name="o">
+        <literal 
datatype="http://www.w3.org/2001/XMLSchema#integer";>123</literal>
+      </binding>
+    </result>
+    <result>
+      <binding name="s">
+        <uri>http://example/s</uri>
+      </binding>
+      <binding name="p">
+        <uri>http://example/p</uri>
+      </binding>
+      <binding name="o">
+        <uri>http://example/o</uri>
+      </binding>
+    </result>
+  </results>
+</sparql>

Reply via email to