Search code examples
javasparqljenatopbraid-composerspin-rdf

Convertering between textual SPARQL syntax and the SPIN RDF Vocabulary: How to add rdfs:comment and sp:text


Using the SPIN API (http://topbraid.org/spin/api/) and working from the example code at https://github.com/spinrdf/spinrdf/blob/master/src-examples/org/topbraid/spin/examples/SPINParsingExample.java I'm trying to add handling of rdfs:comment and spin:text to the example. Topbraid Composer Free Edition (TBC FE) does allow one comment per SPIN rule which is included in the RDF. TBC FE also optionally includes the SPIN SPARQL source code as an xsd:string value via the sp:text property. I'd like to do the same in an extended verion of the example and then transfer that over to my working code where I'd like to embed SPIN rule editing.

Here's my current extended version of the example code. Please ignore the logging warnings. Note that the comment I've inserted at the top of the example query (line 54) is silently dropped in the output (output also included below).

/*
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  See the NOTICE file distributed with this work for additional
 *  information regarding copyright ownership.
 */

package mil.disa.dso.spo.a2i.nsc.sharing2025.raaDemo;

import org.topbraid.spin.arq.ARQ2SPIN;
import org.topbraid.spin.arq.ARQFactory;
import org.topbraid.spin.model.Select;
import org.topbraid.spin.system.SPINModuleRegistry;

import org.apache.jena.query.Query;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.util.FileUtils;
import org.apache.jena.vocabulary.RDF;


/**
 * Converts between textual SPARQL representation and SPIN RDF model.
 * 
 * @author Holger Knublauch
 */
public class SPINParsingExample {

    /**
     * @param args
     */
    public static void main(String[] args) {

        // Register system functions (such as sp:gt (>))
        SPINModuleRegistry.get().init();

        // Create an empty OntModel importing SP
        Model model = ModelFactory.createDefaultModel();
        model.setNsPrefix("rdf", RDF.getURI());
        model.setNsPrefix("ex", "http://example.org/demo#");

        String query =
            "# This is an example SPARQL comment\n" +
            "SELECT ?person\n" +
            "WHERE {\n" +
            "    ?person a ex:Person .\n" +
            "    ?person ex:age ?age .\n" +
            "    FILTER (?age > 18) .\n" +
            "}";

        System.out.println("Original SPARQL query string:\n\n" + query);

        Query arqQuery = ARQFactory.get().createQuery(model, query);
        ARQ2SPIN arq2SPIN = new ARQ2SPIN(model);
        Select spinQuery = (Select) arq2SPIN.createQuery(arqQuery, null);

        // TODO what about the sp:text?  It's not in the artifacts printed below...
        // TODO figure out how to add a comment to the tokenized query... does not propagate from source string above
        //  perhaps this is through and addProperty call to add an rdfs:comment??  many calls, not clear how to use...
        //  get javadoc?

        System.out.println("\n-----");
        System.out.println("SPIN query in Turtle:\n");
        model.write(System.out, FileUtils.langTurtle);

        System.out.println("\n-----");
        System.out.println("SPIN query in XML:\n");
        model.write(System.out, FileUtils.langXML);

        System.out.println("\n-----");
        String str = spinQuery.toString();
        System.out.println("SPIN query:\n\n" + str);

        // Now turn it back into a Jena Query
        Query parsedBack = ARQFactory.get().createQuery(spinQuery);
        System.out.println("Jena query:\n" + parsedBack);
    }
}

Output from the above...

log4j:WARN No appenders could be found for logger (Jena).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Original SPARQL query string:

# This is an example SPARQL comment
SELECT ?person
WHERE {
    ?person a ex:Person .
    ?person ex:age ?age .
    FILTER (?age > 18) .
}

-----
SPIN query in Turtle:

@prefix ex:    <http://example.org/demo#> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sp:    <http://spinrdf.org/sp#> .

[ a                   sp:Select ;
  sp:resultVariables  ( [ sp:varName  "person" ]
                      ) ;
  sp:where            ( [ sp:object     ex:Person ;
                          sp:predicate  rdf:type ;
                          sp:subject    [ sp:varName  "person" ]
                        ]
                        [ sp:object     [ sp:varName  "age" ] ;
                          sp:predicate  ex:age ;
                          sp:subject    [ sp:varName  "person" ]
                        ]
                        [ a              sp:Filter ;
                          sp:expression  [ a        sp:gt ;
                                           sp:arg1  [ sp:varName  "age" ] ;
                                           sp:arg2  18
                                         ]
                        ]
                      )
] .

-----
SPIN query in XML:

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:ex="http://example.org/demo#"
    xmlns:sp="http://spinrdf.org/sp#">
  <sp:Select>
    <sp:resultVariables rdf:parseType="Collection">
      <rdf:Description>
        <sp:varName>person</sp:varName>
      </rdf:Description>
    </sp:resultVariables>
    <sp:where rdf:parseType="Collection">
      <rdf:Description>
        <sp:object rdf:resource="http://example.org/demo#Person"/>
        <sp:predicate rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"/>
        <sp:subject rdf:parseType="Resource">
          <sp:varName>person</sp:varName>
        </sp:subject>
      </rdf:Description>
      <rdf:Description>
        <sp:object rdf:parseType="Resource">
          <sp:varName>age</sp:varName>
        </sp:object>
        <sp:predicate rdf:resource="http://example.org/demo#age"/>
        <sp:subject rdf:parseType="Resource">
          <sp:varName>person</sp:varName>
        </sp:subject>
      </rdf:Description>
      <sp:Filter>
        <sp:expression>
          <sp:gt>
            <sp:arg2 rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
            >18</sp:arg2>
            <sp:arg1 rdf:parseType="Resource">
              <sp:varName>age</sp:varName>
            </sp:arg1>
          </sp:gt>
        </sp:expression>
      </sp:Filter>
    </sp:where>
  </sp:Select>
</rdf:RDF>

-----
SPIN query:

SELECT ?person
WHERE {
    ?person a ex:Person .
    ?person ex:age ?age .
    FILTER (?age > 18) .
}
Jena query:
SELECT  ?person
WHERE
  { ?person  a                     <http://example.org/demo#Person> ;
             <http://example.org/demo#age>  ?age
    FILTER ( ?age > 18 )
  }

I'm sure that there's a way to add an rdfs:comment and the source code (via sp:text) to the RDF, but I have not found it yet. I suspect that one can do both via calls to methods of the Select class via its spinQuery instantiation in the example (line 66), but I'm not sure how. Any pointers would be appreciated.

This question relates to my prior answered question How to turn a SPARQL/SPIN query/rule into an RDF structure from Java? but specializes on comments and source code in the RDF.


Solution

  • It seems you can add rdfs:comment only to the verbose rdf-form of query. It is not possible to pass rdfs:comment to sp:text because the last one is a predicate for string literal, you can do it only as part of string. It seems Topbraid allows commenting only through RDF-graph, and text-query exists only in GUI (as separated model). An advice: keep your queries in RDF-form, in this case you will have no possible problems with prefixes also.

    Example how to add rdfs:comment and sp:text:

    public static void main(String ... args) {
        Model model = ModelFactory.createDefaultModel();
        model.setNsPrefix("rdf", RDF.getURI());
        model.setNsPrefix("ex", "http://example.org/demo#");
        model.setNsPrefix("sp", SP.getURI());
        model.setNsPrefix("rdfs", RDFS.getURI());
    
        String query = "SELECT ?person\n" +
                        "WHERE {\n" +
                        "    ?person a ex:Person .\n" +
                        "    ?person ex:age ?age .\n" +
                        "    FILTER (?age > 18) .\n" +
                        "}";
        Query arqQuery = ARQFactory.get().createQuery(model, query);
        ARQ2SPIN arq2SPIN = new ARQ2SPIN(model);
        Select select1 = (Select) arq2SPIN.createQuery(arqQuery, null);
        select1.addProperty(RDFS.comment, "Comment1"); // <-- as part of rdf
    
        Resource anon = model.createResource();
        anon.addProperty(RDF.type, SP.Select);
        anon.addProperty(SP.text, model.createTypedLiteral(
                "# Comment2\n" + // <-- as part of string
                "SELECT ?person\n" +
                "WHERE {\n" +
                "    ?person a ex:Person .\n" +
                "    ?person ex:age ?age .\n" +
                "    FILTER (?age < 22) .\n" +
                "}"));
        Select select2 = anon.as(Select.class);
        System.out.println("========================");
        model.write(System.out, "ttl");
        System.out.println("========================");
        System.out.println("Select1:\n" + select1);
        System.out.println("Select2:\n" + select2);
    }
    

    And output:

    ========================
    @prefix ex:    <http://example.org/demo#> .
    @prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
    @prefix sp:    <http://spinrdf.org/sp#> .
    
    _:b0    a              sp:Filter ;
            sp:expression  [ a        sp:gt ;
                             sp:arg1  [ sp:varName  "age" ] ;
                             sp:arg2  18
                           ] .
    
    _:b1    sp:object     [ sp:varName  "age" ] ;
            sp:predicate  ex:age ;
            sp:subject    [ sp:varName  "person" ] .
    
    [ a                   sp:Select ;
      rdfs:comment        "Comment1" ;
      sp:resultVariables  ( _:b2 ) ;
      sp:where            ( _:b3 _:b1 _:b0 )
    ] .
    
    _:b3    sp:object     ex:Person ;
            sp:predicate  rdf:type ;
            sp:subject    [ sp:varName  "person" ] .
    
    _:b2    sp:varName  "person" .
    
    [ a        sp:Select ;
      sp:text  "# Comment2\nSELECT ?person\nWHERE {\n    ?person a ex:Person .\n    ?person ex:age ?age .\n    FILTER (?age < 22) .\n}"
    ] .
    ========================
    Select1:
    # Comment1
    SELECT ?person
    WHERE {
        ?person a ex:Person .
        ?person ex:age ?age .
        FILTER sp:gt(?age, 18) .
    }
    Select2:
    # Comment2
    SELECT ?person
    WHERE {
        ?person a ex:Person .
        ?person ex:age ?age .
        FILTER (?age < 22) .
    }