001: package com.opensymphony.webwork.components;
002:
003: import com.opensymphony.webwork.util.MakeIterator;
004: import com.opensymphony.webwork.views.jsp.IteratorStatus;
005: import com.opensymphony.xwork.util.OgnlValueStack;
006:
007: import java.io.Writer;
008: import java.util.Iterator;
009:
010: /**
011: * <!-- START SNIPPET: javadoc -->
012: *
013: * <p>Iterator will iterate over a value. An iterable value can be either of: java.util.Collection, java.util.Iterator,
014: * java.util.Enumeration, java.util.Map, array.</p> <p/> <!-- END SNIPPET: javadoc -->
015: *
016: * <!-- START SNIPPET: params -->
017: *
018: * <ul>
019: *
020: * <li>status (String) - if specified, an instanceof IteratorStatus will be pushed into stack upon each iteration</li>
021: *
022: * <li>value (Object) - the source to iterate over, must be iteratable, else an the object itself will be put into a
023: * newly created List (see MakeIterator#convert(Object)</li>
024: *
025: * <li>id (String) - if specified the current iteration object will be place with this id in webwork stack's context
026: * scope</li>
027: *
028: * </ul>
029: *
030: * <!-- END SNIPPET: params -->
031: *
032: * <!-- START SNIPPET: example1description -->
033: *
034: * <p>The following example retrieves the value of the getDays() method of the current object on the value stack and
035: * uses it to iterate over. The <ww:property/> tag prints out the current value of the iterator.</p>
036: *
037: * <!-- END SNIPPET: example1description -->
038: *
039: * <pre>
040: * <!-- START SNIPPET: example1code -->
041: * <ww:iterator value="days">
042: * <p>day is: <ww:property/></p>
043: * </ww:iterator>
044: * <!-- END SNIPPET: example1code -->
045: * </pre>
046: *
047: *
048: * <!-- START SNIPPET: example2description -->
049: *
050: * <p>The following example uses a {@link Bean} tag and places it into the ActionContext. The iterator tag will retrieve
051: * that object from the ActionContext and then calls its getDays() method as above. The status attribute is also used to
052: * create a {@link IteratorStatus} object, which in this example, its odd() method is used to alternate row
053: * colours:</p>
054: *
055: * <!-- END SNIPPET: example2description -->
056: *
057: *
058: * <pre>
059: * <!-- START SNIPPET: example2code -->
060: *
061: * <ww:bean name="com.opensymphony.webwork.example.IteratorExample" id="it">
062: * <ww:param name="day" value="'foo'"/>
063: * <ww:param name="day" value="'bar'"/>
064: * </ww:bean>
065: * <p/>
066: * <table border="0" cellspacing="0" cellpadding="1">
067: * <tr>
068: * <th>Days of the week</th>
069: * </tr>
070: * <p/>
071: * <ww:iterator value="#it.days" status="rowstatus">
072: * <tr>
073: * <ww:if test="#rowstatus.odd == true">
074: * <td style="background: grey"><ww:property/></td>
075: * </ww:if>
076: * <ww:else>
077: * <td><ww:property/></td>
078: * </ww:else>
079: * </tr>
080: * </ww:iterator>
081: * </table>
082: *
083: * <!-- END SNIPPET: example2code -->
084: * </pre>
085: *
086: * <!--START SNIPPET: example3description -->
087: *
088: * <p> The next example will further demonstrate the use of the status attribute, using a DAO obtained from the action
089: * class through OGNL, iterating over groups and their users (in a security context). The last() method indicates if the
090: * current object is the last available in the iteration, and if not, we need to seperate the users using a comma: </p>
091: *
092: * <!-- END SNIPPET: example3description -->
093: *
094: * <pre>
095: * <!-- START SNIPPET: example3code -->
096: *
097: * <webwork:iterator value="groupDao.groups" status="groupStatus">
098: * <tr class="<webwork:if test="#groupStatus.odd == true ">odd</webwork:if><webwork:else>even</webwork:else>">
099: * <td><webwork:property value="name" /></td>
100: * <td><webwork:property value="description" /></td>
101: * <td>
102: * <webwork:iterator value="users" status="userStatus">
103: * <webwork:property value="fullName" /><webwork:if test="!#userStatus.last">,</webwork:if>
104: * </webwork:iterator>
105: * </td>
106: * </tr>
107: * </webwork:iterator>
108: *
109: * <!-- END SNIPPET: example3code -->
110: * </pre>
111: * <p>
112: *
113: * <!-- START SNIPPET: example4description -->
114: *
115: * </p> The next example iterates over a an action collection and passes every iterator value to another action. The
116: * trick here lies in the use of the '[0]' operator. It takes the current iterator value and passes it on to the edit
117: * action. Using the '[0]' operator has the same effect as using >ww:property />. (The latter, however, does not
118: * work from inside the param tag). </p>
119: *
120: * <!-- END SNIPPET: example4description -->
121: *
122: * <pre>
123: * <!-- START SNIPPET: example4code -->
124: *
125: * <ww:action name="entries" id="entries"/>
126: * <ww:iterator value="#entries.entries" >
127: * <ww:property value="name" />
128: * <ww:property />
129: * <ww:push value="...">
130: * <ww:action name="edit" id="edit" >
131: * <ww:param name="entry" value="[0]" />
132: * </ww:action>
133: * </push>
134: * </ww:iterator>
135: *
136: * <!-- END SNIPPET: example4code -->
137: * </pre>
138: *
139: * <!-- START SNIPPET: example5description -->
140: *
141: * </p>To simulate a simple loop with iterator tag, the following could be done. It does the loop 5 times.
142: *
143: * <!-- END SNIPPET: example5description -->
144: *
145: * <pre>
146: * <!-- START SNIPPET: example5code -->
147: *
148: * <ww:iterator status="stat" value="{1,2,3,4,5}" >
149: * <!-- grab the index (start with 0 ... ) -->
150: * <ww:property value="#stat.index" />
151: *
152: * <!-- grab the top of the stack which should be the -->
153: * <!-- current iteration value (0, 1, ... 5) -->
154: * <ww:property value="top" />
155: * </ww:iterator>
156: *
157: * <!-- END SNIPPET: example5code -->
158: * </pre>
159: *
160: *
161: * @author $Author: tmjee $
162: * @author Rick Salsa (rsal@mb.sympatico.ca)
163: * @author tm_jee ( tm_jee(at)yahoo.co.uk )
164: * @version $Revision: 2573 $
165: * @ww.tag name="iterator" tld-body-content="JSP" tld-tag-class="com.opensymphony.webwork.views.jsp.IteratorTag"
166: * description="Iterate over a iterable value"
167: */
168: public class IteratorComponent extends Component {
169: protected Iterator iterator;
170: protected IteratorStatus status;
171: protected Object oldStatus;
172: protected IteratorStatus.StatusState statusState;
173: protected String statusAttr;
174: protected String value;
175:
176: public IteratorComponent(OgnlValueStack stack) {
177: super (stack);
178: }
179:
180: public boolean start(Writer writer) {
181: //Create an iterator status if the status attribute was set.
182: if (statusAttr != null) {
183: statusState = new IteratorStatus.StatusState();
184: status = new IteratorStatus(statusState);
185: }
186:
187: OgnlValueStack stack = getStack();
188:
189: if (value == null) {
190: value = "top";
191: }
192: iterator = MakeIterator.convert(findValue(value));
193:
194: // get the first
195: if ((iterator != null) && iterator.hasNext()) {
196: Object currentValue = iterator.next();
197: stack.push(currentValue);
198:
199: String id = getId();
200:
201: if ((id != null) && (currentValue != null)) {
202: //pageContext.setAttribute(id, currentValue);
203: //pageContext.setAttribute(id, currentValue, PageContext.REQUEST_SCOPE);
204: stack.getContext().put(id, currentValue);
205: }
206:
207: // Status object
208: if (statusAttr != null) {
209: statusState.setLast(!iterator.hasNext());
210: oldStatus = stack.getContext().get(statusAttr);
211: stack.getContext().put(statusAttr, status);
212: }
213:
214: return true;
215: } else {
216: super .end(writer, "");
217: return false;
218: }
219: }
220:
221: public boolean end(Writer writer, String body) {
222: OgnlValueStack stack = getStack();
223: if (iterator != null) {
224: stack.pop();
225: }
226:
227: if (iterator != null && iterator.hasNext()) {
228: Object currentValue = iterator.next();
229: stack.push(currentValue);
230:
231: String id = getId();
232:
233: if ((id != null) && (currentValue != null)) {
234: //pageContext.setAttribute(id, currentValue);
235: //pageContext.setAttribute(id, currentValue, PageContext.REQUEST_SCOPE);
236: stack.getContext().put(id, currentValue);
237: }
238:
239: // Update status
240: if (status != null) {
241: statusState.next(); // Increase counter
242: statusState.setLast(!iterator.hasNext());
243: }
244:
245: return true;
246: } else {
247: // Reset status object in case someone else uses the same name in another iterator tag instance
248: if (status != null) {
249: if (oldStatus == null) {
250: stack.getContext().put(statusAttr, null);
251: } else {
252: stack.getContext().put(statusAttr, oldStatus);
253: }
254: }
255: super .end(writer, "");
256: return false;
257: }
258: }
259:
260: /**
261: * if specified, an instanceof IteratorStatus will be pushed into stack upon each iteration
262: * @ww.tagattribute required="false" type="Boolean" default="false"
263: */
264: public void setStatus(String status) {
265: this .statusAttr = status;
266: }
267:
268: /**
269: * the iteratable source to iterate over, else an the object itself will be put into a newly created List
270: * @ww.tagattribute required="false"
271: */
272: public void setValue(String value) {
273: this.value = value;
274: }
275:
276: }
|