Skip to content

Commit 968e855

Browse files
committed
GH-5327 changed base direction implementation to store as separate attribute
1 parent bbb3caf commit 968e855

File tree

34 files changed

+369
-117
lines changed

34 files changed

+369
-117
lines changed

core/model-api/src/main/java/org/eclipse/rdf4j/model/Literal.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ default boolean isLiteral() {
6565
*/
6666
Optional<String> getLanguage();
6767

68+
default String getBaseDirection() {
69+
return "";
70+
}
71+
6872
/**
6973
* Gets the datatype for this literal.
7074
* <p>

core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ public interface ValueFactory {
8686
*/
8787
Literal createLiteral(String label, String language);
8888

89+
/**
90+
* Creates a new literal with the supplied label and language attribute. The return value of
91+
* {@link Literal#getDatatype()} for the returned object must be
92+
* <a href="http://www.w3.org/1999/02/22-rdf-syntax-ns#langString">{@code rdf:langString}</a>.
93+
*
94+
* @param label The literal's label, must not be <var>null</var>.
95+
* @param language The literal's language attribute, must not be <var>null</var>.
96+
* @param baseDirection The literal's base direction, either "", "--ltr", or "--rtl".
97+
* @return A literal for the specified value and language attribute.
98+
*/
99+
Literal createLiteral(String label, String language, String baseDirection);
100+
89101
/**
90102
* Creates a new literal with the supplied label and datatype.
91103
*

core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public String toString() {
187187

188188
return getLanguage()
189189

190-
.map(language -> label + '@' + language)
190+
.map(language -> label + '@' + language + getBaseDirection())
191191

192192
.orElseGet(() -> CoreDatatype.XSD.STRING == getCoreDatatype() ? label
193193
: label + "^^<" + getDatatype().stringValue() + ">");
@@ -201,6 +201,32 @@ private boolean equals(Optional<String> x, Optional<String> y) {
201201
return px && py && x.get().equalsIgnoreCase(y.get()) || !px && !py;
202202
}
203203

204+
public enum BaseDirection {
205+
NONE(""),
206+
LTR(Literal.LTR_SUFFIX),
207+
RTL(Literal.RTL_SUFFIX);
208+
209+
private final String suffix;
210+
211+
BaseDirection(final String suffix) {
212+
this.suffix = suffix;
213+
}
214+
215+
public String getSuffix() {
216+
return suffix;
217+
}
218+
219+
public static BaseDirection fromString(String dir) {
220+
if (dir == null || dir.isEmpty())
221+
return NONE;
222+
if (dir.equalsIgnoreCase(Literal.LTR_SUFFIX))
223+
return LTR;
224+
if (dir.equalsIgnoreCase(Literal.RTL_SUFFIX))
225+
return RTL;
226+
return NONE;
227+
}
228+
}
229+
204230
static class TypedLiteral extends AbstractLiteral {
205231

206232
private static final long serialVersionUID = -19640527584237291L;
@@ -269,12 +295,16 @@ static class TaggedLiteral extends AbstractLiteral {
269295

270296
private final String label;
271297
private final String language;
272-
private final boolean hasBaseDirection;
298+
private final BaseDirection baseDirection;
273299

274300
TaggedLiteral(String label, String language) {
301+
this(label, language, null);
302+
}
303+
304+
TaggedLiteral(String label, String language, String baseDirection) {
275305
this.label = label;
276306
this.language = language;
277-
hasBaseDirection = language.endsWith(Literal.LTR_SUFFIX) || language.endsWith(Literal.RTL_SUFFIX);
307+
this.baseDirection = BaseDirection.fromString(baseDirection);
278308
}
279309

280310
@Override
@@ -287,14 +317,20 @@ public Optional<String> getLanguage() {
287317
return Optional.of(language);
288318
}
289319

320+
@Override
321+
public String getBaseDirection() {
322+
return baseDirection.suffix;
323+
}
324+
290325
@Override
291326
public IRI getDatatype() {
292-
return hasBaseDirection ? CoreDatatype.RDF.DIRLANGSTRING.getIri() : CoreDatatype.RDF.LANGSTRING.getIri();
327+
return baseDirection == BaseDirection.NONE ? CoreDatatype.RDF.LANGSTRING.getIri()
328+
: CoreDatatype.RDF.DIRLANGSTRING.getIri();
293329
}
294330

295331
@Override
296332
public CoreDatatype.RDF getCoreDatatype() {
297-
return hasBaseDirection ? CoreDatatype.RDF.DIRLANGSTRING : CoreDatatype.RDF.LANGSTRING;
333+
return baseDirection == BaseDirection.NONE ? CoreDatatype.RDF.LANGSTRING : CoreDatatype.RDF.DIRLANGSTRING;
298334
}
299335
}
300336

core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDataty
146146

147147
@Override
148148
public Literal createLiteral(String label, String language) {
149+
return createLiteral(label, language, null);
150+
}
151+
152+
@Override
153+
public Literal createLiteral(String label, String language, String baseDirection) {
149154

150155
Objects.requireNonNull(label, "null label");
151156
Objects.requireNonNull(language, "null language");
@@ -154,7 +159,7 @@ public Literal createLiteral(String label, String language) {
154159
throw new IllegalArgumentException("empty language tag");
155160
}
156161

157-
return new TaggedLiteral(label, language);
162+
return new TaggedLiteral(label, language, baseDirection);
158163
}
159164

160165
@Override

core/model-api/src/test/java/org/eclipse/rdf4j/model/LiteralTest.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ public abstract class LiteralTest {
104104
*/
105105
protected abstract Literal literal(String label, String language);
106106

107+
/**
108+
* Creates a test literal instance.
109+
*
110+
* @param label the label of the literal
111+
* @param language the language of the literal
112+
* @param dir the language direction of the literal
113+
* @return a new instance of the concrete literal class under test
114+
*/
115+
protected abstract Literal literal(String label, String language, String dir);
116+
107117
/**
108118
* Creates a test literal instance.
109119
*
@@ -173,17 +183,20 @@ public final void testTaggedConstructor() {
173183

174184
assertThat(languageLiteral.getLabel()).isEqualTo(label);
175185
assertThat(languageLiteral.getLanguage()).contains(language);
186+
assertThat(languageLiteral.getBaseDirection()).isEmpty();
176187
assertThat(languageLiteral.getDatatype().stringValue()).isEqualTo(RDF_LANG_STRING);
177188

178-
final String directedLanguage = "en--ltr";
179-
final Literal directedLanguageLiteral = literal(label, directedLanguage);
189+
final Literal directedLanguageLiteral = literal(label, language, Literal.LTR_SUFFIX);
180190

181191
assertThat(directedLanguageLiteral.getLabel()).isEqualTo(label);
182-
assertThat(directedLanguageLiteral.getLanguage()).contains(directedLanguage);
192+
assertThat(directedLanguageLiteral.getLanguage()).contains(language);
193+
assertThat(directedLanguageLiteral.getBaseDirection()).isEqualTo(Literal.LTR_SUFFIX);
183194
assertThat(directedLanguageLiteral.getDatatype().stringValue()).isEqualTo(RDF_DIR_LANG_STRING);
184195

185196
assertThatNullPointerException().isThrownBy(() -> literal(null, (String) null));
186197
assertThatNullPointerException().isThrownBy(() -> literal("", (String) null));
198+
assertThatNullPointerException().isThrownBy(() -> literal("", (String) null, ""));
199+
assertThatNullPointerException().isThrownBy(() -> literal("", (String) null, Literal.LTR_SUFFIX));
187200
assertThatNullPointerException().isThrownBy(() -> literal(null, ""));
188201
assertThatNullPointerException().isThrownBy(() -> literal(null, (IRI) null));
189202

@@ -883,14 +896,16 @@ public final void testCoreDatatypeTaggedConstructor() {
883896
String direction = "--ltr";
884897

885898
Literal languageLiteral = literal(label, language);
886-
Literal directedLanguageLiteral = literal(label, language + direction);
899+
Literal directedLanguageLiteral = literal(label, language, direction);
887900

888901
assertThat(languageLiteral.getLabel()).isEqualTo(label);
889902
assertThat(languageLiteral.getLanguage()).contains(language);
903+
assertThat(languageLiteral.getBaseDirection()).isEmpty();
890904
assertThat(languageLiteral.getCoreDatatype()).isEqualTo(CoreDatatype.RDF.LANGSTRING);
891905

892906
assertThat(directedLanguageLiteral.getLabel()).isEqualTo(label);
893-
assertThat(directedLanguageLiteral.getLanguage()).contains(language + direction);
907+
assertThat(directedLanguageLiteral.getLanguage()).contains(language);
908+
assertThat(directedLanguageLiteral.getBaseDirection()).isEqualTo(direction);
894909
assertThat(directedLanguageLiteral.getCoreDatatype()).isEqualTo(CoreDatatype.RDF.DIRLANGSTRING);
895910

896911
assertThatNullPointerException().isThrownBy(() -> literal(null, (String) null));
@@ -1518,17 +1533,19 @@ public final void testSerializationWithCoreDatatypeRdfLangString() {
15181533

15191534
@Test
15201535
public final void testSerializationWithCoreDatatypeRdfDirLangString() {
1521-
Literal literal = literal("hello", "en--ltr");
1536+
Literal literal = literal("hello", "en", "--ltr");
15221537
assertEquals(CoreDatatype.RDF.DIRLANGSTRING, literal.getCoreDatatype());
15231538
assertThat(literal.getLanguage()).isPresent();
1524-
assertEquals("en--ltr", literal.getLanguage().get());
1539+
assertEquals("en", literal.getLanguage().get());
1540+
assertEquals("--ltr", literal.getBaseDirection());
15251541

15261542
byte[] bytes = objectToBytes(literal);
15271543
Literal roundTrip = (Literal) bytesToObject(bytes);
15281544

15291545
assertEquals(CoreDatatype.RDF.DIRLANGSTRING, roundTrip.getCoreDatatype());
15301546
assertThat(roundTrip.getLanguage()).isPresent();
1531-
assertEquals("en--ltr", roundTrip.getLanguage().get());
1547+
assertEquals("en", roundTrip.getLanguage().get());
1548+
assertEquals("--ltr", roundTrip.getBaseDirection());
15321549
}
15331550

15341551
@Test

core/model-api/src/test/java/org/eclipse/rdf4j/model/ValueFactoryTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import javax.xml.datatype.DatatypeFactory;
7272
import javax.xml.datatype.XMLGregorianCalendar;
7373

74+
import org.eclipse.rdf4j.model.base.CoreDatatype;
7475
import org.junit.jupiter.api.Test;
7576

7677
/**
@@ -537,4 +538,15 @@ public void testCreateLiteralDate() throws DatatypeConfigurationException {
537538

538539
}
539540

541+
@Test
542+
public void testCreateDirLangLiteral() {
543+
final Literal literal = factory().createLiteral("label", "he", "--rtl");
544+
545+
assertThat(literal).isNotNull();
546+
assertThat(literal.getLabel()).isEqualTo("label");
547+
assertThat(literal.getLanguage()).contains("he");
548+
assertThat(literal.getBaseDirection()).isEqualTo("--rtl");
549+
assertThat(literal.getDatatype()).isEqualTo(CoreDatatype.RDF.DIRLANGSTRING.getIri());
550+
}
551+
540552
}

core/model-api/src/test/java/org/eclipse/rdf4j/model/base/AbstractLiteralTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111

1212
package org.eclipse.rdf4j.model.base;
1313

14+
import static org.assertj.core.api.Assertions.assertThat;
15+
1416
import org.eclipse.rdf4j.model.IRI;
1517
import org.eclipse.rdf4j.model.Literal;
1618
import org.eclipse.rdf4j.model.LiteralTest;
1719
import org.eclipse.rdf4j.model.ValueFactory;
1820
import org.eclipse.rdf4j.model.base.AbstractValueFactoryTest.GenericValueFactory;
21+
import org.junit.jupiter.api.Test;
1922

2023
/**
2124
* Unit tests for {@link AbstractLiteral}.
@@ -37,6 +40,11 @@ protected Literal literal(String label, String language) {
3740
return factory.createLiteral(label, language);
3841
}
3942

43+
@Override
44+
protected Literal literal(String label, String language, String dir) {
45+
return factory.createLiteral(label, language, dir);
46+
}
47+
4048
@Override
4149
protected Literal literal(String label, IRI datatype) {
4250
return factory.createLiteral(label, datatype);
@@ -52,4 +60,21 @@ protected IRI datatype(String iri) {
5260
return factory.createIRI(iri);
5361
}
5462

63+
@Test
64+
void testBaseDirectionEnumFromString() {
65+
assertThat(AbstractLiteral.BaseDirection.fromString("")).isEqualTo(AbstractLiteral.BaseDirection.NONE);
66+
assertThat(AbstractLiteral.BaseDirection.fromString(Literal.LTR_SUFFIX)).isEqualTo(
67+
AbstractLiteral.BaseDirection.LTR);
68+
assertThat(AbstractLiteral.BaseDirection.fromString(Literal.RTL_SUFFIX)).isEqualTo(
69+
AbstractLiteral.BaseDirection.RTL);
70+
assertThat(AbstractLiteral.BaseDirection.fromString("invalid")).isEqualTo(AbstractLiteral.BaseDirection.NONE);
71+
}
72+
73+
@Test
74+
void testBaseDirectionEnumSuffix() {
75+
assertThat(AbstractLiteral.BaseDirection.LTR.getSuffix()).isEqualTo(Literal.LTR_SUFFIX);
76+
assertThat(AbstractLiteral.BaseDirection.RTL.getSuffix()).isEqualTo(Literal.RTL_SUFFIX);
77+
assertThat(AbstractLiteral.BaseDirection.NONE.getSuffix()).isEmpty();
78+
}
79+
5580
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public class SimpleLiteral extends AbstractLiteral {
6363
// Cached CoreDatatype, or null if not yet computed.
6464
private CoreDatatype coreDatatype = null;
6565

66+
/**
67+
* The literal's base direction.
68+
*/
69+
private BaseDirection baseDirection = BaseDirection.NONE;
70+
6671
/*--------------*
6772
* Constructors *
6873
*--------------*/
@@ -88,8 +93,13 @@ protected SimpleLiteral(String label) {
8893
* @param language The language tag for the literal, must not be <var>null</var> and not be empty.
8994
*/
9095
protected SimpleLiteral(String label, String language) {
96+
this(label, language, "");
97+
}
98+
99+
protected SimpleLiteral(String label, String language, String baseDirection) {
91100
setLabel(label);
92101
setLanguage(language);
102+
setBaseDirection(baseDirection);
93103
}
94104

95105
/**
@@ -164,8 +174,11 @@ protected void setLanguage(String language) {
164174
}
165175
this.language = language;
166176
optionalLanguageCache = Optional.of(language);
177+
}
167178

168-
if (language.endsWith(Literal.LTR_SUFFIX) || language.endsWith(Literal.RTL_SUFFIX)) {
179+
protected void setBaseDirection(String baseDirection) {
180+
this.baseDirection = BaseDirection.fromString(baseDirection);
181+
if (this.baseDirection == BaseDirection.LTR || this.baseDirection == BaseDirection.RTL) {
169182
setDatatype(CoreDatatype.RDF.DIRLANGSTRING);
170183
} else {
171184
setDatatype(CoreDatatype.RDF.LANGSTRING);
@@ -180,6 +193,10 @@ public Optional<String> getLanguage() {
180193
return optionalLanguageCache;
181194
}
182195

196+
public String getBaseDirection() {
197+
return baseDirection.getSuffix();
198+
}
199+
183200
protected void setDatatype(IRI datatype) {
184201
this.datatype = datatype;
185202
coreDatatype = CoreDatatype.from(datatype);
@@ -266,6 +283,7 @@ public String toString() {
266283
StringBuilder sb = new StringBuilder(label.length() + language.length() + 3);
267284
sb.append('"').append(label).append('"');
268285
sb.append('@').append(language);
286+
sb.append(getBaseDirection());
269287
return sb.toString();
270288
} else if (org.eclipse.rdf4j.model.vocabulary.XSD.STRING.equals(datatype) || datatype == null) {
271289
StringBuilder sb = new StringBuilder(label.length() + 2);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ public Literal createLiteral(String value, String language) {
103103
return new SimpleLiteral(value, language);
104104
}
105105

106+
@Override
107+
public Literal createLiteral(String value, String language, String baseDirection) {
108+
return new SimpleLiteral(value, language, baseDirection);
109+
}
110+
106111
@Override
107112
public Literal createLiteral(boolean b) {
108113
return b ? BooleanLiteral.TRUE : BooleanLiteral.FALSE;

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,15 @@ public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDataty
134134

135135
@Override
136136
public Literal createLiteral(String label, String language) {
137+
return createLiteral(label, language, "");
138+
}
139+
140+
@Override
141+
public Literal createLiteral(String label, String language, String baseDirection) {
137142
if (!Literals.isValidLanguageTag(language)) {
138143
throw new IllegalArgumentException("Not a valid language tag: " + language);
139144
}
140-
return delegate.createLiteral(label, language);
145+
return delegate.createLiteral(label, language, baseDirection);
141146
}
142147

143148
@Override

0 commit comments

Comments
 (0)