/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source 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 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.jstl.el; import com.caucho.el.Expr; import com.caucho.jsp.PageContextImpl; import com.caucho.util.CharBuffer; import com.caucho.util.EnumIterator; import com.caucho.util.L10N; import com.caucho.util.NullIterator; import javax.el.ELContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.jstl.core.LoopTag; import javax.servlet.jsp.jstl.core.LoopTagStatus; import javax.servlet.jsp.tagext.IterationTag; import javax.servlet.jsp.tagext.TagSupport; import java.lang.reflect.Array; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.Map; public class ForEachTag extends TagSupport implements IterationTag, LoopTag, LoopTagStatus { private static L10N L = new L10N(ForEachTag.class); protected Expr _itemsExpr; protected Expr _beginExpr; protected Expr _endExpr; protected Expr _stepExpr; protected String _var; protected String _varStatus; // runtime values protected Iterator _iterator; protected int _begin; protected int _end; protected int _step; protected Object _initialVar; protected Object _current; protected int _index; protected int _count; /** * Sets the collection expression. */ public void setItems(Expr items) { _itemsExpr = items; } /** * Sets the beginning index. */ public void setBegin(Expr begin) { _beginExpr = begin; } /** * Sets the ending index. */ public void setEnd(Expr end) { _endExpr = end; } /** * Sets the step index. */ public void setStep(Expr step) { _stepExpr = step; } /** * Sets the iteration variable. */ public void setVar(String var) { _var = var; } /** * Sets the status variable. */ public void setVarStatus(String var) { _varStatus = var; } /** * Process the tag. */ public int doStartTag() throws JspException { try { _iterator = null; _index = 0; _count = 0; PageContextImpl pageContext = (PageContextImpl) this.pageContext; ELContext env = pageContext.getELContext(); if (_beginExpr != null) _begin = (int) _beginExpr.evalLong(env); else _begin = -1; if (_endExpr != null) _end = (int) _endExpr.evalLong(env); else _end = Integer.MAX_VALUE; if (_stepExpr != null) _step = (int) _stepExpr.evalLong(env); else _step = 0; Object items = null; if (_itemsExpr != null) { items = _itemsExpr.evalObject(env); _iterator = getIterator(items); while (_index < _begin && _iterator.hasNext()) { _index++; _iterator.next(); } } else if (_beginExpr == null) throw new JspException(L.l("c:forEach must specify `items' or `begin'")); else if (_endExpr == null) throw new JspException(L.l("c:forEach must specify `items' or `begin'")); else { _iterator = new RangeIterator(_begin, _end); _end = -1; } if (_varStatus != null) pageContext.setAttribute(_varStatus, this); if (_var != null) _initialVar = pageContext.getAttribute(_var); return doAfterBody(); } catch (Exception e) { throw new JspException(e); } } public int doAfterBody() throws JspException { if (_iterator == null) { if (_var != null) pageContext.setAttribute(_var, _initialVar); return SKIP_BODY; } else if (_iterator.hasNext()) { int stepCount; if (_step <= 0 || _count == 0) stepCount = 1; else stepCount = _step; for (; stepCount > 0; stepCount--) { if (! _iterator.hasNext()) { if (_var != null) pageContext.setAttribute(_var, _initialVar); return SKIP_BODY; } _index++; _current = _iterator.next(); } _count++; if (_var != null) pageContext.setAttribute(_var, _current); if (_index - 1 <= _end || _end < 0) return EVAL_BODY_AGAIN; else { if (_var != null) pageContext.setAttribute(_var, _initialVar); return SKIP_BODY; } } else { if (_var != null) pageContext.setAttribute(_var, _initialVar); return SKIP_BODY; } } // LoopTag /** * Returns the status. */ public LoopTagStatus getLoopStatus() { return this; } // LoopTagStatus /** * Returns the current object. */ public Object getCurrent() { return _current; } /** * Returns the index. */ public int getIndex() { return _index - 1; } /** * Returns the number of objects returned. */ public int getCount() { return _count; } /** * Returns true if this is the first item. */ public boolean isFirst() { return _count == 1; } /** * Returns true if this is the last item. */ public boolean isLast() { if (_iterator == null || ! _iterator.hasNext()) return true; else if (_step > 0 && _step + _index > _end) return true; else return false; } /** * Returns the begin index. */ public Integer getBegin() { if (_beginExpr != null && _itemsExpr != null) return new Integer(_begin); else return null; } /** * Returns the end index. */ public Integer getEnd() { if (_endExpr != null && _itemsExpr != null) return new Integer(_end); else return null; } /** * Returns the step index. */ public Integer getStep() { if (_stepExpr != null) return new Integer(_step); else return null; } public static Iterator getIterator(Object items) throws JspTagException { if (items == null) return NullIterator.create(); else if (items instanceof Map) return ((Map) items).entrySet().iterator(); else if (items instanceof Collection) return ((Collection) items).iterator(); else if (items.getClass().isArray()) return new ArrayIterator(items); else if (items instanceof Iterator) return (Iterator) items; else if (items instanceof Enumeration) return new EnumIterator((Enumeration) items); else if (items instanceof String) return new StringIterator((String) items); else throw new JspTagException(L.l("unknown items value `{0}'", items)); } public static class ArrayIterator implements Iterator { private Object _array; private int _index; private int _length; ArrayIterator(Object array) { _array = array; _length = Array.getLength(array); } public boolean hasNext() { return _index < _length; } public Object next() { if (_index < _length) return Array.get(_array, _index++); else return null; } public void remove() { throw new UnsupportedOperationException(); } } public static class StringIterator implements Iterator { private String _value; private int _length; private int _i; StringIterator(String value) { _value = value; _length = value.length(); for (; _i < _length; _i++) if (_value.charAt(_i) != ',') break; } public boolean hasNext() { return _i < _length; } public Object next() { char ch; int begin = _i; int tail = -1; for (; _i < _length; _i++) { ch =_value.charAt(_i); if (ch == ',') { if (tail == -1) tail = _i; } else { if (tail != -1) break; } } if (tail == -1) tail = _length; String value = _value.substring(begin, tail); return value; } public void remove() { throw new UnsupportedOperationException(); } } public static class RangeIterator implements Iterator { private int _end; private int _i; RangeIterator(int begin, int end) { _i = begin; _end = end; } public boolean hasNext() { return _i <= _end; } public Object next() { if (_i <= _end) return new Integer(_i++); else return null; } public void remove() { throw new UnsupportedOperationException(); } } }