org.richfaces.renderkit.ForEachLoop Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.renderkit;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import javax.servlet.jsp.jstl.core.LoopTagStatus;
/**
*
* Provides implementation of {@link Iterable} object wrapping {@link Collection}, {@link Iterable}, array or {@link Iterator}.
*
*
*
* Provides the status of iteration compatible with {@link LoopTagStatus}.
*
*
* @author Lukas Fryc
*/
public class ForEachLoop implements Iterable {
private StateAwareIterator stateAwareIterator;
private Status status;
private boolean iterationStarted = false;
/**
* the current stateAwareIterator
*/
private T currentElement;
private int count = 1;
private Integer index;
private Integer step;
private Integer begin;
private Integer end;
/**
* the current element of last call to iterator.next() of original provided iterator
*/
private T element;
/**
* the next item which will be returned when calling stateAwareIterator.next()
*
* used as cache for determining if the original provided iterator has next element available
*/
private T nextElement;
/**
* determines if we looked for next time - in that case, nextElement contains next element to return
*/
private boolean foundNext = false;
private ForEachLoop(Iterator iterator) {
this.stateAwareIterator = new StateAwareIterator(iterator);
this.status = new Status();
}
public static ForEachLoop getInstance(Iterator iterator) {
return new ForEachLoop(iterator);
}
public static ForEachLoop getInstance(Iterable iterable) {
ForEachLoop loop = new ForEachLoop(iterable.iterator());
return loop;
}
public static ForEachLoop getInstance(T[] array) {
List arrayList = Arrays.asList(array);
return ForEachLoop.getInstance(arrayList);
}
@Override
public Iterator iterator() {
return stateAwareIterator;
}
public Status getStatus() {
return status;
}
/**
* Wraps {@link Iterator} instance in order to allow setup properties begin, end and step of iteration.
*/
public class StateAwareIterator implements Iterator {
private Iterator iterator;
public StateAwareIterator(Iterator iterator) {
this.iterator = iterator;
}
/**
* Asks underlying iterator if there is next element available.
*
* This method can call method {@link Iterator#next()} for underlying iterator in order to determine if there is next
* element (especially when there are begin or step properties setup).
*/
@Override
public boolean hasNext() {
try {
nextElement = next();
if (end != null && index > end) {
foundNext = false;
return false;
}
foundNext = true;
return true;
} catch (NoSuchElementException e) {
return false;
}
}
/**
* Provides next element of iteration given by begin, end and step properties
*/
@Override
public T next() {
if (foundNext) {
foundNext = false;
currentElement = nextElement;
return nextElement;
}
if (!iterationStarted) {
iterateToBegin();
iterationStarted = true;
} else {
iterateByStep();
}
currentElement = element;
return currentElement;
}
/**
* Iterates to first element provided by underlying iterator based on given begin property.
*/
private void iterateToBegin() {
int numberOfAdditionalIterations = (begin == null) ? 0 : begin;
element = iterator.next();
index = 0;
for (int i = 0; i < numberOfAdditionalIterations; i++) {
element = iterator.next();
index += 1;
}
}
/**
* Iterates by the step of elements to next element
*/
private void iterateByStep() {
int realStep = (step == null) ? 1 : step;
for (int i = 0; i < realStep; i++) {
element = iterator.next();
index += 1;
}
count += 1;
}
@Override
public void remove() {
iterator.remove();
}
}
/**
* Provides the functionality of {@link LoopTagStatus} for state of iteration.
*/
public class Status implements LoopTagStatus {
@Override
public Object getCurrent() {
return currentElement;
}
@Override
public int getIndex() {
return index;
}
@Override
public int getCount() {
return count;
}
@Override
public boolean isFirst() {
return count == 1;
}
@Override
public boolean isLast() {
return !stateAwareIterator.hasNext();
}
@Override
public Integer getBegin() {
return begin;
}
@Override
public Integer getEnd() {
return end;
}
@Override
public Integer getStep() {
return step;
}
}
public void setBegin(int begin) {
this.begin = begin;
}
public void setEnd(int end) {
this.end = end;
}
public void setStep(int step) {
this.step = step;
}
}