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 }