getLanguage();
+ default BaseDirection getBaseDirection() {
+ return BaseDirection.NONE;
+ }
+
/**
* Gets the datatype for this literal.
*
diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java
index 00441731e57..0c13899133b 100644
--- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java
+++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java
@@ -86,6 +86,18 @@ public interface ValueFactory {
*/
Literal createLiteral(String label, String language);
+ /**
+ * Creates a new literal with the supplied label and language attribute. The return value of
+ * {@link Literal#getDatatype()} for the returned object must be
+ * {@code rdf:langString}.
+ *
+ * @param label The literal's label, must not be null.
+ * @param language The literal's language attribute, must not be null.
+ * @param baseDirection The literal's base direction, either "", "--ltr", or "--rtl".
+ * @return A literal for the specified value and language attribute.
+ */
+ Literal createLiteral(String label, String language, Literal.BaseDirection baseDirection);
+
/**
* Creates a new literal with the supplied label and datatype.
*
diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java
index 635c12a8847..d64ae697c1f 100644
--- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java
+++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java
@@ -77,11 +77,12 @@ public abstract class AbstractLiteral implements Literal {
private static final long serialVersionUID = -1286527360744086451L;
static boolean reserved(IRI datatype) {
- return CoreDatatype.RDF.LANGSTRING.getIri().equals(datatype);
+ return CoreDatatype.RDF.LANGSTRING.getIri().equals(datatype)
+ || CoreDatatype.RDF.DIRLANGSTRING.getIri().equals(datatype);
}
static boolean reserved(CoreDatatype datatype) {
- return CoreDatatype.RDF.LANGSTRING == datatype;
+ return CoreDatatype.RDF.LANGSTRING == datatype || CoreDatatype.RDF.DIRLANGSTRING == datatype;
}
/**
@@ -186,7 +187,7 @@ public String toString() {
return getLanguage()
- .map(language -> label + '@' + language)
+ .map(language -> label + '@' + language + getBaseDirection())
.orElseGet(() -> CoreDatatype.XSD.STRING == getCoreDatatype() ? label
: label + "^^<" + getDatatype().stringValue() + ">");
@@ -268,10 +269,16 @@ static class TaggedLiteral extends AbstractLiteral {
private final String label;
private final String language;
+ private final BaseDirection baseDirection;
TaggedLiteral(String label, String language) {
+ this(label, language, BaseDirection.NONE);
+ }
+
+ TaggedLiteral(String label, String language, BaseDirection baseDirection) {
this.label = label;
this.language = language;
+ this.baseDirection = baseDirection;
}
@Override
@@ -284,14 +291,20 @@ public Optional getLanguage() {
return Optional.of(language);
}
+ @Override
+ public BaseDirection getBaseDirection() {
+ return baseDirection;
+ }
+
@Override
public IRI getDatatype() {
- return CoreDatatype.RDF.LANGSTRING.getIri();
+ return baseDirection == BaseDirection.NONE ? CoreDatatype.RDF.LANGSTRING.getIri()
+ : CoreDatatype.RDF.DIRLANGSTRING.getIri();
}
@Override
public CoreDatatype.RDF getCoreDatatype() {
- return CoreDatatype.RDF.LANGSTRING;
+ return baseDirection == BaseDirection.NONE ? CoreDatatype.RDF.LANGSTRING : CoreDatatype.RDF.DIRLANGSTRING;
}
}
diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java
index e88070a5af3..4d5ba3f39a3 100644
--- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java
+++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java
@@ -146,15 +146,21 @@ public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDataty
@Override
public Literal createLiteral(String label, String language) {
+ return createLiteral(label, language, Literal.BaseDirection.NONE);
+ }
+
+ @Override
+ public Literal createLiteral(String label, String language, Literal.BaseDirection baseDirection) {
Objects.requireNonNull(label, "null label");
Objects.requireNonNull(language, "null language");
+ Objects.requireNonNull(baseDirection, "null baseDirection");
if (language.isEmpty()) {
throw new IllegalArgumentException("empty language tag");
}
- return new TaggedLiteral(label, language);
+ return new TaggedLiteral(label, language, baseDirection);
}
@Override
diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatype.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatype.java
index 50af84d1df3..c48be81ccfd 100644
--- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatype.java
+++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatype.java
@@ -283,7 +283,9 @@ public String toString() {
enum RDF implements CoreDatatype {
HTML(iri("HTML")),
+ JSON(iri("JSON")),
XMLLITERAL(iri("XMLLiteral")),
+ DIRLANGSTRING(iri("dirLangString")),
LANGSTRING(iri("langString"));
public static final String NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
diff --git a/core/model-api/src/test/java/org/eclipse/rdf4j/model/LiteralTest.java b/core/model-api/src/test/java/org/eclipse/rdf4j/model/LiteralTest.java
index be492efbef6..d7ddc088be3 100644
--- a/core/model-api/src/test/java/org/eclipse/rdf4j/model/LiteralTest.java
+++ b/core/model-api/src/test/java/org/eclipse/rdf4j/model/LiteralTest.java
@@ -85,6 +85,7 @@ public abstract class LiteralTest {
static final String XSD_DURATION_YEARMONTH = XSD + "yearMonthDuration";
static final String RDF_LANG_STRING = RDF + "langString";
+ static final String RDF_DIR_LANG_STRING = RDF + "dirLangString";
/**
* Creates a test literal instance.
@@ -103,6 +104,16 @@ public abstract class LiteralTest {
*/
protected abstract Literal literal(String label, String language);
+ /**
+ * Creates a test literal instance.
+ *
+ * @param label the label of the literal
+ * @param language the language of the literal
+ * @param dir the language direction of the literal
+ * @return a new instance of the concrete literal class under test
+ */
+ protected abstract Literal literal(String label, String language, Literal.BaseDirection dir);
+
/**
* Creates a test literal instance.
*
@@ -168,14 +179,24 @@ public final void testTaggedConstructor() {
final String label = "label";
final String language = "en";
- final Literal literal = literal(label, language);
+ final Literal languageLiteral = literal(label, language);
- assertThat(literal.getLabel()).isEqualTo(label);
- assertThat(literal.getLanguage()).contains(language);
- assertThat(literal.getDatatype().stringValue()).isEqualTo(RDF_LANG_STRING);
+ assertThat(languageLiteral.getLabel()).isEqualTo(label);
+ assertThat(languageLiteral.getLanguage()).contains(language);
+ assertEquals(Literal.BaseDirection.NONE, languageLiteral.getBaseDirection());
+ assertThat(languageLiteral.getDatatype().stringValue()).isEqualTo(RDF_LANG_STRING);
+
+ final Literal directedLanguageLiteral = literal(label, language, Literal.BaseDirection.LTR);
+
+ assertThat(directedLanguageLiteral.getLabel()).isEqualTo(label);
+ assertThat(directedLanguageLiteral.getLanguage()).contains(language);
+ assertThat(directedLanguageLiteral.getBaseDirection().toString()).isEqualTo(Literal.LTR_SUFFIX);
+ assertThat(directedLanguageLiteral.getDatatype().stringValue()).isEqualTo(RDF_DIR_LANG_STRING);
assertThatNullPointerException().isThrownBy(() -> literal(null, (String) null));
assertThatNullPointerException().isThrownBy(() -> literal("", (String) null));
+ assertThatNullPointerException().isThrownBy(() -> literal("", (String) null, Literal.BaseDirection.NONE));
+ assertThatNullPointerException().isThrownBy(() -> literal("", (String) null, Literal.BaseDirection.LTR));
assertThatNullPointerException().isThrownBy(() -> literal(null, ""));
assertThatNullPointerException().isThrownBy(() -> literal(null, (IRI) null));
@@ -198,8 +219,10 @@ public final void testTypedConstructor() {
assertThatNullPointerException().isThrownBy(() -> literal(null, (IRI) null));
assertThatNullPointerException().isThrownBy(() -> literal(null, datatype(XSD_STRING)));
assertThatNullPointerException().isThrownBy(() -> literal(null, datatype(RDF_LANG_STRING)));
+ assertThatNullPointerException().isThrownBy(() -> literal(null, datatype(RDF_DIR_LANG_STRING)));
assertThatIllegalArgumentException().isThrownBy(() -> literal("", datatype(RDF_LANG_STRING)));
+ assertThatIllegalArgumentException().isThrownBy(() -> literal("", datatype(RDF_DIR_LANG_STRING)));
}
@@ -763,10 +786,13 @@ public void testEqualsAndHashCode() {
final Literal plain = literal("plain");
final Literal tagged = literal("tagged", "en");
+ final Literal tagged_with_direction = literal("tagged", "en--ltr");
final Literal typed = literal("typed", datatype("http://example.org/datatype"));
final Literal _plain = literal(plain.getLabel());
final Literal _tagged = literal(tagged.getLabel(), tagged.getLanguage().orElse(""));
+ final Literal _tagged_with_direction = literal(tagged_with_direction.getLabel(),
+ tagged_with_direction.getLanguage().orElse(""));
final Literal _typed = literal(typed.getLabel(), typed.getDatatype());
assertThat(plain).isEqualTo(plain);
@@ -775,6 +801,9 @@ public void testEqualsAndHashCode() {
assertThat(tagged).isEqualTo(tagged);
assertThat(tagged).isEqualTo(_tagged);
+ assertThat(tagged_with_direction).isEqualTo(tagged_with_direction);
+ assertThat(tagged_with_direction).isEqualTo(_tagged_with_direction);
+
assertThat(typed).isEqualTo(typed);
assertThat(typed).isEqualTo(_typed);
@@ -784,16 +813,21 @@ public void testEqualsAndHashCode() {
assertThat(plain).isNotEqualTo(tagged);
assertThat(plain).isNotEqualTo(typed);
assertThat(tagged).isNotEqualTo(typed);
+ assertThat(tagged_with_direction).isNotEqualTo(plain);
+ assertThat(tagged_with_direction).isNotEqualTo(tagged);
+ assertThat(tagged_with_direction).isNotEqualTo(typed);
assertThat(plain).isNotEqualTo(literal("other"));
assertThat(tagged).isNotEqualTo(literal(tagged.getLabel(), "other"));
assertThat(typed).isNotEqualTo(literal(typed.getLabel(), datatype("http://example.org/other")));
+ assertThat(_tagged_with_direction).isNotEqualTo(literal(tagged_with_direction.getLabel(), "en--rtl"));
// hashCode() should return identical values for literals for which equals() is true
- assertThat(plain.hashCode()).isEqualTo(_plain.hashCode());
- assertThat(tagged.hashCode()).isEqualTo(_tagged.hashCode());
- assertThat(typed.hashCode()).isEqualTo(_typed.hashCode());
+ assertThat(plain).hasSameHashCodeAs(_plain);
+ assertThat(tagged).hasSameHashCodeAs(_tagged);
+ assertThat(tagged_with_direction).hasSameHashCodeAs(_tagged_with_direction);
+ assertThat(typed).hasSameHashCodeAs(_typed);
assertThat(tagged.hashCode())
.as("computed according to contract")
@@ -860,11 +894,20 @@ public final void testCoreDatatypeTaggedConstructor() {
String label = "label";
String language = "en";
- Literal literal = literal(label, language);
+ Literal languageLiteral = literal(label, language);
+ Literal directedLanguageLiteral = literal(label, language, Literal.BaseDirection.LTR);
- assertThat(literal.getLabel()).isEqualTo(label);
- assertThat(literal.getLanguage()).contains(language);
- assertThat(literal.getCoreDatatype()).isEqualTo(CoreDatatype.RDF.LANGSTRING);
+ assertThat(languageLiteral.getLabel()).isEqualTo(label);
+ assertThat(languageLiteral.getLanguage()).contains(language);
+ assertEquals(Literal.BaseDirection.NONE, languageLiteral.getBaseDirection());
+ assertThat(languageLiteral.getCoreDatatype()).isEqualTo(CoreDatatype.RDF.LANGSTRING);
+ assertEquals("\"label\"@en", languageLiteral.toString());
+
+ assertThat(directedLanguageLiteral.getLabel()).isEqualTo(label);
+ assertThat(directedLanguageLiteral.getLanguage()).contains(language);
+ assertEquals(Literal.BaseDirection.LTR, directedLanguageLiteral.getBaseDirection());
+ assertThat(directedLanguageLiteral.getCoreDatatype()).isEqualTo(CoreDatatype.RDF.DIRLANGSTRING);
+ assertEquals("\"label\"@en--ltr", directedLanguageLiteral.toString());
assertThatNullPointerException().isThrownBy(() -> literal(null, (String) null));
assertThatNullPointerException().isThrownBy(() -> literal("", (String) null));
@@ -890,8 +933,10 @@ public final void testCoreDatatypeTypedConstructor() {
assertThatNullPointerException().isThrownBy(() -> literal(null, (CoreDatatype) null));
assertThatNullPointerException().isThrownBy(() -> literal(null, CoreDatatype.XSD.STRING));
assertThatNullPointerException().isThrownBy(() -> literal(null, CoreDatatype.RDF.LANGSTRING));
+ assertThatNullPointerException().isThrownBy(() -> literal(null, CoreDatatype.RDF.DIRLANGSTRING));
assertThatIllegalArgumentException().isThrownBy(() -> literal("", CoreDatatype.RDF.LANGSTRING));
+ assertThatIllegalArgumentException().isThrownBy(() -> literal("", CoreDatatype.RDF.DIRLANGSTRING));
}
@@ -911,6 +956,7 @@ public void testCoreDatatypeStringValue() {
assertThat(literal(label).stringValue()).isEqualTo(label);
assertThat(literal(label, language).stringValue()).isEqualTo(label);
+ assertThat(literal(label, language, Literal.BaseDirection.LTR).stringValue()).isEqualTo(label);
assertThat(literal(label, datatype).stringValue()).isEqualTo(label);
}
@@ -1442,49 +1488,6 @@ public final void testCoreDatatypeCalendarValue() throws DatatypeConfigurationEx
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- @Test
- public void testCoreDatatypeEqualsAndHashCode() {
-
- Literal plain = literal("plain");
- Literal tagged = literal("tagged", "en");
- Literal typed = literal("typed", datatype("http://example.org/datatype"));
-
- Literal _plain = literal(plain.getLabel());
- Literal _tagged = literal(tagged.getLabel(), tagged.getLanguage().orElse(""));
- Literal _typed = literal(typed.getLabel(), typed.getDatatype());
-
- assertThat(plain).isEqualTo(plain);
- assertThat(plain).isEqualTo(_plain);
-
- assertThat(tagged).isEqualTo(tagged);
- assertThat(tagged).isEqualTo(_tagged);
-
- assertThat(typed).isEqualTo(typed);
- assertThat(typed).isEqualTo(_typed);
-
- assertThat(plain).isNotEqualTo(null);
- assertThat(plain).isNotEqualTo(new Object());
-
- assertThat(plain).isNotEqualTo(tagged);
- assertThat(plain).isNotEqualTo(typed);
- assertThat(tagged).isNotEqualTo(typed);
-
- assertThat(plain).isNotEqualTo(literal("other"));
- assertThat(tagged).isNotEqualTo(literal(tagged.getLabel(), "other"));
- assertThat(typed).isNotEqualTo(literal(typed.getLabel(), "http://example.org/other"));
-
- // hashCode() should return identical values for literals for which equals() is true
-
- assertThat(plain.hashCode()).isEqualTo(_plain.hashCode());
- assertThat(tagged.hashCode()).isEqualTo(_tagged.hashCode());
- assertThat(typed.hashCode()).isEqualTo(_typed.hashCode());
-
- assertThat(tagged.hashCode())
- .as("computed according to contract")
- .isEqualTo(tagged.getLabel().hashCode()); // !!! label >> label+language+datatype
-
- }
-
@Test
public final void testCoreDatatypeEqualsAndHashCodeCaseInsensitiveLanguage() {
@@ -1528,6 +1531,23 @@ public final void testSerializationWithCoreDatatypeRdfLangString() {
assertEquals(CoreDatatype.RDF.LANGSTRING, roundTrip.getCoreDatatype());
}
+ @Test
+ public final void testSerializationWithCoreDatatypeRdfDirLangString() {
+ Literal literal = literal("hello", "en", Literal.BaseDirection.LTR);
+ assertEquals(CoreDatatype.RDF.DIRLANGSTRING, literal.getCoreDatatype());
+ assertThat(literal.getLanguage()).isPresent();
+ assertEquals("en", literal.getLanguage().get());
+ assertEquals(Literal.BaseDirection.LTR, literal.getBaseDirection());
+
+ byte[] bytes = objectToBytes(literal);
+ Literal roundTrip = (Literal) bytesToObject(bytes);
+
+ assertEquals(CoreDatatype.RDF.DIRLANGSTRING, roundTrip.getCoreDatatype());
+ assertThat(roundTrip.getLanguage()).isPresent();
+ assertEquals("en", roundTrip.getLanguage().get());
+ assertEquals(Literal.BaseDirection.LTR, roundTrip.getBaseDirection());
+ }
+
@Test
public final void testSerializationWithCoreDatatypeGEO() {
Literal literal = literal("1", CoreDatatype.GEO.WKT_LITERAL);
@@ -1549,6 +1569,22 @@ public final void testSerializationWithCoreDatatype4() {
assertEquals(CoreDatatype.XSD.NONE, roundTrip.getCoreDatatype());
}
+ @Test
+ void testBaseDirectionEnumFromString() {
+ assertThat(Literal.BaseDirection.fromString("")).isEqualTo(Literal.BaseDirection.NONE);
+ assertThat(Literal.BaseDirection.fromString(null)).isEqualTo(Literal.BaseDirection.NONE);
+ assertThat(Literal.BaseDirection.fromString(Literal.LTR_SUFFIX)).isEqualTo(Literal.BaseDirection.LTR);
+ assertThat(Literal.BaseDirection.fromString(Literal.RTL_SUFFIX)).isEqualTo(Literal.BaseDirection.RTL);
+ assertThrows(IllegalArgumentException.class, () -> Literal.BaseDirection.fromString("--invalid"));
+ }
+
+ @Test
+ void testBaseDirectionEnumSuffix() {
+ assertThat(Literal.BaseDirection.LTR.toString()).isEqualTo(Literal.LTR_SUFFIX);
+ assertThat(Literal.BaseDirection.RTL.toString()).isEqualTo(Literal.RTL_SUFFIX);
+ assertThat(Literal.BaseDirection.NONE.toString()).isEmpty();
+ }
+
private byte[] objectToBytes(Serializable object) {
try (var byteArrayOutputStream = new ByteArrayOutputStream()) {
try (var objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
diff --git a/core/model-api/src/test/java/org/eclipse/rdf4j/model/ValueFactoryTest.java b/core/model-api/src/test/java/org/eclipse/rdf4j/model/ValueFactoryTest.java
index 197b7a45369..5f051a81170 100644
--- a/core/model-api/src/test/java/org/eclipse/rdf4j/model/ValueFactoryTest.java
+++ b/core/model-api/src/test/java/org/eclipse/rdf4j/model/ValueFactoryTest.java
@@ -71,6 +71,7 @@
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
+import org.eclipse.rdf4j.model.base.CoreDatatype;
import org.junit.jupiter.api.Test;
/**
@@ -537,4 +538,16 @@ public void testCreateLiteralDate() throws DatatypeConfigurationException {
}
+ @Test
+ public void testCreateDirLangLiteral() {
+ final Literal literal = factory().createLiteral("label", "he", Literal.BaseDirection.RTL);
+
+ assertThat(literal).isNotNull();
+ assertThat(literal.getLabel()).isEqualTo("label");
+ assertThat(literal.getLanguage()).contains("he");
+ assertThat(literal.getBaseDirection()).isEqualTo(Literal.BaseDirection.RTL);
+ assertThat(literal.getBaseDirection().toString()).isEqualTo("--rtl");
+ assertThat(literal.getDatatype()).isEqualTo(CoreDatatype.RDF.DIRLANGSTRING.getIri());
+ }
+
}
diff --git a/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/AbstractLiteralTest.java b/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/AbstractLiteralTest.java
index 03849f7a2c5..148f1693d96 100644
--- a/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/AbstractLiteralTest.java
+++ b/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/AbstractLiteralTest.java
@@ -11,11 +11,14 @@
package org.eclipse.rdf4j.model.base;
+import static org.assertj.core.api.Assertions.assertThat;
+
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.LiteralTest;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.base.AbstractValueFactoryTest.GenericValueFactory;
+import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link AbstractLiteral}.
@@ -37,6 +40,11 @@ protected Literal literal(String label, String language) {
return factory.createLiteral(label, language);
}
+ @Override
+ protected Literal literal(String label, String language, Literal.BaseDirection dir) {
+ return factory.createLiteral(label, language, dir);
+ }
+
@Override
protected Literal literal(String label, IRI datatype) {
return factory.createLiteral(label, datatype);
@@ -51,5 +59,4 @@ protected Literal literal(String label, CoreDatatype datatype) {
protected IRI datatype(String iri) {
return factory.createIRI(iri);
}
-
}
diff --git a/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/RDF.java b/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/RDF.java
index 92bb07e4fe4..e6b94852872 100644
--- a/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/RDF.java
+++ b/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/RDF.java
@@ -87,7 +87,13 @@ public class RDF {
/** http://www.w3.org/1999/02/22-rdf-syntax-ns#langString */
public static final IRI LANGSTRING = CoreDatatype.RDF.LANGSTRING.getIri();
+ /** http://www.w3.org/1999/02/22-rdf-syntax-ns#dirLangString */
+ public static final IRI DIRLANGSTRING = CoreDatatype.RDF.DIRLANGSTRING.getIri();
+
/** http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML */
public static final IRI HTML = CoreDatatype.RDF.HTML.getIri();
+ /** http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON */
+ public static final IRI JSON = CoreDatatype.RDF.JSON.getIri();
+
}
diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleLiteral.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleLiteral.java
index fb86f383183..e39a424760f 100644
--- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleLiteral.java
+++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleLiteral.java
@@ -63,6 +63,11 @@ public class SimpleLiteral extends AbstractLiteral {
// Cached CoreDatatype, or null if not yet computed.
private CoreDatatype coreDatatype = null;
+ /**
+ * The literal's base direction.
+ */
+ private BaseDirection baseDirection = BaseDirection.NONE;
+
/*--------------*
* Constructors *
*--------------*/
@@ -88,8 +93,13 @@ protected SimpleLiteral(String label) {
* @param language The language tag for the literal, must not be null and not be empty.
*/
protected SimpleLiteral(String label, String language) {
+ this(label, language, BaseDirection.NONE);
+ }
+
+ protected SimpleLiteral(String label, String language, BaseDirection baseDirection) {
setLabel(label);
setLanguage(language);
+ setBaseDirection(baseDirection);
}
/**
@@ -100,8 +110,9 @@ protected SimpleLiteral(String label, String language) {
*/
protected SimpleLiteral(String label, IRI datatype) {
setLabel(label);
- if (org.eclipse.rdf4j.model.vocabulary.RDF.LANGSTRING.equals(datatype)) {
- throw new IllegalArgumentException("datatype rdf:langString requires a language tag");
+ if (org.eclipse.rdf4j.model.vocabulary.RDF.LANGSTRING.equals(datatype)
+ || org.eclipse.rdf4j.model.vocabulary.RDF.DIRLANGSTRING.equals(datatype)) {
+ throw new IllegalArgumentException("datatype rdf:langString or rdf:dirLangString requires a language tag");
} else if (datatype == null) {
setDatatype(CoreDatatype.XSD.STRING);
} else {
@@ -122,8 +133,8 @@ protected SimpleLiteral(String label, IRI datatype, CoreDatatype coreDatatype) {
assert datatype != null;
assert coreDatatype == CoreDatatype.NONE || datatype == coreDatatype.getIri();
- if (CoreDatatype.RDF.LANGSTRING == coreDatatype) {
- throw new IllegalArgumentException("datatype rdf:langString requires a language tag");
+ if (CoreDatatype.RDF.LANGSTRING == coreDatatype || CoreDatatype.RDF.DIRLANGSTRING == coreDatatype) {
+ throw new IllegalArgumentException("datatype rdf:langString or rdf:dirLangString requires a language tag");
}
setLabel(label);
@@ -133,8 +144,8 @@ protected SimpleLiteral(String label, IRI datatype, CoreDatatype coreDatatype) {
protected SimpleLiteral(String label, CoreDatatype datatype) {
setLabel(label);
- if (datatype == CoreDatatype.RDF.LANGSTRING) {
- throw new IllegalArgumentException("datatype rdf:langString requires a language tag");
+ if (datatype == CoreDatatype.RDF.LANGSTRING || datatype == CoreDatatype.RDF.DIRLANGSTRING) {
+ throw new IllegalArgumentException("datatype rdf:langString or rdf:dirLangString requires a language tag");
} else {
setDatatype(datatype);
}
@@ -163,7 +174,16 @@ protected void setLanguage(String language) {
}
this.language = language;
optionalLanguageCache = Optional.of(language);
- setDatatype(CoreDatatype.RDF.LANGSTRING);
+ }
+
+ protected void setBaseDirection(BaseDirection baseDirection) {
+ Objects.requireNonNull(baseDirection, "null baseDirection");
+ this.baseDirection = baseDirection;
+ if (this.baseDirection != BaseDirection.NONE) {
+ setDatatype(CoreDatatype.RDF.DIRLANGSTRING);
+ } else {
+ setDatatype(CoreDatatype.RDF.LANGSTRING);
+ }
}
@Override
@@ -174,6 +194,10 @@ public Optional getLanguage() {
return optionalLanguageCache;
}
+ public BaseDirection getBaseDirection() {
+ return baseDirection;
+ }
+
protected void setDatatype(IRI datatype) {
this.datatype = datatype;
coreDatatype = CoreDatatype.from(datatype);
@@ -260,6 +284,7 @@ public String toString() {
StringBuilder sb = new StringBuilder(label.length() + language.length() + 3);
sb.append('"').append(label).append('"');
sb.append('@').append(language);
+ sb.append(getBaseDirection());
return sb.toString();
} else if (org.eclipse.rdf4j.model.vocabulary.XSD.STRING.equals(datatype) || datatype == null) {
StringBuilder sb = new StringBuilder(label.length() + 2);
diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java
index b9b685b7fcd..e0b208b6c15 100644
--- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java
+++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java
@@ -103,6 +103,11 @@ public Literal createLiteral(String value, String language) {
return new SimpleLiteral(value, language);
}
+ @Override
+ public Literal createLiteral(String value, String language, Literal.BaseDirection baseDirection) {
+ return new SimpleLiteral(value, language, baseDirection);
+ }
+
@Override
public Literal createLiteral(boolean b) {
return b ? BooleanLiteral.TRUE : BooleanLiteral.FALSE;
diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java
index 0cfe2860938..7bddd6d8b15 100644
--- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java
+++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java
@@ -134,10 +134,15 @@ public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDataty
@Override
public Literal createLiteral(String label, String language) {
+ return createLiteral(label, language, Literal.BaseDirection.NONE);
+ }
+
+ @Override
+ public Literal createLiteral(String label, String language, Literal.BaseDirection baseDirection) {
if (!Literals.isValidLanguageTag(language)) {
throw new IllegalArgumentException("Not a valid language tag: " + language);
}
- return delegate.createLiteral(label, language);
+ return delegate.createLiteral(label, language, baseDirection);
}
@Override
diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/util/Literals.java b/core/model/src/main/java/org/eclipse/rdf4j/model/util/Literals.java
index cfd77d59ebe..671aa1fc06e 100644
--- a/core/model/src/main/java/org/eclipse/rdf4j/model/util/Literals.java
+++ b/core/model/src/main/java/org/eclipse/rdf4j/model/util/Literals.java
@@ -473,7 +473,8 @@ public static boolean canCreateLiteral(Object object) {
* @return True if the literal has a language tag attached to it and false otherwise.
*/
public static boolean isLanguageLiteral(Literal literal) {
- return literal.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING;
+ return literal.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING
+ || literal.getCoreDatatype() == CoreDatatype.RDF.DIRLANGSTRING;
}
/**
@@ -495,7 +496,7 @@ public static String normalizeLanguageTag(String languageTag) throws IllformedLo
new Locale.Builder().setLanguageTag(languageTag);
// all subtags are case-insensitive
- String normalizedTag = languageTag.toLowerCase();
+ final String normalizedTag = languageTag.toLowerCase();
String[] subtags = normalizedTag.split("-");
for (int i = 1; i < subtags.length; i++) {
diff --git a/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleLiteralTest.java b/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleLiteralTest.java
index 61dab2ff8e3..82b6a40d8f6 100644
--- a/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleLiteralTest.java
+++ b/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleLiteralTest.java
@@ -30,6 +30,11 @@ protected Literal literal(String label, String language) {
return new SimpleLiteral(label, language);
}
+ @Override
+ protected Literal literal(String label, String language, Literal.BaseDirection dir) {
+ return new SimpleLiteral(label, language, dir);
+ }
+
@Override
protected Literal literal(String label, IRI datatype) {
return new SimpleLiteral(label, datatype);
diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/LanguageHandler.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/LanguageHandler.java
index 935e87ebbb4..40bbb1cf7df 100644
--- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/LanguageHandler.java
+++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/LanguageHandler.java
@@ -86,6 +86,28 @@ public interface LanguageHandler {
Literal normalizeLanguage(String literalValue, String languageTag, ValueFactory valueFactory)
throws LiteralUtilException;
+ /**
+ * Normalize both the language tag and the language if appropriate, and use the given value factory to generate a
+ * literal matching the literal value and language tag.
+ *
+ * This method must only be called after verifying that {@link #isRecognizedLanguage(String)} returns true for the
+ * given language tag, and {@link #verifyLanguage(String, String)} also returns true for the given language and
+ * literal value.
+ *
+ * @param literalValue Required literal value to use in the normalization process and to provide the value for the
+ * resulting literal.
+ * @param languageTag The language tag which is to be normalized. This tag is available in normalized form from the
+ * result using {@link Literal#getLanguage()}.
+ * @param baseDir
+ * @param valueFactory The {@link ValueFactory} to use to create the result literal.
+ * @return A {@link Literal} containing the normalized literal value and language tag.
+ * @throws LiteralUtilException If the language tag was not recognized or verified, or the literal value could not
+ * be normalized due to an error.
+ */
+ Literal normalizeLanguage(String literalValue, String languageTag, Literal.BaseDirection baseDir,
+ ValueFactory valueFactory)
+ throws LiteralUtilException;
+
/**
* A unique key for this language handler to identify it in the LanguageHandlerRegistry.
*
diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesUtil.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesUtil.java
index ad2195abaf0..486d8e2573d 100644
--- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesUtil.java
+++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/NTriplesUtil.java
@@ -565,6 +565,7 @@ public static void append(Literal lit, Appendable appendable, boolean xsdStringT
// Append the literal's language
appendable.append("@");
appendable.append(lit.getLanguage().get());
+ appendable.append(lit.getBaseDirection().toString());
} else {
// SES-1917 : In RDF-1.1, all literals have a type, and if they are not
// language literals we display the type for backwards compatibility
diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelper.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelper.java
index fbb27d608ab..fad0afc2c7b 100644
--- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelper.java
+++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelper.java
@@ -99,6 +99,7 @@ public static Literal createLiteral(String label, String lang, IRI datatype, Par
Literal result = null;
String workingLabel = label;
Optional workingLang = Optional.ofNullable(lang);
+ Literal.BaseDirection workingBaseDirection = Literal.BaseDirection.NONE;
IRI workingDatatype = datatype;
// In RDF-1.1 we must do lang check first as language literals will all
@@ -106,31 +107,50 @@ public static Literal createLiteral(String label, String lang, IRI datatype, Par
// non-null lang
if (workingLang.isPresent() && (workingDatatype == null || RDF.LANGSTRING.equals(workingDatatype))) {
boolean recognisedLanguage = false;
+
+ // Split workingLang into language tag and base direction so both can be separately verified
+ final int index = lang.indexOf(Literal.BASE_DIR_SEPARATOR);
+ if (index != -1) {
+ workingLang = Optional.of(lang.substring(0, index));
+ final String baseDirectionString = lang.substring(index);
+
+ try {
+ workingBaseDirection = Literal.BaseDirection.fromString(baseDirectionString);
+ } catch (IllegalArgumentException e) {
+ reportFatalError("'" + baseDirectionString + "' is not a valid base direction ", lineNo, columnNo,
+ errListener);
+ }
+ }
+
for (LanguageHandler nextHandler : parserConfig.get(BasicParserSettings.LANGUAGE_HANDLERS)) {
if (nextHandler.isRecognizedLanguage(workingLang.get())) {
recognisedLanguage = true;
if (parserConfig.get(BasicParserSettings.VERIFY_LANGUAGE_TAGS)) {
try {
if (!nextHandler.verifyLanguage(workingLabel, workingLang.get())) {
- reportError("'" + lang + "' is not a valid language tag ", lineNo, columnNo,
+ reportError("'" + workingLang.get() + "' is not a valid language tag ", lineNo,
+ columnNo,
BasicParserSettings.VERIFY_LANGUAGE_TAGS, parserConfig, errListener);
}
} catch (LiteralUtilException e) {
reportError("'" + label
+ " could not be verified by a language handler that recognised it. language was "
- + lang, lineNo, columnNo, BasicParserSettings.VERIFY_LANGUAGE_TAGS, parserConfig,
+ + workingLang.get(), lineNo, columnNo, BasicParserSettings.VERIFY_LANGUAGE_TAGS,
+ parserConfig,
errListener);
}
}
if (parserConfig.get(BasicParserSettings.NORMALIZE_LANGUAGE_TAGS)) {
try {
- result = nextHandler.normalizeLanguage(workingLabel, workingLang.get(), valueFactory);
+ result = nextHandler.normalizeLanguage(workingLabel, workingLang.get(),
+ workingBaseDirection, valueFactory);
workingLabel = result.getLabel();
workingLang = result.getLanguage();
workingDatatype = result.getDatatype();
} catch (LiteralUtilException e) {
reportError(
- "'" + label + "' did not have a valid value for language " + lang + ": "
+ "'" + label + "' did not have a valid value for language " + workingLang.get()
+ + ": "
+ e.getMessage() + " and could not be normalised",
lineNo, columnNo, BasicParserSettings.NORMALIZE_LANGUAGE_TAGS, parserConfig,
errListener);
@@ -141,7 +161,8 @@ public static Literal createLiteral(String label, String lang, IRI datatype, Par
if (!recognisedLanguage) {
reportError("'" + label
+ "' was not recognised as a language literal, and could not be verified, with language "
- + lang, lineNo, columnNo, BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES, parserConfig,
+ + workingLang.get(), lineNo, columnNo, BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES,
+ parserConfig,
errListener);
}
} else if (workingDatatype != null) {
@@ -189,15 +210,18 @@ public static Literal createLiteral(String label, String lang, IRI datatype, Par
if (result == null) {
try {
// Removes datatype for langString datatype with no language tag when VERIFY_DATATYPE_VALUES is False.
- if ((workingDatatype == null || RDF.LANGSTRING.equals(workingDatatype))
+ if ((workingDatatype == null || RDF.LANGSTRING.equals(workingDatatype)
+ || RDF.DIRLANGSTRING.equals(workingDatatype))
&& (workingLang.isEmpty() || workingLang.get().isEmpty())
&& !parserConfig.get(BasicParserSettings.VERIFY_DATATYPE_VALUES)) {
workingLang = Optional.ofNullable(null);
workingDatatype = null;
}
// Backup for unnormalised language literal creation
- if (workingLang.isPresent() && (workingDatatype == null || RDF.LANGSTRING.equals(workingDatatype))) {
- result = valueFactory.createLiteral(workingLabel, workingLang.get().intern());
+ if (workingLang.isPresent()
+ && (workingDatatype == null || RDF.LANGSTRING.equals(workingDatatype)
+ || RDF.DIRLANGSTRING.equals(workingDatatype))) {
+ result = valueFactory.createLiteral(workingLabel, workingLang.get().intern(), workingBaseDirection);
}
// Backup for unnormalised datatype literal creation
else if (workingDatatype != null) {
diff --git a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFStarDecodingValueFactory.java b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFStarDecodingValueFactory.java
index a324b58c748..30d84a67de6 100644
--- a/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFStarDecodingValueFactory.java
+++ b/core/rio/api/src/main/java/org/eclipse/rdf4j/rio/helpers/RDFStarDecodingValueFactory.java
@@ -71,6 +71,11 @@ public Literal createLiteral(String label, String language) {
return delegate.createLiteral(label, language);
}
+ @Override
+ public Literal createLiteral(String label, String language, Literal.BaseDirection baseDirection) {
+ return delegate.createLiteral(label, language, baseDirection);
+ }
+
@Override
public Literal createLiteral(String label, IRI datatype) {
return delegate.createLiteral(label, datatype);
diff --git a/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/AbstractParserTest.java b/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/AbstractParserTest.java
new file mode 100644
index 00000000000..ec21623b260
--- /dev/null
+++ b/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/AbstractParserTest.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Eclipse RDF4J contributors, Aduna, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Distribution License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *******************************************************************************/
+package org.eclipse.rdf4j.rio;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.StringReader;
+import java.util.Collection;
+
+import org.eclipse.rdf4j.model.Literal;
+import org.eclipse.rdf4j.model.Statement;
+import org.eclipse.rdf4j.model.base.CoreDatatype;
+import org.eclipse.rdf4j.rio.helpers.BasicParserSettings;
+import org.eclipse.rdf4j.rio.helpers.ParseErrorCollector;
+import org.eclipse.rdf4j.rio.helpers.SimpleParseLocationListener;
+import org.eclipse.rdf4j.rio.helpers.StatementCollector;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/*
+* Testing base class for Turtle, TriG, NTriples, and NQuads parsers
+ */
+public abstract class AbstractParserTest {
+ protected RDFParser parser;
+ protected ParseErrorCollector errorCollector = new ParseErrorCollector();
+ protected StatementCollector statementCollector = new StatementCollector();
+ protected TestParseLocationListener locationListener = new TestParseLocationListener();
+
+ @BeforeEach
+ public void setUp() {
+ parser = createRDFParser();
+ parser.setParseErrorListener(errorCollector);
+ parser.setRDFHandler(statementCollector);
+ parser.setParseLocationListener(locationListener);
+ }
+
+ protected void dirLangStringTestHelper(
+ final String data, final String expectedLang, final String expectedBaseDir, final boolean normalize,
+ final boolean shouldFail) {
+ parser.getParserConfig().set(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES, true);
+ parser.getParserConfig().set(BasicParserSettings.NORMALIZE_LANGUAGE_TAGS, normalize);
+
+ try {
+ parser.parse(new StringReader(data));
+
+ if (shouldFail) {
+ fail("default config should result in fatal error / parse exception");
+ }
+
+ assertThat(errorCollector.getErrors()).isEmpty();
+
+ final Collection stmts = statementCollector.getStatements();
+
+ assertThat(stmts).hasSize(1);
+
+ final Statement stmt = stmts.iterator().next();
+
+ assertEquals(CoreDatatype.RDF.DIRLANGSTRING.getIri(), ((Literal) stmt.getObject()).getDatatype());
+ assertTrue(((Literal) stmt.getObject()).getLanguage().isPresent());
+ assertEquals(expectedLang, ((Literal) stmt.getObject()).getLanguage().get());
+ assertEquals(expectedBaseDir, ((Literal) stmt.getObject()).getBaseDirection().toString());
+ } catch (final Exception e) {
+ if (!shouldFail) {
+ fail("parse error on correct data: " + e.getMessage());
+ }
+ }
+ }
+
+ protected void dirLangStringNoLanguageTestHelper(String data) {
+ try {
+ parser.parse(new StringReader(data));
+
+ assertThat(errorCollector.getErrors()).isEmpty();
+
+ Collection stmts = statementCollector.getStatements();
+
+ assertThat(stmts).hasSize(1);
+
+ Statement stmt = stmts.iterator().next();
+
+ assertEquals(CoreDatatype.XSD.STRING.getIri(), ((Literal) stmt.getObject()).getDatatype());
+ } catch (Exception e) {
+ fail("parse error on correct data: " + e.getMessage());
+ }
+ }
+
+ protected abstract RDFParser createRDFParser();
+
+ protected static class TestParseLocationListener extends SimpleParseLocationListener {
+
+ public void assertListener(int row, int col) {
+ assertEquals(row, this.getLineNo(), "Unexpected last row");
+ assertEquals(col, this.getColumnNo(), "Unexpected last col");
+ }
+
+ }
+
+ @Test
+ public void dummy() {
+ }
+
+}
diff --git a/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/RDFWriterTest.java b/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/RDFWriterTest.java
index 3a4125c54ac..b266dfd1c02 100644
--- a/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/RDFWriterTest.java
+++ b/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/RDFWriterTest.java
@@ -31,6 +31,7 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
+import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -50,6 +51,7 @@
import org.eclipse.rdf4j.model.Triple;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.ValueFactory;
+import org.eclipse.rdf4j.model.impl.DynamicModelFactory;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.util.Models;
@@ -1938,4 +1940,21 @@ private void assertSameModel(Model expected, Model actual) {
assertEquals(actual.filter(subj, pred, obj).size(), actual.filter(subj, pred, obj).size());
}
}
+
+ // Helper method for testing dirLangString literals in supported formats (Turtle, NTriples, TriG, NQuads)
+ public void dirLangStringTest(RDFFormat format) throws Exception {
+ Model model = new DynamicModelFactory().createEmptyModel();
+ String ns = "http://example.org/";
+ IRI uri1 = vf.createIRI(ns, "uri1");
+ IRI uri2 = vf.createIRI(ns, "uri2");
+ model.add(vf.createStatement(uri1, uri2, vf.createLiteral("hello", "en", Literal.BaseDirection.LTR)));
+ model.add(vf.createStatement(uri1, uri2, vf.createLiteral("שלום", "he", Literal.BaseDirection.RTL)));
+
+ StringWriter stringWriter = new StringWriter();
+ Rio.write(model, stringWriter, format);
+ String output = stringWriter.toString();
+
+ assertThat(output).contains("\"hello\"@en--ltr");
+ assertThat(output).contains("\"שלום\"@he--rtl");
+ }
}
diff --git a/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelperTest.java b/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelperTest.java
index 2213d046d69..83e7868be60 100644
--- a/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelperTest.java
+++ b/core/rio/api/src/test/java/org/eclipse/rdf4j/rio/helpers/RDFParserHelperTest.java
@@ -13,6 +13,7 @@
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@@ -116,6 +117,33 @@ public final void testCreateLiteralLabelAndLanguage() {
assertEquals(RDF.LANGSTRING, literal.getDatatype());
}
+ /**
+ * Test method for
+ * {@link org.eclipse.rdf4j.rio.helpers.RDFParserHelper#createLiteral(java.lang.String, java.lang.String, org.eclipse.rdf4j.model.URI, org.eclipse.rdf4j.rio.ParserConfig, org.eclipse.rdf4j.rio.ParseErrorListener, org.eclipse.rdf4j.model.ValueFactory)}
+ * .
+ */
+ @Test
+ public final void testCreateLiteralLabelAndLanguageAndDirection() {
+ Literal literalLTR = RDFParserHelper.createLiteral(LABEL_TESTA, LANG_EN + "--ltr", null, parserConfig,
+ errListener,
+ valueFactory);
+ Literal literalRTL = RDFParserHelper.createLiteral(LABEL_TESTA, "ar--rtl", null, parserConfig, errListener,
+ valueFactory);
+
+ assertEquals(LABEL_TESTA, literalLTR.getLabel());
+ assertEquals(LANG_EN, literalLTR.getLanguage().orElse(null));
+ assertEquals(Literal.BaseDirection.LTR, literalLTR.getBaseDirection());
+ assertEquals(RDF.DIRLANGSTRING, literalLTR.getDatatype());
+
+ assertEquals(LABEL_TESTA, literalRTL.getLabel());
+ assertEquals("ar", literalRTL.getLanguage().orElse(null));
+ assertEquals(Literal.BaseDirection.RTL, literalRTL.getBaseDirection());
+ assertEquals(RDF.DIRLANGSTRING, literalRTL.getDatatype());
+
+ assertThrows(RDFParseException.class, () -> RDFParserHelper.createLiteral(LABEL_TESTA, "he--jsldkfjds", null,
+ parserConfig, errListener, valueFactory));
+ }
+
/**
* Test method for
* {@link org.eclipse.rdf4j.rio.helpers.RDFParserHelper#createLiteral(java.lang.String, java.lang.String, org.eclipse.rdf4j.model.URI, org.eclipse.rdf4j.rio.ParserConfig, org.eclipse.rdf4j.rio.ParseErrorListener, org.eclipse.rdf4j.model.ValueFactory)}
@@ -171,6 +199,15 @@ public final void testCreateLiteralLabelNoLanguageWithRDFLangStringWithVerify()
.isInstanceOf(RDFParseException.class);
}
+ @Test
+ public final void testCreateLiteralLabelNoLanguageWithRDFDirLangStringWithVerify() {
+ parserConfig.set(BasicParserSettings.VERIFY_DATATYPE_VALUES, true);
+ assertTrue(parserConfig.get(BasicParserSettings.VERIFY_DATATYPE_VALUES));
+ assertThatThrownBy(() -> RDFParserHelper.createLiteral(LABEL_TESTA, null, RDF.DIRLANGSTRING, parserConfig,
+ errListener, valueFactory))
+ .isInstanceOf(RDFParseException.class);
+ }
+
@Test
public final void testCreateLiteralLabelNoLanguageWithRDFLangStringWithNoVerify() {
parserConfig.set(BasicParserSettings.VERIFY_DATATYPE_VALUES, false);
@@ -180,6 +217,15 @@ public final void testCreateLiteralLabelNoLanguageWithRDFLangStringWithNoVerify(
assertEquals(XSD.STRING, literal.getDatatype());
}
+ @Test
+ public final void testCreateLiteralLabelNoLanguageWithRDFDirLangStringWithNoVerify() {
+ parserConfig.set(BasicParserSettings.VERIFY_DATATYPE_VALUES, false);
+ Literal literal = RDFParserHelper.createLiteral(LABEL_TESTA, null, RDF.DIRLANGSTRING, parserConfig, errListener,
+ valueFactory);
+ assertFalse(literal.getLanguage().isPresent());
+ assertEquals(XSD.STRING, literal.getDatatype());
+ }
+
@Test
public final void testReportErrorStringFatalActive() {
parserConfig.set(BasicParserSettings.VERIFY_DATATYPE_VALUES, true);
diff --git a/core/rio/datatypes/src/main/java/org/eclipse/rdf4j/rio/datatypes/RDFDatatypeHandler.java b/core/rio/datatypes/src/main/java/org/eclipse/rdf4j/rio/datatypes/RDFDatatypeHandler.java
index 3868981bfce..d4b70cd2da6 100644
--- a/core/rio/datatypes/src/main/java/org/eclipse/rdf4j/rio/datatypes/RDFDatatypeHandler.java
+++ b/core/rio/datatypes/src/main/java/org/eclipse/rdf4j/rio/datatypes/RDFDatatypeHandler.java
@@ -37,8 +37,10 @@ public boolean isRecognizedDatatype(IRI datatypeUri) {
}
return org.eclipse.rdf4j.model.vocabulary.RDF.LANGSTRING.equals(datatypeUri)
+ || org.eclipse.rdf4j.model.vocabulary.RDF.DIRLANGSTRING.equals(datatypeUri)
|| org.eclipse.rdf4j.model.vocabulary.RDF.XMLLITERAL.equals(datatypeUri)
- || org.eclipse.rdf4j.model.vocabulary.RDF.HTML.equals(datatypeUri);
+ || org.eclipse.rdf4j.model.vocabulary.RDF.HTML.equals(datatypeUri)
+ || org.eclipse.rdf4j.model.vocabulary.RDF.JSON.equals(datatypeUri);
}
@Override
diff --git a/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/BCP47LanguageHandler.java b/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/BCP47LanguageHandler.java
index e5e91163349..453268bd25a 100644
--- a/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/BCP47LanguageHandler.java
+++ b/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/BCP47LanguageHandler.java
@@ -65,11 +65,19 @@ public boolean verifyLanguage(String literalValue, String languageTag) throws Li
@Override
public Literal normalizeLanguage(String literalValue, String languageTag, ValueFactory valueFactory)
throws LiteralUtilException {
+ return normalizeLanguage(literalValue, languageTag, Literal.BaseDirection.NONE, valueFactory);
+ }
+
+ @Override
+ public Literal normalizeLanguage(String literalValue, String languageTag, Literal.BaseDirection baseDir,
+ ValueFactory valueFactory)
+ throws LiteralUtilException {
Objects.requireNonNull(languageTag, "Language tag cannot be null");
Objects.requireNonNull(literalValue, "Literal value cannot be null");
try {
- return valueFactory.createLiteral(literalValue, Literals.normalizeLanguageTag(languageTag));
+ return valueFactory.createLiteral(literalValue,
+ Literals.normalizeLanguageTag(languageTag), baseDir);
} catch (IllformedLocaleException e) {
throw new LiteralUtilException("Could not normalize BCP47 language tag", e);
}
diff --git a/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/RFC3066LanguageHandler.java b/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/RFC3066LanguageHandler.java
index 37ae95506ce..1d89fb1cd33 100644
--- a/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/RFC3066LanguageHandler.java
+++ b/core/rio/languages/src/main/java/org/eclipse/rdf4j/rio/languages/RFC3066LanguageHandler.java
@@ -67,11 +67,18 @@ public boolean verifyLanguage(String literalValue, String languageTag) throws Li
@Override
public Literal normalizeLanguage(String literalValue, String languageTag, ValueFactory valueFactory)
throws LiteralUtilException {
+ return normalizeLanguage(literalValue, languageTag, Literal.BaseDirection.NONE, valueFactory);
+ }
+
+ @Override
+ public Literal normalizeLanguage(String literalValue, String languageTag, Literal.BaseDirection baseDir,
+ ValueFactory valueFactory)
+ throws LiteralUtilException {
Objects.requireNonNull(languageTag, "Language tag cannot be null");
Objects.requireNonNull(literalValue, "Literal value cannot be null");
if (isRecognizedLanguage(languageTag)) {
- return valueFactory.createLiteral(literalValue, languageTag.toLowerCase().intern());
+ return valueFactory.createLiteral(literalValue, languageTag.toLowerCase().intern(), baseDir);
}
throw new LiteralUtilException("Could not normalize RFC3066 language tag");
diff --git a/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsParserUnitTest.java b/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsParserUnitTest.java
index 87b9d1c91ae..dae45494c80 100644
--- a/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsParserUnitTest.java
+++ b/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsParserUnitTest.java
@@ -30,23 +30,20 @@
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.vocabulary.RDF;
+import org.eclipse.rdf4j.rio.AbstractParserTest;
import org.eclipse.rdf4j.rio.RDFHandlerException;
import org.eclipse.rdf4j.rio.RDFParseException;
import org.eclipse.rdf4j.rio.RDFParser;
import org.eclipse.rdf4j.rio.helpers.AbstractRDFHandler;
import org.eclipse.rdf4j.rio.helpers.BasicParserSettings;
import org.eclipse.rdf4j.rio.helpers.NTriplesParserSettings;
-import org.eclipse.rdf4j.rio.helpers.SimpleParseLocationListener;
-import org.eclipse.rdf4j.rio.helpers.StatementCollector;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* JUnit test for the N-Quads parser that uses the tests that are available
* online.
*/
-public abstract class AbstractNQuadsParserUnitTest {
+public abstract class AbstractNQuadsParserUnitTest extends AbstractParserTest {
/*-----------*
* Constants *
@@ -60,33 +57,16 @@ public abstract class AbstractNQuadsParserUnitTest {
private static final String NTRIPLES_TEST_FILE = "/testcases/ntriples/test.nt";
- private RDFParser parser;
-
- private TestRDFHandler rdfHandler;
-
- @BeforeEach
- public void setUp() {
- parser = createRDFParser();
- rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(this.rdfHandler);
- }
-
- @AfterEach
- public void tearDown() {
- parser = null;
- }
-
/*---------*
* Methods *
*---------*/
public void testNQuadsFile() throws Exception {
- RDFParser nquadsParser = createRDFParser();
- nquadsParser.setRDFHandler(new AbstractRDFHandler() {
+ parser.setRDFHandler(new AbstractRDFHandler() {
});
try (InputStream in = AbstractNQuadsParserUnitTest.class.getResourceAsStream(NQUADS_TEST_FILE)) {
- nquadsParser.parse(in, NQUADS_TEST_URL);
+ parser.parse(in, NQUADS_TEST_URL);
} catch (RDFParseException e) {
fail("NQuadsParser failed to parse N-Quads test document: " + e.getMessage());
}
@@ -96,12 +76,11 @@ public void testNQuadsFile() throws Exception {
* The N-Quads parser must be able to parse the N-Triples test file without error.
*/
public void testNTriplesFile() throws Exception {
- RDFParser nquadsParser = createRDFParser();
- nquadsParser.setRDFHandler(new AbstractRDFHandler() {
+ parser.setRDFHandler(new AbstractRDFHandler() {
});
try (InputStream in = AbstractNQuadsParserUnitTest.class.getResourceAsStream(NTRIPLES_TEST_FILE)) {
- nquadsParser.parse(in, NTRIPLES_TEST_URL);
+ parser.parse(in, NTRIPLES_TEST_URL);
} catch (RDFParseException e) {
fail("NQuadsParser failed to parse N-Triples test document: " + e.getMessage());
}
@@ -186,10 +165,8 @@ public void testParseNoContext() throws RDFHandlerException, IOException, RDFPar
public void testParseEmptyLinesAndComments() throws RDFHandlerException, IOException, RDFParseException {
final ByteArrayInputStream bais = new ByteArrayInputStream(
" \n\n\n# This is a comment\n\n#this is another comment.".getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.parse(bais, "http://test.base.uri");
- assertEquals(rdfHandler.getStatements().size(), 0);
+ assertEquals(0, statementCollector.getStatements().size());
}
/**
@@ -200,11 +177,9 @@ public void testParseBasic() throws RDFHandlerException, IOException, RDFParseEx
final ByteArrayInputStream bais = new ByteArrayInputStream(
" ."
.getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.parse(bais, "http://test.base.uri");
- assertEquals(1, rdfHandler.getStatements().size());
- final Statement statement = rdfHandler.getStatements().iterator().next();
+ assertEquals(1, statementCollector.getStatements().size());
+ final Statement statement = statementCollector.getStatements().iterator().next();
assertEquals("http://www.v/dat/4b", statement.getSubject().stringValue());
assertEquals("http://www.w3.org/20/ica#dtend", statement.getPredicate().stringValue());
assertTrue(statement.getObject() instanceof IRI);
@@ -220,11 +195,9 @@ public void testParseBasicBNode() throws RDFHandlerException, IOException, RDFPa
final ByteArrayInputStream bais = new ByteArrayInputStream(
"_:a123456768 ."
.getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.parse(bais, "http://test.base.uri");
- assertThat(rdfHandler.getStatements()).hasSize(1);
- final Statement statement = rdfHandler.getStatements().iterator().next();
+ assertThat(statementCollector.getStatements()).hasSize(1);
+ final Statement statement = statementCollector.getStatements().iterator().next();
assertTrue(statement.getSubject() instanceof BNode);
assertEquals("http://www.w3.org/20/ica#dtend", statement.getPredicate().stringValue());
assertTrue(statement.getObject() instanceof IRI);
@@ -240,11 +213,9 @@ public void testParseBasicLiteral() throws RDFHandlerException, IOException, RDF
final ByteArrayInputStream bais = new ByteArrayInputStream(
"_:a123456768 \"2010-05-02\" ."
.getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.parse(bais, "http://test.base.uri");
- assertThat(rdfHandler.getStatements()).hasSize(1);
- final Statement statement = rdfHandler.getStatements().iterator().next();
+ assertThat(statementCollector.getStatements()).hasSize(1);
+ final Statement statement = statementCollector.getStatements().iterator().next();
assertTrue(statement.getSubject() instanceof BNode);
assertEquals("http://www.w3.org/20/ica#dtend", statement.getPredicate().stringValue());
assertTrue(statement.getObject() instanceof Literal);
@@ -260,10 +231,8 @@ public void testParseBasicLiteralLang() throws RDFHandlerException, IOException,
final ByteArrayInputStream bais = new ByteArrayInputStream(
" \"2010-05-02\"@en ."
.getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.parse(bais, "http://test.base.uri");
- final Statement statement = rdfHandler.getStatements().iterator().next();
+ final Statement statement = statementCollector.getStatements().iterator().next();
assertEquals("http://www.v/dat/4b2-21", statement.getSubject().stringValue());
assertEquals("http://www.w3.org/20/ica#dtend", statement.getPredicate().stringValue());
assertTrue(statement.getObject() instanceof Literal);
@@ -283,10 +252,8 @@ public void testParseBasicLiteralDatatype() throws RDFHandlerException, IOExcept
(" " + " "
+ "\"2010\"^^ " + ".")
.getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.parse(bais, "http://test.base.uri");
- final Statement statement = rdfHandler.getStatements().iterator().next();
+ final Statement statement = statementCollector.getStatements().iterator().next();
assertEquals("http://www.v/dat/4b2-21", statement.getSubject().stringValue());
assertEquals("http://www.w3.org/20/ica#dtend", statement.getPredicate().stringValue());
assertTrue(statement.getObject() instanceof Literal);
@@ -307,8 +274,6 @@ public void testParseBasicLiteralDatatypePrefix() throws RDFHandlerException, IO
final ByteArrayInputStream bais = new ByteArrayInputStream(
(" " + " " + "\"2010\"^^xsd:integer "
+ ".").getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
try {
parser.parse(bais, "http://test.base.uri");
fail("Expected exception when passing in a datatype using an N3 style prefix");
@@ -324,19 +289,14 @@ public void testParseBasicLiteralDatatypePrefix() throws RDFHandlerException, IO
*/
@Test
public void testLiteralEscapeManagement1() throws RDFHandlerException, IOException, RDFParseException {
- TestParseLocationListener parseLocationListener = new TestParseLocationListener();
- TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setParseLocationListener(parseLocationListener);
- parser.setRDFHandler(rdfHandler);
-
final ByteArrayInputStream bais = new ByteArrayInputStream(
" \"\\\\\" .".getBytes());
parser.parse(bais, "http://base-uri");
- rdfHandler.assertHandler(1);
- // parseLocationListener.assertListener(1, 40);
+ assertEquals(1, statementCollector.getStatements().size());
+ // locationListener.assertListener(1, 40);
// FIXME: Enable column numbers when parser supports them
- parseLocationListener.assertListener(1, 1);
+ locationListener.assertListener(1, 1);
}
/**
@@ -344,17 +304,12 @@ public void testLiteralEscapeManagement1() throws RDFHandlerException, IOExcepti
*/
@Test
public void testLiteralEscapeManagement2() throws RDFHandlerException, IOException, RDFParseException {
- TestParseLocationListener parseLocationListener = new TestParseLocationListener();
- TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setParseLocationListener(parseLocationListener);
- parser.setRDFHandler(rdfHandler);
-
final ByteArrayInputStream bais = new ByteArrayInputStream(
" \"Line text 1\\nLine text 2\" .".getBytes());
parser.parse(bais, "http://base-uri");
- rdfHandler.assertHandler(1);
- final Value object = rdfHandler.getStatements().iterator().next().getObject();
+ assertEquals(1, statementCollector.getStatements().size());
+ final Value object = statementCollector.getStatements().iterator().next().getObject();
assertTrue(object instanceof Literal);
final String literalContent = ((Literal) object).getLabel();
assertEquals("Line text 1\nLine text 2", literalContent);
@@ -365,18 +320,13 @@ public void testLiteralEscapeManagement2() throws RDFHandlerException, IOExcepti
*/
@Test
public void testURIDecodingManagement() throws RDFHandlerException, IOException, RDFParseException {
- TestParseLocationListener parseLocationListener = new TestParseLocationListener();
- TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setParseLocationListener(parseLocationListener);
- parser.setRDFHandler(rdfHandler);
-
final ByteArrayInputStream bais = new ByteArrayInputStream(
" ."
.getBytes());
parser.parse(bais, "http://base-uri");
- rdfHandler.assertHandler(1);
- final Statement statement = rdfHandler.getStatements().iterator().next();
+ assertEquals(1, statementCollector.getStatements().size());
+ final Statement statement = statementCollector.getStatements().iterator().next();
final Resource subject = statement.getSubject();
assertTrue(subject instanceof IRI);
@@ -401,16 +351,14 @@ public void testURIDecodingManagement() throws RDFHandlerException, IOException,
@Test
public void testUnicodeLiteralDecoding() throws RDFHandlerException, IOException, RDFParseException {
- TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
final String INPUT_LITERAL_PLAIN = "[は]";
final String INPUT_LITERAL_ENCODED = "[\\u306F]";
final String INPUT_STRING = String.format(" \"%s\" .", INPUT_LITERAL_ENCODED);
final ByteArrayInputStream bais = new ByteArrayInputStream(INPUT_STRING.getBytes());
parser.parse(bais, "http://base-uri");
- rdfHandler.assertHandler(1);
- final Literal obj = (Literal) rdfHandler.getStatements().iterator().next().getObject();
+ assertEquals(1, statementCollector.getStatements().size());
+ final Literal obj = (Literal) statementCollector.getStatements().iterator().next().getObject();
assertEquals(INPUT_LITERAL_PLAIN, obj.getLabel());
}
@@ -452,17 +400,12 @@ public void testEndOfStreamReached() throws RDFHandlerException, IOException, RD
*/
@Test
public void testParserWithAllCases() throws IOException, RDFParseException, RDFHandlerException {
- TestParseLocationListener parseLocationListerner = new TestParseLocationListener();
- // SpecificTestRDFHandler rdfHandler = new SpecificTestRDFHandler();
- parser.setParseLocationListener(parseLocationListerner);
- parser.setRDFHandler(rdfHandler);
-
BufferedReader br = new BufferedReader(new InputStreamReader(
AbstractNQuadsParserUnitTest.class.getResourceAsStream("/testcases/nquads/test1.nq")));
parser.parse(br, "http://test.base.uri");
- rdfHandler.assertHandler(6);
- parseLocationListerner.assertListener(8, 1);
+ assertEquals(6, statementCollector.getStatements().size());
+ locationListener.assertListener(8, 1);
}
/**
@@ -470,16 +413,11 @@ public void testParserWithAllCases() throws IOException, RDFParseException, RDFH
*/
@Test
public void testParserWithRealData() throws IOException, RDFParseException, RDFHandlerException {
- TestParseLocationListener parseLocationListener = new TestParseLocationListener();
- TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setParseLocationListener(parseLocationListener);
- parser.setRDFHandler(rdfHandler);
-
parser.parse(AbstractNQuadsParserUnitTest.class.getResourceAsStream("/testcases/nquads/test2.nq"),
"http://test.base.uri");
- rdfHandler.assertHandler(400);
- parseLocationListener.assertListener(400, 1);
+ assertEquals(400, statementCollector.getStatements().size());
+ locationListener.assertListener(400, 1);
}
@Test
@@ -566,15 +504,13 @@ public void testStopAtFirstErrorTolerantParsing() throws RDFHandlerException, IO
// with
// error.
" .\n").getBytes());
- final TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.getParserConfig().set(NTriplesParserSettings.FAIL_ON_INVALID_LINES, false);
parser.getParserConfig().addNonFatalError(NTriplesParserSettings.FAIL_ON_INVALID_LINES);
parser.parse(bais, "http://base-uri");
- rdfHandler.assertHandler(2);
- final Collection statements = rdfHandler.getStatements();
+ assertEquals(2, statementCollector.getStatements().size());
+ final Collection statements = statementCollector.getStatements();
int i = 0;
for (Statement nextStatement : statements) {
assertEquals("http://s" + i, nextStatement.getSubject().stringValue());
@@ -587,8 +523,6 @@ public void testStopAtFirstErrorTolerantParsing() throws RDFHandlerException, IO
private void verifyStatementWithInvalidDatatype(boolean useDatatypeVerification)
throws RDFHandlerException, IOException, RDFParseException {
- TestRDFHandler rdfHandler = new TestRDFHandler();
- parser.setRDFHandler(rdfHandler);
parser.getParserConfig().set(BasicParserSettings.VERIFY_DATATYPE_VALUES, useDatatypeVerification);
parser.getParserConfig().set(BasicParserSettings.FAIL_ON_UNKNOWN_DATATYPES, useDatatypeVerification);
if (!useDatatypeVerification) {
@@ -603,41 +537,70 @@ private void verifyStatementWithInvalidDatatype(boolean useDatatypeVerification)
+ " .")
.getBytes());
parser.parse(bais, "http://base-uri");
- rdfHandler.assertHandler(1);
+ assertEquals(1, statementCollector.getStatements().size());
+ }
+
+ @Test
+ public void testDirLangStringRTLNoContext() {
+ final String data = " \"שלום\"@he--rtl";
+ dirLangStringTest(data, false, "he", Literal.RTL_SUFFIX, false, false);
}
- private class TestParseLocationListener extends SimpleParseLocationListener {
+ @Test
+ public void testDirLangStringRTLWithContext() {
+ final String data = " \"שלום\"@he--rtl";
+ dirLangStringTest(data, true, "he", Literal.RTL_SUFFIX, false, false);
+ }
- private void assertListener(int row, int col) {
- assertEquals(row, this.getLineNo(), "Unexpected last row");
- assertEquals(col, this.getColumnNo(), "Unexpected last col");
- }
+ @Test
+ public void testDirLangStringLTRWithNormalizationNoContext() {
+ String data = " \"Hello\"@en--ltr";
+ dirLangStringTest(data, false, "en", Literal.LTR_SUFFIX, true, false);
+ }
+ @Test
+ public void testDirLangStringLTRWithNormalizationWithContext() {
+ final String data = " \"Hello\"@en--ltr";
+ dirLangStringTest(data, true, "en", Literal.LTR_SUFFIX, true, false);
}
- private class TestRDFHandler extends StatementCollector {
+ @Test
+ public void testBadDirLangStringNoContext() {
+ final String data = " \"hello\"@en--unk";
+ dirLangStringTest(data, false, "", "", true, true);
+ }
- private boolean started = false;
+ @Test
+ public void testBadDirLangStringWithContext() {
+ final String data = " \"hello\"@en--unk";
+ dirLangStringTest(data, true, "", "", true, true);
+ }
- private boolean ended = false;
+ @Test
+ public void testBadCapitalizationDirLangStringNoContext() {
+ final String data = " \"Hello\"@en--LTR";
+ dirLangStringTest(data, false, "", "", true, true);
+ }
- @Override
- public void startRDF() throws RDFHandlerException {
- super.startRDF();
- started = true;
- }
+ @Test
+ public void testBadCapitalizationDirLangStringWithContext() {
+ final String data = " \"Hello\"@en--LTR";
+ dirLangStringTest(data, true, "", "", true, true);
+ }
- @Override
- public void endRDF() throws RDFHandlerException {
- super.endRDF();
- ended = true;
- }
+ @Test
+ public void testDirLangStringNoLanguage() throws IOException {
+ final String data = " \"Hello\"^^ .";
+ dirLangStringNoLanguageTestHelper(data);
+ }
- public void assertHandler(int expected) {
- assertTrue(started, "Never started.");
- assertTrue(ended, "Never ended.");
- assertEquals(expected, getStatements().size(), "Unexpected number of statements.");
- }
+ private void dirLangStringTest(
+ final String triple, final boolean withContext, final String expectedLang, final String expectedDir,
+ final boolean normalize,
+ final boolean shouldCauseException) {
+ final String data = triple + (withContext ? " " : "") + " .";
+
+ dirLangStringTestHelper(data, expectedLang, expectedDir, normalize, shouldCauseException);
}
@Test
diff --git a/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsWriterTest.java b/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsWriterTest.java
index 3a086558dd3..30354e3a005 100644
--- a/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsWriterTest.java
+++ b/core/rio/nquads/src/test/java/org/eclipse/rdf4j/rio/nquads/AbstractNQuadsWriterTest.java
@@ -19,6 +19,7 @@
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
+import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.RDFHandlerException;
import org.eclipse.rdf4j.rio.RDFParseException;
import org.eclipse.rdf4j.rio.RDFParser;
@@ -155,6 +156,11 @@ public void testBlankNodeContextAddXSDString() throws RDFHandlerException {
" \"test literal\"^^ _:"));
}
+ @Test
+ public void testDirLangString() throws Exception {
+ dirLangStringTest(RDFFormat.NQUADS);
+ }
+
@Override
protected RioSetting>[] getExpectedSupportedSettings() {
return new RioSetting[] {
diff --git a/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesParserUnitTest.java b/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesParserUnitTest.java
index 6787dedc80a..0b929775c9b 100644
--- a/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesParserUnitTest.java
+++ b/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesParserUnitTest.java
@@ -15,6 +15,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
@@ -24,17 +25,19 @@
import java.util.List;
import java.util.TreeSet;
+import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.model.util.Models;
import org.eclipse.rdf4j.model.vocabulary.XSD;
+import org.eclipse.rdf4j.rio.AbstractParserTest;
import org.eclipse.rdf4j.rio.RDFHandlerException;
import org.eclipse.rdf4j.rio.RDFParseException;
import org.eclipse.rdf4j.rio.RDFParser;
import org.eclipse.rdf4j.rio.helpers.BasicParserSettings;
import org.eclipse.rdf4j.rio.helpers.NTriplesParserSettings;
-import org.eclipse.rdf4j.rio.helpers.ParseErrorCollector;
import org.eclipse.rdf4j.rio.helpers.StatementCollector;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
@@ -42,20 +45,26 @@
*
* @author Peter Ansell
*/
-public abstract class AbstractNTriplesParserUnitTest {
+public abstract class AbstractNTriplesParserUnitTest extends AbstractParserTest {
private static final String NTRIPLES_TEST_URL = "http://www.w3.org/2000/10/rdf-tests/rdfcore/ntriples/test.nt";
private static final String NTRIPLES_TEST_FILE = "/testcases/ntriples/test.nt";
+ private Model model;
+
+ @BeforeEach
+ @Override
+ public void setUp() {
+ model = new LinkedHashModel();
+ statementCollector = new StatementCollector(model);
+ super.setUp();
+ }
+
@Test
public void testNTriplesFile() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
-
try (InputStream in = this.getClass().getResourceAsStream(NTRIPLES_TEST_FILE)) {
- ntriplesParser.parse(in, NTRIPLES_TEST_URL);
+ parser.parse(in, NTRIPLES_TEST_URL);
} catch (RDFParseException e) {
fail("Failed to parse N-Triples test document: " + e.getMessage());
}
@@ -70,15 +79,11 @@ public void testNTriplesFile() throws Exception {
public void testExceptionHandlingWithDefaultSettings() throws Exception {
String data = "invalid nt";
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
-
try {
- ntriplesParser.parse(new StringReader(data), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader(data), NTRIPLES_TEST_URL);
fail("expected RDFParseException due to invalid data");
} catch (RDFParseException expected) {
- assertEquals(expected.getLineNumber(), 1);
+ assertEquals(1, expected.getLineNumber());
}
}
@@ -86,17 +91,13 @@ public void testExceptionHandlingWithDefaultSettings() throws Exception {
public void testExceptionHandlingWithStopAtFirstError() throws Exception {
String data = "invalid nt";
- RDFParser ntriplesParser = createRDFParser();
- ntriplesParser.getParserConfig().set(NTriplesParserSettings.FAIL_ON_INVALID_LINES, Boolean.TRUE);
-
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
+ parser.getParserConfig().set(NTriplesParserSettings.FAIL_ON_INVALID_LINES, Boolean.TRUE);
try {
- ntriplesParser.parse(new StringReader(data), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader(data), NTRIPLES_TEST_URL);
fail("expected RDFParseException due to invalid data");
} catch (RDFParseException expected) {
- assertEquals(expected.getLineNumber(), 1);
+ assertEquals(1, expected.getLineNumber());
}
}
@@ -104,13 +105,9 @@ public void testExceptionHandlingWithStopAtFirstError() throws Exception {
public void testExceptionHandlingWithoutStopAtFirstError() throws Exception {
String data = "invalid nt";
- RDFParser ntriplesParser = createRDFParser();
- ntriplesParser.getParserConfig().addNonFatalError(NTriplesParserSettings.FAIL_ON_INVALID_LINES);
-
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
+ parser.getParserConfig().addNonFatalError(NTriplesParserSettings.FAIL_ON_INVALID_LINES);
- ntriplesParser.parse(new StringReader(data), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader(data), NTRIPLES_TEST_URL);
assertEquals(0, model.size());
assertEquals(0, model.subjects().size());
@@ -122,13 +119,9 @@ public void testExceptionHandlingWithoutStopAtFirstError() throws Exception {
public void testExceptionHandlingWithoutStopAtFirstError2() throws Exception {
String data = "invalid nt";
- RDFParser ntriplesParser = createRDFParser();
- ntriplesParser.getParserConfig().set(NTriplesParserSettings.FAIL_ON_INVALID_LINES, false);
+ parser.getParserConfig().set(NTriplesParserSettings.FAIL_ON_INVALID_LINES, false);
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
-
- ntriplesParser.parse(new StringReader(data), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader(data), NTRIPLES_TEST_URL);
assertEquals(0, model.size());
assertEquals(0, model.subjects().size());
@@ -138,10 +131,7 @@ public void testExceptionHandlingWithoutStopAtFirstError2() throws Exception {
@Test
public void testEscapes() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(
+ parser.parse(
new StringReader(" \" \\t \\b \\n \\r \\f \\\" \\' \\\\ \" . "),
"http://example/");
assertEquals(1, model.size());
@@ -150,10 +140,7 @@ public void testEscapes() throws Exception {
@Test
public void testEndOfLineCommentNoSpace() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(
+ parser.parse(
new StringReader(" .#endoflinecomment\n"),
"http://example/");
assertEquals(1, model.size());
@@ -162,10 +149,7 @@ public void testEndOfLineCommentNoSpace() throws Exception {
@Test
public void testEndOfLineCommentWithSpaceBefore() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(
+ parser.parse(
new StringReader(" . #endoflinecomment\n"),
"http://example/");
assertEquals(1, model.size());
@@ -174,10 +158,7 @@ public void testEndOfLineCommentWithSpaceBefore() throws Exception {
@Test
public void testEndOfLineCommentWithSpaceAfter() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(
+ parser.parse(
new StringReader(" .# endoflinecomment\n"),
"http://example/");
assertEquals(1, model.size());
@@ -186,10 +167,7 @@ public void testEndOfLineCommentWithSpaceAfter() throws Exception {
@Test
public void testEndOfLineCommentWithSpaceBoth() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(
+ parser.parse(
new StringReader(" . # endoflinecomment\n"),
"http://example/");
assertEquals(1, model.size());
@@ -198,10 +176,7 @@ public void testEndOfLineCommentWithSpaceBoth() throws Exception {
@Test
public void testEndOfLineCommentsNoSpace() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(
+ parser.parse(new StringReader(
" .#endoflinecomment\n . # endoflinecomment\n"),
"http://example/");
assertEquals(2, model.size());
@@ -209,10 +184,7 @@ public void testEndOfLineCommentsNoSpace() throws Exception {
@Test
public void testEndOfLineCommentsWithSpaceBefore() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(
+ parser.parse(new StringReader(
" . #endoflinecomment\n . # endoflinecomment\n"),
"http://example/");
assertEquals(2, model.size());
@@ -220,10 +192,7 @@ public void testEndOfLineCommentsWithSpaceBefore() throws Exception {
@Test
public void testEndOfLineCommentsWithSpaceAfter() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(
+ parser.parse(new StringReader(
" .# endoflinecomment\n . # endoflinecomment\n"),
"http://example/");
assertEquals(2, model.size());
@@ -231,10 +200,7 @@ public void testEndOfLineCommentsWithSpaceAfter() throws Exception {
@Test
public void testEndOfLineCommentsWithSpaceBoth() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(
+ parser.parse(new StringReader(
" . # endoflinecomment\n . # endoflinecomment\n"),
"http://example/");
assertEquals(2, model.size());
@@ -242,10 +208,7 @@ public void testEndOfLineCommentsWithSpaceBoth() throws Exception {
@Test
public void testEndOfLineEmptyCommentNoSpace() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(" .#\n"),
+ parser.parse(new StringReader(" .#\n"),
"http://example/");
assertEquals(1, model.size());
assertEquals(Collections.singleton("urn:test:object"), Models.objectStrings(model));
@@ -253,10 +216,7 @@ public void testEndOfLineEmptyCommentNoSpace() throws Exception {
@Test
public void testEndOfLineEmptyCommentWithSpaceBefore() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(" . #\n"),
+ parser.parse(new StringReader(" . #\n"),
"http://example/");
assertEquals(1, model.size());
assertEquals(Collections.singleton("urn:test:object"), Models.objectStrings(model));
@@ -264,10 +224,7 @@ public void testEndOfLineEmptyCommentWithSpaceBefore() throws Exception {
@Test
public void testEndOfLineEmptyCommentWithSpaceAfter() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(" .# \n"),
+ parser.parse(new StringReader(" .# \n"),
"http://example/");
assertEquals(1, model.size());
assertEquals(Collections.singleton("urn:test:object"), Models.objectStrings(model));
@@ -275,10 +232,7 @@ public void testEndOfLineEmptyCommentWithSpaceAfter() throws Exception {
@Test
public void testEndOfLineEmptyCommentWithSpaceBoth() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader(" . # \n"),
+ parser.parse(new StringReader(" . # \n"),
"http://example/");
assertEquals(1, model.size());
assertEquals(Collections.singleton("urn:test:object"), Models.objectStrings(model));
@@ -286,10 +240,7 @@ public void testEndOfLineEmptyCommentWithSpaceBoth() throws Exception {
@Test
public void testBlankNodeIdentifiersRDF11() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader("_:123 _:456 ."), "http://example/");
+ parser.parse(new StringReader("_:123 _:456 ."), "http://example/");
assertEquals(1, model.size());
}
@@ -300,14 +251,10 @@ public void testSupportedSettings() {
@Test
public void testUriWithSpaceShouldFailToParse() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
-
String nt = " .";
try {
- ntriplesParser.parse(new StringReader(nt), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader(nt), NTRIPLES_TEST_URL);
fail("Should have failed to parse invalid N-Triples uri with space");
} catch (RDFParseException ignored) {
}
@@ -320,14 +267,10 @@ public void testUriWithSpaceShouldFailToParse() throws Exception {
@Test
public void testUriWithEscapeCharactersShouldFailToParse() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
-
String nt = " .";
try {
- ntriplesParser.parse(new StringReader(nt), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader(nt), NTRIPLES_TEST_URL);
fail("Should have failed to parse invalid N-Triples uri with space");
} catch (RDFParseException ignored) {
}
@@ -341,10 +284,7 @@ public void testUriWithEscapeCharactersShouldFailToParse() throws Exception {
@Test
public void testBlankNodeIdentifiersWithUnderScore() throws Exception {
// The characters _ and [0-9] may appear anywhere in a blank node label.
- RDFParser ntriplesParser = new NTriplesParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader("_:123_ _:_456 ."), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader("_:123_ _:_456 ."), NTRIPLES_TEST_URL);
assertEquals(1, model.size());
assertEquals(1, model.subjects().size());
@@ -355,10 +295,7 @@ public void testBlankNodeIdentifiersWithUnderScore() throws Exception {
@Test
public void testBlankNodeIdentifiersWithDot() throws Exception {
// The character . may appear anywhere except the first or last character.
- RDFParser ntriplesParser = new NTriplesParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
- ntriplesParser.parse(new StringReader("_:1.23 _:45.6 ."), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader("_:1.23 _:45.6 ."), NTRIPLES_TEST_URL);
assertEquals(1, model.size());
assertEquals(1, model.subjects().size());
@@ -369,11 +306,8 @@ public void testBlankNodeIdentifiersWithDot() throws Exception {
@Test
public void testBlankNodeIdentifiersWithDotAsFirstCahracter() {
// The character . may appear anywhere except the first or last character.
- RDFParser ntriplesParser = new NTriplesParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
try {
- ntriplesParser.parse(new StringReader("_:123 _:.456 ."), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader("_:123 _:.456 ."), NTRIPLES_TEST_URL);
fail("Should have failed to parse invalid N-Triples bnode with '.' at the begining of the bnode label");
} catch (Exception e) {
}
@@ -387,11 +321,8 @@ public void testBlankNodeIdentifiersWithDotAsFirstCahracter() {
@Test
public void testBlankNodeIdentifiersWithDotAsLastCahracter() {
// The character . may appear anywhere except the first or last character.
- RDFParser ntriplesParser = new NTriplesParser();
- Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
assertThrows(RDFParseException.class,
- () -> ntriplesParser.parse(new StringReader("_:123 _:456. ."), NTRIPLES_TEST_URL));
+ () -> parser.parse(new StringReader("_:123 _:456. ."), NTRIPLES_TEST_URL));
assertEquals(0, model.size());
assertEquals(0, model.subjects().size());
assertEquals(0, model.predicates().size());
@@ -412,13 +343,12 @@ public void testBlankNodeIdentifiersWithOtherCharacters() {
for (int i = 0; i < charactersList.size(); i++) {
Character character = charactersList.get(i);
- RDFParser ntriplesParser = new NTriplesParser();
Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
+ parser.setRDFHandler(new StatementCollector(model));
String triple = " _:1" + character + " . ";
try {
- ntriplesParser.parse(new StringReader(triple), NTRIPLES_TEST_URL);
+ parser.parse(new StringReader(triple), NTRIPLES_TEST_URL);
} catch (Exception e) {
fail(" Failed to parse triple : " + triple + " containing character '" + character + "' at index " + i
+ " in charactersList");
@@ -448,12 +378,11 @@ public void testBlankNodeIdentifiersWithOtherCharactersAsFirstCharacter() {
charactersList.add('\u203F');
for (Character character : charactersList) {
- RDFParser ntriplesParser = new NTriplesParser();
Model model = new LinkedHashModel();
- ntriplesParser.setRDFHandler(new StatementCollector(model));
+ parser.setRDFHandler(new StatementCollector(model));
assertThrows(RDFParseException.class, () -> {
- ntriplesParser.parse(
+ parser.parse(
new StringReader(" _:" + character + "1 . "),
NTRIPLES_TEST_URL);
});
@@ -479,12 +408,11 @@ public void handleComment(String comment) throws RDFHandlerException {
@Test
public void testHandleComment() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
Model model = new LinkedHashModel();
String commentStr = "some comment in it's own line";
CommentCollector cc = new CommentCollector(model);
- ntriplesParser.setRDFHandler(cc);
- ntriplesParser.parse(
+ parser.setRDFHandler(cc);
+ parser.parse(
new StringReader(" .\n#" + commentStr + "\n ."),
"http://example/");
assertEquals(2, model.size());
@@ -494,16 +422,13 @@ public void testHandleComment() throws Exception {
@Test
public void testLinenumberDatatypeValidation() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- ntriplesParser.getParserConfig().addNonFatalError(BasicParserSettings.VERIFY_DATATYPE_VALUES);
- ntriplesParser.getParserConfig().set(BasicParserSettings.VERIFY_DATATYPE_VALUES, true);
- ParseErrorCollector collector = new ParseErrorCollector();
- ntriplesParser.setParseErrorListener(collector);
+ parser.getParserConfig().addNonFatalError(BasicParserSettings.VERIFY_DATATYPE_VALUES);
+ parser.getParserConfig().set(BasicParserSettings.VERIFY_DATATYPE_VALUES, true);
- ntriplesParser.parse(
+ parser.parse(
new StringReader(" \"invalid\"^^<" + XSD.DATETIME.stringValue() + "> ."),
NTRIPLES_TEST_URL);
- List errors = collector.getErrors();
+ List errors = errorCollector.getErrors();
assertEquals(1, errors.size());
assertTrue(errors.get(0).contains("(1, 32)"), "Unknown line number");
@@ -511,21 +436,60 @@ public void testLinenumberDatatypeValidation() throws Exception {
@Test
public void testLinenumberLanguagetagValidation() throws Exception {
- RDFParser ntriplesParser = createRDFParser();
- ntriplesParser.getParserConfig().addNonFatalError(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES);
- ntriplesParser.getParserConfig().set(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES, true);
- ntriplesParser.getParserConfig().set(BasicParserSettings.VERIFY_LANGUAGE_TAGS, true);
- ParseErrorCollector collector = new ParseErrorCollector();
- ntriplesParser.setParseErrorListener(collector);
-
- ntriplesParser.parse(
+ parser.getParserConfig().addNonFatalError(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES);
+ parser.getParserConfig().set(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES, true);
+ parser.getParserConfig().set(BasicParserSettings.VERIFY_LANGUAGE_TAGS, true);
+
+ parser.parse(
new StringReader(" \"hello\"@inv+alid ."),
NTRIPLES_TEST_URL);
- List errors = collector.getErrors();
+ List errors = errorCollector.getErrors();
assertEquals(1, errors.size());
assertTrue(errors.get(0).contains("(1, 32)"), "Unknown line number");
}
+ @Test
+ public void testDirLangStringLTR() {
+ final String data = " \"Hello\"@en--ltr .";
+ dirLangStringTestHelper(data, "en", Literal.LTR_SUFFIX, false, false);
+ }
+
+ @Test
+ public void testDirLangStringRTL() {
+ final String data = " \"שלום\"@he--rtl .";
+ dirLangStringTestHelper(data, "he", Literal.RTL_SUFFIX, false, false);
+ }
+
+ @Test
+ public void testDirLangStringLTRWithNormalization() {
+ final String data = " \"Hello\"@en--ltr .";
+ dirLangStringTestHelper(data, "en", Literal.LTR_SUFFIX, true, false);
+ }
+
+ @Test
+ public void testDirLangStringRTLWithNormalization() {
+ final String data = " \"שלום\"@HE--rtl .";
+ dirLangStringTestHelper(data, "he", Literal.RTL_SUFFIX, true, false);
+ }
+
+ @Test
+ public void testBadDirLangString() {
+ final String data = " \"hello\"@en--unk .";
+ dirLangStringTestHelper(data, "", "", true, true);
+ }
+
+ @Test
+ public void testBadCapitalizationDirLangString() {
+ final String data = " \"Hello\"@en--LTR .";
+ dirLangStringTestHelper(data, "", "", true, true);
+ }
+
+ @Test
+ public void testDirLangStringNoLanguage() throws IOException {
+ final String data = " \"Hello\"^^ .";
+ dirLangStringNoLanguageTestHelper(data);
+ }
+
protected abstract RDFParser createRDFParser();
}
diff --git a/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesWriterTest.java b/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesWriterTest.java
index 705727b203a..7cca3430323 100644
--- a/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesWriterTest.java
+++ b/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/AbstractNTriplesWriterTest.java
@@ -10,12 +10,14 @@
*******************************************************************************/
package org.eclipse.rdf4j.rio.ntriples;
+import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.RDFParserFactory;
import org.eclipse.rdf4j.rio.RDFWriterFactory;
import org.eclipse.rdf4j.rio.RDFWriterTest;
import org.eclipse.rdf4j.rio.RioSetting;
import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings;
import org.eclipse.rdf4j.rio.helpers.NTriplesWriterSettings;
+import org.junit.jupiter.api.Test;
/**
* @author Jeen Broekstra
@@ -34,4 +36,8 @@ protected RioSetting>[] getExpectedSupportedSettings() {
};
}
+ @Test
+ public void testDirLangString() throws Exception {
+ dirLangStringTest(RDFFormat.NTRIPLES);
+ }
}
diff --git a/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterTest.java b/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterTest.java
index 4b6c195b81d..9284845efff 100644
--- a/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterTest.java
+++ b/core/rio/ntriples/src/test/java/org/eclipse/rdf4j/rio/ntriples/NTriplesWriterTest.java
@@ -10,6 +10,17 @@
*******************************************************************************/
package org.eclipse.rdf4j.rio.ntriples;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.StringWriter;
+
+import org.eclipse.rdf4j.model.IRI;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.model.impl.DynamicModelFactory;
+import org.eclipse.rdf4j.rio.RDFFormat;
+import org.eclipse.rdf4j.rio.Rio;
+import org.junit.jupiter.api.Test;
+
/**
* JUnit test for the RDF/JSON parser.
*
diff --git a/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/AbstractTriGWriterTest.java b/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/AbstractTriGWriterTest.java
index 820f9304fda..f7b2df2fde5 100644
--- a/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/AbstractTriGWriterTest.java
+++ b/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/AbstractTriGWriterTest.java
@@ -10,12 +10,14 @@
*******************************************************************************/
package org.eclipse.rdf4j.rio.trig;
+import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.RDFParserFactory;
import org.eclipse.rdf4j.rio.RDFWriterFactory;
import org.eclipse.rdf4j.rio.RDFWriterTest;
import org.eclipse.rdf4j.rio.RioSetting;
import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings;
import org.eclipse.rdf4j.rio.helpers.TurtleWriterSettings;
+import org.junit.jupiter.api.Test;
/**
* @author Jeen Broesktra
@@ -36,4 +38,9 @@ protected RioSetting>[] getExpectedSupportedSettings() {
TurtleWriterSettings.ABBREVIATE_NUMBERS
};
}
+
+ @Test
+ public void testDirLangString() throws Exception {
+ dirLangStringTest(RDFFormat.TRIG);
+ }
}
diff --git a/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/TriGParserCustomTest.java b/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/TriGParserCustomTest.java
index 7f60657a274..db30bd384fd 100644
--- a/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/TriGParserCustomTest.java
+++ b/core/rio/trig/src/test/java/org/eclipse/rdf4j/rio/trig/TriGParserCustomTest.java
@@ -16,22 +16,20 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import java.io.IOException;
import java.io.StringReader;
import java.util.concurrent.TimeUnit;
import org.eclipse.rdf4j.model.BNode;
+import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Model;
-import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
-import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.util.Models;
-import org.eclipse.rdf4j.rio.ParserConfig;
+import org.eclipse.rdf4j.rio.AbstractParserTest;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.RDFParseException;
import org.eclipse.rdf4j.rio.RDFParser;
import org.eclipse.rdf4j.rio.Rio;
-import org.eclipse.rdf4j.rio.helpers.BasicParserSettings;
-import org.eclipse.rdf4j.rio.helpers.ParseErrorCollector;
import org.eclipse.rdf4j.rio.helpers.StatementCollector;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -43,34 +41,21 @@
* @author Peter Ansell
*/
@Timeout(value = 10, unit = TimeUnit.MINUTES)
-public class TriGParserCustomTest {
- private ValueFactory vf;
+public class TriGParserCustomTest extends AbstractParserTest {
- private ParserConfig settingsNoVerifyLangTag;
+ private Model model;
- private ParseErrorCollector errors;
-
- private RDFParser parser;
-
- private StatementCollector statementCollector;
-
- /**
- */
@BeforeEach
public void setUp() {
- vf = SimpleValueFactory.getInstance();
- settingsNoVerifyLangTag = new ParserConfig();
- settingsNoVerifyLangTag.set(BasicParserSettings.VERIFY_LANGUAGE_TAGS, false);
- errors = new ParseErrorCollector();
- parser = Rio.createParser(RDFFormat.TRIG);
- statementCollector = new StatementCollector(new LinkedHashModel());
- parser.setRDFHandler(statementCollector);
+ model = new LinkedHashModel();
+ statementCollector = new StatementCollector(model);
+ super.setUp();
}
@Test
public void testSPARQLGraphKeyword() throws Exception {
- Model model = Rio.parse(new StringReader("GRAPH { [] \"Foo\" }"), "",
- RDFFormat.TRIG);
+ String data = "GRAPH { [] \"Foo\" }";
+ parser.parse(new StringReader(data));
assertEquals(1, model.size());
assertNotNull(model.contexts().iterator().next());
@@ -82,8 +67,8 @@ public void testSPARQLGraphKeyword() throws Exception {
@Test
public void testGraph() throws Exception {
- Model model = Rio.parse(new StringReader(" { [] \"Foo\" }"), "",
- RDFFormat.TRIG);
+ String data = " { [] \"Foo\" }";
+ parser.parse(new StringReader(data));
assertEquals(1, model.size());
assertNotNull(model.contexts().iterator().next());
@@ -95,9 +80,8 @@ public void testGraph() throws Exception {
@Test
public void testGraphLocalNameGraph() throws Exception {
- Model model = Rio.parse(
- new StringReader("@prefix graph: .\n graph:a { [] \"Foo\" }"), "",
- RDFFormat.TRIG);
+ String data = "@prefix graph: .\n graph:a { [] \"Foo\" }";
+ parser.parse(new StringReader(data));
assertEquals(1, model.size());
assertNotNull(model.contexts().iterator().next());
@@ -109,9 +93,8 @@ public void testGraphLocalNameGraph() throws Exception {
@Test
public void testGraphLocalNameIntegerGraph() throws Exception {
- Model model = Rio.parse(
- new StringReader("@prefix graph: .\n graph:1 { [] \"Foo\" }"), "",
- RDFFormat.TRIG);
+ String data = "@prefix graph: .\n graph:1 { [] \"Foo\" }";
+ parser.parse(new StringReader(data));
assertEquals(1, model.size());
assertNotNull(model.contexts().iterator().next());
@@ -123,9 +106,8 @@ public void testGraphLocalNameIntegerGraph() throws Exception {
@Test
public void testGraphLocalNameNotGraph() throws Exception {
- Model model = Rio.parse(
- new StringReader("@prefix ex: .\n ex:a { [] \"Foo\" }"), "",
- RDFFormat.TRIG);
+ String data = "@prefix ex: .\n ex:a { [] \"Foo\" }";
+ parser.parse(new StringReader(data));
assertEquals(1, model.size());
assertNotNull(model.contexts().iterator().next());
@@ -137,9 +119,8 @@ public void testGraphLocalNameNotGraph() throws Exception {
@Test
public void testGraphLocalNameIntegerNotGraph() throws Exception {
- Model model = Rio.parse(
- new StringReader("@prefix ex: .\n ex:1 { [] \"Foo\" }"), "",
- RDFFormat.TRIG);
+ String data = "@prefix ex: .\n ex:1 { [] \"Foo\" }";
+ parser.parse(new StringReader(data));
assertEquals(1, model.size());
assertNotNull(model.contexts().iterator().next());
@@ -151,40 +132,38 @@ public void testGraphLocalNameIntegerNotGraph() throws Exception {
@Test
public void testTrailingSemicolon() throws Exception {
- Rio.parse(new StringReader("{ ;}"), "", RDFFormat.TRIG);
+ parser.parse(new StringReader("{ ;}"), "");
}
@Test
public void testAnonymousGraph1() throws Exception {
- Rio.parse(new StringReader("PREFIX : \n GRAPH [] { :s :p :o }"), "", RDFFormat.TRIG);
+ parser.parse(new StringReader("PREFIX : \n GRAPH [] { :s :p :o }"), "");
}
@Test
public void testAnonymousGraph2() throws Exception {
- Rio.parse(new StringReader("PREFIX : \n [] { :s :p :o }"), "", RDFFormat.TRIG);
+ parser.parse(new StringReader("PREFIX : \n [] { :s :p :o }"), "");
}
@Test
public void testTurtle() throws Exception {
- Rio.parse(new StringReader(" "), "", RDFFormat.TRIG);
+ parser.parse(new StringReader(" "), "");
}
@Test
public void testMinimalWhitespace() throws Exception {
- Rio.parse(this.getClass().getResourceAsStream("/testcases/trig/trig-syntax-minimal-whitespace-01.trig"), "",
- RDFFormat.TRIG);
+ parser.parse(this.getClass().getResourceAsStream("/testcases/trig/trig-syntax-minimal-whitespace-01.trig"), "");
}
@Test
public void testMinimalWhitespaceLine12() throws Exception {
- Rio.parse(new StringReader("@prefix : . {_:s:p :o ._:s:p\"Alice\". _:s:p _:o .}"), "",
- RDFFormat.TRIG);
+ parser.parse(new StringReader("@prefix : . {_:s:p :o ._:s:p\"Alice\". _:s:p _:o .}"), "");
}
@Test
public void testBadPname02() throws Exception {
try {
- Rio.parse(new StringReader("@prefix : . {:a%2 :p :o .}"), "", RDFFormat.TRIG);
+ parser.parse(new StringReader("@prefix : . {:a%2 :p :o .}"), "");
fail("Did not receive expected exception");
} catch (RDFParseException e) {
@@ -198,35 +177,103 @@ public void testSupportedSettings() {
@Test
public void testParseTruePrefix() throws Exception {
- Rio.parse(new StringReader("@prefix true: . {true:s true:p true:o .}"), "", RDFFormat.TRIG);
+ parser.parse(new StringReader("@prefix true: . {true:s true:p true:o .}"), "");
}
@Test
public void testParseTrig_booleanLiteral() throws Exception {
String trig = "{\n" + " true.\n" + "}";
- Model m = Rio.parse(new StringReader(trig), "http://ex/", RDFFormat.TRIG);
- assertEquals(1, m.size());
+ parser.parse(new StringReader(trig), "http://ex/");
+ assertEquals(1, model.size());
}
@Test
public void testParseTrig_booleanLiteral_space() throws Exception {
String trig = "{\n" + " true .\n" + "}";
- Model m = Rio.parse(new StringReader(trig), "http://ex/", RDFFormat.TRIG);
- assertEquals(1, m.size());
+ parser.parse(new StringReader(trig), "http://ex/");
+ assertEquals(1, model.size());
}
@Test
public void testParseTrig_intLiteral() throws Exception {
String trig = "{\n" + " 1.\n" + "}";
- Model m = Rio.parse(new StringReader(trig), "http://ex/", RDFFormat.TRIG);
- assertEquals(1, Models.objectLiteral(m).get().intValue());
+ parser.parse(new StringReader(trig), "http://ex/");
+ assertEquals(1, Models.objectLiteral(model).get().intValue());
}
@Test
public void testParseTrig_doubleLiteral() throws Exception {
String trig = "{\n" + " 1.2.\n" + "}";
- Model m = Rio.parse(new StringReader(trig), "http://ex/", RDFFormat.TRIG);
- assertEquals(1.2d, Models.objectLiteral(m).get().doubleValue(), 0.01);
+ parser.parse(new StringReader(trig), "http://ex/");
+ assertEquals(1.2d, Models.objectLiteral(model).get().doubleValue(), 0.01);
+ }
+
+ @Test
+ public void testDirLangStringRTLNoContext() {
+ String data = " \"שלום\"@he--rtl";
+ dirLangStringTest(data, false, "he", Literal.RTL_SUFFIX, false, false);
+ }
+
+ @Test
+ public void testDirLangStringRTLWithContext() {
+ String data = " \"שלום\"@he--rtl";
+ dirLangStringTest(data, true, "he", Literal.RTL_SUFFIX, false, false);
+ }
+
+ @Test
+ public void testDirLangStringLTRWithNormalizationNoContext() {
+ String data = " \"Hello\"@en--ltr";
+ dirLangStringTest(data, false, "en", Literal.LTR_SUFFIX, true, false);
+ }
+
+ @Test
+ public void testDirLangStringLTRWithNormalizationWithContext() {
+ String data = " \"Hello\"@en--ltr";
+ dirLangStringTest(data, true, "en", Literal.LTR_SUFFIX, true, false);
+ }
+
+ @Test
+ public void testBadDirLangStringNoContext() {
+ String data = " \"hello\"@en--unk";
+ dirLangStringTest(data, false, "", "", true, true);
+ }
+
+ @Test
+ public void testBadDirLangStringWithContext() {
+ String data = " \"hello\"@en--unk";
+ dirLangStringTest(data, true, "", "", true, true);
+ }
+
+ @Test
+ public void testBadCapitalizationDirLangStringNoContext() {
+ String data = " \"Hello\"@en--LTR";
+ dirLangStringTest(data, false, "", "", true, true);
+ }
+
+ @Test
+ public void testBadCapitalizationDirLangStringWithContext() {
+ final String data = " \"Hello\"@en--LTR";
+ dirLangStringTest(data, true, "", "", true, true);
+ }
+
+ @Test
+ public void testDirLangStringNoLanguage() throws IOException {
+ final String data = " \"Hello\"^^ .";
+ dirLangStringNoLanguageTestHelper(data);
}
+ private void dirLangStringTest(
+ final String triple, final boolean withContext, final String expectedLang, final String expectedBaseDir,
+ final boolean normalize,
+ final boolean shouldCauseException) {
+ final String data = (withContext ? " { " : "") + triple + " ."
+ + (withContext ? " }" : "");
+
+ dirLangStringTestHelper(data, expectedLang, expectedBaseDir, normalize, shouldCauseException);
+ }
+
+ @Override
+ public RDFParser createRDFParser() {
+ return new TriGParser();
+ }
}
diff --git a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriter.java b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriter.java
index 75460b08952..e4c052190a6 100644
--- a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriter.java
+++ b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleWriter.java
@@ -682,6 +682,7 @@ protected void writeLiteral(Literal lit) throws IOException {
// Append the literal's language
writer.write("@");
writer.write(lit.getLanguage().get());
+ writer.write(lit.getBaseDirection().toString());
} else if (!xsdStringToPlainLiteral || !XSD.STRING.equals(datatype)) {
// Append the literal's datatype (possibly written as an abbreviated
// URI)
diff --git a/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/AbstractTurtleWriterTest.java b/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/AbstractTurtleWriterTest.java
index 1a4977f12f8..20f6b87d54c 100644
--- a/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/AbstractTurtleWriterTest.java
+++ b/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/AbstractTurtleWriterTest.java
@@ -15,12 +15,10 @@
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.XSD;
-import org.eclipse.rdf4j.rio.RDFParserFactory;
-import org.eclipse.rdf4j.rio.RDFWriterFactory;
-import org.eclipse.rdf4j.rio.RDFWriterTest;
-import org.eclipse.rdf4j.rio.RioSetting;
+import org.eclipse.rdf4j.rio.*;
import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings;
import org.eclipse.rdf4j.rio.helpers.TurtleWriterSettings;
+import org.junit.jupiter.api.Test;
/**
* @author Jeen Broekstra
@@ -49,4 +47,9 @@ protected Model getAbbrevTestModel() {
m.add(Values.iri("http://www.example.com/decimal"), RDF.VALUE, Values.literal("55.66", XSD.DECIMAL));
return m;
}
+
+ @Test
+ public void dirLangStringTest() throws Exception {
+ dirLangStringTest(RDFFormat.TURTLE);
+ }
}
diff --git a/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleParserTest.java b/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleParserTest.java
index 1ac74f3ac32..69042bf988b 100644
--- a/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleParserTest.java
+++ b/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleParserTest.java
@@ -25,6 +25,7 @@
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
@@ -32,46 +33,36 @@
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Triple;
import org.eclipse.rdf4j.model.ValueFactory;
+import org.eclipse.rdf4j.model.base.CoreDatatype;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.vocabulary.DC;
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
import org.eclipse.rdf4j.model.vocabulary.FOAF;
import org.eclipse.rdf4j.model.vocabulary.XSD;
+import org.eclipse.rdf4j.rio.AbstractParserTest;
+import org.eclipse.rdf4j.rio.LanguageHandler;
import org.eclipse.rdf4j.rio.RDFParseException;
import org.eclipse.rdf4j.rio.RDFParser;
import org.eclipse.rdf4j.rio.helpers.BasicParserSettings;
-import org.eclipse.rdf4j.rio.helpers.ParseErrorCollector;
-import org.eclipse.rdf4j.rio.helpers.SimpleParseLocationListener;
import org.eclipse.rdf4j.rio.helpers.StatementCollector;
import org.eclipse.rdf4j.rio.helpers.TurtleParserSettings;
-import org.junit.jupiter.api.BeforeEach;
+import org.eclipse.rdf4j.rio.languages.RFC3066LanguageHandler;
import org.junit.jupiter.api.Test;
/**
* @author jeen
*/
-public class TurtleParserTest {
-
- private TurtleParser parser;
+public class TurtleParserTest extends AbstractParserTest {
private final ValueFactory vf = SimpleValueFactory.getInstance();
- private final ParseErrorCollector errorCollector = new ParseErrorCollector();
-
- private final StatementCollector statementCollector = new StatementCollector();
-
private final String prefixes = "@prefix ex: . \n@prefix : . \n";
private final String baseURI = "http://example.org/";
- private final SimpleParseLocationListener locationListener = new SimpleParseLocationListener();
-
- @BeforeEach
- public void setUp() {
- parser = new TurtleParser();
- parser.setParseErrorListener(errorCollector);
- parser.setRDFHandler(statementCollector);
- parser.setParseLocationListener(locationListener);
+ @Override
+ protected RDFParser createRDFParser() {
+ return new TurtleParser();
}
@Test
@@ -275,99 +266,80 @@ public void testLineNumberReporting() throws IOException {
final String error = errorCollector.getFatalErrors().get(0);
// expected to fail at line 9.
assertTrue(error.contains("(9,"));
- assertEquals(9, locationListener.getLineNo());
- assertEquals(-1, locationListener.getColumnNo());
+ locationListener.assertListener(9, -1);
}
}
@Test
public void testLineNumberReportingNoErrorsSingleLine() throws IOException {
- assertEquals(0, locationListener.getLineNo());
- assertEquals(0, locationListener.getColumnNo());
+ locationListener.assertListener(0, 0);
Reader in = new StringReader(" .");
parser.parse(in, baseURI);
- assertEquals(1, locationListener.getLineNo());
- assertEquals(-1, locationListener.getColumnNo());
+ locationListener.assertListener(1, -1);
}
@Test
public void testLineNumberReportingNoErrorsSingleLineEndNewline() throws IOException {
- assertEquals(0, locationListener.getLineNo());
- assertEquals(0, locationListener.getColumnNo());
+ locationListener.assertListener(0, 0);
Reader in = new StringReader(" .\n");
parser.parse(in, baseURI);
- assertEquals(2, locationListener.getLineNo());
- assertEquals(-1, locationListener.getColumnNo());
+ locationListener.assertListener(2, -1);
}
@Test
public void testLineNumberReportingNoErrorsMultipleLinesNoEndNewline() throws IOException {
- assertEquals(0, locationListener.getLineNo());
- assertEquals(0, locationListener.getColumnNo());
+ locationListener.assertListener(0, 0);
Reader in = new StringReader(" .\n .");
parser.parse(in, baseURI);
- assertEquals(2, locationListener.getLineNo());
- assertEquals(-1, locationListener.getColumnNo());
+ locationListener.assertListener(2, -1);
}
@Test
public void testLineNumberReportingNoErrorsMultipleLinesEndNewline() throws IOException {
- assertEquals(0, locationListener.getLineNo());
- assertEquals(0, locationListener.getColumnNo());
+ locationListener.assertListener(0, 0);
Reader in = new StringReader(" .\n