View Javadoc

    1   /*
    2    * Copyright 2002,2004 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16
    17  package org.apache.commons.jelly.tags.core;
    18
    19  import java.util.Iterator;
    20
    21  import javax.servlet.jsp.jstl.core.LoopTagStatus;
    22
    23  import org.apache.commons.jelly.JellyTagException;
    24  import org.apache.commons.jelly.MissingAttributeException;
    25  import org.apache.commons.jelly.TagSupport;
    26  import org.apache.commons.jelly.XMLOutput;
    27  import org.apache.commons.jelly.expression.Expression;
    28  import org.apache.commons.jelly.impl.BreakException;
    29  import org.apache.commons.logging.Log;
    30  import org.apache.commons.logging.LogFactory;
    31
    32  /***
    33    * Iterates over a collection, iterator or an array of objects.
    34    * Uses the same syntax as the <a href="http://java.sun.com/products/jsp/jstl/">JSTL</a>
    35    * <code>forEach</code> tag does.
    36    *
    37    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
    38    * @version $Revision: 155420 $
    39    */
    40  public class ForEachTag extends TagSupport {
    41
    42      /*** The Log to which logging calls will be made. */
    43      private static final Log log = LogFactory.getLog(ForEachTag.class);
    44
    45      /*** Holds the variable name to export for the item being iterated over. */
    46      private Expression items;
    47
    48      /***
    49       * If specified then the current item iterated through will be defined
    50       * as the given variable name.
    51       */
    52      private String var;
    53
    54      /***
    55       * If specified then the current index counter will be defined
    56       * as the given variable name.
    57       */
    58      private String indexVar;
    59
    60      /*** variable to hold loop status */
    61      private String statusVar;
    62
    63      /*** The starting index value */
    64      private int begin;
    65
    66      /*** The ending index value */
    67      private int end = Integer.MAX_VALUE;
    68
    69      /*** The index increment step */
    70      private int step = 1;
    71
    72      /*** The iteration index */
    73      private int index;
    74
    75      public ForEachTag() {
    76      }
    77
    78      // Tag interface
    79
    80      //-------------------------------------------------------------------------
    81      public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException {
    82
    83          if (log.isDebugEnabled()) {
    84              log.debug("running with items: " + items);
    85          }
    86
    87          try {
    88              if (items != null) {
    89                  Iterator iter = items.evaluateAsIterator(context);
    90
    91                  if (log.isDebugEnabled()) {
    92                      log.debug("Iterating through: " + iter);
    93                  }
    94
    95                  // ignore the first items of the iterator
    96                  for (index = 0; index < begin && iter.hasNext(); index++ ) {
    97                      iter.next();
    98                  }
    99
    100                 // set up the status
    101                 LoopStatus status = null;
    102                 if (statusVar != null)
    103                 {
    104                     // set up statii as required by JSTL
    105                     Integer statusBegin = (begin == 0) ? null : new Integer(begin);
    106                     Integer statusEnd = (end == Integer.MAX_VALUE) ? null : new Integer(end);
    107                     Integer statusStep = (step == 1) ? null : new Integer(step);
    108                     status = new LoopStatus(statusBegin, statusEnd, statusStep);
    109                     context.setVariable(statusVar, status);
    110                 }
    111
    112                 boolean firstTime = true;
    113                 int count = 0;
    114                 while (iter.hasNext() && index <= end) {
    115                     Object value = iter.next();
    116                     if (var != null) {
    117                         context.setVariable(var, value);
    118                     }
    119                     if (indexVar != null) {
    120                         context.setVariable(indexVar, new Integer(index));
    121                     }
    122                     // set the status var up
    123                     if (statusVar != null) {
    124                         count++;
    125                         status.setCount(count);
    126                         status.setCurrent(value);
    127                         status.setFirst(firstTime);
    128                         status.setIndex(index);
    129                         // set first time up for the next iteration.
    130                         if (firstTime) {
    131                             firstTime = !firstTime;
    132                         }
    133                     }
    134                     // now we need to work out the next index for status isLast
    135                     // and also advance the iterator and index for the loop.
    136                     boolean finished = false;
    137                     index++;
    138                     for ( int i = 1; i < step && !finished; i++, index++ ) {
    139                         if ( ! iter.hasNext() ) {
    140                            finished = true;
    141                         }
    142                         else {
    143                             iter.next();
    144                         }
    145                     }
    146
    147                     if (statusVar != null) {
    148                         status.setLast(finished || !iter.hasNext() || index > end);
    149                     }
    150                     invokeBody(output);
    151
    152                 }
    153             }
    154             else {
    155                 if ( end == Integer.MAX_VALUE && begin == 0 ) {
    156                     throw new MissingAttributeException( "items" );
    157                 }
    158                 else {
    159                     String varName = var;
    160                     if ( varName == null ) {
    161                         varName = indexVar;
    162                     }
    163                     // set up the status
    164                     LoopStatus status = null;
    165                     if (statusVar != null)
    166                     {
    167                         // set up statii as required by JSTL
    168                         Integer statusBegin = new Integer(begin);
    169                         Integer statusEnd = new Integer(end);
    170                         Integer statusStep = new Integer(step);
    171                         status = new LoopStatus(statusBegin, statusEnd, statusStep);
    172                         context.setVariable(statusVar, status);
    173                     }
    174
    175                     int count = 0;
    176                     for (index = begin; index <= end; index += step ) {
    177
    178                         Object value = new Integer(index);
    179                         if (varName != null) {
    180                             context.setVariable(varName, value);
    181                         }
    182                         // set the status var up
    183                         if (status != null) {
    184                             count++;
    185                             status.setIndex(index);
    186                             status.setCount(count);
    187                             status.setCurrent(value);
    188                             status.setFirst(index == begin);
    189                             status.setLast(index > end - step);
    190                         }
    191                         invokeBody(output);
    192                     }
    193                 }
    194             }
    195         }
    196         catch (BreakException e) {
    197             if (log.isDebugEnabled()) {
    198                 log.debug("loop terminated by break: " + e, e);
    199             }
    200         }
    201     }
    202
    203     // Properties
    204     //-------------------------------------------------------------------------
    205
    206     /***
    207      * Sets the expression used to iterate over.
    208      * This expression could resolve to an Iterator, Collection, Map, Array,
    209      * Enumeration or comma separated String.
    210      */
    211     public void setItems(Expression items) {
    212         this.items = items;
    213     }
    214
    215     /*** Sets the variable name to export for the item being iterated over
    216      */
    217     public void setVar(String var) {
    218         this.var = var;
    219     }
    220
    221     /*** Sets the variable name to export the current index counter to
    222      */
    223     public void setIndexVar(String indexVar) {
    224         this.indexVar = indexVar;
    225     }
    226
    227     /*** Sets the starting index value
    228      */
    229     public void setBegin(int begin) {
    230         this.begin = begin;
    231     }
    232
    233     /*** Sets the ending index value
    234      */
    235     public void setEnd(int end) {
    236         this.end = end;
    237     }
    238
    239     /*** Sets the index increment step
    240      */
    241     public void setStep(int step) {
    242         this.step = step;
    243     }
    244
    245     /***
    246      * Sets the variable name to export the current status to.
    247      * The status is an implementation of the JSTL LoopTagStatus interface that provides
    248      * the following bean properties:
    249      * <ul>
    250      *   <li>current - the current value of the loop items being iterated</li>
    251      *   <li>index   - the current index of the items being iterated</li>
    252      *   <li>first   - true if this is the first iteration, false otherwise</li>
    253      *   <li>last    - true if this is the last iteration, false otherwise</li>
    254      *   <li>begin   - the starting index of the loop</li>
    255      *   <li>step    - the stepping value of the loop</li>
    256      *   <li>end     - the end index of the loop</li>
    257      * </ul>
    258      */
    259     public void setVarStatus(String var) {
    260         this.statusVar = var;
    261     }
    262
    263     /***
    264      * Holds the status of the loop.
    265      */
    266     public static final class LoopStatus implements LoopTagStatus
    267     {
    268         private Integer begin;
    269         private int count;
    270         private Object current;
    271         private Integer end;
    272         private int index;
    273         private Integer step;
    274         private boolean first;
    275         private boolean last;
    276
    277         public LoopStatus(Integer begin, Integer end, Integer step) {
    278             this.begin = begin;
    279             this.end = end;
    280             this.step = step;
    281         }
    282         /***
    283          * @return Returns the begin.
    284          */
    285         public Integer getBegin() {
    286             return begin;
    287         }
    288         /***
    289          * @return Returns the count.
    290          */
    291         public int getCount() {
    292             return count;
    293         }
    294         /***
    295          * @return Returns the current.
    296          */
    297         public Object getCurrent() {
    298             return current;
    299         }
    300         /***
    301          * @return Returns the end.
    302          */
    303         public Integer getEnd() {
    304             return end;
    305         }
    306         /***
    307          * @return Returns the first.
    308          */
    309         public boolean isFirst() {
    310             return first;
    311         }
    312         /***
    313          * @return Returns the index.
    314          */
    315         public int getIndex() {
    316             return index;
    317         }
    318         /***
    319          * @return Returns the last.
    320          */
    321         public boolean isLast() {
    322             return last;
    323         }
    324         /***
    325          * @return Returns the step.
    326          */
    327         public Integer getStep() {
    328             return step;
    329         }
    330         /***
    331          * @param count The count to set.
    332          */
    333         public void setCount(int count) {
    334             this.count = count;
    335         }
    336         /***
    337          * @param current The current to set.
    338          */
    339         public void setCurrent(Object current) {
    340             this.current = current;
    341         }
    342         /***
    343          * @param first The first to set.
    344          */
    345         public void setFirst(boolean first) {
    346             this.first = first;
    347         }
    348         /***
    349          * @param last The last to set.
    350          */
    351         public void setLast(boolean last) {
    352             this.last = last;
    353         }
    354         /***
    355          * @param index The index to set.
    356          */
    357         public void setIndex(int index) {
    358             this.index = index;
    359         }
    360     }
    361 }