Skip to content

Commit e2db6a9

Browse files
committed
GH-4921 Turtle writer does not respect namespaces in IRIs
- SimpleValueFactory.createIRI(String, String) how actually properly respects the specified namespace and localname - TurtleWriter tries to use the namespace encoded in the IRI unless the localname contains characters which are not valid in prefixed notation - Added test
1 parent c259fdf commit e2db6a9

5 files changed

Lines changed: 58 additions & 4 deletions

File tree

core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,28 @@ protected SimpleIRI(String iriString) {
6767
setIRIString(iriString);
6868
}
6969

70+
protected SimpleIRI(String namespace, String localname) {
71+
setIRIString(namespace, localname);
72+
}
73+
7074
/*---------*
7175
* Methods *
7276
*---------*/
7377

78+
protected void setIRIString(String namespace, String localname) {
79+
Objects.requireNonNull(namespace, "namespace must not be null");
80+
Objects.requireNonNull(localname, "localname must not be null");
81+
82+
String joinedIriString = namespace + localname;
83+
84+
if (joinedIriString.indexOf(':') < 0) {
85+
throw new IllegalArgumentException("Not a valid (absolute) IRI: " + joinedIriString);
86+
}
87+
88+
this.iriString = joinedIriString;
89+
this.localNameIdx = namespace.length();
90+
}
91+
7492
protected void setIRIString(String iriString) {
7593
Objects.requireNonNull(iriString, "iriString must not be null");
7694

core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public IRI createIRI(String iri) {
8585

8686
@Override
8787
public IRI createIRI(String namespace, String localName) {
88-
return createIRI(namespace + localName);
88+
return new SimpleIRI(namespace, localName);
8989
}
9090

9191
@Override

core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleUtil.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,17 @@ public static String encodeURIString(String s) {
459459
return s;
460460
}
461461

462+
public static boolean isValidPrefixedName(String s) {
463+
final int numberOfCodePoints = s.codePointCount(0, s.length());
464+
for (int i = 1; i < numberOfCodePoints; i++) {
465+
final int codePoint = s.codePointAt(i);
466+
if (!isPN_CHARS(codePoint)) {
467+
return false;
468+
}
469+
}
470+
return true;
471+
}
472+
462473
/**
463474
* Decodes an encoded Turtle string. Any \-escape sequences are substituted with their decoded value.
464475
*

core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriter.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -537,11 +537,20 @@ protected void writeResource(Resource res, boolean canShorten) throws IOExceptio
537537
}
538538

539539
protected void writeURI(IRI uri) throws IOException {
540-
String uriString = uri.toString();
541-
542-
// Try to find a prefix for the URI's namespace
543540
String prefix = null;
541+
if (TurtleUtil.isValidPrefixedName(uri.getLocalName())) {
542+
prefix = namespaceTable.get(uri.getNamespace());
543+
if (prefix != null) {
544+
// Namespace is mapped to a prefix; write abbreviated URI
545+
writer.write(prefix);
546+
writer.write(":");
547+
writer.write(uri.getLocalName());
548+
return;
549+
}
550+
}
544551

552+
// Try to find a prefix for the URI's namespace
553+
String uriString = uri.toString();
545554
int splitIdx = TurtleUtil.findURISplitIndex(uriString);
546555
if (splitIdx > 0) {
547556
String namespace = uriString.substring(0, splitIdx);

core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleWriterTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
*******************************************************************************/
1111
package org.eclipse.rdf4j.rio.turtle;
1212

13+
import static org.assertj.core.api.Assertions.assertThat;
1314
import static org.junit.jupiter.api.Assertions.assertTrue;
1415

1516
import java.io.StringReader;
1617
import java.io.StringWriter;
1718

1819
import org.eclipse.rdf4j.model.IRI;
1920
import org.eclipse.rdf4j.model.Model;
21+
import org.eclipse.rdf4j.model.impl.DynamicModelFactory;
2022
import org.eclipse.rdf4j.model.util.Models;
2123
import org.eclipse.rdf4j.rio.RDFFormat;
2224
import org.eclipse.rdf4j.rio.Rio;
@@ -672,6 +674,20 @@ public void testBlankNodeInlining_indirectCircularReferenceWithIRI() throws Exce
672674
assertTrue(Models.isomorphic(expected, actual));
673675
}
674676

677+
@Test
678+
public void testIriNamespace() throws Exception {
679+
Model model = new DynamicModelFactory().createEmptyModel();
680+
String prefix = "foo-bar";
681+
String ns = "foo:this.is.my.bar.";
682+
model.setNamespace(prefix, ns);
683+
model.add(vf.createIRI(ns, "lala"), vf.createIRI(ns, "lulu"), vf.createIRI(ns, "lolo"));
684+
685+
StringWriter stringWriter = new StringWriter();
686+
Rio.write(model, stringWriter, RDFFormat.TURTLE);
687+
688+
assertThat(stringWriter.toString()).contains("foo-bar:lala foo-bar:lulu foo-bar:lolo .");
689+
}
690+
675691
@Test
676692
public void testIgnoreAbbreviateNumbers() throws Exception {
677693
StringWriter sw = new StringWriter();

0 commit comments

Comments
 (0)