package org.apache.jasper.compiler; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.jsp.tagext.TagInfo; import net.sf.jsptest.TagKey; import org.apache.jasper.JasperException; import org.apache.jasper.compiler.Node.Nodes; /** * @author Lasse Koskela * @author Meinert Schwartau (scwar32) */ public class MockTagPluginManager extends TagPluginManager { protected Map mockTaglibs; public MockTagPluginManager(ServletContext ctx, TagPluginManager manager, Map taglibs) { super(ctx); mockTaglibs = new HashMap(taglibs); } public void apply(Nodes nodes, ErrorDispatcher errorDispatcher, PageInfo pageInfo) throws JasperException { invokePrivateInitWith(errorDispatcher); assignToPrivateField("pageInfo", pageInfo); substituteMocksFor(nodes); } private void substituteMocksFor(Nodes nodes) throws JasperException { nodes.visit(new TagSubstitutor(mockTaglibs)); } /** * Walks through the node tree representing the JSP and replaces the <i>handler</i> of certain * tags with a mock implementation if one has been configured. */ private static class TagSubstitutor extends Node.Visitor { private final Map mocks; public TagSubstitutor(Map mockTaglibs) { this.mocks = mockTaglibs; } public void visit(Node.CustomTag n) throws JasperException { Class mockClass = substitute(n.getTagInfo()); if (mockClass != null) { n.setTagHandlerClass(mockClass); } super.visit(n); } private Class substitute(TagInfo tagInfo) { String prefix = tagInfo.getTagLibrary().getPrefixString(); String name = tagInfo.getTagName(); TagKey[] matchingOrder = new TagKey[] { new TagKey(prefix, name), new TagKey(prefix) }; return firstMatch(mocks, matchingOrder); } private Class firstMatch(Map map, TagKey[] keys) { for (int i = 0; i < keys.length; i++) { if (map.containsKey(keys[i])) { return (Class) mocks.get(keys[i]); } } return null; } } private void assignToPrivateField(String fieldName, Object value) { try { Field field = getClass().getSuperclass().getDeclaredField(fieldName); field.setAccessible(true); field.set(this, value); } catch (Exception e) { throw new RuntimeException(e); } } private void invokePrivateInitWith(ErrorDispatcher errorDispatcher) { try { Method init = getClass().getSuperclass().getDeclaredMethod("init", new Class[] { ErrorDispatcher.class }); init.setAccessible(true); init.invoke(this, new Object[] { errorDispatcher }); } catch (Exception e) { throw new RuntimeException(e); } } }