View Javadoc

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