    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 org.apache.logging.log4j.LogManager;
    022import org.apache.logging.log4j.Logger;
    023import org.apache.wiki.api.core.Context;
    024import org.apache.wiki.api.core.Page;
    025
    026import jakarta.servlet.jsp.JspWriter;
    027import jakarta.servlet.jsp.PageContext;
    028import jakarta.servlet.jsp.tagext.BodyTagSupport;
    029import jakarta.servlet.jsp.tagext.TryCatchFinally;
    030import java.io.IOException;
    031import java.util.Arrays;
    032import java.util.Collection;
    033import java.util.Iterator;
    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 Context m_wikiContext;
    052
    053    private static final Logger LOG = LogManager.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( final Collection< ? > arg ) {
    061        if( arg != null ) {
    062            m_iterator = arg.iterator();
    063        }
    064    }
    065
    066    /**
    067     *  Sets the collection list, but using an array.
    068     *
    069     *  @param arg An array of objects which will be iterated.
    070     */
    071    public void setList( final Object[] arg ) {
    072        if( arg != null ) {
    073            m_iterator = Arrays.asList(arg).iterator();
    074        }
    075    }
    076
    077    /**
    078     *  Clears the iterator away.  After calling this method doStartTag() will always return SKIP_BODY
    079     */
    080    public void clearList() {
    081        m_iterator = null;
    082    }
    083
    084    /**
    085     *  Override this method to reset your own iterator.
    086     */
    087    public void resetIterator() {
    088        // No operation here
    089    }
    090
    091    /** {@inheritDoc} */
    092    @Override
    093    public int doStartTag() {
    094        m_wikiContext = Context.findContext(pageContext);
    095        resetIterator();
    096        if( m_iterator == null ) {
    097            return SKIP_BODY;
    098        }
    099        if( m_iterator.hasNext() ) {
    100            buildContext();
    101        }
    102
    103        return EVAL_BODY_BUFFERED;
    104    }
    105
    106    /**
    107     *  Arg, I hate globals.
    108     */
    109    private void buildContext() {
    110        final Context context = m_wikiContext.clone();
    111        final Object o = m_iterator.next();
    112        if( o instanceof Page ) {
    113            context.setPage( ( Page )o );
    114        }
    115
    116        pageContext.setAttribute( Context.ATTR_CONTEXT, context, PageContext.REQUEST_SCOPE );
    117        pageContext.setAttribute( getId(), o );
    118    }
    119
    120    /** {@inheritDoc} */
    121    @Override
    122    public int doEndTag() {
    123        // Return back to the original.
    124        pageContext.setAttribute( Context.ATTR_CONTEXT, m_wikiContext, PageContext.REQUEST_SCOPE );
    125
    126        return EVAL_PAGE;
    127    }
    128
    129    /** {@inheritDoc} */
    130    @Override
    131    public int doAfterBody() {
    132        if( bodyContent != null ) {
    133            try {
    134                final JspWriter out = getPreviousOut();
    135                out.print( bodyContent.getString() );
    136                bodyContent.clearBody();
    137            } catch( final IOException e ) {
    138                LOG.error( "Unable to get inner tag text", e );
    139                // FIXME: throw something?
    140            }
    141        }
    142
    143        if( m_iterator != null && m_iterator.hasNext() ) {
    144            buildContext();
    145            return EVAL_BODY_BUFFERED;
    146        }
    147
    148        return SKIP_BODY;
    149    }
    150
    151    /**
    152     *  In case your tag throws an exception at any point, you can override this method and implement a custom exception handler.
    153     *  <p>
    154     *  By default, this handler does nothing.
    155     *
    156     *  @param arg0 The Throwable that the tag threw
    157     *  @throws Throwable I have no idea why this would throw anything
    158     */
    159    @Override
    160    public void doCatch( final Throwable arg0) throws Throwable {
    161    }
    162
    163    /**
    164     *  Executed after the tag has been finished.  This is a great place to put any cleanup code.  However you <b>must</b> call
    165     *  super.doFinally() if you override this method, or else some of the things may not work as expected.
    166     */
    167    @Override
    168    public void doFinally() {
    169        resetIterator();
    170        m_iterator = null;
    171        m_pageName = null;
    172        m_wikiContext = null;
    173    }
    174
    175}