Skip to content

Commit 10e9244

Browse files
committed
GH-4921 Turtle writer does not respect namespaces in IRIs
- Fix RDFXML test by checking if a localname preference indicated by the IRI is valid in XML - if not, try to find an alternative representation
1 parent 0e584a4 commit 10e9244

3 files changed

Lines changed: 45 additions & 9 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ protected void setIRIString(String namespace, String localname) {
7979
Objects.requireNonNull(namespace, "namespace must not be null");
8080
Objects.requireNonNull(localname, "localname must not be null");
8181

82-
String joinedIriString = namespace + localname;
82+
var joinedIriString = namespace + localname;
8383

8484
if (joinedIriString.indexOf(':') < 0) {
8585
throw new IllegalArgumentException("Not a valid (absolute) IRI: " + joinedIriString);

core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLWriter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public Writer getWriter() {
106106
return writer;
107107
}
108108

109+
@Override
109110
public Collection<RioSetting<?>> getSupportedSettings() {
110111
final Collection<RioSetting<?>> settings = new HashSet<>(super.getSupportedSettings());
111112
settings.add(BasicWriterSettings.BASE_DIRECTIVE);

core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/util/RDFXMLPrettyWriter.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
import java.util.Collection;
1919
import java.util.HashSet;
2020
import java.util.Stack;
21+
import java.util.regex.Pattern;
2122

2223
import org.eclipse.rdf4j.common.net.ParsedIRI;
24+
import org.eclipse.rdf4j.common.xml.XMLUtil;
2325
import org.eclipse.rdf4j.model.BNode;
2426
import org.eclipse.rdf4j.model.IRI;
2527
import org.eclipse.rdf4j.model.Literal;
@@ -260,8 +262,9 @@ private void popStacks(Resource newSubject) throws IOException, RDFHandlerExcept
260262
writeIndents(i * 2 - 1);
261263

262264
IRI predicate = predicateStack.get(i - 1);
265+
var predicateQName = new QName(predicate);
263266

264-
writeStartTag(predicate.getNamespace(), predicate.getLocalName());
267+
writeStartTag(predicateQName.getNamespace(), predicateQName.getLocalName());
265268
writeNewLine();
266269
}
267270

@@ -281,6 +284,7 @@ private void popStacks(Resource newSubject) throws IOException, RDFHandlerExcept
281284
writeNewLine();
282285
} else {
283286
IRI topPredicate = predicateStack.pop();
287+
var topPredicateQName = new QName(topPredicate);
284288

285289
if (!topNode.hasType()) {
286290
// we can use an abbreviated predicate
@@ -291,7 +295,7 @@ private void popStacks(Resource newSubject) throws IOException, RDFHandlerExcept
291295
// written out as well
292296

293297
writeIndents(nodeStack.size() * 2 - 1);
294-
writeStartTag(topPredicate.getNamespace(), topPredicate.getLocalName());
298+
writeStartTag(topPredicateQName.getNamespace(), topPredicateQName.getLocalName());
295299
writeNewLine();
296300

297301
// write out an empty subject
@@ -300,7 +304,7 @@ private void popStacks(Resource newSubject) throws IOException, RDFHandlerExcept
300304
writeNewLine();
301305

302306
writeIndents(nodeStack.size() * 2 - 1);
303-
writeEndTag(topPredicate.getNamespace(), topPredicate.getLocalName());
307+
writeEndTag(topPredicateQName.getNamespace(), topPredicateQName.getLocalName());
304308
writeNewLine();
305309
}
306310
}
@@ -322,10 +326,11 @@ private void popStacks(Resource newSubject) throws IOException, RDFHandlerExcept
322326

323327
if (predicateStack.size() > 0) {
324328
IRI nextPredicate = predicateStack.pop();
329+
var nextPredicateQName = new QName(nextPredicate);
325330

326331
writeIndents(predicateStack.size() + nodeStack.size());
327332

328-
writeEndTag(nextPredicate.getNamespace(), nextPredicate.getLocalName());
333+
writeEndTag(nextPredicateQName.getNamespace(), nextPredicateQName.getLocalName());
329334

330335
writeNewLine();
331336
}
@@ -392,7 +397,8 @@ private void writeNodeStartOfStartTag(Node node) throws IOException, RDFHandlerE
392397

393398
if (node.hasType()) {
394399
// We can use abbreviated syntax
395-
writeStartOfStartTag(node.getType().getNamespace(), node.getType().getLocalName());
400+
var nodeTypeQName = new QName(node.getType());
401+
writeStartOfStartTag(nodeTypeQName.getNamespace(), nodeTypeQName.getLocalName());
396402
} else {
397403
// We cannot use abbreviated syntax
398404
writeStartOfStartTag(RDF.NAMESPACE, "Description");
@@ -423,7 +429,8 @@ private void writeNodeStartTag(Node node) throws IOException, RDFHandlerExceptio
423429
*/
424430
private void writeNodeEndTag(Node node) throws IOException {
425431
if (node.getType() != null) {
426-
writeEndTag(node.getType().getNamespace(), node.getType().getLocalName());
432+
var nodeTypeQName = new QName(node.getType());
433+
writeEndTag(nodeTypeQName.getNamespace(), nodeTypeQName.getLocalName());
427434
} else {
428435
writeEndTag(RDF.NAMESPACE, "Description");
429436
}
@@ -442,7 +449,8 @@ private void writeNodeEmptyTag(Node node) throws IOException, RDFHandlerExceptio
442449
* Write out an empty property element.
443450
*/
444451
private void writeAbbreviatedPredicate(IRI pred, Value obj) throws IOException, RDFHandlerException {
445-
writeStartOfStartTag(pred.getNamespace(), pred.getLocalName());
452+
var predQName = new QName(pred);
453+
writeStartOfStartTag(predQName.getNamespace(), predQName.getLocalName());
446454

447455
if (obj instanceof Resource) {
448456
Resource objRes = (Resource) obj;
@@ -484,7 +492,7 @@ private void writeAbbreviatedPredicate(IRI pred, Value obj) throws IOException,
484492
writeCharacterData(objLit.getLabel());
485493
}
486494

487-
writeEndTag(pred.getNamespace(), pred.getLocalName());
495+
writeEndTag(predQName.getNamespace(), predQName.getLocalName());
488496
}
489497

490498
writeNewLine();
@@ -565,4 +573,31 @@ public boolean isWritten() {
565573
return isWritten;
566574
}
567575
}
576+
577+
private static class QName {
578+
private static final Pattern VALID_XML_ELEMENT_NAME = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_\\-\\.]*");
579+
580+
private final String namespace;
581+
private final String localName;
582+
583+
public QName(IRI resource) {
584+
if (!VALID_XML_ELEMENT_NAME.matcher(resource.getLocalName()).matches()) {
585+
var iriString = resource.getNamespace() + resource.getLocalName();
586+
var sep = XMLUtil.findURISplitIndex(iriString);
587+
namespace = iriString.substring(0, sep);
588+
localName = iriString.substring(sep);
589+
} else {
590+
localName = resource.getLocalName();
591+
namespace = resource.getNamespace();
592+
}
593+
}
594+
595+
public String getLocalName() {
596+
return localName;
597+
}
598+
599+
public String getNamespace() {
600+
return namespace;
601+
}
602+
}
568603
}

0 commit comments

Comments
 (0)