Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public final class SimpleDate extends AbstractTemporalPointLiteral {
@Serial
private static final long serialVersionUID = 1L;

private static final DatatypeFactory DATATYPE_FACTORY = DatatypeFactory.newDefaultInstance();

private final String label;
@SuppressWarnings("serial") // Calendar implementations have VM-specific serialization; RDF is the portable representation.
private final XMLGregorianCalendar calendar;
Expand All @@ -36,7 +38,7 @@ public SimpleDate(XMLGregorianCalendar calendar) {
public SimpleDate(String lexicalValue, IRI datatype) {
super(datatype == null ? XSDDatatype.DATE.getIRI() : datatype);
this.label = Objects.requireNonNull(lexicalValue, "lexicalValue");
this.calendar = DatatypeFactory.newDefaultInstance().newXMLGregorianCalendar(lexicalValue);
this.calendar = DATATYPE_FACTORY.newXMLGregorianCalendar(lexicalValue);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public final class SimpleDateTime extends AbstractTemporalPointLiteral {
@Serial
private static final long serialVersionUID = 1L;

private static final DatatypeFactory DATATYPE_FACTORY = DatatypeFactory.newDefaultInstance();

private final String label;
@SuppressWarnings("serial") // Calendar implementations have VM-specific serialization; RDF is the portable representation.
private final XMLGregorianCalendar calendar;
Expand All @@ -36,7 +38,7 @@ public SimpleDateTime(XMLGregorianCalendar calendar) {
public SimpleDateTime(String lexicalValue, IRI datatype) {
super(datatype == null ? XSDDatatype.DATETIME.getIRI() : datatype);
this.label = Objects.requireNonNull(lexicalValue, "lexicalValue");
this.calendar = DatatypeFactory.newDefaultInstance().newXMLGregorianCalendar(lexicalValue);
this.calendar = DATATYPE_FACTORY.newXMLGregorianCalendar(lexicalValue);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ private static String floatingString(Literal literal, double number) {
}

private static DatatypeValue dateTime(Literal value, NativeEvaluationContext context) {
if (value.getCoreDatatype() != XSDDatatype.STRING && value.getCoreDatatype() != XSDDatatype.DATETIME) {
if (value.getCoreDatatype() == XSDDatatype.DATETIME) {
return value;
}
if (value.getCoreDatatype() != XSDDatatype.STRING) {
throw new QueryTypeErrorException("Cannot cast this datatype to xsd:dateTime");
}
Literal result = context.values().createLiteral(whitespaceCollapsed(value.getLabel()), XSDDatatype.DATETIME.getIRI());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,46 +129,87 @@ public static IDatatype create(String valueJType,
lang = lang.toLowerCase();
}
switch (valueJType) {
case Cst.jTypeString:
case Cst.jTypeString -> {
return new CoreseString(label);
case Cst.jTypeURI:
}
case Cst.jTypeURI -> {
return new CoreseURI(label);
case Cst.jTypeLiteral:
}
case Cst.jTypeLiteral -> {
return new CoreseLiteral(label, lang);
case Cst.jTypeJSON:
}
case Cst.jTypeJSON -> {
return DatatypeMap.json(label);
}
default -> {
// Non-primitive datatypes fall through to constructBuiltin
}
}

String display = (datatype == null) ? valueJType : datatype;

CoreseDatatype o = null;
Class valueClass = null;

try {
o = constructBuiltin(valueJType, label);
} catch (CoreseDatatypeException | RuntimeException e) {
if (cast) {
throw failure;
} else {
throw new CoreseDatatypeException(e, display, label);
}
}

if (o == null) {
o = constructReflective(valueJType, label, display, cast);
}

o.setLang(lang);
o.setDatatype(datatype);
return o.typeCheck();
}

private static CoreseDatatype constructBuiltin(String valueJType, String label) throws CoreseDatatypeException {
return switch (valueJType) {
case Cst.jTypeBoolean -> new CoreseBoolean(label);
case Cst.jTypeInteger -> new CoreseInteger(label);
case Cst.jTypeGenericInteger -> new CoreseGenericInteger(label);
case Cst.jTypeDecimal -> new CoreseDecimal(label);
case Cst.jTypeFloat -> new CoreseFloat(label);
case Cst.jTypeDouble -> new CoreseDouble(label);
case Cst.jTypeDate -> new CoreseDate(label);
case Cst.jTypeDateTime -> new CoreseDateTime(label);
case Cst.jTypeDay -> new CoreseDay(label);
case Cst.jTypeMonth -> new CoreseMonth(label);
case Cst.jTypeYear -> new CoreseYear(label);
case Cst.jTypeUndef -> new CoreseUndefLiteral(label);
case Cst.jTypeXMLString -> new CoreseXMLLiteral(label);
case Cst.jTypeGeneric -> new CoreseGeneric(label);
default -> null;
};
}

private static CoreseDatatype constructReflective(String valueJType, String label, String display, boolean cast)
throws CoreseDatatypeException {
Class<?> valueClass;
try {
valueClass = Class.forName(valueJType);
} catch (ClassNotFoundException e) {
throw new CoreseDatatypeException(e, valueJType, label);
}

try {
Class[] argClass = {label.getClass()};
Class<?>[] argClass = {label.getClass()};
String[] arg = {label};
o = (CoreseDatatype) valueClass.getConstructor(argClass).newInstance((Object[]) arg);
return (CoreseDatatype) valueClass.getConstructor(argClass).newInstance((Object[]) arg);
} catch (ClassCastException | IllegalAccessException | InstantiationException | NoSuchMethodException e) {
throw new CoreseDatatypeException(e, valueJType, label);
} catch (java.lang.reflect.InvocationTargetException e) {
// CoreseDatatypeException arrives here
if (cast) {
// to speed up sparql cast we throw a static exception
// because it will be transformed to null
throw failure;
} else {
throw new CoreseDatatypeException(e, display, label);
}
}

o.setLang(lang);
o.setDatatype(datatype);
return o.typeCheck();
}

static boolean isNumber(String name, String cname) {
Expand Down
119 changes: 69 additions & 50 deletions src/main/java/fr/inria/corese/core/sparql/datatype/CoreseDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
public class CoreseDate extends CoreseDatatype {

private final XMLGregorianCalendar cal;
private final long epochMillis;
private final boolean hasZone;
private final boolean hasFastEpoch;
private static final java.util.TimeZone UTC_ZONE = java.util.TimeZone.getTimeZone("UTC");
private static final String TODAY = "today";
private static final IDatatype.Datatype code = IDatatype.Datatype.DATE;
private static final CoreseURI datatype = new CoreseURI(RDF.xsddate);
Expand All @@ -22,11 +26,19 @@ public class CoreseDate extends CoreseDatatype {
public CoreseDate() {
this.cal = DatatypeMap.newXMLGregorianCalendar();
setLabel(cal.toString());
this.hasZone = (this.cal.getTimezone() != DatatypeConstants.FIELD_UNDEFINED);
long em = computeSafeEpoch(this.cal, this.hasZone);
this.epochMillis = em;
this.hasFastEpoch = (em != Long.MIN_VALUE);
}

public CoreseDate(XMLGregorianCalendar cal) {
this.cal = cal;
setLabel(cal.toString());
this.hasZone = (this.cal.getTimezone() != DatatypeConstants.FIELD_UNDEFINED);
long em = computeSafeEpoch(this.cal, this.hasZone);
this.epochMillis = em;
this.hasFastEpoch = (em != Long.MIN_VALUE);
}

public CoreseDate(String label) {
Expand All @@ -37,6 +49,25 @@ public CoreseDate(String label) {
this.cal = DatatypeMap.newXMLGregorianCalendar(label);
setLabel(label);
}
this.hasZone = (this.cal.getTimezone() != DatatypeConstants.FIELD_UNDEFINED);
long em = computeSafeEpoch(this.cal, this.hasZone);
this.epochMillis = em;
this.hasFastEpoch = (em != Long.MIN_VALUE);
}

private static long computeSafeEpoch(XMLGregorianCalendar calendar, boolean zone) {
try {
return computeEpoch(calendar, zone);
} catch (IllegalArgumentException | IllegalStateException | ArithmeticException e) {
return Long.MIN_VALUE;
}
}

private static long computeEpoch(XMLGregorianCalendar calendar, boolean zone) {
if (zone) {
return calendar.normalize().toGregorianCalendar().getTimeInMillis();
}
return calendar.toGregorianCalendar(UTC_ZONE, java.util.Locale.ROOT, null).getTimeInMillis();
}


Expand Down Expand Up @@ -161,88 +192,76 @@ void check(IDatatype icod) throws CoreseDatatypeException {
}
}

public int compareTo(CoreseDate dt) throws CoreseDatatypeException {
if (this.hasFastEpoch && dt.hasFastEpoch && this.hasZone == dt.hasZone) {
if (this.epochMillis < dt.epochMillis) {
return -1;
} else if (this.epochMillis > dt.epochMillis) {
return 1;
}
}
return this.compare(this.getCalendar(), dt.getCalendar());
}

@Override
public IDatatype cast(String target) {
if (RDF.xsddate.equals(target) && getCode() == IDatatype.Datatype.DATE) {
return this;
}
return super.cast(target);
}

@Override
public int compare(IDatatype iod) throws CoreseDatatypeException {
switch (iod.getCode()) {
case DATE:
case DATETIME:
CoreseDate dt = (CoreseDate) iod;
XMLGregorianCalendar cal1 = this.getCalendar();
XMLGregorianCalendar cal2 = dt.getCalendar();
return this.compare(cal1, cal2);
if (iod.getCode() == IDatatype.Datatype.DATE || iod.getCode() == IDatatype.Datatype.DATETIME) {
return this.compareTo((CoreseDate) iod);
}
throw failure();
}

@Override
public boolean less(IDatatype iod) throws CoreseDatatypeException {
switch (iod.getCode()) {
case DATE:
case DATETIME:
this.check(iod);
CoreseDate dt = (CoreseDate) iod;
XMLGregorianCalendar cal1 = this.getCalendar();
XMLGregorianCalendar cal2 = dt.getCalendar();
return this.compare(cal1, cal2) < 0;
if (iod.getCode() == IDatatype.Datatype.DATE || iod.getCode() == IDatatype.Datatype.DATETIME) {
this.check(iod);
return this.compareTo((CoreseDate) iod) < 0;
}
throw failure();
}

@Override
public boolean lessOrEqual(IDatatype iod) throws CoreseDatatypeException {
switch (iod.getCode()) {
case DATE:
case DATETIME:
this.check(iod);
CoreseDate dt = (CoreseDate) iod;
XMLGregorianCalendar cal1 = this.getCalendar();
XMLGregorianCalendar cal2 = dt.getCalendar();
return this.compare(cal1, cal2) <= 0;
if (iod.getCode() == IDatatype.Datatype.DATE || iod.getCode() == IDatatype.Datatype.DATETIME) {
this.check(iod);
return this.compareTo((CoreseDate) iod) <= 0;
}
throw failure();
}

@Override
public boolean greater(IDatatype iod) throws CoreseDatatypeException {
switch (iod.getCode()) {
case DATE:
case DATETIME:
this.check(iod);
CoreseDate dt = (CoreseDate) iod;
XMLGregorianCalendar cal1 = this.getCalendar();
XMLGregorianCalendar cal2 = dt.getCalendar();
return this.compare(cal1, cal2) > 0;
if (iod.getCode() == IDatatype.Datatype.DATE || iod.getCode() == IDatatype.Datatype.DATETIME) {
this.check(iod);
return this.compareTo((CoreseDate) iod) > 0;
}
throw failure();
}

@Override
public boolean greaterOrEqual(IDatatype iod) throws CoreseDatatypeException {
switch (iod.getCode()) {
case DATE:
case DATETIME:
this.check(iod);
CoreseDate dt = (CoreseDate) iod;
XMLGregorianCalendar cal1 = this.getCalendar();
XMLGregorianCalendar cal2 = dt.getCalendar();
return this.compare(cal1, cal2) >= 0;
if (iod.getCode() == IDatatype.Datatype.DATE || iod.getCode() == IDatatype.Datatype.DATETIME) {
this.check(iod);
return this.compareTo((CoreseDate) iod) >= 0;
}
throw failure();
}

@Override
public boolean equalsWE(IDatatype iod) throws CoreseDatatypeException {
switch (iod.getCode()) {
case DATE:
case DATETIME:
CoreseDate dt = (CoreseDate) iod;
XMLGregorianCalendar cal1 = this.getCalendar();
XMLGregorianCalendar cal2 = dt.getCalendar();
return this.compare(cal1, cal2) == 0;

case URI:
case BLANK: case TRIPLE:
return false;
if (iod.getCode() == IDatatype.Datatype.DATE || iod.getCode() == IDatatype.Datatype.DATETIME) {
return this.compareTo((CoreseDate) iod) == 0;
}
if (iod.isURI() || iod.isBlank() || iod.isTriple()) {
return false;
}
throw failure();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,12 @@ public IDatatype.Datatype getCode() {
return code;
}

@Override
public IDatatype cast(String target) {
if (RDF.xsddateTime.equals(target)) {
return this;
}
return super.cast(target);
}

}
Loading
Loading