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}