BindingSetAssignmentQueryEvaluationStep.java

/*******************************************************************************
 * 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.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *******************************************************************************/
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps;

import java.util.Iterator;
import java.util.function.Function;

import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration;
import org.eclipse.rdf4j.common.iteration.LookAheadIteration;
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.BindingSetAssignment;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext;

public class BindingSetAssignmentQueryEvaluationStep implements QueryEvaluationStep {
	private final BindingSetAssignment node;
	private final Function<BindingSet, MutableBindingSet> bsMaker;

	public BindingSetAssignmentQueryEvaluationStep(BindingSetAssignment node, QueryEvaluationContext context) {
		this.node = node;
		bsMaker = context::createBindingSet;
	}

	@Override
	public CloseableIteration<BindingSet> evaluate(BindingSet bindings) {
		final Iterator<BindingSet> assignments = node.getBindingSets().iterator();
		if (bindings.isEmpty()) {
			// we can just return the assignments directly without checking existing bindings
			return new CloseableIteratorIteration<>(assignments);
		}

		// we need to verify that new binding assignments do not overwrite existing bindings
		CloseableIteration<BindingSet> result;

		result = new LookAheadIteration<>() {

			@Override
			protected BindingSet getNextElement() throws QueryEvaluationException {
				MutableBindingSet nextResult = null;
				while (nextResult == null && assignments.hasNext()) {
					final BindingSet assignedBindings = assignments.next();

					for (String name : assignedBindings.getBindingNames()) {
						if (nextResult == null) {
							nextResult = bsMaker.apply(bindings);
						}

						final Value assignedValue = assignedBindings.getValue(name);
						if (assignedValue != null) {
							// check that the binding assignment does not overwrite existing bindings.
							Value existingValue = bindings.getValue(name);
							if (existingValue == null || assignedValue.equals(existingValue)) {
								if (existingValue == null) {
									// we are not overwriting an existing binding.
									nextResult.addBinding(name, assignedValue);
								}
							} else {
								// if values are not equal there is no compatible merge and we should return no next
								// element.
								nextResult = null;
								break;
							}
						}
					}
				}
				return nextResult;
			}

			@Override
			protected void handleClose() {

			}
		};

		return result;
	}
}