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