001: package com.mockrunner.tag;
002:
003: import java.io.IOException;
004: import java.util.HashMap;
005: import java.util.Map;
006:
007: import javax.servlet.jsp.JspException; //import javax.servlet.jsp.tagext.JspTag;
008: import javax.servlet.jsp.tagext.Tag;
009: import javax.servlet.jsp.tagext.TagSupport;
010:
011: import com.mockrunner.base.HTMLOutputModule;
012: import com.mockrunner.base.NestedApplicationException;
013: import com.mockrunner.mock.web.MockJspWriter;
014: import com.mockrunner.mock.web.MockPageContext;
015: import com.mockrunner.mock.web.WebMockObjectFactory;
016:
017: /**
018: * Module for custom tag tests. Simulates the container by
019: * performing the tag lifecycle.
020: */
021: public class TagTestModule extends HTMLOutputModule {
022: private WebMockObjectFactory mockFactory;
023: private NestedTag tag;
024:
025: public TagTestModule(WebMockObjectFactory mockFactory) {
026: super (mockFactory);
027: this .mockFactory = mockFactory;
028: }
029:
030: /**
031: * Creates a tag. Internally a {@link NestedTag}
032: * is created but the wrapped tag is returned. If you
033: * simply want to test the output of the tag without
034: * nesting other tags, you do not have to care about the
035: * {@link NestedTag}, just use the returned instance.
036: * An empty attribute <code>Map</code> will be used for
037: * the tag.
038: * @param tagClass the class of the tag
039: * @return instance of <code>TagSupport</code> or <code>BodyTagSupport</code>
040: * @throws <code>RuntimeException</code>, if the created tag
041: * is not an instance of <code>TagSupport</code>
042: */
043: public TagSupport createTag(Class tagClass) {
044: if (!TagSupport.class.isAssignableFrom(tagClass)) {
045: throw new IllegalArgumentException(
046: "specified class is not an instance of TagSupport. Please use createWrappedTag");
047: }
048: return createTag(tagClass, new HashMap());
049: }
050:
051: /**
052: * Creates a tag. Internally a {@link NestedTag}
053: * is created but the wrapped tag is returned. If you
054: * simply want to test the output of the tag without
055: * nesting other tags, you do not have to care about the
056: * {@link NestedTag}, just use the returned instance.
057: * The attributes <code>Map</code> contains the attributes
058: * of this tag (<i>propertyname</i> maps to <i>propertyvalue</i>).
059: * The attributes are populated (i.e. the tags setters are called)
060: * during the lifecycle or with an explicit call of
061: * {@link #populateAttributes}.
062: * @param tagClass the class of the tag
063: * @param attributes the attribute map
064: * @return instance of <code>TagSupport</code> or <code>BodyTagSupport</code>
065: * @throws <code>RuntimeException</code>, if the created tag
066: * is not an instance of <code>TagSupport</code>
067: */
068: public TagSupport createTag(Class tagClass, Map attributes) {
069: if (!TagSupport.class.isAssignableFrom(tagClass)) {
070: throw new IllegalArgumentException(
071: "specified class is not an instance of TagSupport. Please use createWrappedTag");
072: }
073: return createNestedTag(tagClass, attributes).getTag();
074: }
075:
076: /**
077: * Creates a tag. Internally a {@link NestedTag}
078: * is created but the wrapped tag is returned. If you
079: * simply want to test the output of the tag without
080: * nesting other tags, you do not have to care about the
081: * {@link NestedTag}, just use the returned instance.
082: * An empty attribute <code>Map</code> will be used for
083: * the tag.
084: * This method can be used for all kind of tags. The tag
085: * class does not need to be a subclass of <code>TagSupport</code>.
086: * @param tagClass the class of the tag
087: * @return instance of <code>JspTag</code>
088: */
089: /*public JspTag createWrappedTag(Class tagClass)
090: {
091: return createWrappedTag(tagClass, new HashMap());
092: }*/
093:
094: /**
095: * Creates a tag. Internally a {@link NestedTag}
096: * is created but the wrapped tag is returned. If you
097: * simply want to test the output of the tag without
098: * nesting other tags, you do not have to care about the
099: * {@link NestedTag}, just use the returned instance.
100: * The attributes <code>Map</code> contains the attributes
101: * of this tag (<i>propertyname</i> maps to <i>propertyvalue</i>).
102: * The attributes are populated (i.e. the tags setters are called)
103: * during the lifecycle or with an explicit call of
104: * {@link #populateAttributes}.
105: * This method can be used for all kind of tags. The tag
106: * class does not need to be a subclass of <code>TagSupport</code>.
107: * @param tagClass the class of the tag
108: * @param attributes the attribute map
109: * @return instance of <code>JspTag</code>
110: */
111: /*public JspTag createWrappedTag(Class tagClass, Map attributes)
112: {
113: return createNestedTag(tagClass, attributes).getWrappedTag();
114: }*/
115:
116: /**
117: * Creates a {@link NestedTag} and returns it. You can
118: * add child tags or body blocks to the {@link NestedTag}.
119: * Use {@link #getTag} to get the wrapped tag.
120: * An empty attribute <code>Map</code> will be used for
121: * the tag.
122: * @param tagClass the class of the tag
123: * @return instance of {@link NestedStandardTag}, {@link NestedBodyTag} or
124: * {@link NestedSimpleTag}
125: */
126: public NestedTag createNestedTag(Class tagClass) {
127: return createNestedTag(tagClass, new HashMap());
128: }
129:
130: /**
131: * Creates a {@link NestedTag} and returns it. You can
132: * add child tags or body blocks to the {@link NestedTag}.
133: * Use {@link #getTag} to get the wrapped tag.
134: * The attributes <code>Map</code> contains the attributes
135: * of this tag (<i>propertyname</i> maps to <i>propertyvalue</i>).
136: * The attributes are populated (i.e. the tags setters are called)
137: * during the lifecycle or with an explicit call of
138: * {@link #populateAttributes}.
139: * @param tagClass the class of the tag
140: * @param attributes the attribute map
141: * @return instance of {@link NestedStandardTag}, {@link NestedBodyTag} or
142: * {@link NestedSimpleTag}
143: */
144: public NestedTag createNestedTag(Class tagClass, Map attributes) {
145: try {
146: this .tag = (NestedTag) TagUtil.createNestedTagInstance(
147: tagClass, getMockPageContext(), attributes);
148: return this .tag;
149: } catch (IllegalArgumentException exc) {
150: throw exc;
151: } catch (Exception exc) {
152: throw new NestedApplicationException(exc);
153: }
154: }
155:
156: /**
157: * Creates a {@link NestedTag} and returns it. You can
158: * add child tags or body blocks to the {@link NestedTag}.
159: * Use {@link #getTag} to get the wrapped tag.
160: * An empty attribute <code>Map</code> will be used for
161: * the tag.
162: * @param tag the tag
163: * @return instance of {@link NestedStandardTag} or {@link NestedBodyTag}
164: */
165: public NestedTag setTag(TagSupport tag) {
166: return setTag(tag, new HashMap());
167: }
168:
169: /**
170: * Creates a {@link NestedTag} and returns it. You can
171: * add child tags or body blocks to the {@link NestedTag}.
172: * Use {@link #getTag} to get the wrapped tag.
173: * The attributes <code>Map</code> contains the attributes
174: * of this tag (<i>propertyname</i> maps to <i>propertyvalue</i>).
175: * The attributes are populated (i.e. the tags setters are called)
176: * during the lifecycle or with an explicit call of
177: * {@link #populateAttributes}.
178: * @param tag the tag
179: * @param attributes the attribute map
180: * @return instance of {@link NestedStandardTag} or {@link NestedBodyTag}
181: */
182: public NestedTag setTag(TagSupport tag, Map attributes) {
183: try {
184: this .tag = (NestedTag) TagUtil.createNestedTagInstance(tag,
185: getMockPageContext(), attributes);
186: return this .tag;
187: } catch (IllegalArgumentException exc) {
188: throw exc;
189: } catch (Exception exc) {
190: throw new NestedApplicationException(exc);
191: }
192: }
193:
194: /**
195: * Creates a {@link NestedTag} and returns it. You can
196: * add child tags or body blocks to the {@link NestedTag}.
197: * Use {@link #getTag} to get the wrapped tag.
198: * An empty attribute <code>Map</code> will be used for
199: * the tag.
200: * This method can be used for all kind of tags. The tag
201: * class does not need to be a subclass of <code>TagSupport</code>.
202: * @param tag the tag
203: * @return instance of {@link NestedStandardTag}, {@link NestedBodyTag} or
204: * {@link NestedSimpleTag}
205: */
206: /*public NestedTag setTag(JspTag tag)
207: {
208: return setTag(tag, new HashMap());
209: }*/
210:
211: /**
212: * Creates a {@link NestedTag} and returns it. You can
213: * add child tags or body blocks to the {@link NestedTag}.
214: * Use {@link #getTag} to get the wrapped tag.
215: * The attributes <code>Map</code> contains the attributes
216: * of this tag (<i>propertyname</i> maps to <i>propertyvalue</i>).
217: * The attributes are populated (i.e. the tags setters are called)
218: * during the lifecycle or with an explicit call of
219: * {@link #populateAttributes}.
220: * This method can be used for all kind of tags. The tag
221: * class does not need to be a subclass of <code>TagSupport</code>.
222: * @param tag the tag
223: * @param attributes the attribute map
224: * @return instance of {@link NestedStandardTag}, {@link NestedBodyTag} or
225: * {@link NestedSimpleTag}
226: */
227: /*public NestedTag setTag(JspTag tag, Map attributes)
228: {
229: try
230: {
231: this.tag = (NestedTag)TagUtil.createNestedTagInstance(tag, getMockPageContext(), attributes);
232: return this.tag;
233: }
234: catch(IllegalArgumentException exc)
235: {
236: throw exc;
237: }
238: catch(Exception exc)
239: {
240: throw new NestedApplicationException(exc);
241: }
242: }*/
243:
244: /**
245: * Specify if the <code>release</code> method should be called
246: * after processing the tag lifecycle. Delegates to {@link NestedTag#setDoRelease}
247: * Defaults to <code>false</code>. It's the container behaviour to call
248: * <code>release</code>, but it's usually not necessary in the tests,
249: * because the tag instances are not reused during a test run.
250: * @param doRelease should release be called
251: */
252: public void setDoRelease(boolean doRelease) {
253: if (null == tag) {
254: throw new RuntimeException("Not current tag set");
255: }
256: tag.setDoRelease(doRelease);
257: }
258:
259: /**
260: * Specify if the <code>release</code> method should be called
261: * after processing the tag lifecycle. Delegates to {@link NestedTag#setDoReleaseRecursive}
262: * Defaults to <code>false</code>. It's the container behaviour to call
263: * <code>release</code>, but it's usually not necessary in the tests,
264: * because the tag instances are not reused during a test run.
265: * @param doRelease should release be called
266: */
267: public void setDoReleaseRecursive(boolean doRelease) {
268: if (null == tag) {
269: throw new RuntimeException("Not current tag set");
270: }
271: tag.setDoReleaseRecursive(doRelease);
272: }
273:
274: /**
275: * Populates the attributes of the underlying tag by
276: * calling {@link NestedTag#populateAttributes}. The setters
277: * of the tag are called. Please note that child tags are not
278: * populated. This is done during the lifecycle.
279: */
280: public void populateAttributes() {
281: if (null == tag) {
282: throw new RuntimeException("Not current tag set");
283: }
284: tag.populateAttributes();
285: }
286:
287: /**
288: * Sets the body of the tag as a static string. Please
289: * note that all childs of the underlying {@link NestedTag}
290: * are deleted and the static content is set. If you want
291: * to use nested tags, please use the method {@link NestedTag#addTextChild}
292: * to set static content.
293: * @param body the static body content
294: */
295: public void setBody(String body) {
296: if (null == tag) {
297: throw new RuntimeException("Not current tag set");
298: }
299: tag.removeChilds();
300: tag.addTextChild(body);
301: }
302:
303: /**
304: * Returns the current wrapped tag.
305: * @return instance of <code>TagSupport</code> or <code>BodyTagSupport</code>
306: * @throws <code>RuntimeException</code>, if the wrapped tag
307: * is not an instance of <code>TagSupport</code>
308: */
309: public TagSupport getTag() {
310: if (null == tag)
311: return null;
312: return tag.getTag();
313: }
314:
315: /**
316: * Returns the current wrapped tag.
317: * This method can be used for all kind of tags. The tag
318: * class does not need to be a subclass of <code>TagSupport</code>.
319: * @return instance of <code>JspTag</code>
320: */
321: /*public JspTag getWrappedTag()
322: {
323: if(null == tag) return null;
324: return tag.getWrappedTag();
325: }*/
326:
327: /**
328: * Returns the current nested tag. You can
329: * add child tags or body blocks to the {@link NestedTag}.
330: * Use {@link #getTag} to get the wrapped tag.
331: * @return instance of {@link NestedStandardTag} or {@link NestedBodyTag}
332: */
333: public NestedTag getNestedTag() {
334: return tag;
335: }
336:
337: /**
338: * Returns the <code>MockPageContext</code> object.
339: * Delegates to {@link com.mockrunner.mock.web.WebMockObjectFactory#getMockPageContext}.
340: * @return the MockPageContext
341: */
342: public MockPageContext getMockPageContext() {
343: return mockFactory.getMockPageContext();
344: }
345:
346: /**
347: * Calls the <code>doStartTag</code> method of the current tag.
348: * @throws <code>RuntimeException</code>, if the tag
349: * is not a simple tag
350: */
351: /*public void doTag()
352: {
353: if(null == tag)
354: {
355: throw new RuntimeException("No current tag set");
356: }
357: if(!isSimpleTag())
358: {
359: throw new RuntimeException("Tag is no simple tag");
360: }
361: try
362: {
363: ((NestedSimpleTag)tag).doTag();
364: }
365: catch(Exception exc)
366: {
367: throw new NestedApplicationException(exc);
368: }
369: }*/
370:
371: /**
372: * Calls the <code>doStartTag</code> method of the current tag.
373: * @return the result of <code>doStartTag</code>
374: * @throws <code>RuntimeException</code>, if the tag
375: * is a simple tag
376: */
377: public int doStartTag() {
378: if (null == tag) {
379: throw new RuntimeException("No current tag set");
380: }
381: /*if(isSimpleTag())
382: {
383: throw new RuntimeException("Cannot call doStartTag() on simple tags");
384: }*/
385: try {
386: return ((Tag) tag).doStartTag();
387: } catch (JspException exc) {
388: throw new NestedApplicationException(exc);
389: }
390: }
391:
392: /**
393: * Calls the <code>doEndTag</code> method of the current tag.
394: * @return the result of <code>doEndTag</code>
395: * @throws <code>RuntimeException</code>, if the tag
396: * is a simple tag
397: */
398: public int doEndTag() {
399: if (null == tag) {
400: throw new RuntimeException("No current tag set");
401: }
402: /*if(isSimpleTag())
403: {
404: throw new RuntimeException("Cannot call doEndTag() on simple tags");
405: }*/
406: try {
407: return ((Tag) tag).doEndTag();
408: } catch (JspException exc) {
409: throw new NestedApplicationException(exc);
410: }
411: }
412:
413: /**
414: * Calls the <code>doInitBody</code> method of the current tag.
415: * @throws RuntimeException if the current tag is no body tag
416: * @throws <code>RuntimeException</code>, if the tag
417: * is a simple tag
418: */
419: public void doInitBody() {
420: if (null == tag) {
421: throw new RuntimeException("No current tag set");
422: }
423: if (!isBodyTag()) {
424: throw new RuntimeException("Tag is no body tag");
425: }
426: try {
427: NestedBodyTag bodyTag = (NestedBodyTag) tag;
428: bodyTag.doInitBody();
429: } catch (JspException exc) {
430: throw new NestedApplicationException(exc);
431: }
432: }
433:
434: /**
435: * Calls the <code>doAfterBody</code> method of the current tag.
436: * @return the result of <code>doAfterBody</code>
437: * @throws <code>RuntimeException</code>, if the tag
438: * is a simple tag
439: */
440: public int doAfterBody() {
441: if (null == tag) {
442: throw new RuntimeException("No current tag set");
443: }
444: /*if(isSimpleTag())
445: {
446: throw new RuntimeException("Cannot call doAfterBody() on simple tags");
447: }*/
448: try {
449: return ((TagSupport) tag).doAfterBody();
450: } catch (JspException exc) {
451: throw new NestedApplicationException(exc);
452: }
453: }
454:
455: /**
456: * Calls the <code>release</code> method of the current tag.
457: * @throws <code>RuntimeException</code>, if the tag
458: * is a simple tag
459: */
460: public void release() {
461: /*if(isSimpleTag())
462: {
463: throw new RuntimeException("Cannot call release() on simple tags");
464: }*/
465: ((Tag) tag).release();
466: }
467:
468: /**
469: * Performs the tags lifecycle by calling {@link NestedTag#doLifecycle}.
470: * All <code>doBody</code> and <code>doTag</code> methods are called as
471: * in the real web container. The evaluation of the body is simulated
472: * by performing the lifecycle recursively for all childs of the
473: * {@link NestedTag}.
474: * @return the result of the final <code>doEndTag</code> call or -1 in
475: * the case of a simple tag
476: */
477: public int processTagLifecycle() {
478: if (null == tag) {
479: throw new RuntimeException("No current tag set");
480: }
481: try {
482: return ((NestedTag) tag).doLifecycle();
483: } catch (JspException exc) {
484: throw new NestedApplicationException(exc);
485: }
486: }
487:
488: /**
489: * Resets the output buffer.
490: */
491: public void clearOutput() {
492: MockJspWriter writer = (MockJspWriter) mockFactory
493: .getMockPageContext().getOut();
494: try {
495: writer.clearBuffer();
496: } catch (IOException exc) {
497: throw new NestedApplicationException(exc);
498: }
499: }
500:
501: /**
502: * Gets the output data the current tag has rendered. Makes only sense
503: * after calling at least {@link #doStartTag} or {@link #processTagLifecycle}
504: * @return the output data
505: */
506: public String getOutput() {
507: MockJspWriter writer = (MockJspWriter) mockFactory
508: .getMockPageContext().getOut();
509: return writer.getOutputAsString();
510: }
511:
512: private boolean isBodyTag() {
513: return (tag instanceof NestedBodyTag);
514: }
515:
516: /*private boolean isSimpleTag()
517: {
518: return (tag instanceof NestedSimpleTag);
519: }*/
520: }
|