001/* 
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.  
018 */
019package org.apache.wiki.tags;
020
021import java.io.IOException;
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.Iterator;
025
026import javax.servlet.jsp.JspWriter;
027import javax.servlet.jsp.PageContext;
028import javax.servlet.jsp.tagext.BodyTagSupport;
029import javax.servlet.jsp.tagext.TryCatchFinally;
030
031import org.apache.log4j.Logger;
032import org.apache.wiki.WikiContext;
033import org.apache.wiki.WikiPage;
034
035
036/**
037 *  Iterates through tags.
038 *
039 *  <P><B>Attributes</B></P>
040 *  <UL>
041 *    <LI>list - a collection.
042 *  </UL>
043 *
044 *  @since 2.0
045 */
046public abstract class IteratorTag extends BodyTagSupport implements TryCatchFinally {
047
048    private static final long serialVersionUID = 8945334759300595321L;
049    protected String      m_pageName;
050    protected Iterator    m_iterator;
051    protected WikiContext m_wikiContext;
052
053    private static final Logger log = Logger.getLogger( IteratorTag.class );
054
055    /**
056     *  Sets the collection that is used to form the iteration.
057     *  
058     *  @param arg A Collection which will be iterated.
059     */
060    public void setList( Collection arg )
061    {
062        if( arg != null )
063            m_iterator = arg.iterator();
064    }
065
066    /**
067     *  Sets the collection list, but using an array.
068     *  @param arg An array of objects which will be iterated.
069     */
070    public void setList( Object[] arg )
071    {
072        if( arg != null )
073        {
074            m_iterator = Arrays.asList(arg).iterator();
075        }
076    }
077    
078    /**
079     *  Sets the iterator directly that is used to form the iteration.
080     */
081    /*
082    public void setList( Iterator arg )
083    {
084        m_iterator = arg;
085    }
086    */
087
088    /**
089     *  Clears the iterator away.  After calling this method doStartTag()
090     *  will always return SKIP_BODY
091     */
092    public void clearList()
093    {
094        m_iterator = null;
095    }
096    
097    /**
098     *  Override this method to reset your own iterator.
099     */
100    public void resetIterator()
101    {
102        // No operation here
103    }
104    
105    /**
106     *  {@inheritDoc}
107     */
108    public int doStartTag()
109    {
110        m_wikiContext = WikiContext.findContext(pageContext);
111        
112        resetIterator();
113        
114        if( m_iterator == null ) return SKIP_BODY;
115
116        if( m_iterator.hasNext() )
117        {
118            buildContext();
119        }
120
121        return EVAL_BODY_BUFFERED;
122    }
123
124    /**
125     *  Arg, I hate globals.
126     */
127    private void buildContext()
128    {
129        //
130        //  Build a clone of the current context
131        //
132        WikiContext context = (WikiContext)m_wikiContext.clone();
133        
134        Object o = m_iterator.next();
135        
136        if( o instanceof WikiPage )
137            context.setPage( (WikiPage)o );
138
139        //
140        //  Push it to the iterator stack, and set the id.
141        //
142        pageContext.setAttribute( WikiTagBase.ATTR_CONTEXT,
143                                  context,
144                                  PageContext.REQUEST_SCOPE );
145        pageContext.setAttribute( getId(),
146                                  o );
147    }
148
149    /**
150     *  {@inheritDoc}
151     */
152    public int doEndTag()
153    {
154        // Return back to the original.
155        pageContext.setAttribute( WikiTagBase.ATTR_CONTEXT,
156                                  m_wikiContext,
157                                  PageContext.REQUEST_SCOPE );
158
159        return EVAL_PAGE;
160    }
161
162    /**
163     *  {@inheritDoc}
164     */
165    public int doAfterBody()
166    {
167        if( bodyContent != null )
168        {
169            try
170            {
171                JspWriter out = getPreviousOut();
172                out.print(bodyContent.getString());
173                bodyContent.clearBody();
174            }
175            catch( IOException e )
176            {
177                log.error("Unable to get inner tag text", e);
178                // FIXME: throw something?
179            }
180        }
181
182        if( m_iterator != null && m_iterator.hasNext() )
183        {
184            buildContext();
185            return EVAL_BODY_BUFFERED;
186        }
187
188        return SKIP_BODY;
189    }
190    
191    /**
192     *  In case your tag throws an exception at any point, you can
193     *  override this method and implement a custom exception handler.
194     *  <p>
195     *  By default, this handler does nothing.
196     *  
197     *  @param arg0 The Throwable that the tag threw
198     *  
199     *  @throws Throwable I have no idea why this would throw anything
200     */
201    public void doCatch(Throwable arg0) throws Throwable
202    {
203    }
204
205    /**
206     *  Executed after the tag has been finished.  This is a great place
207     *  to put any cleanup code.  However you <b>must</b> call super.doFinally()
208     *  if you override this method, or else some of the things may not
209     *  work as expected.
210     */
211    public void doFinally()
212    {
213        resetIterator();
214        m_iterator = null;
215        m_pageName = null;
216        m_wikiContext = null;        
217    }
218
219}