org.apache.jena.sparql.expr.nodevalue.NodeValueOps Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jena-arq Show documentation
Show all versions of jena-arq Show documentation
ARQ is a SPARQL 1.1 query engine for Apache Jena
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.sparql.expr.nodevalue;
import static javax.xml.datatype.DatatypeConstants.FIELD_UNDEFINED ;
import static org.apache.jena.datatypes.xsd.XSDDatatype.XSD ;
import static org.apache.jena.sparql.expr.ValueSpaceClassification.VSPACE_DATE ;
import static org.apache.jena.sparql.expr.ValueSpaceClassification.VSPACE_DATETIME ;
import static org.apache.jena.sparql.expr.ValueSpaceClassification.VSPACE_DURATION ;
import static org.apache.jena.sparql.expr.ValueSpaceClassification.VSPACE_NUM ;
import static org.apache.jena.sparql.expr.ValueSpaceClassification.VSPACE_STRING ;
import static org.apache.jena.sparql.expr.ValueSpaceClassification.VSPACE_TIME ;
import java.math.BigDecimal ;
import java.util.GregorianCalendar ;
import javax.xml.datatype.Duration ;
import javax.xml.datatype.XMLGregorianCalendar ;
import org.apache.jena.datatypes.xsd.XSDDatatype ;
import org.apache.jena.graph.Node ;
import org.apache.jena.sparql.expr.ExprEvalTypeException ;
import org.apache.jena.sparql.expr.NodeValue ;
import org.apache.jena.sparql.expr.ValueSpaceClassification ;
import org.apache.jena.sparql.util.NodeFactoryExtra ;
/** The code parts of arithmetic operations on {@link NodeValue}s.
*/
public class NodeValueOps
{
/* Date/time/duration coverage
* add, subtract (duration, duration)
*
(xsd:duration, xsd:duration) -> xsd:duration
10.6.1 op:add-yearMonthDurations
10.6.2 op:subtract-yearMonthDurations
(xsd:duration, xsd:duration) -> xsd:duration
10.6.6 op:add-dayTimeDurations
10.6.7 op:subtract-dayTimeDurations
** Java has general duration subtract
* Subtract two date/times
(xsd:dateTime, xsd:dateTime) -> xsd:duration
10.8.1 op:subtract-dateTimes -> xsd:dayTimeDuration
(xsd:date, xsd:date) -> xsd:duration
10.8.2 op:subtract-dates
(xsd:time, xsd:time) -> xsd:duration
10.8.3 op:subtract-times
* Date/time and duration
** Java has general duration subtract (error needed?)
(xsd:dateTime, xsd:duration) -> xsd:dateTime
10.8.4 op:add-yearMonthDuration-to-dateTime
10.8.5 op:add-dayTimeDuration-to-dateTime
10.8.6 op:subtract-yearMonthDuration-from-dateTime
10.8.7 op:subtract-dayTimeDuration-from-dateTime
(xsd:date, xsd:duration) -> xsd:date
10.8.8 op:add-yearMonthDuration-to-date
10.8.9 op:add-dayTimeDuration-to-date
10.8.10 op:subtract-yearMonthDuration-from-date
10.8.11 op:subtract-dayTimeDuration-from-date
(xsd:time, xsd:duration) -> xsd:time
10.8.12 op:add-dayTimeDuration-to-time
10.8.13 op:subtract-dayTimeDuration-from-time
(xsd:duration, xsd:double) -> xsd:duration
10.6.8 op:multiply-dayTimeDuration
*/
/* Missing:
(xsd:duration, xsd:double) -> xsd:duration
10.6.9 op:divide-dayTimeDuration
(xsd:duration, xsd:duration) -> xsd:decimal
10.6.10 op:divide-dayTimeDuration-by-dayTimeDuration
*/
/* Notes:
* Does not consider whether a duration is dayTime or yearMonth
* for addition and subtraction.
* As per the Java (Xerces) implementation, it just work on durations.
*/
// Until part of Jena datatypes.
private static final String dtXSDdateTimeStamp = XSD+"#dateTimeStamp" ;
private static final String dtXSDdayTimeDuration = XSD+"#dayTimeDuration" ;
private static final String dtXSDyearMonthDuration = XSD+"#yearMonthDuration" ;
public static NodeValue additionNV(NodeValue nv1, NodeValue nv2)
{
ValueSpaceClassification vs1 = nv1.getValueSpace() ;
ValueSpaceClassification vs2 = nv2.getValueSpace() ;
if ( vs1.equals(VSPACE_NUM) && vs2.equals(VSPACE_NUM) )
return XSDFuncOp.numAdd(nv1, nv2) ;
if ( vs1.equals(VSPACE_STRING) && vs2.equals(VSPACE_STRING) )
return NodeValue.makeString(nv1.asString()+nv2.asString()) ;
if ( vs1.equals(VSPACE_DURATION) && vs2.equals(VSPACE_DURATION) )
{
// A lot of testing to keep it as derived types.
boolean isDTDur = dtXSDdayTimeDuration.equals(nv1.getDatatypeURI()) &&
dtXSDdayTimeDuration.equals(nv2.getDatatypeURI()) ;
boolean isYMDur = dtXSDyearMonthDuration.equals(nv1.getDatatypeURI()) &&
dtXSDyearMonthDuration.equals(nv2.getDatatypeURI()) ;
Duration d3 = nv1.getDuration().add(nv2.getDuration()) ;
String lex = d3.toString() ;
Node n ;
if ( isDTDur )
n = NodeFactoryExtra.createLiteralNode(lex, null, dtXSDdayTimeDuration) ;
else if ( isYMDur )
n = NodeFactoryExtra.createLiteralNode(lex, null, dtXSDyearMonthDuration) ;
else
n = org.apache.jena.graph.NodeFactory.createLiteral(lex, XSDDatatype.XSDduration) ;
return NodeValue.makeNodeDuration(d3, n) ;
}
// Loose style. Add any duration to any date or time value.
if ( vs1.equals(VSPACE_DATETIME) && vs2.equals(VSPACE_DURATION) )
{
XMLGregorianCalendar cal = nv1.getDateTime() ;
XMLGregorianCalendar result = xsd_add(cal, nv2.getDuration()) ;
NodeValue r = NodeValue.makeDateTime(result) ;
return r ;
}
// Loose style. Add any duration to any date or time value.
if ( vs1.equals(VSPACE_DATE) && vs2.equals(VSPACE_DURATION) )
{
XMLGregorianCalendar cal = nv1.getDateTime() ;
XMLGregorianCalendar result = xsd_add(cal, nv2.getDuration()) ;
NodeValue r = NodeValue.makeDate(result) ;
return r ;
}
// Loose style. Add any duration to any date or time value.
if ( vs1.equals(VSPACE_TIME) && vs2.equals(VSPACE_DURATION) )
{
// ONLY dayTime.
XMLGregorianCalendar cal = nv1.getDateTime() ;
XMLGregorianCalendar result = xsd_add(cal, nv2.getDuration()) ;
NodeValue r = NodeValue.makeNode(result.toXMLFormat(), XSDDatatype.XSDtime) ;
return r ;
}
if ( isDT(vs2) && vs1.equals(VSPACE_DURATION) )
// Carefully ...
return additionNV(nv2, nv1) ;
throw new ExprEvalTypeException("Operator '+' : Undefined addition: "+nv1+" and "+nv2) ;
}
// NodeFunctions
public static NodeValue subtractionNV(NodeValue nv1, NodeValue nv2)
{
ValueSpaceClassification vs1 = nv1.getValueSpace() ;
ValueSpaceClassification vs2 = nv2.getValueSpace() ;
if ( vs1.equals(VSPACE_NUM) && vs2.equals(VSPACE_NUM) )
return XSDFuncOp.numSubtract(nv1, nv2) ;
if ( vs1.equals(VSPACE_DURATION) && vs2.equals(VSPACE_DURATION) )
{
// A lot of testing to keep it as derived types.
boolean isDTDur = dtXSDdayTimeDuration.equals(nv1.getDatatypeURI()) &&
dtXSDdayTimeDuration.equals(nv2.getDatatypeURI()) ;
boolean isYMDur = dtXSDyearMonthDuration.equals(nv1.getDatatypeURI()) &&
dtXSDyearMonthDuration.equals(nv2.getDatatypeURI()) ;
Duration d3 = nv1.getDuration().subtract(nv2.getDuration()) ;
String lex = d3.toString() ;
Node n ;
if ( isDTDur )
n = NodeFactoryExtra.createLiteralNode(lex, null, dtXSDdayTimeDuration) ;
else if ( isYMDur )
n = NodeFactoryExtra.createLiteralNode(lex, null, dtXSDyearMonthDuration) ;
else
n = org.apache.jena.graph.NodeFactory.createLiteral(lex, XSDDatatype.XSDduration) ;
return NodeValue.makeNodeDuration(d3, n) ;
}
if ( isDT(vs1) && isDT(vs2) )
{
XMLGregorianCalendar cal1 = nv1.getDateTime() ;
XMLGregorianCalendar cal2 = nv2.getDateTime() ;
boolean isDef1 = ( cal1.getTimezone() == FIELD_UNDEFINED ) ;
boolean isDef2 = ( cal2.getTimezone() == FIELD_UNDEFINED ) ;
if ( ( isDef1 && !isDef2 ) || ( !isDef1 && isDef2 ) )
throw new ExprEvalTypeException("Operator '-': can't substract timezone/non-timezone values") ;
// Inspect duration and force to better type? xsd:dayTimeDuration
return NodeValue.makeDuration(xsd_substract(cal1, cal2));
}
// Loose style. Subtract any duration to any date or time value.
if ( vs1.equals(VSPACE_DATETIME) && vs2.equals(VSPACE_DURATION) )
{
XMLGregorianCalendar cal = nv1.getDateTime() ;
// add-negation
XMLGregorianCalendar result = xsd_subtract(cal, nv2.getDuration()) ;
NodeValue r = NodeValue.makeDateTime(result) ;
return r ;
}
if ( vs1.equals(VSPACE_DATE) && vs2.equals(VSPACE_DURATION) )
{
XMLGregorianCalendar cal = nv1.getDateTime() ;
// add-negation
XMLGregorianCalendar result = xsd_subtract(cal, nv2.getDuration()) ;
NodeValue r = NodeValue.makeDate(result) ;
return r ;
}
if ( vs1.equals(VSPACE_TIME) && vs2.equals(VSPACE_DURATION) )
{
XMLGregorianCalendar cal = nv1.getDateTime() ;
// add-negation
XMLGregorianCalendar result = xsd_subtract(cal, nv2.getDuration()) ;
NodeValue r = NodeValue.makeNode(result.toXMLFormat(), XSDDatatype.XSDtime) ;
return r ;
}
throw new ExprEvalTypeException("Operator '-' : Undefined subtraction: "+nv1+" and "+nv2) ;
}
public static NodeValue multiplicationNV(NodeValue nv1, NodeValue nv2)
{
ValueSpaceClassification vs1 = nv1.getValueSpace() ;
ValueSpaceClassification vs2 = nv2.getValueSpace() ;
if ( vs1.equals(VSPACE_NUM) && vs2.equals(VSPACE_NUM) )
return XSDFuncOp.numMultiply(nv1, nv2) ;
if ( vs1.equals(VSPACE_DURATION) && vs2.equals(VSPACE_NUM) )
{
// ONLY defined for dayTime.
Duration dur = nv1.getDuration() ;
boolean valid = XSDFuncOp.isDayTime(dur) ;
if ( ! valid )
throw new ExprEvalTypeException("Operator '*': only dayTime duration. Got: "+nv1) ;
BigDecimal dec = nv2.getDecimal() ;
Duration r = dur.multiply(dec) ;
Node n = NodeFactoryExtra.createLiteralNode(r.toString(), null, dtXSDdayTimeDuration) ;
return NodeValue.makeNodeDuration(r, n) ;
}
throw new ExprEvalTypeException("Operator '*' : Undefined multiply: "+nv1+" and "+nv2) ;
}
public static NodeValue divisionNV(NodeValue nv1, NodeValue nv2)
{
ValueSpaceClassification vs1 = nv1.getValueSpace() ;
ValueSpaceClassification vs2 = nv2.getValueSpace() ;
if ( vs1.equals(VSPACE_NUM) && vs2.equals(VSPACE_NUM) )
return XSDFuncOp.numDivide(nv1, nv2) ;
throw new ExprEvalTypeException("Operator '/' : Undefined division: "+nv1+" and "+nv2) ;
}
private static boolean isDT(ValueSpaceClassification vs)
{
switch (vs)
{
case VSPACE_DATETIME:
case VSPACE_DATE:
case VSPACE_TIME:
case VSPACE_G_YEAR:
case VSPACE_G_YEARMONTH:
case VSPACE_G_MONTHDAY:
case VSPACE_G_MONTH:
case VSPACE_G_DAY:
return true ;
default:
return false ;
}
}
private static XMLGregorianCalendar xsd_add(XMLGregorianCalendar cal, Duration duration)
{
//if ( ! isYearMonth(duration) && ! isDayTime(duration) )
XMLGregorianCalendar result = (XMLGregorianCalendar)cal.clone() ;
result.add(duration) ;
return result ;
}
private static XMLGregorianCalendar xsd_subtract(XMLGregorianCalendar cal, Duration duration)
{
return xsd_add(cal, duration.negate()) ;
}
private static Duration xsd_substract(XMLGregorianCalendar cal1, XMLGregorianCalendar cal2)
{
GregorianCalendar gcal1 = cal1.toGregorianCalendar() ;
GregorianCalendar gcal2 = cal2.toGregorianCalendar() ;
long x1 = gcal1.getTimeInMillis() ;
long x2 = gcal2.getTimeInMillis() ;
return NodeValue.xmlDatatypeFactory.newDuration(x1-x2) ;
}
}