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 183292d241a..a1bacd16b41 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 @@ -100,6 +100,16 @@ public interface ValueFactory { */ Literal createLiteral(String label, CoreDatatype datatype); + /** + * Creates a new literal with the supplied label and datatype. + * + * @param label The literal's label, must not be null. + * @param datatype The literal's datatype. If it is null, the datatype + * {@code xsd:string} will be assigned to this + * literal. + */ + Literal createLiteral(String label, IRI datatype, CoreDatatype coreDatatype); + /** * Creates a new xsd:boolean-typed literal representing the specified value. * 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 e9f7a113330..f8c16a70bcf 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 @@ -236,6 +236,16 @@ static class TypedLiteral extends AbstractLiteral { this.datatype = datatype.getIri(); } + TypedLiteral(String label, IRI datatype, CoreDatatype coreDatatype) { + assert datatype != null; + assert coreDatatype != null; + assert coreDatatype == CoreDatatype.NONE || datatype == coreDatatype.getIri(); + + this.label = label; + this.datatype = datatype; + this.coreDatatype = coreDatatype; + } + @Override public String getLabel() { return label; @@ -379,7 +389,7 @@ private static String toString(double value) { protected Number value; private final String label; - private final CoreDatatype datatype; + private final CoreDatatype.XSD datatype; NumberLiteral(byte value) { this(value, Byte.toString(value), CoreDatatype.XSD.BYTE); @@ -405,7 +415,7 @@ private static String toString(double value) { this(value, toString(value), CoreDatatype.XSD.DOUBLE); } - NumberLiteral(Number value, String label, CoreDatatype datatype) { + NumberLiteral(Number value, String label, CoreDatatype.XSD datatype) { this.value = value; this.label = label; this.datatype = datatype; @@ -585,7 +595,7 @@ static class TemporalAccessorLiteral extends AbstractLiteral { .toFormatter(); - private static final Map DATATYPES = datatypes(); + private static final Map DATATYPES = datatypes(); private static final Map FORMATTERS = formatters(); static TemporalAccessor parseTemporalAccessor(String label) throws DateTimeException { @@ -601,14 +611,14 @@ static TemporalAccessor parseTemporalAccessor(String label) throws DateTimeExcep return value; } - private static Map datatypes() { + private static Map datatypes() { int date = key(YEAR, MONTH_OF_YEAR, DAY_OF_MONTH); int time = key(HOUR_OF_DAY, MINUTE_OF_HOUR, SECOND_OF_MINUTE); int nano = key(NANO_OF_SECOND); int zone = key(OFFSET_SECONDS); - Map datatypes = new HashMap<>(); + Map datatypes = new HashMap<>(); datatypes.put(date + time, CoreDatatype.XSD.DATETIME); datatypes.put(date + time + nano, CoreDatatype.XSD.DATETIME); @@ -689,7 +699,7 @@ private static int key(Predicate include, ChronoField... fields) { private final TemporalAccessor value; private final String label; - private final CoreDatatype datatype; + private final CoreDatatype.XSD datatype; TemporalAccessorLiteral(TemporalAccessor value) { 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 9938c299e77..b75ecf02d99 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 @@ -128,6 +128,19 @@ public Literal createLiteral(String label, CoreDatatype datatype) { return new TypedLiteral(label, datatype); } + @Override + public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDatatype) { + Objects.requireNonNull(label, "Label may not be null"); + Objects.requireNonNull(datatype, "Datatype may not be null"); + Objects.requireNonNull(coreDatatype, "CoreDatatype may not be null"); + + if (reserved(coreDatatype)) { + throw new IllegalArgumentException("reserved datatype <" + datatype + ">"); + } + + return new TypedLiteral(label, datatype, coreDatatype); + } + @Override public Literal createLiteral(String label, String language) { 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 497e49b09e5..c37230a4bd6 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 @@ -263,10 +263,17 @@ public Optional asXSDDatatype() { return optional; } + @Override + public String toString() { + return iri.toString(); + } + } enum RDF implements CoreDatatype { + HTML(iri("HTML")), + XMLLITERAL(iri("XMLLiteral")), LANGSTRING(iri("langString")); public static final String NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; @@ -299,6 +306,11 @@ public Optional asRDFDatatype() { return optional; } + @Override + public String toString() { + return iri.toString(); + } + } enum GEO implements CoreDatatype { @@ -335,6 +347,10 @@ public Optional asGEODatatype() { return optional; } + @Override + public String toString() { + return iri.toString(); + } } } diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatypeHelper.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatypeHelper.java index 82d6c836d72..b9e79d83717 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatypeHelper.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/CoreDatatypeHelper.java @@ -19,7 +19,7 @@ @InternalUseOnly class CoreDatatypeHelper { - static Map reverseLookup; + private static Map reverseLookup; static Map getReverseLookup() { @@ -55,7 +55,7 @@ static class DatatypeIRI extends AbstractIRI { public DatatypeIRI(String namespace, String localName) { this.namespace = namespace; this.localName = localName; - this.stringValue = namespace.concat(localName); + this.stringValue = namespace.concat(localName).intern(); } @Override @@ -73,5 +73,9 @@ public String getLocalName() { return localName; } + @Override + public String toString() { + return stringValue; + } } } diff --git a/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/CoreDatatypeTest.java b/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/CoreDatatypeTest.java index cfe67e9c49a..0daa617ce1c 100644 --- a/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/CoreDatatypeTest.java +++ b/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/CoreDatatypeTest.java @@ -9,6 +9,8 @@ package org.eclipse.rdf4j.model.base; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; @@ -24,9 +26,9 @@ public class CoreDatatypeTest { @Test public void testOrderOfXSD() { - ArrayList datatypes = getDatatypesShuffled(); + ArrayList datatypes = getXSDDatatypesShuffled(); - List datatypeIRIs = getDatatypesShuffled().stream() + List datatypeIRIs = getXSDDatatypesShuffled().stream() .map(CoreDatatype::getIri) .map(Object::toString) .collect(Collectors.toList()); @@ -42,13 +44,48 @@ public void testOrderOfXSD() { } - private ArrayList getDatatypesShuffled() { - Random random = new Random(42353245); + @Test + public void testOrderOfRDF() { + ArrayList datatypes = getRDFDatatypesShuffled(); - ArrayList xsds = new ArrayList<>(Arrays.asList(CoreDatatype.XSD.values())); + List datatypeIRIs = getRDFDatatypesShuffled().stream() + .map(CoreDatatype::getIri) + .map(Object::toString) + .collect(Collectors.toList()); + + Collections.sort(datatypes); + Collections.sort(datatypeIRIs); + + List datatypeIRIsSortedByEnum = datatypes.stream() + .map(CoreDatatype::getIri) + .map(Object::toString) + .collect(Collectors.toList()); + Assert.assertEquals(datatypeIRIs, datatypeIRIsSortedByEnum); + + } + + private ArrayList getXSDDatatypesShuffled() { + + ArrayList datatypes = new ArrayList<>(Arrays.asList(CoreDatatype.XSD.values())); + + Collections.shuffle(datatypes, new Random(42353245)); + return datatypes; + } + + private ArrayList getRDFDatatypesShuffled() { + + ArrayList datatypes = new ArrayList<>(Arrays.asList(CoreDatatype.RDF.values())); + + Collections.shuffle(datatypes, new Random(42353245)); + return datatypes; + } + + @Test + public void testToString() { + for (CoreDatatype value : CoreDatatypeHelper.getReverseLookup().values()) { + assertSame(value.toString(), value.getIri().toString()); + } - Collections.shuffle(xsds); - return xsds; } @Test 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 e210520988f..ba355e9c8c2 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 @@ -32,77 +32,57 @@ public class RDF { public static final Namespace NS = Vocabularies.createNamespace(PREFIX, NAMESPACE); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#type */ - public final static IRI TYPE; + public final static IRI TYPE = Vocabularies.createIRI(RDF.NAMESPACE, "type"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#Property */ - public final static IRI PROPERTY; + public final static IRI PROPERTY = Vocabularies.createIRI(RDF.NAMESPACE, "Property"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral */ - public final static IRI XMLLITERAL; + public final static IRI XMLLITERAL = CoreDatatype.RDF.XMLLITERAL.getIri(); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#subject */ - public final static IRI SUBJECT; + public final static IRI SUBJECT = Vocabularies.createIRI(RDF.NAMESPACE, "subject"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate */ - public final static IRI PREDICATE; + public final static IRI PREDICATE = Vocabularies.createIRI(RDF.NAMESPACE, "predicate"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#object */ - public final static IRI OBJECT; + public final static IRI OBJECT = Vocabularies.createIRI(RDF.NAMESPACE, "object"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement */ - public final static IRI STATEMENT; + public final static IRI STATEMENT = Vocabularies.createIRI(RDF.NAMESPACE, "Statement"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag */ - public final static IRI BAG; + public final static IRI BAG = Vocabularies.createIRI(RDF.NAMESPACE, "Bag"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt */ - public final static IRI ALT; + public final static IRI ALT = Vocabularies.createIRI(RDF.NAMESPACE, "Alt"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq */ - public final static IRI SEQ; + public final static IRI SEQ = Vocabularies.createIRI(RDF.NAMESPACE, "Seq"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#value */ - public final static IRI VALUE; + public final static IRI VALUE = Vocabularies.createIRI(RDF.NAMESPACE, "value"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#li */ - public final static IRI LI; + public final static IRI LI = Vocabularies.createIRI(RDF.NAMESPACE, "li"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#List */ - public final static IRI LIST; + public final static IRI LIST = Vocabularies.createIRI(RDF.NAMESPACE, "List"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#first */ - public final static IRI FIRST; + public final static IRI FIRST = Vocabularies.createIRI(RDF.NAMESPACE, "first"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#rest */ - public final static IRI REST; + public final static IRI REST = Vocabularies.createIRI(RDF.NAMESPACE, "rest"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#nil */ - public final static IRI NIL; + public final static IRI NIL = Vocabularies.createIRI(RDF.NAMESPACE, "nil"); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#langString */ - public static final IRI LANGSTRING; + public static final IRI LANGSTRING = CoreDatatype.RDF.LANGSTRING.getIri(); /** http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML */ - public static final IRI HTML; - - static { - TYPE = Vocabularies.createIRI(RDF.NAMESPACE, "type"); - PROPERTY = Vocabularies.createIRI(RDF.NAMESPACE, "Property"); - XMLLITERAL = Vocabularies.createIRI(RDF.NAMESPACE, "XMLLiteral"); - SUBJECT = Vocabularies.createIRI(RDF.NAMESPACE, "subject"); - PREDICATE = Vocabularies.createIRI(RDF.NAMESPACE, "predicate"); - OBJECT = Vocabularies.createIRI(RDF.NAMESPACE, "object"); - STATEMENT = Vocabularies.createIRI(RDF.NAMESPACE, "Statement"); - BAG = Vocabularies.createIRI(RDF.NAMESPACE, "Bag"); - ALT = Vocabularies.createIRI(RDF.NAMESPACE, "Alt"); - SEQ = Vocabularies.createIRI(RDF.NAMESPACE, "Seq"); - VALUE = Vocabularies.createIRI(RDF.NAMESPACE, "value"); - LI = Vocabularies.createIRI(RDF.NAMESPACE, "li"); - LIST = Vocabularies.createIRI(RDF.NAMESPACE, "List"); - FIRST = Vocabularies.createIRI(RDF.NAMESPACE, "first"); - REST = Vocabularies.createIRI(RDF.NAMESPACE, "rest"); - NIL = Vocabularies.createIRI(RDF.NAMESPACE, "nil"); - LANGSTRING = CoreDatatype.RDF.LANGSTRING.getIri(); - HTML = Vocabularies.createIRI(RDF.NAMESPACE, "HTML"); - } + public static final IRI HTML = CoreDatatype.RDF.HTML.getIri(); + } diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/datatypes/XMLDatatypeUtil.java b/core/model/src/main/java/org/eclipse/rdf4j/model/datatypes/XMLDatatypeUtil.java index 6d297ddf2c3..77b51eaace7 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/datatypes/XMLDatatypeUtil.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/datatypes/XMLDatatypeUtil.java @@ -315,77 +315,85 @@ public static boolean isValidValue(String value, IRI datatype) { } return true; + } public static boolean isValidValue(String value, CoreDatatype datatype) { if (datatype.isXSDDatatype()) { - switch (((CoreDatatype.XSD) datatype)) { - case DECIMAL: - return isValidDecimal(value); - case INTEGER: - return isValidInteger(value); - case NEGATIVE_INTEGER: - return isValidNegativeInteger(value); - case NON_POSITIVE_INTEGER: - return isValidNonPositiveInteger(value); - case NON_NEGATIVE_INTEGER: - return isValidNonNegativeInteger(value); - case POSITIVE_INTEGER: - return isValidPositiveInteger(value); - case LONG: - return isValidLong(value); - case INT: - return isValidInt(value); - case SHORT: - return isValidShort(value); - case BYTE: - return isValidByte(value); - case UNSIGNED_LONG: - return isValidUnsignedLong(value); - case UNSIGNED_INT: - return isValidUnsignedInt(value); - case UNSIGNED_SHORT: - return isValidUnsignedShort(value); - case UNSIGNED_BYTE: - return isValidUnsignedByte(value); - case FLOAT: - return isValidFloat(value); - case DOUBLE: - return isValidDouble(value); - case BOOLEAN: - return isValidBoolean(value); - case DATETIME: - return isValidDateTime(value); - case DATETIMESTAMP: - return isValidDateTimeStamp(value); - case DATE: - return isValidDate(value); - case TIME: - return isValidTime(value); - case GDAY: - return isValidGDay(value); - case GMONTH: - return isValidGMonth(value); - case GMONTHDAY: - return isValidGMonthDay(value); - case GYEAR: - return isValidGYear(value); - case GYEARMONTH: - return isValidGYearMonth(value); - case DURATION: - return isValidDuration(value); - case DAYTIMEDURATION: - return isValidDayTimeDuration(value); - case YEARMONTHDURATION: - return isValidYearMonthDuration(value); - case QNAME: - return isValidQName(value); - case ANYURI: - return isValidAnyURI(value); - case LANGUAGE: - return Literals.isValidLanguageTag(value); - } + return isValidValue(value, ((CoreDatatype.XSD) datatype)); + } + return true; + + } + + public static boolean isValidValue(String value, CoreDatatype.XSD datatype) { + switch (datatype) { + case DECIMAL: + return isValidDecimal(value); + case INTEGER: + return isValidInteger(value); + case NEGATIVE_INTEGER: + return isValidNegativeInteger(value); + case NON_POSITIVE_INTEGER: + return isValidNonPositiveInteger(value); + case NON_NEGATIVE_INTEGER: + return isValidNonNegativeInteger(value); + case POSITIVE_INTEGER: + return isValidPositiveInteger(value); + case LONG: + return isValidLong(value); + case INT: + return isValidInt(value); + case SHORT: + return isValidShort(value); + case BYTE: + return isValidByte(value); + case UNSIGNED_LONG: + return isValidUnsignedLong(value); + case UNSIGNED_INT: + return isValidUnsignedInt(value); + case UNSIGNED_SHORT: + return isValidUnsignedShort(value); + case UNSIGNED_BYTE: + return isValidUnsignedByte(value); + case FLOAT: + return isValidFloat(value); + case DOUBLE: + return isValidDouble(value); + case BOOLEAN: + return isValidBoolean(value); + case DATETIME: + return isValidDateTime(value); + case DATETIMESTAMP: + return isValidDateTimeStamp(value); + case DATE: + return isValidDate(value); + case TIME: + return isValidTime(value); + case GDAY: + return isValidGDay(value); + case GMONTH: + return isValidGMonth(value); + case GMONTHDAY: + return isValidGMonthDay(value); + case GYEAR: + return isValidGYear(value); + case GYEARMONTH: + return isValidGYearMonth(value); + case DURATION: + return isValidDuration(value); + case DAYTIMEDURATION: + return isValidDayTimeDuration(value); + case YEARMONTHDURATION: + return isValidYearMonthDuration(value); + case QNAME: + return isValidQName(value); + case ANYURI: + return isValidAnyURI(value); + case LANGUAGE: + return Literals.isValidLanguageTag(value); } + return true; } @@ -890,95 +898,93 @@ private static boolean isValidCalendarValue(String value) { * @throws IllegalArgumentException If the supplied value is illegal considering the supplied datatype. */ public static String normalize(String value, IRI datatype) { - String result = value; - if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.DECIMAL)) { - result = normalizeDecimal(value); + return normalizeDecimal(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.INTEGER)) { - result = normalizeInteger(value); + return normalizeInteger(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.NEGATIVE_INTEGER)) { - result = normalizeNegativeInteger(value); + return normalizeNegativeInteger(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.NON_POSITIVE_INTEGER)) { - result = normalizeNonPositiveInteger(value); + return normalizeNonPositiveInteger(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.NON_NEGATIVE_INTEGER)) { - result = normalizeNonNegativeInteger(value); + return normalizeNonNegativeInteger(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.POSITIVE_INTEGER)) { - result = normalizePositiveInteger(value); + return normalizePositiveInteger(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.LONG)) { - result = normalizeLong(value); + return normalizeLong(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.INT)) { - result = normalizeInt(value); + return normalizeInt(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.SHORT)) { - result = normalizeShort(value); + return normalizeShort(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.BYTE)) { - result = normalizeByte(value); + return normalizeByte(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.UNSIGNED_LONG)) { - result = normalizeUnsignedLong(value); + return normalizeUnsignedLong(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.UNSIGNED_INT)) { - result = normalizeUnsignedInt(value); + return normalizeUnsignedInt(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.UNSIGNED_SHORT)) { - result = normalizeUnsignedShort(value); + return normalizeUnsignedShort(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.UNSIGNED_BYTE)) { - result = normalizeUnsignedByte(value); + return normalizeUnsignedByte(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.FLOAT)) { - result = normalizeFloat(value); + return normalizeFloat(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.DOUBLE)) { - result = normalizeDouble(value); + return normalizeDouble(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.BOOLEAN)) { - result = normalizeBoolean(value); + return normalizeBoolean(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.DATETIME)) { - result = normalizeDateTime(value); + return normalizeDateTime(value); } else if (datatype.equals(org.eclipse.rdf4j.model.vocabulary.XSD.ANYURI)) { - result = collapseWhiteSpace(value); + return collapseWhiteSpace(value); } - return result; + return value; } public static String normalize(String value, CoreDatatype.XSD datatype) { - String result = value; - - if (datatype == CoreDatatype.XSD.DECIMAL) { - result = normalizeDecimal(value); - } else if (datatype == CoreDatatype.XSD.INTEGER) { - result = normalizeInteger(value); - } else if (datatype == CoreDatatype.XSD.NEGATIVE_INTEGER) { - result = normalizeNegativeInteger(value); - } else if (datatype == CoreDatatype.XSD.NON_POSITIVE_INTEGER) { - result = normalizeNonPositiveInteger(value); - } else if (datatype == CoreDatatype.XSD.NON_NEGATIVE_INTEGER) { - result = normalizeNonNegativeInteger(value); - } else if (datatype == CoreDatatype.XSD.POSITIVE_INTEGER) { - result = normalizePositiveInteger(value); - } else if (datatype == CoreDatatype.XSD.LONG) { - result = normalizeLong(value); - } else if (datatype == CoreDatatype.XSD.INT) { - result = normalizeInt(value); - } else if (datatype == CoreDatatype.XSD.SHORT) { - result = normalizeShort(value); - } else if (datatype == CoreDatatype.XSD.BYTE) { - result = normalizeByte(value); - } else if (datatype == CoreDatatype.XSD.UNSIGNED_LONG) { - result = normalizeUnsignedLong(value); - } else if (datatype == CoreDatatype.XSD.UNSIGNED_INT) { - result = normalizeUnsignedInt(value); - } else if (datatype == CoreDatatype.XSD.UNSIGNED_SHORT) { - result = normalizeUnsignedShort(value); - } else if (datatype == CoreDatatype.XSD.UNSIGNED_BYTE) { - result = normalizeUnsignedByte(value); - } else if (datatype == CoreDatatype.XSD.FLOAT) { - result = normalizeFloat(value); - } else if (datatype == CoreDatatype.XSD.DOUBLE) { - result = normalizeDouble(value); - } else if (datatype == CoreDatatype.XSD.BOOLEAN) { - result = normalizeBoolean(value); - } else if (datatype == CoreDatatype.XSD.DATETIME) { - result = normalizeDateTime(value); - } else if (datatype == CoreDatatype.XSD.ANYURI) { - result = collapseWhiteSpace(value); - } - return result; + switch (datatype) { + case DECIMAL: + return normalizeDecimal(value); + case INTEGER: + return normalizeInteger(value); + case NEGATIVE_INTEGER: + return normalizeNegativeInteger(value); + case NON_POSITIVE_INTEGER: + return normalizeNonPositiveInteger(value); + case NON_NEGATIVE_INTEGER: + return normalizeNonNegativeInteger(value); + case POSITIVE_INTEGER: + return normalizePositiveInteger(value); + case LONG: + return normalizeLong(value); + case INT: + return normalizeInt(value); + case SHORT: + return normalizeShort(value); + case BYTE: + return normalizeByte(value); + case UNSIGNED_LONG: + return normalizeUnsignedLong(value); + case UNSIGNED_INT: + return normalizeUnsignedInt(value); + case UNSIGNED_SHORT: + return normalizeUnsignedShort(value); + case UNSIGNED_BYTE: + return normalizeUnsignedByte(value); + case FLOAT: + return normalizeFloat(value); + case DOUBLE: + return normalizeDouble(value); + case BOOLEAN: + return normalizeBoolean(value); + case DATETIME: + return normalizeDateTime(value); + case ANYURI: + return collapseWhiteSpace(value); + } + + return value; } /** @@ -2137,6 +2143,34 @@ public static IRI qnameToURI(QName qname) { } } + public static CoreDatatype.XSD qnameToCoreDatatype(QName qname) { + if (DatatypeConstants.DATETIME == qname) { + return CoreDatatype.XSD.DATETIME; + } else if (DatatypeConstants.DATE == qname) { + return CoreDatatype.XSD.DATE; + } else if (DatatypeConstants.TIME == qname) { + return CoreDatatype.XSD.TIME; + } else if (DatatypeConstants.GYEARMONTH == qname) { + return CoreDatatype.XSD.GYEARMONTH; + } else if (DatatypeConstants.GMONTHDAY == qname) { + return CoreDatatype.XSD.GMONTHDAY; + } else if (DatatypeConstants.GYEAR == qname) { + return CoreDatatype.XSD.GYEAR; + } else if (DatatypeConstants.GMONTH == qname) { + return CoreDatatype.XSD.GMONTH; + } else if (DatatypeConstants.GDAY == qname) { + return CoreDatatype.XSD.GDAY; + } else if (DatatypeConstants.DURATION == qname) { + return CoreDatatype.XSD.DURATION; + } else if (DatatypeConstants.DURATION_DAYTIME == qname) { + return CoreDatatype.XSD.DAYTIMEDURATION; + } else if (DatatypeConstants.DURATION_YEARMONTH == qname) { + return CoreDatatype.XSD.YEARMONTHDURATION; + } else { + throw new IllegalArgumentException("QName cannot be mapped to an XML Schema IRI: " + qname.toString()); + } + } + public static String toString(Number value) { double d = value.doubleValue(); if (Double.POSITIVE_INFINITY == d) { diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/BooleanLiteral.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/BooleanLiteral.java index e5b8dd8e46b..39f8014dfdb 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/BooleanLiteral.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/BooleanLiteral.java @@ -7,6 +7,9 @@ *******************************************************************************/ package org.eclipse.rdf4j.model.impl; +import java.util.Optional; + +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.vocabulary.XSD; /** @@ -61,4 +64,9 @@ public boolean booleanValue() { public static BooleanLiteral valueOf(boolean value) { return value ? TRUE : FALSE; } + + @Override + public CoreDatatype.XSD getCoreDatatype() { + return CoreDatatype.XSD.BOOLEAN; + } } diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/CalendarLiteral.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/CalendarLiteral.java index 2afaf23c54d..67f03cf40fa 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/CalendarLiteral.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/CalendarLiteral.java @@ -28,7 +28,7 @@ public class CalendarLiteral extends SimpleLiteral { * {@link XMLGregorianCalendar#getXMLSchemaType()}. */ protected CalendarLiteral(XMLGregorianCalendar calendar) { - super(calendar.toXMLFormat(), XMLDatatypeUtil.qnameToURI(calendar.getXMLSchemaType())); + super(calendar.toXMLFormat(), XMLDatatypeUtil.qnameToCoreDatatype(calendar.getXMLSchemaType())); this.calendar = calendar; } diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/DecimalLiteral.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/DecimalLiteral.java index 094eca3675e..966c644b938 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/DecimalLiteral.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/DecimalLiteral.java @@ -29,7 +29,7 @@ public class DecimalLiteral extends SimpleLiteral { * Creates an xsd:decimal literal with the specified value. */ protected DecimalLiteral(BigDecimal value) { - this(value, XSD.DECIMAL); + this(value, CoreDatatype.XSD.DECIMAL); } /** diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/NumericLiteral.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/NumericLiteral.java index 3b471ffcf30..b6edda14be0 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/NumericLiteral.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/NumericLiteral.java @@ -46,42 +46,42 @@ protected NumericLiteral(Number number, CoreDatatype datatype) { * Creates an xsd:byte typed litral with the specified value. */ protected NumericLiteral(byte number) { - this(number, XSD.BYTE); + this(number, CoreDatatype.XSD.BYTE); } /** * Creates an xsd:short typed litral with the specified value. */ protected NumericLiteral(short number) { - this(number, XSD.SHORT); + this(number, CoreDatatype.XSD.SHORT); } /** * Creates an xsd:int typed litral with the specified value. */ protected NumericLiteral(int number) { - this(number, XSD.INT); + this(number, CoreDatatype.XSD.INT); } /** * Creates an xsd:long typed litral with the specified value. */ protected NumericLiteral(long n) { - this(n, XSD.LONG); + this(n, CoreDatatype.XSD.LONG); } /** * Creates an xsd:float typed litral with the specified value. */ protected NumericLiteral(float n) { - this(n, XSD.FLOAT); + this(n, CoreDatatype.XSD.FLOAT); } /** * Creates an xsd:double typed litral with the specified value. */ protected NumericLiteral(double n) { - this(n, XSD.DOUBLE); + this(n, CoreDatatype.XSD.DOUBLE); } @Override 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 39121fac77a..97dcc35057d 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 @@ -49,6 +49,7 @@ public class SimpleLiteral extends AbstractLiteral { * The literal's language tag. */ private String language; + transient private Optional optionalLanguageCache = null; /** * The literal's datatype. @@ -72,6 +73,7 @@ protected SimpleLiteral() { protected SimpleLiteral(String label) { setLabel(label); setDatatype(org.eclipse.rdf4j.model.vocabulary.XSD.STRING); + optionalLanguageCache = Optional.empty(); } /** @@ -100,6 +102,28 @@ protected SimpleLiteral(String label, IRI datatype) { } else { setDatatype(datatype); } + optionalLanguageCache = Optional.empty(); + + } + + /** + * Creates a new datatyped literal with the supplied label and datatype. + * + * @param label The label for the literal, must not be null. + * @param datatype The datatype for the literal. + */ + protected SimpleLiteral(String label, IRI datatype, CoreDatatype coreDatatype) { + assert coreDatatype != null; + 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"); + } + + setLabel(label); + setDatatype(datatype, coreDatatype); + optionalLanguageCache = Optional.empty(); } @Deprecated(since = "4.0.0", forRemoval = true) @@ -122,6 +146,7 @@ protected SimpleLiteral(String label, CoreDatatype datatype) { } else { setDatatype(datatype); } + optionalLanguageCache = Optional.empty(); } @@ -145,12 +170,16 @@ protected void setLanguage(String language) { throw new IllegalArgumentException("Language tag cannot be empty"); } this.language = language; - setDatatype(org.eclipse.rdf4j.model.vocabulary.RDF.LANGSTRING); + optionalLanguageCache = Optional.of(language); + setDatatype(CoreDatatype.RDF.LANGSTRING); } @Override public Optional getLanguage() { - return Optional.ofNullable(language); + if (optionalLanguageCache == null) { + optionalLanguageCache = Optional.ofNullable(language); + } + return optionalLanguageCache; } protected void setDatatype(IRI datatype) { @@ -158,6 +187,16 @@ protected void setDatatype(IRI datatype) { coreDatatype = CoreDatatype.from(datatype); } + protected void setDatatype(IRI datatype, CoreDatatype coreDatatype) { + assert datatype != null; + assert coreDatatype != null; + assert coreDatatype == CoreDatatype.NONE || datatype == coreDatatype.getIri(); + + this.datatype = datatype; + this.coreDatatype = coreDatatype; + + } + @Deprecated(since = "4.0.0", forRemoval = true) protected void setDatatype(XSD.Datatype datatype) { this.datatype = datatype.getIri(); @@ -165,6 +204,7 @@ protected void setDatatype(XSD.Datatype datatype) { } protected void setDatatype(CoreDatatype datatype) { + Objects.requireNonNull(datatype); this.datatype = datatype.getIri(); this.coreDatatype = datatype; } @@ -195,22 +235,32 @@ public boolean equals(Object o) { if (o instanceof Literal) { Literal other = (Literal) o; - // Compare labels - if (!label.equals(other.getLabel())) { + CoreDatatype coreDatatype = getCoreDatatype(); + + // Compare core datatypes + if (coreDatatype != ((Literal) o).getCoreDatatype()) { return false; + } else if (coreDatatype == CoreDatatype.NONE) { + // Compare other datatypes + if (!datatype.equals(other.getDatatype())) { + return false; + } } - // Compare datatypes - if (!datatype.equals(other.getDatatype())) { + // Compare labels + if (!label.equals(other.getLabel())) { return false; } - if (getLanguage().isPresent() && other.getLanguage().isPresent()) { - return getLanguage().get().equalsIgnoreCase(other.getLanguage().get()); + Optional language = getLanguage(); + Optional otherLanguage = other.getLanguage(); + + if (language.isPresent() && otherLanguage.isPresent()) { + return language.get().equalsIgnoreCase(otherLanguage.get()); } // If only one has a language, then return false else { - return !getLanguage().isPresent() && !other.getLanguage().isPresent(); + return language.isEmpty() && otherLanguage.isEmpty(); } } 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 fd78937256a..d8dcb716df2 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 @@ -288,11 +288,11 @@ protected Literal createNumericLiteral(Number number, CoreDatatype datatype) { * * @see XMLGregorianCalendar#toXMLFormat() * @see XMLGregorianCalendar#getXMLSchemaType() - * @see XMLDatatypeUtil#qnameToURI(javax.xml.namespace.QName) + * @see XMLDatatypeUtil#qnameToCoreDatatype(javax.xml.namespace.QName) */ @Override public Literal createLiteral(XMLGregorianCalendar calendar) { - return createLiteral(calendar.toXMLFormat(), XMLDatatypeUtil.qnameToURI(calendar.getXMLSchemaType())); + return createLiteral(calendar.toXMLFormat(), XMLDatatypeUtil.qnameToCoreDatatype(calendar.getXMLSchemaType())); } /** 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 52641a79b93..e85efa00988 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 @@ -121,6 +121,14 @@ public Literal createLiteral(String label, CoreDatatype datatype) { return delegate.createLiteral(label, datatype); } + @Override + public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDatatype) { + if (!XMLDatatypeUtil.isValidValue(label, coreDatatype)) { + throw new IllegalArgumentException("Not a valid literal value"); + } + return delegate.createLiteral(label, datatype, coreDatatype); + } + @Override public Literal createLiteral(String label, String language) { if (!Literals.isValidLanguageTag(language)) { 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 61dc8654aa6..262a9ee357a 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 @@ -12,7 +12,6 @@ import java.util.Date; import java.util.IllformedLocaleException; import java.util.Locale; -import java.util.Objects; import java.util.Optional; import javax.xml.datatype.Duration; @@ -21,6 +20,7 @@ import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; import org.eclipse.rdf4j.model.impl.SimpleLiteral; import org.eclipse.rdf4j.model.vocabulary.XSD; @@ -439,30 +439,30 @@ private static Literal createLiteral(ValueFactory valueFactory, Object object, b } if (object instanceof Boolean) { - return valueFactory.createLiteral(((Boolean) object).booleanValue()); + return valueFactory.createLiteral((Boolean) object); } else if (object instanceof Byte) { - return valueFactory.createLiteral(((Byte) object).byteValue()); + return valueFactory.createLiteral((Byte) object); } else if (object instanceof Double) { - return valueFactory.createLiteral(((Double) object).doubleValue()); + return valueFactory.createLiteral((Double) object); } else if (object instanceof Float) { - return valueFactory.createLiteral(((Float) object).floatValue()); + return valueFactory.createLiteral((Float) object); } else if (object instanceof Integer) { - return valueFactory.createLiteral(((Integer) object).intValue()); + return valueFactory.createLiteral((Integer) object); } else if (object instanceof Long) { - return valueFactory.createLiteral(((Long) object).longValue()); + return valueFactory.createLiteral((Long) object); } else if (object instanceof Short) { - return valueFactory.createLiteral(((Short) object).shortValue()); + return valueFactory.createLiteral((Short) object); } else if (object instanceof XMLGregorianCalendar) { return valueFactory.createLiteral((XMLGregorianCalendar) object); } else if (object instanceof Date) { return valueFactory.createLiteral((Date) object); } else if (object instanceof String) { - return valueFactory.createLiteral(object.toString(), XSD.STRING); + return valueFactory.createLiteral(object.toString(), CoreDatatype.XSD.STRING); } else { if (throwExceptionOnFailure) { throw new LiteralUtilException("Did not recognise object when creating literal"); } - return valueFactory.createLiteral(object.toString(), XSD.STRING); + return valueFactory.createLiteral(object.toString(), CoreDatatype.XSD.STRING); } } @@ -484,13 +484,10 @@ public static boolean canCreateLiteral(Object object) { return false; } - if (object instanceof Boolean || object instanceof Byte || object instanceof Double || object instanceof Float + return object instanceof Boolean || object instanceof Byte || object instanceof Double + || object instanceof Float || object instanceof Integer || object instanceof Long || object instanceof Short - || object instanceof XMLGregorianCalendar || object instanceof Date || object instanceof String) { - return true; - } - - return false; + || object instanceof XMLGregorianCalendar || object instanceof Date || object instanceof String; } /** @@ -500,7 +497,7 @@ 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 Objects.requireNonNull(literal, "Literal cannot be null").getLanguage().isPresent(); + return literal.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING; } /** diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/util/ModelBuilder.java b/core/model/src/main/java/org/eclipse/rdf4j/model/util/ModelBuilder.java index 3b34af7d2db..fe26525f59f 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/util/ModelBuilder.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/util/ModelBuilder.java @@ -19,6 +19,7 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.impl.LinkedHashModel; import org.eclipse.rdf4j.model.impl.SimpleNamespace; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; @@ -41,13 +42,13 @@ * * ModelBuilder builder = new ModelBuilder(); * - * // set some namespaces + * // set some namespaces * builder.setNamespace("ex", "http://example.org/").setNamespace(FOAF.NS); * * // add a new named graph to the model * builder.namedGraph("ex:graph1") * // add statements about resource ex:john - * .subject("ex:john") + * .subject("ex:john") * .add(FOAF.NAME, "John") // add the triple (ex:john, foaf:name "John") to the named graph * .add(FOAF.AGE, 42) * .add(FOAF.MBOX, "john@example.org"); @@ -79,7 +80,7 @@ public ModelBuilder() { /** * Create a new {@link ModelBuilder} which will append to the supplied {@link Model}. - * + * * @param model */ public ModelBuilder(Model model) { @@ -202,7 +203,7 @@ public ModelBuilder add(Resource subject, IRI predicate, Object object) { if (objectValue == null) { Literal literal = Values.literal(object); - if (!literal.getDatatype().equals(XSD.STRING)) { + if (literal.getCoreDatatype() != CoreDatatype.XSD.STRING) { model.setNamespace(XSD.NS); } objectValue = literal; diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/util/Values.java b/core/model/src/main/java/org/eclipse/rdf4j/model/util/Values.java index 6fdc101c019..02f3cf20632 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/util/Values.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/util/Values.java @@ -789,19 +789,19 @@ private static Literal createLiteralFromObject(ValueFactory valueFactory, Object Objects.requireNonNull(object, "object may not be null"); if (object instanceof Boolean) { - return valueFactory.createLiteral(((Boolean) object).booleanValue()); + return valueFactory.createLiteral((Boolean) object); } else if (object instanceof Byte) { - return valueFactory.createLiteral(((Byte) object).byteValue()); + return valueFactory.createLiteral((Byte) object); } else if (object instanceof Double) { - return valueFactory.createLiteral(((Double) object).doubleValue()); + return valueFactory.createLiteral((Double) object); } else if (object instanceof Float) { - return valueFactory.createLiteral(((Float) object).floatValue()); + return valueFactory.createLiteral((Float) object); } else if (object instanceof Integer) { - return valueFactory.createLiteral(((Integer) object).intValue()); + return valueFactory.createLiteral((Integer) object); } else if (object instanceof Long) { - return valueFactory.createLiteral(((Long) object).longValue()); + return valueFactory.createLiteral((Long) object); } else if (object instanceof Short) { - return valueFactory.createLiteral(((Short) object).shortValue()); + return valueFactory.createLiteral((Short) object); } else if (object instanceof XMLGregorianCalendar) { return valueFactory.createLiteral((XMLGregorianCalendar) object); } else if (object instanceof Date) { @@ -809,12 +809,12 @@ private static Literal createLiteralFromObject(ValueFactory valueFactory, Object } else if (object instanceof TemporalAccessor) { return valueFactory.createLiteral((TemporalAccessor) object); } else if (object instanceof String) { - return valueFactory.createLiteral(object.toString(), XSD.STRING); + return valueFactory.createLiteral(object.toString(), CoreDatatype.XSD.STRING); } else { if (throwExceptionOnFailure) { throw new IllegalArgumentException("Unrecognized object type: " + object); } - return valueFactory.createLiteral(object.toString(), XSD.STRING); + return valueFactory.createLiteral(object.toString(), CoreDatatype.XSD.STRING); } } } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java index 57b7654d393..93c7835e7c4 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java @@ -7,9 +7,12 @@ *******************************************************************************/ package org.eclipse.rdf4j.query.algebra.evaluation; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; @@ -34,6 +37,9 @@ public class ArrayBindingSet extends AbstractBindingSet implements MutableBindin private final String[] bindingNames; + // Creating a LinkedHashSet is expensive, so we should cache the binding names set + private Set bindingNamesSetCache; + private final boolean[] whichBindingsHaveBeenSet; private final Value[] values; @@ -83,7 +89,7 @@ public ArrayBindingSet(ArrayBindingSet toCopy, String... names) { /** * This is used to generate a direct setter into the array to put a binding value into. Can be used to avoid many * comparisons to the bindingNames. - * + * * @param bindingName for which you want the setter * @return the setter biconsumer which can operate on any ArrayBindingSet but should only be used on ones with an * identical bindingNames array. Otherwise returns null. @@ -95,6 +101,7 @@ public BiConsumer getDirectSetBinding(String bindingName return (v, a) -> { a.values[idx] = v; a.whichBindingsHaveBeenSet[idx] = true; + a.clearCache(); }; } } @@ -110,6 +117,7 @@ public BiConsumer getDirectAddBinding(String bindingName assert !a.whichBindingsHaveBeenSet[idx] : "variable already bound: " + bindingName; a.values[idx] = v; a.whichBindingsHaveBeenSet[idx] = true; + a.clearCache(); }; } } @@ -158,19 +166,38 @@ public Function getDirectHasBinding(String bindingName @Override public Set getBindingNames() { - final LinkedHashSet bns = new LinkedHashSet<>(); - for (int i = 0; i < this.bindingNames.length; i++) { - if (values[i] != null) - bns.add(bindingNames[i]); + if (bindingNamesSetCache == null) { + int size = size(); + if (size == 0) { + this.bindingNamesSetCache = Collections.emptySet(); + } else if (size == 1) { + for (int i = 0; i < this.bindingNames.length; i++) { + if (whichBindingsHaveBeenSet[i]) { + this.bindingNamesSetCache = Collections.singleton(bindingNames[i]); + break; + } + } + assert this.bindingNamesSetCache != null; + } else { + LinkedHashSet bindingNamesSetCache = new LinkedHashSet<>(size * 2); + for (int i = 0; i < this.bindingNames.length; i++) { + if (whichBindingsHaveBeenSet[i]) { + bindingNamesSetCache.add(bindingNames[i]); + } + } + this.bindingNamesSetCache = Collections.unmodifiableSet(bindingNamesSetCache); + } } - return bns; + + return bindingNamesSetCache; } @Override public Value getValue(String bindingName) { for (int i = 0; i < bindingNames.length; i++) { - if (bindingNames[i].equals(bindingName) && whichBindingsHaveBeenSet[i]) + if (bindingNames[i].equals(bindingName) && whichBindingsHaveBeenSet[i]) { return values[i]; + } } return null; } @@ -189,8 +216,9 @@ public Binding getBinding(String bindingName) { @Override public boolean hasBinding(String bindingName) { for (int i = 0; i < bindingNames.length; i++) { - if (bindingNames[i].equals(bindingName)) + if (bindingNames[i].equals(bindingName)) { return whichBindingsHaveBeenSet[i]; + } } return false; } @@ -213,6 +241,33 @@ public int size() { return size; } + List sortedBindingNames = null; + + public List getSortedBindingNames() { + if (sortedBindingNames == null) { + int size = size(); + + if (size == 1) { + for (int i = 0; i < bindingNames.length; i++) { + if (whichBindingsHaveBeenSet[i]) { + sortedBindingNames = Collections.singletonList(bindingNames[i]); + } + } + } else { + ArrayList names = new ArrayList<>(size); + for (int i = 0; i < bindingNames.length; i++) { + if (whichBindingsHaveBeenSet[i]) { + names.add(bindingNames[i]); + } + } + names.sort(String::compareTo); + sortedBindingNames = names; + } + } + + return sortedBindingNames; + } + /*------------------------------------* * Inner class ArrayBindingSetIterator * *------------------------------------*/ @@ -239,10 +294,11 @@ public Binding next() { String name = bindingNames[index]; Value value = values[index++]; - if (value != null) + if (value != null) { return new SimpleBinding(name, value); - else + } else { return null; + } } @Override @@ -255,11 +311,14 @@ public void remove() { public void addBinding(Binding binding) { for (int i = 0; i < this.bindingNames.length; i++) { if (bindingNames[i].equals(binding.getName())) { - assert this.whichBindingsHaveBeenSet[i] == false; + assert !this.whichBindingsHaveBeenSet[i]; this.values[i] = binding.getValue(); this.whichBindingsHaveBeenSet[i] = true; + clearCache(); + return; } } + assert false : "We don't actually support adding a binding."; } @Override @@ -268,6 +327,8 @@ public void setBinding(Binding binding) { if (bindingNames[i].equals(binding.getName())) { this.values[i] = binding.getValue(); this.whichBindingsHaveBeenSet[i] = true; + clearCache(); + return; } } } @@ -278,6 +339,8 @@ public void setBinding(String name, Value value) { if (bindingNames[i].equals(name)) { this.values[i] = value; this.whichBindingsHaveBeenSet[i] = value != null; + clearCache(); + return; } } } @@ -291,4 +354,8 @@ public boolean isEmpty() { } return true; } + + private void clearCache() { + bindingNamesSetCache = null; + } } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/EvaluationStrategy.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/EvaluationStrategy.java index f5656cef14a..51c2331cd8f 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/EvaluationStrategy.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/EvaluationStrategy.java @@ -94,7 +94,7 @@ CloseableIteration evaluate(TupleExpr expr /** * Prepare a QueryEvaluationStep that tries to do as much work once per query avoiding repeated calls to the same * code as much as possible. This depends on java invoke dynamic for performance. - * + * * @param expr that is to be evaluated later * @return a QueryEvaluationStep that may avoid doing repeating the same work over and over. */ diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/QueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/QueryEvaluationStep.java index 691d6e0cee4..a2a688d500d 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/QueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/QueryEvaluationStep.java @@ -47,7 +47,7 @@ public DelayedEvaluationIteration(QueryEvaluationStep arg, BindingSet bs) { /** * A fall back implementation that wraps a pre-existing evaluate method on a strategy - * + * * @param strategy that can evaluate the tuple expr. * @param expr that is going to be evaluated * @return a thin wrapper arround the evaluation call. @@ -64,7 +64,7 @@ public CloseableIteration evaluate(Binding /** * Wrap an QueryEvalationStep: where we apply a function on every evaluation result of the wrapped EvaluationStep. * Useful to add a timing function - * + * * @param qes an QueryEvaluationStep that needs to return modified evaluation results * @param wrap the function that will do the modification * @return a new evaluation step that executes wrap on the inner qes. diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/MD5.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/MD5.java index 7858fd1e76f..186441d2a23 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/MD5.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/MD5.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} MD5, as defined in @@ -39,7 +39,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx if (args[0] instanceof Literal) { Literal literal = (Literal) args[0]; - if (QueryEvaluationUtil.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { + if (QueryEvaluationUtility.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { String lexValue = literal.getLabel(); try { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA1.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA1.java index f98370359ab..8cfe2c6ec29 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA1.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA1.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} SHA1, as defined in @@ -39,7 +39,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx if (args[0] instanceof Literal) { Literal literal = (Literal) args[0]; - if (QueryEvaluationUtil.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { + if (QueryEvaluationUtility.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { String lexValue = literal.getLabel(); try { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA256.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA256.java index a985f2f123e..2c8c4be1970 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA256.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA256.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} SHA256, as defined in @@ -39,7 +39,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx if (args[0] instanceof Literal) { Literal literal = (Literal) args[0]; - if (QueryEvaluationUtil.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { + if (QueryEvaluationUtility.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { String lexValue = literal.getLabel(); try { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA384.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA384.java index f3a80800e01..7f6bf26b3e7 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA384.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA384.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} SHA384, as defined in @@ -39,7 +39,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx if (args[0] instanceof Literal) { Literal literal = (Literal) args[0]; - if (QueryEvaluationUtil.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { + if (QueryEvaluationUtility.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { String lexValue = literal.getLabel(); try { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA512.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA512.java index 6cc60cebbe2..caec1f63bf0 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA512.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/hash/SHA512.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} SHA512, as defined in @@ -39,7 +39,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx if (args[0] instanceof Literal) { Literal literal = (Literal) args[0]; - if (QueryEvaluationUtil.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { + if (QueryEvaluationUtility.isSimpleLiteral(literal) || XSD.STRING.equals(literal.getDatatype())) { String lexValue = literal.getLabel(); try { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/numeric/Rand.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/numeric/Rand.java index ec5dbbcc63d..f3339f1aca5 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/numeric/Rand.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/numeric/Rand.java @@ -7,8 +7,6 @@ *******************************************************************************/ package org.eclipse.rdf4j.query.algebra.evaluation.function.numeric; -import java.util.Random; - import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.ValueFactory; diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrDt.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrDt.java index 50e07e08b55..22b0ee10660 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrDt.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrDt.java @@ -13,7 +13,7 @@ import org.eclipse.rdf4j.model.ValueFactory; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} STRDT, as defined in @@ -37,7 +37,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Value lexicalValue = args[0]; Value datatypeValue = args[1]; - if (QueryEvaluationUtil.isSimpleLiteral(lexicalValue)) { + if (QueryEvaluationUtility.isSimpleLiteral(lexicalValue)) { Literal lit = (Literal) lexicalValue; if (datatypeValue instanceof IRI) { return valueFactory.createLiteral(lit.getLabel(), (IRI) datatypeValue); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrLang.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrLang.java index 52b440a293a..56865d59f2a 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrLang.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/rdfterm/StrLang.java @@ -12,7 +12,7 @@ import org.eclipse.rdf4j.model.ValueFactory; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} STRLANG, as defined in @@ -36,7 +36,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Value lexicalValue = args[0]; Value languageValue = args[1]; - if (QueryEvaluationUtil.isSimpleLiteral(lexicalValue)) { + if (QueryEvaluationUtility.isSimpleLiteral(lexicalValue)) { Literal lit = (Literal) lexicalValue; if (languageValue instanceof Literal) { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Concat.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Concat.java index 4957908e45e..149bf074272 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Concat.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Concat.java @@ -14,7 +14,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} CONCAT, as defined in @@ -43,7 +43,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx if (arg instanceof Literal) { Literal lit = (Literal) arg; - if (!QueryEvaluationUtil.isStringLiteral(lit)) { + if (!QueryEvaluationUtility.isStringLiteral(lit)) { throw new ValueExprEvaluationException("unexpected datatype for CONCAT operand: " + lit); } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Contains.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Contains.java index 04e30e48ced..5cb0ee4bc94 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Contains.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Contains.java @@ -14,7 +14,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} CONTAINS, as defined in @@ -51,8 +51,8 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx } else { throw new ValueExprEvaluationException("incompatible operands for CONTAINS function"); } - } else if (QueryEvaluationUtil.isStringLiteral(leftLit)) { - if (QueryEvaluationUtil.isStringLiteral(rightLit)) { + } else if (QueryEvaluationUtility.isStringLiteral(leftLit)) { + if (QueryEvaluationUtility.isStringLiteral(rightLit)) { String leftLexVal = leftLit.getLabel(); String rightLexVal = rightLit.getLabel(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/EncodeForUri.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/EncodeForUri.java index 41109ea2e97..32d2350ab91 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/EncodeForUri.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/EncodeForUri.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} ENCODE_FOR_URI, as defined in @@ -40,7 +40,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx if (args[0] instanceof Literal) { Literal literal = (Literal) args[0]; - if (QueryEvaluationUtil.isStringLiteral(literal)) { + if (QueryEvaluationUtility.isStringLiteral(literal)) { String lexValue = literal.getLabel(); return valueFactory.createLiteral(encodeUri(lexValue)); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/LowerCase.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/LowerCase.java index 7b27f7caa6a..58dbf82fb45 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/LowerCase.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/LowerCase.java @@ -16,7 +16,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} LCASE, as defined in @@ -41,7 +41,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal literal = (Literal) args[0]; // LowerCase function accepts only string literals. - if (QueryEvaluationUtil.isStringLiteral(literal)) { + if (QueryEvaluationUtility.isStringLiteral(literal)) { String lexicalValue = literal.getLabel().toLowerCase(); Optional language = literal.getLanguage(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Replace.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Replace.java index 234c0d1f475..1250e633d58 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Replace.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Replace.java @@ -17,7 +17,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} REPLACE, as defined in @@ -47,21 +47,21 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx flags = (Literal) args[3]; } - if (!QueryEvaluationUtil.isStringLiteral(arg)) { + if (!QueryEvaluationUtility.isStringLiteral(arg)) { throw new ValueExprEvaluationException("incompatible operand for REPLACE: " + arg); } - if (!QueryEvaluationUtil.isSimpleLiteral(pattern)) { + if (!QueryEvaluationUtility.isSimpleLiteral(pattern)) { throw new ValueExprEvaluationException("incompatible operand for REPLACE: " + pattern); } - if (!QueryEvaluationUtil.isSimpleLiteral(replacement)) { + if (!QueryEvaluationUtility.isSimpleLiteral(replacement)) { throw new ValueExprEvaluationException("incompatible operand for REPLACE: " + replacement); } String flagString = null; if (flags != null) { - if (!QueryEvaluationUtil.isSimpleLiteral(flags)) { + if (!QueryEvaluationUtility.isSimpleLiteral(flags)) { throw new ValueExprEvaluationException("incompatible operand for REPLACE: " + flags); } flagString = flags.getLabel(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrAfter.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrAfter.java index 29e739dba50..3d164b215a0 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrAfter.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrAfter.java @@ -16,7 +16,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} STRAFTER, as defined in @@ -44,7 +44,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal leftLit = (Literal) leftArg; Literal rightLit = (Literal) rightArg; - if (QueryEvaluationUtil.compatibleArguments(leftLit, rightLit)) { + if (QueryEvaluationUtility.compatibleArguments(leftLit, rightLit)) { String lexicalValue = leftLit.getLabel(); String substring = rightLit.getLabel(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrBefore.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrBefore.java index 312c2ce73fa..526adb5dc2a 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrBefore.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrBefore.java @@ -16,7 +16,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} STRBEFORE, as defined in @@ -44,7 +44,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal leftLit = (Literal) leftArg; Literal rightLit = (Literal) rightArg; - if (QueryEvaluationUtil.compatibleArguments(leftLit, rightLit)) { + if (QueryEvaluationUtility.compatibleArguments(leftLit, rightLit)) { Optional leftLanguage = leftLit.getLanguage(); IRI leftDt = leftLit.getDatatype(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrEnds.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrEnds.java index 76bc55fd8c9..7342197debb 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrEnds.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrEnds.java @@ -14,7 +14,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} STRENDS, as defined in @@ -42,7 +42,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal leftLit = (Literal) leftVal; Literal rightLit = (Literal) rightVal; - if (QueryEvaluationUtil.compatibleArguments(leftLit, rightLit)) { + if (QueryEvaluationUtility.compatibleArguments(leftLit, rightLit)) { String leftLexVal = leftLit.getLabel(); String rightLexVal = rightLit.getLabel(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrLen.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrLen.java index fca8e8eef7f..b6baa89adbb 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrLen.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrLen.java @@ -14,7 +14,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} STRLEN, as defined in @@ -40,7 +40,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal literal = (Literal) argValue; // strlen function accepts only string literals - if (QueryEvaluationUtil.isStringLiteral(literal)) { + if (QueryEvaluationUtility.isStringLiteral(literal)) { // TODO we jump through some hoops here to get an xsd:integer // literal. Shouldn't createLiteral(int) return an xsd:integer diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrStarts.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrStarts.java index 83dc96a8684..d4f2c8e2ba6 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrStarts.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/StrStarts.java @@ -14,7 +14,7 @@ import org.eclipse.rdf4j.model.vocabulary.FN; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} STRSTARTS, as defined in @@ -42,7 +42,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal leftLit = (Literal) leftVal; Literal rightLit = (Literal) rightVal; - if (QueryEvaluationUtil.compatibleArguments(leftLit, rightLit)) { + if (QueryEvaluationUtility.compatibleArguments(leftLit, rightLit)) { String leftLexVal = leftLit.getLabel(); String rightLexVal = rightLit.getLabel(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Substring.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Substring.java index 6a02fb4f0c9..d30606f93de 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Substring.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/Substring.java @@ -18,7 +18,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} SUBSTR, as defined in @@ -50,7 +50,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal literal = (Literal) argValue; // substr function accepts string literals only. - if (QueryEvaluationUtil.isStringLiteral(literal)) { + if (QueryEvaluationUtility.isStringLiteral(literal)) { String lexicalValue = literal.getLabel(); // determine start index. diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/UpperCase.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/UpperCase.java index 8ef525d9853..3dc6d3df2f2 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/UpperCase.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/string/UpperCase.java @@ -16,7 +16,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * The SPARQL built-in {@link Function} UCASE, as defined in @@ -41,7 +41,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal literal = (Literal) args[0]; // UpperCase function accepts only string literal - if (QueryEvaluationUtil.isStringLiteral(literal)) { + if (QueryEvaluationUtility.isStringLiteral(literal)) { String lexicalValue = literal.getLabel().toUpperCase(); Optional language = literal.getLanguage(); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/CastFunction.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/CastFunction.java index f080a3cd8b9..bbb40db4aaf 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/CastFunction.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/CastFunction.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * Abstract superclass for {@link org.eclipse.rdf4j.query.algebra.evaluation.function.Function}s that cast an argument @@ -42,7 +42,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx Literal literal = (Literal) args[0]; IRI datatype = literal.getDatatype(); - if (QueryEvaluationUtil.isStringLiteral(literal)) { + if (QueryEvaluationUtility.isStringLiteral(literal)) { String lexicalValue = XMLDatatypeUtil.collapseWhiteSpace(literal.getLabel()); if (isValidForDatatype(lexicalValue)) { return valueFactory.createLiteral(lexicalValue, getXsdDatatype()); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/StringCast.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/StringCast.java index d4c4db12459..285eb2e8c1a 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/StringCast.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/xsd/StringCast.java @@ -15,7 +15,7 @@ import org.eclipse.rdf4j.model.util.Literals; import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * A {@link org.eclipse.rdf4j.query.algebra.evaluation.function.Function} that tries to cast its argument to an @@ -39,7 +39,7 @@ public Literal evaluate(ValueFactory valueFactory, Value... args) throws ValueEx // we override because unlike most other cast functions, xsd:string should not accept a language-tagged // string literal. - if (QueryEvaluationUtil.isSimpleLiteral(literal)) { + if (QueryEvaluationUtility.isSimpleLiteral(literal)) { String lexicalValue = XMLDatatypeUtil.collapseWhiteSpace(literal.getLabel()); if (isValidForDatatype(lexicalValue)) { return valueFactory.createLiteral(lexicalValue, getXsdDatatype()); @@ -63,7 +63,7 @@ protected Literal convert(ValueFactory valueFactory, Value value) throws ValueEx Literal literal = (Literal) value; IRI datatype = literal.getDatatype(); - if (QueryEvaluationUtil.isSimpleLiteral(literal)) { + if (QueryEvaluationUtility.isSimpleLiteral(literal)) { return valueFactory.createLiteral(literal.getLabel(), XSD.STRING); } else if (!Literals.isLanguageLiteral(literal)) { if (XMLDatatypeUtil.isNumericDatatype(datatype) || datatype.equals(XSD.BOOLEAN) diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ArrayBindingBasedQueryEvaluationContext.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ArrayBindingBasedQueryEvaluationContext.java index 44c49285f66..998a4ca29fc 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ArrayBindingBasedQueryEvaluationContext.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ArrayBindingBasedQueryEvaluationContext.java @@ -169,4 +169,4 @@ public void meet(ExtensionElem node) throws QueryEvaluationException { String[] varNamesArr = varNames.toArray(new String[0]); return varNamesArr; } -} \ No newline at end of file +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ExtensionQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ExtensionQueryEvaluationStep.java index 185389eab50..0aa9670f6f6 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ExtensionQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ExtensionQueryEvaluationStep.java @@ -36,4 +36,4 @@ public CloseableIteration evaluate(Binding } return new ExtensionIterator(result, consumer, context); } -} \ No newline at end of file +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/FilterOptimizer.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/FilterOptimizer.java index a25b857f928..d0b3417de2b 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/FilterOptimizer.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/FilterOptimizer.java @@ -34,39 +34,39 @@ /** * Optimizes a query model by pushing {@link Filter}s as far down in the model tree as possible. - * + * * To make the first optimization succeed more often it splits filters which contains {@link And} conditions. - * + * * * SELECT * WHERE { - * ?s ?p ?o . - * ?s ?p ?o2 . - * FILTER(?o > '2'^^xsd:int && ?o2 < '4'^^xsd:int) + * ?s ?p ?o . + * ?s ?p ?o2 . + * FILTER(?o > '2'^^xsd:int && ?o2 < '4'^^xsd:int) * } * May be more efficient when decomposed into * SELECT * WHERE { * ?s ?p ?o . - * FILTER(?o > '2'^^xsd:int) - * ?s ?p ?o2 . - * FILTER(?o2 < '4'^^xsd:int) + * FILTER(?o > '2'^^xsd:int) + * ?s ?p ?o2 . + * FILTER(?o2 < '4'^^xsd:int) * } * - * + * * Then it optimizes a query model by merging adjacent {@link Filter}s. e.g. * SELECT * WHERE { * ?s ?p ?o . * FILTER(?o > 2) . * FILTER(?o < 4) . - * } - * may be merged into + * } + * may be merged into * SELECT * WHERE { - * ?s ?p ?o . + * ?s ?p ?o . * FILTER(?o > 2 && ?o < 4) . } * - * + * * This optimization allows for sharing evaluation costs in the future and removes an iterator. This is done as a second * step to not break the first optimization. In the case that the splitting was done but did not help it is now undone. - * + * * @author Arjohn Kampman * @author Jerven Bolleman */ diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleaner.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleaner.java index e7694541498..749b5c2f961 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleaner.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleaner.java @@ -1,9 +1,9 @@ -/******************************************************************************* - * Copyright (c) 2021 Eclipse RDF4J contributors. - * 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. +/******************************************************************************* + * Copyright (c) 2021 Eclipse RDF4J contributors. + * 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. *******************************************************************************/ package org.eclipse.rdf4j.query.algebra.evaluation.impl; @@ -21,7 +21,7 @@ /** * Cleans up {@link QueryModelNode#getParentNode()} references that have become inconsistent with the actual algebra * tree structure due to optimization operations. Typically used at the very end of the optimization pipeline. - * + * * @author Jeen Broekstra */ public class ParentReferenceCleaner implements QueryOptimizer { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ProjectionRemovalOptimizer.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ProjectionRemovalOptimizer.java index a8ddfd7da81..9e6aed6157d 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ProjectionRemovalOptimizer.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ProjectionRemovalOptimizer.java @@ -26,16 +26,16 @@ * SELECT ?s ?p ?o * WHERE {?s ?p ?o } * Does not need a projection as the inner statement pattern returns the same result. - * + * * While - * * SELECT ?s ?p + * * SELECT ?s ?p * WHERE {?s ?p ?o } * Does as the statement pattern has one more variable in use than the projection. - * + * * Note: this optimiser should run after optimisations ran that depend on Projections. e.g. - * + * * @see UnionScopeChangeOptimizer - * + * * @author Jerven Bolleman */ public class ProjectionRemovalOptimizer implements QueryOptimizer { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryEvaluationContext.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryEvaluationContext.java index a3267bdcb83..0926388eee0 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryEvaluationContext.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryEvaluationContext.java @@ -23,10 +23,10 @@ /** * A QueryEvaluationContext stores values and methods that are valid throughout the lifetime of a query execution. - * + * * A classic case is the case of NOW() evaluation to the same instant for all invocations of that function in one query * evaluation. - * + * */ public interface QueryEvaluationContext { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelNormalizer.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelNormalizer.java index e80be9e31a5..f67b98e3b95 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelNormalizer.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelNormalizer.java @@ -28,8 +28,7 @@ import org.eclipse.rdf4j.query.algebra.ValueConstant; import org.eclipse.rdf4j.query.algebra.ValueExpr; import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer; -import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor; import org.eclipse.rdf4j.query.algebra.helpers.VarNameCollector; @@ -112,14 +111,11 @@ public void meet(LeftJoin leftJoin) { } else if (rightArg instanceof SingletonSet) { leftJoin.replaceWith(leftArg); } else if (condition instanceof ValueConstant) { - boolean conditionValue; - try { - conditionValue = QueryEvaluationUtil.getEffectiveBooleanValue(((ValueConstant) condition).getValue()); - } catch (ValueExprEvaluationException e) { - conditionValue = false; - } + boolean conditionValue = QueryEvaluationUtility + .getEffectiveBooleanValue(((ValueConstant) condition).getValue()) + .orElse(false); - if (conditionValue == false) { + if (!conditionValue) { // Constraint is always false leftJoin.replaceWith(leftArg); } else { @@ -189,14 +185,11 @@ public void meet(Filter node) { if (arg instanceof EmptySet) { // see #meetUnaryTupleOperator } else if (condition instanceof ValueConstant) { - boolean conditionValue; - try { - conditionValue = QueryEvaluationUtil.getEffectiveBooleanValue(((ValueConstant) condition).getValue()); - } catch (ValueExprEvaluationException e) { - conditionValue = false; - } + boolean conditionValue = QueryEvaluationUtility + .getEffectiveBooleanValue(((ValueConstant) condition).getValue()) + .orElse(false); - if (conditionValue == false) { + if (!conditionValue) { // Constraint is always false node.replaceWith(new EmptySet()); } else { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelPruner.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelPruner.java index a9da5c31521..bfd19a07da7 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelPruner.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryModelPruner.java @@ -20,8 +20,7 @@ import org.eclipse.rdf4j.query.algebra.ValueConstant; import org.eclipse.rdf4j.query.algebra.ValueExpr; import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer; -import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor; /** @@ -80,15 +79,11 @@ public void meet(LeftJoin leftJoin) { } else if (rightArg instanceof SingletonSet) { leftJoin.replaceWith(leftArg); } else if (condition instanceof ValueConstant) { - boolean conditionValue; - try { - conditionValue = QueryEvaluationUtil - .getEffectiveBooleanValue(((ValueConstant) condition).getValue()); - } catch (ValueExprEvaluationException e) { - conditionValue = false; - } + boolean conditionValue = QueryEvaluationUtility + .getEffectiveBooleanValue(((ValueConstant) condition).getValue()) + .orElse(false); - if (conditionValue == false) { + if (!conditionValue) { // Constraint is always false leftJoin.replaceWith(leftArg); } else { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/StrictEvaluationStrategy.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/StrictEvaluationStrategy.java index ab7345c5bfc..068d025ed2d 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/StrictEvaluationStrategy.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/StrictEvaluationStrategy.java @@ -27,11 +27,10 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Triple; import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; import org.eclipse.rdf4j.model.impl.BooleanLiteral; import org.eclipse.rdf4j.model.util.Literals; -import org.eclipse.rdf4j.model.vocabulary.RDF; -import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.query.Dataset; import org.eclipse.rdf4j.query.MutableBindingSet; @@ -143,6 +142,7 @@ import org.eclipse.rdf4j.query.algebra.evaluation.util.MathUtil; import org.eclipse.rdf4j.query.algebra.evaluation.util.OrderComparator; import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; import org.eclipse.rdf4j.query.algebra.evaluation.util.ValueComparator; import org.eclipse.rdf4j.query.impl.EmptyBindingSet; import org.eclipse.rdf4j.util.UUIDable; @@ -1068,7 +1068,7 @@ protected QueryValueEvaluationStep prepare(Var var, QueryEvaluationContext conte @Override public Value evaluate(BindingSet bindings) - throws ValueExprEvaluationException, QueryEvaluationException { + throws QueryEvaluationException { Value value = getValue.apply(bindings); if (value == null) { throw new ValueExprEvaluationException(); @@ -1128,7 +1128,7 @@ public Value evaluate(Str node, BindingSet bindings) throws QueryEvaluationExcep } else if (argValue instanceof Literal) { Literal literal = (Literal) argValue; - if (QueryEvaluationUtil.isSimpleLiteral(literal)) { + if (QueryEvaluationUtility.isSimpleLiteral(literal)) { return literal; } else { return tripleSource.getValueFactory().createLiteral(literal.getLabel()); @@ -1149,7 +1149,7 @@ public Value evaluate(Label node, BindingSet bindings) if (argValue instanceof Literal) { Literal literal = (Literal) argValue; - if (QueryEvaluationUtil.isSimpleLiteral(literal)) { + if (QueryEvaluationUtility.isSimpleLiteral(literal)) { return literal; } else { return tripleSource.getValueFactory().createLiteral(literal.getLabel()); @@ -1184,10 +1184,10 @@ public Value evaluate(Datatype node, BindingSet bindings) // literal with datatype return literal.getDatatype(); } else if (literal.getLanguage().isPresent()) { - return RDF.LANGSTRING; + return CoreDatatype.RDF.LANGSTRING.getIri(); } else { // simple literal - return XSD.STRING; + return CoreDatatype.XSD.STRING.getIri(); } } @@ -1271,7 +1271,8 @@ public Value evaluate(IsLiteral node, BindingSet bindings) /** * Determines whether the operand (a variable) contains a numeric datatyped literal, i.e. a literal with datatype - * xsd:float, xsd:double, xsd:decimal, or a derived datatype of xsd:decimal. + * CoreDatatype.XSD:float, CoreDatatype.XSD:double, CoreDatatype.XSD:decimal, or a derived datatype of + * CoreDatatype.XSD:decimal. * * @return true if the operand contains a numeric datatyped literal, false otherwise. */ @@ -1373,7 +1374,8 @@ protected QueryValueEvaluationStep prepare(LangMatches node, QueryEvaluationCont } private Value evaluateLangMatch(Value langTagValue, Value langRangeValue) { - if (QueryEvaluationUtil.isSimpleLiteral(langTagValue) && QueryEvaluationUtil.isSimpleLiteral(langRangeValue)) { + if (QueryEvaluationUtility.isSimpleLiteral(langTagValue) + && QueryEvaluationUtility.isSimpleLiteral(langRangeValue)) { String langTag = ((Literal) langTagValue).getLabel(); String langRange = ((Literal) langRangeValue).getLabel(); @@ -1529,8 +1531,9 @@ public QueryValueEvaluationStep prepare(FunctionCall node, QueryEvaluationContex boolean allConstant = true; for (int i = 0; i < args.size(); i++) { argSteps[i] = precompile(args.get(i), context); - if (!argSteps[i].isConstant()) + if (!argSteps[i].isConstant()) { allConstant = false; + } } if (allConstant) { Value[] argValues = evaluateAllArguments(args, argSteps, EmptyBindingSet.getInstance()); @@ -1541,7 +1544,7 @@ public QueryValueEvaluationStep prepare(FunctionCall node, QueryEvaluationContex @Override public Value evaluate(BindingSet bindings) - throws ValueExprEvaluationException, QueryEvaluationException { + throws QueryEvaluationException { Value[] argValues = evaluateAllArguments(args, argSteps, bindings); return function.evaluate(tripleSource, argValues); } @@ -1562,7 +1565,7 @@ private Value[] evaluateAllArguments(List args, QueryValueEvaluationS public Value evaluate(And node, BindingSet bindings) throws QueryEvaluationException { try { Value leftValue = evaluate(node.getLeftArg(), bindings); - if (!QueryEvaluationUtil.getEffectiveBooleanValue(leftValue)) { + if (QueryEvaluationUtility.getEffectiveBooleanValue(leftValue) == QueryEvaluationUtility.Result._false) { // Left argument evaluates to false, we don't need to look any // further return BooleanLiteral.FALSE; @@ -1571,7 +1574,7 @@ public Value evaluate(And node, BindingSet bindings) throws QueryEvaluationExcep // Failed to evaluate the left argument. Result is 'false' when // the right argument evaluates to 'false', failure otherwise. Value rightValue = evaluate(node.getRightArg(), bindings); - if (!QueryEvaluationUtil.getEffectiveBooleanValue(rightValue)) { + if (QueryEvaluationUtility.getEffectiveBooleanValue(rightValue) == QueryEvaluationUtility.Result._false) { return BooleanLiteral.FALSE; } else { throw new ValueExprEvaluationException(); @@ -1633,7 +1636,7 @@ public Value evaluate(Now node, BindingSet bindings) throws QueryEvaluationExcep /** * During the execution of a single query NOW() should always return the same result and is in practical terms a * constant during evaluation. - * + * * @param node that represent the NOW() function * @param context that holds the shared now() of the query invocation * @return a constant value evaluation step @@ -1867,21 +1870,13 @@ public Value evaluate(Exists node, BindingSet bindings) @Override public boolean isTrue(ValueExpr expr, BindingSet bindings) throws QueryEvaluationException { - try { - Value value = evaluate(expr, bindings); - return QueryEvaluationUtil.getEffectiveBooleanValue(value); - } catch (ValueExprEvaluationException e) { - return false; - } + Value value = evaluate(expr, bindings); + return QueryEvaluationUtility.getEffectiveBooleanValue(value).orElse(false); } public boolean isTrue(QueryValueEvaluationStep expr, BindingSet bindings) throws QueryEvaluationException { - try { - Value value = expr.evaluate(bindings); - return QueryEvaluationUtil.getEffectiveBooleanValue(value); - } catch (ValueExprEvaluationException e) { - return false; - } + Value value = expr.evaluate(bindings); + return QueryEvaluationUtility.getEffectiveBooleanValue(value).orElse(false); } protected boolean isReducedOrDistinct(QueryModelNode node) { @@ -2054,7 +2049,7 @@ public void setTrackTime(boolean trackTime) { /** * Supply a QueryValueEvalationStep that will invoke the function (operator passed in). It will try to optimise * constant argument to be called only once per query run, - * + * * @param node the node to evaluate * @param operation the function that wraps the operator. * @param context in which the query is running. @@ -2075,7 +2070,7 @@ protected QueryValueEvaluationStep supplyBinaryValueEvaluation(BinaryValueOperat @Override public Value evaluate(BindingSet bindings) - throws ValueExprEvaluationException, QueryEvaluationException { + throws QueryEvaluationException { Value rightVal = rightStep.evaluate(bindings); return operation.apply(leftVal, rightVal); } @@ -2086,7 +2081,7 @@ public Value evaluate(BindingSet bindings) @Override public Value evaluate(BindingSet bindings) - throws ValueExprEvaluationException, QueryEvaluationException { + throws QueryEvaluationException { Value leftVal = leftStep.evaluate(bindings); Value result = operation.apply(leftVal, rightVal); return result; @@ -2097,7 +2092,7 @@ public Value evaluate(BindingSet bindings) @Override public Value evaluate(BindingSet bindings) - throws ValueExprEvaluationException, QueryEvaluationException { + throws QueryEvaluationException { Value leftVal = leftStep.evaluate(bindings); Value rightVal = rightStep.evaluate(bindings); return operation.apply(leftVal, rightVal); @@ -2108,7 +2103,7 @@ public Value evaluate(BindingSet bindings) /** * Return a QueryEvaluationStep that applies constant propegation. - * + * * @param node that will be evaluated/prepared * @param operation the task to be done * @param context in which the evaluation takes place @@ -2126,7 +2121,7 @@ protected QueryValueEvaluationStep supplyUnaryValueEvaluation(UnaryValueOperator @Override public Value evaluate(BindingSet bindings) - throws ValueExprEvaluationException, QueryEvaluationException { + throws QueryEvaluationException { Value argValue = argStep.evaluate(bindings); return operation.apply(argValue); } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/BindingSetAssignmentQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/BindingSetAssignmentQueryEvaluationStep.java index 4dde71ae336..bd701a3a97c 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/BindingSetAssignmentQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/BindingSetAssignmentQueryEvaluationStep.java @@ -78,4 +78,4 @@ protected BindingSet getNextElement() throws QueryEvaluationException { return result; } -} \ No newline at end of file +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/IntersectionQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/IntersectionQueryEvaluationStep.java index ffcf22dd17c..fec34d81181 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/IntersectionQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/IntersectionQueryEvaluationStep.java @@ -36,4 +36,4 @@ public IntersectionQueryEvaluationStep(QueryEvaluationStep leftArg, QueryEvaluat public CloseableIteration evaluate(BindingSet bs) { return new IntersectIteration<>(leftArgDelayed.evaluate(bs), rightArgDelayed.evaluate(bs), setMaker); } -} \ No newline at end of file +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/LeftJoinQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/LeftJoinQueryEvaluationStep.java index d83d702ad14..ef7c222e183 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/LeftJoinQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/LeftJoinQueryEvaluationStep.java @@ -89,4 +89,4 @@ public CloseableIteration evaluate(Binding return new BadlyDesignedLeftJoinIterator(left, right, condition, bindings, problemVars); } } -} \ No newline at end of file +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/OrderQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/OrderQueryEvaluationStep.java index bc9a9167c0b..de1671bc1a4 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/OrderQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/OrderQueryEvaluationStep.java @@ -37,4 +37,4 @@ public OrderQueryEvaluationStep(Comparator cmp, long limit, boolean public CloseableIteration evaluate(BindingSet bs) { return new OrderIterator(preparedArg.evaluate(bs), cmp, limit, reduced, iterationCacheSyncThreshold); } -} \ No newline at end of file +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/RegexValueEvaluationStepSupplier.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/RegexValueEvaluationStepSupplier.java index 140a5872021..28346606582 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/RegexValueEvaluationStepSupplier.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/RegexValueEvaluationStepSupplier.java @@ -21,7 +21,7 @@ import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep.ConstantQueryValueEvaluationStep; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; import org.eclipse.rdf4j.query.impl.EmptyBindingSet; public class RegexValueEvaluationStepSupplier { @@ -51,8 +51,8 @@ public Value evaluate(BindingSet bindings) farg = strategy.evaluate(flagsArg, bindings); } - if (QueryEvaluationUtil.isStringLiteral(arg) && QueryEvaluationUtil.isSimpleLiteral(parg) - && (farg == null || QueryEvaluationUtil.isSimpleLiteral(farg))) { + if (QueryEvaluationUtility.isStringLiteral(arg) && QueryEvaluationUtility.isSimpleLiteral(parg) + && (farg == null || QueryEvaluationUtility.isSimpleLiteral(farg))) { String text = ((Literal) arg).getLabel(); String ptn = ((Literal) parg).getLabel(); // TODO should this Pattern be cached? @@ -91,8 +91,8 @@ private static QueryValueEvaluationStep regexAndFlagsAreConstant(QueryValueEvalu if (flagsArg != null) { farg = fargStep.evaluate(EmptyBindingSet.getInstance()); } - if (QueryEvaluationUtil.isSimpleLiteral(parg) - && (farg == null || QueryEvaluationUtil.isSimpleLiteral(farg))) { + if (QueryEvaluationUtility.isSimpleLiteral(parg) + && (farg == null || QueryEvaluationUtility.isSimpleLiteral(farg))) { String ptn = ((Literal) parg).getLabel(); int f = extractRegexFlags(farg); Pattern pattern = Pattern.compile(ptn, f); @@ -103,7 +103,7 @@ private static QueryValueEvaluationStep regexAndFlagsAreConstant(QueryValueEvalu public Value evaluate(BindingSet bindings) throws ValueExprEvaluationException, QueryEvaluationException { Value arg = argStep.evaluate(bindings); - if (QueryEvaluationUtil.isStringLiteral(arg)) { + if (QueryEvaluationUtility.isStringLiteral(arg)) { String text = ((Literal) arg).getLabel(); boolean result = pattern.matcher(text).find(); BooleanLiteral valueOf = BooleanLiteral.valueOf(result); @@ -125,8 +125,8 @@ private static QueryValueEvaluationStep allRegexPartsAreConstant(QueryValueEvalu if (flagsArg != null) { farg = fargStep.evaluate(EmptyBindingSet.getInstance()); } - if (QueryEvaluationUtil.isStringLiteral(arg) && QueryEvaluationUtil.isSimpleLiteral(parg) - && (farg == null || QueryEvaluationUtil.isSimpleLiteral(farg))) { + if (QueryEvaluationUtility.isStringLiteral(arg) && QueryEvaluationUtility.isSimpleLiteral(parg) + && (farg == null || QueryEvaluationUtility.isSimpleLiteral(farg))) { String text = ((Literal) arg).getLabel(); String ptn = ((Literal) parg).getLabel(); int f = extractRegexFlags(farg); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/ServiceQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/ServiceQueryEvaluationStep.java index 22b29bcfb89..15720b1bd26 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/ServiceQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/ServiceQueryEvaluationStep.java @@ -116,4 +116,4 @@ public void meet(Var var) { } } -} \ No newline at end of file +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java index f24ded5e41d..6675a1ddf42 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java @@ -276,7 +276,7 @@ private static Predicate subjectVariableHasEquals(boolean subEqPredVa } /** - * + * * @param statementPattern * @param contextValue * @return the contexts that are valid for this statement pattern or null @@ -348,7 +348,7 @@ private static Resource[] fillContextsFromDatasSetGraphs(Set graphs) { /** * Converts statements into the required bindingsets. A lot of work is done in the constructor and then uses * invokedynamic code with lambdas for the actual conversion. - * + * * This allows avoiding of significant work during the iteration. Which pays of if the iteration is long, otherwise * it of course is an unneeded expense. */ @@ -383,10 +383,10 @@ protected BindingSet convert(Statement st) { /** * We are going to chain biconsumer functions allowing us to avoid a lot of equals etc. code - * + * * We need to test every binding with hasBinding etc. as these are not guaranteed to be equivalent between calls of * evaluate(bs). - * + * * @return a converter from statement into MutableBindingSet */ private static BiConsumer makeConverter(QueryEvaluationContext context, @@ -449,7 +449,7 @@ private static Predicate andThen(Predicate pred, Predicate /** * A convience function to chain the consumers together if one exists otherwise returns the new one. - * + * * @param co an earlier chain * @param and the consumer to add to the chain * @return a chain or the new (and) one diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java index b28618d423e..5fdabf5262d 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java @@ -31,9 +31,9 @@ import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration; import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; -import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.query.MutableBindingSet; import org.eclipse.rdf4j.query.QueryEvaluationException; @@ -169,7 +169,7 @@ private Set createSet(String setName) { /** * Only create a disk based set once the contents are large enough that it starts to pay off. - * + * * @param of the contents of the set. */ private class MemoryTillSizeXSet extends AbstractSet { @@ -517,7 +517,7 @@ public BindingSet getPrototype() { /** * This is to collect together in operation an aggregate function the name of it. And the suppliers that will give * the unique set and final value collectors per final binding set. - * + * * Making an aggregate function is quite a lot of work and we do not want to repeat that for each final binding. */ private class AggregatePredicateCollectorSupplier { @@ -656,7 +656,7 @@ private class CountCollector implements AggregateCollector { @Override public Value getFinalValue() { - return vf.createLiteral(Long.toString(value), XSD.INTEGER); + return vf.createLiteral(Long.toString(value), CoreDatatype.XSD.INTEGER); } } @@ -670,7 +670,7 @@ public Value getFinalValue() { } private class IntegerCollector implements AggregateCollector { - private Literal value = vf.createLiteral("0", XSD.INTEGER); + private Literal value = vf.createLiteral("0", CoreDatatype.XSD.INTEGER); @Override public Value getFinalValue() { @@ -679,7 +679,7 @@ public Value getFinalValue() { } private class AvgCollector implements AggregateCollector { - private Literal sum = vf.createLiteral("0", XSD.INTEGER); + private Literal sum = vf.createLiteral("0", CoreDatatype.XSD.INTEGER); private long count; private ValueExprEvaluationException typeError = null; @@ -692,7 +692,7 @@ public Value getFinalValue() throws ValueExprEvaluationException { } if (count == 0) { - return vf.createLiteral("0", XSD.INTEGER); + return vf.createLiteral("0", CoreDatatype.XSD.INTEGER); } Literal sizeLit = vf.createLiteral(count); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/LeftJoinIterator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/LeftJoinIterator.java index 48b9f035e4a..4560f7d0f32 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/LeftJoinIterator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/LeftJoinIterator.java @@ -24,7 +24,7 @@ import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; public class LeftJoinIterator extends LookAheadIteration { @@ -139,12 +139,8 @@ protected BindingSet getNextElement() throws QueryEvaluationException { } private boolean isTrue(QueryValueEvaluationStep expr, QueryBindingSet bindings) { - try { - Value value = expr.evaluate(bindings); - return QueryEvaluationUtil.getEffectiveBooleanValue(value); - } catch (ValueExprEvaluationException e) { - return false; - } + Value value = expr.evaluate(bindings); + return QueryEvaluationUtility.getEffectiveBooleanValue(value).orElse(false); } @Override diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/OrderIterator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/OrderIterator.java index 98aa89360e2..ce76b60278b 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/OrderIterator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/OrderIterator.java @@ -47,7 +47,7 @@ private static class SerializedQueue extends AbstractQue private final File file; - private ObjectOutputStream output; + private final ObjectOutputStream output; private ObjectInputStream input; @@ -134,9 +134,9 @@ public E next() { @Override public int size() { if (next == null) { - return (int) size; + return size; } else { - return (int) size + 1; + return size + 1; } } @@ -277,17 +277,17 @@ protected Iteration createIteration() thro while (iter.hasNext()) { if (list.size() >= syncThreshold && list.size() < limit) { SerializedQueue queue = new SerializedQueue<>("orderiter"); - sort(list).forEach(bs -> queue.add(bs)); + sort(list).forEach(queue::add); serialized.add(queue); decrement(list.size() - queue.size()); list = new ArrayList<>(list.size()); - if (threshold == null && serialized.stream().mapToLong(q -> q.size()).sum() >= limit) { - Stream stream = serialized.stream().map(q -> q.peekLast()); - threshold = stream.sorted(comparator).skip(serialized.size() - 1).findFirst().get(); + if (threshold == null && serialized.stream().mapToLong(SerializedQueue::size).sum() >= limit) { + Stream stream = serialized.stream().map(SerializedQueue::peekLast); + threshold = stream.sorted(comparator).skip(serialized.size() - 1).findFirst().orElseThrow(); } } else if (list.size() >= limit2 || !distinct && threshold == null && list.size() >= limit) { List sorted = new ArrayList<>(limit2); - sort(list).forEach(bs -> sorted.add(bs)); + sort(list).forEach(sorted::add); decrement(list.size() - sorted.size()); list = sorted; if (sorted.size() >= limit) { @@ -305,11 +305,17 @@ protected Iteration createIteration() thro } finally { iter.close(); } - SortedIterators iterator; + List> iterators = new ArrayList<>(serialized.size() + 1); - serialized.forEach(queue -> iterators.add(queue.iterator())); + serialized + .stream() + .map(SerializedQueue::iterator) + .forEach(iterators::add); + iterators.add(sort(list).iterator()); - iterator = new SortedIterators<>(comparator, distinct, iterators); + + SortedIterators iterator = new SortedIterators<>(comparator, distinct, iterators); + return new LimitIteration<>(new CloseableIteratorIteration<>(iterator), limit); } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/OrderComparator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/OrderComparator.java index bd21d71d20c..794546c71e0 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/OrderComparator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/OrderComparator.java @@ -10,12 +10,15 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.List; +import java.util.Set; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.query.QueryEvaluationException; import org.eclipse.rdf4j.query.algebra.Order; import org.eclipse.rdf4j.query.algebra.OrderElem; +import org.eclipse.rdf4j.query.algebra.evaluation.ArrayBindingSet; import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy; import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep; import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; @@ -32,7 +35,7 @@ */ public class OrderComparator implements Comparator { - private final transient Logger logger = LoggerFactory.getLogger(OrderComparator.class); + private final static Logger logger = LoggerFactory.getLogger(OrderComparator.class); private final ValueComparator cmp; @@ -45,16 +48,12 @@ public OrderComparator(EvaluationStrategy strategy, Order order, ValueComparator for (OrderElem element : order.getElements()) { final QueryValueEvaluationStep prepared = strategy.precompile(element.getExpr(), context); final boolean ascending = element.isAscending(); - Comparator comparator = new Comparator() { + Comparator comparator = (o1, o2) -> { + Value v1 = prepared.evaluate(o1); + Value v2 = prepared.evaluate(o2); - @Override - public int compare(BindingSet o1, BindingSet o2) { - Value v1 = prepared.evaluate(o1); - Value v2 = prepared.evaluate(o2); - - int compare = cmp.compare(v1, v2); - return ascending ? compare : -compare; - } + int compare = cmp.compare(v1, v2); + return ascending ? compare : -compare; }; allComparator = andThen(allComparator, comparator); } @@ -75,7 +74,6 @@ private Comparator andThen(Comparator allComparator, Com @Override public int compare(BindingSet o1, BindingSet o2) { - try { int comparedContents = bindingContentsComparator.compare(o1, o2); if (comparedContents != 0) { @@ -104,14 +102,24 @@ public int compare(BindingSet o1, BindingSet o2) { // we create an ordered list of binding names (using natural string order) to use for // consistent iteration over binding names and binding values. - final ArrayList o1bindingNamesOrdered = new ArrayList<>(o1.getBindingNames()); - Collections.sort(o1bindingNamesOrdered); + List o1bindingNamesOrdered; + List o2bindingNamesOrdered; + + if (o1 instanceof ArrayBindingSet && o2 instanceof ArrayBindingSet) { + o1bindingNamesOrdered = ((ArrayBindingSet) o1).getSortedBindingNames(); + o2bindingNamesOrdered = ((ArrayBindingSet) o2).getSortedBindingNames(); + } else { + o1bindingNamesOrdered = getSortedBindingNames(o1.getBindingNames()); + o2bindingNamesOrdered = null; + } // binding set sizes are equal. compare on binding names. - if (!o1.getBindingNames().equals(o2.getBindingNames())) { + if ((o2bindingNamesOrdered != null && !sortedEquals(o1bindingNamesOrdered, o2bindingNamesOrdered)) + || (!o1.getBindingNames().equals(o2.getBindingNames()))) { - final ArrayList o2bindingNamesOrdered = new ArrayList<>(o2.getBindingNames()); - Collections.sort(o2bindingNamesOrdered); + if (o2bindingNamesOrdered == null) { + o2bindingNamesOrdered = getSortedBindingNames(o2.getBindingNames()); + } for (int i = 0; i < o1bindingNamesOrdered.size(); i++) { String o1bn = o1bindingNamesOrdered.get(i); @@ -140,4 +148,29 @@ public int compare(BindingSet o1, BindingSet o2) { return 0; } } + + private boolean sortedEquals(List o1bindingNamesOrdered, List o2bindingNamesOrdered) { + if (o1bindingNamesOrdered.size() != o2bindingNamesOrdered.size()) { + return false; + } + + for (int i = 0; i < o1bindingNamesOrdered.size(); i++) { + if (!o1bindingNamesOrdered.get(i).equals(o2bindingNamesOrdered.get(i))) { + return false; + } + } + + return true; + } + + private static List getSortedBindingNames(Set bindingNames) { + if (bindingNames.size() == 1) { + return Collections.singletonList(bindingNames.iterator().next()); + } + + ArrayList list = new ArrayList<>(bindingNames); + Collections.sort(list); + return list; + } + } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtil.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtil.java index de8b21d7349..fe7a35361c8 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtil.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtil.java @@ -7,18 +7,17 @@ *******************************************************************************/ package org.eclipse.rdf4j.query.algebra.evaluation.util; -import java.util.Optional; +import java.util.Objects; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; -import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; import org.eclipse.rdf4j.model.util.Literals; -import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.Compare.CompareOp; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; @@ -27,15 +26,29 @@ */ public class QueryEvaluationUtil { + public static final ValueExprEvaluationException INDETERMINATE_DATE_TIME_EXCEPTION = new ValueExprEvaluationException( + "Indeterminate result for date/time comparison"); + public static final ValueExprEvaluationException STRING_WITH_OTHER_SUPPORTED_TYPE_EXCEPTION = new ValueExprEvaluationException( + "Unable to compare strings with other supported types"); + public static final ValueExprEvaluationException NUMERIC_WITH_OTHER_SUPPORTED_TYPE_EXCEPTION = new ValueExprEvaluationException( + "Unable to compare numeric types with other supported types"); + public static final ValueExprEvaluationException DATE_WITH_OTHER_SUPPORTED_TYPE_EXCEPTION = new ValueExprEvaluationException( + "Unable to compare date types with other supported types"); + public static final ValueExprEvaluationException UNSUPPOERTED_TYPES_EXCEPTION = new ValueExprEvaluationException( + "Unable to compare literals with unsupported types"); + public static final ValueExprEvaluationException NOT_COMPATIBLE_AND_ORDERED_EXCEPTION = new ValueExprEvaluationException( + "Only literals with compatible, ordered datatypes can be compared using <, <=, > and >= operators"); + /** * Determines the effective boolean value (EBV) of the supplied value as defined in the * SPARQL specification: *
    - *
  • The EBV of any literal whose type is xsd:boolean or numeric is false if the lexical form is not valid for - * that datatype (e.g. "abc"^^xsd:integer). - *
  • If the argument is a typed literal with a datatype of xsd:boolean, the EBV is the value of that argument. - *
  • If the argument is a plain literal or a typed literal with a datatype of xsd:string, the EBV is false if the - * operand value has zero length; otherwise the EBV is true. + *
  • The EBV of any literal whose type is CoreDatatype.XSD:boolean or numeric is false if the lexical form is not + * valid for that datatype (e.g. "abc"^^xsd:integer). + *
  • If the argument is a typed literal with a datatype of CoreDatatype.XSD:boolean, the EBV is the value of that + * argument. + *
  • If the argument is a plain literal or a typed literal with a datatype of CoreDatatype.XSD:string, the EBV is + * false if the operand value has zero length; otherwise the EBV is true. *
  • If the argument is a numeric type or a typed literal with a datatype derived from a numeric type, the EBV is * false if the operand value is NaN or is numerically equal to zero; otherwise the EBV is true. *
  • All other arguments, including unbound arguments, produce a type error. @@ -46,31 +59,31 @@ public class QueryEvaluationUtil { * @throws ValueExprEvaluationException In case the application of the EBV algorithm results in a type error. */ public static boolean getEffectiveBooleanValue(Value value) throws ValueExprEvaluationException { - if (value instanceof Literal) { + if (value.isLiteral()) { Literal literal = (Literal) value; String label = literal.getLabel(); - IRI datatype = literal.getDatatype(); + CoreDatatype.XSD datatype = literal.getCoreDatatype().asXSDDatatype().orElse(null); - if (datatype.equals(XSD.STRING)) { + if (datatype == CoreDatatype.XSD.STRING) { return label.length() > 0; - } else if (datatype.equals(XSD.BOOLEAN)) { + } else if (datatype == CoreDatatype.XSD.BOOLEAN) { // also false for illegal values return "true".equals(label) || "1".equals(label); - } else if (datatype.equals(XSD.DECIMAL)) { + } else if (datatype == CoreDatatype.XSD.DECIMAL) { try { String normDec = XMLDatatypeUtil.normalizeDecimal(label); return !normDec.equals("0.0"); } catch (IllegalArgumentException e) { return false; } - } else if (XMLDatatypeUtil.isIntegerDatatype(datatype)) { + } else if (datatype != null && datatype.isIntegerDatatype()) { try { String normInt = XMLDatatypeUtil.normalize(label, datatype); return !normInt.equals("0"); } catch (IllegalArgumentException e) { return false; } - } else if (XMLDatatypeUtil.isFloatingPointDatatype(datatype)) { + } else if (datatype != null && datatype.isFloatingPointDatatype()) { try { String normFP = XMLDatatypeUtil.normalize(label, datatype); return !normFP.equals("0.0E0") && !normFP.equals("NaN"); @@ -78,6 +91,7 @@ public static boolean getEffectiveBooleanValue(Value value) throws ValueExprEval return false; } } + } throw new ValueExprEvaluationException(); @@ -90,16 +104,16 @@ public static boolean compare(Value leftVal, Value rightVal, CompareOp operator) public static boolean compare(Value leftVal, Value rightVal, CompareOp operator, boolean strict) throws ValueExprEvaluationException { - if (leftVal instanceof Literal && rightVal instanceof Literal) { + if (leftVal.isLiteral() && rightVal.isLiteral()) { // Both left and right argument is a Literal return compareLiterals((Literal) leftVal, (Literal) rightVal, operator, strict); } else { // All other value combinations switch (operator) { case EQ: - return valuesEqual(leftVal, rightVal); + return Objects.equals(leftVal, rightVal); case NE: - return !valuesEqual(leftVal, rightVal); + return !Objects.equals(leftVal, rightVal); default: throw new ValueExprEvaluationException( "Only literals with compatible, ordered datatypes can be compared using <, <=, > and >= operators"); @@ -107,10 +121,6 @@ public static boolean compare(Value leftVal, Value rightVal, CompareOp operator, } } - private static boolean valuesEqual(Value leftVal, Value rightVal) { - return leftVal != null && rightVal != null && leftVal.equals(rightVal); - } - /** * Compares the supplied {@link Literal} arguments using the supplied operator, using strict (minimally-conforming) * SPARQL 1.1 operator behavior. @@ -144,123 +154,75 @@ public static boolean compareLiterals(Literal leftLit, Literal rightLit, Compare // type precendence: // - simple literal // - numeric - // - xsd:boolean - // - xsd:dateTime - // - xsd:string + // - CoreDatatype.XSD:boolean + // - CoreDatatype.XSD:dateTime + // - CoreDatatype.XSD:string // - RDF term (equal and unequal only) - IRI leftDatatype = leftLit.getDatatype(); - IRI rightDatatype = rightLit.getDatatype(); - - XSD.Datatype leftXsdDatatype = Literals.getXsdDatatype(leftLit).orElse(null); - XSD.Datatype rightXsdDatatype = Literals.getXsdDatatype(rightLit).orElse(null); + CoreDatatype.XSD leftCoreDatatype = leftLit.getCoreDatatype().asXSDDatatype().orElse(null); + CoreDatatype.XSD rightCoreDatatype = rightLit.getCoreDatatype().asXSDDatatype().orElse(null); boolean leftLangLit = Literals.isLanguageLiteral(leftLit); boolean rightLangLit = Literals.isLanguageLiteral(rightLit); // for purposes of query evaluation in SPARQL, simple literals and string-typed literals with the same lexical // value are considered equal. - IRI commonDatatype = null; - if (QueryEvaluationUtil.isSimpleLiteral(leftLit) && QueryEvaluationUtil.isSimpleLiteral(rightLit)) { - commonDatatype = XSD.STRING; - } - Integer compareResult = null; - - if (QueryEvaluationUtil.isSimpleLiteral(leftLit) && QueryEvaluationUtil.isSimpleLiteral(rightLit)) { - compareResult = leftLit.getLabel().compareTo(rightLit.getLabel()); - } else if ((!leftLangLit && !rightLangLit) || commonDatatype != null) { - if (commonDatatype == null && (leftXsdDatatype != null && rightXsdDatatype != null)) { - if (leftXsdDatatype == rightXsdDatatype || leftDatatype.equals(rightDatatype)) { - commonDatatype = leftDatatype; - } else if (leftXsdDatatype.isNumericDatatype() && rightXsdDatatype.isNumericDatatype()) { - // left and right arguments have different datatypes, try to find a more general, shared datatype - if (leftXsdDatatype == XSD.Datatype.DOUBLE || rightXsdDatatype == XSD.Datatype.DOUBLE) { - commonDatatype = XSD.DOUBLE; - } else if (leftXsdDatatype == XSD.Datatype.FLOAT || rightXsdDatatype == XSD.Datatype.FLOAT) { - commonDatatype = XSD.FLOAT; - } else if (leftXsdDatatype == XSD.Datatype.DECIMAL - || rightXsdDatatype == XSD.Datatype.DECIMAL) { - commonDatatype = XSD.DECIMAL; - } else { - commonDatatype = XSD.INTEGER; - } - } else if (!strict && leftXsdDatatype.isCalendarDatatype() && rightXsdDatatype.isCalendarDatatype()) { - // We're not running in strict eval mode so we use extended datatype comparsion. - commonDatatype = XSD.DATETIME; - } else if (!strict && leftXsdDatatype.isDurationDatatype() && rightXsdDatatype.isDurationDatatype()) { - commonDatatype = XSD.DURATION; - } - } else if (commonDatatype == null && (leftXsdDatatype == null || rightXsdDatatype == null)) { - if (leftDatatype.equals(rightDatatype)) { - commonDatatype = leftDatatype; - } else if (XMLDatatypeUtil.isNumericDatatype(leftDatatype) - && XMLDatatypeUtil.isNumericDatatype(rightDatatype)) { - // left and right arguments have different datatypes, try to find a more general, shared datatype - if (leftDatatype.equals(XSD.DOUBLE) || rightDatatype.equals(XSD.DOUBLE)) { - commonDatatype = XSD.DOUBLE; - } else if (leftDatatype.equals(XSD.FLOAT) || rightDatatype.equals(XSD.FLOAT)) { - commonDatatype = XSD.FLOAT; - } else if (leftDatatype.equals(XSD.DECIMAL) || rightDatatype.equals(XSD.DECIMAL)) { - commonDatatype = XSD.DECIMAL; - } else { - commonDatatype = XSD.INTEGER; - } - } else if (!strict && XMLDatatypeUtil.isCalendarDatatype(leftDatatype) - && XMLDatatypeUtil.isCalendarDatatype(rightDatatype)) { - // We're not running in strict eval mode so we use extended datatype comparsion. - commonDatatype = XSD.DATETIME; - } else if (!strict && XMLDatatypeUtil.isDurationDatatype(leftDatatype) - && XMLDatatypeUtil.isDurationDatatype(rightDatatype)) { - commonDatatype = XSD.DURATION; - } - } + if (QueryEvaluationUtil.isSimpleLiteral(leftLangLit, leftCoreDatatype) + && QueryEvaluationUtil.isSimpleLiteral(rightLangLit, rightCoreDatatype)) { + return compareWithOperator(operator, leftLit.getLabel().compareTo(rightLit.getLabel())); + } else if (!(leftLangLit || rightLangLit)) { + + CoreDatatype.XSD commonDatatype = getCommonDatatype(strict, leftCoreDatatype, rightCoreDatatype); if (commonDatatype != null) { try { - if (commonDatatype.equals(XSD.DOUBLE)) { - compareResult = Double.compare(leftLit.doubleValue(), rightLit.doubleValue()); - } else if (commonDatatype.equals(XSD.FLOAT)) { - compareResult = Float.compare(leftLit.floatValue(), rightLit.floatValue()); - } else if (commonDatatype.equals(XSD.DECIMAL)) { - compareResult = leftLit.decimalValue().compareTo(rightLit.decimalValue()); - } else if (XMLDatatypeUtil.isIntegerDatatype(commonDatatype)) { - compareResult = leftLit.integerValue().compareTo(rightLit.integerValue()); - } else if (commonDatatype.equals(XSD.BOOLEAN)) { - Boolean leftBool = leftLit.booleanValue(); - Boolean rightBool = rightLit.booleanValue(); - compareResult = leftBool.compareTo(rightBool); - } else if (XMLDatatypeUtil.isCalendarDatatype(commonDatatype)) { + if (commonDatatype == CoreDatatype.XSD.DOUBLE) { + return compareWithOperator(operator, + Double.compare(leftLit.doubleValue(), rightLit.doubleValue())); + } else if (commonDatatype == CoreDatatype.XSD.FLOAT) { + return compareWithOperator(operator, + Float.compare(leftLit.floatValue(), rightLit.floatValue())); + } else if (commonDatatype == CoreDatatype.XSD.DECIMAL) { + return compareWithOperator(operator, leftLit.decimalValue().compareTo(rightLit.decimalValue())); + } else if (commonDatatype.isIntegerDatatype()) { + return compareWithOperator(operator, leftLit.integerValue().compareTo(rightLit.integerValue())); + } else if (commonDatatype == CoreDatatype.XSD.BOOLEAN) { + return compareWithOperator(operator, + Boolean.compare(leftLit.booleanValue(), rightLit.booleanValue())); + } else if (commonDatatype.isCalendarDatatype()) { XMLGregorianCalendar left = leftLit.calendarValue(); XMLGregorianCalendar right = rightLit.calendarValue(); - compareResult = left.compare(right); + int compare = left.compare(right); // Note: XMLGregorianCalendar.compare() returns compatible values (-1, 0, 1) but INDETERMINATE // needs special treatment - if (compareResult == DatatypeConstants.INDETERMINATE) { - // If we compare two xsd:dateTime we should use the specific comparison specified in SPARQL + if (compare == DatatypeConstants.INDETERMINATE) { + // If we compare two CoreDatatype.XSD:dateTime we should use the specific comparison + // specified in SPARQL // 1.1 - if ((leftXsdDatatype == XSD.Datatype.DATETIME - || (leftXsdDatatype == null && leftDatatype.equals(XSD.DATETIME))) - && (rightXsdDatatype == XSD.Datatype.DATETIME - || (rightXsdDatatype == null && rightDatatype.equals(XSD.DATETIME)))) { - throw new ValueExprEvaluationException("Indeterminate result for date/time comparison"); - } else { - // We fallback to the regular RDF term compare - compareResult = null; + if (leftCoreDatatype == CoreDatatype.XSD.DATETIME + && rightCoreDatatype == CoreDatatype.XSD.DATETIME) { + throw INDETERMINATE_DATE_TIME_EXCEPTION; } - + } else { + return compareWithOperator(operator, compare); } - } else if (!strict && XMLDatatypeUtil.isDurationDatatype(commonDatatype)) { + + } else if (!strict && commonDatatype.isDurationDatatype()) { Duration left = XMLDatatypeUtil.parseDuration(leftLit.getLabel()); Duration right = XMLDatatypeUtil.parseDuration(rightLit.getLabel()); - compareResult = left.compare(right); - if (compareResult == DatatypeConstants.INDETERMINATE) { - compareResult = null; // We fallback to regular term comparison + int compare = left.compare(right); + if (compare != DatatypeConstants.INDETERMINATE) { + return compareWithOperator(operator, compare); + } else { + return otherCases(leftLit, rightLit, operator, leftCoreDatatype, rightCoreDatatype, + leftLangLit, rightLangLit); } - } else if (commonDatatype.equals(XSD.STRING)) { - compareResult = leftLit.getLabel().compareTo(rightLit.getLabel()); + + } else if (commonDatatype == CoreDatatype.XSD.STRING) { + return compareWithOperator(operator, leftLit.getLabel().compareTo(rightLit.getLabel())); } } catch (IllegalArgumentException e) { // One of the basic-type method calls failed, try syntactic match before throwing an error @@ -278,105 +240,116 @@ public static boolean compareLiterals(Literal leftLit, Literal rightLit, Compare } } - if (compareResult != null) { - // Literals have compatible ordered datatypes - switch (operator) { - case LT: - return compareResult.intValue() < 0; - case LE: - return compareResult.intValue() <= 0; - case EQ: - return compareResult.intValue() == 0; - case NE: - return compareResult.intValue() != 0; - case GE: - return compareResult.intValue() >= 0; - case GT: - return compareResult.intValue() > 0; - default: - throw new IllegalArgumentException("Unknown operator: " + operator); - } - } else { - // All other cases, e.g. literals with languages, unequal or - // unordered datatypes, etc. These arguments can only be compared - // using the operators 'EQ' and 'NE'. See SPARQL's RDFterm-equal - // operator + // All other cases, e.g. literals with languages, unequal or + // unordered datatypes, etc. These arguments can only be compared + // using the operators 'EQ' and 'NE'. See SPARQL's RDFterm-equal + // operator - boolean literalsEqual = leftLit.equals(rightLit); + return otherCases(leftLit, rightLit, operator, leftCoreDatatype, rightCoreDatatype, leftLangLit, rightLangLit); - if (!literalsEqual) { - if (!leftLangLit && !rightLangLit && isSupportedDatatype(leftDatatype) - && isSupportedDatatype(rightDatatype)) { - // left and right arguments have incompatible but supported datatypes + } - // we need to check that the lexical-to-value mapping for both datatypes succeeds - if (!XMLDatatypeUtil.isValidValue(leftLit.getLabel(), leftDatatype)) { - throw new ValueExprEvaluationException("not a valid datatype value: " + leftLit); - } + private static boolean otherCases(Literal leftLit, Literal rightLit, CompareOp operator, + CoreDatatype.XSD leftCoreDatatype, CoreDatatype.XSD rightCoreDatatype, boolean leftLangLit, + boolean rightLangLit) { + boolean literalsEqual = leftLit.equals(rightLit); - if (!XMLDatatypeUtil.isValidValue(rightLit.getLabel(), rightDatatype)) { - throw new ValueExprEvaluationException("not a valid datatype value: " + rightLit); - } + if (!literalsEqual) { + if (!leftLangLit && !rightLangLit && isSupportedDatatype(leftCoreDatatype) + && isSupportedDatatype(rightCoreDatatype)) { + // left and right arguments have incompatible but supported datatypes - boolean leftString; - boolean rightString; - boolean leftNumeric; - boolean rightNumeric; - boolean leftDate; - boolean rightDate; - - if (leftXsdDatatype != null) { - leftString = leftXsdDatatype == XSD.Datatype.STRING; - leftNumeric = leftXsdDatatype.isNumericDatatype(); - leftDate = leftXsdDatatype.isCalendarDatatype(); - } else { - leftString = leftDatatype.equals(XSD.STRING); - leftNumeric = XMLDatatypeUtil.isNumericDatatype(leftDatatype); - leftDate = XMLDatatypeUtil.isCalendarDatatype(leftDatatype); - } + // we need to check that the lexical-to-value mapping for both datatypes succeeds + if (!XMLDatatypeUtil.isValidValue(leftLit.getLabel(), leftCoreDatatype)) { + throw new ValueExprEvaluationException("not a valid datatype value: " + leftLit); + } - if (rightXsdDatatype != null) { - rightString = rightXsdDatatype == XSD.Datatype.STRING; - rightNumeric = rightXsdDatatype.isNumericDatatype(); - rightDate = rightXsdDatatype.isCalendarDatatype(); - } else { - rightString = rightDatatype.equals(XSD.STRING); - rightNumeric = XMLDatatypeUtil.isNumericDatatype(rightDatatype); - rightDate = XMLDatatypeUtil.isCalendarDatatype(rightDatatype); - } + if (!XMLDatatypeUtil.isValidValue(rightLit.getLabel(), rightCoreDatatype)) { + throw new ValueExprEvaluationException("not a valid datatype value: " + rightLit); + } - if (leftString != rightString) { - throw new ValueExprEvaluationException("Unable to compare strings with other supported types"); - } - if (leftNumeric != rightNumeric) { - throw new ValueExprEvaluationException( - "Unable to compare numeric types with other supported types"); - } - if (leftDate != rightDate) { - throw new ValueExprEvaluationException( - "Unable to compare date types with other supported types"); - } - } else if (!leftLangLit && !rightLangLit) { - // For literals with unsupported datatypes we don't know if their values are equal - throw new ValueExprEvaluationException("Unable to compare literals with unsupported types"); + boolean leftString = leftCoreDatatype == CoreDatatype.XSD.STRING; + boolean leftNumeric = leftCoreDatatype.isNumericDatatype(); + boolean leftDate = leftCoreDatatype.isCalendarDatatype(); + + boolean rightString = rightCoreDatatype == CoreDatatype.XSD.STRING; + boolean rightNumeric = rightCoreDatatype.isNumericDatatype(); + boolean rightDate = rightCoreDatatype.isCalendarDatatype(); + + if (leftString != rightString) { + throw STRING_WITH_OTHER_SUPPORTED_TYPE_EXCEPTION; + } + if (leftNumeric != rightNumeric) { + throw NUMERIC_WITH_OTHER_SUPPORTED_TYPE_EXCEPTION; } + if (leftDate != rightDate) { + throw DATE_WITH_OTHER_SUPPORTED_TYPE_EXCEPTION; + } + } else if (!leftLangLit && !rightLangLit) { + // For literals with unsupported datatypes we don't know if their values are equal + throw UNSUPPOERTED_TYPES_EXCEPTION; } + } - switch (operator) { - case EQ: - return literalsEqual; - case NE: - return !literalsEqual; - case LT: - case LE: - case GE: - case GT: - throw new ValueExprEvaluationException( - "Only literals with compatible, ordered datatypes can be compared using <, <=, > and >= operators"); - default: - throw new IllegalArgumentException("Unknown operator: " + operator); + switch (operator) { + case EQ: + return literalsEqual; + case NE: + return !literalsEqual; + case LT: + case LE: + case GE: + case GT: + throw NOT_COMPATIBLE_AND_ORDERED_EXCEPTION; + default: + throw new IllegalArgumentException("Unknown operator: " + operator); + } + } + + private static CoreDatatype.XSD getCommonDatatype(boolean strict, CoreDatatype.XSD leftCoreDatatype, + CoreDatatype.XSD rightCoreDatatype) { + if (leftCoreDatatype != null && rightCoreDatatype != null) { + if (leftCoreDatatype == rightCoreDatatype) { + return leftCoreDatatype; + } else if (leftCoreDatatype.isNumericDatatype() && rightCoreDatatype.isNumericDatatype()) { + // left and right arguments have different datatypes, try to find a more general, shared datatype + if (leftCoreDatatype == CoreDatatype.XSD.DOUBLE || rightCoreDatatype == CoreDatatype.XSD.DOUBLE) { + return CoreDatatype.XSD.DOUBLE; + } else if (leftCoreDatatype == CoreDatatype.XSD.FLOAT || rightCoreDatatype == CoreDatatype.XSD.FLOAT) { + return CoreDatatype.XSD.FLOAT; + } else if (leftCoreDatatype == CoreDatatype.XSD.DECIMAL + || rightCoreDatatype == CoreDatatype.XSD.DECIMAL) { + return CoreDatatype.XSD.DECIMAL; + } else { + return CoreDatatype.XSD.INTEGER; + } + } else if (!strict && leftCoreDatatype.isCalendarDatatype() && rightCoreDatatype.isCalendarDatatype()) { + // We're not running in strict eval mode so we use extended datatype comparsion. + return CoreDatatype.XSD.DATETIME; + } else if (!strict && leftCoreDatatype.isDurationDatatype() && rightCoreDatatype.isDurationDatatype()) { + return CoreDatatype.XSD.DURATION; } } + return null; + } + + private static boolean compareWithOperator(CompareOp operator, int i) { + switch (operator) { + case LT: + return i < 0; + case LE: + return i <= 0; + case EQ: + return i == 0; + case NE: + return i != 0; + case GE: + return i >= 0; + case GT: + return i > 0; + default: + throw new IllegalArgumentException("Unknown operator: " + operator); + } } /** @@ -387,20 +360,22 @@ && isSupportedDatatype(rightDatatype)) { * Documentation */ public static boolean isPlainLiteral(Value v) { - if (v instanceof Literal) { + if (v.isLiteral()) { return isPlainLiteral(((Literal) v)); } return false; } public static boolean isPlainLiteral(Literal l) { - Optional xsdDatatype = Literals.getXsdDatatype(l); - return xsdDatatype - .map(datatype -> datatype == XSD.Datatype.STRING) - .orElseGet(() -> (l.getDatatype().equals(XSD.STRING))); - + assert l.getLanguage().isEmpty() || (l.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING); + return l.getCoreDatatype() == CoreDatatype.XSD.STRING || l.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING; } +// public static boolean isPlainLiteral(Literal l) { +// return l.getCoreDatatype().filter(d -> d == CoreDatatype.XSD.STRING).isPresent(); +//// return l.getCoreDatatype().orElse(null) == CoreDatatype.XSD.STRING; +// } + /** * Checks whether the supplied value is a "simple literal". A "simple literal" is a literal with no language tag nor * datatype. @@ -408,7 +383,7 @@ public static boolean isPlainLiteral(Literal l) { * @see SPARQL Simple Literal Documentation */ public static boolean isSimpleLiteral(Value v) { - if (v instanceof Literal) { + if (v.isLiteral()) { return isSimpleLiteral((Literal) v); } @@ -417,22 +392,32 @@ public static boolean isSimpleLiteral(Value v) { /** * Checks whether the supplied literal is a "simple literal". A "simple literal" is a literal with no language tag - * and the datatype {@link XSD#STRING}. + * and the datatype {@link CoreDatatype.XSD#STRING}. * * @see SPARQL Simple Literal Documentation */ public static boolean isSimpleLiteral(Literal l) { - return !Literals.isLanguageLiteral(l) && l.getDatatype().equals(XSD.STRING); + return l.getCoreDatatype() == CoreDatatype.XSD.STRING && !Literals.isLanguageLiteral(l); + } + + /** + * Checks whether the supplied literal is a "simple literal". A "simple literal" is a literal with no language tag + * and the datatype {@link CoreDatatype.XSD#STRING}. + * + * @see SPARQL Simple Literal Documentation + */ + public static boolean isSimpleLiteral(boolean isLang, CoreDatatype datatype) { + return !isLang && datatype == CoreDatatype.XSD.STRING; } /** * Checks whether the supplied literal is a "string literal". A "string literal" is either a simple literal, a plain - * literal with language tag, or a literal with datatype xsd:string. + * literal with language tag, or a literal with datatype CoreDatatype.XSD:string. * * @see SPARQL Functions on Strings Documentation */ public static boolean isStringLiteral(Value v) { - if (v instanceof Literal) { + if (v.isLiteral()) { return isStringLiteral((Literal) v); } @@ -449,37 +434,30 @@ public static boolean isStringLiteral(Value v) { * Rules */ public static boolean compatibleArguments(Literal arg1, Literal arg2) { - boolean arg1Language = Literals.isLanguageLiteral(arg1); - boolean arg2Language = Literals.isLanguageLiteral(arg2); - boolean arg1Simple = isSimpleLiteral(arg1); - boolean arg2Simple = isSimpleLiteral(arg2); - // 1. The arguments are literals typed as xsd:string + // 1. The arguments are literals typed as CoreDatatype.XSD:string // 2. The arguments are language literals with identical language tags // 3. The first argument is a language literal and the second - // argument is a literal typed as xsd:string - - boolean compatible = - - (arg1Simple && arg2Simple) - || (arg1Language && arg2Language && arg1.getLanguage().equals(arg2.getLanguage())) - || (arg1Language && arg2Simple); + // argument is a literal typed as CoreDatatype.XSD:string - return compatible; + return (isSimpleLiteral(arg1) && isSimpleLiteral(arg2)) + || (Literals.isLanguageLiteral(arg1) && Literals.isLanguageLiteral(arg2) + && arg1.getLanguage().equals(arg2.getLanguage())) + || (Literals.isLanguageLiteral(arg1) && isSimpleLiteral(arg2)); } /** * Checks whether the supplied literal is a "string literal". A "string literal" is either a simple literal, a plain - * literal with language tag, or a literal with datatype xsd:string. + * literal with language tag, or a literal with datatype CoreDatatype.XSD:string. * * @see SPARQL Functions on Strings Documentation */ public static boolean isStringLiteral(Literal l) { - IRI datatype = l.getDatatype(); - return Literals.isLanguageLiteral(l) || datatype.equals(XSD.STRING); + return l.getCoreDatatype() == CoreDatatype.XSD.STRING || Literals.isLanguageLiteral(l); } - private static boolean isSupportedDatatype(IRI datatype) { - return (XSD.STRING.equals(datatype) || XMLDatatypeUtil.isNumericDatatype(datatype) - || XMLDatatypeUtil.isCalendarDatatype(datatype)); + private static boolean isSupportedDatatype(CoreDatatype.XSD datatype) { + return datatype != null && (datatype == CoreDatatype.XSD.STRING || + datatype.isNumericDatatype() || + datatype.isCalendarDatatype()); } } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtility.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtility.java new file mode 100644 index 00000000000..81ca2f95a20 --- /dev/null +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtility.java @@ -0,0 +1,547 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse RDF4J contributors. + * 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. + ******************************************************************************/ +package org.eclipse.rdf4j.query.algebra.evaluation.util; + +import java.util.Objects; + +import javax.xml.datatype.DatatypeConstants; +import javax.xml.datatype.Duration; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; +import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; +import org.eclipse.rdf4j.model.util.Literals; +import org.eclipse.rdf4j.query.algebra.Compare.CompareOp; + +/** + * This class will take over for QueryEvaluationUtil. Currently marked as InternalUseOnly because there may still be + * changes to how this class works. + * + * @author Arjohn Kampman + * @author Håvard M. Ottestad + */ +@InternalUseOnly() +public class QueryEvaluationUtility { + + /** + * Determines the effective boolean value (EBV) of the supplied value as defined in the + * SPARQL specification: + *
      + *
    • The EBV of any literal whose type is CoreDatatype.XSD:boolean or numeric is false if the lexical form is not + * valid for that datatype (e.g. "abc"^^xsd:integer). + *
    • If the argument is a typed literal with a datatype of CoreDatatype.XSD:boolean, the EBV is the value of that + * argument. + *
    • If the argument is a plain literal or a typed literal with a datatype of CoreDatatype.XSD:string, the EBV is + * false if the operand value has zero length; otherwise the EBV is true. + *
    • If the argument is a numeric type or a typed literal with a datatype derived from a numeric type, the EBV is + * false if the operand value is NaN or is numerically equal to zero; otherwise the EBV is true. + *
    • All other arguments, including unbound arguments, produce a type error. + *
    + * + * @param value Some value. + * @return The EBV of value. + */ + public static Result getEffectiveBooleanValue(Value value) { + if (value.isLiteral()) { + Literal literal = (Literal) value; + String label = literal.getLabel(); + CoreDatatype.XSD datatype = literal.getCoreDatatype().asXSDDatatype().orElse(null); + + if (datatype == CoreDatatype.XSD.STRING) { + return Result.fromBoolean(label.length() > 0); + } else if (datatype == CoreDatatype.XSD.BOOLEAN) { + // also false for illegal values + return Result.fromBoolean("true".equals(label) || "1".equals(label)); + } else if (datatype == CoreDatatype.XSD.DECIMAL) { + try { + String normDec = XMLDatatypeUtil.normalizeDecimal(label); + return Result.fromBoolean(!normDec.equals("0.0")); + } catch (IllegalArgumentException e) { + return Result.fromBoolean(false); + } + } else if (datatype != null && datatype.isIntegerDatatype()) { + try { + String normInt = XMLDatatypeUtil.normalize(label, datatype); + return Result.fromBoolean(!normInt.equals("0")); + } catch (IllegalArgumentException e) { + return Result.fromBoolean(false); + } + } else if (datatype != null && datatype.isFloatingPointDatatype()) { + try { + String normFP = XMLDatatypeUtil.normalize(label, datatype); + return Result.fromBoolean(!normFP.equals("0.0E0") && !normFP.equals("NaN")); + } catch (IllegalArgumentException e) { + return Result.fromBoolean(false); + } + } + + } + + return Result.incompatibleValueExpression; + } + + public static Result compare(Value leftVal, Value rightVal, CompareOp operator) { + return compare(leftVal, rightVal, operator, true); + } + + public static Result compare(Value leftVal, Value rightVal, CompareOp operator, boolean strict) { + if (leftVal.isLiteral() && rightVal.isLiteral()) { + // Both left and right argument is a Literal + return compareLiterals((Literal) leftVal, (Literal) rightVal, operator, strict); + } else { + // All other value combinations + switch (operator) { + case EQ: + return Result.fromBoolean(Objects.equals(leftVal, rightVal)); + case NE: + return Result.fromBoolean(!Objects.equals(leftVal, rightVal)); + default: + return Result.incompatibleValueExpression; + } + } + } + + /** + * Compares the supplied {@link Literal} arguments using the supplied operator, using strict (minimally-conforming) + * SPARQL 1.1 operator behavior. + * + * @param leftLit the left literal argument of the comparison. + * @param rightLit the right literal argument of the comparison. + * @param operator the comparison operator to use. + * @return {@code true} if execution of the supplied operator on the supplied arguments succeeds, {@code false} + * otherwise. + */ + public static Result compareLiterals(Literal leftLit, Literal rightLit, CompareOp operator) { + return compareLiterals(leftLit, rightLit, operator, true); + } + + public static Order compareLiterals(Literal leftLit, Literal rightLit, boolean strict) { + // type precendence: + // - simple literal + // - numeric + // - CoreDatatype.XSD:boolean + // - CoreDatatype.XSD:dateTime + // - CoreDatatype.XSD:string + // - RDF term (equal and unequal only) + + CoreDatatype leftCoreDatatype = leftLit.getCoreDatatype(); + CoreDatatype rightCoreDatatype = rightLit.getCoreDatatype(); + + boolean leftLangLit = leftCoreDatatype == CoreDatatype.RDF.LANGSTRING; + boolean rightLangLit = rightCoreDatatype == CoreDatatype.RDF.LANGSTRING; + + CoreDatatype.XSD leftXSDDatatype = leftCoreDatatype.asXSDDatatype().orElse(null); + CoreDatatype.XSD rightXSDDatatype = rightCoreDatatype.asXSDDatatype().orElse(null); + + // for purposes of query evaluation in SPARQL, simple literals and string-typed literals with the same lexical + // value are considered equal. + + if (leftCoreDatatype == CoreDatatype.XSD.STRING && rightCoreDatatype == CoreDatatype.XSD.STRING) { + return Order.from(leftLit.getLabel().compareTo(rightLit.getLabel())); + } else if (!(leftLangLit || rightLangLit)) { + + CoreDatatype.XSD commonDatatype = getCommonDatatype(strict, leftXSDDatatype, rightXSDDatatype); + + if (commonDatatype != null) { + + try { + Order order = handleCommonDatatype(leftLit, rightLit, strict, leftXSDDatatype, rightXSDDatatype, + leftLangLit, rightLangLit, commonDatatype); + + if (order == Order.illegalArgument) { + if (leftLit.equals(rightLit)) { + return Order.equal; + } + } + + if (order != null) { + return order; + } + } catch (IllegalArgumentException e) { + if (leftLit.equals(rightLit)) { + return Order.equal; + } + } + + } + } + + // All other cases, e.g. literals with languages, unequal or + // unordered datatypes, etc. These arguments can only be compared + // using the operators 'EQ' and 'NE'. See SPARQL's RDFterm-equal + // operator + + return otherCases(leftLit, rightLit, leftXSDDatatype, rightXSDDatatype, leftLangLit, rightLangLit); + + } + + /** + * Compares the supplied {@link Literal} arguments using the supplied operator. + * + * @param leftLit the left literal argument of the comparison. + * @param rightLit the right literal argument of the comparison. + * @param operator the comparison operator to use. + * @param strict boolean indicating whether comparison should use strict (minimally-conforming) SPARQL 1.1 + * operator behavior, or extended behavior. + * @return {@code true} if execution of the supplied operator on the supplied arguments succeeds, {@code false} + * otherwise. + */ + public static Result compareLiterals(Literal leftLit, Literal rightLit, CompareOp operator, boolean strict) { + Order order = compareLiterals(leftLit, rightLit, strict); + return order.toResult(operator); + } + + private static Order handleCommonDatatype(Literal leftLit, Literal rightLit, boolean strict, + CoreDatatype.XSD leftCoreDatatype, CoreDatatype.XSD rightCoreDatatype, boolean leftLangLit, + boolean rightLangLit, CoreDatatype.XSD commonDatatype) { + if (commonDatatype == CoreDatatype.XSD.DOUBLE) { + return Order.from(Double.compare(leftLit.doubleValue(), rightLit.doubleValue())); + } else if (commonDatatype == CoreDatatype.XSD.FLOAT) { + return Order.from(Float.compare(leftLit.floatValue(), rightLit.floatValue())); + } else if (commonDatatype == CoreDatatype.XSD.DECIMAL) { + return Order.from(leftLit.decimalValue().compareTo(rightLit.decimalValue())); + } else if (commonDatatype.isIntegerDatatype()) { + return Order.from(leftLit.integerValue().compareTo(rightLit.integerValue())); + } else if (commonDatatype == CoreDatatype.XSD.BOOLEAN) { + return Order.from(Boolean.compare(leftLit.booleanValue(), rightLit.booleanValue())); + } else if (commonDatatype.isCalendarDatatype()) { + XMLGregorianCalendar left = leftLit.calendarValue(); + XMLGregorianCalendar right = rightLit.calendarValue(); + + int compare = left.compare(right); + + // Note: XMLGregorianCalendar.compare() returns compatible values (-1, 0, 1) but INDETERMINATE + // needs special treatment + if (compare == DatatypeConstants.INDETERMINATE) { + // If we compare two CoreDatatype.XSD:dateTime we should use the specific comparison specified in SPARQL + // 1.1 + if (leftCoreDatatype == CoreDatatype.XSD.DATETIME && rightCoreDatatype == CoreDatatype.XSD.DATETIME) { + return Order.incompatibleValueExpression; + } + } else { + return Order.from(compare); + } + + } else if (!strict && commonDatatype.isDurationDatatype()) { + Duration left = XMLDatatypeUtil.parseDuration(leftLit.getLabel()); + Duration right = XMLDatatypeUtil.parseDuration(rightLit.getLabel()); + int compare = left.compare(right); + if (compare != DatatypeConstants.INDETERMINATE) { + return Order.from(compare); + } else { + return otherCases(leftLit, rightLit, leftCoreDatatype, rightCoreDatatype, leftLangLit, rightLangLit); + } + + } else if (commonDatatype == CoreDatatype.XSD.STRING) { + return Order.from(leftLit.getLabel().compareTo(rightLit.getLabel())); + } + + return null; + } + + private static Order otherCases(Literal leftLit, Literal rightLit, CoreDatatype.XSD leftCoreDatatype, + CoreDatatype.XSD rightCoreDatatype, boolean leftLangLit, boolean rightLangLit) { + boolean literalsEqual = leftLit.equals(rightLit); + + if (!literalsEqual) { + if (!leftLangLit && !rightLangLit && isSupportedDatatype(leftCoreDatatype) + && isSupportedDatatype(rightCoreDatatype)) { + // left and right arguments have incompatible but supported datatypes + + // we need to check that the lexical-to-value mapping for both datatypes succeeds + if (!XMLDatatypeUtil.isValidValue(leftLit.getLabel(), leftCoreDatatype)) { + return Order.incompatibleValueExpression; + } + + if (!XMLDatatypeUtil.isValidValue(rightLit.getLabel(), rightCoreDatatype)) { + return Order.incompatibleValueExpression; + } + + boolean leftString = leftCoreDatatype == CoreDatatype.XSD.STRING; + boolean leftNumeric = leftCoreDatatype.isNumericDatatype(); + boolean leftDate = leftCoreDatatype.isCalendarDatatype(); + + boolean rightString = rightCoreDatatype == CoreDatatype.XSD.STRING; + boolean rightNumeric = rightCoreDatatype.isNumericDatatype(); + boolean rightDate = rightCoreDatatype.isCalendarDatatype(); + + if (leftString != rightString) { + return Order.incompatibleValueExpression; + } + if (leftNumeric != rightNumeric) { + return Order.incompatibleValueExpression; + } + if (leftDate != rightDate) { + return Order.incompatibleValueExpression; + } + } else if (!leftLangLit && !rightLangLit) { + // For literals with unsupported datatypes we don't know if their values are equal + return Order.incompatibleValueExpression; + } + } + + if (literalsEqual) { + return Order.equal; + } + return Order.notEqual; + } + + private static CoreDatatype.XSD getCommonDatatype(boolean strict, CoreDatatype.XSD leftCoreDatatype, + CoreDatatype.XSD rightCoreDatatype) { + if (leftCoreDatatype != null && rightCoreDatatype != null) { + if (leftCoreDatatype == rightCoreDatatype) { + return leftCoreDatatype; + } else if (leftCoreDatatype.isNumericDatatype() && rightCoreDatatype.isNumericDatatype()) { + // left and right arguments have different datatypes, try to find a more general, shared datatype + if (leftCoreDatatype == CoreDatatype.XSD.DOUBLE || rightCoreDatatype == CoreDatatype.XSD.DOUBLE) { + return CoreDatatype.XSD.DOUBLE; + } else if (leftCoreDatatype == CoreDatatype.XSD.FLOAT || rightCoreDatatype == CoreDatatype.XSD.FLOAT) { + return CoreDatatype.XSD.FLOAT; + } else if (leftCoreDatatype == CoreDatatype.XSD.DECIMAL + || rightCoreDatatype == CoreDatatype.XSD.DECIMAL) { + return CoreDatatype.XSD.DECIMAL; + } else { + return CoreDatatype.XSD.INTEGER; + } + } else if (!strict && leftCoreDatatype.isCalendarDatatype() && rightCoreDatatype.isCalendarDatatype()) { + // We're not running in strict eval mode so we use extended datatype comparsion. + return CoreDatatype.XSD.DATETIME; + } else if (!strict && leftCoreDatatype.isDurationDatatype() && rightCoreDatatype.isDurationDatatype()) { + return CoreDatatype.XSD.DURATION; + } + } + return null; + } + + /** + * Checks whether the supplied value is a "plain literal". A "plain literal" is a literal with no datatype and + * optionally a language tag. + * + * @see RDF Literal + * Documentation + */ + public static boolean isPlainLiteral(Value v) { + if (v.isLiteral()) { + return isPlainLiteral(((Literal) v)); + } + return false; + } + + public static boolean isPlainLiteral(Literal l) { + assert l.getLanguage().isEmpty() || (l.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING); + return l.getCoreDatatype() == CoreDatatype.XSD.STRING || l.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING; + } + + /** + * Checks whether the supplied value is a "simple literal". A "simple literal" is a literal with no language tag nor + * datatype. + * + * @see SPARQL Simple Literal Documentation + */ + public static boolean isSimpleLiteral(Value v) { + if (v.isLiteral()) { + return isSimpleLiteral((Literal) v); + } + + return false; + } + +// public static boolean isPlainLiteral(Literal l) { +// return l.getCoreDatatype().filter(d -> d == CoreDatatype.XSD.STRING).isPresent(); +//// return l.getCoreDatatype().orElse(null) == CoreDatatype.XSD.STRING; +// } + + /** + * Checks whether the supplied literal is a "simple literal". A "simple literal" is a literal with no language tag + * and the datatype {@link CoreDatatype.XSD#STRING}. + * + * @see SPARQL Simple Literal Documentation + */ + public static boolean isSimpleLiteral(Literal l) { + return l.getCoreDatatype() == CoreDatatype.XSD.STRING; + } + + /** + * Checks whether the supplied literal is a "simple literal". A "simple literal" is a literal with no language tag + * and the datatype {@link CoreDatatype.XSD#STRING}. + * + * @see SPARQL Simple Literal Documentation + */ + public static boolean isSimpleLiteral(boolean isLang, CoreDatatype datatype) { + return datatype == CoreDatatype.XSD.STRING; + } + + /** + * Checks whether the supplied literal is a "string literal". A "string literal" is either a simple literal, a plain + * literal with language tag, or a literal with datatype CoreDatatype.XSD:string. + * + * @see SPARQL Functions on Strings Documentation + */ + public static boolean isStringLiteral(Value v) { + if (v.isLiteral()) { + return isStringLiteral((Literal) v); + } + + return false; + } + + /** + * Checks whether the supplied two literal arguments are 'argument compatible' according to the SPARQL definition. + * + * @param arg1 the first argument + * @param arg2 the second argument + * @return true iff the two supplied arguments are argument compatible, false otherwise + * @see SPARQL Argument Compatibility + * Rules + */ + public static boolean compatibleArguments(Literal arg1, Literal arg2) { + // 1. The arguments are literals typed as CoreDatatype.XSD:string + // 2. The arguments are language literals with identical language tags + // 3. The first argument is a language literal and the second + // argument is a literal typed as CoreDatatype.XSD:string + + return (isSimpleLiteral(arg1) && isSimpleLiteral(arg2)) + || (Literals.isLanguageLiteral(arg1) && Literals.isLanguageLiteral(arg2) + && arg1.getLanguage().equals(arg2.getLanguage())) + || (Literals.isLanguageLiteral(arg1) && isSimpleLiteral(arg2)); + } + + /** + * Checks whether the supplied literal is a "string literal". A "string literal" is either a simple literal, a plain + * literal with language tag, or a literal with datatype CoreDatatype.XSD:string. + * + * @see SPARQL Functions on Strings Documentation + */ + public static boolean isStringLiteral(Literal l) { + return l.getCoreDatatype() == CoreDatatype.XSD.STRING || l.getCoreDatatype() == CoreDatatype.RDF.LANGSTRING; + } + + private static boolean isSupportedDatatype(CoreDatatype.XSD datatype) { + return datatype != null && (datatype == CoreDatatype.XSD.STRING || datatype.isNumericDatatype() + || datatype.isCalendarDatatype()); + } + + public enum Result { + _true(true), + _false(false), + incompatibleValueExpression(), + illegalArgument(); + + static Result fromBoolean(boolean b) { + if (b) { + return _true; + } + return _false; + } + + private final boolean value; + private final boolean isIncompatible; + + Result(boolean value) { + this.value = value; + isIncompatible = false; + } + + Result() { + isIncompatible = true; + value = false; + } + + public boolean orElse(boolean alternative) { + if (this == incompatibleValueExpression) { + return alternative; + } else if (this == illegalArgument) { + throw new IllegalStateException("IllegalArgument needs to be handled"); + } + return value; + } + } + + enum Order { + smaller(-1), + greater(1), + equal(0), + notEqual(0), + incompatibleValueExpression(0), + illegalArgument(0); + + private final int value; + + Order(int value) { + this.value = value; + } + + public static Order from(int value) { + if (value < 0) { + return smaller; + } + if (value > 0) { + return greater; + } + return equal; + } + + public int asInt() { + if (!isValid() && this != notEqual) { + throw new IllegalStateException(); + } + return value; + } + + public boolean isValid() { + return !(this == incompatibleValueExpression || this == illegalArgument); + } + + public Result toResult(CompareOp operator) { + if (!isValid()) { + if (this == incompatibleValueExpression) { + return Result.incompatibleValueExpression; + } + if (this == illegalArgument) { + return Result.illegalArgument; + } + } + + if (this == notEqual) { + switch (operator) { + case EQ: + return Result._false; + case NE: + return Result._true; + case LT: + case LE: + case GE: + case GT: + return Result.incompatibleValueExpression; + default: + return Result.illegalArgument; + } + } + + switch (operator) { + case LT: + return Result.fromBoolean(value < 0); + case LE: + return Result.fromBoolean(value <= 0); + case EQ: + return Result.fromBoolean(value == 0); + case NE: + return Result.fromBoolean(value != 0); + case GE: + return Result.fromBoolean(value >= 0); + case GT: + return Result.fromBoolean(value > 0); + default: + return Result.illegalArgument; + } + } + } +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/ValueComparator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/ValueComparator.java index 33fd5a1909f..a0eceffb8ff 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/ValueComparator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/ValueComparator.java @@ -15,11 +15,7 @@ import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Triple; import org.eclipse.rdf4j.model.Value; -import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; -import org.eclipse.rdf4j.model.util.Literals; -import org.eclipse.rdf4j.model.vocabulary.XSD; -import org.eclipse.rdf4j.query.algebra.Compare.CompareOp; -import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; +import org.eclipse.rdf4j.model.base.CoreDatatype; /** * A comparator that compares values according the SPARQL value ordering as specified in @@ -108,27 +104,34 @@ private int compareURIs(IRI leftURI, IRI rightURI) { private int compareLiterals(Literal leftLit, Literal rightLit) { // Additional constraint for ORDER BY: "A plain literal is lower - // than an RDF literal with type xsd:string of the same lexical + // than an RDF literal with type CoreDatatype.XSD:string of the same lexical // form." - if (!(QueryEvaluationUtil.isPlainLiteral(leftLit) || QueryEvaluationUtil.isPlainLiteral(rightLit))) { - try { - boolean isSmaller = QueryEvaluationUtil.compareLiterals(leftLit, rightLit, CompareOp.LT, strict); - if (isSmaller) { - return -1; - } else { - boolean isEquivalent = QueryEvaluationUtil.compareLiterals(leftLit, rightLit, CompareOp.EQ, strict); - if (isEquivalent) { - return 0; - } - return 1; - } - } catch (ValueExprEvaluationException e) { - // literals cannot be compared using the '<' operator, continue - // below + if (!(QueryEvaluationUtility.isPlainLiteral(leftLit) || QueryEvaluationUtility.isPlainLiteral(rightLit))) { + QueryEvaluationUtility.Order order = compareNonPlainLiterals(leftLit, rightLit); + if (order.isValid()) { + return order.asInt(); + } + if (order == QueryEvaluationUtility.Order.illegalArgument) { + throw new IllegalStateException(); } } + return comparePlainLiterals(leftLit, rightLit); + } + + private QueryEvaluationUtility.Order compareNonPlainLiterals(Literal leftLit, Literal rightLit) { + + QueryEvaluationUtility.Order order = QueryEvaluationUtility.compareLiterals(leftLit, rightLit, strict); + + if (order == QueryEvaluationUtility.Order.notEqual) { + return QueryEvaluationUtility.Order.smaller; + } + + return order; + } + + private int comparePlainLiterals(Literal leftLit, Literal rightLit) { int result = 0; // FIXME: Confirm these rules work with RDF-1.1 @@ -139,101 +142,74 @@ private int compareLiterals(Literal leftLit, Literal rightLit) { if (leftDatatype != null) { if (rightDatatype != null) { // Both literals have datatypes - Optional leftXmlDatatype = Literals.getXsdDatatype(leftLit); - Optional rightXmlDatatype = Literals.getXsdDatatype(rightLit); + CoreDatatype.XSD leftXmlDatatype = leftLit.getCoreDatatype().asXSDDatatype().orElse(null); + CoreDatatype.XSD rightXmlDatatype = rightLit.getCoreDatatype().asXSDDatatype().orElse(null); - if (leftXmlDatatype.isPresent() && rightXmlDatatype.isPresent()) { - result = compareDatatypes(leftXmlDatatype.get(), rightXmlDatatype.get()); - } else { - result = compareDatatypes(leftDatatype, rightDatatype); + result = compareDatatypes(leftXmlDatatype, rightXmlDatatype, leftDatatype, rightDatatype); + if (result != 0) { + return result; } } else { - result = 1; + return 1; } } else if (rightDatatype != null) { - result = -1; + return -1; } - if (result == 0) { - // datatypes are equal or both literals are untyped; sort by language - // tags, simple literals come before literals with language tags - Optional leftLanguage = leftLit.getLanguage(); - Optional rightLanguage = rightLit.getLanguage(); + // datatypes are equal or both literals are untyped; sort by language + // tags, simple literals come before literals with language tags + Optional leftLanguage = leftLit.getLanguage(); + Optional rightLanguage = rightLit.getLanguage(); - if (leftLanguage.isPresent()) { - if (rightLanguage.isPresent()) { - result = leftLanguage.get().compareTo(rightLanguage.get()); - } else { - result = 1; + if (leftLanguage.isPresent()) { + if (rightLanguage.isPresent()) { + result = leftLanguage.get().compareTo(rightLanguage.get()); + if (result != 0) { + return result; } - } else if (rightLanguage.isPresent()) { - result = -1; + } else { + return 1; } + } else if (rightLanguage.isPresent()) { + return -1; } - if (result == 0) { - // Literals are equal as fas as their datatypes and language tags are - // concerned, compare their labels - result = leftLit.getLabel().compareTo(rightLit.getLabel()); - } - - return result; + // Literals are equal as fas as their datatypes and language tags are + // concerned, compare their labels + return leftLit.getLabel().compareTo(rightLit.getLabel()); } - /** - * Compares two literal datatypes and indicates if one should be ordered after the other. This algorithm ensures - * that compatible ordered datatypes (numeric and date/time) are grouped together so that - * {@link QueryEvaluationUtil#compareLiterals(Literal, Literal, CompareOp)} is used in consecutive ordering steps. - */ - private int compareDatatypes(IRI leftDatatype, IRI rightDatatype) { - if (XMLDatatypeUtil.isNumericDatatype(leftDatatype)) { - if (XMLDatatypeUtil.isNumericDatatype(rightDatatype)) { + private int compareDatatypes(CoreDatatype.XSD leftDatatype, CoreDatatype.XSD rightDatatype, IRI leftDatatypeIRI, + IRI rightDatatypeIRI) { + if (leftDatatype != null && leftDatatype == rightDatatype) { + return 0; + } else if (leftDatatype != null && leftDatatype.isNumericDatatype()) { + if (rightDatatype != null && rightDatatype.isNumericDatatype()) { // both are numeric datatypes - return compareURIs(leftDatatype, rightDatatype); + return leftDatatype.compareTo(rightDatatype); } else { return -1; } - } else if (XMLDatatypeUtil.isNumericDatatype(rightDatatype)) { + } else if (rightDatatype != null && rightDatatype.isNumericDatatype()) { return 1; - } else if (XMLDatatypeUtil.isCalendarDatatype(leftDatatype)) { - if (XMLDatatypeUtil.isCalendarDatatype(rightDatatype)) { - // both are calendar datatypes - return compareURIs(leftDatatype, rightDatatype); + } else if (leftDatatype != null && leftDatatype.isCalendarDatatype()) { + if (rightDatatype != null && rightDatatype.isCalendarDatatype()) { + return leftDatatype.compareTo(rightDatatype); } else { return -1; } - } else if (XMLDatatypeUtil.isCalendarDatatype(rightDatatype)) { + } else if (rightDatatype != null && rightDatatype.isCalendarDatatype()) { return 1; - } else { - // incompatible or unordered datatypes - return compareURIs(leftDatatype, rightDatatype); } - } - private int compareDatatypes(XSD.Datatype leftDatatype, XSD.Datatype rightDatatype) { - if (leftDatatype.isNumericDatatype()) { - if (rightDatatype.isNumericDatatype()) { - // both are numeric datatypes - return compareURIs(leftDatatype.getIri(), rightDatatype.getIri()); - } else { - return -1; - } - } else if (rightDatatype.isNumericDatatype()) { - return 1; - } else if (leftDatatype.isCalendarDatatype()) { - if (rightDatatype.isCalendarDatatype()) { - // both are calendar datatype - return compareURIs(leftDatatype.getIri(), rightDatatype.getIri()); - } else { - return -1; - } - } else if (rightDatatype.isCalendarDatatype()) { - return 1; - } else { - // incompatible or unordered datatype - return compareURIs(leftDatatype.getIri(), rightDatatype.getIri()); + if (leftDatatype != null && rightDatatype != null) { + return leftDatatype.compareTo(rightDatatype); } + + // incompatible or unordered datatype + return compareURIs(leftDatatypeIRI, rightDatatypeIRI); + } private int compareTriples(Triple leftTriple, Triple rightTriple) { diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtil.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtil.java index c9cf8982600..0b4a5731df2 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtil.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtil.java @@ -11,11 +11,10 @@ import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; -import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; -import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.query.algebra.MathExpr.MathOp; import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; @@ -36,29 +35,27 @@ public class XMLDatatypeMathUtil { * @return a datatype literal */ public static Literal compute(Literal leftLit, Literal rightLit, MathOp op) throws ValueExprEvaluationException { - IRI leftDatatype = leftLit.getDatatype(); - IRI rightDatatype = rightLit.getDatatype(); - - if (XMLDatatypeUtil.isNumericDatatype(leftDatatype) && XMLDatatypeUtil.isNumericDatatype(rightDatatype)) { - return MathUtil.compute(leftLit, rightLit, op); - } else if (XMLDatatypeUtil.isDurationDatatype(leftDatatype) - && XMLDatatypeUtil.isDurationDatatype(rightDatatype)) { - return operationsBetweenDurations(leftLit, rightLit, op); - } else if (XMLDatatypeUtil.isDecimalDatatype(leftDatatype) - && XMLDatatypeUtil.isDurationDatatype(rightDatatype)) { - return operationsBetweenDurationAndDecimal(rightLit, leftLit, op); - } else if (XMLDatatypeUtil.isDurationDatatype(leftDatatype) - && XMLDatatypeUtil.isDecimalDatatype(rightDatatype)) { - return operationsBetweenDurationAndDecimal(leftLit, rightLit, op); - } else if (XMLDatatypeUtil.isCalendarDatatype(leftDatatype) - && XMLDatatypeUtil.isDurationDatatype(rightDatatype)) { - return operationsBetweenCalendarAndDuration(leftLit, rightLit, op); - } else if (XMLDatatypeUtil.isDurationDatatype(leftDatatype) - && XMLDatatypeUtil.isCalendarDatatype(rightDatatype)) { - return operationsBetweenDurationAndCalendar(leftLit, rightLit, op); - } else { - throw new ValueExprEvaluationException("Mathematical operators are not supported on these operands"); + CoreDatatype.XSD leftDatatype = leftLit.getCoreDatatype().asXSDDatatype().orElse(null); + CoreDatatype.XSD rightDatatype = rightLit.getCoreDatatype().asXSDDatatype().orElse(null); + + if (leftDatatype != null && rightDatatype != null) { + if (leftDatatype.isNumericDatatype() && rightDatatype.isNumericDatatype()) { + return MathUtil.compute(leftLit, rightLit, op); + } else if (leftDatatype.isDurationDatatype() && rightDatatype.isDurationDatatype()) { + return operationsBetweenDurations(leftLit, rightLit, op); + } else if (leftDatatype.isDecimalDatatype() && rightDatatype.isDurationDatatype()) { + return operationsBetweenDurationAndDecimal(rightLit, leftLit, op); + } else if (leftDatatype.isDurationDatatype() && rightDatatype.isDecimalDatatype()) { + return operationsBetweenDurationAndDecimal(leftLit, rightLit, op); + } else if (leftDatatype.isCalendarDatatype() && rightDatatype.isDurationDatatype()) { + return operationsBetweenCalendarAndDuration(leftLit, rightLit, op); + } else if (leftDatatype.isDurationDatatype() && rightDatatype.isCalendarDatatype()) { + return operationsBetweenDurationAndCalendar(leftLit, rightLit, op); + } } + + throw new ValueExprEvaluationException("Mathematical operators are not supported on these operands"); + } private static Literal operationsBetweenDurations(Literal leftLit, Literal rightLit, MathOp op) { @@ -156,7 +153,7 @@ private static Literal buildLiteral(Duration duration) { return SimpleValueFactory.getInstance().createLiteral(duration.toString(), getDatatypeForDuration(duration)); } - private static IRI getDatatypeForDuration(Duration duration) { + private static CoreDatatype.XSD getDatatypeForDuration(Duration duration) { // Could not be implemented with Duration.getXMLSchemaType that is too strict ("P1Y" is considered invalid) boolean yearSet = duration.isSet(DatatypeConstants.YEARS); @@ -167,11 +164,11 @@ private static IRI getDatatypeForDuration(Duration duration) { boolean secondSet = duration.isSet(DatatypeConstants.SECONDS); if (!yearSet && !monthSet) { - return XSD.DAYTIMEDURATION; + return CoreDatatype.XSD.DAYTIMEDURATION; } if (!daySet && !hourSet && !minuteSet && !secondSet) { - return XSD.YEARMONTHDURATION; + return CoreDatatype.XSD.YEARMONTHDURATION; } - return XSD.DURATION; + return CoreDatatype.XSD.DURATION; } } diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java index 727b8a0f7ca..978ef7500c9 100644 --- a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java @@ -1,9 +1,9 @@ -/******************************************************************************* - * Copyright (c) 2021 Eclipse RDF4J contributors. - * 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. +/******************************************************************************* + * Copyright (c) 2021 Eclipse RDF4J contributors. + * 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. *******************************************************************************/ package org.eclipse.rdf4j.query.algebra.evaluation.impl; diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleanerTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleanerTest.java index 9a8ed968640..af59c45f0b9 100644 --- a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleanerTest.java +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/ParentReferenceCleanerTest.java @@ -1,9 +1,9 @@ -/******************************************************************************* - * Copyright (c) 2021 Eclipse RDF4J contributors. - * 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. +/******************************************************************************* + * Copyright (c) 2021 Eclipse RDF4J contributors. + * 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. *******************************************************************************/ package org.eclipse.rdf4j.query.algebra.evaluation.impl; diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtilityTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtilityTest.java new file mode 100644 index 00000000000..25291978fa8 --- /dev/null +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/util/QueryEvaluationUtilityTest.java @@ -0,0 +1,641 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse RDF4J contributors. + * 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. + ******************************************************************************/ + +package org.eclipse.rdf4j.query.algebra.evaluation.util; + +import static org.eclipse.rdf4j.query.algebra.Compare.CompareOp.EQ; +import static org.eclipse.rdf4j.query.algebra.Compare.CompareOp.LT; +import static org.eclipse.rdf4j.query.algebra.Compare.CompareOp.NE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; + +import javax.xml.datatype.XMLGregorianCalendar; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +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.XSD; +import org.eclipse.rdf4j.query.algebra.Compare.CompareOp; +import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; +import org.junit.Before; +import org.junit.Test; + +/** + * @author Jeen Broekstra + * @author Håvard M. Ottestad + */ +public class QueryEvaluationUtilityTest { + + private final ValueFactory f = SimpleValueFactory.getInstance(); + + private Literal arg1simple; + + private Literal arg2simple; + + private Literal arg1en; + + private Literal arg2en; + + private Literal arg1cy; + + private Literal arg2cy; + + private Literal arg1string; + + private Literal arg2string; + + private Literal arg1int; + + private Literal arg2int; + + private Literal arg1year; + + private Literal arg2year; + + private Literal arg1dateTime; + + private Literal arg2dateTime; + + private Literal arg1duration; + + private Literal arg2duration; + + private Literal arg1yearMonthDuration; + + private Literal arg2yearMonthDuration; + + private Literal arg1unknown; + + private Literal arg2unknown; + + @Before + public void setUp() { + arg1simple = f.createLiteral("abc"); + arg2simple = f.createLiteral("b"); + + arg1en = f.createLiteral("abc", "en"); + arg2en = f.createLiteral("b", "en"); + + arg1cy = f.createLiteral("abc", "cy"); + arg2cy = f.createLiteral("b", "cy"); + + arg1string = f.createLiteral("abc", XSD.STRING); + arg2string = f.createLiteral("b", XSD.STRING); + + arg1year = f.createLiteral("2007", XSD.GYEAR); + arg2year = f.createLiteral("2009", XSD.GYEAR); + + arg1dateTime = f.createLiteral("2009-01-01T20:20:20Z", XSD.DATETIME); + arg2dateTime = f.createLiteral("2007-01-01T20:20:20+02:00", XSD.DATETIME); + + arg1int = f.createLiteral(10); + arg2int = f.createLiteral(1); + + arg1duration = f.createLiteral("P1Y30DT1H1M1S", XSD.DURATION); + arg2duration = f.createLiteral("P1Y31DT1H1M1S", XSD.DURATION); + + arg1yearMonthDuration = f.createLiteral("P1M", XSD.YEARMONTHDURATION); + arg2yearMonthDuration = f.createLiteral("P1Y1M", XSD.YEARMONTHDURATION); + + arg1unknown = f.createLiteral("foo", f.createIRI("http://example.com/datatype")); + arg2unknown = f.createLiteral("bar", f.createIRI("http://example.com/datatype")); + } + + @Test + public void testCompatibleArguments() { + + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1simple, arg2simple)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1simple, arg2en)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1simple, arg2cy)); + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1simple, arg2string)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1simple, arg2int)); + + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1en, arg2simple)); + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1en, arg2en)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg2en, arg2cy)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1en, arg2cy)); + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1en, arg2string)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1en, arg2int)); + + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1cy, arg2simple)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1cy, arg2en)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg2cy, arg2en)); + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1cy, arg2cy)); + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1cy, arg2string)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1cy, arg2int)); + + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1string, arg2simple)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1string, arg2en)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1string, arg2cy)); + assertTrue(QueryEvaluationUtility.compatibleArguments(arg1string, arg2string)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1string, arg2int)); + + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1int, arg2simple)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1int, arg2en)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1int, arg2cy)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1int, arg2string)); + assertFalse(QueryEvaluationUtility.compatibleArguments(arg1int, arg2int)); + + } + + @Test + public void testCompareEQ() { + assertCompareTrue(arg1simple, arg1simple, EQ); + assertCompareTrue(arg1en, arg1en, EQ); + assertCompareTrue(arg2cy, arg2cy, EQ); + assertCompareTrue(arg1string, arg1string, EQ); + assertCompareTrue(arg1int, arg1int, EQ); + assertCompareTrue(arg1year, arg1year, EQ); + assertCompareTrue(arg1dateTime, arg1dateTime, EQ); + assertCompareTrue(arg1duration, arg1duration, EQ); + assertCompareTrue(arg1yearMonthDuration, arg1yearMonthDuration, EQ); + assertCompareException(arg1unknown, arg2unknown, EQ); + + assertCompareFalse(arg1simple, arg2simple, EQ); + assertCompareFalse(arg1simple, arg2en, EQ); + assertCompareFalse(arg1simple, arg2cy, EQ); + assertCompareFalse(arg1simple, arg2string, EQ); + assertCompareException(arg1simple, arg2int, EQ); + assertCompareException(arg1simple, arg2year, EQ); + assertCompareException(arg1simple, arg2unknown, EQ); + + assertCompareFalse(arg1en, arg2simple, EQ); + assertCompareFalse(arg1en, arg2en, EQ); + assertCompareFalse(arg2en, arg2cy, EQ); + assertCompareFalse(arg1en, arg2cy, EQ); + assertCompareFalse(arg1en, arg2string, EQ); + assertCompareFalse(arg1en, arg2int, EQ); + assertCompareFalse(arg1en, arg2unknown, EQ); + + assertCompareFalse(arg1cy, arg2simple, EQ); + assertCompareFalse(arg1cy, arg2en, EQ); + assertCompareFalse(arg2cy, arg2en, EQ); + assertCompareFalse(arg1cy, arg2cy, EQ); + assertCompareFalse(arg1cy, arg2string, EQ); + assertCompareFalse(arg1cy, arg2int, EQ); + assertCompareFalse(arg1cy, arg2unknown, EQ); + + assertCompareFalse(arg1string, arg2simple, EQ); + assertCompareFalse(arg1string, arg2en, EQ); + assertCompareFalse(arg1string, arg2cy, EQ); + assertCompareFalse(arg1string, arg2string, EQ); + assertCompareException(arg1string, arg2int, EQ); + assertCompareException(arg1string, arg2year, EQ); + assertCompareException(arg1string, arg2unknown, EQ); + + assertCompareException(arg1int, arg2simple, EQ); + assertCompareFalse(arg1int, arg2en, EQ); + assertCompareFalse(arg1int, arg2cy, EQ); + assertCompareException(arg1int, arg2string, EQ); + assertCompareFalse(arg1int, arg2int, EQ); + assertCompareException(arg1int, arg2year, EQ); + assertCompareException(arg1int, arg2unknown, EQ); + + assertCompareException(arg1year, arg2simple, EQ); + assertCompareFalse(arg1year, arg2en, EQ); + assertCompareException(arg1year, arg2string, EQ); + assertCompareException(arg1year, arg2int, EQ); + assertCompareFalse(arg1year, arg2year, EQ); + assertCompareFalse(arg1year, arg2dateTime, EQ); + assertCompareException(arg1year, arg2unknown, EQ); + + assertCompareException(arg1dateTime, arg2simple, EQ); + assertCompareFalse(arg1dateTime, arg2en, EQ); + assertCompareException(arg1dateTime, arg2string, EQ); + assertCompareException(arg1dateTime, arg2int, EQ); + assertCompareFalse(arg1dateTime, arg2year, EQ); + assertCompareFalse(arg1dateTime, arg2dateTime, EQ); + assertCompareException(arg1dateTime, arg2unknown, EQ); + + assertCompareException(arg1duration, arg2simple, EQ); + assertCompareFalse(arg1duration, arg2en, EQ); + assertCompareException(arg1duration, arg2string, EQ); + assertCompareException(arg1duration, arg2int, EQ); + assertCompareException(arg1duration, arg2year, EQ); + assertCompareException(arg1duration, arg2dateTime, EQ); + assertCompareException(arg1duration, arg2duration, EQ); + assertCompareFalse(arg1duration, arg2duration, EQ, false); + assertCompareException(arg1duration, arg2yearMonthDuration, EQ); + assertCompareException(arg1duration, arg2yearMonthDuration, EQ, false); + assertCompareException(arg1duration, arg2unknown, EQ); + } + + @Test + public void testCompareNE() { + assertCompareFalse(arg1simple, arg1simple, NE); + assertCompareFalse(arg1en, arg1en, NE); + assertCompareFalse(arg1cy, arg1cy, NE); + assertCompareFalse(arg1string, arg1string, NE); + assertCompareFalse(arg1int, arg1int, NE); + assertCompareFalse(arg1year, arg1year, NE); + assertCompareFalse(arg1dateTime, arg1dateTime, NE); + assertCompareException(arg1unknown, arg2unknown, NE); + + assertCompareTrue(arg1simple, arg2simple, NE); + assertCompareTrue(arg1simple, arg2en, NE); + assertCompareTrue(arg1simple, arg2cy, NE); + assertCompareTrue(arg1simple, arg2string, NE); + assertCompareException(arg1simple, arg2int, NE); + assertCompareException(arg1simple, arg2year, NE); + assertCompareException(arg1unknown, arg2unknown, NE); + + assertCompareTrue(arg1en, arg2simple, NE); + assertCompareTrue(arg1en, arg2en, NE); + assertCompareTrue(arg2en, arg2cy, NE); + assertCompareTrue(arg1en, arg2cy, NE); + assertCompareTrue(arg1en, arg2string, NE); + assertCompareTrue(arg1en, arg2int, NE); + assertCompareTrue(arg1en, arg2unknown, NE); + + assertCompareTrue(arg1cy, arg2simple, NE); + assertCompareTrue(arg1cy, arg2en, NE); + assertCompareTrue(arg2cy, arg2en, NE); + assertCompareTrue(arg1cy, arg2cy, NE); + assertCompareTrue(arg1cy, arg2string, NE); + assertCompareTrue(arg1cy, arg2int, NE); + assertCompareTrue(arg1cy, arg2unknown, NE); + + assertCompareTrue(arg1string, arg2simple, NE); + assertCompareTrue(arg1string, arg2en, NE); + assertCompareTrue(arg1string, arg2cy, NE); + assertCompareTrue(arg1string, arg2string, NE); + assertCompareException(arg1string, arg2int, NE); + assertCompareException(arg1string, arg2year, NE); + assertCompareException(arg1string, arg2unknown, NE); + + assertCompareException(arg1int, arg2simple, NE); + assertCompareTrue(arg1int, arg2en, NE); + assertCompareTrue(arg1int, arg2cy, NE); + assertCompareException(arg1int, arg2string, NE); + assertCompareTrue(arg1int, arg2int, NE); + assertCompareException(arg1int, arg2year, NE); + assertCompareException(arg1int, arg2unknown, NE); + + assertCompareException(arg1year, arg2simple, NE); + assertCompareTrue(arg1year, arg2en, NE); + assertCompareException(arg1year, arg2string, NE); + assertCompareException(arg1year, arg2int, NE); + assertCompareTrue(arg1year, arg2year, NE); + assertCompareTrue(arg1year, arg2dateTime, NE); + assertCompareException(arg1year, arg2unknown, NE); + + assertCompareException(arg1dateTime, arg2simple, NE); + assertCompareTrue(arg1dateTime, arg2en, NE); + assertCompareException(arg1dateTime, arg2string, NE); + assertCompareException(arg1dateTime, arg2int, NE); + assertCompareTrue(arg1dateTime, arg2year, NE); + assertCompareTrue(arg1dateTime, arg2dateTime, NE); + assertCompareException(arg1dateTime, arg2unknown, NE); + + assertCompareException(arg1duration, arg2simple, NE); + assertCompareTrue(arg1duration, arg2en, NE); + assertCompareException(arg1duration, arg2string, NE); + assertCompareException(arg1duration, arg2int, NE); + assertCompareException(arg1duration, arg2year, NE); + assertCompareException(arg1duration, arg2dateTime, NE); + assertCompareException(arg1duration, arg2duration, NE); + assertCompareTrue(arg1duration, arg2duration, NE, false); + assertCompareException(arg1duration, arg2yearMonthDuration, NE); + assertCompareException(arg1duration, arg2yearMonthDuration, NE, false); + assertCompareException(arg1duration, arg2unknown, NE); + } + + @Test + public void testCompareLT() { + assertCompareFalse(arg1simple, arg1simple, LT); + +// The arguments are equal, so they one can not be less than the other. +// assertCompareException(arg1en, arg1en, LT); + + assertCompareFalse(arg1string, arg1string, LT); + assertCompareFalse(arg1int, arg1int, LT); + assertCompareFalse(arg1year, arg1year, LT); + assertCompareFalse(arg1dateTime, arg1dateTime, LT); + assertCompareException(arg1unknown, arg2unknown, LT); + + assertCompareTrue(arg1simple, arg2simple, LT); + assertCompareException(arg1simple, arg2en, LT); + assertCompareTrue(arg1simple, arg2string, LT); + assertCompareException(arg1simple, arg2int, LT); + assertCompareException(arg1simple, arg2year, LT); + assertCompareException(arg1unknown, arg2unknown, LT); + + assertCompareException(arg1en, arg2simple, LT); + assertCompareException(arg1en, arg2en, LT); + assertCompareException(arg1en, arg2string, LT); + assertCompareException(arg1en, arg2int, LT); + assertCompareException(arg1en, arg2unknown, LT); + + assertCompareTrue(arg1string, arg2simple, LT); + assertCompareException(arg1string, arg2en, LT); + assertCompareTrue(arg1string, arg2string, LT); + assertCompareException(arg1string, arg2int, LT); + assertCompareException(arg1string, arg2year, LT); + assertCompareException(arg1string, arg2unknown, LT); + + assertCompareException(arg1int, arg2simple, LT); + assertCompareException(arg1int, arg2en, LT); + assertCompareException(arg1int, arg2string, LT); + assertCompareFalse(arg1int, arg2int, LT); + assertCompareException(arg1int, arg2year, LT); + assertCompareException(arg1int, arg2unknown, LT); + + assertCompareException(arg1year, arg2simple, LT); + assertCompareException(arg1year, arg2en, LT); + assertCompareException(arg1year, arg2string, LT); + assertCompareException(arg1year, arg2int, LT); + assertCompareTrue(arg1year, arg2year, LT); + + // comparison between xsd:gYear and xsd:dateTime should raise type error in strict mode + assertCompareException(arg1year, arg1dateTime, LT); + + // ... but should succeed in extended mode. + assertCompareTrue(arg1year, arg1dateTime, LT, false); + + assertCompareException(arg1year, arg2dateTime, LT); + assertCompareException(arg1year, arg2unknown, LT); + + assertCompareException(arg1dateTime, arg2simple, LT); + assertCompareException(arg1dateTime, arg2en, LT); + assertCompareException(arg1dateTime, arg2string, LT); + assertCompareException(arg1dateTime, arg2int, LT); + assertCompareFalse(arg1dateTime, arg1year, LT, false); + assertCompareException(arg1dateTime, arg2year, LT); + assertCompareFalse(arg1dateTime, arg2dateTime, LT); + assertCompareException(arg1dateTime, arg2unknown, LT); + + assertCompareException(arg1duration, arg2simple, LT); + assertCompareException(arg1duration, arg2en, LT); + assertCompareException(arg1duration, arg2string, LT); + assertCompareException(arg1duration, arg2int, LT); + assertCompareException(arg1duration, arg2year, LT); + assertCompareException(arg1duration, arg2dateTime, LT); + assertCompareException(arg1duration, arg2duration, LT); + assertCompareTrue(arg1duration, arg2duration, LT, false); + assertCompareException(arg1duration, arg2yearMonthDuration, LT); + assertCompareException(arg1duration, arg2yearMonthDuration, LT, false); + assertCompareException(arg1duration, arg2unknown, LT); + + assertCompareException(arg1yearMonthDuration, arg2simple, LT); + assertCompareException(arg1yearMonthDuration, arg2en, LT); + assertCompareException(arg1yearMonthDuration, arg2string, LT); + assertCompareException(arg1yearMonthDuration, arg2int, LT); + assertCompareException(arg1yearMonthDuration, arg2year, LT); + assertCompareException(arg1yearMonthDuration, arg2dateTime, LT); + assertCompareException(arg1yearMonthDuration, arg2duration, LT); + assertCompareTrue(arg1yearMonthDuration, arg2duration, LT, false); + assertCompareException(arg1yearMonthDuration, arg2yearMonthDuration, LT); + assertCompareTrue(arg1yearMonthDuration, arg2yearMonthDuration, LT, false); + assertCompareException(arg1yearMonthDuration, arg2unknown, LT); + } + + /** + * Assert that there is an exception as a result of comparing the two literals with the given operator. + * + * @param lit1 The left literal + * @param lit2 The right literal + * @param op The operator for the comparison + */ + private void assertCompareException(Literal lit1, Literal lit2, CompareOp op) { + assertCompareException(lit1, lit2, op, true); + } + + /** + * Assert that there is an exception as a result of comparing the two literals with the given operator. + * + * @param lit1 The left literal + * @param lit2 The right literal + * @param op The operator for the comparison + */ + private void assertCompareException(Literal lit1, Literal lit2, CompareOp op, boolean strict) { + QueryEvaluationUtility.Result result = QueryEvaluationUtility.compareLiterals(lit1, lit2, op, strict); + assertSame(QueryEvaluationUtility.Result.incompatibleValueExpression, result); + } + + private void assertCompareFalse(Literal lit1, Literal lit2, CompareOp op) { + assertCompareFalse(lit1, lit2, op, true); + } + + /** + * Assert that there is no exception as a result of comparing the two literals with the given operator and it + * returns false. + * + * @param lit1 The left literal + * @param lit2 The right literal + * @param op The operator for the comparison + */ + private void assertCompareFalse(Literal lit1, Literal lit2, CompareOp op, boolean strict) { + assertFalse("Compare did not return false for " + lit1.toString() + op.getSymbol() + lit2.toString(), + QueryEvaluationUtility.compareLiterals(lit1, lit2, op, strict).orElse(false)); + } + + private void assertCompareTrue(Literal lit1, Literal lit2, CompareOp op) { + assertCompareTrue(lit1, lit2, op, true); + } + + /** + * Assert that there is no exception as a result of comparing the two literals with the given operator and it + * returns true. + * + * @param lit1 The left literal + * @param lit2 The right literal + * @param op The operator for the comparison + * @param strict boolean switch between strict and extended comparison + */ + private void assertCompareTrue(Literal lit1, Literal lit2, CompareOp op, boolean strict) { + assertTrue("Compare did not return true for " + lit1.toString() + op.getSymbol() + lit2.toString(), + QueryEvaluationUtility.compareLiterals(lit1, lit2, op, strict).orElse(false)); + } + + /** + * Reporoduces GH-2760: an NPE has been thrown when comparing custom literal implementations + */ + @Test + public void testCompareWithCustomLiterals() { + SimpleValueFactory vf = SimpleValueFactory.getInstance(); + Literal left = vf.createLiteral(5); + + Literal right = getCustomLiteral(vf.createLiteral(6)); + // GH-2760: should not throw an NPE, simply try all avaliable comparator operators + for (CompareOp op : CompareOp.values()) { + QueryEvaluationUtility.compareLiterals(left, right, op, true); + QueryEvaluationUtility.compareLiterals(right, left, op, true); + } + + } + + /** + * Reporoduces GH-2760: an NPE has been thrown when comparing custom literal implementations + */ + @Test + public void testCompareWithCustomLiteralsIncompatible() { + SimpleValueFactory vf = SimpleValueFactory.getInstance(); + Literal left = vf.createLiteral("abc"); + + Literal right = getCustomLiteral(vf.createLiteral(6)); + // GH-2760: should not throw an NPE, simply try all avaliable comparator operators + for (CompareOp op : CompareOp.values()) { + try { + QueryEvaluationUtility.compareLiterals(left, right, op, true); + } catch (ValueExprEvaluationException e) { + assertEquals("Unable to compare strings with other supported types", e.getMessage()); + } + try { + QueryEvaluationUtility.compareLiterals(right, left, op, true); + } catch (ValueExprEvaluationException e) { + assertEquals("Unable to compare strings with other supported types", e.getMessage()); + } + } + } + + @Test + public void testCompareWithCustomLiteralsInditerminate() { + SimpleValueFactory vf = SimpleValueFactory.getInstance(); + Literal left = vf.createLiteral("2000-01-01T12:00:00", XSD.DATETIME); + + Literal right = getCustomLiteral(vf.createLiteral("1999-12-31T23:00:00Z", XSD.DATETIME)); + // GH-2760: should not throw an NPE, simply try all avaliable comparator operators + for (CompareOp op : CompareOp.values()) { + try { + QueryEvaluationUtility.compareLiterals(left, right, op, true); + } catch (ValueExprEvaluationException e) { + assertEquals("Indeterminate result for date/time comparison", e.getMessage()); + } + try { + QueryEvaluationUtility.compareLiterals(right, left, op, true); + } catch (ValueExprEvaluationException e) { + assertEquals("Indeterminate result for date/time comparison", e.getMessage()); + } + } + } + + @Test + public void testCompareCustomDatatypes() { + SimpleValueFactory vf = SimpleValueFactory.getInstance(); + Literal left = vf.createLiteral(1); + + Literal right = vf.createLiteral("I", vf.createIRI("http://example.org/romanNumeral")); + // GH-2760: should not throw an NPE, simply try all avaliable comparator operators + for (CompareOp op : CompareOp.values()) { + try { + QueryEvaluationUtility.compareLiterals(left, right, op, true); + } catch (ValueExprEvaluationException e) { + assertEquals("Unable to compare literals with unsupported types", e.getMessage()); + } + try { + QueryEvaluationUtility.compareLiterals(right, left, op, true); + } catch (ValueExprEvaluationException e) { + assertEquals("Unable to compare literals with unsupported types", e.getMessage()); + } + } + } + + /** + * Reporoduces GH-2760: an NPE has been thrown when comparing custom literal implementations + */ + @Test + public void testCompareWithOnlyCustomLiterals() { + SimpleValueFactory vf = SimpleValueFactory.getInstance(); + Literal left = getCustomLiteral(vf.createLiteral(1)); + + Literal right = getCustomLiteral(vf.createLiteral(6)); + // GH-2760: should not throw an NPE, simply try all avaliable comparator operators + for (CompareOp op : CompareOp.values()) { + QueryEvaluationUtility.compareLiterals(left, right, op, true); + } + } + + private Literal getCustomLiteral(Literal nested) { + Literal right = new Literal() { + + @Override + public String stringValue() { + return nested.stringValue(); + } + + @Override + public short shortValue() { + return nested.shortValue(); + } + + @Override + public long longValue() { + return nested.longValue(); + } + + @Override + public BigInteger integerValue() { + return nested.integerValue(); + } + + @Override + public int intValue() { + return nested.intValue(); + } + + @Override + public Optional getLanguage() { + return nested.getLanguage(); + } + + @Override + public String getLabel() { + return nested.getLabel(); + } + + @Override + public IRI getDatatype() { + return nested.getDatatype(); + } + + @Override + public float floatValue() { + return nested.floatValue(); + } + + @Override + public double doubleValue() { + return nested.doubleValue(); + } + + @Override + public BigDecimal decimalValue() { + return nested.decimalValue(); + } + + @Override + public XMLGregorianCalendar calendarValue() { + return nested.calendarValue(); + } + + @Override + public CoreDatatype getCoreDatatype() { + return nested.getCoreDatatype(); + } + + @Override + public byte byteValue() { + return nested.byteValue(); + } + + @Override + public boolean booleanValue() { + return nested.booleanValue(); + } + }; + return right; + } +} diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtilTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtilTest.java index 7602fad37aa..c355d3c7c71 100644 --- a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtilTest.java +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/util/XMLDatatypeMathUtilTest.java @@ -12,6 +12,7 @@ import org.eclipse.rdf4j.model.Literal; 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.XSD; import org.junit.Test; @@ -26,24 +27,27 @@ public class XMLDatatypeMathUtilTest { @Test public void testCompute() throws Exception { Literal float1 = vf.createLiteral("12", XSD.INTEGER); - Literal float2 = vf.createLiteral("2", XSD.INTEGER); + Literal float2 = vf.createLiteral("2", CoreDatatype.XSD.INTEGER); Literal duration1 = vf.createLiteral("P1Y1M", XSD.YEARMONTHDURATION); - Literal duration2 = vf.createLiteral("P1Y", XSD.YEARMONTHDURATION); + Literal duration2 = vf.createLiteral("P1Y", CoreDatatype.XSD.YEARMONTHDURATION); Literal yearMonth1 = vf.createLiteral("2012-10", XSD.GYEARMONTH); assertComputeEquals(vf.createLiteral("14", XSD.INTEGER), float1, float2, MathOp.PLUS); - assertComputeEquals(vf.createLiteral("10", XSD.INTEGER), float1, float2, MathOp.MINUS); + assertComputeEquals(vf.createLiteral("10", CoreDatatype.XSD.INTEGER), float1, float2, MathOp.MINUS); assertComputeEquals(vf.createLiteral("24", XSD.INTEGER), float1, float2, MathOp.MULTIPLY); - assertComputeEquals(vf.createLiteral("6", XSD.DECIMAL), float1, float2, MathOp.DIVIDE); + assertComputeEquals(vf.createLiteral("6", CoreDatatype.XSD.DECIMAL), float1, float2, MathOp.DIVIDE); assertComputeEquals(vf.createLiteral("P2Y1M", XSD.YEARMONTHDURATION), duration1, duration2, MathOp.PLUS); - assertComputeEquals(vf.createLiteral("P0Y1M", XSD.YEARMONTHDURATION), duration1, duration2, MathOp.MINUS); + assertComputeEquals(vf.createLiteral("P0Y1M", CoreDatatype.XSD.YEARMONTHDURATION), duration1, duration2, + MathOp.MINUS); - assertComputeEquals(vf.createLiteral("P12Y", XSD.YEARMONTHDURATION), float1, duration2, MathOp.MULTIPLY); + assertComputeEquals(vf.createLiteral("P12Y", CoreDatatype.XSD.YEARMONTHDURATION), float1, duration2, + MathOp.MULTIPLY); assertComputeEquals(vf.createLiteral("P12Y", XSD.YEARMONTHDURATION), duration2, float1, MathOp.MULTIPLY); assertComputeEquals(vf.createLiteral("2013-11", XSD.GYEARMONTH), yearMonth1, duration1, MathOp.PLUS); - assertComputeEquals(vf.createLiteral("2011-09", XSD.GYEARMONTH), yearMonth1, duration1, MathOp.MINUS); + assertComputeEquals(vf.createLiteral("2011-09", CoreDatatype.XSD.GYEARMONTH), yearMonth1, duration1, + MathOp.MINUS); assertComputeEquals(vf.createLiteral("2013-11", XSD.GYEARMONTH), duration1, yearMonth1, MathOp.PLUS); } diff --git a/core/repository/api/src/test/java/org/eclipse/rdf4j/repository/util/RDFLoaderTest.java b/core/repository/api/src/test/java/org/eclipse/rdf4j/repository/util/RDFLoaderTest.java index 83665274910..8ce5a8cccc1 100644 --- a/core/repository/api/src/test/java/org/eclipse/rdf4j/repository/util/RDFLoaderTest.java +++ b/core/repository/api/src/test/java/org/eclipse/rdf4j/repository/util/RDFLoaderTest.java @@ -50,14 +50,14 @@ /** * Unit tests for {@link RDFLoader}. - * + * * @author Manuel Fiorelli * */ public class RDFLoaderTest { @ClassRule - public static WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort().dynamicHttpsPort()); + public static WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort()); @Test public void testTurtleJavaResource() throws Exception { @@ -172,7 +172,7 @@ public void testNonInformationResource() throws Exception { try { stubFor(get("/Socrates") .willReturn( - permanentRedirect("https://localhost:" + wireMockRule.httpsPort() + "/Socrates.ttl"))); + permanentRedirect("http://localhost:" + wireMockRule.port() + "/Socrates.ttl"))); stubFor(get("/Socrates.ttl") .willReturn(aResponse() 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 0fa61082af9..c1012cafeca 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 @@ -12,9 +12,9 @@ import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.util.LiteralUtilException; import org.eclipse.rdf4j.model.vocabulary.RDF; -import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.rio.DatatypeHandler; import org.eclipse.rdf4j.rio.LanguageHandler; import org.eclipse.rdf4j.rio.ParseErrorListener; @@ -198,9 +198,13 @@ public static final Literal createLiteral(String label, String lang, IRI datatyp } // Backup for unnormalised datatype literal creation else if (workingDatatype != null) { - result = valueFactory.createLiteral(workingLabel, workingDatatype); + CoreDatatype coreDatatype = CoreDatatype.from(workingDatatype); + + result = valueFactory.createLiteral(workingLabel, + coreDatatype != CoreDatatype.NONE ? coreDatatype.getIri() : workingDatatype, coreDatatype); + } else { - result = valueFactory.createLiteral(workingLabel, XSD.STRING); + result = valueFactory.createLiteral(workingLabel, CoreDatatype.XSD.STRING); } } catch (Exception e) { reportFatalError(e, lineNo, columnNo, errListener); 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 42a9dcdf94d..ede66d08356 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 @@ -78,6 +78,11 @@ public Literal createLiteral(String label, CoreDatatype datatype) { return delegate.createLiteral(label, datatype); } + @Override + public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDatatype) { + return delegate.createLiteral(label, datatype, coreDatatype); + } + @Override public Literal createLiteral(boolean value) { return delegate.createLiteral(value); diff --git a/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLWriter.java b/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLWriter.java index 016bbd74511..b592539ede0 100644 --- a/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLWriter.java +++ b/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLWriter.java @@ -26,6 +26,7 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.util.Literals; import org.eclipse.rdf4j.model.vocabulary.RDF; import org.eclipse.rdf4j.model.vocabulary.XSD; @@ -298,14 +299,15 @@ protected void consumeStatement(Statement st) { if (Literals.isLanguageLiteral(objLit)) { writeAttribute("xml:lang", objLit.getLanguage().get()); } else { - IRI datatype = objLit.getDatatype(); + CoreDatatype coreDatatype = objLit.getCoreDatatype(); + // Check if datatype is rdf:XMLLiteral - isXMLLiteral = datatype.equals(RDF.XMLLITERAL); + isXMLLiteral = coreDatatype == CoreDatatype.RDF.XMLLITERAL; if (isXMLLiteral) { writeAttribute(RDF.NAMESPACE, "parseType", "Literal"); - } else if (!datatype.equals(XSD.STRING)) { - writeAttribute(RDF.NAMESPACE, "datatype", datatype.toString()); + } else if (coreDatatype != CoreDatatype.XSD.STRING) { + writeAttribute(RDF.NAMESPACE, "datatype", objLit.getDatatype().toString()); } } diff --git a/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/util/RDFXMLPrettyWriter.java b/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/util/RDFXMLPrettyWriter.java index cd71b17096e..9163aed09a3 100644 --- a/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/util/RDFXMLPrettyWriter.java +++ b/core/rio/rdfxml/src/main/java/org/eclipse/rdf4j/rio/rdfxml/util/RDFXMLPrettyWriter.java @@ -23,6 +23,7 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; import org.eclipse.rdf4j.model.util.Literals; import org.eclipse.rdf4j.model.vocabulary.RDF; @@ -455,9 +456,9 @@ private void writeAbbreviatedPredicate(IRI pred, Value obj) throws IOException, } else if (obj instanceof Literal) { Literal objLit = (Literal) obj; // datatype attribute - IRI datatype = objLit.getDatatype(); + CoreDatatype datatype = objLit.getCoreDatatype(); // Check if datatype is rdf:XMLLiteral - boolean isXmlLiteral = datatype.equals(RDF.XMLLITERAL); + boolean isXmlLiteral = datatype == CoreDatatype.RDF.XMLLITERAL; // language attribute if (Literals.isLanguageLiteral(objLit)) { @@ -466,7 +467,7 @@ private void writeAbbreviatedPredicate(IRI pred, Value obj) throws IOException, if (isXmlLiteral) { writeAttribute(RDF.NAMESPACE, "parseType", "Literal"); } else { - writeAttribute(RDF.NAMESPACE, "datatype", datatype.toString()); + writeAttribute(RDF.NAMESPACE, "datatype", objLit.getDatatype().toString()); } } diff --git a/core/rio/rdfxml/src/test/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLParserTest.java b/core/rio/rdfxml/src/test/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLParserTest.java index 6a59e1dc01c..0729f18d44a 100644 --- a/core/rio/rdfxml/src/test/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLParserTest.java +++ b/core/rio/rdfxml/src/test/java/org/eclipse/rdf4j/rio/rdfxml/RDFXMLParserTest.java @@ -16,6 +16,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.StringReader; import java.net.URL; import java.util.Collection; import java.util.Iterator; @@ -31,6 +32,7 @@ import org.eclipse.rdf4j.model.vocabulary.RDFS; import org.eclipse.rdf4j.rio.RDFParseException; import org.eclipse.rdf4j.rio.RDFParser; +import org.eclipse.rdf4j.rio.Rio; import org.eclipse.rdf4j.rio.helpers.ParseErrorCollector; import org.eclipse.rdf4j.rio.helpers.StatementCollector; import org.eclipse.rdf4j.rio.helpers.XMLParserSettings; @@ -217,7 +219,7 @@ public void testInsertUsedContextPrefixes() { " 0\n" + " 0\n" + " \"^^)"; - String s2 = "(http://mycorp.example.com/papers/NobelPaper1, http://purl.org/metadata/dublin_core#Creator, \"David Hume\")"; + String s2 = "(http://mycorp.example.com/papers/NobelPaper1, http://purl.org/metadata/dublin_core#Creator, \"David Hume\"^^)"; expectedLiteral[0] = s1; expectedLiteral[1] = s2; @@ -226,5 +228,6 @@ public void testInsertUsedContextPrefixes() { assertEquals(s.toString(), expectedLiteral[ind]); ind += 1; } + } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java index b1b510293ad..fb8bfe6e363 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java @@ -107,6 +107,9 @@ public LmdbLiteral(ValueStoreRevision revision, String label, IRI datatype, long public LmdbLiteral(ValueStoreRevision revision, String label, IRI datatype, CoreDatatype coreDatatype, long internalID) { this.label = label; + assert datatype != null; + assert coreDatatype != null; + assert coreDatatype == CoreDatatype.NONE || coreDatatype.getIri() == datatype; this.datatype = datatype; this.coreDatatype = coreDatatype; setInternalID(internalID, revision); @@ -150,8 +153,9 @@ public IRI getDatatype() { @Override public CoreDatatype getCoreDatatype() { init(); - if (coreDatatype == null) + if (coreDatatype == null) { coreDatatype = CoreDatatype.from(datatype); + } return coreDatatype; } diff --git a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/CalendarMemLiteral.java b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/CalendarMemLiteral.java index 4693b366da3..48ad302ba2d 100644 --- a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/CalendarMemLiteral.java +++ b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/CalendarMemLiteral.java @@ -12,6 +12,7 @@ import javax.xml.datatype.XMLGregorianCalendar; import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; /** @@ -39,7 +40,7 @@ public CalendarMemLiteral(Object creator, XMLGregorianCalendar calendar) { } public CalendarMemLiteral(Object creator, String label, XMLGregorianCalendar calendar) { - this(creator, label, XMLDatatypeUtil.qnameToURI(calendar.getXMLSchemaType()), calendar); + this(creator, label, XMLDatatypeUtil.qnameToCoreDatatype(calendar.getXMLSchemaType()), calendar); } public CalendarMemLiteral(Object creator, String label, IRI datatype, XMLGregorianCalendar calendar) { @@ -47,6 +48,11 @@ public CalendarMemLiteral(Object creator, String label, IRI datatype, XMLGregori this.calendar = calendar; } + public CalendarMemLiteral(Object creator, String label, CoreDatatype datatype, XMLGregorianCalendar calendar) { + super(creator, label, datatype); + this.calendar = calendar; + } + /*---------* * Methods * *---------*/ diff --git a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/DecimalMemLiteral.java b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/DecimalMemLiteral.java index 53b100c1a3a..9004c7488e3 100644 --- a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/DecimalMemLiteral.java +++ b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/DecimalMemLiteral.java @@ -11,6 +11,7 @@ import java.math.BigInteger; import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.vocabulary.XSD; /** @@ -45,6 +46,11 @@ public DecimalMemLiteral(Object creator, String label, BigDecimal value, IRI dat this.value = value; } + public DecimalMemLiteral(Object creator, String label, BigDecimal value, CoreDatatype datatype) { + super(creator, label, datatype); + this.value = value; + } + /*---------* * Methods * *---------*/ diff --git a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/IntegerMemLiteral.java b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/IntegerMemLiteral.java index 296832f7702..d8d63fdcd33 100644 --- a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/IntegerMemLiteral.java +++ b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/IntegerMemLiteral.java @@ -11,6 +11,7 @@ import java.math.BigInteger; import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.vocabulary.XSD; /** @@ -33,18 +34,27 @@ public class IntegerMemLiteral extends MemLiteral { *--------------*/ public IntegerMemLiteral(Object creator, BigInteger value) { - this(creator, value, XSD.INTEGER); + this(creator, value, CoreDatatype.XSD.INTEGER); } public IntegerMemLiteral(Object creator, BigInteger value, IRI datatype) { this(creator, value.toString(), value, datatype); } + public IntegerMemLiteral(Object creator, BigInteger value, CoreDatatype datatype) { + this(creator, value.toString(), value, datatype); + } + public IntegerMemLiteral(Object creator, String label, BigInteger value, IRI datatype) { super(creator, label, datatype); this.value = value; } + public IntegerMemLiteral(Object creator, String label, BigInteger value, CoreDatatype datatype) { + super(creator, label, datatype); + this.value = value; + } + /*---------* * Methods * *---------*/ diff --git a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemLiteral.java b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemLiteral.java index 611420854bd..5b36c9fe2ab 100644 --- a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemLiteral.java +++ b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemLiteral.java @@ -8,6 +8,7 @@ package org.eclipse.rdf4j.sail.memory.model; import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.impl.SimpleLiteral; import org.eclipse.rdf4j.model.vocabulary.XSD; @@ -73,6 +74,16 @@ public MemLiteral(Object creator, String label, IRI datatype) { this.creator = creator; } + public MemLiteral(Object creator, String label, IRI datatype, CoreDatatype coreDatatype) { + super(label, datatype, coreDatatype); + this.creator = creator; + } + + public MemLiteral(Object creator, String label, CoreDatatype datatype) { + super(label, datatype); + this.creator = creator; + } + /*---------* * Methods * *---------*/ diff --git a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemValueFactory.java b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemValueFactory.java index 07d399fc697..f6609c6ba18 100644 --- a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemValueFactory.java +++ b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/MemValueFactory.java @@ -19,11 +19,10 @@ import org.eclipse.rdf4j.model.Triple; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.base.AbstractValueFactory; -import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.util.Literals; import org.eclipse.rdf4j.model.util.URIUtil; import org.eclipse.rdf4j.model.util.Values; -import org.eclipse.rdf4j.model.vocabulary.XSD; /** * A factory for MemValue objects that keeps track of created objects to prevent the creation of duplicate objects, @@ -285,37 +284,38 @@ public MemBNode getOrCreateMemBNode(BNode bnode) { public MemLiteral getOrCreateMemLiteral(Literal literal) { return literalRegistry.getOrAdd(literal, () -> { String label = literal.getLabel(); - IRI datatype = literal.getDatatype(); - MemLiteral memLiteral; + CoreDatatype coreDatatype = literal.getCoreDatatype(); + IRI datatype = coreDatatype != CoreDatatype.NONE ? coreDatatype.getIri() : literal.getDatatype(); if (Literals.isLanguageLiteral(literal)) { - memLiteral = new MemLiteral(this, label, literal.getLanguage().get()); + return new MemLiteral(this, label, literal.getLanguage().get()); } else { try { - if (XMLDatatypeUtil.isIntegerDatatype(datatype)) { - memLiteral = new IntegerMemLiteral(this, label, literal.integerValue(), datatype); - } else if (datatype.equals(XSD.DECIMAL)) { - memLiteral = new DecimalMemLiteral(this, label, literal.decimalValue(), datatype); - } else if (datatype.equals(XSD.FLOAT)) { - memLiteral = new NumericMemLiteral(this, label, literal.floatValue(), datatype); - } else if (datatype.equals(XSD.DOUBLE)) { - memLiteral = new NumericMemLiteral(this, label, literal.doubleValue(), datatype); - } else if (datatype.equals(XSD.BOOLEAN)) { - memLiteral = new BooleanMemLiteral(this, label, literal.booleanValue()); - } else if (datatype.equals(XSD.DATETIME)) { - memLiteral = new CalendarMemLiteral(this, label, datatype, literal.calendarValue()); - } else if (datatype.equals(XSD.DATETIMESTAMP)) { - memLiteral = new CalendarMemLiteral(this, label, datatype, literal.calendarValue()); - } else { - memLiteral = new MemLiteral(this, label, datatype); + if (coreDatatype.isXSDDatatype()) { + if (((CoreDatatype.XSD) coreDatatype).isIntegerDatatype()) { + return new IntegerMemLiteral(this, label, literal.integerValue(), coreDatatype); + } else if (coreDatatype == CoreDatatype.XSD.DECIMAL) { + return new DecimalMemLiteral(this, label, literal.decimalValue(), coreDatatype); + } else if (coreDatatype == CoreDatatype.XSD.FLOAT) { + return new NumericMemLiteral(this, label, literal.floatValue(), coreDatatype); + } else if (coreDatatype == CoreDatatype.XSD.DOUBLE) { + return new NumericMemLiteral(this, label, literal.doubleValue(), coreDatatype); + } else if (coreDatatype == CoreDatatype.XSD.BOOLEAN) { + return new BooleanMemLiteral(this, label, literal.booleanValue()); + } else if (coreDatatype == CoreDatatype.XSD.DATETIME) { + return new CalendarMemLiteral(this, label, coreDatatype, literal.calendarValue()); + } else if (coreDatatype == CoreDatatype.XSD.DATETIMESTAMP) { + return new CalendarMemLiteral(this, label, coreDatatype, literal.calendarValue()); + } } + + return new MemLiteral(this, label, datatype, coreDatatype); + } catch (IllegalArgumentException e) { // Unable to parse literal label to primitive type - memLiteral = new MemLiteral(this, label, datatype); + return new MemLiteral(this, label, datatype); } } - - return memLiteral; }); } @@ -374,6 +374,11 @@ public Literal createLiteral(String value, IRI datatype) { return getOrCreateMemLiteral(super.createLiteral(value, datatype)); } + @Override + public Literal createLiteral(String value, CoreDatatype datatype) { + return getOrCreateMemLiteral(super.createLiteral(value, datatype)); + } + @Override public Literal createLiteral(boolean value) { MemLiteral newLiteral = new BooleanMemLiteral(this, value); diff --git a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/NumericMemLiteral.java b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/NumericMemLiteral.java index 157decbe282..18182470758 100644 --- a/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/NumericMemLiteral.java +++ b/core/sail/memory/src/main/java/org/eclipse/rdf4j/sail/memory/model/NumericMemLiteral.java @@ -8,6 +8,7 @@ package org.eclipse.rdf4j.sail.memory.model; import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil; import org.eclipse.rdf4j.model.vocabulary.XSD; @@ -36,6 +37,11 @@ public NumericMemLiteral(Object creator, String label, Number number, IRI dataty this.number = number; } + public NumericMemLiteral(Object creator, String label, Number number, CoreDatatype datatype) { + super(creator, label, datatype); + this.number = number; + } + public NumericMemLiteral(Object creator, Number number, IRI datatype) { this(creator, XMLDatatypeUtil.toString(number), number, datatype); } diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/OOMBenchmark.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/OOMBenchmark.java new file mode 100644 index 00000000000..baa380e4432 --- /dev/null +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/OOMBenchmark.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse RDF4J contributors. + * 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. + *******************************************************************************/ + +package org.eclipse.rdf4j.sail.memory.benchmark; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.io.IOUtils; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.algebra.evaluation.util.ValueComparator; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * This is a special benchmark that counts the number of benchmark iterations that can be run before running out of + * memory. The benchmarks will all fail because they will all eventually run out of memory because the Epsilon garbage + * collector is a no-op collector. Each benchmark prints the number of iterations they manage before running out of + * memory, this is the measurement that we care about. A higher number of iterations means that we produce less garbage. + * It doesn't necessarily mean that we use less memory. + * + * @author Håvard Ottestad + */ +@State(Scope.Benchmark) +@Warmup(iterations = 0) +@BenchmarkMode({ Mode.AverageTime }) +@Fork(value = 1, jvmArgs = { "-Xms8G", "-Xmx8G", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseEpsilonGC", + "-XX:+AlwaysPreTouch" }) +@Measurement(iterations = 1, time = 99999999) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class OOMBenchmark { + + private SailRepository repository; + + private static final String query9; + int count = 0; + List valuesList; + + static { + try { + query9 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query9.qr"), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include("OOMBenchmark.*") // adapt to run other benchmark tests + .forks(1) + .build(); + + new Runner(opt).run(); + } + + @Setup(Level.Trial) + public void setup() throws IOException, InterruptedException { + + repository = new SailRepository(new MemoryStore()); + + try (SailRepositoryConnection connection = repository.getConnection()) { + connection.begin(IsolationLevels.NONE); + connection.add(getResourceAsStream("benchmarkFiles/datagovbe-valid.ttl"), "", RDFFormat.TURTLE); + connection.commit(); + } + + try (SailRepositoryConnection connection = repository.getConnection()) { + try (Stream stream = connection.getStatements(null, null, null, false).stream()) { + valuesList = new ArrayList<>((int) stream.count()); + } + + try (Stream stream = connection.getStatements(null, null, null, false).stream()) { + stream + .map(Statement::getObject) + .sorted(new ValueComparator()) + .forEach(valuesList::add); + } + + } + + } + + @Setup(Level.Iteration) + public void setupIteration() { + count = 0; + } + + @TearDown(Level.Trial) + public void afterClass() { + + repository.shutDown(); + + } + + @Benchmark + public List sortAllObjectsWithSparql() { + + try (SailRepositoryConnection connection = repository.getConnection()) { + try (Stream stream = connection + .prepareTupleQuery(query9) + .evaluate() + .stream()) { + List collect = stream.limit(1).collect(Collectors.toList()); + incrementAndPrintCount(); + return collect; + } + } + } + + @Benchmark + public Value sortDirectly() { + + Collections.shuffle(valuesList, new Random(47583672)); + + valuesList.sort(new ValueComparator()); + incrementAndPrintCount(); + + return valuesList.get(0); + + } + + private void incrementAndPrintCount() { + System.out.println("\nCount: " + (++count)); + } + + private static InputStream getResourceAsStream(String name) { + return OOMBenchmark.class.getClassLoader().getResourceAsStream(name); + } + +} diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/SortBenchmark.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/SortBenchmark.java index 4d4669dbbb5..3989950075a 100644 --- a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/SortBenchmark.java +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/SortBenchmark.java @@ -40,21 +40,18 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; /** * @author Håvard Ottestad */ @State(Scope.Benchmark) -@Warmup(iterations = 5) +@Warmup(iterations = 0) @BenchmarkMode({ Mode.AverageTime }) // use G1GC because the workload is multi-threaded -@Fork(value = 1, jvmArgs = { "-Xms400M", "-Xmx400M", "-XX:+UseG1GC" }) -//@Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-Xmn4G", "-XX:+UseSerialGC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) -@Measurement(iterations = 5) +@Fork(value = 1, jvmArgs = { "-Xms400M", "-Xmx400M", "-XX:+UseSerialGC" }) +//@Fork(value = 1, jvmArgs = {"-Xms400M", "-Xmx400M", "-XX:+UseSerialGC", "-XX:StartFlightRecording=delay=60s,duration=240s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) +@Measurement(iterations = 100) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class SortBenchmark { @@ -73,18 +70,19 @@ public class SortBenchmark { List valuesList; - public static void main(String[] args) throws RunnerException { - Options opt = new OptionsBuilder() - .include("SortBenchmark.*") // adapt to run other benchmark tests - // .addProfiler("stack", "lines=20;period=1;top=20") - .forks(1) - .build(); - - new Runner(opt).run(); + public static void main(String[] args) throws RunnerException, IOException, InterruptedException { + SortBenchmark sortBenchmark = new SortBenchmark(); + sortBenchmark.setup(); + System.out.println("sort"); + while (true) { + sortBenchmark.sortDirectly(); + System.out.println("."); + } +// sortBenchmark.tearDown(); } @Setup(Level.Trial) - public void beforeClass() throws IOException, InterruptedException { + public void setup() throws IOException, InterruptedException { repository = new SailRepository(new MemoryStore()); @@ -109,7 +107,7 @@ private static InputStream getResourceAsStream(String name) { } @TearDown(Level.Trial) - public void afterClass() { + public void tearDown() { repository.shutDown(); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LiteralComparatorFilter.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LiteralComparatorFilter.java index be284125954..e6a992ca6e5 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LiteralComparatorFilter.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/LiteralComparatorFilter.java @@ -13,8 +13,7 @@ import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.query.algebra.Compare; -import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; -import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil; +import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility; /** * @author Håvard Ottestad @@ -35,13 +34,7 @@ public LiteralComparatorFilter(PlanNode parent, Literal compareTo, Compare.Compa @Override boolean checkTuple(ValidationTuple t) { Value literal = t.getValue(); - - try { - return QueryEvaluationUtil.compare(compareTo, literal, this.compareOp); - } catch (ValueExprEvaluationException e) { - return false; - } - + return QueryEvaluationUtility.compare(compareTo, literal, this.compareOp).orElse(false); } @Override diff --git a/core/spin/src/main/java/org/eclipse/rdf4j/spin/function/spif/ParseDate.java b/core/spin/src/main/java/org/eclipse/rdf4j/spin/function/spif/ParseDate.java index 77a4775e7ac..92535b64cf1 100644 --- a/core/spin/src/main/java/org/eclipse/rdf4j/spin/function/spif/ParseDate.java +++ b/core/spin/src/main/java/org/eclipse/rdf4j/spin/function/spif/ParseDate.java @@ -89,7 +89,7 @@ protected Value evaluate(ValueFactory valueFactory, Value arg1, Value arg2) thro } } - return valueFactory.createLiteral(dateValue, XMLDatatypeUtil.qnameToURI(dateType)); + return valueFactory.createLiteral(dateValue, XMLDatatypeUtil.qnameToCoreDatatype(dateType)); } static final class FieldAwareGregorianCalendar extends GregorianCalendar { diff --git a/testsuites/rio/src/main/java/org/eclipse/rdf4j/testsuite/rio/rdfxml/RDFXMLParserTestCase.java b/testsuites/rio/src/main/java/org/eclipse/rdf4j/testsuite/rio/rdfxml/RDFXMLParserTestCase.java index 220f401d94b..521b303eabc 100644 --- a/testsuites/rio/src/main/java/org/eclipse/rdf4j/testsuite/rio/rdfxml/RDFXMLParserTestCase.java +++ b/testsuites/rio/src/main/java/org/eclipse/rdf4j/testsuite/rio/rdfxml/RDFXMLParserTestCase.java @@ -9,12 +9,13 @@ import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.LinkedHashSet; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import javax.xml.parsers.ParserConfigurationException; @@ -24,6 +25,7 @@ import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; import org.eclipse.rdf4j.model.util.Models; import org.eclipse.rdf4j.model.vocabulary.RDF; @@ -53,11 +55,11 @@ public abstract class RDFXMLParserTestCase { * Constants * *-----------*/ - private static String W3C_TESTS_DIR = "http://www.w3.org/2000/10/rdf-tests/rdfcore/"; + private static final String W3C_TESTS_DIR = "http://www.w3.org/2000/10/rdf-tests/rdfcore/"; - private static String LOCAL_TESTS_DIR = "/testcases/rdfxml/"; + private static final String LOCAL_TESTS_DIR = "/testcases/rdfxml/"; - private static String W3C_MANIFEST_FILE = W3C_TESTS_DIR + "Manifest.rdf"; + private static final String W3C_MANIFEST_FILE = W3C_TESTS_DIR + "Manifest.rdf"; /*--------------------* * Static initializer * @@ -140,9 +142,9 @@ private class PositiveParserTest extends TestCase { * Variables * *-----------*/ - private String inputURL; + private final String inputURL; - private String outputURL; + private final String outputURL; /*--------------* * Constructors * @@ -170,9 +172,9 @@ protected void runTest() throws Exception { StatementCollector inputCollector = new StatementCollector(inputCollection); rdfxmlParser.setRDFHandler(inputCollector); - InputStream in = resolveURL(inputURL).openStream(); - rdfxmlParser.parse(in, base(inputURL)); - in.close(); + try (InputStream in = resolveURL(inputURL).openStream()) { + rdfxmlParser.parse(in, base(inputURL)); + } // Parse expected output data NTriplesParser ntriplesParser = new NTriplesParser(); @@ -184,24 +186,18 @@ protected void runTest() throws Exception { StatementCollector outputCollector = new StatementCollector(outputCollection); ntriplesParser.setRDFHandler(outputCollector); - in = resolveURL(outputURL).openStream(); - ntriplesParser.parse(in, base(inputURL)); - in.close(); + try (InputStream in = resolveURL(outputURL).openStream()) { + ntriplesParser.parse(in, base(inputURL)); + } // Check equality of the two models if (!Models.isomorphic(inputCollection, outputCollection)) { - StringBuilder sb = new StringBuilder(1024); - sb.append("models not equal\n"); - sb.append("Expected:\n"); - for (Statement st : outputCollection) { - sb.append(st).append("\n"); - } - sb.append("Actual:\n"); - for (Statement st : inputCollection) { - sb.append(st).append("\n"); - } - fail(sb.toString()); + String expected = outputCollection.stream().map(Objects::toString).collect(Collectors.joining()); + String actual = inputCollection.stream().map(Objects::toString).collect(Collectors.joining("\n")); + + assertEquals(expected, actual); + } } @@ -217,7 +213,7 @@ private class NegativeParserTest extends TestCase { * Variables * *-----------*/ - private String inputURL; + private final String inputURL; /*--------------* * Constructors * @@ -260,7 +256,7 @@ protected void runTest() { private static class CanonXMLValueFactory extends SimpleValueFactory { - private Canonicalizer c14n; + private final Canonicalizer c14n; public CanonXMLValueFactory() throws InvalidCanonicalizerException, ParserConfigurationException { org.apache.xml.security.Init.init(); @@ -271,25 +267,44 @@ public CanonXMLValueFactory() throws InvalidCanonicalizerException, ParserConfig @Override public Literal createLiteral(String value, IRI datatype) { if (RDF.XMLLITERAL.equals(datatype)) { - // Canonicalize the literal value - try { - value = new String(c14n.canonicalize(value.getBytes(StandardCharsets.UTF_8)), - StandardCharsets.UTF_8); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } catch (CanonicalizationException | SAXException e) { - // ignore - } catch (ParserConfigurationException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - // ignore + value = canonicalizeXmlLiteral(value); + } + + return super.createLiteral(value, datatype); + } + @Override + public Literal createLiteral(String value, CoreDatatype datatype) { + if (datatype == CoreDatatype.RDF.XMLLITERAL) { + value = canonicalizeXmlLiteral(value); } return super.createLiteral(value, datatype); } + + @Override + public Literal createLiteral(String value, IRI datatype, CoreDatatype coreDatatype) { + if (coreDatatype == CoreDatatype.RDF.XMLLITERAL) { + assert RDF.XMLLITERAL.equals(datatype); + value = canonicalizeXmlLiteral(value); + } else { + assert !RDF.XMLLITERAL.equals(datatype); + } + + return super.createLiteral(value, datatype, coreDatatype); + } + + private String canonicalizeXmlLiteral(String value) { + // Canonicalize the literal value + try { + return new String(c14n.canonicalize(value.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + } catch (ParserConfigurationException | IOException e) { + throw new RuntimeException(e); + } catch (CanonicalizationException | SAXException e) { + return value; + } + } + } private static URL url(String uri) throws MalformedURLException {