/*
* Licensed to Aduna under one or more contributor license agreements.
* See the NOTICE.txt file distributed with this work for additional
* information regarding copyright ownership.
*
* Aduna licenses this file to you under the terms of the Aduna BSD
* License (the "License"); you may not use this file except in compliance
* with the License. See the LICENSE.txt file distributed with this work
* for the full License.
*
* 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 com.bigdata.rdf.sparql.ast.eval;
import info.aduna.iteration.Iteration;
import info.aduna.iteration.Iterations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.TupleQueryResult;
/**
* An implementation of the {@link TupleQueryResult} interface that stores the
* complete query result in memory. The query results in a
* MutableTupleQueryResult can be iterated over multiple times and can also be
* iterated over in reverse order.
*
* @author Arjohn Kampman
*/
public class MutableTupleQueryResult implements TupleQueryResult, Cloneable {
/*-----------*
* Variables *
*-----------*/
private Set<String> bindingNames = new LinkedHashSet<String>();
private List<BindingSet> bindingSets = new ArrayList<BindingSet>();
/**
* The index of the next element that will be returned by a call to
* {@link #next()}.
*/
private int currentIndex = 0;
/**
* The index of the last element that was returned by a call to
* {@link #next()} or {@link #previous()}. Equal to -1 if there is no such
* element.
*/
private int lastReturned = -1;
/*--------------*
* Constructors *
*--------------*/
public <E extends Exception> MutableTupleQueryResult(Collection<String> bindingNames,
BindingSet... bindingSets)
{
this(bindingNames, Arrays.asList(bindingSets));
}
/**
* Creates a query result table with the supplied binding names.
* <em>The supplied list of binding names is assumed to be constant</em>;
* care should be taken that the contents of this list doesn't change after
* supplying it to this solution.
*
* @param bindingNames
* The binding names, in order of projection.
*/
public MutableTupleQueryResult(Collection<String> bindingNames,
Collection<? extends BindingSet> bindingSets)
{
this.bindingNames.addAll(bindingNames);
this.bindingSets.addAll(bindingSets);
}
public <E extends Exception> MutableTupleQueryResult(Collection<String> bindingNames,
Iteration<? extends BindingSet, E> bindingSetIter)
throws E
{
this.bindingNames.addAll(bindingNames);
Iterations.addAll(bindingSetIter, this.bindingSets);
}
public MutableTupleQueryResult(TupleQueryResult tqr)
throws QueryEvaluationException
{
this(tqr.getBindingNames(), tqr);
}
/*---------*
* Methods *
*---------*/
public List<String> getBindingNames() {
return new ArrayList<String>(bindingNames);
}
public int size() {
return bindingSets.size();
}
public BindingSet get(int index) {
return bindingSets.get(index);
}
public int getIndex() {
return currentIndex;
}
public void setIndex(int index) {
if (index < 0 || index > bindingSets.size() + 1) {
throw new IllegalArgumentException("Index out of range: " + index);
}
this.currentIndex = index;
}
public boolean hasNext() {
return currentIndex < bindingSets.size();
}
public BindingSet next() {
if (hasNext()) {
BindingSet result = get(currentIndex);
lastReturned = currentIndex;
currentIndex++;
return result;
}
throw new NoSuchElementException();
}
public boolean hasPrevious() {
return currentIndex > 0;
}
public BindingSet previous() {
if (hasPrevious()) {
BindingSet result = bindingSets.get(currentIndex - 1);
currentIndex--;
lastReturned = currentIndex;
return result;
}
throw new NoSuchElementException();
}
/**
* Moves the cursor to the start of the query result, just before the first
* binding set. After calling this method, the result can be iterated over
* from scratch.
*/
public void beforeFirst() {
currentIndex = 0;
}
/**
* Moves the cursor to the end of the query result, just after the last
* binding set.
*/
public void afterLast() {
currentIndex = bindingSets.size() + 1;
}
/**
* Inserts the specified binding set into the list. The binding set is
* inserted immediately before the next element that would be returned by
* {@link #next()}, if any, and after the next element that would be
* returned by {@link #previous}, if any. (If the table contains no binding
* sets, the new element becomes the sole element on the table.) The new
* element is inserted before the implicit cursor: a subsequent call to
* <tt>next()</tt> would be unaffected, and a subsequent call to
* <tt>previous()</tt> would return the new binding set.
*
* @param bindingSet
* The binding set to insert.
*/
public void insert(BindingSet bindingSet) {
insert(currentIndex, bindingSet);
}
public void insert(int index, BindingSet bindingSet) {
bindingSets.add(index, bindingSet);
if (currentIndex > index) {
currentIndex++;
}
lastReturned = -1;
}
public void append(BindingSet bindingSet) {
bindingSets.add(bindingSet);
lastReturned = -1;
}
public void set(BindingSet bindingSet) {
if (lastReturned == -1) {
throw new IllegalStateException();
}
set(lastReturned, bindingSet);
}
public BindingSet set(int index, BindingSet bindingSet) {
return bindingSets.set(index, bindingSet);
}
public void remove() {
if (lastReturned == -1) {
throw new IllegalStateException();
}
remove(lastReturned);
if (currentIndex > lastReturned) {
currentIndex--;
}
lastReturned = -1;
}
public BindingSet remove(int index) {
BindingSet result = bindingSets.remove(index);
if (currentIndex > index) {
currentIndex--;
}
lastReturned = -1;
return result;
}
public void clear() {
bindingNames.clear();
bindingSets.clear();
currentIndex = 0;
lastReturned = -1;
}
public void close() {
// no-opp
}
@Override
public MutableTupleQueryResult clone()
throws CloneNotSupportedException
{
MutableTupleQueryResult clone = (MutableTupleQueryResult)super.clone();
clone.bindingNames = new LinkedHashSet<String>(bindingNames);
clone.bindingSets = new LinkedList<BindingSet>(bindingSets);
return clone;
}
}