001    package com.mockrunner.tag;
002    
003    import java.util.ArrayList;
004    import java.util.Enumeration;
005    import java.util.HashMap;
006    import java.util.List;
007    import java.util.Map;
008    
009    import javax.servlet.jsp.JspException;
010    import javax.servlet.jsp.JspWriter;
011    import javax.servlet.jsp.PageContext;
012    import javax.servlet.jsp.tagext.BodyContent;
013    import javax.servlet.jsp.tagext.BodyTag;
014    import javax.servlet.jsp.tagext.BodyTagSupport;
015    import javax.servlet.jsp.tagext.IterationTag;
016    import javax.servlet.jsp.tagext.JspTag;
017    import javax.servlet.jsp.tagext.SimpleTag;
018    import javax.servlet.jsp.tagext.Tag;
019    import javax.servlet.jsp.tagext.TagSupport;
020    
021    import com.mockrunner.mock.web.MockBodyContent;
022    
023    /**
024     * Implementation of {@link NestedTag} wrapping tags of
025     * type <code>BodyTag</code>. <code>NestedBodyTag</code> instances 
026     * are created with the help of {@link TagTestModule#createNestedTag}. 
027     * You do not need to create them on your own in the tests.
028     */
029    public class NestedBodyTag extends BodyTagSupport implements NestedTag
030    {
031        private BodyTag tag;
032        private PageContext pageContext;
033        private Map attributes;
034        private List childs;
035        private boolean doRelease;
036        
037        /**
038         * Constructor for a tag with an empty attribute map.
039         * If the specified tag is not an instance of <code>BodyTagSupport</code>,
040         * the methods that delegate to <code>BodyTagSupport</code> specific methods
041         * throw an exception.
042         * @param tag the tag
043         * @param pageContext the corresponding <code>PageContext</code>
044         */
045        public NestedBodyTag(BodyTag tag, PageContext pageContext)
046        {
047            this(tag, pageContext, new HashMap());
048        }
049        
050        /**
051         * Constructor for a tag with the specified attribute map.
052         * If the specified tag is not an instance of <code>BodyTagSupport</code>,
053         * the methods that delegate to <code>BodyTagSupport</code> specific methods
054         * throw an exception.
055         * @param tag the tag
056         * @param pageContext the corresponding <code>PageContext</code>
057         * @param attributes the attribute map
058         */
059        public NestedBodyTag(BodyTag tag, PageContext pageContext, Map attributes)
060        {
061            this.tag = tag;
062            this.pageContext = pageContext;
063            tag.setPageContext(pageContext);
064            childs = new ArrayList();
065            this.attributes = attributes;
066            doRelease = false;
067        }
068        
069        /**
070         * Constructor for a tag with an empty attribute map.
071         * @param tag the tag
072         * @param pageContext the corresponding <code>PageContext</code>
073         */
074        public NestedBodyTag(BodyTagSupport tag, PageContext pageContext)
075        {
076            this(tag, pageContext, new HashMap());
077        }
078        
079        /**
080         * Constructor for a tag with the specified attribute map.
081         * @param tag the tag
082         * @param pageContext the corresponding <code>PageContext</code>
083         * @param attributes the attribute map
084         */
085        public NestedBodyTag(BodyTagSupport tag, PageContext pageContext, Map attributes)
086        {
087            this((BodyTag)tag, pageContext, attributes);
088        }
089        
090        /**
091         * @inheritDoc
092         */
093        public void setDoRelease(boolean doRelease)
094        {
095            this.doRelease = doRelease;
096        }
097        
098        /**
099         * @inheritDoc
100         */
101        public void setDoReleaseRecursive(boolean doRelease)
102        {
103            this.doRelease = doRelease;
104            for(int ii = 0; ii < childs.size(); ii++)
105            {
106                Object child = childs.get(ii);
107                if(child instanceof NestedTag)
108                {
109                    ((NestedTag)child).setDoReleaseRecursive(doRelease);
110                }
111            }
112        }
113        
114        /**
115         * @inheritDoc
116         */
117        public void populateAttributes()
118        {
119            TagUtil.populateTag(tag, attributes);
120        }
121        
122        /**
123         * @inheritDoc
124         */
125        public int doLifecycle() throws JspException
126        {
127            populateAttributes();
128            int returnValue = -1;
129            try
130            {
131                int result = tag.doStartTag();
132                if(Tag.EVAL_BODY_INCLUDE == result)
133                {
134                    TagUtil.evalBody(childs, pageContext);
135                    while(IterationTag.EVAL_BODY_AGAIN == doAfterBody())
136                    {
137                        TagUtil.evalBody(childs, pageContext);
138                    }
139                }
140                else if(BodyTag.EVAL_BODY_BUFFERED == result)
141                {
142                    MockBodyContent bodyContent = (MockBodyContent)pageContext.pushBody();
143                    tag.setBodyContent(bodyContent);
144                    tag.doInitBody();
145                    TagUtil.evalBody(childs, pageContext);
146                    while(IterationTag.EVAL_BODY_AGAIN == doAfterBody())
147                    {
148                        TagUtil.evalBody(childs, pageContext);
149                    }
150                    pageContext.popBody();
151                }
152                returnValue = tag.doEndTag();
153            } 
154            catch(Throwable exc)
155            {
156                TagUtil.handleException(tag, exc);
157            }
158            finally
159            {
160                TagUtil.handleFinally(tag);
161            }
162            if(doRelease) tag.release();
163            return returnValue;
164        }
165        
166        /**
167         * @inheritDoc
168         * @throws <code>RuntimeException</code>, if the wrapped tag
169         *         is not an instance of <code>TagSupport</code>
170         */
171        public TagSupport getTag()
172        {
173            checkTagSupport();
174            return (TagSupport)tag;
175        }
176        
177        /**
178         * @inheritDoc
179         */
180        public JspTag getWrappedTag()
181        {
182            return tag;
183        }
184        
185        /**
186         * @inheritDoc
187         */
188        public void removeChilds()
189        {
190            childs.clear();
191        }
192        
193        /**
194         * @inheritDoc
195         */
196        public List getChilds()
197        {
198            return childs;
199        }
200        
201        /**
202         * @inheritDoc
203         */
204        public Object getChild(int index)
205        {
206            return childs.get(index);
207        }
208        
209        /**
210         * @inheritDoc
211         */
212        public void addTextChild(String text)
213        {
214            if(null == text) text = "";
215            childs.add(text);
216        }
217        
218        /**
219         * @inheritDoc
220         */
221        public void addDynamicChild(DynamicChild child)
222        {
223            if(null == child) return;
224            childs.add(child);
225        }
226        
227        /**
228         * @inheritDoc
229         */
230        public NestedTag addTagChild(Class tag)
231        {
232            return addTagChild(tag, new HashMap());
233        }
234        
235        /**
236         * @inheritDoc
237         */
238        public NestedTag addTagChild(Class tag, Map attributeMap)
239        {
240            Object childTag = TagUtil.createNestedTagInstance(tag, this.pageContext, attributeMap); 
241            return (NestedTag)addChild(childTag);
242        }
243        
244        /**
245         * @inheritDoc
246         */
247        public NestedTag addTagChild(TagSupport tag)
248        {
249            return addTagChild(tag, new HashMap());
250        }
251        
252        /**
253         * @inheritDoc
254         */
255        public NestedTag addTagChild(TagSupport tag, Map attributeMap)
256        {
257            Object childTag = (Object)TagUtil.createNestedTagInstance(tag, this.pageContext, attributeMap);   
258            return (NestedTag)addChild(childTag);
259        }
260        
261        /**
262         * @inheritDoc
263         */
264        public NestedTag addTagChild(JspTag tag)
265        {
266            return addTagChild(tag, new HashMap());
267        }
268        
269        /**
270         * @inheritDoc
271         */
272        public NestedTag addTagChild(JspTag tag, Map attributeMap)
273        {
274            Object childTag = TagUtil.createNestedTagInstance(tag, this.pageContext, attributeMap);   
275            return (NestedTag)addChild(childTag);
276        }
277        
278        /**
279         * Delegates to wrapped tag.
280         */
281        public int doAfterBody() throws JspException
282        {
283            return tag.doAfterBody();
284        }
285        
286        /**
287         * Delegates to wrapped tag.
288         */
289        public int doEndTag() throws JspException
290        {
291            return tag.doEndTag();
292        }
293        
294        /**
295         * Delegates to wrapped tag.
296         */
297        public int doStartTag() throws JspException
298        {
299            return tag.doStartTag();
300        }
301        
302        /**
303         * Delegates to wrapped tag.
304         * @throws <code>RuntimeException</code>, if the wrapped tag
305         *         is not an instance of <code>TagSupport</code>
306         */
307        public String getId()
308        {
309            checkTagSupport();
310            return ((TagSupport)tag).getId();
311        }
312        
313        /**
314         * Delegates to wrapped tag.
315         */
316        public Tag getParent()
317        {
318            return tag.getParent();
319        }
320        
321        /**
322         * Delegates to wrapped tag.
323         * @throws <code>RuntimeException</code>, if the wrapped tag
324         *         is not an instance of <code>TagSupport</code>
325         */
326        public Object getValue(String key)
327        {
328            checkTagSupport();
329            return ((TagSupport)tag).getValue(key);
330        }
331        
332        /**
333         * Delegates to wrapped tag.
334         * @throws <code>RuntimeException</code>, if the wrapped tag
335         *         is not an instance of <code>TagSupport</code>
336         */
337        public Enumeration getValues()
338        {
339            checkTagSupport();
340            return ((TagSupport)tag).getValues();
341        }
342        
343        /**
344         * Delegates to wrapped tag.
345         */
346        public void release()
347        {
348            tag.release();
349        }
350        
351        /**
352         * Delegates to wrapped tag.
353         * @throws <code>RuntimeException</code>, if the wrapped tag
354         *         is not an instance of <code>TagSupport</code>
355         */
356        public void removeValue(String value)
357        {
358            checkTagSupport();
359            ((TagSupport)tag).removeValue(value);
360        }
361        
362        /**
363         * Delegates to wrapped tag.
364         * @throws <code>RuntimeException</code>, if the wrapped tag
365         *         is not an instance of <code>TagSupport</code>
366         */
367        public void setId(String id)
368        {
369            checkTagSupport();
370            ((TagSupport)tag).setId(id);
371        }
372        
373        /**
374         * Delegates to wrapped tag. Also calls <code>setPageContext</code>
375         * for all child tags.
376         */
377        public void setPageContext(PageContext pageContext)
378        {
379            this.pageContext = pageContext;
380            tag.setPageContext(pageContext);
381            for(int ii = 0; ii < childs.size(); ii++)
382            {
383                Object child = childs.get(ii);
384                if(child instanceof Tag)
385                {
386                    ((Tag)child).setPageContext(pageContext);
387                }
388                else if(child instanceof SimpleTag)
389                {
390                    ((SimpleTag)child).setJspContext(pageContext);
391                }
392            }
393        }
394        
395        /**
396         * Delegates to wrapped tag.
397         */
398        public void setParent(Tag parent)
399        {
400            tag.setParent(parent);
401        }
402        
403        /**
404         * Delegates to wrapped tag.
405         * @throws <code>RuntimeException</code>, if the wrapped tag
406         *         is not an instance of <code>TagSupport</code>
407         */
408        public void setValue(String key, Object value)
409        {
410            checkTagSupport();
411            ((TagSupport)tag).setValue(key, value);
412        }
413        
414        /**
415         * Delegates to wrapped tag.
416         */ 
417        public void doInitBody() throws JspException
418        {
419            tag.doInitBody();
420        }
421        
422        /**
423         * Delegates to wrapped tag.
424         * @throws <code>RuntimeException</code>, if the wrapped tag
425         *         is not an instance of <code>BodyTagSupport</code>
426         */
427        public BodyContent getBodyContent()
428        {
429            checkTagSupport();
430            return ((BodyTagSupport)tag).getBodyContent();
431        }
432        
433        /**
434         * Delegates to wrapped tag.
435         * @throws <code>RuntimeException</code>, if the wrapped tag
436         *         is not an instance of <code>BodyTagSupport</code>
437         */
438        public JspWriter getPreviousOut()
439        {
440            checkTagSupport();
441            return ((BodyTagSupport)tag).getPreviousOut();
442        }
443        
444        /**
445         * Delegates to wrapped tag.
446         */
447        public void setBodyContent(BodyContent content)
448        {
449            tag.setBodyContent(content);
450        }
451        
452        /**
453         * Dumps the content of this and the nested tags.
454         */
455        public String toString()
456        {
457            return TagUtil.dumpTag(this, new StringBuffer(), 0);
458        }
459        
460        private NestedTag addChild(Object childTag)
461        {
462            if(childTag instanceof Tag)
463            {
464                ((Tag)childTag).setParent(this.tag);
465            }
466            else if(childTag instanceof SimpleTag)
467            {
468                ((SimpleTag)childTag).setParent(this.tag);
469            }
470            childs.add(childTag);
471            return (NestedTag)childTag;
472        }
473        
474        private void checkTagSupport()
475        {
476            if(!(tag instanceof BodyTagSupport))
477            {
478                throw new RuntimeException("This method can only be called if the wrapped tag is an instance of BodyTagSupport.");
479            }
480        }
481    }