001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.query.impl;
007:
008: import java.util.ArrayList;
009: import java.util.Arrays;
010: import java.util.Collection;
011: import java.util.LinkedHashSet;
012: import java.util.LinkedList;
013: import java.util.List;
014: import java.util.NoSuchElementException;
015: import java.util.Set;
016:
017: import info.aduna.iteration.Iteration;
018: import info.aduna.iteration.Iterations;
019:
020: import org.openrdf.query.BindingSet;
021: import org.openrdf.query.QueryEvaluationException;
022: import org.openrdf.query.TupleQueryResult;
023:
024: /**
025: * An implementation of the {@link TupleQueryResult} interface that stores the
026: * complete query result in memory. The query results in a
027: * MutableTupleQueryResult can be iterated over multiple times and can also be
028: * iterated over in reverse order.
029: *
030: * @author Arjohn Kampman
031: */
032: public class MutableTupleQueryResult implements TupleQueryResult,
033: Cloneable {
034:
035: /*-----------*
036: * Variables *
037: *-----------*/
038:
039: private Set<String> bindingNames = new LinkedHashSet<String>();
040:
041: private List<BindingSet> bindingSets = new LinkedList<BindingSet>();
042:
043: /**
044: * The index of the next element that will be returned by a call to
045: * {@link #next()}.
046: */
047: private int currentIndex = 0;
048:
049: /**
050: * The index of the last element that was returned by a call to
051: * {@link #next()} or {@link #previous()}. Equal to -1 if there is no such
052: * element.
053: */
054: private int lastReturned = -1;
055:
056: /*--------------*
057: * Constructors *
058: *--------------*/
059:
060: public <E extends Exception> MutableTupleQueryResult(
061: Collection<String> bindingNames, BindingSet... bindingSets)
062: throws E {
063: this (bindingNames, Arrays.asList(bindingSets));
064: }
065:
066: /**
067: * Creates a query result table with the supplied binding names.
068: * <em>The supplied list of binding names is assumed to be constant</em>;
069: * care should be taken that the contents of this list doesn't change after
070: * supplying it to this solution.
071: *
072: * @param bindingNames
073: * The binding names, in order of projection.
074: */
075: public MutableTupleQueryResult(Collection<String> bindingNames,
076: Collection<? extends BindingSet> bindingSets) {
077: this .bindingNames.addAll(bindingNames);
078: this .bindingSets.addAll(bindingSets);
079: }
080:
081: public <E extends Exception> MutableTupleQueryResult(
082: Collection<String> bindingNames,
083: Iteration<? extends BindingSet, E> bindingSetIter) throws E {
084: this .bindingNames.addAll(bindingNames);
085: Iterations.addAll(bindingSetIter, this .bindingSets);
086: }
087:
088: public MutableTupleQueryResult(TupleQueryResult tqr)
089: throws QueryEvaluationException {
090: this (tqr.getBindingNames(), tqr);
091: }
092:
093: /*---------*
094: * Methods *
095: *---------*/
096:
097: public List<String> getBindingNames() {
098: return new ArrayList<String>(bindingNames);
099: }
100:
101: public int size() {
102: return bindingSets.size();
103: }
104:
105: public BindingSet get(int index) {
106: return bindingSets.get(index);
107: }
108:
109: public int getIndex() {
110: return currentIndex;
111: }
112:
113: public void setIndex(int index) {
114: if (index < 0 || index > bindingSets.size() + 1) {
115: throw new IllegalArgumentException("Index out of range: "
116: + index);
117: }
118:
119: this .currentIndex = index;
120: }
121:
122: public boolean hasNext() {
123: return currentIndex < bindingSets.size();
124: }
125:
126: public BindingSet next() {
127: if (hasNext()) {
128: BindingSet result = get(currentIndex);
129: lastReturned = currentIndex;
130: currentIndex++;
131: return result;
132: }
133:
134: throw new NoSuchElementException();
135: }
136:
137: public boolean hasPrevious() {
138: return currentIndex > 0;
139: }
140:
141: public BindingSet previous() {
142: if (hasPrevious()) {
143: BindingSet result = bindingSets.get(currentIndex - 1);
144: currentIndex--;
145: lastReturned = currentIndex;
146: return result;
147: }
148:
149: throw new NoSuchElementException();
150: }
151:
152: /**
153: * Moves the cursor to the start of the query result, just before the first
154: * binding set. After calling this method, the result can be iterated over
155: * from scratch.
156: */
157: public void beforeFirst() {
158: currentIndex = 0;
159: }
160:
161: /**
162: * Moves the cursor to the end of the query result, just after the last
163: * binding set.
164: */
165: public void afterLast() {
166: currentIndex = bindingSets.size() + 1;
167: }
168:
169: /**
170: * Inserts the specified binding set into the list. The binding set is
171: * inserted immediately before the next element that would be returned by
172: * {@link #next()}, if any, and after the next element that would be
173: * returned by {@link #previous}, if any. (If the table contains no binding
174: * sets, the new element becomes the sole element on the table.) The new
175: * element is inserted before the implicit cursor: a subsequent call to
176: * <tt>next()</tt> would be unaffected, and a subsequent call to
177: * <tt>previous()</tt> would return the new binding set.
178: *
179: * @param bindingSet
180: * The binding set to insert.
181: */
182: public void insert(BindingSet bindingSet) {
183: insert(currentIndex, bindingSet);
184: }
185:
186: public void insert(int index, BindingSet bindingSet) {
187: bindingSets.add(index, bindingSet);
188:
189: if (currentIndex > index) {
190: currentIndex++;
191: }
192:
193: lastReturned = -1;
194: }
195:
196: public void append(BindingSet bindingSet) {
197: bindingSets.add(bindingSet);
198: lastReturned = -1;
199: }
200:
201: public void set(BindingSet bindingSet) {
202: if (lastReturned == -1) {
203: throw new IllegalStateException();
204: }
205:
206: set(lastReturned, bindingSet);
207: }
208:
209: public BindingSet set(int index, BindingSet bindingSet) {
210: return bindingSets.set(index, bindingSet);
211: }
212:
213: public void remove() {
214: if (lastReturned == -1) {
215: throw new IllegalStateException();
216: }
217:
218: remove(lastReturned);
219:
220: if (currentIndex > lastReturned) {
221: currentIndex--;
222: }
223:
224: lastReturned = -1;
225: }
226:
227: public BindingSet remove(int index) {
228: BindingSet result = bindingSets.remove(index);
229:
230: if (currentIndex > index) {
231: currentIndex--;
232: }
233:
234: lastReturned = -1;
235:
236: return result;
237: }
238:
239: public void clear() {
240: bindingNames.clear();
241: bindingSets.clear();
242: currentIndex = 0;
243: lastReturned = -1;
244: }
245:
246: public void close() {
247: // no-opp
248: }
249:
250: @Override
251: public MutableTupleQueryResult clone()
252: throws CloneNotSupportedException {
253: MutableTupleQueryResult clone = (MutableTupleQueryResult) super
254: .clone();
255: clone.bindingNames = new LinkedHashSet<String>(bindingNames);
256: clone.bindingSets = new LinkedList<BindingSet>(bindingSets);
257: return clone;
258: }
259: }
|