001 package com.mockrunner.tag;
002
003 import java.io.IOException;
004 import java.util.Iterator;
005 import java.util.List;
006 import java.util.Map;
007
008 import javax.servlet.jsp.JspContext;
009 import javax.servlet.jsp.JspException;
010 import javax.servlet.jsp.PageContext;
011 import javax.servlet.jsp.tagext.BodyTag;
012 import javax.servlet.jsp.tagext.DynamicAttributes;
013 import javax.servlet.jsp.tagext.SimpleTag;
014 import javax.servlet.jsp.tagext.Tag;
015
016 import org.apache.commons.beanutils.BeanUtils;
017 import org.apache.commons.beanutils.PropertyUtils;
018 import org.apache.commons.logging.Log;
019 import org.apache.commons.logging.LogFactory;
020
021 import com.mockrunner.base.NestedApplicationException;
022 import com.mockrunner.util.common.MethodUtil;
023 import com.mockrunner.util.common.StringUtil;
024
025 /**
026 * Util class for tag test framework.
027 * Please note, that the methods of this class take
028 * <code>Object</code> parameters where <code>JspTag</code>
029 * or <code>JspContext</code> would be suitable. The reason is,
030 * that these classes do not exist in J2EE 1.3. This class is
031 * usable with J2EE 1.3 and J2EE 1.4.
032 */
033 public class TagUtil
034 {
035 private final static Log log = LogFactory.getLog(TagUtil.class);
036
037 /**
038 * Creates an {@link com.mockrunner.tag.NestedTag} instance wrapping the
039 * specified tag. Returns an instance of {@link com.mockrunner.tag.NestedStandardTag}
040 * resp. {@link com.mockrunner.tag.NestedBodyTag} depending on the
041 * type of specified tag.
042 * @param tag the tag class
043 * @param pageContext the corresponding <code>PageContext</code> or <code>JspContext</code>
044 * @param attributes the attribute map
045 * @return the instance of {@link com.mockrunner.tag.NestedTag}
046 * @throws IllegalArgumentException if <code>tag</code> is <code>null</code>
047 */
048 public static Object createNestedTagInstance(Class tag, Object pageContext, Map attributes)
049 {
050 if(null == tag) throw new IllegalArgumentException("tag must not be null");
051 Object tagObject;
052 try
053 {
054 tagObject = tag.newInstance();
055 }
056 catch(Exception exc)
057 {
058 log.error(exc.getMessage(), exc);
059 throw new NestedApplicationException(exc);
060 }
061 return createNestedTagInstance(tagObject, pageContext, attributes);
062 }
063
064 /**
065 * Creates an {@link com.mockrunner.tag.NestedTag} instance wrapping the
066 * specified tag. Returns an instance of {@link com.mockrunner.tag.NestedStandardTag}
067 * resp. {@link com.mockrunner.tag.NestedBodyTag} depending on the
068 * type of specified tag.
069 * @param tag the tag
070 * @param pageContext the corresponding <code>PageContext</code> or <code>JspContext</code>
071 * @param attributes the attribute map
072 * @return the instance of {@link com.mockrunner.tag.NestedTag}
073 * @throws IllegalArgumentException if <code>tag</code> is <code>null</code>
074 */
075 public static Object createNestedTagInstance(Object tag, Object pageContext, Map attributes)
076 {
077 if(null == tag) throw new IllegalArgumentException("tag must not be null");
078 Object nestedTag = null;
079 if(tag instanceof BodyTag)
080 {
081 checkPageContext(pageContext);
082 nestedTag = new NestedBodyTag((BodyTag)tag, (PageContext)pageContext, attributes);
083 }
084 else if(tag instanceof Tag)
085 {
086 checkPageContext(pageContext);
087 nestedTag = new NestedStandardTag((Tag)tag, (PageContext)pageContext, attributes);
088 }
089 else if(tag instanceof SimpleTag)
090 {
091 checkJspContext(pageContext);
092 nestedTag = new NestedSimpleTag((SimpleTag)tag, (JspContext)pageContext, attributes);
093 }
094 else
095 {
096 throw new IllegalArgumentException("tag must be an instance of Tag or SimpleTag");
097 }
098 return nestedTag;
099 }
100
101 private static void checkPageContext(Object pageContext)
102 {
103 if(pageContext instanceof PageContext) return;
104 throw new IllegalArgumentException("pageContext must be an instance of PageContext");
105 }
106
107 private static void checkJspContext(Object pageContext)
108 {
109 if(pageContext instanceof JspContext) return;
110 throw new IllegalArgumentException("pageContext must be an instance of JspContext");
111 }
112
113 /**
114 * Populates the specified attributes to the specified tag. Calls the
115 * <code>release</code> method before populating, if <i>doRelease</i> is set to
116 * <code>true</code>.
117 * @param tag the tag
118 * @param attributes the attribute map
119 * @param doRelease should release be called
120 */
121 public static void populateTag(Object tag, Map attributes, boolean doRelease)
122 {
123 if(doRelease) MethodUtil.invoke(tag, "release");
124 if(null == attributes || attributes.isEmpty()) return;
125 try
126 {
127 Iterator names = attributes.keySet().iterator();
128 while(names.hasNext())
129 {
130 String currentName = (String)names.next();
131 Object currentValue = attributes.get(currentName);
132 if(currentValue instanceof DynamicAttribute)
133 {
134 populateDynamicAttribute(tag, currentName, (DynamicAttribute)currentValue);
135 return;
136 }
137 if(PropertyUtils.isWriteable(tag, currentName))
138 {
139 BeanUtils.copyProperty(tag, currentName, attributes.get(currentName));
140 }
141 else if(tag instanceof DynamicAttributes)
142 {
143 populateDynamicAttribute(tag, currentName, new DynamicAttribute(null, currentValue));
144 }
145 }
146 }
147 catch(IllegalArgumentException exc)
148 {
149 throw exc;
150 }
151 catch(Exception exc)
152 {
153 log.error(exc.getMessage(), exc);
154 throw new NestedApplicationException(exc);
155 }
156 }
157
158 private static void populateDynamicAttribute(Object tag, String name, DynamicAttribute attribute) throws JspException
159 {
160 if(!(tag instanceof DynamicAttributes))
161 {
162 String message = "Attribute " + name + " specified as dynamic attribute but tag ";
163 message += "is not an instance of avax.servlet.jsp.tagext.DynamicAttributes.";
164 throw new IllegalArgumentException(message);
165 }
166 ((DynamicAttributes)tag).setDynamicAttribute(attribute.getUri(), name, attribute.getValue());
167 }
168
169 /**
170 * Handles body evaluation of a tag. Iterated through the childs.
171 * If the child is an instance of {@link com.mockrunner.tag.NestedTag},
172 * the {@link com.mockrunner.tag.NestedTag#doLifecycle} method of
173 * this tag is called. If the child is an instance of
174 * {@link com.mockrunner.tag.DynamicChild}, the
175 * {@link com.mockrunner.tag.DynamicChild#evaluate} method is called
176 * and the result is written to the out <code>JspWriter</code> as a
177 * string. If the result is another object (usually a string) it is written
178 * to the out <code>JspWriter</code> (the <code>toString</code> method will
179 * be called).
180 * @param bodyList the list of body entries
181 * @param pageContext the corresponding <code>PageContext</code> or <code>JspContext</code>
182 */
183 public static void evalBody(List bodyList, Object pageContext) throws JspException
184 {
185 for(int ii = 0; ii < bodyList.size(); ii++)
186 {
187 Object nextChild = bodyList.get(ii);
188 if(nextChild instanceof NestedBodyTag)
189 {
190 int result = ((NestedBodyTag)nextChild).doLifecycle();
191 if(Tag.SKIP_PAGE == result) return;
192 }
193 else if(nextChild instanceof NestedStandardTag)
194 {
195 int result = ((NestedStandardTag)nextChild).doLifecycle();
196 if(Tag.SKIP_PAGE == result) return;
197 }
198 else if(nextChild instanceof NestedSimpleTag)
199 {
200 ((NestedSimpleTag)nextChild).doLifecycle();
201 }
202 else
203 {
204 try
205 {
206 if(pageContext instanceof PageContext)
207 {
208 ((PageContext)pageContext).getOut().print(getChildText(nextChild));
209 }
210 else if(pageContext instanceof JspContext)
211 {
212 ((JspContext)pageContext).getOut().print(getChildText(nextChild));
213 }
214 else
215 {
216 throw new IllegalArgumentException("pageContext must be an instance of JspContext");
217 }
218 }
219 catch(IOException exc)
220 {
221 log.error(exc.getMessage(), exc);
222 throw new NestedApplicationException(exc);
223 }
224 }
225 }
226 }
227
228 private static String getChildText(Object child)
229 {
230 if(null == child) return "null";
231 if(child instanceof DynamicChild)
232 {
233 Object result = ((DynamicChild)child).evaluate();
234 if(null == result) return "null";
235 return result.toString();
236 }
237 return child.toString();
238 }
239
240 /**
241 * Helper method to dump tags incl. child tags.
242 */
243 public static String dumpTag(NestedTag tag, StringBuffer buffer, int level)
244 {
245 StringUtil.appendTabs(buffer, level);
246 buffer.append("<" + tag.getClass().getName() + ">\n");
247 TagUtil.dumpTagTree(tag.getChilds(), buffer, level);
248 StringUtil.appendTabs(buffer, level);
249 buffer.append("</" + tag.getClass().getName() + ">");
250 return buffer.toString();
251 }
252
253 /**
254 * Helper method to dump tags incl. child tags.
255 */
256 public static void dumpTagTree(List bodyList, StringBuffer buffer, int level)
257 {
258 for(int ii = 0; ii < bodyList.size(); ii++)
259 {
260 Object nextChild = bodyList.get(ii);
261 if(nextChild instanceof NestedTag)
262 {
263 dumpTag((NestedTag)nextChild, buffer, level + 1);
264 }
265 else
266 {
267 StringUtil.appendTabs(buffer, level + 1);
268 buffer.append(bodyList.get(ii).toString());
269 }
270 buffer.append("\n");
271 }
272 }
273 }