001 package com.mockrunner.mock.web;
002
003 import java.io.IOException;
004 import java.io.Writer;
005 import java.util.ArrayList;
006 import java.util.HashMap;
007 import java.util.List;
008 import java.util.Map;
009
010 import javax.servlet.jsp.JspContext;
011 import javax.servlet.jsp.JspException;
012 import javax.servlet.jsp.PageContext;
013 import javax.servlet.jsp.tagext.JspFragment;
014 import javax.servlet.jsp.tagext.JspTag;
015 import javax.servlet.jsp.tagext.SimpleTag;
016 import javax.servlet.jsp.tagext.Tag;
017 import javax.servlet.jsp.tagext.TagAdapter;
018
019 import com.mockrunner.tag.DynamicChild;
020 import com.mockrunner.tag.NestedTag;
021 import com.mockrunner.tag.TagUtil;
022
023 /**
024 * Mock implementation of <code>JspFragment</code>.
025 * The body of a simple tag is a <code>JspFragment</code>.
026 * All child handling methods of {@link com.mockrunner.tag.NestedSimpleTag}
027 * delegate to an underlying instance of this class.
028 */
029 public class MockJspFragment extends JspFragment
030 {
031 private JspContext jspContext;
032 private List childs;
033 private JspTag parent;
034
035 public MockJspFragment(JspContext jspContext)
036 {
037 this(jspContext, null);
038 }
039
040 public MockJspFragment(JspContext jspContext, JspTag parent)
041 {
042 this.jspContext = jspContext;
043 this.parent = parent;
044 childs = new ArrayList();
045 }
046
047 /**
048 * Returns the parent tag.
049 * @return the parent tag
050 */
051 public JspTag getParent()
052 {
053 return parent;
054 }
055
056 /**
057 * Sets the parent tag.
058 * @param parent the parent tag
059 */
060 public void setParent(JspTag parent)
061 {
062 this.parent = parent;
063 }
064
065 /**
066 * Returns the <code>JspContext</code>.
067 * @return the <code>JspContext</code>
068 */
069 public JspContext getJspContext()
070 {
071 return jspContext;
072 }
073
074 /**
075 * Sets the <code>JspContext</code>. Also calls <code>setJspContext</code>
076 * (or <code>setPageContext</code>) for all child tags.
077 * <code>setPageContext</code> is only called if the specified <code>JspContext</code>
078 * is an instance of <code>PageContext</code>.
079 * @param jspContext the <code>JspContext</code>
080 */
081 public void setJspContext(JspContext jspContext)
082 {
083 this.jspContext = jspContext;
084 for(int ii = 0; ii < childs.size(); ii++)
085 {
086 Object child = childs.get(ii);
087 if(child instanceof Tag && jspContext instanceof PageContext)
088 {
089 ((Tag)child).setPageContext((PageContext)jspContext);
090 }
091 else if(child instanceof SimpleTag)
092 {
093 ((SimpleTag)child).setJspContext(jspContext);
094 }
095 }
096 }
097
098 /**
099 * Executes the fragment and directs all output to the given Writer, or the JspWriter
100 * returned by the getOut() method of the JspContext associated with the fragment
101 * if out is null (copied from <code>JspFragment</code> JavaDoc).
102 * @param writer the Writer to output the fragment to, or null if output should be
103 * sent to JspContext.getOut().
104 */
105 public void invoke(Writer writer) throws JspException, IOException
106 {
107 if(null == jspContext) return;
108 if(null != writer)
109 {
110 jspContext.pushBody(writer);
111 }
112 TagUtil.evalBody(childs, jspContext);
113 jspContext.getOut().flush();
114 if(null != writer)
115 {
116 jspContext.popBody();
117 }
118 }
119
120 /**
121 * Removes all childs.
122 */
123 public void removeChilds()
124 {
125 childs.clear();
126 }
127
128 /**
129 * Returns the <code>List</code> of childs.
130 * @return the <code>List</code> of childs
131 */
132 public List getChilds()
133 {
134 return childs;
135 }
136
137 /**
138 * Returns a child specified by its index.
139 * @param index the index
140 * @return the child
141 */
142 public Object getChild(int index)
143 {
144 return childs.get(index);
145 }
146
147 /**
148 * Adds a text child simulating static body content.
149 * @param text the static text
150 */
151 public void addTextChild(String text)
152 {
153 if(null == text) text = "";
154 childs.add(text);
155 }
156
157 /**
158 * Adds a dynamic child simulating scriptlets and
159 * EL expressions. Check out
160 * {@link com.mockrunner.tag.TagUtil#evalBody(List, Object)}
161 * for details about child handling.
162 * @param child the dynamic child instance
163 */
164 public void addDynamicChild(DynamicChild child)
165 {
166 if(null == child) return;
167 childs.add(child);
168 }
169
170 /**
171 * Adds a tag child simulating nested tags.
172 * The corresponding <code>NestedTag</code> will be created
173 * automatically wrapping the specified tag. An empty attribute
174 * <code>Map</code> will be used for the tag.
175 * @param tag the tag class
176 */
177 public NestedTag addTagChild(Class tag)
178 {
179 return addTagChild(tag, new HashMap());
180 }
181
182 /**
183 * Adds a tag child simulating nested tags.
184 * The corresponding <code>NestedTag</code> will be created
185 * automatically wrapping the specified tag. The attributes
186 * <code>Map</code> contains the attributes of this tag
187 * (<i>propertyname</i> maps to <i>propertyvalue</i>).
188 * @param tag the tag class
189 * @param attributeMap the attribute map
190 */
191 public NestedTag addTagChild(Class tag, Map attributeMap)
192 {
193 Object childTag = TagUtil.createNestedTagInstance(tag, jspContext, attributeMap);
194 return addChild(childTag);
195 }
196
197 /**
198 * Adds a tag child simulating nested tags.
199 * <code>NestedTag</code> will be created automatically
200 * wrapping the specified tag. An empty attribute <code>Map</code>
201 * will be used for the tag.
202 * @param tag the tag
203 */
204 public NestedTag addTagChild(JspTag tag)
205 {
206 return addTagChild(tag, new HashMap());
207 }
208
209 /**
210 * Adds a tag child simulating nested tags.
211 * The corresponding <code>NestedTag</code> will be created
212 * automatically wrapping the specified tag. The attributes
213 * <code>Map</code> contains the attributes of this tag
214 * (<i>propertyname</i> maps to <i>propertyvalue</i>).
215 * @param tag the tag
216 * @param attributeMap the attribute map
217 */
218 public NestedTag addTagChild(JspTag tag, Map attributeMap)
219 {
220 Object childTag = TagUtil.createNestedTagInstance(tag, jspContext, attributeMap);
221 return addChild(childTag);
222 }
223
224 private NestedTag addChild(Object childTag)
225 {
226 if(childTag instanceof SimpleTag)
227 {
228 ((SimpleTag)childTag).setParent(parent);
229 }
230 else if(parent instanceof Tag)
231 {
232 if(childTag instanceof Tag)
233 {
234 ((Tag)childTag).setParent((Tag)parent);
235 }
236 }
237 else if(parent instanceof SimpleTag)
238 {
239 if(childTag instanceof Tag)
240 {
241 ((Tag)childTag).setParent(new TagAdapter((SimpleTag)parent));
242 }
243 }
244 childs.add(childTag);
245 return (NestedTag)childTag;
246 }
247 }