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