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 either of: java.util.Collection, java.util.Iterator,
38 * java.util.Enumeration, java.util.Map, array.</p> <p/> <!-- END SNIPPET: javadoc -->
39 *
40 * <!-- START SNIPPET: params -->
41 *
42 * <ul>
43 *
44 * <li>status (String) - if specified, an instanceof IteratorStatus will be pushed into stack upon each iteration</li>
45 *
46 * <li>value (Object) - the source to iterate over, must be iteratable, else an 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 a {@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 seperate 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 (0, 1, ... 5) -->
179 * <s:property value="top" />
180 * </s:iterator>
181 *
182 * <!-- END SNIPPET: example5code -->
183 * </pre>
184 *
185 */
186 @StrutsTag(name="iterator", tldTagClass="org.apache.struts2.views.jsp.IteratorTag", description="Iterate over a iterable value")
187 public class IteratorComponent extends ContextBean {
188 protected Iterator iterator;
189 protected IteratorStatus status;
190 protected Object oldStatus;
191 protected IteratorStatus.StatusState statusState;
192 protected String statusAttr;
193 protected String value;
194
195 public IteratorComponent(ValueStack stack) {
196 super(stack);
197 }
198
199 public boolean start(Writer writer) {
200
201 if (statusAttr != null) {
202 statusState = new IteratorStatus.StatusState();
203 status = new IteratorStatus(statusState);
204 }
205
206 ValueStack stack = getStack();
207
208 if (value == null) {
209 value = "top";
210 }
211 iterator = MakeIterator.convert(findValue(value));
212
213
214 if ((iterator != null) && iterator.hasNext()) {
215 Object currentValue = iterator.next();
216 stack.push(currentValue);
217
218 String var = getVar();
219
220 if ((var != null) && (currentValue != null)) {
221
222
223 putInContext(currentValue);
224 }
225
226
227 if (statusAttr != null) {
228 statusState.setLast(!iterator.hasNext());
229 oldStatus = stack.getContext().get(statusAttr);
230 stack.getContext().put(statusAttr, status);
231 }
232
233 return true;
234 } else {
235 super.end(writer, "");
236 return false;
237 }
238 }
239
240 public boolean end(Writer writer, String body) {
241 ValueStack stack = getStack();
242 if (iterator != null) {
243 stack.pop();
244 }
245
246 if (iterator!=null && iterator.hasNext()) {
247 Object currentValue = iterator.next();
248 stack.push(currentValue);
249
250 putInContext(currentValue);
251
252
253 if (status != null) {
254 statusState.next();
255 statusState.setLast(!iterator.hasNext());
256 }
257
258 return true;
259 } else {
260
261 if (status != null) {
262 if (oldStatus == null) {
263 stack.getContext().put(statusAttr, null);
264 } else {
265 stack.getContext().put(statusAttr, oldStatus);
266 }
267 }
268 super.end(writer, "");
269 return false;
270 }
271 }
272
273 @StrutsTagAttribute(description="If specified, an instanceof IteratorStatus will be pushed into stack upon each iteration",
274 type="Boolean", defaultValue="false")
275 public void setStatus(String status) {
276 this.statusAttr = status;
277 }
278
279 @StrutsTagAttribute(description="the iteratable source to iterate over, else an the object itself will be put into a newly created List")
280 public void setValue(String value) {
281 this.value = value;
282 }
283
284 }