com.amazonaws.services.dynamodb.datamodeling.PaginatedList Maven / Gradle / Ivy
Show all versions of aws-java-sdk Show documentation
/*
* Copyright 2011-2014 Amazon Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://aws.amazon.com/apache2.0
*
* This file 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.amazonaws.services.dynamodb.datamodeling;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import com.amazonaws.services.dynamodb.AmazonDynamoDB;
/**
* Unmodifiable list supporting paginated result sets from Amazon DynamoDB.
*
* Pages of results are fetched lazily from DynamoDB as they are needed. Some
* methods, such as {@link PaginatedList#size()} and
* {@link PaginatedList#toArray()}, require fetching the entire result set
* eagerly. See the javadoc of individual methods for details on which are lazy.
*
* @param
* The domain object type stored in this list.
*
* @deprecated Use {@link com.amazonaws.services.dynamodbv2.datamodeling.PaginatedList} instead.
*/
@Deprecated
public abstract class PaginatedList implements List {
private static final String UNMODIFIABLE_MESSAGE = "This is an unmodifiable list";
/**
* Reference to the DynamoDB mapper for marshalling DynamoDB attributes back
* into objects
*/
protected final DynamoDBMapper mapper;
/**
* The class annotated with DynamoDB tags declaring how to load/store
* objects into DynamoDB
*/
protected final Class clazz;
/** The client for working with DynamoDB */
protected final AmazonDynamoDB dynamo;
/** Tracks if all results have been loaded yet or not */
protected boolean allResultsLoaded = false;
/** All currently loaded results for this list */
protected final List allResults;
/** Lazily loaded next results waiting to be added into allResults */
protected final List nextResults = new LinkedList();
public PaginatedList(DynamoDBMapper mapper, Class clazz, AmazonDynamoDB dynamo) {
this.mapper = mapper;
this.clazz = clazz;
this.dynamo = dynamo;
this.allResults = new ArrayList();
}
/**
* Eagerly loads all results for this list.
*/
public synchronized void loadAllResults() {
if ( allResultsLoaded )
return;
while ( nextResultsAvailable() ) {
moveNextResults();
}
allResultsLoaded = true;
}
/**
* Returns whether there are more results available not yet included in the
* allResults member field. These could already have been fetched and are
* sitting in the nextResults buffer, or they could be fetched from the
* service opportunistically at the time this method is called. A return
* value of true guarantees that nextResults is non-empty.
*/
private boolean nextResultsAvailable() {
return !nextResults.isEmpty() || loadNextResults();
}
/**
* Attempts to load the next batch of results, if there are any, into the
* nextResults buffer. Returns whether there were any results to load. A
* return value of true guarantees that nextResults had items added to it.
*/
private synchronized boolean loadNextResults() {
if ( atEndOfResults() )
return false;
do {
nextResults.addAll(fetchNextPage());
} while ( !atEndOfResults() && nextResults.isEmpty() );
return !nextResults.isEmpty();
}
/**
* Moves the contents of the nextResults buffer into allResults and resets
* the buffer.
*/
private void moveNextResults() {
allResults.addAll(nextResults);
nextResults.clear();
}
/**
* Fetches the next page of results (which may be empty) and returns any
* items found.
*/
protected abstract List fetchNextPage();
/**
* Returns whether we have reached the end of the result set.
*/
protected abstract boolean atEndOfResults();
/**
* Returns an iterator over this list that lazily initializes results as
* necessary.
*/
@Override
public Iterator iterator() {
/*
* We make a copy of the allResults list to iterate over in order to
* avoid ConcurrentModificationExceptions caused by other methods
* loading more results into the list while someone iterates over it.
* This is a more severe problem than it might seem, because even
* innocuous-seeming operations such as contains() can modify the
* underlying result set.
*/
final List allResultsCopy = new ArrayList();
allResultsCopy.addAll(allResults);
final Iterator iter = allResultsCopy.iterator();
return new Iterator() {
Iterator iterator = iter;
int pos = 0;
@Override
public boolean hasNext() {
return iterator.hasNext() || nextResultsAvailable();
}
@Override
public T next() {
if ( !iterator.hasNext() ) {
/*
* Our private copy of the result list may be out of date
* with the full one. If it is, we just need to update our
* private copy to get more results. If it's not, we need to
* fetch more results from the service.
*/
if ( allResults.size() == allResultsCopy.size() ) {
if ( !nextResultsAvailable() ) {
throw new NoSuchElementException();
}
moveNextResults();
}
if ( allResults.size() > allResultsCopy.size() )
allResultsCopy.addAll(allResults.subList(allResultsCopy.size(), allResults.size()));
iterator = allResultsCopy.listIterator(pos);
}
pos++;
return iterator.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
};
}
/**
* Returns whether the collection is empty. At most one (non-empty) page of
* results is loaded to make the check.
*/
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
/**
* Returns the Nth element of the list. Results are loaded until N elements
* are present, if necessary.
*/
@Override
public T get(int n) {
while ( allResults.size() <= n && nextResultsAvailable() ) {
moveNextResults();
}
return allResults.get(n);
}
/**
* Returns whether the collection contains the given element. Results are
* loaded and checked incrementally until a match is found or the end of the
* result set is reached.
*/
@Override
public boolean contains(Object arg0) {
if ( allResults.contains(arg0) )
return true;
while ( nextResultsAvailable() ) {
boolean found = nextResults.contains(arg0);
moveNextResults();
if ( found )
return true;
}
return false;
}
/**
* Returns a sub-list in the range specified, loading more results as
* necessary.
*/
@Override
public List subList(int arg0, int arg1) {
while ( allResults.size() < arg1 && nextResultsAvailable() ) {
moveNextResults();
}
return Collections.unmodifiableList(allResults.subList(arg0, arg1));
}
/**
* Returns the first index of the object given in the list. Additional
* results are loaded incrementally as necessary.
*/
@Override
public int indexOf(Object arg0) {
int indexOf = allResults.indexOf(arg0);
if ( indexOf >= 0 )
return indexOf;
while ( nextResultsAvailable() ) {
indexOf = nextResults.indexOf(arg0);
int size = allResults.size();
moveNextResults();
if ( indexOf >= 0 )
return indexOf + size;
}
return -1;
}
// Operations requiring the entire result set
@Override
public int size() {
loadAllResults();
return allResults.size();
}
@Override
public boolean containsAll(Collection> arg0) {
loadAllResults();
return allResults.containsAll(arg0);
}
@Override
public int lastIndexOf(Object arg0) {
loadAllResults();
return allResults.lastIndexOf(arg0);
}
@Override
public Object[] toArray() {
loadAllResults();
return allResults.toArray();
}
@Override
public X[] toArray(X[] a) {
loadAllResults();
return allResults.toArray(a);
}
// Unsupported Operations
@Override
public ListIterator listIterator() {
throw new UnsupportedOperationException("ListIterators are not supported for this list");
}
@Override
public ListIterator listIterator(int arg0) {
throw new UnsupportedOperationException("ListIterators are not supported for this list");
}
@Override
public boolean remove(Object arg0) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public T remove(int arg0) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public boolean removeAll(Collection> arg0) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public boolean retainAll(Collection> arg0) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public T set(int arg0, T arg1) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public boolean add(T arg0) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public void add(int arg0, T arg1) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public boolean addAll(Collection extends T> arg0) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public boolean addAll(int arg0, Collection extends T> arg1) {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
@Override
public void clear() {
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}
}