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     */
019    package org.apache.wiki.tags;
020    
021    import java.io.IOException;
022    import java.util.Arrays;
023    import java.util.Collection;
024    import java.util.Iterator;
025    
026    import javax.servlet.jsp.JspWriter;
027    import javax.servlet.jsp.PageContext;
028    import javax.servlet.jsp.tagext.BodyTagSupport;
029    import javax.servlet.jsp.tagext.TryCatchFinally;
030    
031    import org.apache.log4j.Logger;
032    import org.apache.wiki.WikiContext;
033    import 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     */
046    public 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    }