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     *  Clears the iterator away.  After calling this method doStartTag()
080     *  will always return SKIP_BODY
081     */
082    public void clearList()
083    {
084        m_iterator = null;
085    }
086    
087    /**
088     *  Override this method to reset your own iterator.
089     */
090    public void resetIterator()
091    {
092        // No operation here
093    }
094    
095    /**
096     *  {@inheritDoc}
097     */
098    @Override
099    public int doStartTag()
100    {
101        m_wikiContext = WikiContext.findContext(pageContext);
102        
103        resetIterator();
104        
105        if( m_iterator == null ) return SKIP_BODY;
106
107        if( m_iterator.hasNext() )
108        {
109            buildContext();
110        }
111
112        return EVAL_BODY_BUFFERED;
113    }
114
115    /**
116     *  Arg, I hate globals.
117     */
118    private void buildContext()
119    {
120        //
121        //  Build a clone of the current context
122        //
123        WikiContext context = (WikiContext)m_wikiContext.clone();
124        
125        Object o = m_iterator.next();
126        
127        if( o instanceof WikiPage )
128            context.setPage( (WikiPage)o );
129
130        //
131        //  Push it to the iterator stack, and set the id.
132        //
133        pageContext.setAttribute( WikiTagBase.ATTR_CONTEXT, context, PageContext.REQUEST_SCOPE );
134        pageContext.setAttribute( getId(), o );
135    }
136
137    /**
138     *  {@inheritDoc}
139     */
140    @Override
141    public int doEndTag()
142    {
143        // Return back to the original.
144        pageContext.setAttribute( WikiTagBase.ATTR_CONTEXT, m_wikiContext, PageContext.REQUEST_SCOPE );
145
146        return EVAL_PAGE;
147    }
148
149    /**
150     *  {@inheritDoc}
151     */
152    @Override
153    public int doAfterBody()
154    {
155        if( bodyContent != null )
156        {
157            try
158            {
159                JspWriter out = getPreviousOut();
160                out.print(bodyContent.getString());
161                bodyContent.clearBody();
162            }
163            catch( IOException e )
164            {
165                log.error("Unable to get inner tag text", e);
166                // FIXME: throw something?
167            }
168        }
169
170        if( m_iterator != null && m_iterator.hasNext() )
171        {
172            buildContext();
173            return EVAL_BODY_BUFFERED;
174        }
175
176        return SKIP_BODY;
177    }
178    
179    /**
180     *  In case your tag throws an exception at any point, you can
181     *  override this method and implement a custom exception handler.
182     *  <p>
183     *  By default, this handler does nothing.
184     *  
185     *  @param arg0 The Throwable that the tag threw
186     *  
187     *  @throws Throwable I have no idea why this would throw anything
188     */
189    @Override
190    public void doCatch(Throwable arg0) throws Throwable
191    {
192    }
193
194    /**
195     *  Executed after the tag has been finished.  This is a great place
196     *  to put any cleanup code.  However you <b>must</b> call super.doFinally()
197     *  if you override this method, or else some of the things may not
198     *  work as expected.
199     */
200    @Override
201    public void doFinally()
202    {
203        resetIterator();
204        m_iterator = null;
205        m_pageName = null;
206        m_wikiContext = null;        
207    }
208
209}