From a239ef381a9c240d23f77e0792c2781265828838 Mon Sep 17 00:00:00 2001 From: Ostrzyciel Date: Thu, 8 May 2025 15:07:18 +0200 Subject: [PATCH 1/2] rdf validate: allow quoted triples in S position Closes: #107 This was not allowed by mistake. I've added a regression test for this. --- .../jelly/cli/util/jena/StatementUtils.scala | 8 +- .../cli/command/rdf/RdfValidateSpec.scala | 86 ++++++++++++++++++- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/main/scala/eu/neverblink/jelly/cli/util/jena/StatementUtils.scala b/src/main/scala/eu/neverblink/jelly/cli/util/jena/StatementUtils.scala index c571da4..84ce8c6 100644 --- a/src/main/scala/eu/neverblink/jelly/cli/util/jena/StatementUtils.scala +++ b/src/main/scala/eu/neverblink/jelly/cli/util/jena/StatementUtils.scala @@ -12,11 +12,13 @@ object StatementUtils: q.getSubject :: q.getPredicate :: q.getObject :: q.getGraph :: Nil def isGeneralized(t: Triple): Boolean = - (!t.getSubject.isBlank && !t.getSubject.isURI) || !t.getPredicate.isURI + (!t.getSubject.isBlank && !t.getSubject.isURI && !t.getObject.isNodeTriple) + || !t.getPredicate.isURI def isGeneralized(q: Quad): Boolean = - (!q.getSubject.isBlank && !q.getSubject.isURI) || !q.getPredicate.isURI || - (!q.getGraph.isBlank && !q.getGraph.isURI) + (!q.getSubject.isBlank && !q.getSubject.isURI && !q.getSubject.isNodeTriple) + || !q.getPredicate.isURI + || (!q.getGraph.isBlank && !q.getGraph.isURI) def isRdfStar(t: Triple): Boolean = iterateTerms(t).exists(_.isNodeTriple) diff --git a/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala b/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala index d5f7083..2dc8b55 100644 --- a/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala +++ b/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala @@ -1,10 +1,11 @@ package eu.neverblink.jelly.cli.command.rdf -import eu.neverblink.jelly.cli.{CriticalException, ExitException} import eu.neverblink.jelly.cli.command.helpers.TestFixtureHelper -import eu.ostrzyciel.jelly.core.RdfProtoDeserializationError -import eu.ostrzyciel.jelly.core.JellyOptions +import eu.neverblink.jelly.cli.{CriticalException, ExitException} +import eu.ostrzyciel.jelly.convert.jena.JenaConverterFactory import eu.ostrzyciel.jelly.core.proto.v1.* +import eu.ostrzyciel.jelly.core.{JellyOptions, ProtoEncoder, RdfProtoDeserializationError} +import org.apache.jena.graph.{NodeFactory, Triple} import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @@ -511,5 +512,84 @@ class RdfValidateSpec extends AnyWordSpec, Matchers, TestFixtureHelper: "Invalid format option: \"invalid\"", ) } + + "RDF-star triples in subject and object positions (generalized=false)" in { + val t = Triple.create( + NodeFactory.createTripleNode( + Triple.create( + NodeFactory.createBlankNode(), + NodeFactory.createURI("http://example.org/predicate"), + NodeFactory.createBlankNode(), + ), + ), + NodeFactory.createURI("http://example.org/predicate"), + NodeFactory.createTripleNode( + Triple.create( + NodeFactory.createBlankNode(), + NodeFactory.createURI("http://example.org/predicate"), + NodeFactory.createBlankNode(), + ), + ), + ) + val enc = JenaConverterFactory.encoder( + ProtoEncoder.Params( + JellyOptions.smallRdfStar.withPhysicalType(PhysicalStreamType.TRIPLES), + ), + ) + val rows = enc.addTripleStatement(t) + val f = RdfStreamFrame(rows.toSeq) + val is = ByteArrayInputStream(f.toByteArray) + RdfValidate.setStdIn(is) + val (out, err) = RdfValidate.runTestCommand(List("rdf", "validate")) + err shouldBe empty + } + + "not validate RDF-star triples in predicate position (generalized=false)" in { + val t = Triple.create( + NodeFactory.createBlankNode(), + NodeFactory.createTripleNode( + Triple.create( + NodeFactory.createBlankNode(), + NodeFactory.createURI("http://example.org/predicate"), + NodeFactory.createBlankNode(), + ), + ), + NodeFactory.createBlankNode(), + ) + val enc = JenaConverterFactory.encoder( + ProtoEncoder.Params( + JellyOptions.smallRdfStar.withPhysicalType(PhysicalStreamType.TRIPLES), + ), + ) + val rows = enc.addTripleStatement(t) + val f = RdfStreamFrame(rows.toSeq) + val is = ByteArrayInputStream(f.toByteArray) + RdfValidate.setStdIn(is) + val e = intercept[ExitException] { + RdfValidate.runTestCommand(List("rdf", "validate")) + } + e.cause.get shouldBe a[CriticalException] + e.cause.get.getMessage should include("Unexpected generalized triple in frame 0:") + } + + "RDF-star triples in S, P, and O positions (generalized=true)" in { + val quoted = NodeFactory.createTripleNode(Triple.create( + NodeFactory.createBlankNode(), + NodeFactory.createURI("http://example.org/predicate"), + NodeFactory.createBlankNode(), + )) + val t = Triple.create(quoted, quoted, quoted) + val enc = JenaConverterFactory.encoder( + ProtoEncoder.Params( + JellyOptions.smallAllFeatures.withPhysicalType(PhysicalStreamType.TRIPLES), + ), + ) + val rows = enc.addTripleStatement(t) + val f = RdfStreamFrame(rows.toSeq) + val is = ByteArrayInputStream(f.toByteArray) + RdfValidate.setStdIn(is) + val (out, err) = RdfValidate.runTestCommand(List("rdf", "validate")) + err shouldBe empty + } } } From 9b3211b3b655ef2100b234ca7c3800f1e079d1a9 Mon Sep 17 00:00:00 2001 From: Ostrzyciel Date: Thu, 8 May 2025 15:08:02 +0200 Subject: [PATCH 2/2] fixAll --- .../jelly/cli/command/rdf/RdfValidateSpec.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala b/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala index 2dc8b55..8c745fc 100644 --- a/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala +++ b/src/test/scala/eu/neverblink/jelly/cli/command/rdf/RdfValidateSpec.scala @@ -573,11 +573,13 @@ class RdfValidateSpec extends AnyWordSpec, Matchers, TestFixtureHelper: } "RDF-star triples in S, P, and O positions (generalized=true)" in { - val quoted = NodeFactory.createTripleNode(Triple.create( - NodeFactory.createBlankNode(), - NodeFactory.createURI("http://example.org/predicate"), - NodeFactory.createBlankNode(), - )) + val quoted = NodeFactory.createTripleNode( + Triple.create( + NodeFactory.createBlankNode(), + NodeFactory.createURI("http://example.org/predicate"), + NodeFactory.createBlankNode(), + ), + ) val t = Triple.create(quoted, quoted, quoted) val enc = JenaConverterFactory.encoder( ProtoEncoder.Params(