View Javadoc

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