Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » jasper » compiler » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   package org.apache.jasper.compiler;
   19   
   20   import java.beans.BeanInfo;
   21   import java.beans.IntrospectionException;
   22   import java.beans.Introspector;
   23   import java.beans.PropertyDescriptor;
   24   import java.lang.reflect.Method;
   25   import java.lang.reflect.Modifier;
   26   import java.util.ArrayList;
   27   import java.util.Arrays;
   28   import java.util.Collections;
   29   import java.util.Enumeration;
   30   import java.util.Hashtable;
   31   import java.util.HashMap;
   32   import java.util.Iterator;
   33   import java.util.List;
   34   import java.util.Vector;
   35   
   36   import javax.el.MethodExpression;
   37   import javax.el.ValueExpression;
   38   import javax.servlet.jsp.tagext.TagAttributeInfo;
   39   import javax.servlet.jsp.tagext.TagInfo;
   40   import javax.servlet.jsp.tagext.TagVariableInfo;
   41   import javax.servlet.jsp.tagext.VariableInfo;
   42   
   43   import org.apache.jasper.Constants;
   44   import org.apache.jasper.JasperException;
   45   import org.apache.jasper.JspCompilationContext;
   46   import org.apache.jasper.runtime.JspRuntimeLibrary;
   47   import org.xml.sax.Attributes;
   48   
   49   /**
   50    * Generate Java source from Nodes
   51    * 
   52    * @author Anil K. Vijendran
   53    * @author Danno Ferrin
   54    * @author Mandar Raje
   55    * @author Rajiv Mordani
   56    * @author Pierre Delisle
   57    * 
   58    * Tomcat 4.1.x and Tomcat 5:
   59    * @author Kin-man Chung
   60    * @author Jan Luehe
   61    * @author Shawn Bayern
   62    * @author Mark Roth
   63    * @author Denis Benoit
   64    * 
   65    * Tomcat 6.x
   66    * @author Jacob Hookom
   67    * @author Remy Maucherat
   68    */
   69   
   70   class Generator {
   71   
   72       private static final Class[] OBJECT_CLASS = { Object.class };
   73   
   74       private static final String VAR_EXPRESSIONFACTORY = 
   75           System.getProperty("org.apache.jasper.compiler.Generator.VAR_EXPRESSIONFACTORY", "_el_expressionfactory");
   76       private static final String VAR_ANNOTATIONPROCESSOR = 
   77           System.getProperty("org.apache.jasper.compiler.Generator.VAR_ANNOTATIONPROCESSOR", "_jsp_annotationprocessor");
   78   
   79       private ServletWriter out;
   80   
   81       private ArrayList methodsBuffered;
   82   
   83       private FragmentHelperClass fragmentHelperClass;
   84   
   85       private ErrorDispatcher err;
   86   
   87       private BeanRepository beanInfo;
   88   
   89       private JspCompilationContext ctxt;
   90   
   91       private boolean isPoolingEnabled;
   92   
   93       private boolean breakAtLF;
   94   
   95       private String jspIdPrefix;
   96   
   97       private int jspId;
   98   
   99       private PageInfo pageInfo;
  100   
  101       private Vector<String> tagHandlerPoolNames;
  102   
  103       private GenBuffer charArrayBuffer;
  104   
  105       /**
  106        * @param s
  107        *            the input string
  108        * @return quoted and escaped string, per Java rule
  109        */
  110       static String quote(String s) {
  111   
  112           if (s == null)
  113               return "null";
  114   
  115           return '"' + escape(s) + '"';
  116       }
  117   
  118       /**
  119        * @param s
  120        *            the input string
  121        * @return escaped string, per Java rule
  122        */
  123       static String escape(String s) {
  124   
  125           if (s == null)
  126               return "";
  127   
  128           StringBuffer b = new StringBuffer();
  129           for (int i = 0; i < s.length(); i++) {
  130               char c = s.charAt(i);
  131               if (c == '"')
  132                   b.append('\\').append('"');
  133               else if (c == '\\')
  134                   b.append('\\').append('\\');
  135               else if (c == '\n')
  136                   b.append('\\').append('n');
  137               else if (c == '\r')
  138                   b.append('\\').append('r');
  139               else
  140                   b.append(c);
  141           }
  142           return b.toString();
  143       }
  144   
  145       /**
  146        * Single quote and escape a character
  147        */
  148       static String quote(char c) {
  149   
  150           StringBuffer b = new StringBuffer();
  151           b.append('\'');
  152           if (c == '\'')
  153               b.append('\\').append('\'');
  154           else if (c == '\\')
  155               b.append('\\').append('\\');
  156           else if (c == '\n')
  157               b.append('\\').append('n');
  158           else if (c == '\r')
  159               b.append('\\').append('r');
  160           else
  161               b.append(c);
  162           b.append('\'');
  163           return b.toString();
  164       }
  165   
  166       private String createJspId() throws JasperException {
  167           if (this.jspIdPrefix == null) {
  168               StringBuffer sb = new StringBuffer(32);
  169               String name = ctxt.getServletJavaFileName();
  170               sb.append("jsp_").append(Math.abs(name.hashCode())).append('_');
  171               this.jspIdPrefix = sb.toString();
  172           }
  173           return this.jspIdPrefix + (this.jspId++);
  174       }
  175   
  176       /**
  177        * Generates declarations. This includes "info" of the page directive, and
  178        * scriptlet declarations.
  179        */
  180       private void generateDeclarations(Node.Nodes page) throws JasperException {
  181   
  182           class DeclarationVisitor extends Node.Visitor {
  183   
  184               private boolean getServletInfoGenerated = false;
  185   
  186               /*
  187                * Generates getServletInfo() method that returns the value of the
  188                * page directive's 'info' attribute, if present.
  189                * 
  190                * The Validator has already ensured that if the translation unit
  191                * contains more than one page directive with an 'info' attribute,
  192                * their values match.
  193                */
  194               public void visit(Node.PageDirective n) throws JasperException {
  195   
  196                   if (getServletInfoGenerated) {
  197                       return;
  198                   }
  199   
  200                   String info = n.getAttributeValue("info");
  201                   if (info == null)
  202                       return;
  203   
  204                   getServletInfoGenerated = true;
  205                   out.printil("public String getServletInfo() {");
  206                   out.pushIndent();
  207                   out.printin("return ");
  208                   out.print(quote(info));
  209                   out.println(";");
  210                   out.popIndent();
  211                   out.printil("}");
  212                   out.println();
  213               }
  214   
  215               public void visit(Node.Declaration n) throws JasperException {
  216                   n.setBeginJavaLine(out.getJavaLine());
  217                   out.printMultiLn(new String(n.getText()));
  218                   out.println();
  219                   n.setEndJavaLine(out.getJavaLine());
  220               }
  221   
  222               // Custom Tags may contain declarations from tag plugins.
  223               public void visit(Node.CustomTag n) throws JasperException {
  224                   if (n.useTagPlugin()) {
  225                       if (n.getAtSTag() != null) {
  226                           n.getAtSTag().visit(this);
  227                       }
  228                       visitBody(n);
  229                       if (n.getAtETag() != null) {
  230                           n.getAtETag().visit(this);
  231                       }
  232                   } else {
  233                       visitBody(n);
  234                   }
  235               }
  236           }
  237   
  238           out.println();
  239           page.visit(new DeclarationVisitor());
  240       }
  241   
  242       /**
  243        * Compiles list of tag handler pool names.
  244        */
  245       private void compileTagHandlerPoolList(Node.Nodes page)
  246               throws JasperException {
  247   
  248           class TagHandlerPoolVisitor extends Node.Visitor {
  249   
  250               private Vector names;
  251   
  252               /*
  253                * Constructor
  254                * 
  255                * @param v Vector of tag handler pool names to populate
  256                */
  257               TagHandlerPoolVisitor(Vector v) {
  258                   names = v;
  259               }
  260   
  261               /*
  262                * Gets the name of the tag handler pool for the given custom tag
  263                * and adds it to the list of tag handler pool names unless it is
  264                * already contained in it.
  265                */
  266               public void visit(Node.CustomTag n) throws JasperException {
  267   
  268                   if (!n.implementsSimpleTag()) {
  269                       String name = createTagHandlerPoolName(n.getPrefix(), n
  270                               .getLocalName(), n.getAttributes(), n
  271                               .hasEmptyBody());
  272                       n.setTagHandlerPoolName(name);
  273                       if (!names.contains(name)) {
  274                           names.add(name);
  275                       }
  276                   }
  277                   visitBody(n);
  278               }
  279   
  280               /*
  281                * Creates the name of the tag handler pool whose tag handlers may
  282                * be (re)used to service this action.
  283                * 
  284                * @return The name of the tag handler pool
  285                */
  286               private String createTagHandlerPoolName(String prefix,
  287                       String shortName, Attributes attrs, boolean hasEmptyBody) {
  288                   String poolName = null;
  289   
  290                   poolName = "_jspx_tagPool_" + prefix + "_" + shortName;
  291                   if (attrs != null) {
  292                       String[] attrNames = new String[attrs.getLength()];
  293                       for (int i = 0; i < attrNames.length; i++) {
  294                           attrNames[i] = attrs.getQName(i);
  295                       }
  296                       Arrays.sort(attrNames, Collections.reverseOrder());
  297                       for (int i = 0; i < attrNames.length; i++) {
  298                           poolName = poolName + "_" + attrNames[i];
  299                       }
  300                   }
  301                   if (hasEmptyBody) {
  302                       poolName = poolName + "_nobody";
  303                   }
  304                   return JspUtil.makeJavaIdentifier(poolName);
  305               }
  306           }
  307   
  308           page.visit(new TagHandlerPoolVisitor(tagHandlerPoolNames));
  309       }
  310   
  311       private void declareTemporaryScriptingVars(Node.Nodes page)
  312               throws JasperException {
  313   
  314           class ScriptingVarVisitor extends Node.Visitor {
  315   
  316               private Vector vars;
  317   
  318               ScriptingVarVisitor() {
  319                   vars = new Vector();
  320               }
  321   
  322               public void visit(Node.CustomTag n) throws JasperException {
  323   
  324                   if (n.getCustomNestingLevel() > 0) {
  325                       TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
  326                       VariableInfo[] varInfos = n.getVariableInfos();
  327   
  328                       if (varInfos.length > 0) {
  329                           for (int i = 0; i < varInfos.length; i++) {
  330                               String varName = varInfos[i].getVarName();
  331                               String tmpVarName = "_jspx_" + varName + "_"
  332                                       + n.getCustomNestingLevel();
  333                               if (!vars.contains(tmpVarName)) {
  334                                   vars.add(tmpVarName);
  335                                   out.printin(varInfos[i].getClassName());
  336                                   out.print(" ");
  337                                   out.print(tmpVarName);
  338                                   out.print(" = ");
  339                                   out.print(null);
  340                                   out.println(";");
  341                               }
  342                           }
  343                       } else {
  344                           for (int i = 0; i < tagVarInfos.length; i++) {
  345                               String varName = tagVarInfos[i].getNameGiven();
  346                               if (varName == null) {
  347                                   varName = n.getTagData().getAttributeString(
  348                                           tagVarInfos[i].getNameFromAttribute());
  349                               } else if (tagVarInfos[i].getNameFromAttribute() != null) {
  350                                   // alias
  351                                   continue;
  352                               }
  353                               String tmpVarName = "_jspx_" + varName + "_"
  354                                       + n.getCustomNestingLevel();
  355                               if (!vars.contains(tmpVarName)) {
  356                                   vars.add(tmpVarName);
  357                                   out.printin(tagVarInfos[i].getClassName());
  358                                   out.print(" ");
  359                                   out.print(tmpVarName);
  360                                   out.print(" = ");
  361                                   out.print(null);
  362                                   out.println(";");
  363                               }
  364                           }
  365                       }
  366                   }
  367   
  368                   visitBody(n);
  369               }
  370           }
  371   
  372           page.visit(new ScriptingVarVisitor());
  373       }
  374   
  375       /**
  376        * Generates the _jspInit() method for instantiating the tag handler pools.
  377        * For tag file, _jspInit has to be invoked manually, and the ServletConfig
  378        * object explicitly passed.
  379        * 
  380        * In JSP 2.1, we also instantiate an ExpressionFactory
  381        */
  382       private void generateInit() {
  383   
  384           if (ctxt.isTagFile()) {
  385               out.printil("private void _jspInit(ServletConfig config) {");
  386           } else {
  387               out.printil("public void _jspInit() {");
  388           }
  389   
  390           out.pushIndent();
  391           if (isPoolingEnabled) {
  392               for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
  393                   out.printin(tagHandlerPoolNames.elementAt(i));
  394                   out.print(" = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool(");
  395                   if (ctxt.isTagFile()) {
  396                       out.print("config");
  397                   } else {
  398                       out.print("getServletConfig()");
  399                   }
  400                   out.println(");");
  401               }
  402           }
  403           
  404           out.printin(VAR_EXPRESSIONFACTORY);
  405           out.print(" = _jspxFactory.getJspApplicationContext(");
  406           if (ctxt.isTagFile()) {
  407               out.print("config");
  408           } else {
  409               out.print("getServletConfig()");
  410           }
  411           out.println(".getServletContext()).getExpressionFactory();");
  412   
  413           out.printin(VAR_ANNOTATIONPROCESSOR);
  414           out.print(" = (org.apache.AnnotationProcessor) ");
  415           if (ctxt.isTagFile()) {
  416               out.print("config");
  417           } else {
  418               out.print("getServletConfig()");
  419           }
  420           out.println(".getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());");
  421   
  422           out.popIndent();
  423           out.printil("}");
  424           out.println();
  425       }
  426   
  427       /**
  428        * Generates the _jspDestroy() method which is responsible for calling the
  429        * release() method on every tag handler in any of the tag handler pools.
  430        */
  431       private void generateDestroy() {
  432   
  433           out.printil("public void _jspDestroy() {");
  434           out.pushIndent();
  435           
  436           if (isPoolingEnabled) {
  437               for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
  438                                   out.printin((String) tagHandlerPoolNames.elementAt(i));
  439                                   out.println(".release();");
  440               }
  441           }
  442           
  443           out.popIndent();
  444           out.printil("}");
  445           out.println();
  446       }
  447   
  448       /**
  449        * Generate preamble package name (shared by servlet and tag handler
  450        * preamble generation)
  451        */
  452       private void genPreamblePackage(String packageName) throws JasperException {
  453           if (!"".equals(packageName) && packageName != null) {
  454               out.printil("package " + packageName + ";");
  455               out.println();
  456           }
  457       }
  458   
  459       /**
  460        * Generate preamble imports (shared by servlet and tag handler preamble
  461        * generation)
  462        */
  463       private void genPreambleImports() throws JasperException {
  464           Iterator iter = pageInfo.getImports().iterator();
  465           while (iter.hasNext()) {
  466               out.printin("import ");
  467               out.print((String) iter.next());
  468               out.println(";");
  469           }
  470   
  471           out.println();
  472       }
  473   
  474       /**
  475        * Generation of static initializers in preamble. For example, dependant
  476        * list, el function map, prefix map. (shared by servlet and tag handler
  477        * preamble generation)
  478        */
  479       private void genPreambleStaticInitializers() throws JasperException {
  480           out.printil("private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();");
  481           out.println();
  482   
  483           // Static data for getDependants()
  484           out.printil("private static java.util.List _jspx_dependants;");
  485           out.println();
  486           List dependants = pageInfo.getDependants();
  487           Iterator iter = dependants.iterator();
  488           if (!dependants.isEmpty()) {
  489               out.printil("static {");
  490               out.pushIndent();
  491               out.printin("_jspx_dependants = new java.util.ArrayList(");
  492               out.print("" + dependants.size());
  493               out.println(");");
  494               while (iter.hasNext()) {
  495                   out.printin("_jspx_dependants.add(\"");
  496                   out.print((String) iter.next());
  497                   out.println("\");");
  498               }
  499               out.popIndent();
  500               out.printil("}");
  501               out.println();
  502           }
  503       }
  504   
  505       /**
  506        * Declare tag handler pools (tags of the same type and with the same
  507        * attribute set share the same tag handler pool) (shared by servlet and tag
  508        * handler preamble generation)
  509        * 
  510        * In JSP 2.1, we also scope an instance of ExpressionFactory
  511        */
  512       private void genPreambleClassVariableDeclarations(String className)
  513               throws JasperException {
  514           if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
  515               for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
  516                   out.printil("private org.apache.jasper.runtime.TagHandlerPool "
  517                           + tagHandlerPoolNames.elementAt(i) + ";");
  518               }
  519               out.println();
  520           }
  521           out.printin("private javax.el.ExpressionFactory ");
  522           out.print(VAR_EXPRESSIONFACTORY);
  523           out.println(";");
  524           out.printin("private org.apache.AnnotationProcessor ");
  525           out.print(VAR_ANNOTATIONPROCESSOR);
  526           out.println(";");
  527           out.println();
  528       }
  529   
  530       /**
  531        * Declare general-purpose methods (shared by servlet and tag handler
  532        * preamble generation)
  533        */
  534       private void genPreambleMethods() throws JasperException {
  535           // Method used to get compile time file dependencies
  536           out.printil("public Object getDependants() {");
  537           out.pushIndent();
  538           out.printil("return _jspx_dependants;");
  539           out.popIndent();
  540           out.printil("}");
  541           out.println();
  542           
  543           generateInit();
  544           generateDestroy();
  545       }
  546   
  547       /**
  548        * Generates the beginning of the static portion of the servlet.
  549        */
  550       private void generatePreamble(Node.Nodes page) throws JasperException {
  551   
  552           String servletPackageName = ctxt.getServletPackageName();
  553           String servletClassName = ctxt.getServletClassName();
  554           String serviceMethodName = Constants.SERVICE_METHOD_NAME;
  555   
  556           // First the package name:
  557           genPreamblePackage(servletPackageName);
  558   
  559           // Generate imports
  560           genPreambleImports();
  561   
  562           // Generate class declaration
  563           out.printin("public final class ");
  564           out.print(servletClassName);
  565           out.print(" extends ");
  566           out.println(pageInfo.getExtends());
  567           out.printin("    implements org.apache.jasper.runtime.JspSourceDependent");
  568           if (!pageInfo.isThreadSafe()) {
  569               out.println(",");
  570               out.printin("                 SingleThreadModel");
  571           }
  572           out.println(" {");
  573           out.pushIndent();
  574   
  575           // Class body begins here
  576           generateDeclarations(page);
  577   
  578           // Static initializations here
  579           genPreambleStaticInitializers();
  580   
  581           // Class variable declarations
  582           genPreambleClassVariableDeclarations(servletClassName);
  583   
  584           // Constructor
  585           // generateConstructor(className);
  586   
  587           // Methods here
  588           genPreambleMethods();
  589   
  590           // Now the service method
  591           out.printin("public void ");
  592           out.print(serviceMethodName);
  593           out.println("(HttpServletRequest request, HttpServletResponse response)");
  594           out.println("        throws java.io.IOException, ServletException {");
  595   
  596           out.pushIndent();
  597           out.println();
  598   
  599           // Local variable declarations
  600           out.printil("PageContext pageContext = null;");
  601   
  602           if (pageInfo.isSession())
  603               out.printil("HttpSession session = null;");
  604   
  605           if (pageInfo.isErrorPage()) {
  606               out.printil("Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);");
  607               out.printil("if (exception != null) {");
  608               out.pushIndent();
  609               out.printil("response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);");
  610               out.popIndent();
  611               out.printil("}");
  612           }
  613   
  614           out.printil("ServletContext application = null;");
  615           out.printil("ServletConfig config = null;");
  616           out.printil("JspWriter out = null;");
  617           out.printil("Object page = this;");
  618   
  619           out.printil("JspWriter _jspx_out = null;");
  620           out.printil("PageContext _jspx_page_context = null;");
  621           out.println();
  622   
  623           declareTemporaryScriptingVars(page);
  624           out.println();
  625   
  626           out.printil("try {");
  627           out.pushIndent();
  628   
  629           out.printin("response.setContentType(");
  630           out.print(quote(pageInfo.getContentType()));
  631           out.println(");");
  632   
  633           if (ctxt.getOptions().isXpoweredBy()) {
  634               out.printil("response.addHeader(\"X-Powered-By\", \"JSP/2.1\");");
  635           }
  636   
  637           out
  638                   .printil("pageContext = _jspxFactory.getPageContext(this, request, response,");
  639           out.printin("\t\t\t");
  640           out.print(quote(pageInfo.getErrorPage()));
  641           out.print(", " + pageInfo.isSession());
  642           out.print(", " + pageInfo.getBuffer());
  643           out.print(", " + pageInfo.isAutoFlush());
  644           out.println(");");
  645           out.printil("_jspx_page_context = pageContext;");
  646   
  647           out.printil("application = pageContext.getServletContext();");
  648           out.printil("config = pageContext.getServletConfig();");
  649   
  650           if (pageInfo.isSession())
  651               out.printil("session = pageContext.getSession();");
  652           out.printil("out = pageContext.getOut();");
  653           out.printil("_jspx_out = out;");
  654           out.println();
  655       }
  656   
  657       /**
  658        * Generates an XML Prolog, which includes an XML declaration and an XML
  659        * doctype declaration.
  660        */
  661       private void generateXmlProlog(Node.Nodes page) {
  662   
  663           /*
  664            * An XML declaration is generated under the following conditions: -
  665            * 'omit-xml-declaration' attribute of <jsp:output> action is set to
  666            * "no" or "false" - JSP document without a <jsp:root>
  667            */
  668           String omitXmlDecl = pageInfo.getOmitXmlDecl();
  669           if ((omitXmlDecl != null && !JspUtil.booleanValue(omitXmlDecl))
  670                   || (omitXmlDecl == null && page.getRoot().isXmlSyntax()
  671                           && !pageInfo.hasJspRoot() && !ctxt.isTagFile())) {
  672               String cType = pageInfo.getContentType();
  673               String charSet = cType.substring(cType.indexOf("charset=") + 8);
  674               out.printil("out.write(\"<?xml version=\\\"1.0\\\" encoding=\\\""
  675                       + charSet + "\\\"?>\\n\");");
  676           }
  677   
  678           /*
  679            * Output a DOCTYPE declaration if the doctype-root-element appears. If
  680            * doctype-public appears: <!DOCTYPE name PUBLIC "doctypePublic"
  681            * "doctypeSystem"> else <!DOCTYPE name SYSTEM "doctypeSystem" >
  682            */
  683   
  684           String doctypeName = pageInfo.getDoctypeName();
  685           if (doctypeName != null) {
  686               String doctypePublic = pageInfo.getDoctypePublic();
  687               String doctypeSystem = pageInfo.getDoctypeSystem();
  688               out.printin("out.write(\"<!DOCTYPE ");
  689               out.print(doctypeName);
  690               if (doctypePublic == null) {
  691                   out.print(" SYSTEM \\\"");
  692               } else {
  693                   out.print(" PUBLIC \\\"");
  694                   out.print(doctypePublic);
  695                   out.print("\\\" \\\"");
  696               }
  697               out.print(doctypeSystem);
  698               out.println("\\\">\\n\");");
  699           }
  700       }
  701   
  702       /*
  703        * Generates the constructor. (shared by servlet and tag handler preamble
  704        * generation)
  705        */
  706       private void generateConstructor(String className) {
  707           out.printil("public " + className + "() {");
  708           out.printil("}");
  709           out.println();
  710       }
  711   
  712       /**
  713        * A visitor that generates codes for the elements in the page.
  714        */
  715       class GenerateVisitor extends Node.Visitor {
  716   
  717           /*
  718            * Hashtable containing introspection information on tag handlers:
  719            * <key>: tag prefix <value>: hashtable containing introspection on tag
  720            * handlers: <key>: tag short name <value>: introspection info of tag
  721            * handler for <prefix:shortName> tag
  722            */
  723           private Hashtable handlerInfos;
  724   
  725           private Hashtable tagVarNumbers;
  726   
  727           private String parent;
  728   
  729           private boolean isSimpleTagParent; // Is parent a SimpleTag?
  730   
  731           private String pushBodyCountVar;
  732   
  733           private String simpleTagHandlerVar;
  734   
  735           private boolean isSimpleTagHandler;
  736   
  737           private boolean isFragment;
  738   
  739           private boolean isTagFile;
  740   
  741           private ServletWriter out;
  742   
  743           private ArrayList methodsBuffered;
  744   
  745           private FragmentHelperClass fragmentHelperClass;
  746   
  747           private int methodNesting;
  748   
  749           private TagInfo tagInfo;
  750   
  751           private ClassLoader loader;
  752   
  753           private int charArrayCount;
  754   
  755           private HashMap textMap;
  756   
  757           /**
  758            * Constructor.
  759            */
  760           public GenerateVisitor(boolean isTagFile, ServletWriter out,
  761                   ArrayList methodsBuffered,
  762                   FragmentHelperClass fragmentHelperClass, ClassLoader loader,
  763                   TagInfo tagInfo) {
  764   
  765               this.isTagFile = isTagFile;
  766               this.out = out;
  767               this.methodsBuffered = methodsBuffered;
  768               this.fragmentHelperClass = fragmentHelperClass;
  769               this.loader = loader;
  770               this.tagInfo = tagInfo;
  771               methodNesting = 0;
  772               handlerInfos = new Hashtable();
  773               tagVarNumbers = new Hashtable();
  774               textMap = new HashMap();
  775           }
  776   
  777           /**
  778            * Returns an attribute value, optionally URL encoded. If the value is a
  779            * runtime expression, the result is the expression itself, as a string.
  780            * If the result is an EL expression, we insert a call to the
  781            * interpreter. If the result is a Named Attribute we insert the
  782            * generated variable name. Otherwise the result is a string literal,
  783            * quoted and escaped.
  784            * 
  785            * @param attr
  786            *            An JspAttribute object
  787            * @param encode
  788            *            true if to be URL encoded
  789            * @param expectedType
  790            *            the expected type for an EL evaluation (ignored for
  791            *            attributes that aren't EL expressions)
  792            */
  793           private String attributeValue(Node.JspAttribute attr, boolean encode,
  794                   Class expectedType) {
  795               String v = attr.getValue();
  796               if (!attr.isNamedAttribute() && (v == null))
  797                   return "";
  798   
  799               if (attr.isExpression()) {
  800                   if (encode) {
  801                       return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf("
  802                               + v + "), request.getCharacterEncoding())";
  803                   }
  804                   return v;
  805               } else if (attr.isELInterpreterInput()) {
  806                   boolean replaceESC = v.indexOf(Constants.ESC) > 0;
  807                   v = JspUtil.interpreterCall(this.isTagFile, v, expectedType,
  808                           attr.getEL().getMapName(), false);
  809                   // XXX ESC replacement hack
  810                   if (replaceESC) {
  811                       v = "(" + v + ").replace(" + Constants.ESCStr + ", '$')";
  812                   }
  813                   if (encode) {
  814                       return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("
  815                               + v + ", request.getCharacterEncoding())";
  816                   }
  817                   return v;
  818               } else if (attr.isNamedAttribute()) {
  819                   return attr.getNamedAttributeNode().getTemporaryVariableName();
  820               } else {
  821                   if (encode) {
  822                       return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("
  823                               + quote(v) + ", request.getCharacterEncoding())";
  824                   }
  825                   return quote(v);
  826               }
  827           }
  828   
  829           /**
  830            * Prints the attribute value specified in the param action, in the form
  831            * of name=value string.
  832            * 
  833            * @param n
  834            *            the parent node for the param action nodes.
  835            */
  836           private void printParams(Node n, String pageParam, boolean literal)
  837                   throws JasperException {
  838   
  839               class ParamVisitor extends Node.Visitor {
  840                   String separator;
  841   
  842                   ParamVisitor(String separator) {
  843                       this.separator = separator;
  844                   }
  845   
  846                   public void visit(Node.ParamAction n) throws JasperException {
  847   
  848                       out.print(" + ");
  849                       out.print(separator);
  850                       out.print(" + ");
  851                       out.print("org.apache.jasper.runtime.JspRuntimeLibrary."
  852                               + "URLEncode(" + quote(n.getTextAttribute("name"))
  853                               + ", request.getCharacterEncoding())");
  854                       out.print("+ \"=\" + ");
  855                       out.print(attributeValue(n.getValue(), true, String.class));
  856   
  857                       // The separator is '&' after the second use
  858                       separator = "\"&\"";
  859                   }
  860               }
  861   
  862               String sep;
  863               if (literal) {
  864                   sep = pageParam.indexOf('?') > 0 ? "\"&\"" : "\"?\"";
  865               } else {
  866                   sep = "((" + pageParam + ").indexOf('?')>0? '&': '?')";
  867               }
  868               if (n.getBody() != null) {
  869                   n.getBody().visit(new ParamVisitor(sep));
  870               }
  871           }
  872   
  873           public void visit(Node.Expression n) throws JasperException {
  874               n.setBeginJavaLine(out.getJavaLine());
  875               out.printin("out.print(");
  876               out.printMultiLn(n.getText());
  877               out.println(");");
  878               n.setEndJavaLine(out.getJavaLine());
  879           }
  880   
  881           public void visit(Node.Scriptlet n) throws JasperException {
  882               n.setBeginJavaLine(out.getJavaLine());
  883               out.printMultiLn(n.getText());
  884               out.println();
  885               n.setEndJavaLine(out.getJavaLine());
  886           }
  887   
  888           public void visit(Node.ELExpression n) throws JasperException {
  889               n.setBeginJavaLine(out.getJavaLine());
  890               if (!pageInfo.isELIgnored() && (n.getEL() != null)) {
  891                   out.printil("out.write("
  892                           + JspUtil.interpreterCall(this.isTagFile, n.getType() + "{"
  893                                   + new String(n.getText()) + "}", String.class,
  894                                   n.getEL().getMapName(), false) + ");");
  895               } else {
  896                   out.printil("out.write("
  897                           + quote(n.getType() + "{" + new String(n.getText()) + "}") + ");");
  898               }
  899               n.setEndJavaLine(out.getJavaLine());
  900           }
  901   
  902           public void visit(Node.IncludeAction n) throws JasperException {
  903   
  904               String flush = n.getTextAttribute("flush");
  905               Node.JspAttribute page = n.getPage();
  906   
  907               boolean isFlush = false; // default to false;
  908               if ("true".equals(flush))
  909                   isFlush = true;
  910   
  911               n.setBeginJavaLine(out.getJavaLine());
  912   
  913               String pageParam;
  914               if (page.isNamedAttribute()) {
  915                   // If the page for jsp:include was specified via
  916                   // jsp:attribute, first generate code to evaluate
  917                   // that body.
  918                   pageParam = generateNamedAttributeValue(page
  919                           .getNamedAttributeNode());
  920               } else {
  921                   pageParam = attributeValue(page, false, String.class);
  922               }
  923   
  924               // If any of the params have their values specified by
  925               // jsp:attribute, prepare those values first.
  926               Node jspBody = findJspBody(n);
  927               if (jspBody != null) {
  928                   prepareParams(jspBody);
  929               } else {
  930                   prepareParams(n);
  931               }
  932   
  933               out
  934                       .printin("org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "
  935                               + pageParam);
  936               printParams(n, pageParam, page.isLiteral());
  937               out.println(", out, " + isFlush + ");");
  938   
  939               n.setEndJavaLine(out.getJavaLine());
  940           }
  941   
  942           /**
  943            * Scans through all child nodes of the given parent for <param>
  944            * subelements. For each <param> element, if its value is specified via
  945            * a Named Attribute (<jsp:attribute>), generate the code to evaluate
  946            * those bodies first.
  947            * <p>
  948            * If parent is null, simply returns.
  949            */
  950           private void prepareParams(Node parent) throws JasperException {
  951               if (parent == null)
  952                   return;
  953   
  954               Node.Nodes subelements = parent.getBody();
  955               if (subelements != null) {
  956                   for (int i = 0; i < subelements.size(); i++) {
  957                       Node n = subelements.getNode(i);
  958                       if (n instanceof Node.ParamAction) {
  959                           Node.Nodes paramSubElements = n.getBody();
  960                           for (int j = 0; (paramSubElements != null)
  961                                   && (j < paramSubElements.size()); j++) {
  962                               Node m = paramSubElements.getNode(j);
  963                               if (m instanceof Node.NamedAttribute) {
  964                                   generateNamedAttributeValue((Node.NamedAttribute) m);
  965                               }
  966                           }
  967                       }
  968                   }
  969               }
  970           }
  971   
  972           /**
  973            * Finds the <jsp:body> subelement of the given parent node. If not
  974            * found, null is returned.
  975            */
  976           private Node.JspBody findJspBody(Node parent) throws JasperException {
  977               Node.JspBody result = null;
  978   
  979               Node.Nodes subelements = parent.getBody();
  980               for (int i = 0; (subelements != null) && (i < subelements.size()); i++) {
  981                   Node n = subelements.getNode(i);
  982                   if (n instanceof Node.JspBody) {
  983                       result = (Node.JspBody) n;
  984                       break;
  985                   }
  986               }
  987   
  988               return result;
  989           }
  990   
  991           public void visit(Node.ForwardAction n) throws JasperException {
  992               Node.JspAttribute page = n.getPage();
  993   
  994               n.setBeginJavaLine(out.getJavaLine());
  995   
  996               out.printil("if (true) {"); // So that javac won't complain about
  997               out.pushIndent(); // codes after "return"
  998   
  999               String pageParam;
 1000               if (page.isNamedAttribute()) {
 1001                   // If the page for jsp:forward was specified via
 1002                   // jsp:attribute, first generate code to evaluate
 1003                   // that body.
 1004                   pageParam = generateNamedAttributeValue(page
 1005                           .getNamedAttributeNode());
 1006               } else {
 1007                   pageParam = attributeValue(page, false, String.class);
 1008               }
 1009   
 1010               // If any of the params have their values specified by
 1011               // jsp:attribute, prepare those values first.
 1012               Node jspBody = findJspBody(n);
 1013               if (jspBody != null) {
 1014                   prepareParams(jspBody);
 1015               } else {
 1016                   prepareParams(n);
 1017               }
 1018   
 1019               out.printin("_jspx_page_context.forward(");
 1020               out.print(pageParam);
 1021               printParams(n, pageParam, page.isLiteral());
 1022               out.println(");");
 1023               if (isTagFile || isFragment) {
 1024                   out.printil("throw new SkipPageException();");
 1025               } else {
 1026                   out.printil((methodNesting > 0) ? "return true;" : "return;");
 1027               }
 1028               out.popIndent();
 1029               out.printil("}");
 1030   
 1031               n.setEndJavaLine(out.getJavaLine());
 1032               // XXX Not sure if we can eliminate dead codes after this.
 1033           }
 1034   
 1035           public void visit(Node.GetProperty n) throws JasperException {
 1036               String name = n.getTextAttribute("name");
 1037               String property = n.getTextAttribute("property");
 1038   
 1039               n.setBeginJavaLine(out.getJavaLine());
 1040   
 1041               if (beanInfo.checkVariable(name)) {
 1042                   // Bean is defined using useBean, introspect at compile time
 1043                   Class bean = beanInfo.getBeanType(name);
 1044                   String beanName = JspUtil.getCanonicalName(bean);
 1045                   java.lang.reflect.Method meth = JspRuntimeLibrary
 1046                           .getReadMethod(bean, property);
 1047                   String methodName = meth.getName();
 1048                   out
 1049                           .printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString("
 1050                                   + "((("
 1051                                   + beanName
 1052                                   + ")_jspx_page_context.findAttribute("
 1053                                   + "\""
 1054                                   + name + "\"))." + methodName + "())));");
 1055               } else {
 1056                   // The object could be a custom action with an associated
 1057                   // VariableInfo entry for this name.
 1058                   // Get the class name and then introspect at runtime.
 1059                   out
 1060                           .printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString"
 1061                                   + "(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
 1062                                   + "(_jspx_page_context.getAttribute(\""
 1063                                   + name
 1064                                   + "\", PageContext.PAGE_SCOPE), \""
 1065                                   + property
 1066                                   + "\")));");
 1067               }
 1068   
 1069               n.setEndJavaLine(out.getJavaLine());
 1070           }
 1071   
 1072           public void visit(Node.SetProperty n) throws JasperException {
 1073               String name = n.getTextAttribute("name");
 1074               String property = n.getTextAttribute("property");
 1075               String param = n.getTextAttribute("param");
 1076               Node.JspAttribute value = n.getValue();
 1077   
 1078               n.setBeginJavaLine(out.getJavaLine());
 1079   
 1080               if ("*".equals(property)) {
 1081                   out
 1082                           .printil("org.apache.jasper.runtime.JspRuntimeLibrary.introspect("
 1083                                   + "_jspx_page_context.findAttribute("
 1084                                   + "\""
 1085                                   + name + "\"), request);");
 1086               } else if (value == null) {
 1087                   if (param == null)
 1088                       param = property; // default to same as property
 1089                   out
 1090                           .printil("org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
 1091                                   + "_jspx_page_context.findAttribute(\""
 1092                                   + name
 1093                                   + "\"), \""
 1094                                   + property
 1095                                   + "\", request.getParameter(\""
 1096                                   + param
 1097                                   + "\"), "
 1098                                   + "request, \""
 1099                                   + param
 1100                                   + "\", false);");
 1101               } else if (value.isExpression()) {
 1102                   out
 1103                           .printil("org.apache.jasper.runtime.JspRuntimeLibrary.handleSetProperty("
 1104                                   + "_jspx_page_context.findAttribute(\""
 1105                                   + name
 1106                                   + "\"), \"" + property + "\",");
 1107                   out.print(attributeValue(value, false, null));
 1108                   out.println(");");
 1109               } else if (value.isELInterpreterInput()) {
 1110                   // We've got to resolve the very call to the interpreter
 1111                   // at runtime since we don't know what type to expect
 1112                   // in the general case; we thus can't hard-wire the call
 1113                   // into the generated code. (XXX We could, however,
 1114                   // optimize the case where the bean is exposed with
 1115                   // <jsp:useBean>, much as the code here does for
 1116                   // getProperty.)
 1117   
 1118                   // The following holds true for the arguments passed to
 1119                   // JspRuntimeLibrary.handleSetPropertyExpression():
 1120                   // - 'pageContext' is a VariableResolver.
 1121                   // - 'this' (either the generated Servlet or the generated tag
 1122                   // handler for Tag files) is a FunctionMapper.
 1123                   out
 1124                           .printil("org.apache.jasper.runtime.JspRuntimeLibrary.handleSetPropertyExpression("
 1125                                   + "_jspx_page_context.findAttribute(\""
 1126                                   + name
 1127                                   + "\"), \""
 1128                                   + property
 1129                                   + "\", "
 1130                                   + quote(value.getValue())
 1131                                   + ", "
 1132                                   + "_jspx_page_context, "
 1133                                   + value.getEL().getMapName() + ");");
 1134               } else if (value.isNamedAttribute()) {
 1135                   // If the value for setProperty was specified via
 1136                   // jsp:attribute, first generate code to evaluate
 1137                   // that body.
 1138                   String valueVarName = generateNamedAttributeValue(value
 1139                           .getNamedAttributeNode());
 1140                   out
 1141                           .printil("org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
 1142                                   + "_jspx_page_context.findAttribute(\""
 1143                                   + name
 1144                                   + "\"), \""
 1145                                   + property
 1146                                   + "\", "
 1147                                   + valueVarName
 1148                                   + ", null, null, false);");
 1149               } else {
 1150                   out
 1151                           .printin("org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
 1152                                   + "_jspx_page_context.findAttribute(\""
 1153                                   + name
 1154                                   + "\"), \"" + property + "\", ");
 1155                   out.print(attributeValue(value, false, null));
 1156                   out.println(", null, null, false);");
 1157               }
 1158   
 1159               n.setEndJavaLine(out.getJavaLine());
 1160           }
 1161   
 1162           public void visit(Node.UseBean n) throws JasperException {
 1163   
 1164               String name = n.getTextAttribute("id");
 1165               String scope = n.getTextAttribute("scope");
 1166               String klass = n.getTextAttribute("class");
 1167               String type = n.getTextAttribute("type");
 1168               Node.JspAttribute beanName = n.getBeanName();
 1169   
 1170               // If "class" is specified, try an instantiation at compile time
 1171               boolean generateNew = false;
 1172               String canonicalName = null; // Canonical name for klass
 1173               if (klass != null) {
 1174                   try {
 1175                       Class bean = ctxt.getClassLoader().loadClass(klass);
 1176                       if (klass.indexOf('$') >= 0) {
 1177                           // Obtain the canonical type name
 1178                           canonicalName = JspUtil.getCanonicalName(bean);
 1179                       } else {
 1180                           canonicalName = klass;
 1181                       }
 1182                       int modifiers = bean.getModifiers();
 1183                       if (!Modifier.isPublic(modifiers)
 1184                               || Modifier.isInterface(modifiers)
 1185                               || Modifier.isAbstract(modifiers)) {
 1186                           throw new Exception("Invalid bean class modifier");
 1187                       }
 1188                       // Check that there is a 0 arg constructor
 1189                       bean.getConstructor(new Class[] {});
 1190                       // At compile time, we have determined that the bean class
 1191                       // exists, with a public zero constructor, new() can be
 1192                       // used for bean instantiation.
 1193                       generateNew = true;
 1194                   } catch (Exception e) {
 1195                       // Cannot instantiate the specified class, either a
 1196                       // compilation error or a runtime error will be raised,
 1197                       // depending on a compiler flag.
 1198                       if (ctxt.getOptions()
 1199                               .getErrorOnUseBeanInvalidClassAttribute()) {
 1200                           err.jspError(n, "jsp.error.invalid.bean", klass);
 1201                       }
 1202                       if (canonicalName == null) {
 1203                           // Doing our best here to get a canonical name
 1204                           // from the binary name, should work 99.99% of time.
 1205                           canonicalName = klass.replace('$', '.');
 1206                       }
 1207                   }
 1208                   if (type == null) {
 1209                       // if type is unspecified, use "class" as type of bean
 1210                       type = canonicalName;
 1211                   }
 1212               }
 1213   
 1214               String scopename = "PageContext.PAGE_SCOPE"; // Default to page
 1215               String lock = "_jspx_page_context";
 1216   
 1217               if ("request".equals(scope)) {
 1218                   scopename = "PageContext.REQUEST_SCOPE";
 1219                   lock = "request";
 1220               } else if ("session".equals(scope)) {
 1221                   scopename = "PageContext.SESSION_SCOPE";
 1222                   lock = "session";
 1223               } else if ("application".equals(scope)) {
 1224                   scopename = "PageContext.APPLICATION_SCOPE";
 1225                   lock = "application";
 1226               }
 1227   
 1228               n.setBeginJavaLine(out.getJavaLine());
 1229   
 1230               // Declare bean
 1231               out.printin(type);
 1232               out.print(' ');
 1233               out.print(name);
 1234               out.println(" = null;");
 1235   
 1236               // Lock while getting or creating bean
 1237               out.printin("synchronized (");
 1238               out.print(lock);
 1239               out.println(") {");
 1240               out.pushIndent();
 1241   
 1242               // Locate bean from context
 1243               out.printin(name);
 1244               out.print(" = (");
 1245               out.print(type);
 1246               out.print(") _jspx_page_context.getAttribute(");
 1247               out.print(quote(name));
 1248               out.print(", ");
 1249               out.print(scopename);
 1250               out.println(");");
 1251   
 1252               // Create bean
 1253               /*
 1254                * Check if bean is alredy there
 1255                */
 1256               out.printin("if (");
 1257               out.print(name);
 1258               out.println(" == null){");
 1259               out.pushIndent();
 1260               if (klass == null && beanName == null) {
 1261                   /*
 1262                    * If both class name and beanName is not specified, the bean
 1263                    * must be found locally, otherwise it's an error
 1264                    */
 1265                   out
 1266                           .printin("throw new java.lang.InstantiationException(\"bean ");
 1267                   out.print(name);
 1268                   out.println(" not found within scope\");");
 1269               } else {
 1270                   /*
 1271                    * Instantiate the bean if it is not in the specified scope.
 1272                    */
 1273                   if (!generateNew) {
 1274                       String binaryName;
 1275                       if (beanName != null) {
 1276                           if (beanName.isNamedAttribute()) {
 1277                               // If the value for beanName was specified via
 1278                               // jsp:attribute, first generate code to evaluate
 1279                               // that body.
 1280                               binaryName = generateNamedAttributeValue(beanName
 1281                                       .getNamedAttributeNode());
 1282                           } else {
 1283                               binaryName = attributeValue(beanName, false,
 1284                                       String.class);
 1285                           }
 1286                       } else {
 1287                           // Implies klass is not null
 1288                           binaryName = quote(klass);
 1289                       }
 1290                       out.printil("try {");
 1291                       out.pushIndent();
 1292                       out.printin(name);
 1293                       out.print(" = (");
 1294                       out.print(type);
 1295                       out.print(") java.beans.Beans.instantiate(");
 1296                       out.print("this.getClass().getClassLoader(), ");
 1297                       out.print(binaryName);
 1298                       out.println(");");
 1299                       out.popIndent();
 1300                       /*
 1301                        * Note: Beans.instantiate throws ClassNotFoundException if
 1302                        * the bean class is abstract.
 1303                        */
 1304                       out.printil("} catch (ClassNotFoundException exc) {");
 1305                       out.pushIndent();
 1306                       out
 1307                               .printil("throw new InstantiationException(exc.getMessage());");
 1308                       out.popIndent();
 1309                       out.printil("} catch (Exception exc) {");
 1310                       out.pushIndent();
 1311                       out.printin("throw new ServletException(");
 1312                       out.print("\"Cannot create bean of class \" + ");
 1313                       out.print(binaryName);
 1314                       out.println(", exc);");
 1315                       out.popIndent();
 1316                       out.printil("}"); // close of try
 1317                   } else {
 1318                       // Implies klass is not null
 1319                       // Generate codes to instantiate the bean class
 1320                       out.printin(name);
 1321                       out.print(" = new ");
 1322                       out.print(canonicalName);
 1323                       out.println("();");
 1324                   }
 1325                   /*
 1326                    * Set attribute for bean in the specified scope
 1327                    */
 1328                   out.printin("_jspx_page_context.setAttribute(");
 1329                   out.print(quote(name));
 1330                   out.print(", ");
 1331                   out.print(name);
 1332                   out.print(", ");
 1333                   out.print(scopename);
 1334                   out.println(");");
 1335   
 1336                   // Only visit the body when bean is instantiated
 1337                   visitBody(n);
 1338               }
 1339               out.popIndent();
 1340               out.printil("}");
 1341   
 1342               // End of lock block
 1343               out.popIndent();
 1344               out.printil("}");
 1345   
 1346               n.setEndJavaLine(out.getJavaLine());
 1347           }
 1348   
 1349           /**
 1350            * @return a string for the form 'attr = "value"'
 1351            */
 1352           private String makeAttr(String attr, String value) {
 1353               if (value == null)
 1354                   return "";
 1355   
 1356               return " " + attr + "=\"" + value + '\"';
 1357           }
 1358   
 1359           public void visit(Node.PlugIn n) throws JasperException {
 1360   
 1361               /**
 1362                * A visitor to handle <jsp:param> in a plugin
 1363                */
 1364               class ParamVisitor extends Node.Visitor {
 1365   
 1366                   private boolean ie;
 1367   
 1368                   ParamVisitor(boolean ie) {
 1369                       this.ie = ie;
 1370                   }
 1371   
 1372                   public void visit(Node.ParamAction n) throws JasperException {
 1373   
 1374                       String name = n.getTextAttribute("name");
 1375                       if (name.equalsIgnoreCase("object"))
 1376                           name = "java_object";
 1377                       else if (name.equalsIgnoreCase("type"))
 1378                           name = "java_type";
 1379   
 1380                       n.setBeginJavaLine(out.getJavaLine());
 1381                       // XXX - Fixed a bug here - value used to be output
 1382                       // inline, which is only okay if value is not an EL
 1383                       // expression. Also, key/value pairs for the
 1384                       // embed tag were not being generated correctly.
 1385                       // Double check that this is now the correct behavior.
 1386                       if (ie) {
 1387                           // We want something of the form
 1388                           // out.println( "<param name=\"blah\"
 1389                           // value=\"" + ... + "\">" );
 1390                           out.printil("out.write( \"<param name=\\\""
 1391                                   + escape(name)
 1392                                   + "\\\" value=\\\"\" + "
 1393                                   + attributeValue(n.getValue(), false,
 1394                                           String.class) + " + \"\\\">\" );");
 1395                           out.printil("out.write(\"\\n\");");
 1396                       } else {
 1397                           // We want something of the form
 1398                           // out.print( " blah=\"" + ... + "\"" );
 1399                           out.printil("out.write( \" "
 1400                                   + escape(name)
 1401                                   + "=\\\"\" + "
 1402                                   + attributeValue(n.getValue(), false,
 1403                                           String.class) + " + \"\\\"\" );");
 1404                       }
 1405   
 1406                       n.setEndJavaLine(out.getJavaLine());
 1407                   }
 1408               }
 1409   
 1410               String type = n.getTextAttribute("type");
 1411               String code = n.getTextAttribute("code");
 1412               String name = n.getTextAttribute("name");
 1413               Node.JspAttribute height = n.getHeight();
 1414               Node.JspAttribute width = n.getWidth();
 1415               String hspace = n.getTextAttribute("hspace");
 1416               String vspace = n.getTextAttribute("vspace");
 1417               String align = n.getTextAttribute("align");
 1418               String iepluginurl = n.getTextAttribute("iepluginurl");
 1419               String nspluginurl = n.getTextAttribute("nspluginurl");
 1420               String codebase = n.getTextAttribute("codebase");
 1421               String archive = n.getTextAttribute("archive");
 1422               String jreversion = n.getTextAttribute("jreversion");
 1423   
 1424               String widthStr = null;
 1425               if (width != null) {
 1426                   if (width.isNamedAttribute()) {
 1427                       widthStr = generateNamedAttributeValue(width
 1428                               .getNamedAttributeNode());
 1429                   } else {
 1430                       widthStr = attributeValue(width, false, String.class);
 1431                   }
 1432               }
 1433   
 1434               String heightStr = null;
 1435               if (height != null) {
 1436                   if (height.isNamedAttribute()) {
 1437                       heightStr = generateNamedAttributeValue(height
 1438                               .getNamedAttributeNode());
 1439                   } else {
 1440                       heightStr = attributeValue(height, false, String.class);
 1441                   }
 1442               }
 1443   
 1444               if (iepluginurl == null)
 1445                   iepluginurl = Constants.IE_PLUGIN_URL;
 1446               if (nspluginurl == null)
 1447                   nspluginurl = Constants.NS_PLUGIN_URL;
 1448   
 1449               n.setBeginJavaLine(out.getJavaLine());
 1450   
 1451               // If any of the params have their values specified by
 1452               // jsp:attribute, prepare those values first.
 1453               // Look for a params node and prepare its param subelements:
 1454               Node.JspBody jspBody = findJspBody(n);
 1455               if (jspBody != null) {
 1456                   Node.Nodes subelements = jspBody.getBody();
 1457                   if (subelements != null) {
 1458                       for (int i = 0; i < subelements.size(); i++) {
 1459                           Node m = subelements.getNode(i);
 1460                           if (m instanceof Node.ParamsAction) {
 1461                               prepareParams(m);
 1462                               break;
 1463                           }
 1464                       }
 1465                   }
 1466               }
 1467   
 1468               // XXX - Fixed a bug here - width and height can be set
 1469               // dynamically. Double-check if this generation is correct.
 1470   
 1471               // IE style plugin
 1472               // <object ...>
 1473               // First compose the runtime output string
 1474               String s0 = "<object"
 1475                       + makeAttr("classid", ctxt.getOptions().getIeClassId())
 1476                       + makeAttr("name", name);
 1477   
 1478               String s1 = "";
 1479               if (width != null) {
 1480                   s1 = " + \" width=\\\"\" + " + widthStr + " + \"\\\"\"";
 1481               }
 1482   
 1483               String s2 = "";
 1484               if (height != null) {
 1485                   s2 = " + \" height=\\\"\" + " + heightStr + " + \"\\\"\"";
 1486               }
 1487   
 1488               String s3 = makeAttr("hspace", hspace) + makeAttr("vspace", vspace)
 1489                       + makeAttr("align", align)
 1490                       + makeAttr("codebase", iepluginurl) + '>';
 1491   
 1492               // Then print the output string to the java file
 1493               out.printil("out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3)
 1494                       + ");");
 1495               out.printil("out.write(\"\\n\");");
 1496   
 1497               // <param > for java_code
 1498               s0 = "<param name=\"java_code\"" + makeAttr("value", code) + '>';
 1499               out.printil("out.write(" + quote(s0) + ");");
 1500               out.printil("out.write(\"\\n\");");
 1501   
 1502               // <param > for java_codebase
 1503               if (codebase != null) {
 1504                   s0 = "<param name=\"java_codebase\""
 1505                           + makeAttr("value", codebase) + '>';
 1506                   out.printil("out.write(" + quote(s0) + ");");
 1507                   out.printil("out.write(\"\\n\");");
 1508               }
 1509   
 1510               // <param > for java_archive
 1511               if (archive != null) {
 1512                   s0 = "<param name=\"java_archive\""
 1513                           + makeAttr("value", archive) + '>';
 1514                   out.printil("out.write(" + quote(s0) + ");");
 1515                   out.printil("out.write(\"\\n\");");
 1516               }
 1517   
 1518               // <param > for type
 1519               s0 = "<param name=\"type\""
 1520                       + makeAttr("value", "application/x-java-"
 1521                               + type
 1522                               + ";"
 1523                               + ((jreversion == null) ? "" : "version="
 1524                                       + jreversion)) + '>';
 1525               out.printil("out.write(" + quote(s0) + ");");
 1526               out.printil("out.write(\"\\n\");");
 1527   
 1528               /*
 1529                * generate a <param> for each <jsp:param> in the plugin body
 1530                */
 1531               if (n.getBody() != null)
 1532                   n.getBody().visit(new ParamVisitor(true));
 1533   
 1534               /*
 1535                * Netscape style plugin part
 1536                */
 1537               out.printil("out.write(" + quote("<comment>") + ");");
 1538               out.printil("out.write(\"\\n\");");
 1539               s0 = "<EMBED"
 1540                       + makeAttr("type", "application/x-java-"
 1541                               + type
 1542                               + ";"
 1543                               + ((jreversion == null) ? "" : "version="
 1544                                       + jreversion)) + makeAttr("name", name);
 1545   
 1546               // s1 and s2 are the same as before.
 1547   
 1548               s3 = makeAttr("hspace", hspace) + makeAttr("vspace", vspace)
 1549                       + makeAttr("align", align)
 1550                       + makeAttr("pluginspage", nspluginurl)
 1551                       + makeAttr("java_code", code)
 1552                       + makeAttr("java_codebase", codebase)
 1553                       + makeAttr("java_archive", archive);
 1554               out.printil("out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3)
 1555                       + ");");
 1556   
 1557               /*
 1558                * Generate a 'attr = "value"' for each <jsp:param> in plugin body
 1559                */
 1560               if (n.getBody() != null)
 1561                   n.getBody().visit(new ParamVisitor(false));
 1562   
 1563               out.printil("out.write(" + quote("/>") + ");");
 1564               out.printil("out.write(\"\\n\");");
 1565   
 1566               out.printil("out.write(" + quote("<noembed>") + ");");
 1567               out.printil("out.write(\"\\n\");");
 1568   
 1569               /*
 1570                * Fallback
 1571                */
 1572               if (n.getBody() != null) {
 1573                   visitBody(n);
 1574                   out.printil("out.write(\"\\n\");");
 1575               }
 1576   
 1577               out.printil("out.write(" + quote("</noembed>") + ");");
 1578               out.printil("out.write(\"\\n\");");
 1579   
 1580               out.printil("out.write(" + quote("</comment>") + ");");
 1581               out.printil("out.write(\"\\n\");");
 1582   
 1583               out.printil("out.write(" + quote("</object>") + ");");
 1584               out.printil("out.write(\"\\n\");");
 1585   
 1586               n.setEndJavaLine(out.getJavaLine());
 1587           }
 1588   
 1589           public void visit(Node.NamedAttribute n) throws JasperException {
 1590               // Don't visit body of this tag - we already did earlier.
 1591           }
 1592   
 1593           public void visit(Node.CustomTag n) throws JasperException {
 1594   
 1595               // Use plugin to generate more efficient code if there is one.
 1596               if (n.useTagPlugin()) {
 1597                   generateTagPlugin(n);
 1598                   return;
 1599               }
 1600   
 1601               TagHandlerInfo handlerInfo = getTagHandlerInfo(n);
 1602   
 1603               // Create variable names
 1604               String baseVar = createTagVarName(n.getQName(), n.getPrefix(), n
 1605                       .getLocalName());
 1606               String tagEvalVar = "_jspx_eval_" + baseVar;
 1607               String tagHandlerVar = "_jspx_th_" + baseVar;
 1608               String tagPushBodyCountVar = "_jspx_push_body_count_" + baseVar;
 1609   
 1610               // If the tag contains no scripting element, generate its codes
 1611               // to a method.
 1612               ServletWriter outSave = null;
 1613               Node.ChildInfo ci = n.getChildInfo();
 1614               if (ci.isScriptless() && !ci.hasScriptingVars()) {
 1615                   // The tag handler and its body code can reside in a separate
 1616                   // method if it is scriptless and does not have any scripting
 1617                   // variable defined.
 1618   
 1619                   String tagMethod = "_jspx_meth_" + baseVar;
 1620   
 1621                   // Generate a call to this method
 1622                   out.printin("if (");
 1623                   out.print(tagMethod);
 1624                   out.print("(");
 1625                   if (parent != null) {
 1626                       out.print(parent);
 1627                       out.print(", ");
 1628                   }
 1629                   out.print("_jspx_page_context");
 1630                   if (pushBodyCountVar != null) {
 1631                       out.print(", ");
 1632                       out.print(pushBodyCountVar);
 1633                   }
 1634                   out.println("))");
 1635                   out.pushIndent();
 1636                   out.printil((methodNesting > 0) ? "return true;" : "return;");
 1637                   out.popIndent();
 1638   
 1639                   // Set up new buffer for the method
 1640                   outSave = out;
 1641                   /*
 1642                    * For fragments, their bodies will be generated in fragment
 1643                    * helper classes, and the Java line adjustments will be done
 1644                    * there, hence they are set to null here to avoid double
 1645                    * adjustments.
 1646                    */
 1647                   GenBuffer genBuffer = new GenBuffer(n,
 1648                           n.implementsSimpleTag() ? null : n.getBody());
 1649                   methodsBuffered.add(genBuffer);
 1650                   out = genBuffer.getOut();
 1651   
 1652                   methodNesting++;
 1653                   // Generate code for method declaration
 1654                   out.println();
 1655                   out.pushIndent();
 1656                   out.printin("private boolean ");
 1657                   out.print(tagMethod);
 1658                   out.print("(");
 1659                   if (parent != null) {
 1660                       out.print("javax.servlet.jsp.tagext.JspTag ");
 1661                       out.print(parent);
 1662                       out.print(", ");
 1663                   }
 1664                   out.print("PageContext _jspx_page_context");
 1665                   if (pushBodyCountVar != null) {
 1666                       out.print(", int[] ");
 1667                       out.print(pushBodyCountVar);
 1668                   }
 1669                   out.println(")");
 1670                   out.printil("        throws Throwable {");
 1671                   out.pushIndent();
 1672   
 1673                   // Initilaize local variables used in this method.
 1674                   if (!isTagFile) {
 1675                       out
 1676                               .printil("PageContext pageContext = _jspx_page_context;");
 1677                   }
 1678                   out.printil("JspWriter out = _jspx_page_context.getOut();");
 1679                   generateLocalVariables(out, n);
 1680               }
 1681   
 1682               if (n.implementsSimpleTag()) {
 1683                   generateCustomDoTag(n, handlerInfo, tagHandlerVar);
 1684               } else {
 1685                   /*
 1686                    * Classic tag handler: Generate code for start element, body,
 1687                    * and end element
 1688                    */
 1689                   generateCustomStart(n, handlerInfo, tagHandlerVar, tagEvalVar,
 1690                           tagPushBodyCountVar);
 1691   
 1692                   // visit body
 1693                   String tmpParent = parent;
 1694                   parent = tagHandlerVar;
 1695                   boolean isSimpleTagParentSave = isSimpleTagParent;
 1696                   isSimpleTagParent = false;
 1697                   String tmpPushBodyCountVar = null;
 1698                   if (n.implementsTryCatchFinally()) {
 1699                       tmpPushBodyCountVar = pushBodyCountVar;
 1700                       pushBodyCountVar = tagPushBodyCountVar;
 1701                   }
 1702                   boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
 1703                   isSimpleTagHandler = false;
 1704   
 1705                   visitBody(n);
 1706   
 1707                   parent = tmpParent;
 1708                   isSimpleTagParent = isSimpleTagParentSave;
 1709                   if (n.implementsTryCatchFinally()) {
 1710                       pushBodyCountVar = tmpPushBodyCountVar;
 1711                   }
 1712                   isSimpleTagHandler = tmpIsSimpleTagHandler;
 1713   
 1714                   generateCustomEnd(n, tagHandlerVar, tagEvalVar,
 1715                           tagPushBodyCountVar);
 1716               }
 1717   
 1718               if (ci.isScriptless() && !ci.hasScriptingVars()) {
 1719                   // Generate end of method
 1720                   if (methodNesting > 0) {
 1721                       out.printil("return false;");
 1722                   }
 1723                   out.popIndent();
 1724                   out.printil("}");
 1725                   out.popIndent();
 1726   
 1727                   methodNesting--;
 1728   
 1729                   // restore previous writer
 1730                   out = outSave;
 1731               }
 1732           }
 1733   
 1734           private static final String SINGLE_QUOTE = "'";
 1735   
 1736           private static final String DOUBLE_QUOTE = "\\\"";
 1737   
 1738           public void visit(Node.UninterpretedTag n) throws JasperException {
 1739   
 1740               n.setBeginJavaLine(out.getJavaLine());
 1741   
 1742               /*
 1743                * Write begin tag
 1744                */
 1745               out.printin("out.write(\"<");
 1746               out.print(n.getQName());
 1747   
 1748               Attributes attrs = n.getNonTaglibXmlnsAttributes();
 1749               int attrsLen = (attrs == null) ? 0 : attrs.getLength();
 1750               for (int i = 0; i < attrsLen; i++) {
 1751                   out.print(" ");
 1752                   out.print(attrs.getQName(i));
 1753                   out.print("=");
 1754                   String quote = DOUBLE_QUOTE;
 1755                   String value = attrs.getValue(i);
 1756                   if (value.indexOf('"') != -1) {
 1757                       quote = SINGLE_QUOTE;
 1758                   }
 1759                   out.print(quote);
 1760                   out.print(value);
 1761                   out.print(quote);
 1762               }
 1763   
 1764               attrs = n.getAttributes();
 1765               attrsLen = (attrs == null) ? 0 : attrs.getLength();
 1766               Node.JspAttribute[] jspAttrs = n.getJspAttributes();
 1767               for (int i = 0; i < attrsLen; i++) {
 1768                   out.print(" ");
 1769                   out.print(attrs.getQName(i));
 1770                   out.print("=");
 1771                   if (jspAttrs[i].isELInterpreterInput()) {
 1772                       out.print("\\\"\" + ");
 1773                       out.print(attributeValue(jspAttrs[i], false, String.class));
 1774                       out.print(" + \"\\\"");
 1775                   } else {
 1776                       String quote = DOUBLE_QUOTE;
 1777                       String value = attrs.getValue(i);
 1778                       if (value.indexOf('"') != -1) {
 1779                           quote = SINGLE_QUOTE;
 1780                       }
 1781                       out.print(quote);
 1782                       out.print(value);
 1783                       out.print(quote);
 1784                   }
 1785               }
 1786   
 1787               if (n.getBody() != null) {
 1788                   out.println(">\");");
 1789   
 1790                   // Visit tag body
 1791                   visitBody(n);
 1792   
 1793                   /*
 1794                    * Write end tag
 1795                    */
 1796                   out.printin("out.write(\"</");
 1797                   out.print(n.getQName());
 1798                   out.println(">\");");
 1799               } else {
 1800                   out.println("/>\");");
 1801               }
 1802   
 1803               n.setEndJavaLine(out.getJavaLine());
 1804           }
 1805   
 1806           public void visit(Node.JspElement n) throws JasperException {
 1807   
 1808               n.setBeginJavaLine(out.getJavaLine());
 1809   
 1810               // Compute attribute value string for XML-style and named
 1811               // attributes
 1812               Hashtable map = new Hashtable();
 1813               Node.JspAttribute[] attrs = n.getJspAttributes();
 1814               for (int i = 0; attrs != null && i < attrs.length; i++) {
 1815                   String attrStr = null;
 1816                   if (attrs[i].isNamedAttribute()) {
 1817                       attrStr = generateNamedAttributeValue(attrs[i]
 1818                               .getNamedAttributeNode());
 1819                   } else {
 1820                       attrStr = attributeValue(attrs[i], false, Object.class);
 1821                   }
 1822                   String s = " + \" " + attrs[i].getName() + "=\\\"\" + "
 1823                           + attrStr + " + \"\\\"\"";
 1824                   map.put(attrs[i].getName(), s);
 1825               }
 1826   
 1827               // Write begin tag, using XML-style 'name' attribute as the
 1828               // element name
 1829               String elemName = attributeValue(n.getNameAttribute(), false,
 1830                       String.class);
 1831               out.printin("out.write(\"<\"");
 1832               out.print(" + " + elemName);
 1833   
 1834               // Write remaining attributes
 1835               Enumeration enumeration = map.keys();
 1836               while (enumeration.hasMoreElements()) {
 1837                   String attrName = (String) enumeration.nextElement();
 1838                   out.print((String) map.get(attrName));
 1839               }
 1840   
 1841               // Does the <jsp:element> have nested tags other than
 1842               // <jsp:attribute>
 1843               boolean hasBody = false;
 1844               Node.Nodes subelements = n.getBody();
 1845               if (subelements != null) {
 1846                   for (int i = 0; i < subelements.size(); i++) {
 1847                       Node subelem = subelements.getNode(i);
 1848                       if (!(subelem instanceof Node.NamedAttribute)) {
 1849                           hasBody = true;
 1850                           break;
 1851                       }
 1852                   }
 1853               }
 1854               if (hasBody) {
 1855                   out.println(" + \">\");");
 1856   
 1857                   // Smap should not include the body
 1858                   n.setEndJavaLine(out.getJavaLine());
 1859   
 1860                   // Visit tag body
 1861                   visitBody(n);
 1862   
 1863                   // Write end tag
 1864                   out.printin("out.write(\"</\"");
 1865                   out.print(" + " + elemName);
 1866                   out.println(" + \">\");");
 1867               } else {
 1868                   out.println(" + \"/>\");");
 1869                   n.setEndJavaLine(out.getJavaLine());
 1870               }
 1871           }
 1872   
 1873           public void visit(Node.TemplateText n) throws JasperException {
 1874   
 1875               String text = n.getText();
 1876   
 1877               int textSize = text.length();
 1878               if (textSize == 0) {
 1879                   return;
 1880               }
 1881   
 1882               if (textSize <= 3) {
 1883                   // Special case small text strings
 1884                   n.setBeginJavaLine(out.getJavaLine());
 1885                   int lineInc = 0;
 1886                   for (int i = 0; i < textSize; i++) {
 1887                       char ch = text.charAt(i);
 1888                       out.printil("out.write(" + quote(ch) + ");");
 1889                       if (i > 0) {
 1890                           n.addSmap(lineInc);
 1891                       }
 1892                       if (ch == '\n') {
 1893                           lineInc++;
 1894                       }
 1895                   }
 1896                   n.setEndJavaLine(out.getJavaLine());
 1897                   return;
 1898               }
 1899   
 1900               if (ctxt.getOptions().genStringAsCharArray()) {
 1901                   // Generate Strings as char arrays, for performance
 1902                   ServletWriter caOut;
 1903                   if (charArrayBuffer == null) {
 1904                       charArrayBuffer = new GenBuffer();
 1905                       caOut = charArrayBuffer.getOut();
 1906                       caOut.pushIndent();
 1907                       textMap = new HashMap();
 1908                   } else {
 1909                       caOut = charArrayBuffer.getOut();
 1910                   }
 1911                   String charArrayName = (String) textMap.get(text);
 1912                   if (charArrayName == null) {
 1913                       charArrayName = "_jspx_char_array_" + charArrayCount++;
 1914                       textMap.put(text, charArrayName);
 1915                       caOut.printin("static char[] ");
 1916                       caOut.print(charArrayName);
 1917                       caOut.print(" = ");
 1918                       caOut.print(quote(text));
 1919                       caOut.println(".toCharArray();");
 1920                   }
 1921   
 1922                   n.setBeginJavaLine(out.getJavaLine());
 1923                   out.printil("out.write(" + charArrayName + ");");
 1924                   n.setEndJavaLine(out.getJavaLine());
 1925                   return;
 1926               }
 1927   
 1928               n.setBeginJavaLine(out.getJavaLine());
 1929   
 1930               out.printin();
 1931               StringBuffer sb = new StringBuffer("out.write(\"");
 1932               int initLength = sb.length();
 1933               int count = JspUtil.CHUNKSIZE;
 1934               int srcLine = 0; // relative to starting srouce line
 1935               for (int i = 0; i < text.length(); i++) {
 1936                   char ch = text.charAt(i);
 1937                   --count;
 1938                   switch (ch) {
 1939                   case '"':
 1940                       sb.append('\\').append('\"');
 1941                       break;
 1942                   case '\\':
 1943                       sb.append('\\').append('\\');
 1944                       break;
 1945                   case '\r':
 1946                       sb.append('\\').append('r');
 1947                       break;
 1948                   case '\n':
 1949                       sb.append('\\').append('n');
 1950                       srcLine++;
 1951   
 1952                       if (breakAtLF || count < 0) {
 1953                           // Generate an out.write() when see a '\n' in template
 1954                           sb.append("\");");
 1955                           out.println(sb.toString());
 1956                           if (i < text.length() - 1) {
 1957                               out.printin();
 1958                           }
 1959                           sb.setLength(initLength);
 1960                           count = JspUtil.CHUNKSIZE;
 1961                       }
 1962                       // add a Smap for this line
 1963                       n.addSmap(srcLine);
 1964                       break;
 1965                   case '\t': // Not sure we need this
 1966                       sb.append('\\').append('t');
 1967                       break;
 1968                   default:
 1969                       sb.append(ch);
 1970                   }
 1971               }
 1972   
 1973               if (sb.length() > initLength) {
 1974                   sb.append("\");");
 1975                   out.println(sb.toString());
 1976               }
 1977   
 1978               n.setEndJavaLine(out.getJavaLine());
 1979           }
 1980   
 1981           public void visit(Node.JspBody n) throws JasperException {
 1982               if (n.getBody() != null) {
 1983                   if (isSimpleTagHandler) {
 1984                       out.printin(simpleTagHandlerVar);
 1985                       out.print(".setJspBody(");
 1986                       generateJspFragment(n, simpleTagHandlerVar);
 1987                       out.println(");");
 1988                   } else {
 1989                       visitBody(n);
 1990                   }
 1991               }
 1992           }
 1993   
 1994           public void visit(Node.InvokeAction n) throws JasperException {
 1995   
 1996               n.setBeginJavaLine(out.getJavaLine());
 1997   
 1998               // Copy virtual page scope of tag file to page scope of invoking
 1999               // page
 2000               out.printil("((org.apache.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
 2001               String varReaderAttr = n.getTextAttribute("varReader");
 2002               String varAttr = n.getTextAttribute("var");
 2003               if (varReaderAttr != null || varAttr != null) {
 2004                   out.printil("_jspx_sout = new java.io.StringWriter();");
 2005               } else {
 2006                   out.printil("_jspx_sout = null;");
 2007               }
 2008   
 2009               // Invoke fragment, unless fragment is null
 2010               out.printin("if (");
 2011               out.print(toGetterMethod(n.getTextAttribute("fragment")));
 2012               out.println(" != null) {");
 2013               out.pushIndent();
 2014               out.printin(toGetterMethod(n.getTextAttribute("fragment")));
 2015               out.println(".invoke(_jspx_sout);");
 2016               out.popIndent();
 2017               out.printil("}");
 2018   
 2019               // Store varReader in appropriate scope
 2020               if (varReaderAttr != null || varAttr != null) {
 2021                   String scopeName = n.getTextAttribute("scope");
 2022                   out.printin("_jspx_page_context.setAttribute(");
 2023                   if (varReaderAttr != null) {
 2024                       out.print(quote(varReaderAttr));
 2025                       out.print(", new java.io.StringReader(_jspx_sout.toString())");
 2026                   } else {
 2027                       out.print(quote(varAttr));
 2028                       out.print(", _jspx_sout.toString()");
 2029                   }
 2030                   if (scopeName != null) {
 2031                       out.print(", ");
 2032                       out.print(getScopeConstant(scopeName));
 2033                   }
 2034                   out.println(");");
 2035               }
 2036   
 2037               // Restore EL context
 2038               out.printil("jspContext.getELContext().putContext(JspContext.class,getJspContext());");
 2039   
 2040               n.setEndJavaLine(out.getJavaLine());
 2041           }
 2042   
 2043           public void visit(Node.DoBodyAction n) throws JasperException {
 2044   
 2045               n.setBeginJavaLine(out.getJavaLine());
 2046   
 2047               // Copy virtual page scope of tag file to page scope of invoking
 2048               // page
 2049               out.printil("((org.apache.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
 2050   
 2051               // Invoke body
 2052               String varReaderAttr = n.getTextAttribute("varReader");
 2053               String varAttr = n.getTextAttribute("var");
 2054               if (varReaderAttr != null || varAttr != null) {
 2055                   out.printil("_jspx_sout = new java.io.StringWriter();");
 2056               } else {
 2057                   out.printil("_jspx_sout = null;");
 2058               }
 2059               out.printil("if (getJspBody() != null)");
 2060               out.pushIndent();
 2061               out.printil("getJspBody().invoke(_jspx_sout);");
 2062               out.popIndent();
 2063   
 2064               // Store varReader in appropriate scope
 2065               if (varReaderAttr != null || varAttr != null) {
 2066                   String scopeName = n.getTextAttribute("scope");
 2067                   out.printin("_jspx_page_context.setAttribute(");
 2068                   if (varReaderAttr != null) {
 2069                       out.print(quote(varReaderAttr));
 2070                       out
 2071                               .print(", new java.io.StringReader(_jspx_sout.toString())");
 2072                   } else {
 2073                       out.print(quote(varAttr));
 2074                       out.print(", _jspx_sout.toString()");
 2075                   }
 2076                   if (scopeName != null) {
 2077                       out.print(", ");
 2078                       out.print(getScopeConstant(scopeName));
 2079                   }
 2080                   out.println(");");
 2081               }
 2082   
 2083               // Restore EL context
 2084               out.printil("jspContext.getELContext().putContext(JspContext.class,getJspContext());");
 2085   
 2086               n.setEndJavaLine(out.getJavaLine());
 2087           }
 2088   
 2089           public void visit(Node.AttributeGenerator n) throws JasperException {
 2090               Node.CustomTag tag = n.getTag();
 2091               Node.JspAttribute[] attrs = tag.getJspAttributes();
 2092               for (int i = 0; attrs != null && i < attrs.length; i++) {
 2093                   if (attrs[i].getName().equals(n.getName())) {
 2094                       out.print(evaluateAttribute(getTagHandlerInfo(tag),
 2095                               attrs[i], tag, null));
 2096                       break;
 2097                   }
 2098               }
 2099           }
 2100   
 2101           private TagHandlerInfo getTagHandlerInfo(Node.CustomTag n)
 2102                   throws JasperException {
 2103               Hashtable handlerInfosByShortName = (Hashtable) handlerInfos.get(n
 2104                       .getPrefix());
 2105               if (handlerInfosByShortName == null) {
 2106                   handlerInfosByShortName = new Hashtable();
 2107                   handlerInfos.put(n.getPrefix(), handlerInfosByShortName);
 2108               }
 2109               TagHandlerInfo handlerInfo = (TagHandlerInfo) handlerInfosByShortName
 2110                       .get(n.getLocalName());
 2111               if (handlerInfo == null) {
 2112                   handlerInfo = new TagHandlerInfo(n, n.getTagHandlerClass(), err);
 2113                   handlerInfosByShortName.put(n.getLocalName(), handlerInfo);
 2114               }
 2115               return handlerInfo;
 2116           }
 2117   
 2118           private void generateTagPlugin(Node.CustomTag n) throws JasperException {
 2119               if (n.getAtSTag() != null) {
 2120                   n.getAtSTag().visit(this);
 2121               }
 2122               visitBody(n);
 2123               if (n.getAtETag() != null) {
 2124                   n.getAtETag().visit(this);
 2125               }
 2126           }
 2127   
 2128           private void generateCustomStart(Node.CustomTag n,
 2129                   TagHandlerInfo handlerInfo, String tagHandlerVar,
 2130                   String tagEvalVar, String tagPushBodyCountVar)
 2131                   throws JasperException {
 2132   
 2133               Class tagHandlerClass = handlerInfo.getTagHandlerClass();
 2134   
 2135               out.printin("//  ");
 2136               out.println(n.getQName());
 2137               n.setBeginJavaLine(out.getJavaLine());
 2138   
 2139               // Declare AT_BEGIN scripting variables
 2140               declareScriptingVars(n, VariableInfo.AT_BEGIN);
 2141               saveScriptingVars(n, VariableInfo.AT_BEGIN);
 2142   
 2143               String tagHandlerClassName = JspUtil
 2144                       .getCanonicalName(tagHandlerClass);
 2145               out.printin(tagHandlerClassName);
 2146               out.print(" ");
 2147               out.print(tagHandlerVar);
 2148               out.print(" = ");
 2149               if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
 2150                   out.print("(");
 2151                   out.print(tagHandlerClassName);
 2152                   out.print(") ");
 2153                   out.print(n.getTagHandlerPoolName());
 2154                   out.print(".get(");
 2155                   out.print(tagHandlerClassName);
 2156                   out.println(".class);");
 2157               } else {
 2158                   out.print("new ");
 2159                   out.print(tagHandlerClassName);
 2160                   out.println("();");
 2161                   out.printin("org.apache.jasper.runtime.AnnotationHelper.postConstruct(");
 2162                   out.print(VAR_ANNOTATIONPROCESSOR);
 2163                   out.print(", ");
 2164                   out.print(tagHandlerVar);
 2165                   out.println(");");
 2166               }
 2167   
 2168               // includes setting the context
 2169               generateSetters(n, tagHandlerVar, handlerInfo, false);
 2170   
 2171               // JspIdConsumer (after context has been set)
 2172               if (n.implementsJspIdConsumer()) {
 2173                   out.printin(tagHandlerVar);
 2174                   out.print(".setJspId(\"");
 2175                   out.print(createJspId());
 2176                   out.println("\");");
 2177               }
 2178   
 2179               if (n.implementsTryCatchFinally()) {
 2180                   out.printin("int[] ");
 2181                   out.print(tagPushBodyCountVar);
 2182                   out.println(" = new int[] { 0 };");
 2183                   out.printil("try {");
 2184                   out.pushIndent();
 2185               }
 2186               out.printin("int ");
 2187               out.print(tagEvalVar);
 2188               out.print(" = ");
 2189               out.print(tagHandlerVar);
 2190               out.println(".doStartTag();");
 2191   
 2192               if (!n.implementsBodyTag()) {
 2193                   // Synchronize AT_BEGIN scripting variables
 2194                   syncScriptingVars(n, VariableInfo.AT_BEGIN);
 2195               }
 2196   
 2197               if (!n.hasEmptyBody()) {
 2198                   out.printin("if (");
 2199                   out.print(tagEvalVar);
 2200                   out.println(" != javax.servlet.jsp.tagext.Tag.SKIP_BODY) {");
 2201                   out.pushIndent();
 2202   
 2203                   // Declare NESTED scripting variables
 2204                   declareScriptingVars(n, VariableInfo.NESTED);
 2205                   saveScriptingVars(n, VariableInfo.NESTED);
 2206   
 2207                   if (n.implementsBodyTag()) {
 2208                       out.printin("if (");
 2209                       out.print(tagEvalVar);
 2210                       out
 2211                               .println(" != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
 2212                       // Assume EVAL_BODY_BUFFERED
 2213                       out.pushIndent();
 2214                       out.printil("out = _jspx_page_context.pushBody();");
 2215                       if (n.implementsTryCatchFinally()) {
 2216                           out.printin(tagPushBodyCountVar);
 2217                           out.println("[0]++;");
 2218                       } else if (pushBodyCountVar != null) {
 2219                           out.printin(pushBodyCountVar);
 2220                           out.println("[0]++;");
 2221                       }
 2222                       out.printin(tagHandlerVar);
 2223                       out
 2224                               .println(".setBodyContent((javax.servlet.jsp.tagext.BodyContent) out);");
 2225                       out.printin(tagHandlerVar);
 2226                       out.println(".doInitBody();");
 2227   
 2228                       out.popIndent();
 2229                       out.printil("}");
 2230   
 2231                       // Synchronize AT_BEGIN and NESTED scripting variables
 2232                       syncScriptingVars(n, VariableInfo.AT_BEGIN);
 2233                       syncScriptingVars(n, VariableInfo.NESTED);
 2234   
 2235                   } else {
 2236                       // Synchronize NESTED scripting variables
 2237                       syncScriptingVars(n, VariableInfo.NESTED);
 2238                   }
 2239   
 2240                   if (n.implementsIterationTag()) {
 2241                       out.printil("do {");
 2242                       out.pushIndent();
 2243                   }
 2244               }
 2245               // Map the Java lines that handles start of custom tags to the
 2246               // JSP line for this tag
 2247               n.setEndJavaLine(out.getJavaLine());
 2248           }
 2249   
 2250           private void generateCustomEnd(Node.CustomTag n, String tagHandlerVar,
 2251                   String tagEvalVar, String tagPushBodyCountVar) {
 2252   
 2253               if (!n.hasEmptyBody()) {
 2254                   if (n.implementsIterationTag()) {
 2255                       out.printin("int evalDoAfterBody = ");
 2256                       out.print(tagHandlerVar);
 2257                       out.println(".doAfterBody();");
 2258   
 2259                       // Synchronize AT_BEGIN and NESTED scripting variables
 2260                       syncScriptingVars(n, VariableInfo.AT_BEGIN);
 2261                       syncScriptingVars(n, VariableInfo.NESTED);
 2262   
 2263                       out
 2264                               .printil("if (evalDoAfterBody != javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_AGAIN)");
 2265                       out.pushIndent();
 2266                       out.printil("break;");
 2267                       out.popIndent();
 2268   
 2269                       out.popIndent();
 2270                       out.printil("} while (true);");
 2271                   }
 2272   
 2273                   restoreScriptingVars(n, VariableInfo.NESTED);
 2274   
 2275                   if (n.implementsBodyTag()) {
 2276                       out.printin("if (");
 2277                       out.print(tagEvalVar);
 2278                       out
 2279                               .println(" != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
 2280                       out.pushIndent();
 2281                       out.printil("out = _jspx_page_context.popBody();");
 2282                       if (n.implementsTryCatchFinally()) {
 2283                           out.printin(tagPushBodyCountVar);
 2284                           out.println("[0]--;");
 2285                       } else if (pushBodyCountVar != null) {
 2286                           out.printin(pushBodyCountVar);
 2287                           out.println("[0]--;");
 2288                       }
 2289                       out.popIndent();
 2290                       out.printil("}");
 2291                   }
 2292   
 2293                   out.popIndent(); // EVAL_BODY
 2294                   out.printil("}");
 2295               }
 2296   
 2297               out.printin("if (");
 2298               out.print(tagHandlerVar);
 2299               out
 2300                       .println(".doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {");
 2301               out.pushIndent();
 2302               if (!n.implementsTryCatchFinally()) {
 2303                   if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
 2304                       out.printin(n.getTagHandlerPoolName());
 2305                       out.print(".reuse(");
 2306                       out.print(tagHandlerVar);
 2307                       out.println(");");
 2308                   } else {
 2309                       out.printin(tagHandlerVar);
 2310                       out.println(".release();");
 2311                       out.printin("org.apache.jasper.runtime.AnnotationHelper.preDestroy(");
 2312                       out.print(VAR_ANNOTATIONPROCESSOR);
 2313                       out.print(", ");
 2314                       out.print(tagHandlerVar);
 2315                       out.println(");");
 2316                   }
 2317               }
 2318               if (isTagFile || isFragment) {
 2319                   out.printil("throw new SkipPageException();");
 2320               } else {
 2321                   out.printil((methodNesting > 0) ? "return true;" : "return;");
 2322               }
 2323               out.popIndent();
 2324               out.printil("}");
 2325               // Synchronize AT_BEGIN scripting variables
 2326               syncScriptingVars(n, VariableInfo.AT_BEGIN);
 2327   
 2328               // TryCatchFinally
 2329               if (n.implementsTryCatchFinally()) {
 2330                   out.popIndent(); // try
 2331                   out.printil("} catch (Throwable _jspx_exception) {");
 2332                   out.pushIndent();
 2333   
 2334                   out.printin("while (");
 2335                   out.print(tagPushBodyCountVar);
 2336                   out.println("[0]-- > 0)");
 2337                   out.pushIndent();
 2338                   out.printil("out = _jspx_page_context.popBody();");
 2339                   out.popIndent();
 2340   
 2341                   out.printin(tagHandlerVar);
 2342                   out.println(".doCatch(_jspx_exception);");
 2343                   out.popIndent();
 2344                   out.printil("} finally {");
 2345                   out.pushIndent();
 2346                   out.printin(tagHandlerVar);
 2347                   out.println(".doFinally();");
 2348               }
 2349   
 2350               if (isPoolingEnabled) {
 2351                   out.printin(n.getTagHandlerPoolName());
 2352                   out.print(".reuse(");
 2353                   out.print(tagHandlerVar);
 2354                   out.println(");");
 2355               } else {
 2356                   out.printin(tagHandlerVar);
 2357                   out.println(".release();");
 2358                   out.printin("org.apache.jasper.runtime.AnnotationHelper.preDestroy(");
 2359                   out.print(VAR_ANNOTATIONPROCESSOR);
 2360                   out.print(", ");
 2361                   out.print(tagHandlerVar);
 2362                   out.println(");");
 2363               }
 2364   
 2365               if (n.implementsTryCatchFinally()) {
 2366                   out.popIndent();
 2367                   out.printil("}");
 2368               }
 2369   
 2370               // Declare and synchronize AT_END scripting variables (must do this
 2371               // outside the try/catch/finally block)
 2372               declareScriptingVars(n, VariableInfo.AT_END);
 2373               syncScriptingVars(n, VariableInfo.AT_END);
 2374   
 2375               restoreScriptingVars(n, VariableInfo.AT_BEGIN);
 2376           }
 2377   
 2378           private void generateCustomDoTag(Node.CustomTag n,
 2379                   TagHandlerInfo handlerInfo, String tagHandlerVar)
 2380                   throws JasperException {
 2381   
 2382               Class tagHandlerClass = handlerInfo.getTagHandlerClass();
 2383   
 2384               n.setBeginJavaLine(out.getJavaLine());
 2385               out.printin("//  ");
 2386               out.println(n.getQName());
 2387   
 2388               // Declare AT_BEGIN scripting variables
 2389               declareScriptingVars(n, VariableInfo.AT_BEGIN);
 2390               saveScriptingVars(n, VariableInfo.AT_BEGIN);
 2391   
 2392               String tagHandlerClassName = JspUtil
 2393                       .getCanonicalName(tagHandlerClass);
 2394               out.printin(tagHandlerClassName);
 2395               out.print(" ");
 2396               out.print(tagHandlerVar);
 2397               out.print(" = ");
 2398               out.print("new ");
 2399               out.print(tagHandlerClassName);
 2400               out.println("();");
 2401   
 2402               // Resource injection
 2403               out.printin("org.apache.jasper.runtime.AnnotationHelper.postConstruct(");
 2404               out.print(VAR_ANNOTATIONPROCESSOR);
 2405               out.print(", ");
 2406               out.print(tagHandlerVar);
 2407               out.println(");");
 2408               
 2409               generateSetters(n, tagHandlerVar, handlerInfo, true);
 2410   
 2411               // JspIdConsumer (after context has been set)
 2412               if (n.implementsJspIdConsumer()) {
 2413                   out.printin(tagHandlerVar);
 2414                   out.print(".setJspId(\"");
 2415                   out.print(createJspId());
 2416                   out.println("\");");
 2417               }
 2418   
 2419               // Set the body
 2420               if (findJspBody(n) == null) {
 2421                   /*
 2422                    * Encapsulate body of custom tag invocation in JspFragment and
 2423                    * pass it to tag handler's setJspBody(), unless tag body is
 2424                    * empty
 2425                    */
 2426                   if (!n.hasEmptyBody()) {
 2427                       out.printin(tagHandlerVar);
 2428                       out.print(".setJspBody(");
 2429                       generateJspFragment(n, tagHandlerVar);
 2430                       out.println(");");
 2431                   }
 2432               } else {
 2433                   /*
 2434                    * Body of tag is the body of the <jsp:body> element. The visit
 2435                    * method for that element is going to encapsulate that
 2436                    * element's body in a JspFragment and pass it to the tag
 2437                    * handler's setJspBody()
 2438                    */
 2439                   String tmpTagHandlerVar = simpleTagHandlerVar;
 2440                   simpleTagHandlerVar = tagHandlerVar;
 2441                   boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
 2442                   isSimpleTagHandler = true;
 2443                   visitBody(n);
 2444                   simpleTagHandlerVar = tmpTagHandlerVar;
 2445                   isSimpleTagHandler = tmpIsSimpleTagHandler;
 2446               }
 2447   
 2448               out.printin(tagHandlerVar);
 2449               out.println(".doTag();");
 2450   
 2451               restoreScriptingVars(n, VariableInfo.AT_BEGIN);
 2452   
 2453               // Synchronize AT_BEGIN scripting variables
 2454               syncScriptingVars(n, VariableInfo.AT_BEGIN);
 2455   
 2456               // Declare and synchronize AT_END scripting variables
 2457               declareScriptingVars(n, VariableInfo.AT_END);
 2458               syncScriptingVars(n, VariableInfo.AT_END);
 2459   
 2460               // Resource injection
 2461               out.printin("org.apache.jasper.runtime.AnnotationHelper.preDestroy(");
 2462               out.print(VAR_ANNOTATIONPROCESSOR);
 2463               out.print(", ");
 2464               out.print(tagHandlerVar);
 2465               out.println(");");
 2466   
 2467               n.setEndJavaLine(out.getJavaLine());
 2468           }
 2469   
 2470           private void declareScriptingVars(Node.CustomTag n, int scope) {
 2471   
 2472               Vector vec = n.getScriptingVars(scope);
 2473               if (vec != null) {
 2474                   for (int i = 0; i < vec.size(); i++) {
 2475                       Object elem = vec.elementAt(i);
 2476                       if (elem instanceof VariableInfo) {
 2477                           VariableInfo varInfo = (VariableInfo) elem;
 2478                           if (varInfo.getDeclare()) {
 2479                               out.printin(varInfo.getClassName());
 2480                               out.print(" ");
 2481                               out.print(varInfo.getVarName());
 2482                               out.println(" = null;");
 2483                           }
 2484                       } else {
 2485                           TagVariableInfo tagVarInfo = (TagVariableInfo) elem;
 2486                           if (tagVarInfo.getDeclare()) {
 2487                               String varName = tagVarInfo.getNameGiven();
 2488                               if (varName == null) {
 2489                                   varName = n.getTagData().getAttributeString(
 2490                                           tagVarInfo.getNameFromAttribute());
 2491                               } else if (tagVarInfo.getNameFromAttribute() != null) {
 2492                                   // alias
 2493                                   continue;
 2494                               }
 2495                               out.printin(tagVarInfo.getClassName());
 2496                               out.print(" ");
 2497                               out.print(varName);
 2498                               out.println(" = null;");
 2499                           }
 2500                       }
 2501                   }
 2502               }
 2503           }
 2504   
 2505           /*
 2506            * This method is called as part of the custom tag's start element.
 2507            * 
 2508            * If the given custom tag has a custom nesting level greater than 0,
 2509            * save the current values of its scripting variables to temporary
 2510            * variables, so those values may be restored in the tag's end element.
 2511            * This way, the scripting variables may be synchronized by the given
 2512            * tag without affecting their original values.
 2513            */
 2514           private void saveScriptingVars(Node.CustomTag n, int scope) {
 2515               if (n.getCustomNestingLevel() == 0) {
 2516                   return;
 2517               }
 2518   
 2519               TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
 2520               VariableInfo[] varInfos = n.getVariableInfos();
 2521               if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
 2522                   return;
 2523               }
 2524   
 2525               if (varInfos.length > 0) {
 2526                   for (int i = 0; i < varInfos.length; i++) {
 2527                       if (varInfos[i].getScope() != scope)
 2528                           continue;
 2529                       // If the scripting variable has been declared, skip codes
 2530                       // for saving and restoring it.
 2531                       if (n.getScriptingVars(scope).contains(varInfos[i]))
 2532                           continue;
 2533                       String varName = varInfos[i].getVarName();
 2534                       String tmpVarName = "_jspx_" + varName + "_"
 2535                               + n.getCustomNestingLevel();
 2536                       out.printin(tmpVarName);
 2537                       out.print(" = ");
 2538                       out.print(varName);
 2539                       out.println(";");
 2540                   }
 2541               } else {
 2542                   for (int i = 0; i < tagVarInfos.length; i++) {
 2543                       if (tagVarInfos[i].getScope() != scope)
 2544                           continue;
 2545                       // If the scripting variable has been declared, skip codes
 2546                       // for saving and restoring it.
 2547                       if (n.getScriptingVars(scope).contains(tagVarInfos[i]))
 2548                           continue;
 2549                       String varName = tagVarInfos[i].getNameGiven();
 2550                       if (varName == null) {
 2551                           varName = n.getTagData().getAttributeString(
 2552                                   tagVarInfos[i].getNameFromAttribute());
 2553                       } else if (tagVarInfos[i].getNameFromAttribute() != null) {
 2554                           // alias
 2555                           continue;
 2556                       }
 2557                       String tmpVarName = "_jspx_" + varName + "_"
 2558                               + n.getCustomNestingLevel();
 2559                       out.printin(tmpVarName);
 2560                       out.print(" = ");
 2561                       out.print(varName);
 2562                       out.println(";");
 2563                   }
 2564               }
 2565           }
 2566   
 2567           /*
 2568            * This method is called as part of the custom tag's end element.
 2569            * 
 2570            * If the given custom tag has a custom nesting level greater than 0,
 2571            * restore its scripting variables to their original values that were
 2572            * saved in the tag's start element.
 2573            */
 2574           private void restoreScriptingVars(Node.CustomTag n, int scope) {
 2575               if (n.getCustomNestingLevel() == 0) {
 2576                   return;
 2577               }
 2578   
 2579               TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
 2580               VariableInfo[] varInfos = n.getVariableInfos();
 2581               if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
 2582                   return;
 2583               }
 2584   
 2585               if (varInfos.length > 0) {
 2586                   for (int i = 0; i < varInfos.length; i++) {
 2587                       if (varInfos[i].getScope() != scope)
 2588                           continue;
 2589                       // If the scripting variable has been declared, skip codes
 2590                       // for saving and restoring it.
 2591                       if (n.getScriptingVars(scope).contains(varInfos[i]))
 2592                           continue;
 2593                       String varName = varInfos[i].getVarName();
 2594                       String tmpVarName = "_jspx_" + varName + "_"
 2595                               + n.getCustomNestingLevel();
 2596                       out.printin(varName);
 2597                       out.print(" = ");
 2598                       out.print(tmpVarName);
 2599                       out.println(";");
 2600                   }
 2601               } else {
 2602                   for (int i = 0; i < tagVarInfos.length; i++) {
 2603                       if (tagVarInfos[i].getScope() != scope)
 2604                           continue;
 2605                       // If the scripting variable has been declared, skip codes
 2606                       // for saving and restoring it.
 2607                       if (n.getScriptingVars(scope).contains(tagVarInfos[i]))
 2608                           continue;
 2609                       String varName = tagVarInfos[i].getNameGiven();
 2610                       if (varName == null) {
 2611                           varName = n.getTagData().getAttributeString(
 2612                                   tagVarInfos[i].getNameFromAttribute());
 2613                       } else if (tagVarInfos[i].getNameFromAttribute() != null) {
 2614                           // alias
 2615                           continue;
 2616                       }
 2617                       String tmpVarName = "_jspx_" + varName + "_"
 2618                               + n.getCustomNestingLevel();
 2619                       out.printin(varName);
 2620                       out.print(" = ");
 2621                       out.print(tmpVarName);
 2622                       out.println(";");
 2623                   }
 2624               }
 2625           }
 2626   
 2627           /*
 2628            * Synchronizes the scripting variables of the given custom tag for the
 2629            * given scope.
 2630            */
 2631           private void syncScriptingVars(Node.CustomTag n, int scope) {
 2632               TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
 2633               VariableInfo[] varInfos = n.getVariableInfos();
 2634   
 2635               if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
 2636                   return;
 2637               }
 2638   
 2639               if (varInfos.length > 0) {
 2640                   for (int i = 0; i < varInfos.length; i++) {
 2641                       if (varInfos[i].getScope() == scope) {
 2642                           out.printin(varInfos[i].getVarName());
 2643                           out.print(" = (");
 2644                           out.print(varInfos[i].getClassName());
 2645                           out.print(") _jspx_page_context.findAttribute(");
 2646                           out.print(quote(varInfos[i].getVarName()));
 2647                           out.println(");");
 2648                       }
 2649                   }
 2650               } else {
 2651                   for (int i = 0; i < tagVarInfos.length; i++) {
 2652                       if (tagVarInfos[i].getScope() == scope) {
 2653                           String name = tagVarInfos[i].getNameGiven();
 2654                           if (name == null) {
 2655                               name = n.getTagData().getAttributeString(
 2656                                       tagVarInfos[i].getNameFromAttribute());
 2657                           } else if (tagVarInfos[i].getNameFromAttribute() != null) {
 2658                               // alias
 2659                               continue;
 2660                           }
 2661                           out.printin(name);
 2662                           out.print(" = (");
 2663                           out.print(tagVarInfos[i].getClassName());
 2664                           out.print(") _jspx_page_context.findAttribute(");
 2665                           out.print(quote(name));
 2666                           out.println(");");
 2667                       }
 2668                   }
 2669               }
 2670           }
 2671   
 2672           private String getJspContextVar() {
 2673               if (this.isTagFile) {
 2674                   return "this.getJspContext()";
 2675               } else {
 2676                   return "_jspx_page_context";
 2677               }
 2678           }
 2679   
 2680           private String getExpressionFactoryVar() {
 2681               return VAR_EXPRESSIONFACTORY;
 2682           }
 2683   
 2684           /*
 2685            * Creates a tag variable name by concatenating the given prefix and
 2686            * shortName and endcoded to make the resultant string a valid Java
 2687            * Identifier.
 2688            */
 2689           private String createTagVarName(String fullName, String prefix,
 2690                   String shortName) {
 2691   
 2692               String varName;
 2693               synchronized (tagVarNumbers) {
 2694                   varName = prefix + "_" + shortName + "_";
 2695                   if (tagVarNumbers.get(fullName) != null) {
 2696                       Integer i = (Integer) tagVarNumbers.get(fullName);
 2697                       varName = varName + i.intValue();
 2698                       tagVarNumbers.put(fullName, new Integer(i.intValue() + 1));
 2699                   } else {
 2700                       tagVarNumbers.put(fullName, new Integer(1));
 2701                       varName = varName + "0";
 2702                   }
 2703               }
 2704               return JspUtil.makeJavaIdentifier(varName);
 2705           }
 2706   
 2707           private String evaluateAttribute(TagHandlerInfo handlerInfo,
 2708                   Node.JspAttribute attr, Node.CustomTag n, String tagHandlerVar)
 2709                   throws JasperException {
 2710   
 2711               String attrValue = attr.getValue();
 2712               if (attrValue == null) {
 2713                   if (attr.isNamedAttribute()) {
 2714                       if (n.checkIfAttributeIsJspFragment(attr.getName())) {
 2715                           // XXX - no need to generate temporary variable here
 2716                           attrValue = generateNamedAttributeJspFragment(attr
 2717                                   .getNamedAttributeNode(), tagHandlerVar);
 2718                       } else {
 2719                           attrValue = generateNamedAttributeValue(attr
 2720                                   .getNamedAttributeNode());
 2721                       }
 2722                   } else {
 2723                       return null;
 2724                   }
 2725               }
 2726   
 2727               String localName = attr.getLocalName();
 2728   
 2729               Method m = null;
 2730               Class[] c = null;
 2731               if (attr.isDynamic()) {
 2732                   c = OBJECT_CLASS;
 2733               } else {
 2734                   m = handlerInfo.getSetterMethod(localName);
 2735                   if (m == null) {
 2736                       err.jspError(n, "jsp.error.unable.to_find_method", attr
 2737                               .getName());
 2738                   }
 2739                   c = m.getParameterTypes();
 2740                   // XXX assert(c.length > 0)
 2741               }
 2742   
 2743               if (attr.isExpression()) {
 2744                   // Do nothing
 2745               } else if (attr.isNamedAttribute()) {
 2746                   if (!n.checkIfAttributeIsJspFragment(attr.getName())
 2747                           && !attr.isDynamic()) {
 2748                       attrValue = convertString(c[0], attrValue, localName,
 2749                               handlerInfo.getPropertyEditorClass(localName), true);
 2750                   }
 2751               } else if (attr.isELInterpreterInput()) {
 2752   
 2753                   // results buffer
 2754                   StringBuffer sb = new StringBuffer(64);
 2755   
 2756                   TagAttributeInfo tai = attr.getTagAttributeInfo();
 2757   
 2758                   // generate elContext reference
 2759                   sb.append(getJspContextVar());
 2760                   sb.append(".getELContext()");
 2761                   String elContext = sb.toString();
 2762                   if (attr.getEL() != null && attr.getEL().getMapName() != null) {
 2763                       sb.setLength(0);
 2764                       sb.append("new org.apache.jasper.el.ELContextWrapper(");
 2765                       sb.append(elContext);
 2766                       sb.append(',');
 2767                       sb.append(attr.getEL().getMapName());
 2768                       sb.append(')');
 2769                       elContext = sb.toString();
 2770                   }
 2771   
 2772                   // reset buffer
 2773                   sb.setLength(0);
 2774                   
 2775                   // create our mark
 2776                   sb.append(n.getStart().toString());
 2777                   sb.append(" '");
 2778                   sb.append(attrValue);
 2779                   sb.append('\'');                
 2780                   String mark = sb.toString();
 2781                   
 2782                   // reset buffer
 2783                   sb.setLength(0);
 2784   
 2785                   // depending on type
 2786                   if (attr.isDeferredInput()
 2787                           || ((tai != null) && ValueExpression.class.getName().equals(tai.getTypeName()))) {
 2788                       sb.append("new org.apache.jasper.el.JspValueExpression(");
 2789                       sb.append(quote(mark));
 2790                       sb.append(',');
 2791                       sb.append(getExpressionFactoryVar());
 2792                       sb.append(".createValueExpression(");
 2793                       if (attr.getEL() != null) { // optimize
 2794                           sb.append(elContext);
 2795                           sb.append(',');
 2796                       }
 2797                       sb.append(quote(attrValue));
 2798                       sb.append(',');
 2799                       sb.append(JspUtil.toJavaSourceTypeFromTld(attr.getExpectedTypeName()));
 2800                       sb.append("))");
 2801                       // should the expression be evaluated before passing to
 2802                       // the setter?
 2803                       boolean evaluate = false;
 2804                       if (tai.canBeRequestTime()) {
 2805                           evaluate = true; // JSP.2.3.2
 2806                       }
 2807                       if (attr.isDeferredInput()) {
 2808                           evaluate = false; // JSP.2.3.3
 2809                       }
 2810                       if (attr.isDeferredInput() && tai.canBeRequestTime()) {
 2811                           evaluate = !attrValue.contains("#{"); // JSP.2.3.5
 2812                       }
 2813                       if (evaluate) {
 2814                           sb.append(".getValue(");
 2815                           sb.append(getJspContextVar());
 2816                           sb.append(".getELContext()");
 2817                           sb.append(")");
 2818                       }
 2819                       attrValue = sb.toString();
 2820                   } else if (attr.isDeferredMethodInput()
 2821                           || ((tai != null) && MethodExpression.class.getName().equals(tai.getTypeName()))) {
 2822                       sb.append("new org.apache.jasper.el.JspMethodExpression(");
 2823                       sb.append(quote(mark));
 2824                       sb.append(',');
 2825                       sb.append(getExpressionFactoryVar());
 2826                       sb.append(".createMethodExpression(");
 2827                       sb.append(elContext);
 2828                       sb.append(',');
 2829                       sb.append(quote(attrValue));
 2830                       sb.append(',');
 2831                       sb.append(JspUtil.toJavaSourceTypeFromTld(attr.getExpectedTypeName()));
 2832                       sb.append(',');
 2833                       sb.append("new Class[] {");
 2834   
 2835                       String[] p = attr.getParameterTypeNames();
 2836                       for (int i = 0; i < p.length; i++) {
 2837                           sb.append(JspUtil.toJavaSourceTypeFromTld(p[i]));
 2838                           sb.append(',');
 2839                       }
 2840                       if (p.length > 0) {
 2841                           sb.setLength(sb.length() - 1);
 2842                       }
 2843   
 2844                       sb.append("}))");
 2845                       attrValue = sb.toString();
 2846                   } else {
 2847                       // run attrValue through the expression interpreter
 2848                       boolean replaceESC = attrValue.indexOf(Constants.ESC) > 0;
 2849                       String mapName = (attr.getEL() != null) ? attr.getEL()
 2850                               .getMapName() : null;
 2851                       attrValue = JspUtil.interpreterCall(this.isTagFile,
 2852                               attrValue, c[0], mapName, false);
 2853                       // XXX hack: Replace ESC with '$'
 2854                       if (replaceESC) {
 2855                           attrValue = "(" + attrValue + ").replace("
 2856                                   + Constants.ESCStr + ", '$')";
 2857                       }
 2858                   }
 2859               } else {
 2860                   attrValue = convertString(c[0], attrValue, localName,
 2861                           handlerInfo.getPropertyEditorClass(localName), false);
 2862               }
 2863               return attrValue;
 2864           }
 2865   
 2866           /**
 2867            * Generate code to create a map for the alias variables
 2868            * 
 2869            * @return the name of the map
 2870            */
 2871           private String generateAliasMap(Node.CustomTag n, String tagHandlerVar)
 2872                   throws JasperException {
 2873   
 2874               TagVariableInfo[] tagVars = n.getTagVariableInfos();
 2875               String aliasMapVar = null;
 2876   
 2877               boolean aliasSeen = false;
 2878               for (int i = 0; i < tagVars.length; i++) {
 2879   
 2880                   String nameFrom = tagVars[i].getNameFromAttribute();
 2881                   if (nameFrom != null) {
 2882                       String aliasedName = n.getAttributeValue(nameFrom);
 2883                       if (aliasedName == null)
 2884                           continue;
 2885   
 2886                       if (!aliasSeen) {
 2887                           out.printin("java.util.HashMap ");
 2888                           aliasMapVar = tagHandlerVar + "_aliasMap";
 2889                           out.print(aliasMapVar);
 2890                           out.println(" = new java.util.HashMap();");
 2891                           aliasSeen = true;
 2892                       }
 2893                       out.printin(aliasMapVar);
 2894                       out.print(".put(");
 2895                       out.print(quote(tagVars[i].getNameGiven()));
 2896                       out.print(", ");
 2897                       out.print(quote(aliasedName));
 2898                       out.println(");");
 2899                   }
 2900               }
 2901               return aliasMapVar;
 2902           }
 2903   
 2904           private void generateSetters(Node.CustomTag n, String tagHandlerVar,
 2905                   TagHandlerInfo handlerInfo, boolean simpleTag)
 2906                   throws JasperException {
 2907   
 2908               // Set context
 2909               if (simpleTag) {
 2910                   // Generate alias map
 2911                   String aliasMapVar = null;
 2912                   if (n.isTagFile()) {
 2913                       aliasMapVar = generateAliasMap(n, tagHandlerVar);
 2914                   }
 2915                   out.printin(tagHandlerVar);
 2916                   if (aliasMapVar == null) {
 2917                       out.println(".setJspContext(_jspx_page_context);");
 2918                   } else {
 2919                       out.print(".setJspContext(_jspx_page_context, ");
 2920                       out.print(aliasMapVar);
 2921                       out.println(");");
 2922                   }
 2923               } else {
 2924                   out.printin(tagHandlerVar);
 2925                   out.println(".setPageContext(_jspx_page_context);");
 2926               }
 2927   
 2928               // Set parent
 2929               if (isTagFile && parent == null) {
 2930                   out.printin(tagHandlerVar);
 2931                   out.print(".setParent(");
 2932                   out.print("new javax.servlet.jsp.tagext.TagAdapter(");
 2933                   out.print("(javax.servlet.jsp.tagext.SimpleTag) this ));");
 2934               } else if (!simpleTag) {
 2935                   out.printin(tagHandlerVar);
 2936                   out.print(".setParent(");
 2937                   if (parent != null) {
 2938                       if (isSimpleTagParent) {
 2939                           out.print("new javax.servlet.jsp.tagext.TagAdapter(");
 2940                           out.print("(javax.servlet.jsp.tagext.SimpleTag) ");
 2941                           out.print(parent);
 2942                           out.println("));");
 2943                       } else {
 2944                           out.print("(javax.servlet.jsp.tagext.Tag) ");
 2945                           out.print(parent);
 2946                           out.println(");");
 2947                       }
 2948                   } else {
 2949                       out.println("null);");
 2950                   }
 2951               } else {
 2952                   // The setParent() method need not be called if the value being
 2953                   // passed is null, since SimpleTag instances are not reused
 2954                   if (parent != null) {
 2955                       out.printin(tagHandlerVar);
 2956                       out.print(".setParent(");
 2957                       out.print(parent);
 2958                       out.println(");");
 2959                   }
 2960               }
 2961   
 2962               // need to handle deferred values and methods
 2963               Node.JspAttribute[] attrs = n.getJspAttributes();
 2964               for (int i = 0; attrs != null && i < attrs.length; i++) {
 2965                   String attrValue = evaluateAttribute(handlerInfo, attrs[i], n,
 2966                           tagHandlerVar);
 2967                   
 2968                   Mark m = n.getStart();
 2969                   out.printil("// "+m.getFile()+"("+m.getLineNumber()+","+m.getColumnNumber()+") "+ attrs[i].getTagAttributeInfo());
 2970                   if (attrs[i].isDynamic()) {
 2971                       out.printin(tagHandlerVar);
 2972                       out.print(".");
 2973                       out.print("setDynamicAttribute(");
 2974                       String uri = attrs[i].getURI();
 2975                       if ("".equals(uri) || (uri == null)) {
 2976                           out.print("null");
 2977                       } else {
 2978                           out.print("\"" + attrs[i].getURI() + "\"");
 2979                       }
 2980                       out.print(", \"");
 2981                       out.print(attrs[i].getLocalName());
 2982                       out.print("\", ");
 2983                       out.print(attrValue);
 2984                       out.println(");");
 2985                   } else {
 2986                       out.printin(tagHandlerVar);
 2987                       out.print(".");
 2988                       out.print(handlerInfo.getSetterMethod(
 2989                               attrs[i].getLocalName()).getName());
 2990                       out.print("(");
 2991                       out.print(attrValue);
 2992                       out.println(");");
 2993                   }
 2994               }
 2995           }
 2996   
 2997           /*
 2998            * @param c The target class to which to coerce the given string @param
 2999            * s The string value @param attrName The name of the attribute whose
 3000            * value is being supplied @param propEditorClass The property editor
 3001            * for the given attribute @param isNamedAttribute true if the given
 3002            * attribute is a named attribute (that is, specified using the
 3003            * jsp:attribute standard action), and false otherwise
 3004            */
 3005           private String convertString(Class c, String s, String attrName,
 3006                   Class propEditorClass, boolean isNamedAttribute)
 3007                   throws JasperException {
 3008   
 3009               String quoted = s;
 3010               if (!isNamedAttribute) {
 3011                   quoted = quote(s);
 3012               }
 3013   
 3014               if (propEditorClass != null) {
 3015                   String className = JspUtil.getCanonicalName(c);
 3016                   return "("
 3017                           + className
 3018                           + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor("
 3019                           + className + ".class, \"" + attrName + "\", " + quoted
 3020                           + ", " + JspUtil.getCanonicalName(propEditorClass)
 3021                           + ".class)";
 3022               } else if (c == String.class) {
 3023                   return quoted;
 3024               } else if (c == boolean.class) {
 3025                   return JspUtil.coerceToPrimitiveBoolean(s, isNamedAttribute);
 3026               } else if (c == Boolean.class) {
 3027                   return JspUtil.coerceToBoolean(s, isNamedAttribute);
 3028               } else if (c == byte.class) {
 3029                   return JspUtil.coerceToPrimitiveByte(s, isNamedAttribute);
 3030               } else if (c == Byte.class) {
 3031                   return JspUtil.coerceToByte(s, isNamedAttribute);
 3032               } else if (c == char.class) {
 3033                   return JspUtil.coerceToChar(s, isNamedAttribute);
 3034               } else if (c == Character.class) {
 3035                   return JspUtil.coerceToCharacter(s, isNamedAttribute);
 3036               } else if (c == double.class) {
 3037                   return JspUtil.coerceToPrimitiveDouble(s, isNamedAttribute);
 3038               } else if (c == Double.class) {
 3039                   return JspUtil.coerceToDouble(s, isNamedAttribute);
 3040               } else if (c == float.class) {
 3041                   return JspUtil.coerceToPrimitiveFloat(s, isNamedAttribute);
 3042               } else if (c == Float.class) {
 3043                   return JspUtil.coerceToFloat(s, isNamedAttribute);
 3044               } else if (c == int.class) {
 3045                   return JspUtil.coerceToInt(s, isNamedAttribute);
 3046               } else if (c == Integer.class) {
 3047                   return JspUtil.coerceToInteger(s, isNamedAttribute);
 3048               } else if (c == short.class) {
 3049                   return JspUtil.coerceToPrimitiveShort(s, isNamedAttribute);
 3050               } else if (c == Short.class) {
 3051                   return JspUtil.coerceToShort(s, isNamedAttribute);
 3052               } else if (c == long.class) {
 3053                   return JspUtil.coerceToPrimitiveLong(s, isNamedAttribute);
 3054               } else if (c == Long.class) {
 3055                   return JspUtil.coerceToLong(s, isNamedAttribute);
 3056               } else if (c == Object.class) {
 3057                   return "new String(" + quoted + ")";
 3058               } else {
 3059                   String className = JspUtil.getCanonicalName(c);
 3060                   return "("
 3061                           + className
 3062                           + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager("
 3063                           + className + ".class, \"" + attrName + "\", " + quoted
 3064                           + ")";
 3065               }
 3066           }
 3067   
 3068           /*
 3069            * Converts the scope string representation, whose possible values are
 3070            * "page", "request", "session", and "application", to the corresponding
 3071            * scope constant.
 3072            */
 3073           private String getScopeConstant(String scope) {
 3074               String scopeName = "PageContext.PAGE_SCOPE"; // Default to page
 3075   
 3076               if ("request".equals(scope)) {
 3077                   scopeName = "PageContext.REQUEST_SCOPE";
 3078               } else if ("session".equals(scope)) {
 3079                   scopeName = "PageContext.SESSION_SCOPE";
 3080               } else if ("application".equals(scope)) {
 3081                   scopeName = "PageContext.APPLICATION_SCOPE";
 3082               }
 3083   
 3084               return scopeName;
 3085           }
 3086   
 3087           /**
 3088            * Generates anonymous JspFragment inner class which is passed as an
 3089            * argument to SimpleTag.setJspBody().
 3090            */
 3091           private void generateJspFragment(Node n, String tagHandlerVar)
 3092                   throws JasperException {
 3093               // XXX - A possible optimization here would be to check to see
 3094               // if the only child of the parent node is TemplateText. If so,
 3095               // we know there won't be any parameters, etc, so we can
 3096               // generate a low-overhead JspFragment that just echoes its
 3097               // body. The implementation of this fragment can come from
 3098               // the org.apache.jasper.runtime package as a support class.
 3099               FragmentHelperClass.Fragment fragment = fragmentHelperClass
 3100                       .openFragment(n, tagHandlerVar, methodNesting);
 3101               ServletWriter outSave = out;
 3102               out = fragment.getGenBuffer().getOut();
 3103               String tmpParent = parent;
 3104               parent = "_jspx_parent";
 3105               boolean isSimpleTagParentSave = isSimpleTagParent;
 3106               isSimpleTagParent = true;
 3107               boolean tmpIsFragment = isFragment;
 3108               isFragment = true;
 3109               String pushBodyCountVarSave = pushBodyCountVar;
 3110               if (pushBodyCountVar != null) {
 3111                   // Use a fixed name for push body count, to simplify code gen
 3112                   pushBodyCountVar = "_jspx_push_body_count";
 3113               }
 3114               visitBody(n);
 3115               out = outSave;
 3116               parent = tmpParent;
 3117               isSimpleTagParent = isSimpleTagParentSave;
 3118               isFragment = tmpIsFragment;
 3119               pushBodyCountVar = pushBodyCountVarSave;
 3120               fragmentHelperClass.closeFragment(fragment, methodNesting);
 3121               // XXX - Need to change pageContext to jspContext if
 3122               // we're not in a place where pageContext is defined (e.g.
 3123               // in a fragment or in a tag file.
 3124               out.print("new " + fragmentHelperClass.getClassName() + "( "
 3125                       + fragment.getId() + ", _jspx_page_context, "
 3126                       + tagHandlerVar + ", " + pushBodyCountVar + ")");
 3127           }
 3128   
 3129           /**
 3130            * Generate the code required to obtain the runtime value of the given
 3131            * named attribute.
 3132            * 
 3133            * @return The name of the temporary variable the result is stored in.
 3134            */
 3135           public String generateNamedAttributeValue(Node.NamedAttribute n)
 3136                   throws JasperException {
 3137   
 3138               String varName = n.getTemporaryVariableName();
 3139   
 3140               // If the only body element for this named attribute node is
 3141               // template text, we need not generate an extra call to
 3142               // pushBody and popBody. Maybe we can further optimize
 3143               // here by getting rid of the temporary variable, but in
 3144               // reality it looks like javac does this for us.
 3145               Node.Nodes body = n.getBody();
 3146               if (body != null) {
 3147                   boolean templateTextOptimization = false;
 3148                   if (body.size() == 1) {
 3149                       Node bodyElement = body.getNode(0);
 3150                       if (bodyElement instanceof Node.TemplateText) {
 3151                           templateTextOptimization = true;
 3152                           out.printil("String "
 3153                                   + varName
 3154                                   + " = "
 3155                                   + quote(new String(
 3156                                           ((Node.TemplateText) bodyElement)
 3157                                                   .getText())) + ";");
 3158                       }
 3159                   }
 3160   
 3161                   // XXX - Another possible optimization would be for
 3162                   // lone EL expressions (no need to pushBody here either).
 3163   
 3164                   if (!templateTextOptimization) {
 3165                       out.printil("out = _jspx_page_context.pushBody();");
 3166                       visitBody(n);
 3167                       out.printil("String " + varName + " = "
 3168                               + "((javax.servlet.jsp.tagext.BodyContent)"
 3169                               + "out).getString();");
 3170                       out.printil("out = _jspx_page_context.popBody();");
 3171                   }
 3172               } else {
 3173                   // Empty body must be treated as ""
 3174                   out.printil("String " + varName + " = \"\";");
 3175               }
 3176   
 3177               return varName;
 3178           }
 3179   
 3180           /**
 3181            * Similar to generateNamedAttributeValue, but create a JspFragment
 3182            * instead.
 3183            * 
 3184            * @param n
 3185            *            The parent node of the named attribute
 3186            * @param tagHandlerVar
 3187            *            The variable the tag handler is stored in, so the fragment
 3188            *            knows its parent tag.
 3189            * @return The name of the temporary variable the fragment is stored in.
 3190            */
 3191           public String generateNamedAttributeJspFragment(Node.NamedAttribute n,
 3192                   String tagHandlerVar) throws JasperException {
 3193               String varName = n.getTemporaryVariableName();
 3194   
 3195               out.printin("javax.servlet.jsp.tagext.JspFragment " + varName
 3196                       + " = ");
 3197               generateJspFragment(n, tagHandlerVar);
 3198               out.println(";");
 3199   
 3200               return varName;
 3201           }
 3202       }
 3203   
 3204       private static void generateLocalVariables(ServletWriter out, Node n)
 3205               throws JasperException {
 3206           Node.ChildInfo ci;
 3207           if (n instanceof Node.CustomTag) {
 3208               ci = ((Node.CustomTag) n).getChildInfo();
 3209           } else if (n instanceof Node.JspBody) {
 3210               ci = ((Node.JspBody) n).getChildInfo();
 3211           } else if (n instanceof Node.NamedAttribute) {
 3212               ci = ((Node.NamedAttribute) n).getChildInfo();
 3213           } else {
 3214               // Cannot access err since this method is static, but at
 3215               // least flag an error.
 3216               throw new JasperException("Unexpected Node Type");
 3217               // err.getString(
 3218               // "jsp.error.internal.unexpected_node_type" ) );
 3219           }
 3220   
 3221           if (ci.hasUseBean()) {
 3222               out
 3223                       .printil("HttpSession session = _jspx_page_context.getSession();");
 3224               out
 3225                       .printil("ServletContext application = _jspx_page_context.getServletContext();");
 3226           }
 3227           if (ci.hasUseBean() || ci.hasIncludeAction() || ci.hasSetProperty()
 3228                   || ci.hasParamAction()) {
 3229               out
 3230                       .printil("HttpServletRequest request = (HttpServletRequest)_jspx_page_context.getRequest();");
 3231           }
 3232           if (ci.hasIncludeAction()) {
 3233               out
 3234                       .printil("HttpServletResponse response = (HttpServletResponse)_jspx_page_context.getResponse();");
 3235           }
 3236       }
 3237   
 3238       /**
 3239        * Common part of postamble, shared by both servlets and tag files.
 3240        */
 3241       private void genCommonPostamble() {
 3242           // Append any methods that were generated in the buffer.
 3243           for (int i = 0; i < methodsBuffered.size(); i++) {
 3244               GenBuffer methodBuffer = (GenBuffer) methodsBuffered.get(i);
 3245               methodBuffer.adjustJavaLines(out.getJavaLine() - 1);
 3246               out.printMultiLn(methodBuffer.toString());
 3247           }
 3248   
 3249           // Append the helper class
 3250           if (fragmentHelperClass.isUsed()) {
 3251               fragmentHelperClass.generatePostamble();
 3252               fragmentHelperClass.adjustJavaLines(out.getJavaLine() - 1);
 3253               out.printMultiLn(fragmentHelperClass.toString());
 3254           }
 3255   
 3256           // Append char array declarations
 3257           if (charArrayBuffer != null) {
 3258               out.printMultiLn(charArrayBuffer.toString());
 3259           }
 3260   
 3261           // Close the class definition
 3262           out.popIndent();
 3263           out.printil("}");
 3264       }
 3265   
 3266       /**
 3267        * Generates the ending part of the static portion of the servlet.
 3268        */
 3269       private void generatePostamble(Node.Nodes page) {
 3270           out.popIndent();
 3271           out.printil("} catch (Throwable t) {");
 3272           out.pushIndent();
 3273           out.printil("if (!(t instanceof SkipPageException)){");
 3274           out.pushIndent();
 3275           out.printil("out = _jspx_out;");
 3276           out.printil("if (out != null && out.getBufferSize() != 0)");
 3277           out.pushIndent();
 3278           out.printil("try { out.clearBuffer(); } catch (java.io.IOException e) {}");
 3279           out.popIndent();
 3280   
 3281           out
 3282                   .printil("if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);");
 3283           out.popIndent();
 3284           out.printil("}");
 3285           out.popIndent();
 3286           out.printil("} finally {");
 3287           out.pushIndent();
 3288   
 3289           out
 3290                   .printil("_jspxFactory.releasePageContext(_jspx_page_context);");
 3291   
 3292           out.popIndent();
 3293           out.printil("}");
 3294   
 3295           // Close the service method
 3296           out.popIndent();
 3297           out.printil("}");
 3298   
 3299           // Generated methods, helper classes, etc.
 3300           genCommonPostamble();
 3301       }
 3302   
 3303       /**
 3304        * Constructor.
 3305        */
 3306       Generator(ServletWriter out, Compiler compiler) {
 3307           this.out = out;
 3308           methodsBuffered = new ArrayList();
 3309           charArrayBuffer = null;
 3310           err = compiler.getErrorDispatcher();
 3311           ctxt = compiler.getCompilationContext();
 3312           fragmentHelperClass = new FragmentHelperClass("Helper");
 3313           pageInfo = compiler.getPageInfo();
 3314   
 3315           /*
 3316            * Temporary hack. If a JSP page uses the "extends" attribute of the
 3317            * page directive, the _jspInit() method of the generated servlet class
 3318            * will not be called (it is only called for those generated servlets
 3319            * that extend HttpJspBase, the default), causing the tag handler pools
 3320            * not to be initialized and resulting in a NPE. The JSP spec needs to
 3321            * clarify whether containers can override init() and destroy(). For
 3322            * now, we just disable tag pooling for pages that use "extends".
 3323            */
 3324           if (pageInfo.getExtends(false) == null) {
 3325               isPoolingEnabled = ctxt.getOptions().isPoolingEnabled();
 3326           } else {
 3327               isPoolingEnabled = false;
 3328           }
 3329           beanInfo = pageInfo.getBeanRepository();
 3330           breakAtLF = ctxt.getOptions().getMappedFile();
 3331           if (isPoolingEnabled) {
 3332               tagHandlerPoolNames = new Vector();
 3333           }
 3334       }
 3335   
 3336       /**
 3337        * The main entry for Generator.
 3338        * 
 3339        * @param out
 3340        *            The servlet output writer
 3341        * @param compiler
 3342        *            The compiler
 3343        * @param page
 3344        *            The input page
 3345        */
 3346       public static void generate(ServletWriter out, Compiler compiler,
 3347               Node.Nodes page) throws JasperException {
 3348   
 3349           Generator gen = new Generator(out, compiler);
 3350   
 3351           if (gen.isPoolingEnabled) {
 3352               gen.compileTagHandlerPoolList(page);
 3353           }
 3354           if (gen.ctxt.isTagFile()) {
 3355               JasperTagInfo tagInfo = (JasperTagInfo) gen.ctxt.getTagInfo();
 3356               gen.generateTagHandlerPreamble(tagInfo, page);
 3357   
 3358               if (gen.ctxt.isPrototypeMode()) {
 3359                   return;
 3360               }
 3361   
 3362               gen.generateXmlProlog(page);
 3363               gen.fragmentHelperClass.generatePreamble();
 3364               page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out,
 3365                       gen.methodsBuffered, gen.fragmentHelperClass, gen.ctxt
 3366                               .getClassLoader(), tagInfo));
 3367               gen.generateTagHandlerPostamble(tagInfo);
 3368           } else {
 3369               gen.generatePreamble(page);
 3370               gen.generateXmlProlog(page);
 3371               gen.fragmentHelperClass.generatePreamble();
 3372               page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out,
 3373                       gen.methodsBuffered, gen.fragmentHelperClass, gen.ctxt
 3374                               .getClassLoader(), null));
 3375               gen.generatePostamble(page);
 3376           }
 3377       }
 3378   
 3379       /*
 3380        * Generates tag handler preamble.
 3381        */
 3382       private void generateTagHandlerPreamble(JasperTagInfo tagInfo,
 3383               Node.Nodes tag) throws JasperException {
 3384   
 3385           // Generate package declaration
 3386           String className = tagInfo.getTagClassName();
 3387           int lastIndex = className.lastIndexOf('.');
 3388           if (lastIndex != -1) {
 3389               String pkgName = className.substring(0, lastIndex);
 3390               genPreamblePackage(pkgName);
 3391               className = className.substring(lastIndex + 1);
 3392           }
 3393   
 3394           // Generate imports
 3395           genPreambleImports();
 3396   
 3397           // Generate class declaration
 3398           out.printin("public final class ");
 3399           out.println(className);
 3400           out.printil("    extends javax.servlet.jsp.tagext.SimpleTagSupport");
 3401           out.printin("    implements org.apache.jasper.runtime.JspSourceDependent");
 3402           if (tagInfo.hasDynamicAttributes()) {
 3403               out.println(",");
 3404               out.printin("               javax.servlet.jsp.tagext.DynamicAttributes");
 3405           }
 3406           out.println(" {");
 3407           out.println();
 3408           out.pushIndent();
 3409   
 3410           /*
 3411            * Class body begins here
 3412            */
 3413           generateDeclarations(tag);
 3414   
 3415           // Static initializations here
 3416           genPreambleStaticInitializers();
 3417   
 3418           out.printil("private JspContext jspContext;");
 3419   
 3420           // Declare writer used for storing result of fragment/body invocation
 3421           // if 'varReader' or 'var' attribute is specified
 3422           out.printil("private java.io.Writer _jspx_sout;");
 3423   
 3424           // Class variable declarations
 3425           genPreambleClassVariableDeclarations(tagInfo.getTagName());
 3426   
 3427           generateSetJspContext(tagInfo);
 3428   
 3429           // Tag-handler specific declarations
 3430           generateTagHandlerAttributes(tagInfo);
 3431           if (tagInfo.hasDynamicAttributes())
 3432               generateSetDynamicAttribute();
 3433   
 3434           // Methods here
 3435           genPreambleMethods();
 3436   
 3437           // Now the doTag() method
 3438           out.printil("public void doTag() throws JspException, java.io.IOException {");
 3439   
 3440           if (ctxt.isPrototypeMode()) {
 3441               out.printil("}");
 3442               out.popIndent();
 3443               out.printil("}");
 3444               return;
 3445           }
 3446   
 3447           out.pushIndent();
 3448   
 3449           /*
 3450            * According to the spec, 'pageContext' must not be made available as an
 3451            * implicit object in tag files. Declare _jspx_page_context, so we can
 3452            * share the code generator with JSPs.
 3453            */
 3454           out.printil("PageContext _jspx_page_context = (PageContext)jspContext;");
 3455           
 3456           // Declare implicit objects.
 3457           out.printil("HttpServletRequest request = "
 3458                   + "(HttpServletRequest) _jspx_page_context.getRequest();");
 3459           out.printil("HttpServletResponse response = "
 3460                   + "(HttpServletResponse) _jspx_page_context.getResponse();");
 3461           out.printil("HttpSession session = _jspx_page_context.getSession();");
 3462           out.printil("ServletContext application = _jspx_page_context.getServletContext();");
 3463           out.printil("ServletConfig config = _jspx_page_context.getServletConfig();");
 3464           out.printil("JspWriter out = jspContext.getOut();");
 3465           out.printil("_jspInit(config);");
 3466           
 3467           // set current JspContext on ELContext
 3468           out.printil("jspContext.getELContext().putContext(JspContext.class,jspContext);");
 3469           
 3470           generatePageScopedVariables(tagInfo);
 3471   
 3472           declareTemporaryScriptingVars(tag);
 3473           out.println();
 3474   
 3475           out.printil("try {");
 3476           out.pushIndent();
 3477       }
 3478   
 3479       private void generateTagHandlerPostamble(TagInfo tagInfo) {
 3480           out.popIndent();
 3481   
 3482           // Have to catch Throwable because a classic tag handler
 3483           // helper method is declared to throw Throwable.
 3484           out.printil("} catch( Throwable t ) {");
 3485           out.pushIndent();
 3486           out.printil("if( t instanceof SkipPageException )");
 3487           out.printil("    throw (SkipPageException) t;");
 3488           out.printil("if( t instanceof java.io.IOException )");
 3489           out.printil("    throw (java.io.IOException) t;");
 3490           out.printil("if( t instanceof IllegalStateException )");
 3491           out.printil("    throw (IllegalStateException) t;");
 3492           out.printil("if( t instanceof JspException )");
 3493           out.printil("    throw (JspException) t;");
 3494           out.printil("throw new JspException(t);");
 3495           out.popIndent();
 3496           out.printil("} finally {");
 3497           out.pushIndent();
 3498           
 3499           // handle restoring VariableMapper
 3500           TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
 3501           for (int i = 0; i < attrInfos.length; i++) {
 3502               if (attrInfos[i].isDeferredMethod() || attrInfos[i].isDeferredValue()) {
 3503                   out.printin("_el_variablemapper.setVariable(");
 3504                   out.print(quote(attrInfos[i].getName()));
 3505                   out.print(",_el_ve");
 3506                   out.print(i);
 3507                   out.println(");");
 3508               }
 3509           }
 3510           
 3511           // restore nested JspContext on ELContext
 3512           out.printil("jspContext.getELContext().putContext(JspContext.class,super.getJspContext());");
 3513           
 3514           out.printil("((org.apache.jasper.runtime.JspContextWrapper) jspContext).syncEndTagFile();");
 3515           if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
 3516               out.printil("_jspDestroy();");
 3517           }
 3518           out.popIndent();
 3519           out.printil("}");
 3520   
 3521           // Close the doTag method
 3522           out.popIndent();
 3523           out.printil("}");
 3524   
 3525           // Generated methods, helper classes, etc.
 3526           genCommonPostamble();
 3527       }
 3528   
 3529       /**
 3530        * Generates declarations for tag handler attributes, and defines the getter
 3531        * and setter methods for each.
 3532        */
 3533       private void generateTagHandlerAttributes(TagInfo tagInfo)
 3534               throws JasperException {
 3535   
 3536           if (tagInfo.hasDynamicAttributes()) {
 3537               out.printil("private java.util.HashMap _jspx_dynamic_attrs = new java.util.HashMap();");
 3538           }
 3539   
 3540           // Declare attributes
 3541           TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
 3542           for (int i = 0; i < attrInfos.length; i++) {
 3543               out.printin("private ");
 3544               if (attrInfos[i].isFragment()) {
 3545                   out.print("javax.servlet.jsp.tagext.JspFragment ");
 3546               } else {
 3547                   out.print(JspUtil.toJavaSourceType(attrInfos[i].getTypeName()));
 3548                   out.print(" ");
 3549               }
 3550               out.print(attrInfos[i].getName());
 3551               out.println(";");
 3552           }
 3553           out.println();
 3554   
 3555           // Define attribute getter and setter methods
 3556           if (attrInfos != null) {
 3557               for (int i = 0; i < attrInfos.length; i++) {
 3558                   // getter method
 3559                   out.printin("public ");
 3560                   if (attrInfos[i].isFragment()) {
 3561                       out.print("javax.servlet.jsp.tagext.JspFragment ");
 3562                   } else {
 3563                       out.print(JspUtil.toJavaSourceType(attrInfos[i]
 3564                               .getTypeName()));
 3565                       out.print(" ");
 3566                   }
 3567                   out.print(toGetterMethod(attrInfos[i].getName()));
 3568                   out.println(" {");
 3569                   out.pushIndent();
 3570                   out.printin("return this.");
 3571                   out.print(attrInfos[i].getName());
 3572                   out.println(";");
 3573                   out.popIndent();
 3574                   out.printil("}");
 3575                   out.println();
 3576   
 3577                   // setter method
 3578                   out.printin("public void ");
 3579                   out.print(toSetterMethodName(attrInfos[i].getName()));
 3580                   if (attrInfos[i].isFragment()) {
 3581                       out.print("(javax.servlet.jsp.tagext.JspFragment ");
 3582                   } else {
 3583                       out.print("(");
 3584                       out.print(JspUtil.toJavaSourceType(attrInfos[i]
 3585                               .getTypeName()));
 3586                       out.print(" ");
 3587                   }
 3588                   out.print(attrInfos[i].getName());
 3589                   out.println(") {");
 3590                   out.pushIndent();
 3591                   out.printin("this.");
 3592                   out.print(attrInfos[i].getName());
 3593                   out.print(" = ");
 3594                   out.print(attrInfos[i].getName());
 3595                   out.println(";");
 3596                   if (ctxt.isTagFile()) {
 3597                       // Tag files should also set jspContext attributes
 3598                       out.printin("jspContext.setAttribute(\"");
 3599                       out.print(attrInfos[i].getName());
 3600                       out.print("\", ");
 3601                       out.print(attrInfos[i].getName());
 3602                       out.println(");");
 3603                   }
 3604                   out.popIndent();
 3605                   out.printil("}");
 3606                   out.println();
 3607               }
 3608           }
 3609       }
 3610   
 3611       /*
 3612        * Generate setter for JspContext so we can create a wrapper and store both
 3613        * the original and the wrapper. We need the wrapper to mask the page
 3614        * context from the tag file and simulate a fresh page context. We need the
 3615        * original to do things like sync AT_BEGIN and AT_END scripting variables.
 3616        */
 3617       private void generateSetJspContext(TagInfo tagInfo) {
 3618   
 3619           boolean nestedSeen = false;
 3620           boolean atBeginSeen = false;
 3621           boolean atEndSeen = false;
 3622   
 3623           // Determine if there are any aliases
 3624           boolean aliasSeen = false;
 3625           TagVariableInfo[] tagVars = tagInfo.getTagVariableInfos();
 3626           for (int i = 0; i < tagVars.length; i++) {
 3627               if (tagVars[i].getNameFromAttribute() != null
 3628                       && tagVars[i].getNameGiven() != null) {
 3629                   aliasSeen = true;
 3630                   break;
 3631               }
 3632           }
 3633   
 3634           if (aliasSeen) {
 3635               out
 3636                       .printil("public void setJspContext(JspContext ctx, java.util.Map aliasMap) {");
 3637           } else {
 3638               out.printil("public void setJspContext(JspContext ctx) {");
 3639           }
 3640           out.pushIndent();
 3641           out.printil("super.setJspContext(ctx);");
 3642           out.printil("java.util.ArrayList _jspx_nested = null;");
 3643           out.printil("java.util.ArrayList _jspx_at_begin = null;");
 3644           out.printil("java.util.ArrayList _jspx_at_end = null;");
 3645   
 3646           for (int i = 0; i < tagVars.length; i++) {
 3647   
 3648               switch (tagVars[i].getScope()) {
 3649               case VariableInfo.NESTED:
 3650                   if (!nestedSeen) {
 3651                       out.printil("_jspx_nested = new java.util.ArrayList();");
 3652                       nestedSeen = true;
 3653                   }
 3654                   out.printin("_jspx_nested.add(");
 3655                   break;
 3656   
 3657               case VariableInfo.AT_BEGIN:
 3658                   if (!atBeginSeen) {
 3659                       out.printil("_jspx_at_begin = new java.util.ArrayList();");
 3660                       atBeginSeen = true;
 3661                   }
 3662                   out.printin("_jspx_at_begin.add(");
 3663                   break;
 3664   
 3665               case VariableInfo.AT_END:
 3666                   if (!atEndSeen) {
 3667                       out.printil("_jspx_at_end = new java.util.ArrayList();");
 3668                       atEndSeen = true;
 3669                   }
 3670                   out.printin("_jspx_at_end.add(");
 3671                   break;
 3672               } // switch
 3673   
 3674               out.print(quote(tagVars[i].getNameGiven()));
 3675               out.println(");");
 3676           }
 3677           if (aliasSeen) {
 3678               out
 3679                       .printil("this.jspContext = new org.apache.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, aliasMap);");
 3680           } else {
 3681               out
 3682                       .printil("this.jspContext = new org.apache.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, null);");
 3683           }
 3684           out.popIndent();
 3685           out.printil("}");
 3686           out.println();
 3687           out.printil("public JspContext getJspContext() {");
 3688           out.pushIndent();
 3689           out.printil("return this.jspContext;");
 3690           out.popIndent();
 3691           out.printil("}");
 3692       }
 3693   
 3694       /*
 3695        * Generates implementation of
 3696        * javax.servlet.jsp.tagext.DynamicAttributes.setDynamicAttribute() method,
 3697        * which saves each dynamic attribute that is passed in so that a scoped
 3698        * variable can later be created for it.
 3699        */
 3700       public void generateSetDynamicAttribute() {
 3701           out
 3702                   .printil("public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {");
 3703           out.pushIndent();
 3704           /*
 3705            * According to the spec, only dynamic attributes with no uri are to be
 3706            * present in the Map; all other dynamic attributes are ignored.
 3707            */
 3708           out.printil("if (uri == null)");
 3709           out.pushIndent();
 3710           out.printil("_jspx_dynamic_attrs.put(localName, value);");
 3711           out.popIndent();
 3712           out.popIndent();
 3713           out.printil("}");
 3714       }
 3715   
 3716       /*
 3717        * Creates a page-scoped variable for each declared tag attribute. Also, if
 3718        * the tag accepts dynamic attributes, a page-scoped variable is made
 3719        * available for each dynamic attribute that was passed in.
 3720        */
 3721       private void generatePageScopedVariables(JasperTagInfo tagInfo) {
 3722   
 3723           // "normal" attributes
 3724           TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
 3725           boolean variableMapperVar = false;
 3726           for (int i = 0; i < attrInfos.length; i++) {
 3727               String attrName = attrInfos[i].getName();
 3728               
 3729               // handle assigning deferred vars to VariableMapper, storing
 3730               // previous values under '_el_ve[i]' for later re-assignment
 3731               if (attrInfos[i].isDeferredValue() || attrInfos[i].isDeferredMethod()) {
 3732                   
 3733                   // we need to scope the modified VariableMapper for consistency and performance
 3734                   if (!variableMapperVar) {
 3735                       out.printil("javax.el.VariableMapper _el_variablemapper = jspContext.getELContext().getVariableMapper();");
 3736                       variableMapperVar = true;
 3737                   }
 3738                   
 3739                   out.printin("javax.el.ValueExpression _el_ve");
 3740                   out.print(i);
 3741                   out.print(" = _el_variablemapper.setVariable(");
 3742                   out.print(quote(attrName));
 3743                   out.print(',');
 3744                   if (attrInfos[i].isDeferredMethod()) {
 3745                       out.print(VAR_EXPRESSIONFACTORY);
 3746                       out.print(".createValueExpression(");
 3747                       out.print(toGetterMethod(attrName));
 3748                       out.print(",javax.el.MethodExpression.class)");
 3749                   } else {
 3750                       out.print(toGetterMethod(attrName));
 3751                   }
 3752                   out.println(");");
 3753               } else {
 3754                   out.printil("if( " + toGetterMethod(attrName) + " != null ) ");
 3755                   out.pushIndent();
 3756                   out.printin("_jspx_page_context.setAttribute(");
 3757                   out.print(quote(attrName));
 3758                   out.print(", ");
 3759                   out.print(toGetterMethod(attrName));
 3760                   out.println(");");
 3761                   out.popIndent();
 3762               }
 3763           }
 3764   
 3765           // Expose the Map containing dynamic attributes as a page-scoped var
 3766           if (tagInfo.hasDynamicAttributes()) {
 3767               out.printin("_jspx_page_context.setAttribute(\"");
 3768               out.print(tagInfo.getDynamicAttributesMapName());
 3769               out.print("\", _jspx_dynamic_attrs);");
 3770           }
 3771       }
 3772   
 3773       /*
 3774        * Generates the getter method for the given attribute name.
 3775        */
 3776       private String toGetterMethod(String attrName) {
 3777           char[] attrChars = attrName.toCharArray();
 3778           attrChars[0] = Character.toUpperCase(attrChars[0]);
 3779           return "get" + new String(attrChars) + "()";
 3780       }
 3781   
 3782       /*
 3783        * Generates the setter method name for the given attribute name.
 3784        */
 3785       private String toSetterMethodName(String attrName) {
 3786           char[] attrChars = attrName.toCharArray();
 3787           attrChars[0] = Character.toUpperCase(attrChars[0]);
 3788           return "set" + new String(attrChars);
 3789       }
 3790   
 3791       /**
 3792        * Class storing the result of introspecting a custom tag handler.
 3793        */
 3794       private static class TagHandlerInfo {
 3795   
 3796           private Hashtable methodMaps;
 3797   
 3798           private Hashtable propertyEditorMaps;
 3799   
 3800           private Class tagHandlerClass;
 3801   
 3802           /**
 3803            * Constructor.
 3804            * 
 3805            * @param n
 3806            *            The custom tag whose tag handler class is to be
 3807            *            introspected
 3808            * @param tagHandlerClass
 3809            *            Tag handler class
 3810            * @param err
 3811            *            Error dispatcher
 3812            */
 3813           TagHandlerInfo(Node n, Class tagHandlerClass, ErrorDispatcher err)
 3814                   throws JasperException {
 3815               this.tagHandlerClass = tagHandlerClass;
 3816               this.methodMaps = new Hashtable();
 3817               this.propertyEditorMaps = new Hashtable();
 3818   
 3819               try {
 3820                   BeanInfo tagClassInfo = Introspector
 3821                           .getBeanInfo(tagHandlerClass);
 3822                   PropertyDescriptor[] pd = tagClassInfo.getPropertyDescriptors();
 3823                   for (int i = 0; i < pd.length; i++) {
 3824                       /*
 3825                        * FIXME: should probably be checking for things like
 3826                        * pageContext, bodyContent, and parent here -akv
 3827                        */
 3828                       if (pd[i].getWriteMethod() != null) {
 3829                           methodMaps.put(pd[i].getName(), pd[i].getWriteMethod());
 3830                       }
 3831                       if (pd[i].getPropertyEditorClass() != null)
 3832                           propertyEditorMaps.put(pd[i].getName(), pd[i]
 3833                                   .getPropertyEditorClass());
 3834                   }
 3835               } catch (IntrospectionException ie) {
 3836                   err.jspError(n, "jsp.error.introspect.taghandler",
 3837                           tagHandlerClass.getName(), ie);
 3838               }
 3839           }
 3840   
 3841           /**
 3842            * XXX
 3843            */
 3844           public Method getSetterMethod(String attrName) {
 3845               return (Method) methodMaps.get(attrName);
 3846           }
 3847   
 3848           /**
 3849            * XXX
 3850            */
 3851           public Class getPropertyEditorClass(String attrName) {
 3852               return (Class) propertyEditorMaps.get(attrName);
 3853           }
 3854   
 3855           /**
 3856            * XXX
 3857            */
 3858           public Class getTagHandlerClass() {
 3859               return tagHandlerClass;
 3860           }
 3861       }
 3862   
 3863       /**
 3864        * A class for generating codes to a buffer. Included here are some support
 3865        * for tracking source to Java lines mapping.
 3866        */
 3867       private static class GenBuffer {
 3868   
 3869           /*
 3870            * For a CustomTag, the codes that are generated at the beginning of the
 3871            * tag may not be in the same buffer as those for the body of the tag.
 3872            * Two fields are used here to keep this straight. For codes that do not
 3873            * corresponds to any JSP lines, they should be null.
 3874            */
 3875           private Node node;
 3876   
 3877           private Node.Nodes body;
 3878   
 3879           private java.io.CharArrayWriter charWriter;
 3880   
 3881           protected ServletWriter out;
 3882   
 3883           GenBuffer() {
 3884               this(null, null);
 3885           }
 3886   
 3887           GenBuffer(Node n, Node.Nodes b) {
 3888               node = n;
 3889               body = b;
 3890               if (body != null) {
 3891                   body.setGeneratedInBuffer(true);
 3892               }
 3893               charWriter = new java.io.CharArrayWriter();
 3894               out = new ServletWriter(new java.io.PrintWriter(charWriter));
 3895           }
 3896   
 3897           public ServletWriter getOut() {
 3898               return out;
 3899           }
 3900   
 3901           public String toString() {
 3902               return charWriter.toString();
 3903           }
 3904   
 3905           /**
 3906            * Adjust the Java Lines. This is necessary because the Java lines
 3907            * stored with the nodes are relative the beginning of this buffer and
 3908            * need to be adjusted when this buffer is inserted into the source.
 3909            */
 3910           public void adjustJavaLines(final int offset) {
 3911   
 3912               if (node != null) {
 3913                   adjustJavaLine(node, offset);
 3914               }
 3915   
 3916               if (body != null) {
 3917                   try {
 3918                       body.visit(new Node.Visitor() {
 3919   
 3920                           public void doVisit(Node n) {
 3921                               adjustJavaLine(n, offset);
 3922                           }
 3923   
 3924                           public void visit(Node.CustomTag n)
 3925                                   throws JasperException {
 3926                               Node.Nodes b = n.getBody();
 3927                               if (b != null && !b.isGeneratedInBuffer()) {
 3928                                   // Don't adjust lines for the nested tags that
 3929                                   // are also generated in buffers, because the
 3930                                   // adjustments will be done elsewhere.
 3931                                   b.visit(this);
 3932                               }
 3933                           }
 3934                       });
 3935                   } catch (JasperException ex) {
 3936                   }
 3937               }
 3938           }
 3939   
 3940           private static void adjustJavaLine(Node n, int offset) {
 3941               if (n.getBeginJavaLine() > 0) {
 3942                   n.setBeginJavaLine(n.getBeginJavaLine() + offset);
 3943                   n.setEndJavaLine(n.getEndJavaLine() + offset);
 3944               }
 3945           }
 3946       }
 3947   
 3948       /**
 3949        * Keeps track of the generated Fragment Helper Class
 3950        */
 3951       private static class FragmentHelperClass {
 3952   
 3953           private static class Fragment {
 3954               private GenBuffer genBuffer;
 3955   
 3956               private int id;
 3957   
 3958               public Fragment(int id, Node node) {
 3959                   this.id = id;
 3960                   genBuffer = new GenBuffer(null, node.getBody());
 3961               }
 3962   
 3963               public GenBuffer getGenBuffer() {
 3964                   return this.genBuffer;
 3965               }
 3966   
 3967               public int getId() {
 3968                   return this.id;
 3969               }
 3970           }
 3971   
 3972           // True if the helper class should be generated.
 3973           private boolean used = false;
 3974   
 3975           private ArrayList fragments = new ArrayList();
 3976   
 3977           private String className;
 3978   
 3979           // Buffer for entire helper class
 3980           private GenBuffer classBuffer = new GenBuffer();
 3981   
 3982           public FragmentHelperClass(String className) {
 3983               this.className = className;
 3984           }
 3985   
 3986           public String getClassName() {
 3987               return this.className;
 3988           }
 3989   
 3990           public boolean isUsed() {
 3991               return this.used;
 3992           }
 3993   
 3994           public void generatePreamble() {
 3995               ServletWriter out = this.classBuffer.getOut();
 3996               out.println();
 3997               out.pushIndent();
 3998               // Note: cannot be static, as we need to reference things like
 3999               // _jspx_meth_*
 4000               out.printil("private class " + className);
 4001               out.printil("    extends "
 4002                       + "org.apache.jasper.runtime.JspFragmentHelper");
 4003               out.printil("{");
 4004               out.pushIndent();
 4005               out
 4006                       .printil("private javax.servlet.jsp.tagext.JspTag _jspx_parent;");
 4007               out.printil("private int[] _jspx_push_body_count;");
 4008               out.println();
 4009               out.printil("public " + className
 4010                       + "( int discriminator, JspContext jspContext, "
 4011                       + "javax.servlet.jsp.tagext.JspTag _jspx_parent, "
 4012                       + "int[] _jspx_push_body_count ) {");
 4013               out.pushIndent();
 4014               out.printil("super( discriminator, jspContext, _jspx_parent );");
 4015               out.printil("this._jspx_parent = _jspx_parent;");
 4016               out.printil("this._jspx_push_body_count = _jspx_push_body_count;");
 4017               out.popIndent();
 4018               out.printil("}");
 4019           }
 4020   
 4021           public Fragment openFragment(Node parent, String tagHandlerVar,
 4022                   int methodNesting) throws JasperException {
 4023               Fragment result = new Fragment(fragments.size(), parent);
 4024               fragments.add(result);
 4025               this.used = true;
 4026               parent.setInnerClassName(className);
 4027   
 4028               ServletWriter out = result.getGenBuffer().getOut();
 4029               out.pushIndent();
 4030               out.pushIndent();
 4031               // XXX - Returns boolean because if a tag is invoked from
 4032               // within this fragment, the Generator sometimes might
 4033               // generate code like "return true". This is ignored for now,
 4034               // meaning only the fragment is skipped. The JSR-152
 4035               // expert group is currently discussing what to do in this case.
 4036               // See comment in closeFragment()
 4037               if (methodNesting > 0) {
 4038                   out.printin("public boolean invoke");
 4039               } else {
 4040                   out.printin("public void invoke");
 4041               }
 4042               out.println(result.getId() + "( " + "JspWriter out ) ");
 4043               out.pushIndent();
 4044               // Note: Throwable required because methods like _jspx_meth_*
 4045               // throw Throwable.
 4046               out.printil("throws Throwable");
 4047               out.popIndent();
 4048               out.printil("{");
 4049               out.pushIndent();
 4050               generateLocalVariables(out, parent);
 4051   
 4052               return result;
 4053           }
 4054   
 4055           public void closeFragment(Fragment fragment, int methodNesting) {
 4056               ServletWriter out = fragment.getGenBuffer().getOut();
 4057               // XXX - See comment in openFragment()
 4058               if (methodNesting > 0) {
 4059                   out.printil("return false;");
 4060               } else {
 4061                   out.printil("return;");
 4062               }
 4063               out.popIndent();
 4064               out.printil("}");
 4065           }
 4066   
 4067           public void generatePostamble() {
 4068               ServletWriter out = this.classBuffer.getOut();
 4069               // Generate all fragment methods:
 4070               for (int i = 0; i < fragments.size(); i++) {
 4071                   Fragment fragment = (Fragment) fragments.get(i);
 4072                   fragment.getGenBuffer().adjustJavaLines(out.getJavaLine() - 1);
 4073                   out.printMultiLn(fragment.getGenBuffer().toString());
 4074               }
 4075   
 4076               // Generate postamble:
 4077               out.printil("public void invoke( java.io.Writer writer )");
 4078               out.pushIndent();
 4079               out.printil("throws JspException");
 4080               out.popIndent();
 4081               out.printil("{");
 4082               out.pushIndent();
 4083               out.printil("JspWriter out = null;");
 4084               out.printil("if( writer != null ) {");
 4085               out.pushIndent();
 4086               out.printil("out = this.jspContext.pushBody(writer);");
 4087               out.popIndent();
 4088               out.printil("} else {");
 4089               out.pushIndent();
 4090               out.printil("out = this.jspContext.getOut();");
 4091               out.popIndent();
 4092               out.printil("}");
 4093               out.printil("try {");
 4094               out.pushIndent();
 4095               out.printil("this.jspContext.getELContext().putContext(JspContext.class,this.jspContext);");
 4096               out.printil("switch( this.discriminator ) {");
 4097               out.pushIndent();
 4098               for (int i = 0; i < fragments.size(); i++) {
 4099                   out.printil("case " + i + ":");
 4100                   out.pushIndent();
 4101                   out.printil("invoke" + i + "( out );");
 4102                   out.printil("break;");
 4103                   out.popIndent();
 4104               }
 4105               out.popIndent();
 4106               out.printil("}"); // switch
 4107               out.popIndent();
 4108               out.printil("}"); // try
 4109               out.printil("catch( Throwable e ) {");
 4110               out.pushIndent();
 4111               out.printil("if (e instanceof SkipPageException)");
 4112               out.printil("    throw (SkipPageException) e;");
 4113               out.printil("throw new JspException( e );");
 4114               out.popIndent();
 4115               out.printil("}"); // catch
 4116               out.printil("finally {");
 4117               out.pushIndent();
 4118   
 4119               out.printil("if( writer != null ) {");
 4120               out.pushIndent();
 4121               out.printil("this.jspContext.popBody();");
 4122               out.popIndent();
 4123               out.printil("}");
 4124   
 4125               out.popIndent();
 4126               out.printil("}"); // finally
 4127               out.popIndent();
 4128               out.printil("}"); // invoke method
 4129               out.popIndent();
 4130               out.printil("}"); // helper class
 4131               out.popIndent();
 4132           }
 4133   
 4134           public String toString() {
 4135               return classBuffer.toString();
 4136           }
 4137   
 4138           public void adjustJavaLines(int offset) {
 4139               for (int i = 0; i < fragments.size(); i++) {
 4140                   Fragment fragment = (Fragment) fragments.get(i);
 4141                   fragment.getGenBuffer().adjustJavaLines(offset);
 4142               }
 4143           }
 4144       }
 4145   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » jasper » compiler » [javadoc | source]