QueryValueEvaluationStepSupplier.java
/*******************************************************************************
* 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.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.values;
import java.util.function.Function;
import java.util.function.Predicate;
import org.eclipse.rdf4j.common.net.ParsedIRI;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Triple;
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.BooleanLiteral;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.algebra.IRIFunction;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep;
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.algebra.evaluation.util.QueryEvaluationUtility.Result;
import org.eclipse.rdf4j.query.impl.EmptyBindingSet;
public class QueryValueEvaluationStepSupplier {
private static Value bound(QueryValueEvaluationStep arg, BindingSet bindings)
throws ValueExprEvaluationException, QueryEvaluationException {
try {
Value argValue = arg.evaluate(bindings);
return BooleanLiteral.valueOf(argValue != null);
} catch (ValueExprEvaluationException e) {
return BooleanLiteral.FALSE;
}
}
private QueryValueEvaluationStepSupplier() {
}
public static QueryValueEvaluationStep prepareStr(QueryValueEvaluationStep arg, ValueFactory valueFactory) {
return make(arg, "Unknown constant argument for STR()", bs -> str(arg, valueFactory, bs));
}
private static Value str(QueryValueEvaluationStep arg, ValueFactory valueFactory, BindingSet bindings) {
Value argValue = arg.evaluate(bindings);
if (argValue instanceof IRI || argValue instanceof Triple) {
return valueFactory.createLiteral(argValue.toString());
} else if (argValue instanceof Literal) {
Literal literal = (Literal) argValue;
if (QueryEvaluationUtility.isSimpleLiteral(literal)) {
return literal;
} else {
return valueFactory.createLiteral(literal.getLabel());
}
} else {
throw new ValueExprEvaluationException("Unknown constant argument for STR()");
}
}
public static QueryValueEvaluationStep prepareBound(QueryValueEvaluationStep arg, QueryEvaluationContext context) {
return make(arg, "bound called on constant argument that throws", bs -> bound(arg, bs));
}
public static QueryValueEvaluationStep prepareDatatype(QueryValueEvaluationStep arg,
QueryEvaluationContext context) {
return make(arg, "datatype called on constant that throws", bs -> datatype(arg, bs));
}
public static QueryValueEvaluationStep prepareLabel(QueryValueEvaluationStep arg, ValueFactory vf) {
return make(arg, "label called on constant that throws", bs -> label(arg, bs, vf));
}
private static QueryValueEvaluationStep make(QueryValueEvaluationStep arg, String errorMessage,
Function<BindingSet, Value> function) {
if (arg.isConstant()) {
try {
Value datatype = function.apply(EmptyBindingSet.getInstance());
return new QueryValueEvaluationStep.ConstantQueryValueEvaluationStep(datatype);
} catch (ValueExprEvaluationException e) {
return new QueryValueEvaluationStep.Fail(errorMessage);
}
}
return new QueryValueEvaluationStep.ApplyFunctionForEachBinding(function);
}
private static Value label(QueryValueEvaluationStep arg, BindingSet bindings, ValueFactory vf) {
Value argValue = arg.evaluate(bindings);
if (argValue instanceof Literal) {
Literal literal = (Literal) argValue;
if (QueryEvaluationUtility.isSimpleLiteral(literal)) {
return literal;
} else {
return vf.createLiteral(literal.getLabel());
}
} else {
throw new ValueExprEvaluationException();
}
}
private static Value datatype(QueryValueEvaluationStep arg, BindingSet bindings) {
Value v = arg.evaluate(bindings);
if (v instanceof Literal) {
Literal literal = (Literal) v;
if (literal.getDatatype() != null) {
// literal with datatype
return literal.getDatatype();
} else if (literal.getLanguage().isPresent()) {
return CoreDatatype.RDF.LANGSTRING.getIri();
} else {
// simple literal
return CoreDatatype.XSD.STRING.getIri();
}
}
throw new ValueExprEvaluationException();
}
public static QueryValueEvaluationStep prepareVar(Var var, QueryEvaluationContext context) {
if (var.getValue() != null) {
return new ConstantQueryValueEvaluationStep(var.getValue());
} else {
Function<BindingSet, Value> getValue = context.getValue(var.getName());
return new QueryValueEvaluationStep.ApplyFunctionForEachBinding(bindings -> {
Value val = getValue.apply(bindings);
if (val == null) {
throw new ValueExprEvaluationException();
}
return val;
});
}
}
public static QueryValueEvaluationStep prepareNamespace(QueryValueEvaluationStep arg, ValueFactory vf) {
return make(arg, "namespace called on constant that throws", bs -> namespace(arg, bs, vf));
}
private static Value namespace(QueryValueEvaluationStep arg, BindingSet bindings, ValueFactory valueFactory) {
Value argValue = arg.evaluate(bindings);
if (argValue instanceof IRI) {
IRI uri = (IRI) argValue;
return valueFactory.createIRI(uri.getNamespace());
} else {
throw new ValueExprEvaluationException();
}
}
public static QueryValueEvaluationStep prepareLocalName(QueryValueEvaluationStep arg, ValueFactory vf) {
return make(arg, "localName called on constant that throws", bs -> localName(arg, bs, vf));
}
private static Value localName(QueryValueEvaluationStep arg, BindingSet bindings, ValueFactory valueFactory) {
Value argValue = arg.evaluate(bindings);
if (argValue instanceof IRI) {
IRI uri = (IRI) argValue;
return valueFactory.createLiteral(uri.getLocalName());
} else {
throw new ValueExprEvaluationException();
}
}
public static QueryValueEvaluationStep prepareLang(QueryValueEvaluationStep arg, ValueFactory vf) {
return make(arg, "lang called on constant that throws", bs -> lang(arg, bs, vf));
}
private static Value lang(QueryValueEvaluationStep arg, BindingSet bindings, ValueFactory valueFactory) {
Value argValue = arg.evaluate(bindings);
if (argValue instanceof Literal) {
Literal literal = (Literal) argValue;
return valueFactory.createLiteral(literal.getLanguage().orElse(""));
}
throw new ValueExprEvaluationException();
}
public static QueryValueEvaluationStep bnode(QueryValueEvaluationStep nodeVes, ValueFactory vf) {
try {
if (nodeVes.isConstant()) {
Value nodeId = nodeVes.evaluate(EmptyBindingSet.getInstance());
if (nodeId instanceof Literal) {
String nodeLabel = nodeId.stringValue();
return new QueryValueEvaluationStep.ApplyFunctionForEachBinding(
bs -> vf.createBNode(nodeLabel + bs.toString().hashCode()));
} else {
return new QueryValueEvaluationStep.Fail("BNODE function argument must be a literal");
}
} else {
return new QueryValueEvaluationStep.ApplyFunctionForEachBinding(
bs -> vf.createBNode(nodeVes.evaluate(bs).stringValue() + bs.toString().hashCode()));
}
} catch (ValueExprEvaluationException e) {
return new QueryValueEvaluationStep.Fail("BNODE function argument must be a literal");
}
}
public static QueryValueEvaluationStep prepareIs(QueryValueEvaluationStep arg, Predicate<Value> is) {
return make(arg, "isResource called on constant that throws", bs -> {
Value argValue = arg.evaluate(bs);
return BooleanLiteral.valueOf(is.test(argValue));
});
}
public static QueryValueEvaluationStep prepareAnd(QueryValueEvaluationStep leftStep,
QueryValueEvaluationStep rightStep) {
if (leftStep.isConstant()) {
Result constantLeftValue = QueryEvaluationUtility
.getEffectiveBooleanValue(leftStep.evaluate(EmptyBindingSet.getInstance()));
if (constantLeftValue == QueryEvaluationUtility.Result._false) {
return new QueryValueEvaluationStep.ConstantQueryValueEvaluationStep(BooleanLiteral.FALSE);
} else if (constantLeftValue == QueryEvaluationUtility.Result._true && rightStep.isConstant()) {
Result constantRightValue = QueryEvaluationUtility
.getEffectiveBooleanValue(rightStep.evaluate(EmptyBindingSet.getInstance()));
if (constantRightValue == QueryEvaluationUtility.Result._false) {
return new QueryValueEvaluationStep.ConstantQueryValueEvaluationStep(BooleanLiteral.FALSE);
} else if (constantRightValue == QueryEvaluationUtility.Result._true) {
return new QueryValueEvaluationStep.ConstantQueryValueEvaluationStep(BooleanLiteral.TRUE);
}
}
}
if (rightStep.isConstant()) {
Result constantRightValue = QueryEvaluationUtility
.getEffectiveBooleanValue(rightStep.evaluate(EmptyBindingSet.getInstance()));
if (constantRightValue == QueryEvaluationUtility.Result._false) {
return new QueryValueEvaluationStep.ConstantQueryValueEvaluationStep(BooleanLiteral.FALSE);
}
}
return new QueryValueEvaluationStep() {
@Override
public Value evaluate(BindingSet bindings) throws ValueExprEvaluationException, QueryEvaluationException {
try {
if (QueryEvaluationUtility.getEffectiveBooleanValue(
leftStep.evaluate(bindings)) == QueryEvaluationUtility.Result._false) {
// Left argument evaluates to false, we don't need to look any
// further
return BooleanLiteral.FALSE;
}
} catch (ValueExprEvaluationException e) {
// Failed to evaluate the left argument. Result is 'false' when
// the right argument evaluates to 'false', failure otherwise.
Value rightValue = rightStep.evaluate(bindings);
if (QueryEvaluationUtility
.getEffectiveBooleanValue(rightValue) == QueryEvaluationUtility.Result._false) {
return BooleanLiteral.FALSE;
} else {
throw new ValueExprEvaluationException();
}
}
// Left argument evaluated to 'true', result is determined
// by the evaluation of the right argument.
Value rightValue = rightStep.evaluate(bindings);
return BooleanLiteral.valueOf(QueryEvaluationUtil.getEffectiveBooleanValue(rightValue));
}
};
}
public static QueryValueEvaluationStep prepareIriFunction(IRIFunction node, QueryValueEvaluationStep arg,
ValueFactory valueFactory) {
return make(arg, "IRI() called on constant that throws",
bs -> iriFunction(node.getBaseURI(), arg.evaluate(bs), valueFactory));
}
private static IRI iriFunction(String baseURI, Value argValue, ValueFactory vf) {
if (argValue instanceof Literal) {
final Literal lit = (Literal) argValue;
String uriString = lit.getLabel();
try {
ParsedIRI iri = ParsedIRI.create(uriString);
if (!iri.isAbsolute() && baseURI != null) {
// uri string may be a relative reference.
uriString = ParsedIRI.create(baseURI).resolve(iri).toString();
} else if (!iri.isAbsolute()) {
throw new ValueExprEvaluationException("not an absolute IRI reference: " + uriString);
}
} catch (IllegalArgumentException e) {
throw new ValueExprEvaluationException("not a valid IRI reference: " + uriString);
}
IRI result;
try {
result = vf.createIRI(uriString);
} catch (IllegalArgumentException e) {
throw new ValueExprEvaluationException(e.getMessage());
}
return result;
} else if (argValue instanceof IRI) {
return (IRI) argValue;
}
throw new ValueExprEvaluationException();
}
}