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 javax.servlet.jsp.JspWriter; 027import javax.servlet.jsp.PageContext; 028import javax.servlet.jsp.tagext.BodyTagSupport; 029import javax.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}