/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.cyclop.web.components.iterablegrid;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.jcip.annotations.NotThreadSafe;
/** @author Maciej Miklas */
@NotThreadSafe
final class NavigableIterator<E> implements Iterator<E> {
private final Iterator<E> wrapped;
/** contains all elements in order returned by iterator */
private final List<E> cache;
/** Contains index of the elements that has been read by #next() */
private final Set<Integer> read = new HashSet<>();
private int nextIndex = 0;
private int count = 0;
private final int limit;
public NavigableIterator(Iterator<E> wrapped, int limit, List<E> cache) {
if (wrapped == null) {
throw new IllegalArgumentException("Wrapped must not be null");
}
this.wrapped = wrapped;
this.limit = limit;
this.cache = cache;
}
public void prepare(int first, int count) {
if (first > limit) {
throw new IllegalArgumentException("Cannot skipp " + first + " elements, because iterator is limited to: "
+ limit);
}
this.nextIndex = first;
this.count = count;
}
/**
* @return true if there is more data to be cache for current setup done by {@link #prepare(int, int)}
*/
@Override
public boolean hasNext() {
if (nextIndex > limit) {
return false;
}
boolean next = (count > 0 && (wrapped.hasNext()) || (nextIndex < cache.size()));
return next;
}
public boolean hasMoreData() {
return wrapped.hasNext();
}
@Override
public E next() {
E next;
if (nextIndex > limit) {
next = null;
} else if (nextIndex < cache.size()) {
next = cache.get(nextIndex);
} else {
if (!wrapped.hasNext()) {
next = null;
} else {
// Call on NavigableIterator#prepare(...) could skip some
// elements, this has to be reflected in iterator position on #wrapper
iterateToIndex(nextIndex);
next = wrapped.next();
cache.add(next);
}
}
if (next != null) {
read.add(nextIndex);
count--;
nextIndex++;
}
return next;
}
@Override
public void remove() {
wrapped.remove();
}
public int iterateToIndex(int toIndex) {
toIndex = Math.min(toIndex, limit);
int rsize = cache.size();
if (toIndex <= rsize) {
return toIndex;
}
int index = rsize + 1;
for (; index < toIndex; index++) {
if (wrapped.hasNext()) {
cache.add(wrapped.next());
} else {
break;
}
}
return index;
}
public int maxSize() {
return Math.max(read.size(), cache.size());
}
public int readSize() {
return read.size();
}
}