View Javadoc

    1   /*
    2    * $Id: IterateTag.java 471754 2006-11-06 14:55:09Z husted $
    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  package org.apache.struts.taglib.logic;
    22
    23  import org.apache.struts.taglib.TagUtils;
    24  import org.apache.struts.util.IteratorAdapter;
    25  import org.apache.struts.util.MessageResources;
    26
    27  import javax.servlet.jsp.JspException;
    28  import javax.servlet.jsp.tagext.BodyTagSupport;
    29
    30  import java.lang.reflect.Array;
    31
    32  import java.util.ArrayList;
    33  import java.util.Arrays;
    34  import java.util.Collection;
    35  import java.util.Enumeration;
    36  import java.util.Iterator;
    37  import java.util.Map;
    38
    39  /**
    40   * Custom tag that iterates the elements of a collection, which can be either
    41   * an attribute or the property of an attribute.  The collection can be any of
    42   * the following:  an array of objects, an Enumeration, an Iterator, a
    43   * Collection (which includes Lists, Sets and Vectors), or a Map (which
    44   * includes Hashtables) whose elements will be iterated over.
    45   *
    46   * @version $Rev: 471754 $ $Date: 2004-11-03 14:20:47 -0500 (Wed, 03 Nov 2004)
    47   *          $
    48   */
    49  public class IterateTag extends BodyTagSupport {
    50      /**
    51       * The message resources for this package.
    52       */
    53      protected static MessageResources messages =
    54          MessageResources.getMessageResources(
    55              "org.apache.struts.taglib.logic.LocalStrings");
    56
    57      // ----------------------------------------------------- Instance Variables
    58
    59      /**
    60       * Iterator of the elements of this collection, while we are actually
    61       * running.
    62       */
    63      protected Iterator iterator = null;
    64
    65      /**
    66       * The number of elements we have already rendered.
    67       */
    68      protected int lengthCount = 0;
    69
    70      /**
    71       * The actual length value (calculated in the start tag).
    72       */
    73      protected int lengthValue = 0;
    74
    75      /**
    76       * The actual offset value (calculated in the start tag).
    77       */
    78      protected int offsetValue = 0;
    79
    80      /**
    81       * Has this tag instance been started?
    82       */
    83      protected boolean started = false;
    84
    85      // ------------------------------------------------------------- Properties
    86
    87      /**
    88       * The collection over which we will be iterating.
    89       */
    90      protected Object collection = null;
    91
    92      /**
    93       * The name of the scripting variable to be exposed.
    94       */
    95      protected String id = null;
    96
    97      /**
    98       * The name of the scripting variable to be exposed as the current index.
    99       */
    100     protected String indexId = null;
    101
    102     /**
    103      * The length value or attribute name (<=0 means no limit).
    104      */
    105     protected String length = null;
    106
    107     /**
    108      * The name of the collection or owning bean.
    109      */
    110     protected String name = null;
    111
    112     /**
    113      * The starting offset (zero relative).
    114      */
    115     protected String offset = null;
    116
    117     /**
    118      * The property name containing the collection.
    119      */
    120     protected String property = null;
    121
    122     /**
    123      * The scope of the bean specified by the name property, if any.
    124      */
    125     protected String scope = null;
    126
    127     /**
    128      * The Java class of each exposed element of the collection.
    129      */
    130     protected String type = null;
    131
    132     public Object getCollection() {
    133         return (this.collection);
    134     }
    135
    136     public void setCollection(Object collection) {
    137         this.collection = collection;
    138     }
    139
    140     public String getId() {
    141         return (this.id);
    142     }
    143
    144     public void setId(String id) {
    145         this.id = id;
    146     }
    147
    148     /**
    149      * <p>Return the zero-relative index of the current iteration through the
    150      * loop.  If you specify an <code>offset</code>, the first iteration
    151      * through the loop will have that value; otherwise, the first iteration
    152      * will return zero.</p>
    153      *
    154      * <p>This property is read-only, and gives nested custom tags access to
    155      * this information.  Therefore, it is <strong>only</strong> valid in
    156      * between calls to <code>doStartTag()</code> and <code>doEndTag()</code>.
    157      * </p>
    158      */
    159     public int getIndex() {
    160         if (started) {
    161             return ((offsetValue + lengthCount) - 1);
    162         } else {
    163             return (0);
    164         }
    165     }
    166
    167     public String getIndexId() {
    168         return (this.indexId);
    169     }
    170
    171     public void setIndexId(String indexId) {
    172         this.indexId = indexId;
    173     }
    174
    175     public String getLength() {
    176         return (this.length);
    177     }
    178
    179     public void setLength(String length) {
    180         this.length = length;
    181     }
    182
    183     public String getName() {
    184         return (this.name);
    185     }
    186
    187     public void setName(String name) {
    188         this.name = name;
    189     }
    190
    191     public String getOffset() {
    192         return (this.offset);
    193     }
    194
    195     public void setOffset(String offset) {
    196         this.offset = offset;
    197     }
    198
    199     public String getProperty() {
    200         return (this.property);
    201     }
    202
    203     public void setProperty(String property) {
    204         this.property = property;
    205     }
    206
    207     public String getScope() {
    208         return (this.scope);
    209     }
    210
    211     public void setScope(String scope) {
    212         this.scope = scope;
    213     }
    214
    215     public String getType() {
    216         return (this.type);
    217     }
    218
    219     public void setType(String type) {
    220         this.type = type;
    221     }
    222
    223     // --------------------------------------------------------- Public Methods
    224
    225     /**
    226      * Construct an iterator for the specified collection, and begin looping
    227      * through the body once per element.
    228      *
    229      * @throws JspException if a JSP exception has occurred
    230      */
    231     public int doStartTag() throws JspException {
    232         // Acquire the collection we are going to iterate over
    233         Object collection = this.collection;
    234
    235         if (collection == null) {
    236             collection =
    237                 TagUtils.getInstance().lookup(pageContext, name, property,
    238                     scope);
    239         }
    240
    241         if (collection == null) {
    242             JspException e =
    243                 new JspException(messages.getMessage("iterate.collection"));
    244
    245             TagUtils.getInstance().saveException(pageContext, e);
    246             throw e;
    247         }
    248
    249         // Construct an iterator for this collection
    250         if (collection.getClass().isArray()) {
    251             try {
    252                 // If we're lucky, it is an array of objects
    253                 // that we can iterate over with no copying
    254                 iterator = Arrays.asList((Object[]) collection).iterator();
    255             } catch (ClassCastException e) {
    256                 // Rats -- it is an array of primitives
    257                 int length = Array.getLength(collection);
    258                 ArrayList c = new ArrayList(length);
    259
    260                 for (int i = 0; i < length; i++) {
    261                     c.add(Array.get(collection, i));
    262                 }
    263
    264                 iterator = c.iterator();
    265             }
    266         } else if (collection instanceof Collection) {
    267             iterator = ((Collection) collection).iterator();
    268         } else if (collection instanceof Iterator) {
    269             iterator = (Iterator) collection;
    270         } else if (collection instanceof Map) {
    271             iterator = ((Map) collection).entrySet().iterator();
    272         } else if (collection instanceof Enumeration) {
    273             iterator = new IteratorAdapter((Enumeration) collection);
    274         } else {
    275             JspException e =
    276                 new JspException(messages.getMessage("iterate.iterator"));
    277
    278             TagUtils.getInstance().saveException(pageContext, e);
    279             throw e;
    280         }
    281
    282         // Calculate the starting offset
    283         if (offset == null) {
    284             offsetValue = 0;
    285         } else {
    286             try {
    287                 offsetValue = Integer.parseInt(offset);
    288             } catch (NumberFormatException e) {
    289                 Integer offsetObject =
    290                     (Integer) TagUtils.getInstance().lookup(pageContext,
    291                         offset, null);
    292
    293                 if (offsetObject == null) {
    294                     offsetValue = 0;
    295                 } else {
    296                     offsetValue = offsetObject.intValue();
    297                 }
    298             }
    299         }
    300
    301         if (offsetValue < 0) {
    302             offsetValue = 0;
    303         }
    304
    305         // Calculate the rendering length
    306         if (length == null) {
    307             lengthValue = 0;
    308         } else {
    309             try {
    310                 lengthValue = Integer.parseInt(length);
    311             } catch (NumberFormatException e) {
    312                 Integer lengthObject =
    313                     (Integer) TagUtils.getInstance().lookup(pageContext,
    314                         length, null);
    315
    316                 if (lengthObject == null) {
    317                     lengthValue = 0;
    318                 } else {
    319                     lengthValue = lengthObject.intValue();
    320                 }
    321             }
    322         }
    323
    324         if (lengthValue < 0) {
    325             lengthValue = 0;
    326         }
    327
    328         lengthCount = 0;
    329
    330         // Skip the leading elements up to the starting offset
    331         for (int i = 0; i < offsetValue; i++) {
    332             if (iterator.hasNext()) {
    333                 iterator.next();
    334             }
    335         }
    336
    337         // Store the first value and evaluate, or skip the body if none
    338         if (iterator.hasNext()) {
    339             Object element = iterator.next();
    340
    341             if (element == null) {
    342                 pageContext.removeAttribute(id);
    343             } else {
    344                 pageContext.setAttribute(id, element);
    345             }
    346
    347             lengthCount++;
    348             started = true;
    349
    350             if (indexId != null) {
    351                 pageContext.setAttribute(indexId, new Integer(getIndex()));
    352             }
    353
    354             return (EVAL_BODY_TAG);
    355         } else {
    356             return (SKIP_BODY);
    357         }
    358     }
    359
    360     /**
    361      * Make the next collection element available and loop, or finish the
    362      * iterations if there are no more elements.
    363      *
    364      * @throws JspException if a JSP exception has occurred
    365      */
    366     public int doAfterBody() throws JspException {
    367         // Render the output from this iteration to the output stream
    368         if (bodyContent != null) {
    369             TagUtils.getInstance().writePrevious(pageContext,
    370                 bodyContent.getString());
    371             bodyContent.clearBody();
    372         }
    373
    374         // Decide whether to iterate or quit
    375         if ((lengthValue > 0) && (lengthCount >= lengthValue)) {
    376             return (SKIP_BODY);
    377         }
    378
    379         if (iterator.hasNext()) {
    380             Object element = iterator.next();
    381
    382             if (element == null) {
    383                 pageContext.removeAttribute(id);
    384             } else {
    385                 pageContext.setAttribute(id, element);
    386             }
    387
    388             lengthCount++;
    389
    390             if (indexId != null) {
    391                 pageContext.setAttribute(indexId, new Integer(getIndex()));
    392             }
    393
    394             return (EVAL_BODY_TAG);
    395         } else {
    396             return (SKIP_BODY);
    397         }
    398     }
    399
    400     /**
    401      * Clean up after processing this enumeration.
    402      *
    403      * @throws JspException if a JSP exception has occurred
    404      */
    405     public int doEndTag() throws JspException {
    406         // Clean up our started state
    407         started = false;
    408         iterator = null;
    409
    410         // Continue processing this page
    411         return (EVAL_PAGE);
    412     }
    413
    414     /**
    415      * Release all allocated resources.
    416      */
    417     public void release() {
    418         super.release();
    419
    420         iterator = null;
    421         lengthCount = 0;
    422         lengthValue = 0;
    423         offsetValue = 0;
    424
    425         id = null;
    426         collection = null;
    427         length = null;
    428         name = null;
    429         offset = null;
    430         property = null;
    431         scope = null;
    432         started = false;
    433     }
    434 }