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