ExtensionIterator.java
/*******************************************************************************
* Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
*
* 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.iterator;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.ConvertingIteration;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.MutableBindingSet;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.algebra.AggregateOperator;
import org.eclipse.rdf4j.query.algebra.Extension;
import org.eclipse.rdf4j.query.algebra.ExtensionElem;
import org.eclipse.rdf4j.query.algebra.ValueExpr;
import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy;
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;
public class ExtensionIterator extends ConvertingIteration<BindingSet, BindingSet> {
private final Consumer<MutableBindingSet> setter;
private final QueryEvaluationContext context;
public ExtensionIterator(Extension extension, CloseableIteration<BindingSet> iter,
EvaluationStrategy strategy, QueryEvaluationContext context) throws QueryEvaluationException {
super(iter);
this.context = context;
this.setter = buildLambdaToEvaluateTheExpressions(extension, strategy, context);
}
public ExtensionIterator(CloseableIteration<BindingSet> iter,
Consumer<MutableBindingSet> setter, QueryEvaluationContext context) throws QueryEvaluationException {
super(iter);
this.setter = setter;
this.context = context;
}
public static Consumer<MutableBindingSet> buildLambdaToEvaluateTheExpressions(Extension extension,
EvaluationStrategy strategy, QueryEvaluationContext context) {
Consumer<MutableBindingSet> consumer = null;
for (ExtensionElem extElem : extension.getElements()) {
ValueExpr expr = extElem.getExpr();
if (!(expr instanceof AggregateOperator)) {
QueryValueEvaluationStep prepared = strategy.precompile(extElem.getExpr(), context);
BiConsumer<Value, MutableBindingSet> setBinding = context.setBinding(extElem.getName());
consumer = andThen(consumer, targetBindings -> setValue(setBinding, prepared, targetBindings));
}
}
if (consumer == null) {
return bs -> {
};
}
return consumer;
}
private static void setValue(BiConsumer<Value, MutableBindingSet> setBinding, QueryValueEvaluationStep prepared,
MutableBindingSet targetBindings) {
try {
// we evaluate each extension element over the targetbindings, so that bindings from
// a previous extension element in this same extension can be used by other extension elements.
// e.g. if a projection contains (?a + ?b as ?c) (?c * 2 as ?d)
Value targetValue = prepared.evaluate(targetBindings);
if (targetValue != null) {
// Potentially overwrites bindings from super
setBinding.accept(targetValue, targetBindings);
}
} catch (ValueExprEvaluationException e) {
// silently ignore type errors in extension arguments. They should not cause the
// query to fail but result in no bindings for this solution
// see https://www.w3.org/TR/sparql11-query/#assignment
// use null as place holder for unbound variables that must remain so
setBinding.accept(null, targetBindings);
}
}
private static Consumer<MutableBindingSet> andThen(Consumer<MutableBindingSet> consumer,
Consumer<MutableBindingSet> next) {
if (consumer == null) {
return next;
} else {
return consumer.andThen(next);
}
}
@Override
public BindingSet convert(BindingSet sourceBindings) throws QueryEvaluationException {
MutableBindingSet targetBindings = context.createBindingSet(sourceBindings);
setter.accept(targetBindings);
return targetBindings;
}
}