View Javadoc

1   /*
2    * $Id: IteratorComponent.java 668673 2008-06-17 13:54:54Z davenewton $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts2.components;
23  
24  import java.io.Writer;
25  import java.util.Iterator;
26  
27  import org.apache.struts2.views.annotations.StrutsTag;
28  import org.apache.struts2.views.annotations.StrutsTagAttribute;
29  import org.apache.struts2.util.MakeIterator;
30  import org.apache.struts2.views.jsp.IteratorStatus;
31  
32  import com.opensymphony.xwork2.util.ValueStack;
33  
34  /***
35   * <!-- START SNIPPET: javadoc -->
36   *
37   * <p>Iterator will iterate over a value. An iterable value can be any of: java.util.Collection, java.util.Iterator,
38   * java.util.Enumeration, java.util.Map, or an array.</p> <p/> <!-- END SNIPPET: javadoc -->
39   *
40   * <!-- START SNIPPET: params -->
41   *
42   * <ul>
43   *
44   * <li>status (String) - if specified, an instance of IteratorStatus will be pushed into stack upon each iteration</li>
45   *
46   * <li>value (Object) - the source to iterate over, must be iteratable, else the object itself will be put into a
47   * newly created List (see MakeIterator#convert(Object)</li>
48   *
49   * <li>id (String) - if specified the current iteration object will be place with this id in Struts stack's context
50   * scope</li>
51   *
52   * </ul>
53   *
54   * <!-- END SNIPPET: params -->
55   *
56   * <!-- START SNIPPET: example1description -->
57   *
58   * <p>The following example retrieves the value of the getDays() method of the current object on the value stack and
59   * uses it to iterate over. The &lt;s:property/&gt; tag prints out the current value of the iterator.</p>
60   *
61   * <!-- END SNIPPET: example1description -->
62   *
63   * <pre>
64   * <!-- START SNIPPET: example1code -->
65   * &lt;s:iterator value="days"&gt;
66   *   &lt;p&gt;day is: &lt;s:property/&gt;&lt;/p&gt;
67   * &lt;/s:iterator&gt;
68   * <!-- END SNIPPET: example1code -->
69   * </pre>
70   *
71   *
72   * <!-- START SNIPPET: example2description -->
73   *
74   * <p>The following example uses a {@link Bean} tag and places it into the ActionContext. The iterator tag will retrieve
75   * that object from the ActionContext and then calls its getDays() method as above. The status attribute is also used to
76   * create an {@link IteratorStatus} object, which in this example, its odd() method is used to alternate row
77   * colours:</p>
78   *
79   * <!-- END SNIPPET: example2description -->
80   *
81   *
82   * <pre>
83   * <!-- START SNIPPET: example2code -->
84   *
85   * &lt;s:bean name="org.apache.struts2.example.IteratorExample" var="it"&gt;
86   *   &lt;s:param name="day" value="'foo'"/&gt;
87   *   &lt;s:param name="day" value="'bar'"/&gt;
88   * &lt;/s:bean&gt;
89   * <p/>
90   * &lt;table border="0" cellspacing="0" cellpadding="1"&gt;
91   * &lt;tr&gt;
92   *   &lt;th&gt;Days of the week&lt;/th&gt;
93   * &lt;/tr&gt;
94   * <p/>
95   * &lt;s:iterator value="#it.days" status="rowstatus"&gt;
96   *   &lt;tr&gt;
97   *     &lt;s:if test="#rowstatus.odd == true"&gt;
98   *       &lt;td style="background: grey"&gt;&lt;s:property/&gt;&lt;/td&gt;
99   *     &lt;/s:if&gt;
100  *     &lt;s:else&gt;
101  *       &lt;td&gt;&lt;s:property/&gt;&lt;/td&gt;
102  *     &lt;/s:else&gt;
103  *   &lt;/tr&gt;
104  * &lt;/s:iterator&gt;
105  * &lt;/table&gt;
106  *
107  * <!-- END SNIPPET: example2code -->
108  * </pre>
109  *
110  * <!--START SNIPPET: example3description -->
111  *
112  * <p> The next example will further demonstrate the use of the status attribute, using a DAO obtained from the action
113  * class through OGNL, iterating over groups and their users (in a security context). The last() method indicates if the
114  * current object is the last available in the iteration, and if not, we need to separate the users using a comma: </p>
115  *
116  * <!-- END SNIPPET: example3description -->
117  *
118  * <pre>
119  * <!-- START SNIPPET: example3code -->
120  *
121  *  &lt;s:iterator value="groupDao.groups" status="groupStatus"&gt;
122  *      &lt;tr class="&lt;s:if test="#groupStatus.odd == true "&gt;odd&lt;/s:if&gt;&lt;s:else&gt;even&lt;/s:else&gt;"&gt;
123  *          &lt;td&gt;&lt;s:property value="name" /&gt;&lt;/td&gt;
124  *          &lt;td&gt;&lt;s:property value="description" /&gt;&lt;/td&gt;
125  *          &lt;td&gt;
126  *              &lt;s:iterator value="users" status="userStatus"&gt;
127  *                  &lt;s:property value="fullName" /&gt;&lt;s:if test="!#userStatus.last"&gt;,&lt;/s:if&gt;
128  *              &lt;/s:iterator&gt;
129  *          &lt;/td&gt;
130  *      &lt;/tr&gt;
131  *  &lt;/s:iterator&gt;
132  *
133  * <!-- END SNIPPET: example3code -->
134  * </pre>
135  * <p>
136  *
137  * <!-- START SNIPPET: example4description -->
138  *
139  * </p> The next example iterates over a an action collection and passes every iterator value to another action. The
140  * trick here lies in the use of the '[0]' operator. It takes the current iterator value and passes it on to the edit
141  * action. Using the '[0]' operator has the same effect as using &lt;s:property /&gt;. (The latter, however, does not
142  * work from inside the param tag). </p>
143  *
144  * <!-- END SNIPPET: example4description -->
145  *
146  * <pre>
147  * <!-- START SNIPPET: example4code -->
148  *
149  *      &lt;s:action name="entries" var="entries"/&gt;
150  *      &lt;s:iterator value="#entries.entries" &gt;
151  *          &lt;s:property value="name" /&gt;
152  *          &lt;s:property /&gt;
153  *          &lt;s:push value="..."&gt;
154  *              &lt;s:action name="edit" var="edit" &gt;
155  *                  &lt;s:param name="entry" value="[0]" /&gt;
156  *              &lt;/s:action&gt;
157  *          &lt;/push&gt;
158  *      &lt;/s:iterator&gt;
159  *
160  * <!-- END SNIPPET: example4code -->
161  * </pre>
162  *
163  * <!-- START SNIPPET: example5description -->
164  *
165  * </p>To simulate a simple loop with iterator tag, the following could be done.
166  * It does the loop 5 times.
167  *
168  * <!-- END SNIPPET: example5description -->
169  *
170  * <pre>
171  * <!-- START SNIPPET: example5code -->
172  *
173  * &lt;s:iterator status="stat" value="{1,2,3,4,5}" &gt;
174  *    &lt;!-- grab the index (start with 0 ... ) --&gt;
175  *    &lt;s:property value="#stat.index" /&gt;
176  *
177  *    &lt;!-- grab the top of the stack which should be the --&gt;
178  *    &lt;!-- current iteration value (1, ... 5) --&gt;
179  *    &lt;s:property value="top" /&gt;
180  * &lt;/s:iterator&gt;
181  *
182  * <!-- END SNIPPET: example5code -->
183  * </pre>
184  *
185  * <!-- START SNIPPET: example6description -->
186  *
187  * </p>Another way to create a simple loop, similar to JSTL's 
188  * &lt;c:forEach begin="..." end="..." ...&gt; is to use some 
189  * OGNL magic, which provides some under-the-covers magic to 
190  * make 0-n loops trivial. This example also loops five times.
191  *
192  * <!-- END SNIPPET: example6description -->
193  *
194  * <pre>
195  * <!-- START SNIPPET: example6code -->
196  *
197  * &lt;s:iterator status="stat" value="(5).{ #this }" &gt;
198  *    &lt;s:property value="#stat.count" /&gt; &lt;!-- Note that "count" is 1-based, "index" is 0-based. --&gt;
199  * &lt;/s:iterator&gt;
200  *
201  * <!-- END SNIPPET: example6code -->
202  * </pre>
203  *
204  */
205 @StrutsTag(name="iterator", tldTagClass="org.apache.struts2.views.jsp.IteratorTag", description="Iterate over a iterable value")
206 public class IteratorComponent extends ContextBean {
207     protected Iterator iterator;
208     protected IteratorStatus status;
209     protected Object oldStatus;
210     protected IteratorStatus.StatusState statusState;
211     protected String statusAttr;
212     protected String value;
213 
214     public IteratorComponent(ValueStack stack) {
215         super(stack);
216     }
217 
218     public boolean start(Writer writer) {
219         //Create an iterator status if the status attribute was set.
220         if (statusAttr != null) {
221             statusState = new IteratorStatus.StatusState();
222             status = new IteratorStatus(statusState);
223         }
224 
225         ValueStack stack = getStack();
226 
227         if (value == null) {
228             value = "top";
229         }
230         iterator = MakeIterator.convert(findValue(value));
231 
232         // get the first
233         if ((iterator != null) && iterator.hasNext()) {
234             Object currentValue = iterator.next();
235             stack.push(currentValue);
236 
237             String var = getVar();
238 
239             if ((var != null) && (currentValue != null)) {
240                 //pageContext.setAttribute(id, currentValue);
241                 //pageContext.setAttribute(id, currentValue, PageContext.REQUEST_SCOPE);
242                 putInContext(currentValue);
243             }
244 
245             // Status object
246             if (statusAttr != null) {
247                 statusState.setLast(!iterator.hasNext());
248                 oldStatus = stack.getContext().get(statusAttr);
249                 stack.getContext().put(statusAttr, status);
250             }
251 
252             return true;
253         } else {
254             super.end(writer, "");
255             return false;
256         }
257     }
258 
259     public boolean end(Writer writer, String body) {
260         ValueStack stack = getStack();
261         if (iterator != null) {
262             stack.pop();
263         }
264 
265         if (iterator!=null && iterator.hasNext()) {
266             Object currentValue = iterator.next();
267             stack.push(currentValue);
268 
269             putInContext(currentValue);
270 
271             // Update status
272             if (status != null) {
273                 statusState.next(); // Increase counter
274                 statusState.setLast(!iterator.hasNext());
275             }
276 
277             return true;
278         } else {
279             // Reset status object in case someone else uses the same name in another iterator tag instance
280             if (status != null) {
281                 if (oldStatus == null) {
282                     stack.getContext().put(statusAttr, null);
283                 } else {
284                     stack.getContext().put(statusAttr, oldStatus);
285                 }
286             }
287             super.end(writer, "");
288             return false;
289         }
290     }
291 
292     @StrutsTagAttribute(description="If specified, an instanceof IteratorStatus will be pushed into stack upon each iteration",
293         type="Boolean", defaultValue="false")
294     public void setStatus(String status) {
295         this.statusAttr = status;
296     }
297 
298     @StrutsTagAttribute(description="the iteratable source to iterate over, else an the object itself will be put into a newly created List")
299     public void setValue(String value) {
300         this.value = value;
301     }
302 
303 }