Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » components » [javadoc | source]
    1   /*
    2    * $Id: IteratorComponent.java 741183 2009-02-05 17:12:08Z musachy $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
   10    * with the License.  You may obtain a copy of the License at
   11    *
   12    *  http://www.apache.org/licenses/LICENSE-2.0
   13    *
   14    * Unless required by applicable law or agreed to in writing,
   15    * software distributed under the License is distributed on an
   16    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   17    * KIND, either express or implied.  See the License for the
   18    * specific language governing permissions and limitations
   19    * under the License.
   20    */
   21   
   22   package org.apache.struts2.components;
   23   
   24   import java.io.Writer;
   25   import java.util.Iterator;
   26   import java.util.List;
   27   import java.util.Arrays;
   28   
   29   import org.apache.struts2.views.annotations.StrutsTag;
   30   import org.apache.struts2.views.annotations.StrutsTagAttribute;
   31   import org.apache.struts2.util.MakeIterator;
   32   import org.apache.struts2.views.jsp.IteratorStatus;
   33   
   34   import com.opensymphony.xwork2.util.ValueStack;
   35   import com.opensymphony.xwork2.util.logging.Logger;
   36   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   37   
   38   /**
   39    * <!-- START SNIPPET: javadoc -->
   40    *
   41    * <p>Iterator will iterate over a value. An iterable value can be any of: java.util.Collection, java.util.Iterator,
   42    * java.util.Enumeration, java.util.Map, or an array.</p> <p/> <!-- END SNIPPET: javadoc -->
   43    *
   44    * <!-- START SNIPPET: params -->
   45    *
   46    * <ul>
   47    *
   48    * <li>status (String) - if specified, an instance of IteratorStatus will be pushed into stack upon each iteration</li>
   49    *
   50    * <li>value (Object) - the source to iterate over, must be iteratable, else the object itself will be put into a
   51    * newly created List (see MakeIterator#convert(Object)</li>
   52    *
   53    * <li>id (String) - if specified the current iteration object will be place with this id in Struts stack's context
   54    * scope</li>
   55    *
   56    * <li>begin (Integer) - if specified the iteration will start on that index</li>
   57    *
   58    * <li>end (Integer) - if specified the iteration will end on that index(inclusive)</li>
   59    *
   60    * <li>step (Integer) - if specified the iteration index will be increased by this value on each iteration. It can be a negative
   61    * value, in which case 'begin' must be greater than 'end'</li>
   62    *
   63    * </ul>
   64    *
   65    * <!-- END SNIPPET: params -->
   66    *
   67    * <!-- START SNIPPET: example1description -->
   68    *
   69    * <p>The following example retrieves the value of the getDays() method of the current object on the value stack and
   70    * uses it to iterate over. The &lt;s:property/&gt; tag prints out the current value of the iterator.</p>
   71    *
   72    * <!-- END SNIPPET: example1description -->
   73    *
   74    * <pre>
   75    * <!-- START SNIPPET: example1code -->
   76    * &lt;s:iterator value="days"&gt;
   77    *   &lt;p&gt;day is: &lt;s:property/&gt;&lt;/p&gt;
   78    * &lt;/s:iterator&gt;
   79    * <!-- END SNIPPET: example1code -->
   80    * </pre>
   81    *
   82    *
   83    * <!-- START SNIPPET: example2description -->
   84    *
   85    * <p>The following example uses a {@link Bean} tag and places it into the ActionContext. The iterator tag will retrieve
   86    * that object from the ActionContext and then calls its getDays() method as above. The status attribute is also used to
   87    * create an {@link IteratorStatus} object, which in this example, its odd() method is used to alternate row
   88    * colours:</p>
   89    *
   90    * <!-- END SNIPPET: example2description -->
   91    *
   92    *
   93    * <pre>
   94    * <!-- START SNIPPET: example2code -->
   95    *
   96    * &lt;s:bean name="org.apache.struts2.example.IteratorExample" var="it"&gt;
   97    *   &lt;s:param name="day" value="'foo'"/&gt;
   98    *   &lt;s:param name="day" value="'bar'"/&gt;
   99    * &lt;/s:bean&gt;
  100    * <p/>
  101    * &lt;table border="0" cellspacing="0" cellpadding="1"&gt;
  102    * &lt;tr&gt;
  103    *   &lt;th&gt;Days of the week&lt;/th&gt;
  104    * &lt;/tr&gt;
  105    * <p/>
  106    * &lt;s:iterator value="#it.days" status="rowstatus"&gt;
  107    *   &lt;tr&gt;
  108    *     &lt;s:if test="#rowstatus.odd == true"&gt;
  109    *       &lt;td style="background: grey"&gt;&lt;s:property/&gt;&lt;/td&gt;
  110    *     &lt;/s:if&gt;
  111    *     &lt;s:else&gt;
  112    *       &lt;td&gt;&lt;s:property/&gt;&lt;/td&gt;
  113    *     &lt;/s:else&gt;
  114    *   &lt;/tr&gt;
  115    * &lt;/s:iterator&gt;
  116    * &lt;/table&gt;
  117    *
  118    * <!-- END SNIPPET: example2code -->
  119    * </pre>
  120    *
  121    * <!--START SNIPPET: example3description -->
  122    *
  123    * <p> The next example will further demonstrate the use of the status attribute, using a DAO obtained from the action
  124    * class through OGNL, iterating over groups and their users (in a security context). The last() method indicates if the
  125    * current object is the last available in the iteration, and if not, we need to separate the users using a comma: </p>
  126    *
  127    * <!-- END SNIPPET: example3description -->
  128    *
  129    * <pre>
  130    * <!-- START SNIPPET: example3code -->
  131    *
  132    *  &lt;s:iterator value="groupDao.groups" status="groupStatus"&gt;
  133    *      &lt;tr class="&lt;s:if test="#groupStatus.odd == true "&gt;odd&lt;/s:if&gt;&lt;s:else&gt;even&lt;/s:else&gt;"&gt;
  134    *          &lt;td&gt;&lt;s:property value="name" /&gt;&lt;/td&gt;
  135    *          &lt;td&gt;&lt;s:property value="description" /&gt;&lt;/td&gt;
  136    *          &lt;td&gt;
  137    *              &lt;s:iterator value="users" status="userStatus"&gt;
  138    *                  &lt;s:property value="fullName" /&gt;&lt;s:if test="!#userStatus.last"&gt;,&lt;/s:if&gt;
  139    *              &lt;/s:iterator&gt;
  140    *          &lt;/td&gt;
  141    *      &lt;/tr&gt;
  142    *  &lt;/s:iterator&gt;
  143    *
  144    * <!-- END SNIPPET: example3code -->
  145    * </pre>
  146    * <p>
  147    *
  148    * <!-- START SNIPPET: example4description -->
  149    *
  150    * </p> The next example iterates over a an action collection and passes every iterator value to another action. The
  151    * trick here lies in the use of the '[0]' operator. It takes the current iterator value and passes it on to the edit
  152    * action. Using the '[0]' operator has the same effect as using &lt;s:property /&gt;. (The latter, however, does not
  153    * work from inside the param tag). </p>
  154    *
  155    * <!-- END SNIPPET: example4description -->
  156    *
  157    * <pre>
  158    * <!-- START SNIPPET: example4code -->
  159    *
  160    *      &lt;s:action name="entries" var="entries"/&gt;
  161    *      &lt;s:iterator value="#entries.entries" &gt;
  162    *          &lt;s:property value="name" /&gt;
  163    *          &lt;s:property /&gt;
  164    *          &lt;s:push value="..."&gt;
  165    *              &lt;s:action name="edit" var="edit" &gt;
  166    *                  &lt;s:param name="entry" value="[0]" /&gt;
  167    *              &lt;/s:action&gt;
  168    *          &lt;/push&gt;
  169    *      &lt;/s:iterator&gt;
  170    *
  171    * <!-- END SNIPPET: example4code -->
  172    * </pre>
  173    *
  174    * <!-- START SNIPPET: example5description -->
  175    *
  176    * </p>A loop that iterates 5 times
  177    *
  178    * <!-- END SNIPPET: example5description -->
  179    *
  180    * <pre>
  181    * <!-- START SNIPPET: example5code -->
  182    *
  183    * &lt;s:iterator var="counter" begin="1" end="5" &gt;
  184    *    &lt;!-- current iteration value (1, ... 5) --&gt;
  185    *    &lt;s:property value="top" /&gt;
  186    * &lt;/s:iterator&gt;
  187    *
  188    * <!-- END SNIPPET: example5code -->
  189    * </pre>
  190    *
  191    * <!-- START SNIPPET: example6description -->
  192    *
  193    * </p>Another way to create a simple loop, similar to JSTL's 
  194    * &lt;c:forEach begin="..." end="..." ...&gt; is to use some 
  195    * OGNL magic, which provides some under-the-covers magic to 
  196    * make 0-n loops trivial. This example also loops five times.
  197    *
  198    * <!-- END SNIPPET: example6description -->
  199    *
  200    * <pre>
  201    * <!-- START SNIPPET: example6code -->
  202    *
  203    * &lt;s:iterator status="stat" value="(5).{ #this }" &gt;
  204    *    &lt;s:property value="#stat.count" /&gt; &lt;!-- Note that "count" is 1-based, "index" is 0-based. --&gt;
  205    * &lt;/s:iterator&gt;
  206    *
  207    * <!-- END SNIPPET: example6code -->
  208    * </pre>
  209    *
  210    *  <!-- START SNIPPET: example7description -->
  211    *
  212    * </p>A loop that iterates over a partial list
  213    *
  214    * <!-- END SNIPPET: example7description -->
  215    *
  216    * <pre>
  217    * <!-- START SNIPPET: example7code -->
  218    *
  219    * &lt;s:iterator value="{1,2,3,4,5}" begin="2" end="4" &gt;
  220    *    &lt;!-- current iteration value (2,3,4) --&gt;
  221    *    &lt;s:property value="top" /&gt;
  222    * &lt;/s:iterator&gt;
  223    *
  224    * <!-- END SNIPPET: example7code -->
  225    * </pre>
  226    */
  227   @StrutsTag(name="iterator", tldTagClass="org.apache.struts2.views.jsp.IteratorTag", description="Iterate over a iterable value")
  228   public class IteratorComponent extends ContextBean {
  229       private static final Logger LOG = LoggerFactory.getLogger(IteratorComponent.class);
  230   
  231       protected Iterator iterator;
  232       protected IteratorStatus status;
  233       protected Object oldStatus;
  234       protected IteratorStatus.StatusState statusState;
  235       protected String statusAttr;
  236       protected String value;
  237       protected String beginStr;
  238       protected Integer begin;
  239       protected String endStr;
  240       protected Integer end;
  241       protected String stepStr;
  242       protected Integer step;
  243   
  244       public IteratorComponent(ValueStack stack) {
  245           super(stack);
  246       }
  247   
  248       public boolean start(Writer writer) {
  249           //Create an iterator status if the status attribute was set.
  250           if (statusAttr != null) {
  251               statusState = new IteratorStatus.StatusState();
  252               status = new IteratorStatus(statusState);
  253           }
  254   
  255           if (beginStr != null)
  256               begin = (Integer) findValue(beginStr,  Integer.class);
  257   
  258           if (endStr != null)
  259               end = (Integer) findValue(endStr,  Integer.class);
  260   
  261           if (stepStr != null)
  262               step = (Integer) findValue(stepStr,  Integer.class);
  263   
  264           ValueStack stack = getStack();
  265   
  266           if (value == null && begin == null && end == null) {
  267               value = "top";
  268           }
  269           Object iteratorTarget = findValue(value);
  270           iterator = MakeIterator.convert(iteratorTarget);
  271   
  272           if (begin != null) {
  273               //default step to 1
  274               if (step == null)
  275                   step = 1;
  276   
  277               if (iterator == null) {
  278                   //classic for loop from 'begin' to 'end'
  279                   iterator = new CounterIterator(begin, end, step, null);
  280               } else if (iterator != null) {
  281                   //only arrays and lists are supported
  282                   if (iteratorTarget.getClass().isArray()) {
  283                       Object[] values = (Object[]) iteratorTarget;
  284                       if (end == null)
  285                           end = step > 0 ? values.length - 1 : 0;
  286                       iterator = new CounterIterator(begin, end, step, Arrays.asList(values));
  287                   } else if (iteratorTarget instanceof List) {
  288                       List values = (List) iteratorTarget;
  289                       if (end == null)
  290                           end = step > 0 ? values.size() - 1 : 0;
  291                       iterator = new CounterIterator(begin, end, step, values);
  292                   } else {
  293                       //so the iterator is not based on an array or a list
  294                       LOG.error("Incorrect use of the iterator tag. When 'begin' is set, 'value' must be" +
  295                               " an Array or a List, or not set at all. 'begin', 'end' and 'step' will be ignored");
  296                   }
  297               }
  298           }
  299   
  300           // get the first
  301           if ((iterator != null) && iterator.hasNext()) {
  302               Object currentValue = iterator.next();
  303               stack.push(currentValue);
  304   
  305               String var = getVar();
  306   
  307               if ((var != null) && (currentValue != null)) {
  308                   //pageContext.setAttribute(id, currentValue);
  309                   //pageContext.setAttribute(id, currentValue, PageContext.REQUEST_SCOPE);
  310                   putInContext(currentValue);
  311               }
  312   
  313               // Status object
  314               if (statusAttr != null) {
  315                   statusState.setLast(!iterator.hasNext());
  316                   oldStatus = stack.getContext().get(statusAttr);
  317                   stack.getContext().put(statusAttr, status);
  318               }
  319   
  320               return true;
  321           } else {
  322               super.end(writer, "");
  323               return false;
  324           }
  325       }
  326   
  327       public boolean end(Writer writer, String body) {
  328           ValueStack stack = getStack();
  329           if (iterator != null) {
  330               stack.pop();
  331           }
  332   
  333           if (iterator!=null && iterator.hasNext()) {
  334               Object currentValue = iterator.next();
  335               stack.push(currentValue);
  336   
  337               putInContext(currentValue);
  338   
  339               // Update status
  340               if (status != null) {
  341                   statusState.next(); // Increase counter
  342                   statusState.setLast(!iterator.hasNext());
  343               }
  344   
  345               return true;
  346           } else {
  347               // Reset status object in case someone else uses the same name in another iterator tag instance
  348               if (status != null) {
  349                   if (oldStatus == null) {
  350                       stack.getContext().put(statusAttr, null);
  351                   } else {
  352                       stack.getContext().put(statusAttr, oldStatus);
  353                   }
  354               }
  355               super.end(writer, "");
  356               return false;
  357           }
  358       }
  359   
  360       class CounterIterator implements Iterator<Object> {
  361           private int step;
  362           private int end;
  363           private int currentIndex;
  364           private List<Object> values;
  365   
  366           CounterIterator(Integer begin, Integer end, Integer step, List<Object> values) {
  367               this.end = end;
  368               if (step != null)
  369                   this.step = step;
  370               this.currentIndex = begin - this.step;
  371               this.values = values;
  372           }
  373   
  374           public boolean hasNext() {
  375               int next = peekNextIndex();
  376               return step > 0 ? next <= end : next >= end;
  377           }
  378   
  379           public Object next() {
  380               if (hasNext()) {
  381                   int nextIndex = peekNextIndex();
  382                   currentIndex += step;
  383                   return value != null ? values.get(nextIndex) : nextIndex;
  384               } else {
  385                   throw new IndexOutOfBoundsException("Index " + ( currentIndex + step) + " must be less than or equal to " + end);
  386               }
  387           }
  388   
  389           private int peekNextIndex() {
  390              return currentIndex + step;
  391           }
  392   
  393           public void remove() {
  394               throw new UnsupportedOperationException("Values cannot be removed from this iterator");
  395           }
  396       }
  397   
  398       @StrutsTagAttribute(description="If specified, an instanceof IteratorStatus will be pushed into stack upon each iteration",
  399           type="Boolean", defaultValue="false")
  400       public void setStatus(String status) {
  401           this.statusAttr = status;
  402       }
  403   
  404       @StrutsTagAttribute(description="the iteratable source to iterate over, else an the object itself will be put into a newly created List")
  405       public void setValue(String value) {
  406           this.value = value;
  407       }
  408   
  409       @StrutsTagAttribute(description="if specified the iteration will start on that index", type="Integer", defaultValue="0")
  410       public void setBegin(String begin) {
  411           this.beginStr = begin;
  412       }
  413   
  414       @StrutsTagAttribute(description="if specified the iteration will end on that index(inclusive)", type="Integer",
  415               defaultValue="Size of the 'values' List or array, or 0 if 'step' is negative")
  416       public void setEnd(String end) {
  417           this.endStr = end;
  418       }
  419   
  420       @StrutsTagAttribute(description="if specified the iteration index will be increased by this value on each iteration. It can be a negative " +
  421               "value, in which case 'begin' must be greater than 'end'", type="Integer", defaultValue="1")
  422       public void setStep(String step) {
  423           this.stepStr = step;
  424       }
  425   }

Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » components » [javadoc | source]