1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 <s:property/> tag prints out the current value of the iterator.</p>
54 *
55 * <!-- END SNIPPET: example1description -->
56 *
57 * <pre>
58 * <!-- START SNIPPET: example1code -->
59 * <s:iterator value="days">
60 * <p>day is: <s:property/></p>
61 * </s:iterator>
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 * <s:bean name="org.apache.struts2.example.IteratorExample" id="it">
80 * <s:param name="day" value="'foo'"/>
81 * <s:param name="day" value="'bar'"/>
82 * </s:bean>
83 * <p/>
84 * <table border="0" cellspacing="0" cellpadding="1">
85 * <tr>
86 * <th>Days of the week</th>
87 * </tr>
88 * <p/>
89 * <s:iterator value="#it.days" status="rowstatus">
90 * <tr>
91 * <s:if test="#rowstatus.odd == true">
92 * <td style="background: grey"><s:property/></td>
93 * </s:if>
94 * <s:else>
95 * <td><s:property/></td>
96 * </s:else>
97 * </tr>
98 * </s:iterator>
99 * </table>
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 * <s:iterator value="groupDao.groups" status="groupStatus">
116 * <tr class="<s:if test="#groupStatus.odd == true ">odd</s:if><s:else>even</s:else>">
117 * <td><s:property value="name" /></td>
118 * <td><s:property value="description" /></td>
119 * <td>
120 * <s:iterator value="users" status="userStatus">
121 * <s:property value="fullName" /><s:if test="!#userStatus.last">,</s:if>
122 * </s:iterator>
123 * </td>
124 * </tr>
125 * </s:iterator>
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 >s:property />. (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 * <s:action name="entries" id="entries"/>
144 * <s:iterator value="#entries.entries" >
145 * <s:property value="name" />
146 * <s:property />
147 * <s:push value="...">
148 * <s:action name="edit" id="edit" >
149 * <s:param name="entry" value="[0]" />
150 * </s:action>
151 * </push>
152 * </s:iterator>
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 * <s:iterator status="stat" value="{1,2,3,4,5}" >
168 * <!-- grab the index (start with 0 ... ) -->
169 * <s:property value="#stat.index" />
170 *
171 * <!-- grab the top of the stack which should be the -->
172 * <!-- current iteration value (0, 1, ... 5) -->
173 * <s:property value="top" />
174 * </s:iterator>
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
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
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
217
218 stack.getContext().put(id, currentValue);
219 }
220
221
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
249
250 stack.getContext().put(id, currentValue);
251 }
252
253
254 if (status != null) {
255 statusState.next();
256 statusState.setLast(!iterator.hasNext());
257 }
258
259 return true;
260 } else {
261
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 }