struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From musa...@apache.org
Subject svn commit: r799681 [4/24] - in /struts/sandbox/trunk/struts2-jsp-plugin: ./ src/main/java/org/apache/struts/ src/main/java/org/apache/struts2/ src/main/java/org/apache/struts2/compiler/ src/main/java/org/apache/struts2/jasper/ src/main/java/org/apache...
Date Fri, 31 Jul 2009 18:12:51 GMT
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Generator.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Generator.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Generator.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Generator.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,4063 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagVariableInfo;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.struts2.jasper.Constants;
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.apache.struts2.jasper.runtime.JspRuntimeLibrary;
+import org.xml.sax.Attributes;
+
+/**
+ * Generate Java source from Nodes
+ *
+ * @author Anil K. Vijendran
+ * @author Danno Ferrin
+ * @author Mandar Raje
+ * @author Rajiv Mordani
+ * @author Pierre Delisle
+ *
+ * Tomcat 4.1.x and Tomcat 5:
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
+ * @author Denis Benoit
+ */
+
+class Generator {
+
+    private static final Class[] OBJECT_CLASS = { Object.class };
+    private ServletWriter out;
+    private ArrayList methodsBuffered;
+    private FragmentHelperClass fragmentHelperClass;
+    private ErrorDispatcher err;
+    private BeanRepository beanInfo;
+    private JspCompilationContext ctxt;
+    private boolean isPoolingEnabled;
+    private boolean breakAtLF;
+    private PageInfo pageInfo;
+    private Vector tagHandlerPoolNames;
+    private GenBuffer charArrayBuffer;
+
+    /**
+     * @param s the input string
+     * @return quoted and escaped string, per Java rule
+     */
+    static String quote(String s) {
+
+        if (s == null)
+            return "null";
+
+        return '"' + escape(s) + '"';
+    }
+
+    /**
+     * @param s the input string
+     * @return escaped string, per Java rule
+     */
+    static String escape(String s) {
+
+        if (s == null)
+            return "";
+
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '"')
+                b.append('\\').append('"');
+            else if (c == '\\')
+                b.append('\\').append('\\');
+            else if (c == '\n')
+                b.append('\\').append('n');
+            else if (c == '\r')
+                b.append('\\').append('r');
+            else
+                b.append(c);
+        }
+        return b.toString();
+    }
+
+    /**
+     * Single quote and escape a character
+     */
+    static String quote(char c) {
+
+        StringBuffer b = new StringBuffer();
+        b.append('\'');
+        if (c == '\'')
+            b.append('\\').append('\'');
+        else if (c == '\\')
+            b.append('\\').append('\\');
+        else if (c == '\n')
+            b.append('\\').append('n');
+        else if (c == '\r')
+            b.append('\\').append('r');
+        else
+            b.append(c);
+        b.append('\'');
+        return b.toString();
+    }
+
+    /**
+     * Generates declarations.  This includes "info" of the page directive,
+     * and scriptlet declarations.
+     */
+    private void generateDeclarations(Node.Nodes page) throws JasperException {
+
+        class DeclarationVisitor extends Node.Visitor {
+
+            private boolean getServletInfoGenerated = false;
+
+            /*
+             * Generates getServletInfo() method that returns the value of the
+             * page directive's 'info' attribute, if present.
+             *
+             * The Validator has already ensured that if the translation unit
+             * contains more than one page directive with an 'info' attribute,
+             * their values match.
+             */
+            public void visit(Node.PageDirective n) throws JasperException {
+
+                if (getServletInfoGenerated) {
+                    return;
+                }
+
+                String info = n.getAttributeValue("info");
+                if (info == null)
+                    return;
+
+                getServletInfoGenerated = true;
+                out.printil("public String getServletInfo() {");
+                out.pushIndent();
+                out.printin("return ");
+                out.print(quote(info));
+                out.println(";");
+                out.popIndent();
+                out.printil("}");
+                out.println();
+            }
+
+            public void visit(Node.Declaration n) throws JasperException {
+                n.setBeginJavaLine(out.getJavaLine());
+                out.printMultiLn(new String(n.getText()));
+                out.println();
+                n.setEndJavaLine(out.getJavaLine());
+            }
+
+            // Custom Tags may contain declarations from tag plugins.
+            public void visit(Node.CustomTag n) throws JasperException {
+                if (n.useTagPlugin()) {
+                    if (n.getAtSTag() != null) {
+                        n.getAtSTag().visit(this);
+                    }
+                    visitBody(n);
+                    if (n.getAtETag() != null) {
+                        n.getAtETag().visit(this);
+                    }
+                } else {
+                    visitBody(n);
+                }
+            }
+        }
+
+        out.println();
+        page.visit(new DeclarationVisitor());
+    }
+
+    /**
+     * Compiles list of tag handler pool names.
+     */
+    private void compileTagHandlerPoolList(Node.Nodes page)
+        throws JasperException {
+
+        class TagHandlerPoolVisitor extends Node.Visitor {
+
+            private Vector names;
+
+            /*
+             * Constructor
+             *
+             * @param v Vector of tag handler pool names to populate
+             */
+            TagHandlerPoolVisitor(Vector v) {
+                names = v;
+            }
+
+            /*
+             * Gets the name of the tag handler pool for the given custom tag
+             * and adds it to the list of tag handler pool names unless it is
+             * already contained in it.
+             */
+            public void visit(Node.CustomTag n) throws JasperException {
+
+                if (!n.implementsSimpleTag()) {
+                    String name =
+                        createTagHandlerPoolName(
+                            n.getPrefix(),
+                            n.getLocalName(),
+                            n.getAttributes(),
+                            n.hasEmptyBody());
+                    n.setTagHandlerPoolName(name);
+                    if (!names.contains(name)) {
+                        names.add(name);
+                    }
+                }
+                visitBody(n);
+            }
+
+            /*
+             * Creates the name of the tag handler pool whose tag handlers may
+             * be (re)used to service this action.
+             *
+             * @return The name of the tag handler pool
+             */
+            private String createTagHandlerPoolName(
+                String prefix,
+                String shortName,
+                Attributes attrs,
+                boolean hasEmptyBody) {
+                String poolName = null;
+
+                poolName = "_jspx_tagPool_" + prefix + "_" + shortName;
+                if (attrs != null) {
+                    String[] attrNames = new String[attrs.getLength()];
+                    for (int i = 0; i < attrNames.length; i++) {
+                        attrNames[i] = attrs.getQName(i);
+                    }
+                    Arrays.sort(attrNames, Collections.reverseOrder());
+                    if (attrNames.length > 0) {
+                        poolName = poolName + "&";
+                    }
+                    for (int i = 0; i < attrNames.length; i++) {
+                        poolName = poolName + "_" + attrNames[i];
+                    }
+                }
+                if (hasEmptyBody) {
+                    poolName = poolName + "_nobody";
+                }
+                return JspUtil.makeJavaIdentifier(poolName);
+            }
+        }
+
+        page.visit(new TagHandlerPoolVisitor(tagHandlerPoolNames));
+    }
+
+    private void declareTemporaryScriptingVars(Node.Nodes page)
+        throws JasperException {
+
+        class ScriptingVarVisitor extends Node.Visitor {
+
+            private Vector vars;
+
+            ScriptingVarVisitor() {
+                vars = new Vector();
+            }
+
+            public void visit(Node.CustomTag n) throws JasperException {
+
+                if (n.getCustomNestingLevel() > 0) {
+                    TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+                    VariableInfo[] varInfos = n.getVariableInfos();
+
+                    if (varInfos.length > 0) {
+                        for (int i = 0; i < varInfos.length; i++) {
+                            String varName = varInfos[i].getVarName();
+                            String tmpVarName =
+                                "_jspx_"
+                                    + varName
+                                    + "_"
+                                    + n.getCustomNestingLevel();
+                            if (!vars.contains(tmpVarName)) {
+                                vars.add(tmpVarName);
+                                out.printin(varInfos[i].getClassName());
+                                out.print(" ");
+                                out.print(tmpVarName);
+                                out.print(" = ");
+                                out.print(null);
+                                out.println(";");
+                            }
+                        }
+                    } else {
+                        for (int i = 0; i < tagVarInfos.length; i++) {
+                            String varName = tagVarInfos[i].getNameGiven();
+                            if (varName == null) {
+                                varName =
+                                    n.getTagData().getAttributeString(
+                                        tagVarInfos[i].getNameFromAttribute());
+                            } else if (
+                                tagVarInfos[i].getNameFromAttribute()
+                                    != null) {
+                                // alias
+                                continue;
+                            }
+                            String tmpVarName =
+                                "_jspx_"
+                                    + varName
+                                    + "_"
+                                    + n.getCustomNestingLevel();
+                            if (!vars.contains(tmpVarName)) {
+                                vars.add(tmpVarName);
+                                out.printin(tagVarInfos[i].getClassName());
+                                out.print(" ");
+                                out.print(tmpVarName);
+                                out.print(" = ");
+                                out.print(null);
+                                out.println(";");
+                            }
+                        }
+                    }
+                }
+
+                visitBody(n);
+            }
+        }
+
+        page.visit(new ScriptingVarVisitor());
+    }
+
+    /**
+     * Generates the _jspInit() method for instantiating the tag handler pools.
+     * For tag file, _jspInit has to be invoked manually, and the ServletConfig
+     * object explicitly passed.
+     */
+    private void generateInit() {
+
+        if (ctxt.isTagFile()) {
+            out.printil("private void _jspInit(ServletConfig config) {");
+        } else {
+            out.printil("public void _jspInit() {");
+        }
+
+        out.pushIndent();
+        for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
+            out.printin((String)tagHandlerPoolNames.elementAt(i));
+            out.print(
+                " = org.apache.struts2.jasper.runtime.TagHandlerPool.getTagHandlerPool(");
+            if (ctxt.isTagFile()) {
+                out.print("config");
+            } else {
+                out.print("getServletConfig()");
+            }
+            out.println(");");
+        }
+        out.popIndent();
+        out.printil("}");
+        out.println();
+    }
+
+    /**
+     * Generates the _jspDestroy() method which is responsible for calling the
+     * release() method on every tag handler in any of the tag handler pools.
+     */
+    private void generateDestroy() {
+
+        out.printil("public void _jspDestroy() {");
+        out.pushIndent();
+        for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
+            out.printin((String)tagHandlerPoolNames.elementAt(i));
+            out.println(".release();");
+        }
+        out.popIndent();
+        out.printil("}");
+        out.println();
+    }
+
+    /**
+     * Generate preamble package name
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreamblePackage(String packageName)
+        throws JasperException {
+        if (!"".equals(packageName) && packageName != null) {
+            out.printil("package " + packageName + ";");
+            out.println();
+        }
+    }
+
+    /**
+     * Generate preamble imports
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleImports() throws JasperException {
+        Iterator iter = pageInfo.getImports().iterator();
+        while (iter.hasNext()) {
+            out.printin("import ");
+            out.print((String)iter.next());
+            out.println(";");
+        }
+        out.println();
+    }
+
+    /**
+     * Generation of static initializers in preamble.
+     * For example, dependant list, el function map, prefix map.
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleStaticInitializers() throws JasperException {
+        // Static data for getDependants()
+        out.printil("private static java.util.List _jspx_dependants;");
+        out.println();
+        List dependants = pageInfo.getDependants();
+        Iterator iter = dependants.iterator();
+        if (!dependants.isEmpty()) {
+            out.printil("static {");
+            out.pushIndent();
+            out.printin("_jspx_dependants = new java.util.ArrayList(");
+            out.print("" + dependants.size());
+            out.println(");");
+            while (iter.hasNext()) {
+                out.printin("_jspx_dependants.add(\"");
+                out.print((String)iter.next());
+                out.println("\");");
+            }
+            out.popIndent();
+            out.printil("}");
+            out.println();
+        }
+    }
+
+    /**
+     * Declare tag handler pools (tags of the same type and with the same
+     * attribute set share the same tag handler pool)
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleClassVariableDeclarations(String className)
+        throws JasperException {
+        if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
+            for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
+                out.printil(
+                    "private org.apache.struts2.jasper.runtime.TagHandlerPool "
+                        + tagHandlerPoolNames.elementAt(i)
+                        + ";");
+            }
+            out.println();
+        }
+    }
+
+    /**
+     * Declare general-purpose methods
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void genPreambleMethods() throws JasperException {
+        // Method used to get compile time file dependencies
+        out.printil("public Object getDependants() {");
+        out.pushIndent();
+        out.printil("return _jspx_dependants;");
+        out.popIndent();
+        out.printil("}");
+        out.println();
+
+        if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
+            generateInit();
+            generateDestroy();
+        }
+    }
+
+    /**
+     * Generates the beginning of the static portion of the servlet.
+     */
+    private void generatePreamble(Node.Nodes page) throws JasperException {
+
+        String servletPackageName = ctxt.getServletPackageName();
+        String servletClassName = ctxt.getServletClassName();
+        String serviceMethodName = Constants.SERVICE_METHOD_NAME;
+
+        // First the package name:
+        genPreamblePackage(servletPackageName);
+
+        // Generate imports
+        genPreambleImports();
+
+        // Generate class declaration
+        out.printin("public final class ");
+        out.print(servletClassName);
+        out.print(" extends ");
+        out.println(pageInfo.getExtends());
+        out.printin(
+            "    implements org.apache.struts2.jasper.runtime.JspSourceDependent");
+        if (!pageInfo.isThreadSafe()) {
+            out.println(",");
+            out.printin("                 SingleThreadModel");
+        }
+        out.println(" {");
+        out.pushIndent();
+
+        // Class body begins here
+        generateDeclarations(page);
+
+        // Static initializations here
+        genPreambleStaticInitializers();
+
+        // Class variable declarations
+        genPreambleClassVariableDeclarations(servletClassName);
+
+        // Constructor
+        //  generateConstructor(className);
+
+        // Methods here
+        genPreambleMethods();
+
+        // Now the service method
+        out.printin("public void ");
+        out.print(serviceMethodName);
+        out.println(
+            "(HttpServletRequest request, HttpServletResponse response)");
+        out.println("        throws java.io.IOException, ServletException {");
+
+        out.pushIndent();
+        out.println();
+
+        // Local variable declarations
+        out.printil("JspFactory _jspxFactory = null;");
+        out.printil("PageContext pageContext = null;");
+        if (pageInfo.isSession())
+            out.printil("HttpSession session = null;");
+
+        if (pageInfo.isErrorPage()) {
+            out.printil(
+                "Throwable exception = org.apache.struts2.jasper.runtime.JspRuntimeLibrary.getThrowable(request);");
+            out.printil("if (exception != null) {");
+            out.pushIndent();
+            out.printil(
+                "response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);");
+            out.popIndent();
+            out.printil("}");
+        }
+
+        out.printil("ServletContext application = null;");
+        out.printil("ServletConfig config = null;");
+        out.printil("JspWriter out = null;");
+        out.printil("Object page = this;");
+
+        out.printil("JspWriter _jspx_out = null;");
+        out.printil("PageContext _jspx_page_context = null;");
+        out.println();
+
+        declareTemporaryScriptingVars(page);
+        out.println();
+
+        out.printil("try {");
+        out.pushIndent();
+
+        out.printil("_jspxFactory = JspFactory.getDefaultFactory();");
+
+        out.printin("response.setContentType(");
+        out.print(quote(pageInfo.getContentType()));
+        out.println(");");
+
+        if (ctxt.getOptions().isXpoweredBy()) {
+            out.printil("response.addHeader(\"X-Powered-By\", \"JSP/2.0\");");
+        }
+
+        out.printil(
+            "pageContext = _jspxFactory.getPageContext(this, request, response,");
+        out.printin("\t\t\t");
+        out.print(quote(pageInfo.getErrorPage()));
+        out.print(", " + pageInfo.isSession());
+        out.print(", " + pageInfo.getBuffer());
+        out.print(", " + pageInfo.isAutoFlush());
+        out.println(");");
+        out.printil("_jspx_page_context = pageContext;");
+
+        out.printil("application = pageContext.getServletContext();");
+        out.printil("config = pageContext.getServletConfig();");
+
+        if (pageInfo.isSession())
+            out.printil("session = pageContext.getSession();");
+        out.printil("out = pageContext.getOut();");
+        out.printil("_jspx_out = out;");
+        out.println();
+    }
+
+    /**
+     * Generates an XML Prolog, which includes an XML declaration and
+     * an XML doctype declaration.
+     */
+    private void generateXmlProlog(Node.Nodes page) {
+
+        /*
+         * An XML declaration is generated under the following conditions:
+         *
+         * - 'omit-xml-declaration' attribute of <jsp:output> action is set to
+         *   "no" or "false"
+         * - JSP document without a <jsp:root>
+         */
+        String omitXmlDecl = pageInfo.getOmitXmlDecl();
+        if ((omitXmlDecl != null && !JspUtil.booleanValue(omitXmlDecl))
+            || (omitXmlDecl == null
+                && page.getRoot().isXmlSyntax()
+                && !pageInfo.hasJspRoot()
+                && !ctxt.isTagFile())) {
+            String cType = pageInfo.getContentType();
+            String charSet = cType.substring(cType.indexOf("charset=") + 8);
+            out.printil(
+                "out.write(\"<?xml version=\\\"1.0\\\" encoding=\\\""
+                    + charSet
+                    + "\\\"?>\\n\");");
+        }
+
+        /*
+         * Output a DOCTYPE declaration if the doctype-root-element appears.
+         * If doctype-public appears:
+         *     <!DOCTYPE name PUBLIC "doctypePublic" "doctypeSystem">
+         * else
+         *     <!DOCTYPE name SYSTEM "doctypeSystem" >
+         */
+
+        String doctypeName = pageInfo.getDoctypeName();
+        if (doctypeName != null) {
+            String doctypePublic = pageInfo.getDoctypePublic();
+            String doctypeSystem = pageInfo.getDoctypeSystem();
+            out.printin("out.write(\"<!DOCTYPE ");
+            out.print(doctypeName);
+            if (doctypePublic == null) {
+                out.print(" SYSTEM \\\"");
+            } else {
+                out.print(" PUBLIC \\\"");
+                out.print(doctypePublic);
+                out.print("\\\" \\\"");
+            }
+            out.print(doctypeSystem);
+            out.println("\\\">\\n\");");
+        }
+    }
+
+    /*
+     * Generates the constructor.
+     * (shared by servlet and tag handler preamble generation)
+     */
+    private void generateConstructor(String className) {
+        out.printil("public " + className + "() {");
+        out.printil("}");
+        out.println();
+    }
+
+    /**
+     * A visitor that generates codes for the elements in the page.
+     */
+    class GenerateVisitor extends Node.Visitor {
+
+        /*
+         * Hashtable containing introspection information on tag handlers:
+         *   <key>: tag prefix
+         *   <value>: hashtable containing introspection on tag handlers:
+         *              <key>: tag short name
+         *              <value>: introspection info of tag handler for
+         *                       <prefix:shortName> tag
+         */
+        private Hashtable handlerInfos;
+
+        private Hashtable tagVarNumbers;
+        private String parent;
+        private boolean isSimpleTagParent; // Is parent a SimpleTag?
+        private String pushBodyCountVar;
+        private String simpleTagHandlerVar;
+        private boolean isSimpleTagHandler;
+        private boolean isFragment;
+        private boolean isTagFile;
+        private ServletWriter out;
+        private ArrayList methodsBuffered;
+        private FragmentHelperClass fragmentHelperClass;
+        private int methodNesting;
+        private TagInfo tagInfo;
+        private ClassLoader loader;
+        private int charArrayCount;
+        private HashMap textMap;
+
+        /**
+         * Constructor.
+         */
+        public GenerateVisitor(
+            boolean isTagFile,
+            ServletWriter out,
+            ArrayList methodsBuffered,
+            FragmentHelperClass fragmentHelperClass,
+            ClassLoader loader,
+            TagInfo tagInfo) {
+
+            this.isTagFile = isTagFile;
+            this.out = out;
+            this.methodsBuffered = methodsBuffered;
+            this.fragmentHelperClass = fragmentHelperClass;
+            this.loader = loader;
+            this.tagInfo = tagInfo;
+            methodNesting = 0;
+            handlerInfos = new Hashtable();
+            tagVarNumbers = new Hashtable();
+            textMap = new HashMap();
+        }
+
+        /**
+         * Returns an attribute value, optionally URL encoded.  If
+         * the value is a runtime expression, the result is the expression
+         * itself, as a string.  If the result is an EL expression, we insert
+         * a call to the interpreter.  If the result is a Named Attribute
+         * we insert the generated variable name.  Otherwise the result is a
+         * string literal, quoted and escaped.
+         *
+         * @param attr An JspAttribute object
+         * @param encode true if to be URL encoded
+         * @param expectedType the expected type for an EL evaluation
+         *        (ignored for attributes that aren't EL expressions)
+         */
+        private String attributeValue(
+            Node.JspAttribute attr,
+            boolean encode,
+            Class expectedType) {
+            String v = attr.getValue();
+            if (!attr.isNamedAttribute() && (v == null))
+                return "";
+
+            if (attr.isExpression()) {
+                if (encode) {
+                    return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf("
+                        + v
+                        + "), request.getCharacterEncoding())";
+                }
+                return v;
+            } else if (attr.isELInterpreterInput()) {
+                boolean replaceESC = v.indexOf(Constants.HACK_CHAR) > 0;
+                v =
+                    JspUtil.interpreterCall(
+                        this.isTagFile,
+                        v,
+                        expectedType,
+                        attr.getEL().getMapName(),
+                        false);
+                // XXX hack replacement
+                if (replaceESC) {
+                    v = "(" + v + ").replace(" + Constants.HACK_STR + ", '$')";
+                }
+                if (encode) {
+                    return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.URLEncode("
+                        + v
+                        + ", request.getCharacterEncoding())";
+                }
+                return v;
+            } else if (attr.isNamedAttribute()) {
+                return attr.getNamedAttributeNode().getTemporaryVariableName();
+            } else {
+                if (encode) {
+                    return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.URLEncode("
+                        + quote(v)
+                        + ", request.getCharacterEncoding())";
+                }
+                return quote(v);
+            }
+        }
+
+        /**
+         * Prints the attribute value specified in the param action, in the
+         * form of name=value string.
+         *
+         * @param n the parent node for the param action nodes.
+         */
+        private void printParams(Node n, String pageParam, boolean literal)
+            throws JasperException {
+
+            class ParamVisitor extends Node.Visitor {
+                String separator;
+
+                ParamVisitor(String separator) {
+                    this.separator = separator;
+                }
+
+                public void visit(Node.ParamAction n) throws JasperException {
+
+                    out.print(" + ");
+                    out.print(separator);
+                    out.print(" + ");
+                    out.print(
+                        "org.apache.struts2.jasper.runtime.JspRuntimeLibrary."
+                            + "URLEncode("
+                            + quote(n.getTextAttribute("name"))
+                            + ", request.getCharacterEncoding())");
+                    out.print("+ \"=\" + ");
+                    out.print(attributeValue(n.getValue(), true, String.class));
+
+                    // The separator is '&' after the second use
+                    separator = "\"&\"";
+                }
+            }
+
+            String sep;
+            if (literal) {
+                sep = pageParam.indexOf('?') > 0 ? "\"&\"" : "\"?\"";
+            } else {
+                sep = "((" + pageParam + ").indexOf('?')>0? '&': '?')";
+            }
+            if (n.getBody() != null) {
+                n.getBody().visit(new ParamVisitor(sep));
+            }
+        }
+
+        public void visit(Node.Expression n) throws JasperException {
+            n.setBeginJavaLine(out.getJavaLine());
+            out.printin("out.print(");
+            out.printMultiLn(n.getText());
+            out.println(");");
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.Scriptlet n) throws JasperException {
+            n.setBeginJavaLine(out.getJavaLine());
+            out.printMultiLn(n.getText());
+            out.println();
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.ELExpression n) throws JasperException {
+            n.setBeginJavaLine(out.getJavaLine());
+            if (!pageInfo.isELIgnored()) {
+                out.printil(
+                    "out.write("
+                        + JspUtil.interpreterCall(
+                            this.isTagFile,
+                            "${" + new String(n.getText()) + "}",
+                            String.class,
+                            n.getEL().getMapName(),
+                            false)
+                        + ");");
+            } else {
+                out.printil(
+                    "out.write("
+                        + quote("${" + new String(n.getText()) + "}")
+                        + ");");
+            }
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.IncludeAction n) throws JasperException {
+
+            String flush = n.getTextAttribute("flush");
+            Node.JspAttribute page = n.getPage();
+
+            boolean isFlush = false; // default to false;
+            if ("true".equals(flush))
+                isFlush = true;
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            String pageParam;
+            if (page.isNamedAttribute()) {
+                // If the page for jsp:include was specified via
+                // jsp:attribute, first generate code to evaluate
+                // that body.
+                pageParam =
+                    generateNamedAttributeValue(page.getNamedAttributeNode());
+            } else {
+                pageParam = attributeValue(page, false, String.class);
+            }
+
+            // If any of the params have their values specified by
+            // jsp:attribute, prepare those values first.
+            Node jspBody = findJspBody(n);
+            if (jspBody != null) {
+                prepareParams(jspBody);
+            } else {
+                prepareParams(n);
+            }
+
+            out.printin(
+                "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.include(request, response, "
+                    + pageParam);
+            printParams(n, pageParam, page.isLiteral());
+            out.println(", out, " + isFlush + ");");
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        /**
+         * Scans through all child nodes of the given parent for
+         * <param> subelements.  For each <param> element, if its value
+         * is specified via a Named Attribute (<jsp:attribute>),
+         * generate the code to evaluate those bodies first.
+         * <p>
+         * If parent is null, simply returns.
+         */
+        private void prepareParams(Node parent) throws JasperException {
+            if (parent == null)
+                return;
+
+            Node.Nodes subelements = parent.getBody();
+            if (subelements != null) {
+                for (int i = 0; i < subelements.size(); i++) {
+                    Node n = subelements.getNode(i);
+                    if (n instanceof Node.ParamAction) {
+                        Node.Nodes paramSubElements = n.getBody();
+                        for (int j = 0;
+                            (paramSubElements != null)
+                                && (j < paramSubElements.size());
+                            j++) {
+                            Node m = paramSubElements.getNode(j);
+                            if (m instanceof Node.NamedAttribute) {
+                                generateNamedAttributeValue(
+                                    (Node.NamedAttribute)m);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Finds the <jsp:body> subelement of the given parent node.
+         * If not found, null is returned.
+         */
+        private Node.JspBody findJspBody(Node parent) throws JasperException {
+            Node.JspBody result = null;
+
+            Node.Nodes subelements = parent.getBody();
+            for (int i = 0;
+                (subelements != null) && (i < subelements.size());
+                i++) {
+                Node n = subelements.getNode(i);
+                if (n instanceof Node.JspBody) {
+                    result = (Node.JspBody)n;
+                    break;
+                }
+            }
+
+            return result;
+        }
+
+        public void visit(Node.ForwardAction n) throws JasperException {
+            Node.JspAttribute page = n.getPage();
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            out.printil("if (true) {"); // So that javac won't complain about
+            out.pushIndent(); // codes after "return"
+
+            String pageParam;
+            if (page.isNamedAttribute()) {
+                // If the page for jsp:forward was specified via
+                // jsp:attribute, first generate code to evaluate
+                // that body.
+                pageParam =
+                    generateNamedAttributeValue(page.getNamedAttributeNode());
+            } else {
+                pageParam = attributeValue(page, false, String.class);
+            }
+
+            // If any of the params have their values specified by
+            // jsp:attribute, prepare those values first.
+            Node jspBody = findJspBody(n);
+            if (jspBody != null) {
+                prepareParams(jspBody);
+            } else {
+                prepareParams(n);
+            }
+
+            out.printin("_jspx_page_context.forward(");
+            out.print(pageParam);
+            printParams(n, pageParam, page.isLiteral());
+            out.println(");");
+            if (isTagFile || isFragment) {
+                out.printil("throw new SkipPageException();");
+            } else {
+                out.printil((methodNesting > 0) ? "return true;" : "return;");
+            }
+            out.popIndent();
+            out.printil("}");
+
+            n.setEndJavaLine(out.getJavaLine());
+            // XXX Not sure if we can eliminate dead codes after this.
+        }
+
+        public void visit(Node.GetProperty n) throws JasperException {
+            String name = n.getTextAttribute("name");
+            String property = n.getTextAttribute("property");
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            if (beanInfo.checkVariable(name)) {
+                // Bean is defined using useBean, introspect at compile time
+                Class bean = beanInfo.getBeanType(name);
+                String beanName = JspUtil.getCanonicalName(bean);
+                java.lang.reflect.Method meth =
+                    JspRuntimeLibrary.getReadMethod(bean, property);
+                String methodName = meth.getName();
+                out.printil(
+                    "out.write(org.apache.struts2.jasper.runtime.JspRuntimeLibrary.toString("
+                        + "((("
+                        + beanName
+                        + ")_jspx_page_context.findAttribute("
+                        + "\""
+                        + name
+                        + "\"))."
+                        + methodName
+                        + "())));");
+            } else {
+                // The object could be a custom action with an associated
+                // VariableInfo entry for this name.
+                // Get the class name and then introspect at runtime.
+                out.printil(
+                    "out.write(org.apache.struts2.jasper.runtime.JspRuntimeLibrary.toString"
+                        + "(org.apache.struts2.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
+                        + "(_jspx_page_context.getAttribute(\""
+                        + name
+                        + "\", PageContext.PAGE_SCOPE), \""
+                        + property
+                        + "\")));");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.SetProperty n) throws JasperException {
+            String name = n.getTextAttribute("name");
+            String property = n.getTextAttribute("property");
+            String param = n.getTextAttribute("param");
+            Node.JspAttribute value = n.getValue();
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            if ("*".equals(property)) {
+                out.printil(
+                    "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.introspect("
+                        + "_jspx_page_context.findAttribute("
+                        + "\""
+                        + name
+                        + "\"), request);");
+            } else if (value == null) {
+                if (param == null)
+                    param = property; // default to same as property
+                out.printil(
+                    "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", request.getParameter(\""
+                        + param
+                        + "\"), "
+                        + "request, \""
+                        + param
+                        + "\", false);");
+            } else if (value.isExpression()) {
+                out.printil(
+                    "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.handleSetProperty("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\",");
+                out.print(attributeValue(value, false, null));
+                out.println(");");
+            } else if (value.isELInterpreterInput()) {
+                // We've got to resolve the very call to the interpreter
+                // at runtime since we don't know what type to expect
+                // in the general case; we thus can't hard-wire the call
+                // into the generated code.  (XXX We could, however,
+                // optimize the case where the bean is exposed with
+                // <jsp:useBean>, much as the code here does for
+                // getProperty.)
+
+                // The following holds true for the arguments passed to
+                // JspRuntimeLibrary.handleSetPropertyExpression():
+                // - 'pageContext' is a VariableResolver.
+                // - 'this' (either the generated Servlet or the generated tag
+                //   handler for Tag files) is a FunctionMapper.
+                out.printil(
+                    "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.handleSetPropertyExpression("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", "
+                        + quote(value.getValue())
+                        + ", "
+                        + "_jspx_page_context, "
+                        + value.getEL().getMapName()
+                        + ");");
+            } else if (value.isNamedAttribute()) {
+                // If the value for setProperty was specified via
+                // jsp:attribute, first generate code to evaluate
+                // that body.
+                String valueVarName =
+                    generateNamedAttributeValue(value.getNamedAttributeNode());
+                out.printil(
+                    "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", "
+                        + valueVarName
+                        + ", null, null, false);");
+            } else {
+                out.printin(
+                    "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+                        + "_jspx_page_context.findAttribute(\""
+                        + name
+                        + "\"), \""
+                        + property
+                        + "\", ");
+                out.print(attributeValue(value, false, null));
+                out.println(", null, null, false);");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.UseBean n) throws JasperException {
+
+            String name = n.getTextAttribute("id");
+            String scope = n.getTextAttribute("scope");
+            String klass = n.getTextAttribute("class");
+            String type = n.getTextAttribute("type");
+            Node.JspAttribute beanName = n.getBeanName();
+
+            // If "class" is specified, try an instantiation at compile time
+            boolean generateNew = false;
+            String canonicalName = null;    // Canonical name for klass
+            if (klass != null) {
+                try {
+                    Class bean = ctxt.getClassLoader().loadClass(klass);
+                    if (klass.indexOf('$') >= 0)  {
+                        // Obtain the canonical type name
+                        canonicalName = JspUtil.getCanonicalName(bean);
+                    } else {
+                        canonicalName = klass;
+                    }
+                    int modifiers = bean.getModifiers();
+                    if (!Modifier.isPublic(modifiers) ||
+                        Modifier.isInterface(modifiers) ||
+                        Modifier.isAbstract(modifiers)) {
+                        throw new Exception("Invalid bean class modifier");
+                    }
+                    // Check that there is a 0 arg constructor
+                    bean.getConstructor(new Class[] {});
+                    // At compile time, we have determined that the bean class
+                    // exists, with a public zero constructor, new() can be
+                    // used for bean instantiation.
+                    generateNew = true;
+                } catch (Exception e) {
+                    // Cannot instantiate the specified class, either a
+                    // compilation error or a runtime error will be raised,
+                    // depending on a compiler flag.
+                    if(ctxt.getOptions().getErrorOnUseBeanInvalidClassAttribute()) {
+                        err.jspError(n, "jsp.error.invalid.bean", klass);
+                    }
+                    if (canonicalName == null) {
+                        // Doing our best here to get a canonical name
+                        // from the binary name, should work 99.99% of time.
+                        canonicalName = klass.replace('$','.');
+                    }
+                }
+                if (type == null) {
+                    // if type is unspecified, use "class" as type of bean
+                    type = canonicalName;
+                }
+            }
+
+            String scopename = "PageContext.PAGE_SCOPE"; // Default to page
+            String lock = "_jspx_page_context";
+
+            if ("request".equals(scope)) {
+                scopename = "PageContext.REQUEST_SCOPE";
+                lock = "request";
+            } else if ("session".equals(scope)) {
+                scopename = "PageContext.SESSION_SCOPE";
+                lock = "session";
+            } else if ("application".equals(scope)) {
+                scopename = "PageContext.APPLICATION_SCOPE";
+                lock = "application";
+            }
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Declare bean
+            out.printin(type);
+            out.print(' ');
+            out.print(name);
+            out.println(" = null;");
+
+            // Lock while getting or creating bean
+            out.printin("synchronized (");
+            out.print(lock);
+            out.println(") {");
+            out.pushIndent();
+
+            // Locate bean from context
+            out.printin(name);
+            out.print(" = (");
+            out.print(type);
+            out.print(") _jspx_page_context.getAttribute(");
+            out.print(quote(name));
+            out.print(", ");
+            out.print(scopename);
+            out.println(");");
+
+            // Create bean
+            /*
+             * Check if bean is alredy there
+             */
+            out.printin("if (");
+            out.print(name);
+            out.println(" == null){");
+            out.pushIndent();
+            if (klass == null && beanName == null) {
+                /*
+                 * If both class name and beanName is not specified, the bean
+                 * must be found locally, otherwise it's an error
+                 */
+                out.printin(
+                    "throw new java.lang.InstantiationException(\"bean ");
+                out.print(name);
+                out.println(" not found within scope\");");
+            } else {
+                /*
+                 * Instantiate the bean if it is not in the specified scope.
+                 */
+                if (!generateNew) {
+                    String binaryName;
+                    if (beanName != null) {
+                        if (beanName.isNamedAttribute()) {
+                            // If the value for beanName was specified via
+                            // jsp:attribute, first generate code to evaluate
+                            // that body.
+                            binaryName =
+                                generateNamedAttributeValue(
+                                    beanName.getNamedAttributeNode());
+                        } else {
+                            binaryName =
+                                attributeValue(beanName, false, String.class);
+                        }
+                    } else {
+                        // Implies klass is not null
+                        binaryName = quote(klass);
+                    }
+                    out.printil("try {");
+                    out.pushIndent();
+                    out.printin(name);
+                    out.print(" = (");
+                    out.print(type);
+                    out.print(") java.beans.Beans.instantiate(");
+                    out.print("this.getClass().getClassLoader(), ");
+                    out.print(binaryName);
+                    out.println(");");
+                    out.popIndent();
+                    /*
+                     * Note: Beans.instantiate throws ClassNotFoundException
+                     * if the bean class is abstract.
+                     */
+                    out.printil("} catch (ClassNotFoundException exc) {");
+                    out.pushIndent();
+                    out.printil(
+                        "throw new InstantiationException(exc.getMessage());");
+                    out.popIndent();
+                    out.printil("} catch (Exception exc) {");
+                    out.pushIndent();
+                    out.printin("throw new ServletException(");
+                    out.print("\"Cannot create bean of class \" + ");
+                    out.print(binaryName);
+                    out.println(", exc);");
+                    out.popIndent();
+                    out.printil("}"); // close of try
+                } else {
+                    // Implies klass is not null
+                    // Generate codes to instantiate the bean class
+                    out.printin(name);
+                    out.print(" = new ");
+                    out.print(canonicalName);
+                    out.println("();");
+                }
+                /*
+                 * Set attribute for bean in the specified scope
+                 */
+                out.printin("_jspx_page_context.setAttribute(");
+                out.print(quote(name));
+                out.print(", ");
+                out.print(name);
+                out.print(", ");
+                out.print(scopename);
+                out.println(");");
+
+                // Only visit the body when bean is instantiated
+                visitBody(n);
+            }
+            out.popIndent();
+            out.printil("}");
+
+            // End of lock block
+            out.popIndent();
+            out.printil("}");
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        /**
+         * @return a string for the form 'attr = "value"'
+         */
+        private String makeAttr(String attr, String value) {
+            if (value == null)
+                return "";
+
+            return " " + attr + "=\"" + value + '\"';
+        }
+
+        public void visit(Node.PlugIn n) throws JasperException {
+
+            /**
+             * A visitor to handle <jsp:param> in a plugin
+             */
+            class ParamVisitor extends Node.Visitor {
+
+                private boolean ie;
+
+                ParamVisitor(boolean ie) {
+                    this.ie = ie;
+                }
+
+                public void visit(Node.ParamAction n) throws JasperException {
+
+                    String name = n.getTextAttribute("name");
+                    if (name.equalsIgnoreCase("object"))
+                        name = "java_object";
+                    else if (name.equalsIgnoreCase("type"))
+                        name = "java_type";
+
+                    n.setBeginJavaLine(out.getJavaLine());
+                    // XXX - Fixed a bug here - value used to be output
+                    // inline, which is only okay if value is not an EL
+                    // expression.  Also, key/value pairs for the
+                    // embed tag were not being generated correctly.
+                    // Double check that this is now the correct behavior.
+                    if (ie) {
+                        // We want something of the form
+                        // out.println( "<param name=\"blah\"
+                        //     value=\"" + ... + "\">" );
+                        out.printil(
+                            "out.write( \"<param name=\\\""
+                                + escape(name)
+                                + "\\\" value=\\\"\" + "
+                                + attributeValue(
+                                    n.getValue(),
+                                    false,
+                                    String.class)
+                                + " + \"\\\">\" );");
+                        out.printil("out.write(\"\\n\");");
+                    } else {
+                        // We want something of the form
+                        // out.print( " blah=\"" + ... + "\"" );
+                        out.printil(
+                            "out.write( \" "
+                                + escape(name)
+                                + "=\\\"\" + "
+                                + attributeValue(
+                                    n.getValue(),
+                                    false,
+                                    String.class)
+                                + " + \"\\\"\" );");
+                    }
+
+                    n.setEndJavaLine(out.getJavaLine());
+                }
+            }
+
+            String type = n.getTextAttribute("type");
+            String code = n.getTextAttribute("code");
+            String name = n.getTextAttribute("name");
+            Node.JspAttribute height = n.getHeight();
+            Node.JspAttribute width = n.getWidth();
+            String hspace = n.getTextAttribute("hspace");
+            String vspace = n.getTextAttribute("vspace");
+            String align = n.getTextAttribute("align");
+            String iepluginurl = n.getTextAttribute("iepluginurl");
+            String nspluginurl = n.getTextAttribute("nspluginurl");
+            String codebase = n.getTextAttribute("codebase");
+            String archive = n.getTextAttribute("archive");
+            String jreversion = n.getTextAttribute("jreversion");
+
+            String widthStr = null;
+            if (width != null) {
+                if (width.isNamedAttribute()) {
+                    widthStr =
+                        generateNamedAttributeValue(
+                            width.getNamedAttributeNode());
+                } else {
+                    widthStr = attributeValue(width, false, String.class);
+                }
+            }
+
+            String heightStr = null;
+            if (height != null) {
+                if (height.isNamedAttribute()) {
+                    heightStr =
+                        generateNamedAttributeValue(
+                            height.getNamedAttributeNode());
+                } else {
+                    heightStr = attributeValue(height, false, String.class);
+                }
+            }
+
+            if (iepluginurl == null)
+                iepluginurl = Constants.IE_PLUGIN_URL;
+            if (nspluginurl == null)
+                nspluginurl = Constants.NS_PLUGIN_URL;
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // If any of the params have their values specified by
+            // jsp:attribute, prepare those values first.
+            // Look for a params node and prepare its param subelements:
+            Node.JspBody jspBody = findJspBody(n);
+            if (jspBody != null) {
+                Node.Nodes subelements = jspBody.getBody();
+                if (subelements != null) {
+                    for (int i = 0; i < subelements.size(); i++) {
+                        Node m = subelements.getNode(i);
+                        if (m instanceof Node.ParamsAction) {
+                            prepareParams(m);
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // XXX - Fixed a bug here - width and height can be set
+            // dynamically.  Double-check if this generation is correct.
+
+            // IE style plugin
+            // <object ...>
+            // First compose the runtime output string
+            String s0 = "<object"
+                    + makeAttr("classid", ctxt.getOptions().getIeClassId())
+                    + makeAttr("name", name);
+
+            String s1 = "";
+            if (width != null) {
+                s1 = " + \" width=\\\"\" + " + widthStr + " + \"\\\"\"";
+            }
+
+            String s2 = "";
+            if (height != null) {
+                s2 = " + \" height=\\\"\" + " + heightStr + " + \"\\\"\"";
+            }
+
+            String s3 = makeAttr("hspace", hspace)
+                    + makeAttr("vspace", vspace)
+                    + makeAttr("align", align)
+                    + makeAttr("codebase", iepluginurl)
+                    + '>';
+
+            // Then print the output string to the java file
+            out.printil(
+                "out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
+            out.printil("out.write(\"\\n\");");
+
+            // <param > for java_code
+            s0 = "<param name=\"java_code\"" + makeAttr("value", code) + '>';
+            out.printil("out.write(" + quote(s0) + ");");
+            out.printil("out.write(\"\\n\");");
+
+            // <param > for java_codebase
+            if (codebase != null) {
+                s0 = "<param name=\"java_codebase\""
+                        + makeAttr("value", codebase)
+                        + '>';
+                out.printil("out.write(" + quote(s0) + ");");
+                out.printil("out.write(\"\\n\");");
+            }
+
+            // <param > for java_archive
+            if (archive != null) {
+                s0 = "<param name=\"java_archive\""
+                        + makeAttr("value", archive)
+                        + '>';
+                out.printil("out.write(" + quote(s0) + ");");
+                out.printil("out.write(\"\\n\");");
+            }
+
+            // <param > for type
+            s0 = "<param name=\"type\""
+                    + makeAttr(
+                        "value",
+                        "application/x-java-"
+                            + type
+                            + ";"
+                            + ((jreversion == null)
+                                ? ""
+                                : "version=" + jreversion))
+                    + '>';
+            out.printil("out.write(" + quote(s0) + ");");
+            out.printil("out.write(\"\\n\");");
+
+            /*
+             * generate a <param> for each <jsp:param> in the plugin body
+             */
+            if (n.getBody() != null)
+                n.getBody().visit(new ParamVisitor(true));
+
+            /*
+             * Netscape style plugin part
+             */
+            out.printil("out.write(" + quote("<comment>") + ");");
+            out.printil("out.write(\"\\n\");");
+            s0 = "<embed"
+                    + makeAttr(
+                        "type",
+                        "application/x-java-"
+                            + type
+                            + ";"
+                            + ((jreversion == null)
+                                ? ""
+                                : "version=" + jreversion))
+                    + makeAttr("name", name);
+
+            // s1 and s2 are the same as before.
+
+            s3 = makeAttr("hspace", hspace)
+                    + makeAttr("vspace", vspace)
+                    + makeAttr("align", align)
+                    + makeAttr("pluginspage", nspluginurl)
+                    + makeAttr("java_code", code)
+                    + makeAttr("java_codebase", codebase)
+                    + makeAttr("java_archive", archive);
+            out.printil(
+                "out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
+
+            /*
+             * Generate a 'attr = "value"' for each <jsp:param> in plugin body
+             */
+            if (n.getBody() != null)
+                n.getBody().visit(new ParamVisitor(false));
+
+            out.printil("out.write(" + quote("/>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            out.printil("out.write(" + quote("<noembed>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            /*
+             * Fallback
+             */
+            if (n.getBody() != null) {
+                visitBody(n);
+                out.printil("out.write(\"\\n\");");
+            }
+
+            out.printil("out.write(" + quote("</noembed>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            out.printil("out.write(" + quote("</comment>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            out.printil("out.write(" + quote("</object>") + ");");
+            out.printil("out.write(\"\\n\");");
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.NamedAttribute n) throws JasperException {
+            // Don't visit body of this tag - we already did earlier.
+        }
+
+        public void visit(Node.CustomTag n) throws JasperException {
+
+            // Use plugin to generate more efficient code if there is one.
+            if (n.useTagPlugin()) {
+                generateTagPlugin(n);
+                return;
+            }
+
+            TagHandlerInfo handlerInfo = getTagHandlerInfo(n);
+
+            // Create variable names
+            String baseVar =
+                createTagVarName(n.getQName(), n.getPrefix(), n.getLocalName());
+            String tagEvalVar = "_jspx_eval_" + baseVar;
+            String tagHandlerVar = "_jspx_th_" + baseVar;
+            String tagPushBodyCountVar = "_jspx_push_body_count_" + baseVar;
+
+            // If the tag contains no scripting element, generate its codes
+            // to a method.
+            ServletWriter outSave = null;
+            Node.ChildInfo ci = n.getChildInfo();
+            if (ci.isScriptless() && !ci.hasScriptingVars()) {
+                // The tag handler and its body code can reside in a separate
+                // method if it is scriptless and does not have any scripting
+                // variable defined.
+
+                String tagMethod = "_jspx_meth_" + baseVar;
+
+                // Generate a call to this method
+                out.printin("if (");
+                out.print(tagMethod);
+                out.print("(");
+                if (parent != null) {
+                    out.print(parent);
+                    out.print(", ");
+                }
+                out.print("_jspx_page_context");
+                if (pushBodyCountVar != null) {
+                    out.print(", ");
+                    out.print(pushBodyCountVar);
+                }
+                out.println("))");
+                out.pushIndent();
+                out.printil((methodNesting > 0) ? "return true;" : "return;");
+                out.popIndent();
+
+                // Set up new buffer for the method
+                outSave = out;
+                /* For fragments, their bodies will be generated in fragment
+                   helper classes, and the Java line adjustments will be done
+                   there, hence they are set to null here to avoid double
+                   adjustments.
+                */
+                GenBuffer genBuffer =
+                    new GenBuffer(n, n.implementsSimpleTag()? null: n.getBody());
+                methodsBuffered.add(genBuffer);
+                out = genBuffer.getOut();
+
+                methodNesting++;
+                // Generate code for method declaration
+                out.println();
+                out.pushIndent();
+                out.printin("private boolean ");
+                out.print(tagMethod);
+                out.print("(");
+                if (parent != null) {
+                    out.print("javax.servlet.jsp.tagext.JspTag ");
+                    out.print(parent);
+                    out.print(", ");
+                }
+                out.print("PageContext _jspx_page_context");
+                if (pushBodyCountVar != null) {
+                    out.print(", int[] ");
+                    out.print(pushBodyCountVar);
+                }
+                out.println(")");
+                out.printil("        throws Throwable {");
+                out.pushIndent();
+
+                // Initilaize local variables used in this method.
+                if (! isTagFile) {
+                    out.printil("PageContext pageContext = _jspx_page_context;");
+                }
+                out.printil("JspWriter out = _jspx_page_context.getOut();");
+                generateLocalVariables(out, n);
+            }
+
+            if (n.implementsSimpleTag()) {
+                generateCustomDoTag(n, handlerInfo, tagHandlerVar);
+            } else {
+                /*
+                 * Classic tag handler: Generate code for start element, body,
+                 * and end element
+                 */
+                generateCustomStart(
+                    n,
+                    handlerInfo,
+                    tagHandlerVar,
+                    tagEvalVar,
+                    tagPushBodyCountVar);
+
+                // visit body
+                String tmpParent = parent;
+                parent = tagHandlerVar;
+                boolean isSimpleTagParentSave = isSimpleTagParent;
+                isSimpleTagParent = false;
+                String tmpPushBodyCountVar = null;
+                if (n.implementsTryCatchFinally()) {
+                    tmpPushBodyCountVar = pushBodyCountVar;
+                    pushBodyCountVar = tagPushBodyCountVar;
+                }
+                boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
+                isSimpleTagHandler = false;
+
+                visitBody(n);
+
+                parent = tmpParent;
+                isSimpleTagParent = isSimpleTagParentSave;
+                if (n.implementsTryCatchFinally()) {
+                    pushBodyCountVar = tmpPushBodyCountVar;
+                }
+                isSimpleTagHandler = tmpIsSimpleTagHandler;
+
+                generateCustomEnd(
+                    n,
+                    tagHandlerVar,
+                    tagEvalVar,
+                    tagPushBodyCountVar);
+            }
+
+            if (ci.isScriptless() && !ci.hasScriptingVars()) {
+                // Generate end of method
+                if (methodNesting > 0) {
+                    out.printil("return false;");
+                }
+                out.popIndent();
+                out.printil("}");
+                out.popIndent();
+
+                methodNesting--;
+
+                // restore previous writer
+                out = outSave;
+            }
+        }
+
+        private static final String SINGLE_QUOTE = "'";
+        private static final String DOUBLE_QUOTE = "\\\"";
+
+        public void visit(Node.UninterpretedTag n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            /*
+             * Write begin tag
+             */
+            out.printin("out.write(\"<");
+            out.print(n.getQName());
+
+            Attributes attrs = n.getNonTaglibXmlnsAttributes();
+            int attrsLen = (attrs == null) ? 0 : attrs.getLength();
+            for (int i = 0; i < attrsLen; i++) {
+                out.print(" ");
+                out.print(attrs.getQName(i));
+                out.print("=");
+                String quote = DOUBLE_QUOTE;
+                String value = attrs.getValue(i);
+                if (value.indexOf('"') != -1) {
+                    quote = SINGLE_QUOTE;
+                }
+                out.print(quote);
+                out.print(value);
+                out.print(quote);
+            }
+
+            attrs = n.getAttributes();
+            attrsLen = (attrs == null) ? 0 : attrs.getLength();
+            Node.JspAttribute[] jspAttrs = n.getJspAttributes();
+            for (int i = 0; i < attrsLen; i++) {
+                out.print(" ");
+                out.print(attrs.getQName(i));
+                out.print("=");
+                if (jspAttrs[i].isELInterpreterInput()) {
+                    out.print("\\\"\" + ");
+                    out.print(attributeValue(jspAttrs[i], false, String.class));
+                    out.print(" + \"\\\"");
+                } else {
+                    String quote = DOUBLE_QUOTE;
+                    String value = attrs.getValue(i);
+                    if (value.indexOf('"') != -1) {
+                        quote = SINGLE_QUOTE;
+                    }
+                    out.print(quote);
+                    out.print(value);
+                    out.print(quote);
+                }
+            }
+
+            if (n.getBody() != null) {
+                out.println(">\");");
+
+                // Visit tag body
+                visitBody(n);
+
+                /*
+                 * Write end tag
+                 */
+                out.printin("out.write(\"</");
+                out.print(n.getQName());
+                out.println(">\");");
+            } else {
+                out.println("/>\");");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.JspElement n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Compute attribute value string for XML-style and named
+            // attributes
+            Hashtable map = new Hashtable();
+            Node.JspAttribute[] attrs = n.getJspAttributes();
+            for (int i = 0; attrs != null && i < attrs.length; i++) {
+                String attrStr = null;
+                if (attrs[i].isNamedAttribute()) {
+                    attrStr =
+                        generateNamedAttributeValue(
+                            attrs[i].getNamedAttributeNode());
+                } else {
+                    attrStr = attributeValue(attrs[i], false, Object.class);
+                }
+                String s =
+                    " + \" "
+                        + attrs[i].getName()
+                        + "=\\\"\" + "
+                        + attrStr
+                        + " + \"\\\"\"";
+                map.put(attrs[i].getName(), s);
+            }
+
+            // Write begin tag, using XML-style 'name' attribute as the
+            // element name
+            String elemName =
+                attributeValue(n.getNameAttribute(), false, String.class);
+            out.printin("out.write(\"<\"");
+            out.print(" + " + elemName);
+
+            // Write remaining attributes
+            Enumeration enumeration = map.keys();
+            while (enumeration.hasMoreElements()) {
+                String attrName = (String)enumeration.nextElement();
+                out.print((String)map.get(attrName));
+            }
+
+            // Does the <jsp:element> have nested tags other than
+            // <jsp:attribute>
+            boolean hasBody = false;
+            Node.Nodes subelements = n.getBody();
+            if (subelements != null) {
+                for (int i = 0; i < subelements.size(); i++) {
+                    Node subelem = subelements.getNode(i);
+                    if (!(subelem instanceof Node.NamedAttribute)) {
+                        hasBody = true;
+                        break;
+                    }
+                }
+            }
+            if (hasBody) {
+                out.println(" + \">\");");
+
+                // Smap should not include the body
+                n.setEndJavaLine(out.getJavaLine());
+
+                // Visit tag body
+                visitBody(n);
+
+                // Write end tag
+                out.printin("out.write(\"</\"");
+                out.print(" + " + elemName);
+                out.println(" + \">\");");
+            } else {
+                out.println(" + \"/>\");");
+                n.setEndJavaLine(out.getJavaLine());
+            }
+        }
+
+        public void visit(Node.TemplateText n) throws JasperException {
+
+            String text = n.getText();
+
+            int textSize = text.length();
+            if (textSize == 0) {
+                return;
+            }
+
+            // Replace marker for \$ sequence with correct sequence
+            if (text.indexOf(Constants.HACK_CHAR) > 0) {
+                if (pageInfo.isELIgnored()) {
+                    text = text.replaceAll(String.valueOf(Constants.HACK_CHAR),
+                            "\\\\\\$");
+                    textSize++;
+                } else {
+                    text = text.replace(Constants.HACK_CHAR, '$');
+                }
+            }
+
+            if (textSize <= 3) {
+               // Special case small text strings
+               n.setBeginJavaLine(out.getJavaLine());
+               int lineInc = 0;
+               for (int i = 0; i < textSize; i++) {
+                   char ch = text.charAt(i);
+                   out.printil("out.write(" + quote(ch) + ");");
+                   if (i > 0) {
+                       n.addSmap(lineInc);
+                   }
+                   if (ch == '\n') {
+                       lineInc++;
+                   }
+               }
+               n.setEndJavaLine(out.getJavaLine());
+               return;
+           }
+
+            if (ctxt.getOptions().genStringAsCharArray()) {
+               // Generate Strings as char arrays, for performance
+                ServletWriter caOut;
+                if (charArrayBuffer == null) {
+                    charArrayBuffer = new GenBuffer();
+                    caOut = charArrayBuffer.getOut();
+                    caOut.pushIndent();
+                    textMap = new HashMap();
+                } else {
+                    caOut = charArrayBuffer.getOut();
+                }
+                String charArrayName = (String) textMap.get(text);
+                if (charArrayName == null) {
+                    charArrayName = "_jspx_char_array_" + charArrayCount++;
+                    textMap.put(text, charArrayName);
+                    caOut.printin("static char[] ");
+                    caOut.print(charArrayName);
+                    caOut.print(" = ");
+                    caOut.print(quote(text));
+                    caOut.println(".toCharArray();");
+                }
+
+                n.setBeginJavaLine(out.getJavaLine());
+                out.printil("out.write(" + charArrayName + ");");
+                n.setEndJavaLine(out.getJavaLine());
+                return;
+            }
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            out.printin();
+            StringBuffer sb = new StringBuffer("out.write(\"");
+            int initLength = sb.length();
+            int count = JspUtil.CHUNKSIZE;
+            int srcLine = 0;    // relative to starting srouce line
+            for (int i = 0; i < text.length(); i++) {
+                char ch = text.charAt(i);
+                --count;
+                switch (ch) {
+                    case '"' :
+                        sb.append('\\').append('\"');
+                        break;
+                    case '\\' :
+                        sb.append('\\').append('\\');
+                        break;
+                    case '\r' :
+                        sb.append('\\').append('r');
+                        break;
+                    case '\n' :
+                        sb.append('\\').append('n');
+                        srcLine++;
+
+                        if (breakAtLF || count < 0) {
+                            // Generate an out.write() when see a '\n' in template
+                            sb.append("\");");
+                            out.println(sb.toString());
+                            if (i < text.length() - 1) {
+                                out.printin();
+                            }
+                            sb.setLength(initLength);
+                            count = JspUtil.CHUNKSIZE;
+                        }
+                        // add a Smap for this line
+                        n.addSmap(srcLine);
+                        break;
+                    case '\t' : // Not sure we need this
+                        sb.append('\\').append('t');
+                        break;
+                    default :
+                        sb.append(ch);
+                }
+            }
+
+            if (sb.length() > initLength) {
+                sb.append("\");");
+                out.println(sb.toString());
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.JspBody n) throws JasperException {
+            if (n.getBody() != null) {
+                if (isSimpleTagHandler) {
+                    out.printin(simpleTagHandlerVar);
+                    out.print(".setJspBody(");
+                    generateJspFragment(n, simpleTagHandlerVar);
+                    out.println(");");
+                } else {
+                    visitBody(n);
+                }
+            }
+        }
+
+        public void visit(Node.InvokeAction n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Copy virtual page scope of tag file to page scope of invoking
+            // page
+            out.printil(
+                "((org.apache.struts2.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
+            String varReaderAttr = n.getTextAttribute("varReader");
+            String varAttr = n.getTextAttribute("var");
+            if (varReaderAttr != null || varAttr != null) {
+                out.printil("_jspx_sout = new java.io.StringWriter();");
+            } else {
+                out.printil("_jspx_sout = null;");
+            }
+
+            // Invoke fragment, unless fragment is null
+            out.printin("if (");
+            out.print(toGetterMethod(n.getTextAttribute("fragment")));
+            out.println(" != null) {");
+            out.pushIndent();
+            out.printin(toGetterMethod(n.getTextAttribute("fragment")));
+            out.println(".invoke(_jspx_sout);");
+            out.popIndent();
+            out.printil("}");
+
+            // Store varReader in appropriate scope
+            if (varReaderAttr != null || varAttr != null) {
+                String scopeName = n.getTextAttribute("scope");
+                out.printin("_jspx_page_context.setAttribute(");
+                if (varReaderAttr != null) {
+                    out.print(quote(varReaderAttr));
+                    out.print(
+                        ", new java.io.StringReader(_jspx_sout.toString())");
+                } else {
+                    out.print(quote(varAttr));
+                    out.print(", _jspx_sout.toString()");
+                }
+                if (scopeName != null) {
+                    out.print(", ");
+                    out.print(getScopeConstant(scopeName));
+                }
+                out.println(");");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.DoBodyAction n) throws JasperException {
+
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Copy virtual page scope of tag file to page scope of invoking
+            // page
+            out.printil(
+                "((org.apache.struts2.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
+
+            // Invoke body
+            String varReaderAttr = n.getTextAttribute("varReader");
+            String varAttr = n.getTextAttribute("var");
+            if (varReaderAttr != null || varAttr != null) {
+                out.printil("_jspx_sout = new java.io.StringWriter();");
+            } else {
+                out.printil("_jspx_sout = null;");
+            }
+            out.printil("if (getJspBody() != null)");
+            out.pushIndent();
+            out.printil("getJspBody().invoke(_jspx_sout);");
+            out.popIndent();
+
+            // Store varReader in appropriate scope
+            if (varReaderAttr != null || varAttr != null) {
+                String scopeName = n.getTextAttribute("scope");
+                out.printin("_jspx_page_context.setAttribute(");
+                if (varReaderAttr != null) {
+                    out.print(quote(varReaderAttr));
+                    out.print(
+                        ", new java.io.StringReader(_jspx_sout.toString())");
+                } else {
+                    out.print(quote(varAttr));
+                    out.print(", _jspx_sout.toString()");
+                }
+                if (scopeName != null) {
+                    out.print(", ");
+                    out.print(getScopeConstant(scopeName));
+                }
+                out.println(");");
+            }
+
+            n.setEndJavaLine(out.getJavaLine());
+        }
+
+        public void visit(Node.AttributeGenerator n) throws JasperException {
+            Node.CustomTag tag = n.getTag();
+            Node.JspAttribute[] attrs = tag.getJspAttributes();
+            for (int i = 0; attrs != null && i < attrs.length; i++) {
+                if (attrs[i].getName().equals(n.getName())) {
+                    out.print(
+                        evaluateAttribute(
+                            getTagHandlerInfo(tag),
+                            attrs[i],
+                            tag,
+                            null));
+                    break;
+                }
+            }
+        }
+
+        private TagHandlerInfo getTagHandlerInfo(Node.CustomTag n)
+            throws JasperException {
+            Hashtable handlerInfosByShortName =
+                (Hashtable)handlerInfos.get(n.getPrefix());
+            if (handlerInfosByShortName == null) {
+                handlerInfosByShortName = new Hashtable();
+                handlerInfos.put(n.getPrefix(), handlerInfosByShortName);
+            }
+            TagHandlerInfo handlerInfo =
+                (TagHandlerInfo)handlerInfosByShortName.get(n.getLocalName());
+            if (handlerInfo == null) {
+                handlerInfo =
+                    new TagHandlerInfo(n, n.getTagHandlerClass(), err);
+                handlerInfosByShortName.put(n.getLocalName(), handlerInfo);
+            }
+            return handlerInfo;
+        }
+
+        private void generateTagPlugin(Node.CustomTag n)
+            throws JasperException {
+            if (n.getAtSTag() != null) {
+                n.getAtSTag().visit(this);
+            }
+            visitBody(n);
+            if (n.getAtETag() != null) {
+                n.getAtETag().visit(this);
+            }
+        }
+
+        private void generateCustomStart(
+            Node.CustomTag n,
+            TagHandlerInfo handlerInfo,
+            String tagHandlerVar,
+            String tagEvalVar,
+            String tagPushBodyCountVar)
+            throws JasperException {
+
+            Class tagHandlerClass = handlerInfo.getTagHandlerClass();
+
+            out.printin("//  ");
+            out.println(n.getQName());
+            n.setBeginJavaLine(out.getJavaLine());
+
+            // Declare AT_BEGIN scripting variables
+            declareScriptingVars(n, VariableInfo.AT_BEGIN);
+            saveScriptingVars(n, VariableInfo.AT_BEGIN);
+
+            String tagHandlerClassName =
+                JspUtil.getCanonicalName(tagHandlerClass);
+            out.printin(tagHandlerClassName);
+            out.print(" ");
+            out.print(tagHandlerVar);
+            out.print(" = ");
+            if (isPoolingEnabled) {
+                out.print("(");
+                out.print(tagHandlerClassName);
+                out.print(") ");
+                out.print(n.getTagHandlerPoolName());
+                out.print(".get(");
+                out.print(tagHandlerClassName);
+                out.println(".class);");
+            } else {
+                out.print("new ");
+                out.print(tagHandlerClassName);
+                out.println("();");
+            }
+
+            generateSetters(n, tagHandlerVar, handlerInfo, false);
+
+            if (n.implementsTryCatchFinally()) {
+                out.printin("int[] ");
+                out.print(tagPushBodyCountVar);
+                out.println(" = new int[] { 0 };");
+                out.printil("try {");
+                out.pushIndent();
+            }
+            out.printin("int ");
+            out.print(tagEvalVar);
+            out.print(" = ");
+            out.print(tagHandlerVar);
+            out.println(".doStartTag();");
+
+            if (!n.implementsBodyTag()) {
+                // Synchronize AT_BEGIN scripting variables
+                syncScriptingVars(n, VariableInfo.AT_BEGIN);
+            }
+
+            if (!n.hasEmptyBody()) {
+                out.printin("if (");
+                out.print(tagEvalVar);
+                out.println(" != javax.servlet.jsp.tagext.Tag.SKIP_BODY) {");
+                out.pushIndent();
+
+                // Declare NESTED scripting variables
+                declareScriptingVars(n, VariableInfo.NESTED);
+                saveScriptingVars(n, VariableInfo.NESTED);
+
+                if (n.implementsBodyTag()) {
+                    out.printin("if (");
+                    out.print(tagEvalVar);
+                    out.println(
+                        " != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
+                    // Assume EVAL_BODY_BUFFERED
+                    out.pushIndent();
+                    out.printil("out = _jspx_page_context.pushBody();");
+                    if (n.implementsTryCatchFinally()) {
+                        out.printin(tagPushBodyCountVar);
+                        out.println("[0]++;");
+                    } else if (pushBodyCountVar != null) {
+                        out.printin(pushBodyCountVar);
+                        out.println("[0]++;");
+                    }
+                    out.printin(tagHandlerVar);
+                    out.println(
+                        ".setBodyContent((javax.servlet.jsp.tagext.BodyContent) out);");
+                    out.printin(tagHandlerVar);
+                    out.println(".doInitBody();");
+
+                    out.popIndent();
+                    out.printil("}");
+

[... 1820 lines stripped ...]


Mime
View raw message