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  package org.apache.commons.jelly;
    17
    18  import java.io.StringWriter;
    19  import java.util.Arrays;
    20  import java.util.Collection;
    21  import java.util.Iterator;
    22
    23  import org.apache.commons.jelly.util.TagUtils;
    24
    25  /*** <p><code>TagSupport</code> an abstract base class which is useful to
    26    * inherit from if developing your own tag.</p>
    27    *
    28    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
    29    * @version $Revision: 155420 $
    30    */
    31
    32  public abstract class TagSupport implements Tag {
    33
    34      /*** the parent of this tag */
    35      protected Tag parent;
    36
    37      /*** the body of the tag */
    38      protected Script body;
    39      /*** The current context */
    40
    41      protected Boolean shouldTrim;
    42      protected boolean hasTrimmed;
    43
    44      protected JellyContext context;
    45
    46      /*** whether xml text should be escaped */
    47      private boolean escapeText = true;
    48
    49      /***
    50       * Searches up the parent hierarchy from the given tag
    51       * for a Tag of the given type
    52       *
    53       * @param from the tag to start searching from
    54       * @param tagClass the type of the tag to find
    55       * @return the tag of the given type or null if it could not be found
    56       */
    57      public static Tag findAncestorWithClass(Tag from, Class tagClass) {
    58          // we could implement this as
    59          //  return findAncestorWithClass(from,Collections.singleton(tagClass));
    60          // but this is so simple let's save the object creation for now
    61          while (from != null) {
    62              if (tagClass.isInstance(from)) {
    63                  return from;
    64              }
    65              from = from.getParent();
    66          }
    67          return null;
    68      }
    69
    70      /***
    71       * Searches up the parent hierarchy from the given tag
    72       * for a Tag matching one or more of given types.
    73       *
    74       * @param from the tag to start searching from
    75       * @param tagClasses a Collection of Class types that might match
    76       * @return the tag of the given type or null if it could not be found
    77       */
    78      public static Tag findAncestorWithClass(Tag from, Collection tagClasses) {
    79          while (from != null) {
    80              for(Iterator iter = tagClasses.iterator();iter.hasNext();) {
    81                  Class klass = (Class)(iter.next());
    82                  if (klass.isInstance(from)) {
    83                      return from;
    84                  }
    85              }
    86              from = from.getParent();
    87          }
    88          return null;
    89      }
    90
    91      /***
    92       * Searches up the parent hierarchy from the given tag
    93       * for a Tag matching one or more of given types.
    94       *
    95       * @param from the tag to start searching from
    96       * @param tagClasses an array of types that might match
    97       * @return the tag of the given type or null if it could not be found
    98       * @see #findAncestorWithClass(Tag,Collection)
    99       */
    100     public static Tag findAncestorWithClass(Tag from, Class[] tagClasses) {
    101         return findAncestorWithClass(from,Arrays.asList(tagClasses));
    102     }
    103
    104     public TagSupport() {
    105     }
    106
    107     public TagSupport(boolean shouldTrim) {
    108         setTrim( shouldTrim );
    109     }
    110
    111     /***
    112      * Sets whether whitespace inside this tag should be trimmed or not.
    113      * Defaults to true so whitespace is trimmed
    114      */
    115     public void setTrim(boolean shouldTrim) {
    116         if ( shouldTrim ) {
    117             this.shouldTrim = Boolean.TRUE;
    118         }
    119         else {
    120             this.shouldTrim = Boolean.FALSE;
    121         }
    122     }
    123
    124     public boolean isTrim() {
    125         if ( this.shouldTrim == null ) {
    126             Tag parent = getParent();
    127             if ( parent == null ) {
    128                 return true;
    129             }
    130             else {
    131                 if ( parent instanceof TagSupport ) {
    132                     TagSupport parentSupport = (TagSupport) parent;
    133
    134                     this.shouldTrim = ( parentSupport.isTrim() ? Boolean.TRUE : Boolean.FALSE );
    135                 }
    136                 else {
    137                     this.shouldTrim = Boolean.TRUE;
    138                 }
    139             }
    140         }
    141
    142         return this.shouldTrim.booleanValue();
    143     }
    144
    145     /*** @return the parent of this tag */
    146     public Tag getParent() {
    147         return parent;
    148     }
    149
    150     /*** Sets the parent of this tag */
    151     public void setParent(Tag parent) {
    152         this.parent = parent;
    153     }
    154
    155     /*** @return the body of the tag */
    156     public Script getBody() {
    157         if (! hasTrimmed) {
    158             hasTrimmed = true;
    159             if (isTrim()) {
    160                 trimBody();
    161             }
    162         }
    163         return body;
    164     }
    165
    166     /*** Sets the body of the tag */
    167     public void setBody(Script body) {
    168         this.body = body;
    169         this.hasTrimmed = false;
    170     }
    171
    172     /*** @return the context in which the tag will be run */
    173     public JellyContext getContext() {
    174         return context;
    175     }
    176
    177     /*** Sets the context in which the tag will be run */
    178     public void setContext(JellyContext context) throws JellyTagException {
    179         this.context = context;
    180     }
    181
    182     /***
    183      * Invokes the body of this tag using the given output
    184      */
    185     public void invokeBody(XMLOutput output) throws JellyTagException {
    186         getBody().run(context, output);
    187     }
    188
    189     // Implementation methods
    190     //-------------------------------------------------------------------------
    191     /***
    192      * Searches up the parent hierarchy for a Tag of the given type.
    193      * @return the tag of the given type or null if it could not be found
    194      */
    195     protected Tag findAncestorWithClass(Class parentClass) {
    196         return findAncestorWithClass(getParent(), parentClass);
    197     }
    198
    199     /***
    200      * Searches up the parent hierarchy for a Tag of one of the given types.
    201      * @return the tag of the given type or null if it could not be found
    202      * @see #findAncestorWithClass(Collection)
    203      */
    204     protected Tag findAncestorWithClass(Class[] parentClasses) {
    205         return findAncestorWithClass(getParent(),parentClasses);
    206     }
    207
    208     /***
    209      * Searches up the parent hierarchy for a Tag of one of the given types.
    210      * @return the tag of the given type or null if it could not be found
    211      */
    212     protected Tag findAncestorWithClass(Collection parentClasses) {
    213         return findAncestorWithClass(getParent(),parentClasses);
    214     }
    215
    216     /***
    217      * Executes the body of the tag and returns the result as a String.
    218      *
    219      * @return the text evaluation of the body
    220      */
    221     protected String getBodyText() throws JellyTagException {
    222         return getBodyText(escapeText);
    223     }
    224
    225     /***
    226      * Executes the body of the tag and returns the result as a String.
    227      *
    228      * @param shouldEscape Signal if the text should be escaped.
    229      *
    230      * @return the text evaluation of the body
    231      */
    232     protected String getBodyText(boolean shouldEscape) throws JellyTagException {
    233         StringWriter writer = new StringWriter();
    234         invokeBody(XMLOutput.createXMLOutput(writer, shouldEscape));
    235         return writer.toString();
    236     }
    237
    238
    239     /***
    240      * Find all text nodes inside the top level of this body and
    241      * if they are just whitespace then remove them
    242      */
    243     protected void trimBody() {
    244         TagUtils.trimScript(body);
    245     }
    246
    247     /***
    248      * Returns whether the body of this tag will be escaped or not.
    249      */
    250     public boolean isEscapeText() {
    251         return escapeText;
    252     }
    253
    254     /***
    255      * Sets whether the body of the tag should be escaped as text (so that &lt; and &gt; are
    256      * escaped as &amp;lt; and &amp;gt;), which is the default or leave the text as XML.
    257      */
    258     public void setEscapeText(boolean escapeText) {
    259         this.escapeText = escapeText;
    260     }
    261 }