org.zkoss.zk.ui.util.ForEachImpl Maven / Gradle / Ivy
/* ForEachImpl.java
Purpose:
Description:
History:
Wed Mar 8 14:21:08 2006, Created by tomyeh
Copyright (C) 2006 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zk.ui.util;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.zkoss.util.CollectionsX;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.ext.Scope;
import org.zkoss.zk.xel.EvaluatorRef;
import org.zkoss.zk.xel.ExValue;
/**
* An implementation of {@link ForEach}.
*
* Note: the use of {@link ForEachImpl} is different from
* {@link ConditionImpl}. While you could use the same instance of
* {@link ConditionImpl} for all evaluation, each instance of
* {@link ForEachImpl} can be used only once (drop it after {@link #next}
* returns false).
*
* @author tomyeh
*/
public class ForEachImpl implements ForEach {
private final EvaluatorRef _evalr;
private final Page _page;
private final Component _comp;
private final ExValue[] _expr;
private final ExValue _begin, _end, _step;
private Status _status;
private Iterator _it;
private Object _oldEach;
private boolean _done;
/** Returns an instance that represents the iterator for the
* specified collection.
*
* @param expr an array of expressions. There are two formats.
*
* - length == 1: it iterates thru the content of expr[0].
* For example, if expr[0] is an array, all items in this array will
* be iterated.
* - length > 1, it will iterate thru expr[0]
* - length == 0 or expr is null, null is returned
*
* @since 3.0.6
*/
public static ForEach getInstance(EvaluatorRef evalr, Component comp, ExValue[] expr, ExValue begin, ExValue end) {
return getInstance(evalr, comp, expr, begin, end, null);
}
/** Returns an instance that represents the iterator for the
* specified collection, or null if expr is null or empty.
*
* @param expr an array of expressions. There are two formats.
*
* - length == 1: it iterates thru the content of expr[0].
* For example, if expr[0] is an array, all items in this array will
* be iterated.
* - length > 1, it will iterate thru expr[0]
* - length == 0 or expr is null, null is returned
*
* @since 3.0.6
*/
public static ForEach getInstance(EvaluatorRef evalr, Page page, ExValue[] expr, ExValue begin, ExValue end) {
return getInstance(evalr, page, expr, begin, end, null);
}
/** Returns an instance that represents the iterator for the
* specified collection.
*
* @param expr an array of expressions. There are two formats.
*
* - length == 1: it iterates thru the content of expr[0].
* For example, if expr[0] is an array, all items in this array will
* be iterated.
* - length > 1, it will iterate thru expr[0]
* - length == 0 or expr is null, null is returned
*
* @since 8.0.0
*/
public static ForEach getInstance(EvaluatorRef evalr, Component comp, ExValue[] expr, ExValue begin, ExValue end,
ExValue step) {
if (expr == null || expr.length == 0)
return null;
return new ForEachImpl(evalr, comp, expr, begin, end, step);
}
/** Returns an instance that represents the iterator for the
* specified collection, or null if expr is null or empty.
*
* @param expr an array of expressions. There are two formats.
*
* - length == 1: it iterates thru the content of expr[0].
* For example, if expr[0] is an array, all items in this array will
* be iterated.
* - length > 1, it will iterate thru expr[0]
* - length == 0 or expr is null, null is returned
*
* @since 8.0.0
*/
public static ForEach getInstance(EvaluatorRef evalr, Page page, ExValue[] expr, ExValue begin, ExValue end,
ExValue step) {
if (expr == null || expr.length == 0)
return null;
return new ForEachImpl(evalr, page, expr, begin, end, step);
}
/** Constructor.
* In most cases, use {@link #getInstance(EvaluatorRef, Component, ExValue[], ExValue, ExValue, ExValue)}
* instead of this constructor.
* @exception IllegalArgumentException if comp or evalr is null
* @since 8.0.0
*/
public ForEachImpl(EvaluatorRef evalr, Component comp, ExValue[] expr, ExValue begin, ExValue end, ExValue step) {
if (comp == null || evalr == null)
throw new IllegalArgumentException("null");
_evalr = evalr;
_page = null;
_comp = comp;
_expr = expr;
_begin = begin;
_end = end;
_step = step;
}
/** Constructor.
* In most cases, use {@link #getInstance(EvaluatorRef, Component, ExValue[], ExValue, ExValue, ExValue)}
* instead of this constructor.
* @exception IllegalArgumentException if page or evalr is null
* @since 8.0.0
*/
public ForEachImpl(EvaluatorRef evalr, Page page, ExValue[] expr, ExValue begin, ExValue end, ExValue step) {
if (page == null || evalr == null)
throw new IllegalArgumentException();
_evalr = evalr;
_page = page;
_comp = null;
_expr = expr;
_begin = begin;
_end = end;
_step = step;
}
/** Constructor.
* In most cases, use {@link #getInstance(EvaluatorRef, Component, ExValue[], ExValue, ExValue)}
* instead of this constructor.
* @exception IllegalArgumentException if comp or evalr is null
* @since 3.0.6
*/
public ForEachImpl(EvaluatorRef evalr, Component comp, ExValue[] expr, ExValue begin, ExValue end) {
if (comp == null || evalr == null)
throw new IllegalArgumentException("null");
_evalr = evalr;
_page = null;
_comp = comp;
_expr = expr;
_begin = begin;
_end = end;
_step = null;
}
/** Constructor.
* In most cases, use {@link #getInstance(EvaluatorRef, Component, ExValue[], ExValue, ExValue)}
* instead of this constructor.
* @exception IllegalArgumentException if page or evalr is null
* @since 3.0.6
*/
public ForEachImpl(EvaluatorRef evalr, Page page, ExValue[] expr, ExValue begin, ExValue end) {
if (page == null || evalr == null)
throw new IllegalArgumentException();
_evalr = evalr;
_page = page;
_comp = null;
_expr = expr;
_begin = begin;
_end = end;
_step = null;
}
/** Returns an instance that represents the iterator for the
* specified collection, or null if expr is null or empty.
*
* @param expr an EL expression that shall return a collection of objects.
* @see #getInstance(EvaluatorRef, Component, ExValue[], ExValue, ExValue)
*/
public static ForEach getInstance(EvaluatorRef evalr, Component comp, String expr, String begin, String end) {
if (expr == null || expr.length() == 0)
return null;
return new ForEachImpl(evalr, comp, expr, begin, end);
}
/** Returns an instance that represents the iterator for the
* specified collection, or null if expr is null or empty.
*
* @param expr an EL expression that shall return a collection of objects.
* @since 3.0.0
* @see #getInstance(EvaluatorRef, Page, ExValue[], ExValue, ExValue)
*/
public static ForEach getInstance(EvaluatorRef evalr, Page page, String expr, String begin, String end) {
if (expr == null || expr.length() == 0)
return null;
return new ForEachImpl(evalr, page, expr, begin, end);
}
/** Constructor.
* In most cases, use {@link #getInstance(EvaluatorRef, Component, String, String, String)}
* instead of this constructor.
* @exception IllegalArgumentException if comp or evalr is null
* @since 3.0.0
* @see #ForEachImpl(EvaluatorRef, Component, ExValue[], ExValue, ExValue)
*/
public ForEachImpl(EvaluatorRef evalr, Component comp, String expr, String begin, String end) {
if (comp == null || evalr == null)
throw new IllegalArgumentException();
_evalr = evalr;
_page = null;
_comp = comp;
_expr = expr != null ? new ExValue[] { new ExValue(expr, Object.class) } : null;
_begin = begin != null && begin.length() > 0 ? new ExValue(begin, Integer.class) : null;
_end = end != null && end.length() > 0 ? new ExValue(end, Integer.class) : null;
_step = null;
}
/** Constructor.
* In most cases, use {@link #getInstance(EvaluatorRef, Component, String, String, String)}
* instead of this constructor.
* @exception IllegalArgumentException if page or evalr is null
* @since 3.0.0
* @see #ForEachImpl(EvaluatorRef, Page, ExValue[], ExValue, ExValue)
*/
public ForEachImpl(EvaluatorRef evalr, Page page, String expr, String begin, String end) {
if (page == null || evalr == null)
throw new IllegalArgumentException();
_evalr = evalr;
_page = page;
_comp = null;
_expr = expr != null ? new ExValue[] { new ExValue(expr, Object.class) } : null;
_begin = begin != null && begin.length() > 0 ? new ExValue(begin, Integer.class) : null;
_end = end != null && end.length() > 0 ? new ExValue(end, Integer.class) : null;
_step = null;
}
private Object eval(ExValue value) {
return value == null ? null : _comp != null ? value.getValue(_evalr, _comp) : value.getValue(_evalr, _page);
}
//-- ForEach --//
public boolean next() {
if (_done)
throw new IllegalStateException("Iterate twice not allowed");
if (_status == null) {
//Bug 2188572: we have to evaluate _expr before setupStatus
final Object o;
if (_expr == null || _expr.length == 0) {
o = null;
} else if (_expr.length == 1) {
o = eval(_expr[0]);
} else {
Object[] ary = new Object[_expr.length];
for (int j = 0; j < _expr.length; ++j)
ary[j] = eval(_expr[j]);
o = ary;
}
if (o == null) {
_done = true;
return false;
}
//Bug 1786154: we have to prepare _status first since _expr,
//_begin or _end might depend on it
setupStatus();
Integer ibeg = (Integer) eval(_begin);
int vbeg = ibeg != null ? ibeg.intValue() : 0;
if (vbeg < 0)
ibeg = new Integer(vbeg = 0);
_status.setBegin(ibeg);
_status.setEnd((Integer) eval(_end));
_status.setStep((Integer) eval(_step));
prepare(o, vbeg); //prepare iterator
}
if (_status.count > 0 && _status.getStep() != null) {
_status.index += _status.getStep().intValue() - 1;
}
if ((_status.end == null || _status.index < _status.end.intValue()) && _it.hasNext()) {
++_status.index;
_status.each = _it.next();
_status.count++;
getScope().setAttribute("each", _status.each);
if (_status.getStep() != null)
discard(_status.getStep().intValue() - 1);
calibrateLast();
return true;
}
//restore
_done = true;
restoreStatus();
return false;
}
private void discard(int n) {
if (n < 0) {
throw new IllegalArgumentException("'step' cannot less than 1");
}
while (n-- > 0) {
if (_it.hasNext()) {
_it.next();
} else {
break;
}
}
}
private void calibrateLast() {
_status.last = !_it.hasNext() && (_status.end == null || _status.index + _status.begin
+ (_status.getStep() == null ? 1 : _status.getStep().intValue()) == _status.end.intValue());
}
private void setupStatus() {
final Scope scope = getScope();
_oldEach = scope.getAttribute("each", true);
_status = new Status(scope.getAttribute("forEachStatus", true));
scope.setAttribute("forEachStatus", _status);
}
private void restoreStatus() {
final Scope scope = getScope();
if (_status.previous != null)
scope.setAttribute("forEachStatus", _status.previous);
else
scope.removeAttribute("forEachStatus");
if (_oldEach != null)
scope.setAttribute("each", _oldEach);
else
scope.removeAttribute("each");
_it = null;
_status = null; //recycle (just in case)
}
private Scope getScope() {
return _comp != null ? (Scope) _comp : _page;
}
@SuppressWarnings("unchecked")
private void prepare(Object o, final int begin) {
if (begin > 0 && (o instanceof List)) {
final List l = (List) o;
final int size = l.size();
_it = l.listIterator(begin > size ? size : begin);
} else if (o instanceof Collection) {
_it = ((Collection) o).iterator();
forward(begin);
} else if (o instanceof Map) {
_it = ((Map) o).entrySet().iterator();
forward(begin);
} else if (o instanceof Iterator) {
_it = (Iterator) o;
forward(begin);
} else if (o instanceof Enumeration) {
_it = new CollectionsX.EnumerationIterator((Enumeration) o);
forward(begin);
} else if (o instanceof Object[]) {
final Object[] ary = (Object[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return ary[_j++];
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof int[]) {
final int[] ary = (int[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return new Integer(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof long[]) {
final long[] ary = (long[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return new Long(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof short[]) {
final short[] ary = (short[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return new Short(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof byte[]) {
final byte[] ary = (byte[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return new Byte(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof double[]) {
final double[] ary = (double[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return new Double(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof float[]) {
final float[] ary = (float[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return new Float(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof char[]) {
final char[] ary = (char[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return new Character(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else if (o instanceof boolean[]) {
final boolean[] ary = (boolean[]) o;
_it = new Iterator() {
private int _j = begin;
public boolean hasNext() {
return _j < ary.length;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return Boolean.valueOf(ary[_j++]);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} else {
_it = new CollectionsX.OneIterator(o);
forward(begin);
}
}
private void forward(int begin) {
while (--begin >= 0 && _it.hasNext())
_it.next();
}
private static class Status implements ForEachStatus {
private final Object previous;
private Object each;
private int index;
private int count = 0;
private boolean last = false;
private Integer begin, end, step;
private Status(Object previous) {
this.previous = previous;
this.index = -1;
}
private void setBegin(Integer begin) {
this.begin = begin;
this.index = begin != null ? begin.intValue() - 1 : -1;
}
private void setEnd(Integer end) {
this.end = end;
}
private void setStep(Integer step) {
this.step = step;
}
public ForEachStatus getPrevious() {
return this.previous instanceof ForEachStatus ? (ForEachStatus) this.previous : null;
}
public int getIndex() {
return this.index;
}
public Integer getBegin() {
return this.begin;
}
public Integer getEnd() {
return this.end;
}
public String toString() {
return "[index=" + index + ']';
}
public Object getCurrent() {
return this.each;
}
public boolean isFirst() {
return getCount() == 1;
}
public boolean isLast() {
return last;
}
public Integer getStep() {
return step;
}
public int getCount() {
return count;
}
}
}