001: /**
002: * $Revision: $
003: * $Date: $
004: *
005: * Copyright (C) 2007 Jive Software. All rights reserved.
006: *
007: * This software is published under the terms of the GNU Public License (GPL),
008: * a copy of which is included in this distribution.
009: */package org.jivesoftware.openfire.resultsetmanager;
010:
011: import java.util.*;
012:
013: /**
014: * A result set representation as described in XEP-0059. Note that this result
015: * 'set' actually makes use of a List implementations, as the Java Set
016: * definition disallows duplicate elements, while the List definition supplies
017: * most of the required indexing operations.
018: *
019: * This ResultSet implementation loads all all results from the set into memory,
020: * which might be undesirable for very large sets, or for sets where the
021: * retrieval of a result is an expensive operation. sets.
022: *
023: * As most methods are backed by the {@link List#subList(int, int)} method,
024: * non-structural changes in the returned lists are reflected in the ResultSet,
025: * and vice-versa.
026: *
027: * @author Guus der Kinderen, guus@nimbuzz.com
028: *
029: * @param <E>
030: * Each result set should be a collection of instances of the exact
031: * same class. This class must implement the {@link Result}
032: * interface.
033: * @see java.util.List#subList(int, int)
034: *
035: */
036: /*
037: * TODO: do we want changes to the returned Lists of methods in this class be
038: * applied to the content of the ResultSet itself? Currently, because of the
039: * usage of java.util.List#subList(int, int), it does. I'm thinking a
040: * immodifiable solution would cause less problems. -Guus
041: */
042: public class ResultSetImpl<E extends Result> extends ResultSet<E> {
043:
044: /**
045: * A list of all results in this ResultSet
046: */
047: public final List<E> resultList;
048:
049: /**
050: * A mapping of the UIDs of all results in resultList, to the index of those
051: * entries in that list.
052: */
053: public final Map<String, Integer> uidToIndex;
054:
055: /**
056: * Creates a new Result Set instance, based on a collection of Result
057: * implementing objects. The collection should contain elements of the exact
058: * same class only, and cannot contain 'null' elements.
059: *
060: * The order that's being used in the new ResultSet instance is the same
061: * order in which {@link Collection#iterator()} iterates over the
062: * collection.
063: *
064: * Note that this constructor throws an IllegalArgumentException if the
065: * Collection that is provided contains Results that have duplicate UIDs.
066: *
067: * @param results
068: * The collection of Results that make up this result set.
069: */
070: public ResultSetImpl(Collection<E> results) {
071: this (results, null);
072: }
073:
074: /**
075: * Creates a new Result Set instance, based on a collection of Result
076: * implementing objects. The collection should contain elements of the exact
077: * same class only, and cannot contain 'null' elements.
078: *
079: * The order that's being used in the new ResultSet instance is defined by
080: * the supplied Comparator class.
081: *
082: * Note that this constructor throws an IllegalArgumentException if the
083: * Collection that is provided contains Results that have duplicate UIDs.
084: *
085: * @param results
086: * The collection of Results that make up this result set.
087: * @param comparator
088: * The Comparator that defines the order of the Results in this
089: * result set.
090: */
091: public ResultSetImpl(Collection<E> results, Comparator<E> comparator) {
092: if (results == null) {
093: throw new NullPointerException(
094: "Argument 'results' cannot be null.");
095: }
096:
097: final int size = results.size();
098: resultList = new ArrayList<E>(size);
099: uidToIndex = new Hashtable<String, Integer>(size);
100:
101: // sort the collection, if need be.
102: List<E> sortedResults = null;
103: if (comparator != null) {
104: sortedResults = new ArrayList<E>(results);
105: Collections.sort(sortedResults, comparator);
106: }
107:
108: int index = 0;
109: // iterate over either the sorted or unsorted collection
110: for (final E result : (sortedResults != null ? sortedResults
111: : results)) {
112: if (result == null) {
113: throw new NullPointerException(
114: "The result set must not contain 'null' elements.");
115: }
116:
117: final String uid = result.getUID();
118: if (uidToIndex.containsKey(uid)) {
119: throw new IllegalArgumentException(
120: "The result set can not contain elements that have the same UID.");
121: }
122:
123: resultList.add(result);
124: uidToIndex.put(uid, index);
125: index++;
126: }
127: }
128:
129: /*
130: * (non-Javadoc)
131: *
132: * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#size()
133: */
134: @Override
135: public int size() {
136: return resultList.size();
137: }
138:
139: /*
140: * (non-Javadoc)
141: *
142: * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getAfter(E, int)
143: */
144: @Override
145: public List<E> getAfter(String uid, int maxAmount) {
146: if (uid == null || uid.length() == 0) {
147: throw new NullPointerException(
148: "Argument 'uid' cannot be null or an empty String.");
149: }
150:
151: if (maxAmount < 1) {
152: throw new IllegalArgumentException(
153: "Argument 'maxAmount' must be a integer higher than zero.");
154: }
155:
156: // the result of this method is exclusive 'result'
157: final int index = uidToIndex.get(uid) + 1;
158:
159: return get(index, maxAmount);
160: }
161:
162: /*
163: * (non-Javadoc)
164: *
165: * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getBefore(E, int)
166: */
167: @Override
168: public List<E> getBefore(String uid, int maxAmount) {
169: if (uid == null || uid.length() == 0) {
170: throw new NullPointerException(
171: "Argument 'uid' cannot be null or an empty String.");
172: }
173:
174: if (maxAmount < 1) {
175: throw new IllegalArgumentException(
176: "Argument 'maxAmount' must be a integer higher than zero.");
177: }
178:
179: // the result of this method is exclusive 'result'
180: final int indexOfLastElement = uidToIndex.get(uid);
181: final int indexOfFirstElement = indexOfLastElement - maxAmount;
182:
183: if (indexOfFirstElement < 0) {
184: return get(0, indexOfLastElement);
185: }
186:
187: return get(indexOfFirstElement, maxAmount);
188: }
189:
190: /*
191: * (non-Javadoc)
192: *
193: * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#get(int)
194: */
195: @Override
196: public E get(int index) {
197: return resultList.get(index);
198: }
199:
200: /*
201: * (non-Javadoc)
202: *
203: * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getFirst(int)
204: */
205: @Override
206: public List<E> getFirst(int maxAmount) {
207: if (maxAmount < 1) {
208: throw new IllegalArgumentException(
209: "Argument 'maxAmount' must be a integer higher than zero.");
210: }
211:
212: return get(0, maxAmount);
213: }
214:
215: /*
216: * (non-Javadoc)
217: *
218: * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getLast(int)
219: */
220: @Override
221: public List<E> getLast(int maxAmount) {
222: if (maxAmount < 1) {
223: throw new IllegalArgumentException(
224: "Argument 'maxAmount' must be a integer higher than zero.");
225: }
226:
227: final int indexOfFirstElement = size() - maxAmount;
228:
229: if (indexOfFirstElement < 0) {
230: return get(0, maxAmount);
231: }
232:
233: return get(indexOfFirstElement, maxAmount);
234: }
235:
236: /*
237: * (non-Javadoc)
238: *
239: * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#get(int, int)
240: */
241: @Override
242: public List<E> get(int fromIndex, int maxAmount) {
243: if (fromIndex < 0) {
244: throw new IllegalArgumentException(
245: "Argument 'fromIndex' must be zero or higher.");
246: }
247:
248: if (maxAmount < 1) {
249: throw new IllegalArgumentException(
250: "Argument 'maxAmount' must be a integer higher than zero.");
251: }
252:
253: if (fromIndex >= size()) {
254: return new ArrayList<E>(0);
255: }
256:
257: // calculate the last index to return, or return up to the end of last
258: // index if 'amount' surpasses the list length.
259: final int absoluteTo = fromIndex + maxAmount;
260: final int toIndex = (absoluteTo > size() ? size() : absoluteTo);
261:
262: return resultList.subList(fromIndex, toIndex);
263: }
264:
265: /*
266: * (non-Javadoc)
267: * @see org.jivesoftware.util.resultsetmanager.ResultSet#indexOf(java.lang.String)
268: */
269: @Override
270: public int indexOf(String uid) {
271: return uidToIndex.get(uid);
272: }
273: }
|