1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 <s:property/> tag prints out the current value of the iterator.</p>
60 *
61 * <!-- END SNIPPET: example1description -->
62 *
63 * <pre>
64 * <!-- START SNIPPET: example1code -->
65 * <s:iterator value="days">
66 * <p>day is: <s:property/></p>
67 * </s:iterator>
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 * <s:bean name="org.apache.struts2.example.IteratorExample" var="it">
86 * <s:param name="day" value="'foo'"/>
87 * <s:param name="day" value="'bar'"/>
88 * </s:bean>
89 * <p/>
90 * <table border="0" cellspacing="0" cellpadding="1">
91 * <tr>
92 * <th>Days of the week</th>
93 * </tr>
94 * <p/>
95 * <s:iterator value="#it.days" status="rowstatus">
96 * <tr>
97 * <s:if test="#rowstatus.odd == true">
98 * <td style="background: grey"><s:property/></td>
99 * </s:if>
100 * <s:else>
101 * <td><s:property/></td>
102 * </s:else>
103 * </tr>
104 * </s:iterator>
105 * </table>
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 * <s:iterator value="groupDao.groups" status="groupStatus">
122 * <tr class="<s:if test="#groupStatus.odd == true ">odd</s:if><s:else>even</s:else>">
123 * <td><s:property value="name" /></td>
124 * <td><s:property value="description" /></td>
125 * <td>
126 * <s:iterator value="users" status="userStatus">
127 * <s:property value="fullName" /><s:if test="!#userStatus.last">,</s:if>
128 * </s:iterator>
129 * </td>
130 * </tr>
131 * </s:iterator>
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 <s:property />. (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 * <s:action name="entries" var="entries"/>
150 * <s:iterator value="#entries.entries" >
151 * <s:property value="name" />
152 * <s:property />
153 * <s:push value="...">
154 * <s:action name="edit" var="edit" >
155 * <s:param name="entry" value="[0]" />
156 * </s:action>
157 * </push>
158 * </s:iterator>
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 * <s:iterator status="stat" value="{1,2,3,4,5}" >
174 * <!-- grab the index (start with 0 ... ) -->
175 * <s:property value="#stat.index" />
176 *
177 * <!-- grab the top of the stack which should be the -->
178 * <!-- current iteration value (1, ... 5) -->
179 * <s:property value="top" />
180 * </s:iterator>
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 * <c:forEach begin="..." end="..." ...> 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 * <s:iterator status="stat" value="(5).{ #this }" >
198 * <s:property value="#stat.count" /> <!-- Note that "count" is 1-based, "index" is 0-based. -->
199 * </s:iterator>
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
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
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
241
242 putInContext(currentValue);
243 }
244
245
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
272 if (status != null) {
273 statusState.next();
274 statusState.setLast(!iterator.hasNext());
275 }
276
277 return true;
278 } else {
279
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 }