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 * Implementation of {@link NestedTag#setDoRelease}.
092 */
093 public void setDoRelease(boolean doRelease)
094 {
095 this.doRelease = doRelease;
096 }
097
098 /**
099 * Implementation of {@link NestedTag#setDoReleaseRecursive}.
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 * Implementation of {@link NestedTag#populateAttributes}.
116 */
117 public void populateAttributes()
118 {
119 TagUtil.populateTag(tag, attributes, doRelease);
120 }
121
122 /**
123 * Implementation of {@link NestedTag#doLifecycle} for body
124 * tags.
125 */
126 public int doLifecycle() throws JspException
127 {
128 populateAttributes();
129 int result = tag.doStartTag();
130 if(Tag.EVAL_BODY_INCLUDE == result)
131 {
132 TagUtil.evalBody(childs, pageContext);
133 while(IterationTag.EVAL_BODY_AGAIN == doAfterBody())
134 {
135 TagUtil.evalBody(childs, pageContext);
136 }
137 }
138 else if(BodyTag.EVAL_BODY_BUFFERED == result)
139 {
140 MockBodyContent bodyContent = (MockBodyContent)pageContext.pushBody();
141 tag.setBodyContent(bodyContent);
142 tag.doInitBody();
143 TagUtil.evalBody(childs, pageContext);
144 while(IterationTag.EVAL_BODY_AGAIN == doAfterBody())
145 {
146 TagUtil.evalBody(childs, pageContext);
147 }
148 pageContext.popBody();
149 }
150 return tag.doEndTag();
151 }
152
153 /**
154 * Implementation of {@link NestedTag#getTag}.
155 * @throws <code>RuntimeException</code>, if the wrapped tag
156 * is not an instance of <code>TagSupport</code>
157 */
158 public TagSupport getTag()
159 {
160 checkTagSupport();
161 return (TagSupport)tag;
162 }
163
164 /**
165 * Implementation of {@link NestedTag#getWrappedTag}.
166 */
167 public JspTag getWrappedTag()
168 {
169 return tag;
170 }
171
172 /**
173 * Implementation of {@link NestedTag#removeChilds}.
174 */
175 public void removeChilds()
176 {
177 childs.clear();
178 }
179
180 /**
181 * Implementation of {@link NestedTag#getChilds}.
182 */
183 public List getChilds()
184 {
185 return childs;
186 }
187
188 /**
189 * Implementation of {@link NestedTag#getChild}.
190 */
191 public Object getChild(int index)
192 {
193 return childs.get(index);
194 }
195
196 /**
197 * Implementation of {@link NestedTag#addTextChild}.
198 */
199 public void addTextChild(String text)
200 {
201 if(null == text) text = "";
202 childs.add(text);
203 }
204
205 /**
206 * Implementation of {@link NestedTag#addDynamicChild}.
207 */
208 public void addDynamicChild(DynamicChild child)
209 {
210 if(null == child) return;
211 childs.add(child);
212 }
213
214 /**
215 * Implementation of {@link NestedTag#addTagChild(Class)}.
216 */
217 public NestedTag addTagChild(Class tag)
218 {
219 return addTagChild(tag, new HashMap());
220 }
221
222 /**
223 * Implementation of {@link NestedTag#addTagChild(Class, Map)}.
224 */
225 public NestedTag addTagChild(Class tag, Map attributeMap)
226 {
227 Object childTag = TagUtil.createNestedTagInstance(tag, this.pageContext, attributeMap);
228 return (NestedTag)addChild(childTag);
229 }
230
231 /**
232 * Implementation of {@link NestedTag#addTagChild(TagSupport)}.
233 */
234 public NestedTag addTagChild(TagSupport tag)
235 {
236 return addTagChild(tag, new HashMap());
237 }
238
239 /**
240 * Implementation of {@link NestedTag#addTagChild(TagSupport, Map)}.
241 */
242 public NestedTag addTagChild(TagSupport tag, Map attributeMap)
243 {
244 Object childTag = (Object)TagUtil.createNestedTagInstance(tag, this.pageContext, attributeMap);
245 return (NestedTag)addChild(childTag);
246 }
247
248 /**
249 * Implementation of {@link NestedTag#addTagChild(JspTag)}.
250 */
251 public NestedTag addTagChild(JspTag tag)
252 {
253 return addTagChild(tag, new HashMap());
254 }
255
256 /**
257 * Implementation of {@link NestedTag#addTagChild(JspTag, Map)}.
258 */
259 public NestedTag addTagChild(JspTag tag, Map attributeMap)
260 {
261 Object childTag = TagUtil.createNestedTagInstance(tag, this.pageContext, attributeMap);
262 return (NestedTag)addChild(childTag);
263 }
264
265 /**
266 * Delegates to wrapped tag.
267 */
268 public int doAfterBody() throws JspException
269 {
270 return tag.doAfterBody();
271 }
272
273 /**
274 * Delegates to wrapped tag.
275 */
276 public int doEndTag() throws JspException
277 {
278 return tag.doEndTag();
279 }
280
281 /**
282 * Delegates to wrapped tag.
283 */
284 public int doStartTag() throws JspException
285 {
286 return tag.doStartTag();
287 }
288
289 /**
290 * Delegates to wrapped tag.
291 * @throws <code>RuntimeException</code>, if the wrapped tag
292 * is not an instance of <code>TagSupport</code>
293 */
294 public String getId()
295 {
296 checkTagSupport();
297 return ((TagSupport)tag).getId();
298 }
299
300 /**
301 * Delegates to wrapped tag.
302 */
303 public Tag getParent()
304 {
305 return tag.getParent();
306 }
307
308 /**
309 * Delegates to wrapped tag.
310 * @throws <code>RuntimeException</code>, if the wrapped tag
311 * is not an instance of <code>TagSupport</code>
312 */
313 public Object getValue(String key)
314 {
315 checkTagSupport();
316 return ((TagSupport)tag).getValue(key);
317 }
318
319 /**
320 * Delegates to wrapped tag.
321 * @throws <code>RuntimeException</code>, if the wrapped tag
322 * is not an instance of <code>TagSupport</code>
323 */
324 public Enumeration getValues()
325 {
326 checkTagSupport();
327 return ((TagSupport)tag).getValues();
328 }
329
330 /**
331 * Delegates to wrapped tag.
332 */
333 public void release()
334 {
335 tag.release();
336 }
337
338 /**
339 * Delegates to wrapped tag.
340 * @throws <code>RuntimeException</code>, if the wrapped tag
341 * is not an instance of <code>TagSupport</code>
342 */
343 public void removeValue(String value)
344 {
345 checkTagSupport();
346 ((TagSupport)tag).removeValue(value);
347 }
348
349 /**
350 * Delegates to wrapped tag.
351 * @throws <code>RuntimeException</code>, if the wrapped tag
352 * is not an instance of <code>TagSupport</code>
353 */
354 public void setId(String id)
355 {
356 checkTagSupport();
357 ((TagSupport)tag).setId(id);
358 }
359
360 /**
361 * Delegates to wrapped tag. Also calls <code>setPageContext</code>
362 * for all child tags.
363 */
364 public void setPageContext(PageContext pageContext)
365 {
366 this.pageContext = pageContext;
367 tag.setPageContext(pageContext);
368 for(int ii = 0; ii < childs.size(); ii++)
369 {
370 Object child = childs.get(ii);
371 if(child instanceof Tag)
372 {
373 ((Tag)child).setPageContext(pageContext);
374 }
375 else if(child instanceof SimpleTag)
376 {
377 ((SimpleTag)child).setJspContext(pageContext);
378 }
379 }
380 }
381
382 /**
383 * Delegates to wrapped tag.
384 */
385 public void setParent(Tag parent)
386 {
387 tag.setParent(parent);
388 }
389
390 /**
391 * Delegates to wrapped tag.
392 * @throws <code>RuntimeException</code>, if the wrapped tag
393 * is not an instance of <code>TagSupport</code>
394 */
395 public void setValue(String key, Object value)
396 {
397 checkTagSupport();
398 ((TagSupport)tag).setValue(key, value);
399 }
400
401 /**
402 * Delegates to wrapped tag.
403 */
404 public void doInitBody() throws JspException
405 {
406 tag.doInitBody();
407 }
408
409 /**
410 * Delegates to wrapped tag.
411 * @throws <code>RuntimeException</code>, if the wrapped tag
412 * is not an instance of <code>BodyTagSupport</code>
413 */
414 public BodyContent getBodyContent()
415 {
416 checkTagSupport();
417 return ((BodyTagSupport)tag).getBodyContent();
418 }
419
420 /**
421 * Delegates to wrapped tag.
422 * @throws <code>RuntimeException</code>, if the wrapped tag
423 * is not an instance of <code>BodyTagSupport</code>
424 */
425 public JspWriter getPreviousOut()
426 {
427 checkTagSupport();
428 return ((BodyTagSupport)tag).getPreviousOut();
429 }
430
431 /**
432 * Delegates to wrapped tag.
433 */
434 public void setBodyContent(BodyContent content)
435 {
436 tag.setBodyContent(content);
437 }
438
439 /**
440 * Dumps the content of this and the nested tags.
441 */
442 public String toString()
443 {
444 return TagUtil.dumpTag(this, new StringBuffer(), 0);
445 }
446
447 private NestedTag addChild(Object childTag)
448 {
449 if(childTag instanceof Tag)
450 {
451 ((Tag)childTag).setParent(this.tag);
452 }
453 else if(childTag instanceof SimpleTag)
454 {
455 ((SimpleTag)childTag).setParent(this.tag);
456 }
457 childs.add(childTag);
458 return (NestedTag)childTag;
459 }
460
461 private void checkTagSupport()
462 {
463 if(!(tag instanceof BodyTagSupport))
464 {
465 throw new RuntimeException("This method can only be called if the wrapped tag is an instance of BodyTagSupport.");
466 }
467 }
468 }