All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.struts2.components.IteratorComponent Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.struts2.components;

import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.util.MakeIterator;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import org.apache.struts2.views.jsp.IteratorStatus;

import java.io.Writer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * 
 *
 * 

Iterator will iterate over a value. An iterable value can be any of: java.util.Collection, java.util.Iterator, * java.util.Enumeration, java.util.Map, or an array.

* * * * * *
    * *
  • status (String) - if specified, an instance of IteratorStatus will be pushed into stack upon each iteration
  • * *
  • value (Object) - the source to iterate over, must be iteratable, else the object itself will be put into a * newly created List (see MakeIterator#convert(Object)
  • * *
  • id (String) - if specified the current iteration object will be place with this id in Struts stack's context * scope
  • * *
  • begin (Integer) - if specified the iteration will start on that index
  • * *
  • end (Integer) - if specified the iteration will end on that index(inclusive)
  • * *
  • step (Integer) - if specified the iteration index will be increased by this value on each iteration. It can be a negative * value, in which case 'begin' must be greater than 'end'
  • * *
* * * * * *

The following example retrieves the value of the getDays() method of the current object on the value stack and * uses it to iterate over. The <s:property/> tag prints out the current value of the iterator.

* * * *
 * 
 * <s:iterator value="days">
 *   <p>day is: <s:property/></p>
 * </s:iterator>
 * 
 * 
* * * * *

The following example uses a {@link Bean} tag and places it into the ActionContext. The iterator tag will retrieve * that object from the ActionContext and then calls its getDays() method as above. The status attribute is also used to * create an {@link IteratorStatus} object, which in this example, its odd() method is used to alternate row * colours:

* * * * *
 * 
 *
 * <s:bean name="org.apache.struts2.example.IteratorExample" var="it">
 *   <s:param name="day" value="'foo'"/>
 *   <s:param name="day" value="'bar'"/>
 * </s:bean>
 *
 * <table border="0" cellspacing="0" cellpadding="1">
 * <tr>
 *   <th>Days of the week</th>
 * </tr>
 *
 * <s:iterator value="#it.days" status="rowstatus">
 *   <tr>
 *     <s:if test="#rowstatus.odd == true">
 *       <td style="background: grey"><s:property/></td>
 *     </s:if>
 *     <s:else>
 *       <td><s:property/></td>
 *     </s:else>
 *   </tr>
 * </s:iterator>
 * </table>
 *
 * 
 * 
* * * *

The next example will further demonstrate the use of the status attribute, using a DAO obtained from the action * class through OGNL, iterating over groups and their users (in a security context). The last() method indicates if the * current object is the last available in the iteration, and if not, we need to separate the users using a comma:

* * * *
 * 
 *
 *  <s:iterator value="groupDao.groups" status="groupStatus">
 *      <tr class="<s:if test="#groupStatus.odd == true ">odd</s:if><s:else>even</s:else>">
 *          <td><s:property value="name" /></td>
 *          <td><s:property value="description" /></td>
 *          <td>
 *              <s:iterator value="users" status="userStatus">
 *                  <s:property value="fullName" /><s:if test="!#userStatus.last">,</s:if>
 *              </s:iterator>
 *          </td>
 *      </tr>
 *  </s:iterator>
 *
 * 
 * 
* * * *

The next example iterates over a an action collection and passes every iterator value to another action. The * trick here lies in the use of the '[0]' operator. It takes the current iterator value and passes it on to the edit * action. Using the '[0]' operator has the same effect as using <s:property />. (The latter, however, does not * work from inside the param tag).

* * * *
 * 
 *
 *      <s:action name="entries" var="entries"/>
 *      <s:iterator value="#entries.entries" >
 *          <s:property value="name" />
 *          <s:property />
 *          <s:push value="...">
 *              <s:action name="edit" var="edit" >
 *                  <s:param name="entry" value="[0]" />
 *              </s:action>
 *          </push>
 *      </s:iterator>
 *
 * 
 * 
* * * *

A loop that iterates 5 times

* * * *
 * 
 *
 * <s:iterator var="counter" begin="1" end="5" >
 *    <!-- current iteration value (1, ... 5) -->
 *    <s:property value="top" />
 * </s:iterator>
 *
 * 
 * 
* * * *

Another way to create a simple loop, similar to JSTL's * <c:forEach begin="..." end="..." ...> is to use some * OGNL magic, which provides some under-the-covers magic to * make 0-n loops trivial. This example also loops five times.

* * * *
 * 
 *
 * <s:iterator status="stat" value="(5).{ #this }" >
 *    <s:property value="#stat.count" /> <!-- Note that "count" is 1-based, "index" is 0-based. -->
 * </s:iterator>
 *
 * 
 * 
* * * *

A loop that iterates over a partial list

* * * *
 * 
 *
 * <s:iterator value="{1,2,3,4,5}" begin="2" end="4" >
 *    <!-- current iteration value (2,3,4) -->
 *    <s:property value="top" />
 * </s:iterator>
 *
 * 
 * 
*/ @StrutsTag(name="iterator", tldTagClass="org.apache.struts2.views.jsp.IteratorTag", description="Iterate over a iterable value") public class IteratorComponent extends ContextBean { private static final Logger LOG = LogManager.getLogger(IteratorComponent.class); protected Iterator iterator; protected IteratorStatus status; protected Object oldStatus; protected IteratorStatus.StatusState statusState; protected String statusAttr; protected String value; protected String beginStr; protected Integer begin; protected String endStr; protected Integer end; protected String stepStr; protected Integer step; public IteratorComponent(ValueStack stack) { super(stack); } public boolean start(Writer writer) { //Create an iterator status if the status attribute was set. if (statusAttr != null) { statusState = new IteratorStatus.StatusState(); status = new IteratorStatus(statusState); } if (beginStr != null) begin = (Integer) findValue(beginStr, Integer.class); if (endStr != null) end = (Integer) findValue(endStr, Integer.class); if (stepStr != null) step = (Integer) findValue(stepStr, Integer.class); ValueStack stack = getStack(); if (value == null && begin == null && end == null) { value = "top"; } Object iteratorTarget = findValue(value); iterator = MakeIterator.convert(iteratorTarget); if (begin != null) { //default step to 1 if (step == null) step = 1; if (iterator == null) { //classic for loop from 'begin' to 'end' iterator = new CounterIterator(begin, end, step, null); } else { //only arrays and lists are supported if (iteratorTarget.getClass().isArray()) { Object[] values = (Object[]) iteratorTarget; if (end == null) end = step > 0 ? values.length - 1 : 0; iterator = new CounterIterator(begin, end, step, Arrays.asList(values)); } else if (iteratorTarget instanceof List) { List values = (List) iteratorTarget; if (end == null) end = step > 0 ? values.size() - 1 : 0; iterator = new CounterIterator(begin, end, step, values); } else { //so the iterator is not based on an array or a list LOG.error("Incorrect use of the iterator tag. When 'begin' is set, 'value' must be" + " an Array or a List, or not set at all. 'begin', 'end' and 'step' will be ignored"); } } } // get the first if ((iterator != null) && iterator.hasNext()) { Object currentValue = iterator.next(); stack.push(currentValue); String var = getVar(); if ((var != null)) { putInContext(currentValue); } // Status object if (statusAttr != null) { statusState.setLast(!iterator.hasNext()); oldStatus = stack.getContext().get(statusAttr); stack.getContext().put(statusAttr, status); } return true; } else { super.end(writer, ""); return false; } } public boolean end(Writer writer, String body) { ValueStack stack = getStack(); if (iterator != null) { stack.pop(); } if (iterator!=null && iterator.hasNext()) { Object currentValue = iterator.next(); stack.push(currentValue); putInContext(currentValue); // Update status if (status != null) { statusState.next(); // Increase counter statusState.setLast(!iterator.hasNext()); } return true; } else { // Reset status object in case someone else uses the same name in another iterator tag instance if (status != null) { if (oldStatus == null) { stack.getContext().put(statusAttr, null); } else { stack.getContext().put(statusAttr, oldStatus); } } super.end(writer, ""); return false; } } static class CounterIterator implements Iterator { private int step; private int end; private int currentIndex; private List values; CounterIterator(Integer begin, Integer end, Integer step, List values) { this.end = end; if (step != null) this.step = step; this.currentIndex = begin - this.step; this.values = values; } public boolean hasNext() { int next = peekNextIndex(); return step > 0 ? next <= end : next >= end; } public Object next() { if (hasNext()) { int nextIndex = peekNextIndex(); currentIndex += step; return values != null ? values.get(nextIndex) : nextIndex; } else { throw new IndexOutOfBoundsException("Index " + ( currentIndex + step) + " must be less than or equal to " + end); } } private int peekNextIndex() { return currentIndex + step; } public void remove() { throw new UnsupportedOperationException("Values cannot be removed from this iterator"); } } @StrutsTagAttribute(description="If specified, an instanceof IteratorStatus will be pushed into stack upon each iteration", type="Boolean", defaultValue="false") public void setStatus(String status) { this.statusAttr = status; } @StrutsTagAttribute(description="the iteratable source to iterate over, else an the object itself will be put into a newly created List") public void setValue(String value) { this.value = value; } @StrutsTagAttribute(description="if specified the iteration will start on that index", type="Integer", defaultValue="0") public void setBegin(String begin) { this.beginStr = begin; } @StrutsTagAttribute(description="if specified the iteration will end on that index(inclusive)", type="Integer", defaultValue="Size of the 'values' List or array, or 0 if 'step' is negative") public void setEnd(String end) { this.endStr = end; } @StrutsTagAttribute(description="if specified the iteration index will be increased by this value on each iteration. It can be a negative " + "value, in which case 'begin' must be greater than 'end'", type="Integer", defaultValue="1") public void setStep(String step) { this.stepStr = step; } }