1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jelly.tags.core;
18
19 import java.util.Iterator;
20
21 import javax.servlet.jsp.jstl.core.LoopTagStatus;
22
23 import org.apache.commons.jelly.JellyTagException;
24 import org.apache.commons.jelly.MissingAttributeException;
25 import org.apache.commons.jelly.TagSupport;
26 import org.apache.commons.jelly.XMLOutput;
27 import org.apache.commons.jelly.expression.Expression;
28 import org.apache.commons.jelly.impl.BreakException;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 /***
33 * Iterates over a collection, iterator or an array of objects.
34 * Uses the same syntax as the <a href="http://java.sun.com/products/jsp/jstl/">JSTL</a>
35 * <code>forEach</code> tag does.
36 *
37 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
38 * @version $Revision: 155420 $
39 */
40 public class ForEachTag extends TagSupport {
41
42 /*** The Log to which logging calls will be made. */
43 private static final Log log = LogFactory.getLog(ForEachTag.class);
44
45 /*** Holds the variable name to export for the item being iterated over. */
46 private Expression items;
47
48 /***
49 * If specified then the current item iterated through will be defined
50 * as the given variable name.
51 */
52 private String var;
53
54 /***
55 * If specified then the current index counter will be defined
56 * as the given variable name.
57 */
58 private String indexVar;
59
60 /*** variable to hold loop status */
61 private String statusVar;
62
63 /*** The starting index value */
64 private int begin;
65
66 /*** The ending index value */
67 private int end = Integer.MAX_VALUE;
68
69 /*** The index increment step */
70 private int step = 1;
71
72 /*** The iteration index */
73 private int index;
74
75 public ForEachTag() {
76 }
77
78
79
80
81 public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException {
82
83 if (log.isDebugEnabled()) {
84 log.debug("running with items: " + items);
85 }
86
87 try {
88 if (items != null) {
89 Iterator iter = items.evaluateAsIterator(context);
90
91 if (log.isDebugEnabled()) {
92 log.debug("Iterating through: " + iter);
93 }
94
95
96 for (index = 0; index < begin && iter.hasNext(); index++ ) {
97 iter.next();
98 }
99
100
101 LoopStatus status = null;
102 if (statusVar != null)
103 {
104
105 Integer statusBegin = (begin == 0) ? null : new Integer(begin);
106 Integer statusEnd = (end == Integer.MAX_VALUE) ? null : new Integer(end);
107 Integer statusStep = (step == 1) ? null : new Integer(step);
108 status = new LoopStatus(statusBegin, statusEnd, statusStep);
109 context.setVariable(statusVar, status);
110 }
111
112 boolean firstTime = true;
113 int count = 0;
114 while (iter.hasNext() && index <= end) {
115 Object value = iter.next();
116 if (var != null) {
117 context.setVariable(var, value);
118 }
119 if (indexVar != null) {
120 context.setVariable(indexVar, new Integer(index));
121 }
122
123 if (statusVar != null) {
124 count++;
125 status.setCount(count);
126 status.setCurrent(value);
127 status.setFirst(firstTime);
128 status.setIndex(index);
129
130 if (firstTime) {
131 firstTime = !firstTime;
132 }
133 }
134
135
136 boolean finished = false;
137 index++;
138 for ( int i = 1; i < step && !finished; i++, index++ ) {
139 if ( ! iter.hasNext() ) {
140 finished = true;
141 }
142 else {
143 iter.next();
144 }
145 }
146
147 if (statusVar != null) {
148 status.setLast(finished || !iter.hasNext() || index > end);
149 }
150 invokeBody(output);
151
152 }
153 }
154 else {
155 if ( end == Integer.MAX_VALUE && begin == 0 ) {
156 throw new MissingAttributeException( "items" );
157 }
158 else {
159 String varName = var;
160 if ( varName == null ) {
161 varName = indexVar;
162 }
163
164 LoopStatus status = null;
165 if (statusVar != null)
166 {
167
168 Integer statusBegin = new Integer(begin);
169 Integer statusEnd = new Integer(end);
170 Integer statusStep = new Integer(step);
171 status = new LoopStatus(statusBegin, statusEnd, statusStep);
172 context.setVariable(statusVar, status);
173 }
174
175 int count = 0;
176 for (index = begin; index <= end; index += step ) {
177
178 Object value = new Integer(index);
179 if (varName != null) {
180 context.setVariable(varName, value);
181 }
182
183 if (status != null) {
184 count++;
185 status.setIndex(index);
186 status.setCount(count);
187 status.setCurrent(value);
188 status.setFirst(index == begin);
189 status.setLast(index > end - step);
190 }
191 invokeBody(output);
192 }
193 }
194 }
195 }
196 catch (BreakException e) {
197 if (log.isDebugEnabled()) {
198 log.debug("loop terminated by break: " + e, e);
199 }
200 }
201 }
202
203
204
205
206 /***
207 * Sets the expression used to iterate over.
208 * This expression could resolve to an Iterator, Collection, Map, Array,
209 * Enumeration or comma separated String.
210 */
211 public void setItems(Expression items) {
212 this.items = items;
213 }
214
215 /*** Sets the variable name to export for the item being iterated over
216 */
217 public void setVar(String var) {
218 this.var = var;
219 }
220
221 /*** Sets the variable name to export the current index counter to
222 */
223 public void setIndexVar(String indexVar) {
224 this.indexVar = indexVar;
225 }
226
227 /*** Sets the starting index value
228 */
229 public void setBegin(int begin) {
230 this.begin = begin;
231 }
232
233 /*** Sets the ending index value
234 */
235 public void setEnd(int end) {
236 this.end = end;
237 }
238
239 /*** Sets the index increment step
240 */
241 public void setStep(int step) {
242 this.step = step;
243 }
244
245 /***
246 * Sets the variable name to export the current status to.
247 * The status is an implementation of the JSTL LoopTagStatus interface that provides
248 * the following bean properties:
249 * <ul>
250 * <li>current - the current value of the loop items being iterated</li>
251 * <li>index - the current index of the items being iterated</li>
252 * <li>first - true if this is the first iteration, false otherwise</li>
253 * <li>last - true if this is the last iteration, false otherwise</li>
254 * <li>begin - the starting index of the loop</li>
255 * <li>step - the stepping value of the loop</li>
256 * <li>end - the end index of the loop</li>
257 * </ul>
258 */
259 public void setVarStatus(String var) {
260 this.statusVar = var;
261 }
262
263 /***
264 * Holds the status of the loop.
265 */
266 public static final class LoopStatus implements LoopTagStatus
267 {
268 private Integer begin;
269 private int count;
270 private Object current;
271 private Integer end;
272 private int index;
273 private Integer step;
274 private boolean first;
275 private boolean last;
276
277 public LoopStatus(Integer begin, Integer end, Integer step) {
278 this.begin = begin;
279 this.end = end;
280 this.step = step;
281 }
282 /***
283 * @return Returns the begin.
284 */
285 public Integer getBegin() {
286 return begin;
287 }
288 /***
289 * @return Returns the count.
290 */
291 public int getCount() {
292 return count;
293 }
294 /***
295 * @return Returns the current.
296 */
297 public Object getCurrent() {
298 return current;
299 }
300 /***
301 * @return Returns the end.
302 */
303 public Integer getEnd() {
304 return end;
305 }
306 /***
307 * @return Returns the first.
308 */
309 public boolean isFirst() {
310 return first;
311 }
312 /***
313 * @return Returns the index.
314 */
315 public int getIndex() {
316 return index;
317 }
318 /***
319 * @return Returns the last.
320 */
321 public boolean isLast() {
322 return last;
323 }
324 /***
325 * @return Returns the step.
326 */
327 public Integer getStep() {
328 return step;
329 }
330 /***
331 * @param count The count to set.
332 */
333 public void setCount(int count) {
334 this.count = count;
335 }
336 /***
337 * @param current The current to set.
338 */
339 public void setCurrent(Object current) {
340 this.current = current;
341 }
342 /***
343 * @param first The first to set.
344 */
345 public void setFirst(boolean first) {
346 this.first = first;
347 }
348 /***
349 * @param last The last to set.
350 */
351 public void setLast(boolean last) {
352 this.last = last;
353 }
354 /***
355 * @param index The index to set.
356 */
357 public void setIndex(int index) {
358 this.index = index;
359 }
360 }
361 }