/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* ***
*
* Community License: GPL 3.0
*
* This file is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ***
*
* Available Commercial License: GraniteDS SLA 1.0
*
* This is the appropriate option if you are creating proprietary
* applications and you are not prepared to distribute and share the
* source code of your application under the GPL v3 license.
*
* Please visit http://www.granitedataservices.com/license for more
* details.
*/
package org.granite.client.tide.collection;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import javax.annotation.PreDestroy;
import org.granite.client.tide.Context;
import org.granite.client.tide.ContextAware;
import org.granite.client.tide.Initializable;
import org.granite.client.tide.NameAware;
import org.granite.client.tide.data.EntityManager.UpdateKind;
import org.granite.client.tide.events.TideEvent;
import org.granite.client.tide.events.TideEventObserver;
import org.granite.client.tide.impl.ComponentImpl;
import org.granite.client.tide.server.Component;
import org.granite.client.tide.server.ServerSession;
import org.granite.client.tide.server.TideFaultEvent;
import org.granite.client.tide.server.TideResponder;
import org.granite.client.tide.server.TideResultEvent;
import org.granite.client.tide.server.TideRpcEvent;
import org.granite.client.util.PropertyHolder;
import org.granite.logging.Logger;
import org.granite.tide.data.model.Page;
import org.granite.tide.data.model.PageInfo;
import org.granite.tide.data.model.SortInfo;
import org.granite.util.TypeUtil;
/**
* @author William DRAI
*/
public abstract class AbstractPagedCollection<E, F> implements List<E>, Component, PropertyHolder, NameAware, ContextAware, Initializable, TideEventObserver {
private static final Logger log = Logger.getLogger(AbstractPagedCollection.class);
private final ServerSession serverSession;
private String componentName = null;
private String remoteComponentName = null;
private Class<? extends Component> remoteComponentClass = null;
private Component component = null;
private Context context = null;
private String methodName = "find";
private boolean usePage = false;
private PageFilterFinder<E> pageFilterFinder = null;
private SimpleFilterFinder<E> simpleFilterFinder = null;
protected SortAdapter sortAdapter = null;
private SortInfo sortInfo = new SortInfo();
private Class<F> filterClass = null;
protected boolean initializing = false;
private boolean initSent = false;
protected int first;
protected int last; // Current last index of local data
protected int max; // Page size
protected int count; // Result count
private E[] localIndex = null;
protected boolean fullRefresh = false;
protected boolean filterRefresh = false;
private boolean cancelPendingCalls = false;
public AbstractPagedCollection() {
// CDI proxying..
this.serverSession = null;
initCollection();
}
public AbstractPagedCollection(ServerSession serverSession) {
this.serverSession = serverSession;
initFilter();
initCollection();
}
public AbstractPagedCollection(Component remoteComponent, String methodName, int maxResults) {
this.component = remoteComponent;
this.methodName = methodName;
this.max = maxResults;
this.serverSession = null;
initFilter();
initCollection();
initFilterFinder();
}
public AbstractPagedCollection(Component remoteComponent, PageFilterFinder<E> finder, int maxResults) {
this.component = remoteComponent;
this.pageFilterFinder = finder;
this.max = maxResults;
this.serverSession = null;
initFilter();
initCollection();
}
public AbstractPagedCollection(Component remoteComponent, SimpleFilterFinder<E> finder, int maxResults) {
this.component = remoteComponent;
this.simpleFilterFinder = finder;
this.max = maxResults;
this.serverSession = null;
initFilter();
initCollection();
}
private void initCollection() {
log.debug("create collection");
first = 0;
last = 0;
count = 0;
initializing = true;
initComponent();
}
@SuppressWarnings("unchecked")
private void initComponent() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof ParameterizedType) {
ParameterizedType supertype = (ParameterizedType)superclass;
if (supertype.getActualTypeArguments()[0] instanceof Class<?>)
setElementClass((Class<E>)supertype.getActualTypeArguments()[0]);
if (supertype.getActualTypeArguments()[1] instanceof Class<?>) {
try {
setFilterClass((Class<F>)supertype.getActualTypeArguments()[1]);
}
catch (Exception e) {
throw new RuntimeException("Could not init filter for type " + supertype.getActualTypeArguments()[1], e);
}
}
}
}
protected abstract void initFilter();
/**
* Get total number of elements
*
* @return collection total size
*/
@Override
public int size() {
initialFind();
if (localIndex != null)
return count;
return 0;
}
protected void updateCount(int cnt) {
this.count = cnt;
}
/**
* Set the page size. The collection will store in memory twice this page size, and each server call
* will return at most the page size.
*
* @param max maximum number of requested elements
*/
public void setMaxResults(int max) {
this.max = max;
}
private Class<? extends E> elementClass;
private String elementName;
private Set<String> entityNames = new HashSet<String>();
public void setElementClass(Class<? extends E> elementClass) {
this.elementClass = elementClass;
if (this.elementName != null)
entityNames.remove(elementName);
elementName = elementClass != null ? elementClass.getSimpleName() : null;
if (this.elementName != null)
entityNames.add(this.elementName);
}
public Class<? extends E> getElementClass() {
return elementClass;
}
public void setCancelPendingCalls(boolean cancel) {
this.cancelPendingCalls = cancel;
}
public void setName(String componentName) {
this.componentName = componentName;
}
public void setContext(Context context) {
this.context = context;
if (remoteComponentName != null)
setRemoteComponentName(remoteComponentName);
if (remoteComponentClass != null) {
try {
setRemoteComponentClass(remoteComponentClass);
}
catch (Exception e) {
throw new RuntimeException("Could not init context", e);
}
}
if (component instanceof ContextAware)
((ContextAware)component).setContext(context);
}
public String getName() {
return remoteComponentName;
}
public void setRemoteComponentName(String remoteComponentName) {
if (remoteComponentName == null)
throw new IllegalArgumentException("remoteComponentName cannot be null");
this.remoteComponentName = remoteComponentName;
if (context == null) {
this.component = null;
return;
}
component = context.byName(remoteComponentName);
if (component == null || !(component instanceof ComponentImpl)) {
component = new ComponentImpl(serverSession);
context.set(remoteComponentName, component);
}
}
public void setRemoteComponentClass(Class<? extends Component> remoteComponentClass) throws IllegalAccessException, InstantiationException {
if (remoteComponentClass == null)
throw new IllegalArgumentException("remoteComponentClass cannot be null");
this.remoteComponentClass = remoteComponentClass;
if (context == null) {
component = null;
return;
}
component = context.byType(remoteComponentClass);
if (component == null) {
component = TypeUtil.newInstance(remoteComponentClass, new Class<?>[] { ServerSession.class }, new Object[] { serverSession });
context.set(component);
}
}
public void setRemoteComponent(Component remoteComponent) {
if (remoteComponent == null)
throw new IllegalArgumentException("remoteComponent cannot be null");
this.component = remoteComponent;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setPageFilterFinder(PageFilterFinder<E> finder) {
this.pageFilterFinder = finder;
}
public void setSimpleFilterFinder(SimpleFilterFinder<E> finder) {
this.simpleFilterFinder = finder;
}
public void setUsePage(boolean usePage) {
this.usePage = usePage;
}
public void init() {
if (component != null)
return;
component = new ComponentImpl(serverSession);
((ComponentImpl)component).setName(componentName);
((ComponentImpl)component).setContext(context);
}
public void setSortAdapter(SortAdapter sortAdapter) {
this.sortAdapter = sortAdapter;
if (sortAdapter != null)
sortAdapter.retrieve(sortInfo);
}
public SortAdapter getSortAdapter() {
return sortAdapter;
}
public void resetSort() {
this.sortAdapter = null;
sortInfo.setOrder(null);
sortInfo.setDesc(null);
}
public void setFilterClass(Class<F> filterClass) throws IllegalAccessException, InstantiationException {
if (Map.class.isAssignableFrom(filterClass)) {
setFilter(null);
return;
}
this.filterClass = filterClass;
setFilter(TypeUtil.newInstance(filterClass, filterClass));
}
public void resetFilter() {
if (filterClass == null) {
setFilter(null);
return;
}
try {
setFilter(TypeUtil.newInstance(filterClass, filterClass));
}
catch (Exception e) {
log.error(e, "Could not reset typed filter for PagedQuery %s", getName());
}
}
public abstract void setFilter(F filter);
public void reset() {
resetFilter();
resetSort();
clear();
}
@Override
public void handleEvent(TideEvent event) {
if (event.getType().startsWith(UpdateKind.REFRESH.eventName() + ".")) {
String entityName = event.getType().substring(UpdateKind.REFRESH.eventName().length()+1);
if (entityNames.contains(entityName))
fullRefresh();
}
}
/**
* Clear collection content
*/
@Override
@PreDestroy
public void clear() {
clearLocalIndex();
first = 0;
last = first+max;
updateCount(0);
getWrappedList().clear();
initializing = true;
initSent = false;
fullRefresh = false;
filterRefresh = false;
}
private List<Integer[]> pendingRanges = new ArrayList<Integer[]>();
private List<Future<?>> pendingCalls = new ArrayList<Future<?>>();
private void executeFind(int first, int last) {
log.debug("find from %d to %d", first, last);
if (cancelPendingCalls) {
for (Future<?> pendingCall : pendingCalls)
pendingCall.cancel(true);
}
pendingRanges.add(0, new Integer[] { first, last });
pendingCalls.add(0, find(first, last));
}
/**
* Trigger a results query for the current filter
* @param first : index of first required result
* @param last : index of last required result
*/
protected Future<?> find(int first, int last) {
int max = 0;
if (this.initializing && this.max > 0)
max = this.max;
else if (!this.initializing)
max = last-first;
Object filter = cloneFilter();
return doFind(filter, first, max);
}
protected synchronized Future<?> doFind(Object filter, int first, int max) {
// Force evaluation of max, results and count
if (sortAdapter != null)
sortAdapter.retrieve(sortInfo);
String[] order = sortInfo.getOrder();
if (order != null && order.length == 0)
order = null;
boolean[] desc = sortInfo.getDesc();
if (desc != null && desc.length == 0)
desc = null;
initFilterFinder();
if (pageFilterFinder != null) {
PageInfo pageInfo = new PageInfo(first, max, order, desc);
PagedCollectionResponder<Page<E>> findResponder = new PagedCollectionResponder<Page<E>>(first, max);
return pageFilterFinder.find(filter, pageInfo, findResponder);
}
PagedCollectionResponder<Map<String, Object>> findResponder = new PagedCollectionResponder<Map<String, Object>>(first, max);
return simpleFilterFinder.find(filter, first, max, order, desc, findResponder);
}
public abstract F getFilter();
protected abstract Object cloneFilter();
private void initFilterFinder() {
if (pageFilterFinder != null || simpleFilterFinder != null)
return;
boolean usePage = this.usePage;
try {
for (Method m : component.getClass().getMethods()) {
if (m.getName().equals(methodName) && m.getParameterTypes().length >= 2
&& PageInfo.class.isAssignableFrom(m.getParameterTypes()[1])) {
usePage = true;
break;
}
}
}
catch (Exception e) {
// Untyped component proxy
}
if (usePage)
pageFilterFinder = new ComponentPageFilterFinder(component, methodName);
else
simpleFilterFinder = new ComponentSimpleFilterFinder(component, methodName);
}
private final class ComponentPageFilterFinder implements PageFilterFinder<E> {
private final Component component;
private final String methodName;
public ComponentPageFilterFinder(Component component, String methodName) {
this.component = component;
this.methodName = methodName;
}
@Override
public Future<Page<E>> find(Object filter, PageInfo pageInfo, TideResponder<Page<E>> responder) {
return component.call(methodName, filter, pageInfo, responder);
}
}
private final class ComponentSimpleFilterFinder implements SimpleFilterFinder<E> {
private final Component component;
private final String methodName;
public ComponentSimpleFilterFinder(Component component, String methodName) {
this.component = component;
this.methodName = methodName;
}
@Override
public Future<Map<String, Object>> find(Object filter, int first, int max, String[] order, boolean[] desc, TideResponder<Map<String, Object>> responder) {
return component.call(methodName, filter, first, max, order, desc, responder);
}
}
/**
* Build a result object from the result event
*
* @param event the result event
* @param first first index requested
* @param max max elements requested
*
* @return a Page object containing data from the collection
* resultList : the retrieved data
* resultCount : the total count of elements (non paged)
* firstResult : the index of the first retrieved element
* maxResults : the maximum count of retrieved elements
*/
@SuppressWarnings("unchecked")
protected Page<E> getResult(TideResultEvent<?> event, int first, int max) {
if (event.getResult() instanceof Page<?>)
return (Page<E>)event.getResult();
Map<String, Object> result = (Map<String, Object>)event.getResult();
Page<E> page = new Page<E>(result.containsKey("firstResult") ? (Integer)result.get("firstResult") : first,
result.containsKey("maxResults") ? (Integer)result.get("maxResults") : max,
((Number)result.get("resultCount")).intValue(), (List<E>)result.get("resultList"));
return page;
}
/**
* Force refresh of collection when filter/sort have been changed
*
* @return always false
*/
public boolean fullRefresh() {
this.fullRefresh = true;
return refresh();
}
/**
* Refresh collection with new filter/sort parameters
*
* @return always false
*/
public boolean refresh() {
Object filter = getFilter();
if (filter != null && this.context.getEntityManager().isDeepDirtyEntity(filter)) {
filterRefresh = true;
fullRefresh = true;
}
// Recheck sort fields to listen for asc/desc change events
pendingRanges.clear();
pendingCalls.clear();
if (fullRefresh) {
log.debug("full refresh");
clearLocalIndex();
fullRefresh = false;
if (filterRefresh) {
first = 0;
last = first+max;
filterRefresh = false;
}
}
else
log.debug("refresh");
if (initSent || !initialFind())
executeFind(first, last);
return true;
}
private boolean initialFind() {
if (max > 0 && !initializing)
return false;
if (!initSent) {
log.debug("initial find");
executeFind(0, max);
initSent = true;
}
return true;
}
private void clearLocalIndex() {
localIndex = null;
}
/**
* Notify listeners of remote page result
*
* @param event the remote event (ResultEvent or FaultEvent)
* @param previousFirst index of first element before last updated list
* @param previousLast index of last element before last updated list
* @param savedSnapshot collection snapshot before last change
*/
protected abstract void firePageChange(TideRpcEvent event, int previousFirst, int previousLast, List<E> savedSnapshot);
/**
* Initialize collection after first find
*
* @param event the result event of the first find
*/
protected void initialize(TideResultEvent<?> event) {
}
/**
* Event handler for results query
*
* @param event the result event
* @param first first requested index
* @param max max elements requested
*/
protected void findResult(TideResultEvent<?> event, int first, int max) {
Page<E> page = getResult(event, first, max);
handleResult(page, event, first, max);
}
private String pendingRangesString() {
if (pendingRanges == null || pendingRanges.size() == 0)
return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pendingRanges.size(); i++) {
if (i > 0)
sb.append(",");
sb.append(pendingRanges.get(i)[0]).append("-").append(pendingRanges.get(i)[1]);
}
return sb.toString();
}
/**
* Event handler for results query
*
* @param page the result page
* @param event the result event
* @param first first requested index
* @param max max elements requested
*/
@SuppressWarnings("unchecked")
protected void handleResult(Page<E> page, TideResultEvent<?> event, int first, int max) {
List<E> list = page.getResultList();
if (log.isDebugEnabled())
log.debug("handle result %d - %d (%s)", first, max, pendingRangesString());
int pendingIndex = -1;
for (Iterator<Integer[]> ipr = pendingRanges.iterator(); ipr.hasNext(); ) {
Integer[] pr = ipr.next();
pendingIndex++;
if (pr[0] == first && pr[1] == first+max) {
ipr.remove();
pendingCalls.remove(pendingIndex);
break;
}
}
if (initializing && event != null) {
if (this.max == 0 && page.getMaxResults() > 0)
this.max = page.getMaxResults();
initialize(event);
}
if (pendingIndex > 0)
return;
int nextFirst = page.getFirstResult();
int nextLast = nextFirst + page.getMaxResults();
int pageNum = max > 0 ? nextFirst / max : 0;
log.debug("handle result page %d (%d - %d)", pageNum, nextFirst, nextLast);
updateCount(page.getResultCount());
if (localIndex != null) {
List<String> entityNames = new ArrayList<String>();
for (int i = 0; i < localIndex.length; i++) {
if (localIndex[i] == null)
continue;
String entityName = localIndex[i].getClass().getSimpleName();
if (!entityName.equals(elementName))
entityNames.remove(entityName);
}
}
for (Object o : list) {
if (elementClass == null || (o != null && o.getClass().isAssignableFrom(elementClass)))
elementClass = (Class<? extends E>)o.getClass();
}
if (elementClass == null) {
localIndex = (E[])new Object[0];
log.warn("Cannot determine elementClass from empty content, consider calling setElementClass manually");
}
else {
localIndex = (E[])Array.newInstance(elementClass, list.size());
localIndex = list.toArray(localIndex);
if (localIndex != null) {
for (int i = 0; i < localIndex.length; i++) {
if (localIndex[i] == null)
continue;
String entityName = localIndex[i].getClass().getSimpleName();
if (!entityName.equals(elementName))
entityNames.add(entityName);
}
}
}
int previousFirst = this.first;
int previousLast = this.last;
this.first = nextFirst;
this.last = nextLast;
List<E> savedSnapshot = null;
if (initializing) {
initializing = false;
getWrappedList().addAll(list);
}
else {
log.debug("Adjusting from %d-%d to %d-%d size %d", previousFirst, previousLast, nextFirst, nextLast, list.size());
// Adjust internal list to expected results without triggering events
if (nextFirst > previousFirst && nextFirst < previousLast) {
getInternalWrappedList().subList(0, Math.min(getInternalWrappedList().size(), nextFirst - previousFirst)).clear();
for (int i = 0; i < nextFirst - previousFirst && previousLast - nextFirst + i < list.size(); i++) {
E elt = list.get(previousLast - nextFirst + i);
getInternalWrappedList().add(elt);
}
}
else if (nextFirst == previousFirst && nextLast > previousLast) {
for (int i = 0; i < (nextLast-nextFirst)-(previousLast-previousFirst) && previousLast + i < list.size(); i++) {
E elt = list.get(previousLast + i);
getInternalWrappedList().add(elt);
}
}
else if (nextLast > previousFirst && nextLast < previousLast) {
if (nextLast-previousFirst < getInternalWrappedList().size())
getInternalWrappedList().subList(nextLast-previousFirst, getInternalWrappedList().size()).clear();
else
getInternalWrappedList().clear();
for (int i = 0; i < previousFirst - nextFirst && i < list.size(); i++) {
E elt = list.get(i);
getInternalWrappedList().add(i, elt);
}
}
else if (nextFirst >= this.last || nextLast <= previousFirst) {
getInternalWrappedList().clear();
for (int i = 0; i < list.size(); i++) {
E elt = list.get(i);
getInternalWrappedList().add(i, elt);
}
}
else {
savedSnapshot = new ArrayList<E>(getInternalWrappedList());
getInternalWrappedList().clear();
getInternalWrappedList().addAll(list);
}
}
firePageChange(event, previousFirst, previousLast, savedSnapshot);
}
/**
* Event handler for results fault
*
* @param event the fault event
* @param first first requested index
* @param max max elements requested
*/
protected void findFault(TideFaultEvent event, int first, int max) {
handleFault(event, first, max);
}
/**
* Event handler for results query fault
*
* @param event the fault event
*/
@SuppressWarnings("unchecked")
protected void handleFault(TideFaultEvent event, int first, int max) {
log.debug("findFault: %s", event);
int pendingIndex = -1;
for (Iterator<Integer[]> ipr = pendingRanges.iterator(); ipr.hasNext(); ) {
Integer[] pr = ipr.next();
pendingIndex++;
if (pr[0] == first && pr[1] == first+max) {
ipr.remove();
pendingCalls.remove(pendingIndex);
break;
}
}
if (initializing)
initSent = false;
firePageChange(event, this.first, this.last, Collections.EMPTY_LIST);
}
protected abstract List<E> getInternalWrappedList();
protected abstract List<E> getWrappedList();
/**
* Override of get() with lazy page loading
*
* @param index index of requested item
* @return object at specified index
*/
@Override
public E get(int index) {
if (index < 0)
return null;
if (initialFind())
return null;
if (localIndex != null && index >= first && index < last) { // Local data available for index
int j = index-first;
if (j >= 0 && j < localIndex.length)
return localIndex[j];
// Index not in current loaded range, max is more than last page size
return null;
}
// If already in a pending range, return null
for (Integer[] pendingRange : pendingRanges) {
if (index >= pendingRange[0] && index < pendingRange[1])
return null;
}
int page = index / max;
// Trigger a results query for requested page
int nfi = 0;
int nla = 0;
@SuppressWarnings("unused")
int idx = page * max;
if (index >= last && index < last + max) {
nfi = first;
nla = last + max;
if (nla > nfi + 2*max)
nfi = nla - 2*max;
if (nfi < 0)
nfi = 0;
if (nla > count)
nla = count;
}
else if (index < first && index >= first - max) {
nfi = first - max;
if (nfi < 0)
nfi = 0;
nla = last;
if (nla > nfi + 2*max)
nla = nfi + 2*max;
if (nla > count)
nla = count;
}
else {
nfi = index - max;
nla = nfi + 2 * max;
if (nfi < 0)
nfi = 0;
if (nla > count)
nla = count;
}
log.debug("request find for index " + index);
executeFind(nfi, nla);
return null;
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean contains(Object o) {
if (o == null)
return false;
if (localIndex != null) {
for (Object obj : localIndex) {
if (o.equals(obj))
return true;
}
}
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return false;
}
@Override
public int indexOf(Object o) {
if (o == null)
return -1;
if (localIndex != null) {
for (int i = 0; i < localIndex.length; i++) {
if (o.equals(localIndex[i]))
return first+i;
}
}
return -1;
}
@Override
public int lastIndexOf(Object o) {
if (o == null)
return -1;
if (localIndex != null) {
int index = -1;
for (int i = 0; i < localIndex.length; i++) {
if (o.equals(localIndex[i]))
index = first+i;;
}
return index;
}
return -1;
}
@Override
public Iterator<E> iterator() {
return new PagedCollectionIterator();
}
@Override
public ListIterator<E> listIterator() {
return new PagedCollectionIterator();
}
@Override
public ListIterator<E> listIterator(int index) {
return new PagedCollectionIterator();
}
@Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public E remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
}
public class PagedCollectionIterator implements ListIterator<E> {
private ListIterator<E> wrappedListIterator;
public PagedCollectionIterator() {
wrappedListIterator = getWrappedList().listIterator();
}
public PagedCollectionIterator(int index) {
wrappedListIterator = getWrappedList().listIterator(index);
}
@Override
public boolean hasNext() {
return wrappedListIterator.hasNext();
}
@Override
public E next() {
return wrappedListIterator.next();
}
@Override
public boolean hasPrevious() {
return wrappedListIterator.hasPrevious();
}
@Override
public E previous() {
return wrappedListIterator.previous();
}
@Override
public int nextIndex() {
return wrappedListIterator.nextIndex();
}
@Override
public int previousIndex() {
return wrappedListIterator.previousIndex();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void set(E e) {
throw new UnsupportedOperationException();
}
@Override
public void add(E e) {
throw new UnsupportedOperationException();
}
}
/**
* PropertyHolder interface
*/
public Object getObject() {
if (component instanceof PropertyHolder)
return ((PropertyHolder)component).getObject();
return null;
}
public void setProperty(String propName, Object value) {
if (component instanceof PropertyHolder)
((PropertyHolder)component).setProperty(propName, value);
}
@Override
public <T> Future<T> call(String operation, Object... args) {
throw new UnsupportedOperationException();
}
private class PagedCollectionResponder<R> implements TideResponder<R> {
private int first;
private int max;
public PagedCollectionResponder(int first, int max) {
this.first = first;
this.max = max;
}
@Override
public void result(TideResultEvent<R> event) {
findResult(event, first, max);
}
public void fault(TideFaultEvent event) {
findFault(event, first, max);
}
}
}