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    }