0001: /*
0002: * Copyright 1999,2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.jasper.compiler;
0018:
0019: import java.beans.BeanInfo;
0020: import java.beans.IntrospectionException;
0021: import java.beans.Introspector;
0022: import java.beans.PropertyDescriptor;
0023: import java.lang.reflect.Method;
0024: import java.lang.reflect.Modifier;
0025: import java.util.ArrayList;
0026: import java.util.Arrays;
0027: import java.util.Collections;
0028: import java.util.Enumeration;
0029: import java.util.Hashtable;
0030: import java.util.HashMap;
0031: import java.util.Iterator;
0032: import java.util.List;
0033: import java.util.Vector;
0034:
0035: import javax.servlet.jsp.tagext.TagAttributeInfo;
0036: import javax.servlet.jsp.tagext.TagInfo;
0037: import javax.servlet.jsp.tagext.TagVariableInfo;
0038: import javax.servlet.jsp.tagext.VariableInfo;
0039:
0040: import org.apache.jasper.Constants;
0041: import org.apache.jasper.JasperException;
0042: import org.apache.jasper.JspCompilationContext;
0043: import org.apache.jasper.runtime.JspRuntimeLibrary;
0044: import org.xml.sax.Attributes;
0045:
0046: /**
0047: * Generate Java source from Nodes
0048: *
0049: * @author Anil K. Vijendran
0050: * @author Danno Ferrin
0051: * @author Mandar Raje
0052: * @author Rajiv Mordani
0053: * @author Pierre Delisle
0054: *
0055: * Tomcat 4.1.x and Tomcat 5:
0056: * @author Kin-man Chung
0057: * @author Jan Luehe
0058: * @author Shawn Bayern
0059: * @author Mark Roth
0060: * @author Denis Benoit
0061: */
0062:
0063: class Generator {
0064:
0065: private static final Class[] OBJECT_CLASS = { Object.class };
0066: private ServletWriter out;
0067: private ArrayList methodsBuffered;
0068: private FragmentHelperClass fragmentHelperClass;
0069: private ErrorDispatcher err;
0070: private BeanRepository beanInfo;
0071: private JspCompilationContext ctxt;
0072: private boolean isPoolingEnabled;
0073: private boolean breakAtLF;
0074: private PageInfo pageInfo;
0075: private int maxTagNesting;
0076: private Vector tagHandlerPoolNames;
0077: private GenBuffer charArrayBuffer;
0078:
0079: /**
0080: * @param s the input string
0081: * @return quoted and escaped string, per Java rule
0082: */
0083: static String quote(String s) {
0084:
0085: if (s == null)
0086: return "null";
0087:
0088: return '"' + escape(s) + '"';
0089: }
0090:
0091: /**
0092: * @param s the input string
0093: * @return escaped string, per Java rule
0094: */
0095: static String escape(String s) {
0096:
0097: if (s == null)
0098: return "";
0099:
0100: StringBuffer b = new StringBuffer();
0101: for (int i = 0; i < s.length(); i++) {
0102: char c = s.charAt(i);
0103: if (c == '"')
0104: b.append('\\').append('"');
0105: else if (c == '\\')
0106: b.append('\\').append('\\');
0107: else if (c == '\n')
0108: b.append('\\').append('n');
0109: else if (c == '\r')
0110: b.append('\\').append('r');
0111: else
0112: b.append(c);
0113: }
0114: return b.toString();
0115: }
0116:
0117: /**
0118: * Single quote and escape a character
0119: */
0120: static String quote(char c) {
0121:
0122: StringBuffer b = new StringBuffer();
0123: b.append('\'');
0124: if (c == '\'')
0125: b.append('\\').append('\'');
0126: else if (c == '\\')
0127: b.append('\\').append('\\');
0128: else if (c == '\n')
0129: b.append('\\').append('n');
0130: else if (c == '\r')
0131: b.append('\\').append('r');
0132: else
0133: b.append(c);
0134: b.append('\'');
0135: return b.toString();
0136: }
0137:
0138: /**
0139: * Generates declarations. This includes "info" of the page directive,
0140: * and scriptlet declarations.
0141: */
0142: private void generateDeclarations(Node.Nodes page)
0143: throws JasperException {
0144:
0145: class DeclarationVisitor extends Node.Visitor {
0146:
0147: private boolean getServletInfoGenerated = false;
0148:
0149: /*
0150: * Generates getServletInfo() method that returns the value of the
0151: * page directive's 'info' attribute, if present.
0152: *
0153: * The Validator has already ensured that if the translation unit
0154: * contains more than one page directive with an 'info' attribute,
0155: * their values match.
0156: */
0157: public void visit(Node.PageDirective n)
0158: throws JasperException {
0159:
0160: if (getServletInfoGenerated) {
0161: return;
0162: }
0163:
0164: String info = n.getAttributeValue("info");
0165: if (info == null)
0166: return;
0167:
0168: getServletInfoGenerated = true;
0169: out.printil("public String getServletInfo() {");
0170: out.pushIndent();
0171: out.printin("return ");
0172: out.print(quote(info));
0173: out.println(";");
0174: out.popIndent();
0175: out.printil("}");
0176: out.println();
0177: }
0178:
0179: public void visit(Node.Declaration n)
0180: throws JasperException {
0181: n.setBeginJavaLine(out.getJavaLine());
0182: out.printMultiLn(new String(n.getText()));
0183: out.println();
0184: n.setEndJavaLine(out.getJavaLine());
0185: }
0186:
0187: // Custom Tags may contain declarations from tag plugins.
0188: public void visit(Node.CustomTag n) throws JasperException {
0189: if (n.useTagPlugin()) {
0190: if (n.getAtSTag() != null) {
0191: n.getAtSTag().visit(this );
0192: }
0193: visitBody(n);
0194: if (n.getAtETag() != null) {
0195: n.getAtETag().visit(this );
0196: }
0197: } else {
0198: visitBody(n);
0199: }
0200: }
0201: }
0202:
0203: out.println();
0204: page.visit(new DeclarationVisitor());
0205: }
0206:
0207: /**
0208: * Compiles list of tag handler pool names.
0209: */
0210: private void compileTagHandlerPoolList(Node.Nodes page)
0211: throws JasperException {
0212:
0213: class TagHandlerPoolVisitor extends Node.Visitor {
0214:
0215: private Vector names;
0216:
0217: /*
0218: * Constructor
0219: *
0220: * @param v Vector of tag handler pool names to populate
0221: */
0222: TagHandlerPoolVisitor(Vector v) {
0223: names = v;
0224: }
0225:
0226: /*
0227: * Gets the name of the tag handler pool for the given custom tag
0228: * and adds it to the list of tag handler pool names unless it is
0229: * already contained in it.
0230: */
0231: public void visit(Node.CustomTag n) throws JasperException {
0232:
0233: if (!n.implements SimpleTag()) {
0234: String name = createTagHandlerPoolName(n
0235: .getPrefix(), n.getLocalName(), n
0236: .getAttributes(), n.hasEmptyBody());
0237: n.setTagHandlerPoolName(name);
0238: if (!names.contains(name)) {
0239: names.add(name);
0240: }
0241: }
0242: visitBody(n);
0243: }
0244:
0245: /*
0246: * Creates the name of the tag handler pool whose tag handlers may
0247: * be (re)used to service this action.
0248: *
0249: * @return The name of the tag handler pool
0250: */
0251: private String createTagHandlerPoolName(String prefix,
0252: String shortName, Attributes attrs,
0253: boolean hasEmptyBody) {
0254: String poolName = null;
0255:
0256: poolName = "_jspx_tagPool_" + prefix + "_" + shortName;
0257: if (attrs != null) {
0258: String[] attrNames = new String[attrs.getLength()];
0259: for (int i = 0; i < attrNames.length; i++) {
0260: attrNames[i] = attrs.getQName(i);
0261: }
0262: Arrays.sort(attrNames, Collections.reverseOrder());
0263: for (int i = 0; i < attrNames.length; i++) {
0264: poolName = poolName + "_" + attrNames[i];
0265: }
0266: }
0267: if (hasEmptyBody) {
0268: poolName = poolName + "_nobody";
0269: }
0270: return JspUtil.makeXmlJavaIdentifier(poolName);
0271: }
0272: }
0273:
0274: page.visit(new TagHandlerPoolVisitor(tagHandlerPoolNames));
0275: }
0276:
0277: private void declareTemporaryScriptingVars(Node.Nodes page)
0278: throws JasperException {
0279:
0280: class ScriptingVarVisitor extends Node.Visitor {
0281:
0282: private Vector vars;
0283:
0284: ScriptingVarVisitor() {
0285: vars = new Vector();
0286: }
0287:
0288: public void visit(Node.CustomTag n) throws JasperException {
0289:
0290: if (n.getCustomNestingLevel() > 0) {
0291: TagVariableInfo[] tagVarInfos = n
0292: .getTagVariableInfos();
0293: VariableInfo[] varInfos = n.getVariableInfos();
0294:
0295: if (varInfos.length > 0) {
0296: for (int i = 0; i < varInfos.length; i++) {
0297: String varName = varInfos[i].getVarName();
0298: String tmpVarName = "_jspx_" + varName
0299: + "_" + n.getCustomNestingLevel();
0300: if (!vars.contains(tmpVarName)) {
0301: vars.add(tmpVarName);
0302: out.printin(varInfos[i].getClassName());
0303: out.print(" ");
0304: out.print(tmpVarName);
0305: out.print(" = ");
0306: out.print(null);
0307: out.println(";");
0308: }
0309: }
0310: } else {
0311: for (int i = 0; i < tagVarInfos.length; i++) {
0312: String varName = tagVarInfos[i]
0313: .getNameGiven();
0314: if (varName == null) {
0315: varName = n
0316: .getTagData()
0317: .getAttributeString(
0318: tagVarInfos[i]
0319: .getNameFromAttribute());
0320: } else if (tagVarInfos[i]
0321: .getNameFromAttribute() != null) {
0322: // alias
0323: continue;
0324: }
0325: String tmpVarName = "_jspx_" + varName
0326: + "_" + n.getCustomNestingLevel();
0327: if (!vars.contains(tmpVarName)) {
0328: vars.add(tmpVarName);
0329: out.printin(tagVarInfos[i]
0330: .getClassName());
0331: out.print(" ");
0332: out.print(tmpVarName);
0333: out.print(" = ");
0334: out.print(null);
0335: out.println(";");
0336: }
0337: }
0338: }
0339: }
0340:
0341: visitBody(n);
0342: }
0343: }
0344:
0345: page.visit(new ScriptingVarVisitor());
0346: }
0347:
0348: /**
0349: * Generates the _jspInit() method for instantiating the tag handler pools.
0350: * For tag file, _jspInit has to be invoked manually, and the ServletConfig
0351: * object explicitly passed.
0352: */
0353: private void generateInit() {
0354:
0355: if (ctxt.isTagFile()) {
0356: out
0357: .printil("private void _jspInit(ServletConfig config) {");
0358: } else {
0359: out.printil("public void _jspInit() {");
0360: }
0361:
0362: out.pushIndent();
0363: for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
0364: out.printin((String) tagHandlerPoolNames.elementAt(i));
0365: out
0366: .print(" = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool(");
0367: if (ctxt.isTagFile()) {
0368: out.print("config");
0369: } else {
0370: out.print("getServletConfig()");
0371: }
0372: out.println(");");
0373: }
0374: out.popIndent();
0375: out.printil("}");
0376: out.println();
0377: }
0378:
0379: /**
0380: * Generates the _jspDestroy() method which is responsible for calling the
0381: * release() method on every tag handler in any of the tag handler pools.
0382: */
0383: private void generateDestroy() {
0384:
0385: out.printil("public void _jspDestroy() {");
0386: out.pushIndent();
0387: for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
0388: out.printin((String) tagHandlerPoolNames.elementAt(i));
0389: out.println(".release();");
0390: }
0391: out.popIndent();
0392: out.printil("}");
0393: out.println();
0394: }
0395:
0396: /**
0397: * Generate preamble package name
0398: * (shared by servlet and tag handler preamble generation)
0399: */
0400: private void genPreamblePackage(String packageName)
0401: throws JasperException {
0402: if (!"".equals(packageName) && packageName != null) {
0403: out.printil("package " + packageName + ";");
0404: out.println();
0405: }
0406: }
0407:
0408: /**
0409: * Generate preamble imports
0410: * (shared by servlet and tag handler preamble generation)
0411: */
0412: private void genPreambleImports() throws JasperException {
0413: Iterator iter = pageInfo.getImports().iterator();
0414: while (iter.hasNext()) {
0415: out.printin("import ");
0416: out.print((String) iter.next());
0417: out.println(";");
0418: }
0419: out.println();
0420: }
0421:
0422: /**
0423: * Generation of static initializers in preamble.
0424: * For example, dependant list, el function map, prefix map.
0425: * (shared by servlet and tag handler preamble generation)
0426: */
0427: private void genPreambleStaticInitializers() throws JasperException {
0428: // Static data for getDependants()
0429: out
0430: .printil("private static java.util.Vector _jspx_dependants;");
0431: out.println();
0432: List dependants = pageInfo.getDependants();
0433: Iterator iter = dependants.iterator();
0434: if (!dependants.isEmpty()) {
0435: out.printil("static {");
0436: out.pushIndent();
0437: out.printin("_jspx_dependants = new java.util.Vector(");
0438: out.print("" + dependants.size());
0439: out.println(");");
0440: while (iter.hasNext()) {
0441: out.printin("_jspx_dependants.add(\"");
0442: out.print((String) iter.next());
0443: out.println("\");");
0444: }
0445: out.popIndent();
0446: out.printil("}");
0447: out.println();
0448: }
0449: }
0450:
0451: /**
0452: * Declare tag handler pools (tags of the same type and with the same
0453: * attribute set share the same tag handler pool)
0454: * (shared by servlet and tag handler preamble generation)
0455: */
0456: private void genPreambleClassVariableDeclarations(String className)
0457: throws JasperException {
0458: if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
0459: for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
0460: out
0461: .printil("private org.apache.jasper.runtime.TagHandlerPool "
0462: + tagHandlerPoolNames.elementAt(i)
0463: + ";");
0464: }
0465: out.println();
0466: }
0467: }
0468:
0469: /**
0470: * Declare general-purpose methods
0471: * (shared by servlet and tag handler preamble generation)
0472: */
0473: private void genPreambleMethods() throws JasperException {
0474: // Method used to get compile time file dependencies
0475: out.printil("public java.util.List getDependants() {");
0476: out.pushIndent();
0477: out.printil("return _jspx_dependants;");
0478: out.popIndent();
0479: out.printil("}");
0480: out.println();
0481:
0482: if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
0483: generateInit();
0484: generateDestroy();
0485: }
0486: }
0487:
0488: /**
0489: * Generates the beginning of the static portion of the servlet.
0490: */
0491: private void generatePreamble(Node.Nodes page)
0492: throws JasperException {
0493:
0494: String servletPackageName = ctxt.getServletPackageName();
0495: String servletClassName = ctxt.getServletClassName();
0496: String serviceMethodName = Constants.SERVICE_METHOD_NAME;
0497:
0498: // First the package name:
0499: genPreamblePackage(servletPackageName);
0500:
0501: // Generate imports
0502: genPreambleImports();
0503:
0504: // Generate class declaration
0505: out.printin("public final class ");
0506: out.print(servletClassName);
0507: out.print(" extends ");
0508: out.println(pageInfo.getExtends());
0509: out
0510: .printin(" implements org.apache.jasper.runtime.JspSourceDependent");
0511: if (!pageInfo.isThreadSafe()) {
0512: out.println(",");
0513: out.printin(" SingleThreadModel");
0514: }
0515: out.println(" {");
0516: out.pushIndent();
0517:
0518: // Class body begins here
0519: generateDeclarations(page);
0520:
0521: // Static initializations here
0522: genPreambleStaticInitializers();
0523:
0524: // Class variable declarations
0525: genPreambleClassVariableDeclarations(servletClassName);
0526:
0527: // Constructor
0528: // generateConstructor(className);
0529:
0530: // Methods here
0531: genPreambleMethods();
0532:
0533: // Now the service method
0534: out.printin("public void ");
0535: out.print(serviceMethodName);
0536: out
0537: .println("(HttpServletRequest request, HttpServletResponse response)");
0538: out
0539: .println(" throws java.io.IOException, ServletException {");
0540:
0541: out.pushIndent();
0542: out.println();
0543:
0544: // Local variable declarations
0545: out.printil("JspFactory _jspxFactory = null;");
0546: out.printil("PageContext pageContext = null;");
0547: if (pageInfo.isSession())
0548: out.printil("HttpSession session = null;");
0549:
0550: if (pageInfo.isErrorPage()) {
0551: out
0552: .printil("Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);");
0553: out.printil("if (exception != null) {");
0554: out.pushIndent();
0555: out
0556: .printil("response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);");
0557: out.popIndent();
0558: out.printil("}");
0559: }
0560:
0561: out.printil("ServletContext application = null;");
0562: out.printil("ServletConfig config = null;");
0563: out.printil("JspWriter out = null;");
0564: out.printil("Object page = this;");
0565:
0566: // Number of tag object that need to be popped
0567: // XXX TODO: use a better criteria
0568: maxTagNesting = pageInfo.getMaxTagNesting();
0569: out.printil("JspWriter _jspx_out = null;");
0570: out.printil("PageContext _jspx_page_context = null;");
0571: out.println();
0572:
0573: declareTemporaryScriptingVars(page);
0574: out.println();
0575:
0576: out.printil("try {");
0577: out.pushIndent();
0578:
0579: out.printil("_jspxFactory = JspFactory.getDefaultFactory();");
0580:
0581: out.printin("response.setContentType(");
0582: out.print(quote(pageInfo.getContentType()));
0583: out.println(");");
0584:
0585: if (ctxt.getOptions().isXpoweredBy()) {
0586: out
0587: .printil("response.addHeader(\"X-Powered-By\", \"JSP/2.0\");");
0588: }
0589:
0590: out
0591: .printil("pageContext = _jspxFactory.getPageContext(this, request, response,");
0592: out.printin("\t\t\t");
0593: out.print(quote(pageInfo.getErrorPage()));
0594: out.print(", " + pageInfo.isSession());
0595: out.print(", " + pageInfo.getBuffer());
0596: out.print(", " + pageInfo.isAutoFlush());
0597: out.println(");");
0598: out.printil("_jspx_page_context = pageContext;");
0599:
0600: out.printil("application = pageContext.getServletContext();");
0601: out.printil("config = pageContext.getServletConfig();");
0602:
0603: if (pageInfo.isSession())
0604: out.printil("session = pageContext.getSession();");
0605: out.printil("out = pageContext.getOut();");
0606: out.printil("_jspx_out = out;");
0607: out.println();
0608: }
0609:
0610: /**
0611: * Generates an XML Prolog, which includes an XML declaration and
0612: * an XML doctype declaration.
0613: */
0614: private void generateXmlProlog(Node.Nodes page) {
0615:
0616: /*
0617: * An XML declaration is generated under the following conditions:
0618: *
0619: * - 'omit-xml-declaration' attribute of <jsp:output> action is set to
0620: * "no" or "false"
0621: * - JSP document without a <jsp:root>
0622: */
0623: String omitXmlDecl = pageInfo.getOmitXmlDecl();
0624: if ((omitXmlDecl != null && !JspUtil.booleanValue(omitXmlDecl))
0625: || (omitXmlDecl == null && page.getRoot().isXmlSyntax()
0626: && !pageInfo.hasJspRoot() && !ctxt.isTagFile())) {
0627: String cType = pageInfo.getContentType();
0628: String charSet = cType
0629: .substring(cType.indexOf("charset=") + 8);
0630: out
0631: .printil("out.write(\"<?xml version=\\\"1.0\\\" encoding=\\\""
0632: + charSet + "\\\"?>\\n\");");
0633: }
0634:
0635: /*
0636: * Output a DOCTYPE declaration if the doctype-root-element appears.
0637: * If doctype-public appears:
0638: * <!DOCTYPE name PUBLIC "doctypePublic" "doctypeSystem">
0639: * else
0640: * <!DOCTYPE name SYSTEM "doctypeSystem" >
0641: */
0642:
0643: String doctypeName = pageInfo.getDoctypeName();
0644: if (doctypeName != null) {
0645: String doctypePublic = pageInfo.getDoctypePublic();
0646: String doctypeSystem = pageInfo.getDoctypeSystem();
0647: out.printin("out.write(\"<!DOCTYPE ");
0648: out.print(doctypeName);
0649: if (doctypePublic == null) {
0650: out.print(" SYSTEM \\\"");
0651: } else {
0652: out.print(" PUBLIC \\\"");
0653: out.print(doctypePublic);
0654: out.print("\\\" \\\"");
0655: }
0656: out.print(doctypeSystem);
0657: out.println("\\\">\\n\");");
0658: }
0659: }
0660:
0661: /*
0662: * Generates the constructor.
0663: * (shared by servlet and tag handler preamble generation)
0664: */
0665: private void generateConstructor(String className) {
0666: out.printil("public " + className + "() {");
0667: out.printil("}");
0668: out.println();
0669: }
0670:
0671: /**
0672: * A visitor that generates codes for the elements in the page.
0673: */
0674: class GenerateVisitor extends Node.Visitor {
0675:
0676: /*
0677: * Hashtable containing introspection information on tag handlers:
0678: * <key>: tag prefix
0679: * <value>: hashtable containing introspection on tag handlers:
0680: * <key>: tag short name
0681: * <value>: introspection info of tag handler for
0682: * <prefix:shortName> tag
0683: */
0684: private Hashtable handlerInfos;
0685:
0686: private Hashtable tagVarNumbers;
0687: private String parent;
0688: private boolean isSimpleTagParent; // Is parent a SimpleTag?
0689: private String pushBodyCountVar;
0690: private String simpleTagHandlerVar;
0691: private boolean isSimpleTagHandler;
0692: private boolean isFragment;
0693: private boolean isTagFile;
0694: private ServletWriter out;
0695: private ArrayList methodsBuffered;
0696: private FragmentHelperClass fragmentHelperClass;
0697: private int methodNesting;
0698: private TagInfo tagInfo;
0699: private ClassLoader loader;
0700: private int charArrayCount;
0701: private HashMap textMap;
0702:
0703: /**
0704: * Constructor.
0705: */
0706: public GenerateVisitor(boolean isTagFile, ServletWriter out,
0707: ArrayList methodsBuffered,
0708: FragmentHelperClass fragmentHelperClass,
0709: ClassLoader loader, TagInfo tagInfo) {
0710:
0711: this .isTagFile = isTagFile;
0712: this .out = out;
0713: this .methodsBuffered = methodsBuffered;
0714: this .fragmentHelperClass = fragmentHelperClass;
0715: this .loader = loader;
0716: this .tagInfo = tagInfo;
0717: methodNesting = 0;
0718: handlerInfos = new Hashtable();
0719: tagVarNumbers = new Hashtable();
0720: textMap = new HashMap();
0721: }
0722:
0723: /**
0724: * Returns an attribute value, optionally URL encoded. If
0725: * the value is a runtime expression, the result is the expression
0726: * itself, as a string. If the result is an EL expression, we insert
0727: * a call to the interpreter. If the result is a Named Attribute
0728: * we insert the generated variable name. Otherwise the result is a
0729: * string literal, quoted and escaped.
0730: *
0731: * @param attr An JspAttribute object
0732: * @param encode true if to be URL encoded
0733: * @param expectedType the expected type for an EL evaluation
0734: * (ignored for attributes that aren't EL expressions)
0735: */
0736: private String attributeValue(Node.JspAttribute attr,
0737: boolean encode, Class expectedType) {
0738: String v = attr.getValue();
0739: if (!attr.isNamedAttribute() && (v == null))
0740: return "";
0741:
0742: if (attr.isExpression()) {
0743: if (encode) {
0744: return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf("
0745: + v + "), request.getCharacterEncoding())";
0746: }
0747: return v;
0748: } else if (attr.isELInterpreterInput()) {
0749: boolean replaceESC = v.indexOf(Constants.ESC) > 0;
0750: v = JspUtil.interpreterCall(this .isTagFile, v,
0751: expectedType, attr.getEL().getMapName(), false);
0752: // XXX ESC replacement hack
0753: if (replaceESC) {
0754: v = "(" + v + ").replace(" + Constants.ESCStr
0755: + ", '$')";
0756: }
0757: if (encode) {
0758: return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("
0759: + v + ", request.getCharacterEncoding())";
0760: }
0761: return v;
0762: } else if (attr.isNamedAttribute()) {
0763: return attr.getNamedAttributeNode()
0764: .getTemporaryVariableName();
0765: } else {
0766: if (encode) {
0767: return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("
0768: + quote(v)
0769: + ", request.getCharacterEncoding())";
0770: }
0771: return quote(v);
0772: }
0773: }
0774:
0775: /**
0776: * Prints the attribute value specified in the param action, in the
0777: * form of name=value string.
0778: *
0779: * @param n the parent node for the param action nodes.
0780: */
0781: private void printParams(Node n, String pageParam,
0782: boolean literal) throws JasperException {
0783:
0784: class ParamVisitor extends Node.Visitor {
0785: String separator;
0786:
0787: ParamVisitor(String separator) {
0788: this .separator = separator;
0789: }
0790:
0791: public void visit(Node.ParamAction n)
0792: throws JasperException {
0793:
0794: out.print(" + ");
0795: out.print(separator);
0796: out.print(" + ");
0797: out
0798: .print("org.apache.jasper.runtime.JspRuntimeLibrary."
0799: + "URLEncode("
0800: + quote(n.getTextAttribute("name"))
0801: + ", request.getCharacterEncoding())");
0802: out.print("+ \"=\" + ");
0803: out.print(attributeValue(n.getValue(), true,
0804: String.class));
0805:
0806: // The separator is '&' after the second use
0807: separator = "\"&\"";
0808: }
0809: }
0810:
0811: String sep;
0812: if (literal) {
0813: sep = pageParam.indexOf('?') > 0 ? "\"&\"" : "\"?\"";
0814: } else {
0815: sep = "((" + pageParam + ").indexOf('?')>0? '&': '?')";
0816: }
0817: if (n.getBody() != null) {
0818: n.getBody().visit(new ParamVisitor(sep));
0819: }
0820: }
0821:
0822: public void visit(Node.Expression n) throws JasperException {
0823: n.setBeginJavaLine(out.getJavaLine());
0824: out.printin("out.print(");
0825: out.printMultiLn(n.getText());
0826: out.println(");");
0827: n.setEndJavaLine(out.getJavaLine());
0828: }
0829:
0830: public void visit(Node.Scriptlet n) throws JasperException {
0831: n.setBeginJavaLine(out.getJavaLine());
0832: out.printMultiLn(n.getText());
0833: out.println();
0834: n.setEndJavaLine(out.getJavaLine());
0835: }
0836:
0837: public void visit(Node.ELExpression n) throws JasperException {
0838: n.setBeginJavaLine(out.getJavaLine());
0839: if (!pageInfo.isELIgnored()) {
0840: out.printil("out.write("
0841: + JspUtil.interpreterCall(this .isTagFile, "${"
0842: + new String(n.getText()) + "}",
0843: String.class, n.getEL().getMapName(),
0844: false) + ");");
0845: } else {
0846: out.printil("out.write("
0847: + quote("${" + new String(n.getText()) + "}")
0848: + ");");
0849: }
0850: n.setEndJavaLine(out.getJavaLine());
0851: }
0852:
0853: public void visit(Node.IncludeAction n) throws JasperException {
0854:
0855: String flush = n.getTextAttribute("flush");
0856: Node.JspAttribute page = n.getPage();
0857:
0858: boolean isFlush = false; // default to false;
0859: if ("true".equals(flush))
0860: isFlush = true;
0861:
0862: n.setBeginJavaLine(out.getJavaLine());
0863:
0864: String pageParam;
0865: if (page.isNamedAttribute()) {
0866: // If the page for jsp:include was specified via
0867: // jsp:attribute, first generate code to evaluate
0868: // that body.
0869: pageParam = generateNamedAttributeValue(page
0870: .getNamedAttributeNode());
0871: } else {
0872: pageParam = attributeValue(page, false, String.class);
0873: }
0874:
0875: // If any of the params have their values specified by
0876: // jsp:attribute, prepare those values first.
0877: Node jspBody = findJspBody(n);
0878: if (jspBody != null) {
0879: prepareParams(jspBody);
0880: } else {
0881: prepareParams(n);
0882: }
0883:
0884: out
0885: .printin("org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "
0886: + pageParam);
0887: printParams(n, pageParam, page.isLiteral());
0888: out.println(", out, " + isFlush + ");");
0889:
0890: n.setEndJavaLine(out.getJavaLine());
0891: }
0892:
0893: /**
0894: * Scans through all child nodes of the given parent for
0895: * <param> subelements. For each <param> element, if its value
0896: * is specified via a Named Attribute (<jsp:attribute>),
0897: * generate the code to evaluate those bodies first.
0898: * <p>
0899: * If parent is null, simply returns.
0900: */
0901: private void prepareParams(Node parent) throws JasperException {
0902: if (parent == null)
0903: return;
0904:
0905: Node.Nodes subelements = parent.getBody();
0906: if (subelements != null) {
0907: for (int i = 0; i < subelements.size(); i++) {
0908: Node n = subelements.getNode(i);
0909: if (n instanceof Node.ParamAction) {
0910: Node.Nodes paramSubElements = n.getBody();
0911: for (int j = 0; (paramSubElements != null)
0912: && (j < paramSubElements.size()); j++) {
0913: Node m = paramSubElements.getNode(j);
0914: if (m instanceof Node.NamedAttribute) {
0915: generateNamedAttributeValue((Node.NamedAttribute) m);
0916: }
0917: }
0918: }
0919: }
0920: }
0921: }
0922:
0923: /**
0924: * Finds the <jsp:body> subelement of the given parent node.
0925: * If not found, null is returned.
0926: */
0927: private Node.JspBody findJspBody(Node parent)
0928: throws JasperException {
0929: Node.JspBody result = null;
0930:
0931: Node.Nodes subelements = parent.getBody();
0932: for (int i = 0; (subelements != null)
0933: && (i < subelements.size()); i++) {
0934: Node n = subelements.getNode(i);
0935: if (n instanceof Node.JspBody) {
0936: result = (Node.JspBody) n;
0937: break;
0938: }
0939: }
0940:
0941: return result;
0942: }
0943:
0944: public void visit(Node.ForwardAction n) throws JasperException {
0945: Node.JspAttribute page = n.getPage();
0946:
0947: n.setBeginJavaLine(out.getJavaLine());
0948:
0949: out.printil("if (true) {"); // So that javac won't complain about
0950: out.pushIndent(); // codes after "return"
0951:
0952: String pageParam;
0953: if (page.isNamedAttribute()) {
0954: // If the page for jsp:forward was specified via
0955: // jsp:attribute, first generate code to evaluate
0956: // that body.
0957: pageParam = generateNamedAttributeValue(page
0958: .getNamedAttributeNode());
0959: } else {
0960: pageParam = attributeValue(page, false, String.class);
0961: }
0962:
0963: // If any of the params have their values specified by
0964: // jsp:attribute, prepare those values first.
0965: Node jspBody = findJspBody(n);
0966: if (jspBody != null) {
0967: prepareParams(jspBody);
0968: } else {
0969: prepareParams(n);
0970: }
0971:
0972: out.printin("_jspx_page_context.forward(");
0973: out.print(pageParam);
0974: printParams(n, pageParam, page.isLiteral());
0975: out.println(");");
0976: if (isTagFile || isFragment) {
0977: out.printil("throw new SkipPageException();");
0978: } else {
0979: out.printil((methodNesting > 0) ? "return true;"
0980: : "return;");
0981: }
0982: out.popIndent();
0983: out.printil("}");
0984:
0985: n.setEndJavaLine(out.getJavaLine());
0986: // XXX Not sure if we can eliminate dead codes after this.
0987: }
0988:
0989: public void visit(Node.GetProperty n) throws JasperException {
0990: String name = n.getTextAttribute("name");
0991: String property = n.getTextAttribute("property");
0992:
0993: n.setBeginJavaLine(out.getJavaLine());
0994:
0995: if (beanInfo.checkVariable(name)) {
0996: // Bean is defined using useBean, introspect at compile time
0997: Class bean = beanInfo.getBeanType(name);
0998: String beanName = JspUtil.getCanonicalName(bean);
0999: java.lang.reflect.Method meth = JspRuntimeLibrary
1000: .getReadMethod(bean, property);
1001: String methodName = meth.getName();
1002: out
1003: .printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString("
1004: + "((("
1005: + beanName
1006: + ")_jspx_page_context.findAttribute("
1007: + "\""
1008: + name
1009: + "\"))."
1010: + methodName
1011: + "())));");
1012: } else {
1013: // The object could be a custom action with an associated
1014: // VariableInfo entry for this name.
1015: // Get the class name and then introspect at runtime.
1016: out
1017: .printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString"
1018: + "(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
1019: + "(_jspx_page_context.findAttribute(\""
1020: + name
1021: + "\"), \""
1022: + property
1023: + "\")));");
1024: }
1025:
1026: n.setEndJavaLine(out.getJavaLine());
1027: }
1028:
1029: public void visit(Node.SetProperty n) throws JasperException {
1030: String name = n.getTextAttribute("name");
1031: String property = n.getTextAttribute("property");
1032: String param = n.getTextAttribute("param");
1033: Node.JspAttribute value = n.getValue();
1034:
1035: n.setBeginJavaLine(out.getJavaLine());
1036:
1037: if ("*".equals(property)) {
1038: out
1039: .printil("org.apache.jasper.runtime.JspRuntimeLibrary.introspect("
1040: + "_jspx_page_context.findAttribute("
1041: + "\"" + name + "\"), request);");
1042: } else if (value == null) {
1043: if (param == null)
1044: param = property; // default to same as property
1045: out
1046: .printil("org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
1047: + "_jspx_page_context.findAttribute(\""
1048: + name
1049: + "\"), \""
1050: + property
1051: + "\", request.getParameter(\""
1052: + param
1053: + "\"), "
1054: + "request, \""
1055: + param
1056: + "\", false);");
1057: } else if (value.isExpression()) {
1058: out
1059: .printil("org.apache.jasper.runtime.JspRuntimeLibrary.handleSetProperty("
1060: + "_jspx_page_context.findAttribute(\""
1061: + name + "\"), \"" + property + "\",");
1062: out.print(attributeValue(value, false, null));
1063: out.println(");");
1064: } else if (value.isELInterpreterInput()) {
1065: // We've got to resolve the very call to the interpreter
1066: // at runtime since we don't know what type to expect
1067: // in the general case; we thus can't hard-wire the call
1068: // into the generated code. (XXX We could, however,
1069: // optimize the case where the bean is exposed with
1070: // <jsp:useBean>, much as the code here does for
1071: // getProperty.)
1072:
1073: // The following holds true for the arguments passed to
1074: // JspRuntimeLibrary.handleSetPropertyExpression():
1075: // - 'pageContext' is a VariableResolver.
1076: // - 'this' (either the generated Servlet or the generated tag
1077: // handler for Tag files) is a FunctionMapper.
1078: out
1079: .printil("org.apache.jasper.runtime.JspRuntimeLibrary.handleSetPropertyExpression("
1080: + "_jspx_page_context.findAttribute(\""
1081: + name
1082: + "\"), \""
1083: + property
1084: + "\", "
1085: + quote(value.getValue())
1086: + ", "
1087: + "_jspx_page_context, "
1088: + value.getEL().getMapName() + ");");
1089: } else if (value.isNamedAttribute()) {
1090: // If the value for setProperty was specified via
1091: // jsp:attribute, first generate code to evaluate
1092: // that body.
1093: String valueVarName = generateNamedAttributeValue(value
1094: .getNamedAttributeNode());
1095: out
1096: .printil("org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
1097: + "_jspx_page_context.findAttribute(\""
1098: + name
1099: + "\"), \""
1100: + property
1101: + "\", "
1102: + valueVarName
1103: + ", null, null, false);");
1104: } else {
1105: out
1106: .printin("org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper("
1107: + "_jspx_page_context.findAttribute(\""
1108: + name + "\"), \"" + property + "\", ");
1109: out.print(attributeValue(value, false, null));
1110: out.println(", null, null, false);");
1111: }
1112:
1113: n.setEndJavaLine(out.getJavaLine());
1114: }
1115:
1116: public void visit(Node.UseBean n) throws JasperException {
1117:
1118: String name = n.getTextAttribute("id");
1119: String scope = n.getTextAttribute("scope");
1120: String klass = n.getTextAttribute("class");
1121: String type = n.getTextAttribute("type");
1122: Node.JspAttribute beanName = n.getBeanName();
1123:
1124: if (type == null) // if unspecified, use class as type of bean
1125: type = klass;
1126:
1127: String scopename = "PageContext.PAGE_SCOPE"; // Default to page
1128: String lock = "_jspx_page_context";
1129:
1130: if ("request".equals(scope)) {
1131: scopename = "PageContext.REQUEST_SCOPE";
1132: lock = "request";
1133: } else if ("session".equals(scope)) {
1134: scopename = "PageContext.SESSION_SCOPE";
1135: lock = "session";
1136: } else if ("application".equals(scope)) {
1137: scopename = "PageContext.APPLICATION_SCOPE";
1138: lock = "application";
1139: }
1140:
1141: n.setBeginJavaLine(out.getJavaLine());
1142:
1143: // Declare bean
1144: out.printin(type);
1145: out.print(' ');
1146: out.print(name);
1147: out.println(" = null;");
1148:
1149: // Lock while getting or creating bean
1150: out.printin("synchronized (");
1151: out.print(lock);
1152: out.println(") {");
1153: out.pushIndent();
1154:
1155: // Locate bean from context
1156: out.printin(name);
1157: out.print(" = (");
1158: out.print(type);
1159: out.print(") _jspx_page_context.getAttribute(");
1160: out.print(quote(name));
1161: out.print(", ");
1162: out.print(scopename);
1163: out.println(");");
1164:
1165: // Create bean
1166: /*
1167: * Check if bean is alredy there
1168: */
1169: out.printin("if (");
1170: out.print(name);
1171: out.println(" == null){");
1172: out.pushIndent();
1173: if (klass == null && beanName == null) {
1174: /*
1175: * If both class name and beanName is not specified, the bean
1176: * must be found locally, otherwise it's an error
1177: */
1178: out
1179: .printin("throw new java.lang.InstantiationException(\"bean ");
1180: out.print(name);
1181: out.println(" not found within scope\");");
1182: } else {
1183: /*
1184: * Instantiate the bean if it is not in the specified scope.
1185: */
1186: boolean generateNew = false;
1187: if (beanName == null) {
1188: try {
1189: Class bean = ctxt.getClassLoader().loadClass(
1190: klass);
1191: int modifiers = bean.getModifiers();
1192: if (!Modifier.isPublic(modifiers)
1193: || Modifier.isInterface(modifiers)
1194: || Modifier.isAbstract(modifiers)) {
1195: throw new Exception(
1196: "Invalid bean class modifier");
1197: }
1198: // Check that there is a 0 arg constructor
1199: bean.getConstructor(new Class[] {});
1200: generateNew = true;
1201: } catch (Exception e) {
1202: // Cannot instantiate the specified class
1203: if (ctxt
1204: .getOptions()
1205: .getErrorOnUseBeanInvalidClassAttribute()) {
1206: err.jspError(n, "jsp.error.invalid.bean",
1207: klass);
1208: }
1209: }
1210: }
1211: if (!generateNew) {
1212: String className;
1213: if (beanName != null) {
1214: if (beanName.isNamedAttribute()) {
1215: // If the value for beanName was specified via
1216: // jsp:attribute, first generate code to evaluate
1217: // that body.
1218: className = generateNamedAttributeValue(beanName
1219: .getNamedAttributeNode());
1220: } else {
1221: className = attributeValue(beanName, false,
1222: String.class);
1223: }
1224: } else {
1225: // Implies klass is not null
1226: className = quote(klass);
1227: }
1228: out.printil("try {");
1229: out.pushIndent();
1230: out.printin(name);
1231: out.print(" = (");
1232: out.print(type);
1233: out.print(") java.beans.Beans.instantiate(");
1234: out.print("this.getClass().getClassLoader(), ");
1235: out.print(className);
1236: out.println(");");
1237: out.popIndent();
1238: /*
1239: * Note: Beans.instantiate throws ClassNotFoundException
1240: * if the bean class is abstract.
1241: */
1242: out
1243: .printil("} catch (ClassNotFoundException exc) {");
1244: out.pushIndent();
1245: out
1246: .printil("throw new InstantiationException(exc.getMessage());");
1247: out.popIndent();
1248: out.printil("} catch (Exception exc) {");
1249: out.pushIndent();
1250: out.printin("throw new ServletException(");
1251: out.print("\"Cannot create bean of class \" + ");
1252: out.print(className);
1253: out.println(", exc);");
1254: out.popIndent();
1255: out.printil("}"); // close of try
1256: } else {
1257: // Implies klass is not null
1258: // Generate codes to instantiate the bean class
1259: out.printin(name);
1260: out.print(" = new ");
1261: out.print(klass);
1262: out.println("();");
1263: }
1264: /*
1265: * Set attribute for bean in the specified scope
1266: */
1267: out.printin("_jspx_page_context.setAttribute(");
1268: out.print(quote(name));
1269: out.print(", ");
1270: out.print(name);
1271: out.print(", ");
1272: out.print(scopename);
1273: out.println(");");
1274:
1275: // Only visit the body when bean is instantiated
1276: visitBody(n);
1277: }
1278: out.popIndent();
1279: out.printil("}");
1280:
1281: // End of lock block
1282: out.popIndent();
1283: out.printil("}");
1284:
1285: n.setEndJavaLine(out.getJavaLine());
1286: }
1287:
1288: /**
1289: * @return a string for the form 'attr = "value"'
1290: */
1291: private String makeAttr(String attr, String value) {
1292: if (value == null)
1293: return "";
1294:
1295: return " " + attr + "=\"" + value + '\"';
1296: }
1297:
1298: public void visit(Node.PlugIn n) throws JasperException {
1299:
1300: /**
1301: * A visitor to handle <jsp:param> in a plugin
1302: */
1303: class ParamVisitor extends Node.Visitor {
1304:
1305: private boolean ie;
1306:
1307: ParamVisitor(boolean ie) {
1308: this .ie = ie;
1309: }
1310:
1311: public void visit(Node.ParamAction n)
1312: throws JasperException {
1313:
1314: String name = n.getTextAttribute("name");
1315: if (name.equalsIgnoreCase("object"))
1316: name = "java_object";
1317: else if (name.equalsIgnoreCase("type"))
1318: name = "java_type";
1319:
1320: n.setBeginJavaLine(out.getJavaLine());
1321: // XXX - Fixed a bug here - value used to be output
1322: // inline, which is only okay if value is not an EL
1323: // expression. Also, key/value pairs for the
1324: // embed tag were not being generated correctly.
1325: // Double check that this is now the correct behavior.
1326: if (ie) {
1327: // We want something of the form
1328: // out.println( "<PARAM name=\"blah\"
1329: // value=\"" + ... + "\">" );
1330: out.printil("out.write( \"<PARAM name=\\\""
1331: + escape(name)
1332: + "\\\" value=\\\"\" + "
1333: + attributeValue(n.getValue(), false,
1334: String.class)
1335: + " + \"\\\">\" );");
1336: out.printil("out.write(\"\\n\");");
1337: } else {
1338: // We want something of the form
1339: // out.print( " blah=\"" + ... + "\"" );
1340: out.printil("out.write( \" "
1341: + escape(name)
1342: + "=\\\"\" + "
1343: + attributeValue(n.getValue(), false,
1344: String.class)
1345: + " + \"\\\"\" );");
1346: }
1347:
1348: n.setEndJavaLine(out.getJavaLine());
1349: }
1350: }
1351:
1352: String type = n.getTextAttribute("type");
1353: String code = n.getTextAttribute("code");
1354: String name = n.getTextAttribute("name");
1355: Node.JspAttribute height = n.getHeight();
1356: Node.JspAttribute width = n.getWidth();
1357: String hspace = n.getTextAttribute("hspace");
1358: String vspace = n.getTextAttribute("vspace");
1359: String align = n.getTextAttribute("align");
1360: String iepluginurl = n.getTextAttribute("iepluginurl");
1361: String nspluginurl = n.getTextAttribute("nspluginurl");
1362: String codebase = n.getTextAttribute("codebase");
1363: String archive = n.getTextAttribute("archive");
1364: String jreversion = n.getTextAttribute("jreversion");
1365:
1366: String widthStr = null;
1367: if (width != null) {
1368: if (width.isNamedAttribute()) {
1369: widthStr = generateNamedAttributeValue(width
1370: .getNamedAttributeNode());
1371: } else {
1372: widthStr = attributeValue(width, false,
1373: String.class);
1374: }
1375: }
1376:
1377: String heightStr = null;
1378: if (height != null) {
1379: if (height.isNamedAttribute()) {
1380: heightStr = generateNamedAttributeValue(height
1381: .getNamedAttributeNode());
1382: } else {
1383: heightStr = attributeValue(height, false,
1384: String.class);
1385: }
1386: }
1387:
1388: if (iepluginurl == null)
1389: iepluginurl = Constants.IE_PLUGIN_URL;
1390: if (nspluginurl == null)
1391: nspluginurl = Constants.NS_PLUGIN_URL;
1392:
1393: n.setBeginJavaLine(out.getJavaLine());
1394:
1395: // If any of the params have their values specified by
1396: // jsp:attribute, prepare those values first.
1397: // Look for a params node and prepare its param subelements:
1398: Node.JspBody jspBody = findJspBody(n);
1399: if (jspBody != null) {
1400: Node.Nodes subelements = jspBody.getBody();
1401: if (subelements != null) {
1402: for (int i = 0; i < subelements.size(); i++) {
1403: Node m = subelements.getNode(i);
1404: if (m instanceof Node.ParamsAction) {
1405: prepareParams(m);
1406: break;
1407: }
1408: }
1409: }
1410: }
1411:
1412: // XXX - Fixed a bug here - width and height can be set
1413: // dynamically. Double-check if this generation is correct.
1414:
1415: // IE style plugin
1416: // <OBJECT ...>
1417: // First compose the runtime output string
1418: String s0 = "<OBJECT"
1419: + makeAttr("classid", ctxt.getOptions()
1420: .getIeClassId()) + makeAttr("name", name);
1421:
1422: String s1 = "";
1423: if (width != null) {
1424: s1 = " + \" width=\\\"\" + " + widthStr + " + \"\\\"\"";
1425: }
1426:
1427: String s2 = "";
1428: if (height != null) {
1429: s2 = " + \" height=\\\"\" + " + heightStr
1430: + " + \"\\\"\"";
1431: }
1432:
1433: String s3 = makeAttr("hspace", hspace)
1434: + makeAttr("vspace", vspace)
1435: + makeAttr("align", align)
1436: + makeAttr("codebase", iepluginurl) + '>';
1437:
1438: // Then print the output string to the java file
1439: out.printil("out.write(" + quote(s0) + s1 + s2 + " + "
1440: + quote(s3) + ");");
1441: out.printil("out.write(\"\\n\");");
1442:
1443: // <PARAM > for java_code
1444: s0 = "<PARAM name=\"java_code\"" + makeAttr("value", code)
1445: + '>';
1446: out.printil("out.write(" + quote(s0) + ");");
1447: out.printil("out.write(\"\\n\");");
1448:
1449: // <PARAM > for java_codebase
1450: if (codebase != null) {
1451: s0 = "<PARAM name=\"java_codebase\""
1452: + makeAttr("value", codebase) + '>';
1453: out.printil("out.write(" + quote(s0) + ");");
1454: out.printil("out.write(\"\\n\");");
1455: }
1456:
1457: // <PARAM > for java_archive
1458: if (archive != null) {
1459: s0 = "<PARAM name=\"java_archive\""
1460: + makeAttr("value", archive) + '>';
1461: out.printil("out.write(" + quote(s0) + ");");
1462: out.printil("out.write(\"\\n\");");
1463: }
1464:
1465: // <PARAM > for type
1466: s0 = "<PARAM name=\"type\""
1467: + makeAttr("value", "application/x-java-"
1468: + type
1469: + ";"
1470: + ((jreversion == null) ? "" : "version="
1471: + jreversion)) + '>';
1472: out.printil("out.write(" + quote(s0) + ");");
1473: out.printil("out.write(\"\\n\");");
1474:
1475: /*
1476: * generate a <PARAM> for each <jsp:param> in the plugin body
1477: */
1478: if (n.getBody() != null)
1479: n.getBody().visit(new ParamVisitor(true));
1480:
1481: /*
1482: * Netscape style plugin part
1483: */
1484: out.printil("out.write(" + quote("<COMMENT>") + ");");
1485: out.printil("out.write(\"\\n\");");
1486: s0 = "<EMBED"
1487: + makeAttr("type", "application/x-java-"
1488: + type
1489: + ";"
1490: + ((jreversion == null) ? "" : "version="
1491: + jreversion))
1492: + makeAttr("name", name);
1493:
1494: // s1 and s2 are the same as before.
1495:
1496: s3 = makeAttr("hspace", hspace)
1497: + makeAttr("vspace", vspace)
1498: + makeAttr("align", align)
1499: + makeAttr("pluginspage", nspluginurl)
1500: + makeAttr("java_code", code)
1501: + makeAttr("java_codebase", codebase)
1502: + makeAttr("java_archive", archive);
1503: out.printil("out.write(" + quote(s0) + s1 + s2 + " + "
1504: + quote(s3) + ");");
1505:
1506: /*
1507: * Generate a 'attr = "value"' for each <jsp:param> in plugin body
1508: */
1509: if (n.getBody() != null)
1510: n.getBody().visit(new ParamVisitor(false));
1511:
1512: out.printil("out.write(" + quote("/>") + ");");
1513: out.printil("out.write(\"\\n\");");
1514:
1515: out.printil("out.write(" + quote("<NOEMBED>") + ");");
1516: out.printil("out.write(\"\\n\");");
1517:
1518: /*
1519: * Fallback
1520: */
1521: if (n.getBody() != null) {
1522: visitBody(n);
1523: out.printil("out.write(\"\\n\");");
1524: }
1525:
1526: out.printil("out.write(" + quote("</NOEMBED>") + ");");
1527: out.printil("out.write(\"\\n\");");
1528:
1529: out.printil("out.write(" + quote("</COMMENT>") + ");");
1530: out.printil("out.write(\"\\n\");");
1531:
1532: out.printil("out.write(" + quote("</OBJECT>") + ");");
1533: out.printil("out.write(\"\\n\");");
1534:
1535: n.setEndJavaLine(out.getJavaLine());
1536: }
1537:
1538: public void visit(Node.NamedAttribute n) throws JasperException {
1539: // Don't visit body of this tag - we already did earlier.
1540: }
1541:
1542: public void visit(Node.CustomTag n) throws JasperException {
1543:
1544: // Use plugin to generate more efficient code if there is one.
1545: if (n.useTagPlugin()) {
1546: generateTagPlugin(n);
1547: return;
1548: }
1549:
1550: TagHandlerInfo handlerInfo = getTagHandlerInfo(n);
1551:
1552: // Create variable names
1553: String baseVar = createTagVarName(n.getQName(), n
1554: .getPrefix(), n.getLocalName());
1555: String tagEvalVar = "_jspx_eval_" + baseVar;
1556: String tagHandlerVar = "_jspx_th_" + baseVar;
1557: String tagPushBodyCountVar = "_jspx_push_body_count_"
1558: + baseVar;
1559:
1560: // If the tag contains no scripting element, generate its codes
1561: // to a method.
1562: ServletWriter outSave = null;
1563: Node.ChildInfo ci = n.getChildInfo();
1564: if (ci.isScriptless() && !ci.hasScriptingVars()) {
1565: // The tag handler and its body code can reside in a separate
1566: // method if it is scriptless and does not have any scripting
1567: // variable defined.
1568:
1569: String tagMethod = "_jspx_meth_" + baseVar;
1570:
1571: // Generate a call to this method
1572: out.printin("if (");
1573: out.print(tagMethod);
1574: out.print("(");
1575: if (parent != null) {
1576: out.print(parent);
1577: out.print(", ");
1578: }
1579: out.print("_jspx_page_context");
1580: if (pushBodyCountVar != null) {
1581: out.print(", ");
1582: out.print(pushBodyCountVar);
1583: }
1584: out.println("))");
1585: out.pushIndent();
1586: out.printil((methodNesting > 0) ? "return true;"
1587: : "return;");
1588: out.popIndent();
1589:
1590: // Set up new buffer for the method
1591: outSave = out;
1592: /* For fragments, their bodies will be generated in fragment
1593: helper classes, and the Java line adjustments will be done
1594: there, hence they are set to null here to avoid double
1595: adjustments.
1596: */
1597: GenBuffer genBuffer = new GenBuffer(n, n
1598: .implements SimpleTag() ? null : n.getBody());
1599: methodsBuffered.add(genBuffer);
1600: out = genBuffer.getOut();
1601:
1602: methodNesting++;
1603: // Generate code for method declaration
1604: out.println();
1605: out.pushIndent();
1606: out.printin("private boolean ");
1607: out.print(tagMethod);
1608: out.print("(");
1609: if (parent != null) {
1610: out.print("javax.servlet.jsp.tagext.JspTag ");
1611: out.print(parent);
1612: out.print(", ");
1613: }
1614: out.print("PageContext _jspx_page_context");
1615: if (pushBodyCountVar != null) {
1616: out.print(", int[] ");
1617: out.print(pushBodyCountVar);
1618: }
1619: out.println(")");
1620: out.printil(" throws Throwable {");
1621: out.pushIndent();
1622:
1623: // Initilaize local variables used in this method.
1624: if (!isTagFile) {
1625: out
1626: .printil("PageContext pageContext = _jspx_page_context;");
1627: }
1628: out
1629: .printil("JspWriter out = _jspx_page_context.getOut();");
1630: generateLocalVariables(out, n);
1631: }
1632:
1633: if (n.implements SimpleTag()) {
1634: generateCustomDoTag(n, handlerInfo, tagHandlerVar);
1635: } else {
1636: /*
1637: * Classic tag handler: Generate code for start element, body,
1638: * and end element
1639: */
1640: generateCustomStart(n, handlerInfo, tagHandlerVar,
1641: tagEvalVar, tagPushBodyCountVar);
1642:
1643: // visit body
1644: String tmpParent = parent;
1645: parent = tagHandlerVar;
1646: boolean isSimpleTagParentSave = isSimpleTagParent;
1647: isSimpleTagParent = false;
1648: String tmpPushBodyCountVar = null;
1649: if (n.implements TryCatchFinally()) {
1650: tmpPushBodyCountVar = pushBodyCountVar;
1651: pushBodyCountVar = tagPushBodyCountVar;
1652: }
1653: boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
1654: isSimpleTagHandler = false;
1655:
1656: visitBody(n);
1657:
1658: parent = tmpParent;
1659: isSimpleTagParent = isSimpleTagParentSave;
1660: if (n.implements TryCatchFinally()) {
1661: pushBodyCountVar = tmpPushBodyCountVar;
1662: }
1663: isSimpleTagHandler = tmpIsSimpleTagHandler;
1664:
1665: generateCustomEnd(n, tagHandlerVar, tagEvalVar,
1666: tagPushBodyCountVar);
1667: }
1668:
1669: if (ci.isScriptless() && !ci.hasScriptingVars()) {
1670: // Generate end of method
1671: if (methodNesting > 0) {
1672: out.printil("return false;");
1673: }
1674: out.popIndent();
1675: out.printil("}");
1676: out.popIndent();
1677:
1678: methodNesting--;
1679:
1680: // restore previous writer
1681: out = outSave;
1682: }
1683: }
1684:
1685: private static final String SINGLE_QUOTE = "'";
1686: private static final String DOUBLE_QUOTE = "\\\"";
1687:
1688: public void visit(Node.UninterpretedTag n)
1689: throws JasperException {
1690:
1691: n.setBeginJavaLine(out.getJavaLine());
1692:
1693: /*
1694: * Write begin tag
1695: */
1696: out.printin("out.write(\"<");
1697: out.print(n.getQName());
1698:
1699: Attributes attrs = n.getNonTaglibXmlnsAttributes();
1700: int attrsLen = (attrs == null) ? 0 : attrs.getLength();
1701: for (int i = 0; i < attrsLen; i++) {
1702: out.print(" ");
1703: out.print(attrs.getQName(i));
1704: out.print("=");
1705: String quote = DOUBLE_QUOTE;
1706: String value = attrs.getValue(i);
1707: if (value.indexOf('"') != -1) {
1708: quote = SINGLE_QUOTE;
1709: }
1710: out.print(quote);
1711: out.print(value);
1712: out.print(quote);
1713: }
1714:
1715: attrs = n.getAttributes();
1716: attrsLen = (attrs == null) ? 0 : attrs.getLength();
1717: Node.JspAttribute[] jspAttrs = n.getJspAttributes();
1718: for (int i = 0; i < attrsLen; i++) {
1719: out.print(" ");
1720: out.print(attrs.getQName(i));
1721: out.print("=");
1722: if (jspAttrs[i].isELInterpreterInput()) {
1723: out.print("\\\"\" + ");
1724: out.print(attributeValue(jspAttrs[i], false,
1725: String.class));
1726: out.print(" + \"\\\"");
1727: } else {
1728: String quote = DOUBLE_QUOTE;
1729: String value = attrs.getValue(i);
1730: if (value.indexOf('"') != -1) {
1731: quote = SINGLE_QUOTE;
1732: }
1733: out.print(quote);
1734: out.print(value);
1735: out.print(quote);
1736: }
1737: }
1738:
1739: if (n.getBody() != null) {
1740: out.println(">\");");
1741:
1742: // Visit tag body
1743: visitBody(n);
1744:
1745: /*
1746: * Write end tag
1747: */
1748: out.printin("out.write(\"</");
1749: out.print(n.getQName());
1750: out.println(">\");");
1751: } else {
1752: out.println("/>\");");
1753: }
1754:
1755: n.setEndJavaLine(out.getJavaLine());
1756: }
1757:
1758: public void visit(Node.JspElement n) throws JasperException {
1759:
1760: n.setBeginJavaLine(out.getJavaLine());
1761:
1762: // Compute attribute value string for XML-style and named
1763: // attributes
1764: Hashtable map = new Hashtable();
1765: Node.JspAttribute[] attrs = n.getJspAttributes();
1766: for (int i = 0; attrs != null && i < attrs.length; i++) {
1767: String attrStr = null;
1768: if (attrs[i].isNamedAttribute()) {
1769: attrStr =
1770: generateNamedAttributeValue(
1771: attrs[i].getNamedAttributeNode());
1772: } else {
1773: attrStr = attributeValue(attrs[i], false, Object.class);
1774: }
1775: String s =
1776: " + \" "
1777: + attrs[i].getName()
1778: + "=\\\"\" + "
1779: + attrStr
1780: + " + \"\\\"\"";
1781: map.put(attrs[i].getName(), s);
1782: }
1783:
1784: // Write begin tag, using XML-style 'name' attribute as the
1785: // element name
1786: String elemName =
1787: attributeValue(n.getNameAttribute(), false, String.class);
1788: out.printin("out.write(\"<\"");
1789: out.print(" + " + elemName);
1790:
1791: // Write remaining attributes
1792: Enumeration enum = map.keys();
1793: while (enum.hasMoreElements()) {
1794: String attrName = (String)enum.nextElement();
1795: out.print((String)map.get(attrName));
1796: }
1797:
1798: // Does the <jsp:element> have nested tags other than
1799: // <jsp:attribute>
1800: boolean hasBody = false;
1801: Node.Nodes subelements = n.getBody();
1802: if (subelements != null) {
1803: for (int i = 0; i < subelements.size(); i++) {
1804: Node subelem = subelements.getNode(i);
1805: if (!(subelem instanceof Node.NamedAttribute)) {
1806: hasBody = true;
1807: break;
1808: }
1809: }
1810: }
1811: if (hasBody) {
1812: out.println(" + \">\");");
1813:
1814: // Visit tag body
1815: visitBody(n);
1816:
1817: // Write end tag
1818: out.printin("out.write(\"</\"");
1819: out.print(" + " + elemName);
1820: out.println(" + \">\");");
1821: } else {
1822: out.println(" + \"/>\");");
1823: }
1824:
1825: n.setEndJavaLine(out.getJavaLine());
1826: }
1827:
1828: public void visit(Node.TemplateText n) throws JasperException {
1829:
1830: String text = n.getText();
1831:
1832: int textSize = text.length();
1833: if (textSize == 0) {
1834: return;
1835: }
1836:
1837: if (textSize <= 3) {
1838: // Special case small text strings
1839: n.setBeginJavaLine(out.getJavaLine());
1840: int lineInc = 0;
1841: for (int i = 0; i < textSize; i++) {
1842: char ch = text.charAt(i);
1843: out.printil("out.write(" + quote(ch) + ");");
1844: if (i > 0) {
1845: n.addSmap(lineInc);
1846: }
1847: if (ch == '\n') {
1848: lineInc++;
1849: }
1850: }
1851: n.setEndJavaLine(out.getJavaLine());
1852: return;
1853: }
1854:
1855: if (ctxt.getOptions().genStringAsCharArray()) {
1856: // Generate Strings as char arrays, for performance
1857: ServletWriter caOut;
1858: if (charArrayBuffer == null) {
1859: charArrayBuffer = new GenBuffer();
1860: caOut = charArrayBuffer.getOut();
1861: caOut.pushIndent();
1862: textMap = new HashMap();
1863: } else {
1864: caOut = charArrayBuffer.getOut();
1865: }
1866: String charArrayName = (String) textMap.get(text);
1867: if (charArrayName == null) {
1868: charArrayName = "_jspx_char_array_"
1869: + charArrayCount++;
1870: textMap.put(text, charArrayName);
1871: caOut.printin("static char[] ");
1872: caOut.print(charArrayName);
1873: caOut.print(" = ");
1874: caOut.print(quote(text));
1875: caOut.println(".toCharArray();");
1876: }
1877:
1878: n.setBeginJavaLine(out.getJavaLine());
1879: out.printil("out.write(" + charArrayName + ");");
1880: n.setEndJavaLine(out.getJavaLine());
1881: return;
1882: }
1883:
1884: n.setBeginJavaLine(out.getJavaLine());
1885:
1886: out.printin();
1887: StringBuffer sb = new StringBuffer("out.write(\"");
1888: int initLength = sb.length();
1889: int count = JspUtil.CHUNKSIZE;
1890: int srcLine = 0; // relative to starting srouce line
1891: for (int i = 0; i < text.length(); i++) {
1892: char ch = text.charAt(i);
1893: --count;
1894: switch (ch) {
1895: case '"':
1896: sb.append('\\').append('\"');
1897: break;
1898: case '\\':
1899: sb.append('\\').append('\\');
1900: break;
1901: case '\r':
1902: sb.append('\\').append('r');
1903: break;
1904: case '\n':
1905: sb.append('\\').append('n');
1906: srcLine++;
1907:
1908: if (breakAtLF || count < 0) {
1909: // Generate an out.write() when see a '\n' in template
1910: sb.append("\");");
1911: out.println(sb.toString());
1912: if (i < text.length() - 1) {
1913: out.printin();
1914: }
1915: sb.setLength(initLength);
1916: count = JspUtil.CHUNKSIZE;
1917: }
1918: // add a Smap for this line
1919: n.addSmap(srcLine);
1920: break;
1921: case '\t': // Not sure we need this
1922: sb.append('\\').append('t');
1923: break;
1924: default:
1925: sb.append(ch);
1926: }
1927: }
1928:
1929: if (sb.length() > initLength) {
1930: sb.append("\");");
1931: out.println(sb.toString());
1932: }
1933:
1934: n.setEndJavaLine(out.getJavaLine());
1935: }
1936:
1937: public void visit(Node.JspBody n) throws JasperException {
1938: if (n.getBody() != null) {
1939: if (isSimpleTagHandler) {
1940: out.printin(simpleTagHandlerVar);
1941: out.print(".setJspBody(");
1942: generateJspFragment(n, simpleTagHandlerVar);
1943: out.println(");");
1944: } else {
1945: visitBody(n);
1946: }
1947: }
1948: }
1949:
1950: public void visit(Node.InvokeAction n) throws JasperException {
1951:
1952: n.setBeginJavaLine(out.getJavaLine());
1953:
1954: // Copy virtual page scope of tag file to page scope of invoking
1955: // page
1956: out
1957: .printil("((org.apache.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
1958: String varReaderAttr = n.getTextAttribute("varReader");
1959: String varAttr = n.getTextAttribute("var");
1960: if (varReaderAttr != null || varAttr != null) {
1961: out.printil("_jspx_sout = new java.io.StringWriter();");
1962: } else {
1963: out.printil("_jspx_sout = null;");
1964: }
1965:
1966: // Invoke fragment, unless fragment is null
1967: out.printin("if (");
1968: out.print(toGetterMethod(n.getTextAttribute("fragment")));
1969: out.println(" != null) {");
1970: out.pushIndent();
1971: out.printin(toGetterMethod(n.getTextAttribute("fragment")));
1972: out.println(".invoke(_jspx_sout);");
1973: out.popIndent();
1974: out.printil("}");
1975:
1976: // Store varReader in appropriate scope
1977: if (varReaderAttr != null || varAttr != null) {
1978: String scopeName = n.getTextAttribute("scope");
1979: out.printin("_jspx_page_context.setAttribute(");
1980: if (varReaderAttr != null) {
1981: out.print(quote(varReaderAttr));
1982: out
1983: .print(", new java.io.StringReader(_jspx_sout.toString())");
1984: } else {
1985: out.print(quote(varAttr));
1986: out.print(", _jspx_sout.toString()");
1987: }
1988: if (scopeName != null) {
1989: out.print(", ");
1990: out.print(getScopeConstant(scopeName));
1991: }
1992: out.println(");");
1993: }
1994:
1995: n.setEndJavaLine(out.getJavaLine());
1996: }
1997:
1998: public void visit(Node.DoBodyAction n) throws JasperException {
1999:
2000: n.setBeginJavaLine(out.getJavaLine());
2001:
2002: // Copy virtual page scope of tag file to page scope of invoking
2003: // page
2004: out
2005: .printil("((org.apache.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
2006:
2007: // Invoke body
2008: String varReaderAttr = n.getTextAttribute("varReader");
2009: String varAttr = n.getTextAttribute("var");
2010: if (varReaderAttr != null || varAttr != null) {
2011: out.printil("_jspx_sout = new java.io.StringWriter();");
2012: } else {
2013: out.printil("_jspx_sout = null;");
2014: }
2015: out.printil("if (getJspBody() != null)");
2016: out.pushIndent();
2017: out.printil("getJspBody().invoke(_jspx_sout);");
2018: out.popIndent();
2019:
2020: // Store varReader in appropriate scope
2021: if (varReaderAttr != null || varAttr != null) {
2022: String scopeName = n.getTextAttribute("scope");
2023: out.printin("_jspx_page_context.setAttribute(");
2024: if (varReaderAttr != null) {
2025: out.print(quote(varReaderAttr));
2026: out
2027: .print(", new java.io.StringReader(_jspx_sout.toString())");
2028: } else {
2029: out.print(quote(varAttr));
2030: out.print(", _jspx_sout.toString()");
2031: }
2032: if (scopeName != null) {
2033: out.print(", ");
2034: out.print(getScopeConstant(scopeName));
2035: }
2036: out.println(");");
2037: }
2038:
2039: n.setEndJavaLine(out.getJavaLine());
2040: }
2041:
2042: public void visit(Node.AttributeGenerator n)
2043: throws JasperException {
2044: Node.CustomTag tag = n.getTag();
2045: Node.JspAttribute[] attrs = tag.getJspAttributes();
2046: for (int i = 0; attrs != null && i < attrs.length; i++) {
2047: if (attrs[i].getName().equals(n.getName())) {
2048: out.print(evaluateAttribute(getTagHandlerInfo(tag),
2049: attrs[i], tag, null));
2050: break;
2051: }
2052: }
2053: }
2054:
2055: private TagHandlerInfo getTagHandlerInfo(Node.CustomTag n)
2056: throws JasperException {
2057: Hashtable handlerInfosByShortName = (Hashtable) handlerInfos
2058: .get(n.getPrefix());
2059: if (handlerInfosByShortName == null) {
2060: handlerInfosByShortName = new Hashtable();
2061: handlerInfos
2062: .put(n.getPrefix(), handlerInfosByShortName);
2063: }
2064: TagHandlerInfo handlerInfo = (TagHandlerInfo) handlerInfosByShortName
2065: .get(n.getLocalName());
2066: if (handlerInfo == null) {
2067: handlerInfo = new TagHandlerInfo(n, n
2068: .getTagHandlerClass(), err);
2069: handlerInfosByShortName.put(n.getLocalName(),
2070: handlerInfo);
2071: }
2072: return handlerInfo;
2073: }
2074:
2075: private void generateTagPlugin(Node.CustomTag n)
2076: throws JasperException {
2077: if (n.getAtSTag() != null) {
2078: n.getAtSTag().visit(this );
2079: }
2080: visitBody(n);
2081: if (n.getAtETag() != null) {
2082: n.getAtETag().visit(this );
2083: }
2084: }
2085:
2086: private void generateCustomStart(Node.CustomTag n,
2087: TagHandlerInfo handlerInfo, String tagHandlerVar,
2088: String tagEvalVar, String tagPushBodyCountVar)
2089: throws JasperException {
2090:
2091: Class tagHandlerClass = handlerInfo.getTagHandlerClass();
2092:
2093: out.printin("// ");
2094: out.println(n.getQName());
2095: n.setBeginJavaLine(out.getJavaLine());
2096:
2097: // Declare AT_BEGIN scripting variables
2098: declareScriptingVars(n, VariableInfo.AT_BEGIN);
2099: saveScriptingVars(n, VariableInfo.AT_BEGIN);
2100:
2101: String tagHandlerClassName = JspUtil
2102: .getCanonicalName(tagHandlerClass);
2103: out.printin(tagHandlerClassName);
2104: out.print(" ");
2105: out.print(tagHandlerVar);
2106: out.print(" = ");
2107: if (isPoolingEnabled) {
2108: out.print("(");
2109: out.print(tagHandlerClassName);
2110: out.print(") ");
2111: out.print(n.getTagHandlerPoolName());
2112: out.print(".get(");
2113: out.print(tagHandlerClassName);
2114: out.println(".class);");
2115: } else {
2116: out.print("new ");
2117: out.print(tagHandlerClassName);
2118: out.println("();");
2119: }
2120:
2121: generateSetters(n, tagHandlerVar, handlerInfo, false);
2122:
2123: if (n.implements TryCatchFinally()) {
2124: out.printin("int[] ");
2125: out.print(tagPushBodyCountVar);
2126: out.println(" = new int[] { 0 };");
2127: out.printil("try {");
2128: out.pushIndent();
2129: }
2130: out.printin("int ");
2131: out.print(tagEvalVar);
2132: out.print(" = ");
2133: out.print(tagHandlerVar);
2134: out.println(".doStartTag();");
2135:
2136: if (!n.implements BodyTag()) {
2137: // Synchronize AT_BEGIN scripting variables
2138: syncScriptingVars(n, VariableInfo.AT_BEGIN);
2139: }
2140:
2141: if (!n.hasEmptyBody()) {
2142: out.printin("if (");
2143: out.print(tagEvalVar);
2144: out
2145: .println(" != javax.servlet.jsp.tagext.Tag.SKIP_BODY) {");
2146: out.pushIndent();
2147:
2148: // Declare NESTED scripting variables
2149: declareScriptingVars(n, VariableInfo.NESTED);
2150: saveScriptingVars(n, VariableInfo.NESTED);
2151:
2152: if (n.implements BodyTag()) {
2153: out.printin("if (");
2154: out.print(tagEvalVar);
2155: out
2156: .println(" != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
2157: // Assume EVAL_BODY_BUFFERED
2158: out.pushIndent();
2159: out.printil("out = _jspx_page_context.pushBody();");
2160: if (n.implements TryCatchFinally()) {
2161: out.printin(tagPushBodyCountVar);
2162: out.println("[0]++;");
2163: } else if (pushBodyCountVar != null) {
2164: out.printin(pushBodyCountVar);
2165: out.println("[0]++;");
2166: }
2167: out.printin(tagHandlerVar);
2168: out
2169: .println(".setBodyContent((javax.servlet.jsp.tagext.BodyContent) out);");
2170: out.printin(tagHandlerVar);
2171: out.println(".doInitBody();");
2172:
2173: out.popIndent();
2174: out.printil("}");
2175:
2176: // Synchronize AT_BEGIN and NESTED scripting variables
2177: syncScriptingVars(n, VariableInfo.AT_BEGIN);
2178: syncScriptingVars(n, VariableInfo.NESTED);
2179:
2180: } else {
2181: // Synchronize NESTED scripting variables
2182: syncScriptingVars(n, VariableInfo.NESTED);
2183: }
2184:
2185: if (n.implements IterationTag()) {
2186: out.printil("do {");
2187: out.pushIndent();
2188: }
2189: }
2190: // Map the Java lines that handles start of custom tags to the
2191: // JSP line for this tag
2192: n.setEndJavaLine(out.getJavaLine());
2193: }
2194:
2195: private void generateCustomEnd(Node.CustomTag n,
2196: String tagHandlerVar, String tagEvalVar,
2197: String tagPushBodyCountVar) {
2198:
2199: if (!n.hasEmptyBody()) {
2200: if (n.implements IterationTag()) {
2201: out.printin("int evalDoAfterBody = ");
2202: out.print(tagHandlerVar);
2203: out.println(".doAfterBody();");
2204:
2205: // Synchronize AT_BEGIN and NESTED scripting variables
2206: syncScriptingVars(n, VariableInfo.AT_BEGIN);
2207: syncScriptingVars(n, VariableInfo.NESTED);
2208:
2209: out
2210: .printil("if (evalDoAfterBody != javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_AGAIN)");
2211: out.pushIndent();
2212: out.printil("break;");
2213: out.popIndent();
2214:
2215: out.popIndent();
2216: out.printil("} while (true);");
2217: }
2218:
2219: restoreScriptingVars(n, VariableInfo.NESTED);
2220:
2221: if (n.implements BodyTag()) {
2222: out.printin("if (");
2223: out.print(tagEvalVar);
2224: out
2225: .println(" != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE)");
2226: out.pushIndent();
2227: out.printil("out = _jspx_page_context.popBody();");
2228: if (n.implements TryCatchFinally()) {
2229: out.printin(tagPushBodyCountVar);
2230: out.println("[0]--;");
2231: } else if (pushBodyCountVar != null) {
2232: out.printin(pushBodyCountVar);
2233: out.println("[0]--;");
2234: }
2235: out.popIndent();
2236: }
2237:
2238: out.popIndent(); // EVAL_BODY
2239: out.printil("}");
2240: }
2241:
2242: out.printin("if (");
2243: out.print(tagHandlerVar);
2244: out
2245: .println(".doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE)");
2246: out.pushIndent();
2247: if (isTagFile || isFragment) {
2248: out.printil("throw new SkipPageException();");
2249: } else {
2250: out.printil((methodNesting > 0) ? "return true;"
2251: : "return;");
2252: }
2253: out.popIndent();
2254:
2255: // Synchronize AT_BEGIN scripting variables
2256: syncScriptingVars(n, VariableInfo.AT_BEGIN);
2257:
2258: // TryCatchFinally
2259: if (n.implements TryCatchFinally()) {
2260: out.popIndent(); // try
2261: out.printil("} catch (Throwable _jspx_exception) {");
2262: out.pushIndent();
2263:
2264: out.printin("while (");
2265: out.print(tagPushBodyCountVar);
2266: out.println("[0]-- > 0)");
2267: out.pushIndent();
2268: out.printil("out = _jspx_page_context.popBody();");
2269: out.popIndent();
2270:
2271: out.printin(tagHandlerVar);
2272: out.println(".doCatch(_jspx_exception);");
2273: out.popIndent();
2274: out.printil("} finally {");
2275: out.pushIndent();
2276: out.printin(tagHandlerVar);
2277: out.println(".doFinally();");
2278: }
2279:
2280: if (isPoolingEnabled) {
2281: out.printin(n.getTagHandlerPoolName());
2282: out.print(".reuse(");
2283: out.print(tagHandlerVar);
2284: out.println(");");
2285: }
2286:
2287: if (n.implements TryCatchFinally()) {
2288: out.popIndent();
2289: out.printil("}");
2290: }
2291:
2292: // Declare and synchronize AT_END scripting variables (must do this
2293: // outside the try/catch/finally block)
2294: declareScriptingVars(n, VariableInfo.AT_END);
2295: syncScriptingVars(n, VariableInfo.AT_END);
2296:
2297: restoreScriptingVars(n, VariableInfo.AT_BEGIN);
2298: }
2299:
2300: private void generateCustomDoTag(Node.CustomTag n,
2301: TagHandlerInfo handlerInfo, String tagHandlerVar)
2302: throws JasperException {
2303:
2304: Class tagHandlerClass = handlerInfo.getTagHandlerClass();
2305:
2306: n.setBeginJavaLine(out.getJavaLine());
2307: out.printin("// ");
2308: out.println(n.getQName());
2309:
2310: // Declare AT_BEGIN scripting variables
2311: declareScriptingVars(n, VariableInfo.AT_BEGIN);
2312: saveScriptingVars(n, VariableInfo.AT_BEGIN);
2313:
2314: String tagHandlerClassName = JspUtil
2315: .getCanonicalName(tagHandlerClass);
2316: out.printin(tagHandlerClassName);
2317: out.print(" ");
2318: out.print(tagHandlerVar);
2319: out.print(" = ");
2320: out.print("new ");
2321: out.print(tagHandlerClassName);
2322: out.println("();");
2323:
2324: generateSetters(n, tagHandlerVar, handlerInfo, true);
2325:
2326: // Set the body
2327: if (findJspBody(n) == null) {
2328: /*
2329: * Encapsulate body of custom tag invocation in JspFragment
2330: * and pass it to tag handler's setJspBody(), unless tag body
2331: * is empty
2332: */
2333: if (!n.hasEmptyBody()) {
2334: out.printin(tagHandlerVar);
2335: out.print(".setJspBody(");
2336: generateJspFragment(n, tagHandlerVar);
2337: out.println(");");
2338: }
2339: } else {
2340: /*
2341: * Body of tag is the body of the <jsp:body> element.
2342: * The visit method for that element is going to encapsulate
2343: * that element's body in a JspFragment and pass it to
2344: * the tag handler's setJspBody()
2345: */
2346: String tmpTagHandlerVar = simpleTagHandlerVar;
2347: simpleTagHandlerVar = tagHandlerVar;
2348: boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
2349: isSimpleTagHandler = true;
2350: visitBody(n);
2351: simpleTagHandlerVar = tmpTagHandlerVar;
2352: isSimpleTagHandler = tmpIsSimpleTagHandler;
2353: }
2354:
2355: out.printin(tagHandlerVar);
2356: out.println(".doTag();");
2357:
2358: restoreScriptingVars(n, VariableInfo.AT_BEGIN);
2359:
2360: // Synchronize AT_BEGIN scripting variables
2361: syncScriptingVars(n, VariableInfo.AT_BEGIN);
2362:
2363: // Declare and synchronize AT_END scripting variables
2364: declareScriptingVars(n, VariableInfo.AT_END);
2365: syncScriptingVars(n, VariableInfo.AT_END);
2366:
2367: n.setEndJavaLine(out.getJavaLine());
2368: }
2369:
2370: private void declareScriptingVars(Node.CustomTag n, int scope) {
2371:
2372: Vector vec = n.getScriptingVars(scope);
2373: if (vec != null) {
2374: for (int i = 0; i < vec.size(); i++) {
2375: Object elem = vec.elementAt(i);
2376: if (elem instanceof VariableInfo) {
2377: VariableInfo varInfo = (VariableInfo) elem;
2378: if (varInfo.getDeclare()) {
2379: out.printin(varInfo.getClassName());
2380: out.print(" ");
2381: out.print(varInfo.getVarName());
2382: out.println(" = null;");
2383: }
2384: } else {
2385: TagVariableInfo tagVarInfo = (TagVariableInfo) elem;
2386: if (tagVarInfo.getDeclare()) {
2387: String varName = tagVarInfo.getNameGiven();
2388: if (varName == null) {
2389: varName = n
2390: .getTagData()
2391: .getAttributeString(
2392: tagVarInfo
2393: .getNameFromAttribute());
2394: } else if (tagVarInfo
2395: .getNameFromAttribute() != null) {
2396: // alias
2397: continue;
2398: }
2399: out.printin(tagVarInfo.getClassName());
2400: out.print(" ");
2401: out.print(varName);
2402: out.println(" = null;");
2403: }
2404: }
2405: }
2406: }
2407: }
2408:
2409: /*
2410: * This method is called as part of the custom tag's start element.
2411: *
2412: * If the given custom tag has a custom nesting level greater than 0,
2413: * save the current values of its scripting variables to
2414: * temporary variables, so those values may be restored in the tag's
2415: * end element. This way, the scripting variables may be synchronized
2416: * by the given tag without affecting their original values.
2417: */
2418: private void saveScriptingVars(Node.CustomTag n, int scope) {
2419: if (n.getCustomNestingLevel() == 0) {
2420: return;
2421: }
2422:
2423: TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
2424: VariableInfo[] varInfos = n.getVariableInfos();
2425: if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
2426: return;
2427: }
2428:
2429: if (varInfos.length > 0) {
2430: for (int i = 0; i < varInfos.length; i++) {
2431: if (varInfos[i].getScope() != scope)
2432: continue;
2433: // If the scripting variable has been declared, skip codes
2434: // for saving and restoring it.
2435: if (n.getScriptingVars(scope).contains(varInfos[i]))
2436: continue;
2437: String varName = varInfos[i].getVarName();
2438: String tmpVarName = "_jspx_" + varName + "_"
2439: + n.getCustomNestingLevel();
2440: out.printin(tmpVarName);
2441: out.print(" = ");
2442: out.print(varName);
2443: out.println(";");
2444: }
2445: } else {
2446: for (int i = 0; i < tagVarInfos.length; i++) {
2447: if (tagVarInfos[i].getScope() != scope)
2448: continue;
2449: // If the scripting variable has been declared, skip codes
2450: // for saving and restoring it.
2451: if (n.getScriptingVars(scope).contains(
2452: tagVarInfos[i]))
2453: continue;
2454: String varName = tagVarInfos[i].getNameGiven();
2455: if (varName == null) {
2456: varName = n.getTagData().getAttributeString(
2457: tagVarInfos[i].getNameFromAttribute());
2458: } else if (tagVarInfos[i].getNameFromAttribute() != null) {
2459: // alias
2460: continue;
2461: }
2462: String tmpVarName = "_jspx_" + varName + "_"
2463: + n.getCustomNestingLevel();
2464: out.printin(tmpVarName);
2465: out.print(" = ");
2466: out.print(varName);
2467: out.println(";");
2468: }
2469: }
2470: }
2471:
2472: /*
2473: * This method is called as part of the custom tag's end element.
2474: *
2475: * If the given custom tag has a custom nesting level greater than 0,
2476: * restore its scripting variables to their original values that were
2477: * saved in the tag's start element.
2478: */
2479: private void restoreScriptingVars(Node.CustomTag n, int scope) {
2480: if (n.getCustomNestingLevel() == 0) {
2481: return;
2482: }
2483:
2484: TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
2485: VariableInfo[] varInfos = n.getVariableInfos();
2486: if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
2487: return;
2488: }
2489:
2490: if (varInfos.length > 0) {
2491: for (int i = 0; i < varInfos.length; i++) {
2492: if (varInfos[i].getScope() != scope)
2493: continue;
2494: // If the scripting variable has been declared, skip codes
2495: // for saving and restoring it.
2496: if (n.getScriptingVars(scope).contains(varInfos[i]))
2497: continue;
2498: String varName = varInfos[i].getVarName();
2499: String tmpVarName = "_jspx_" + varName + "_"
2500: + n.getCustomNestingLevel();
2501: out.printin(varName);
2502: out.print(" = ");
2503: out.print(tmpVarName);
2504: out.println(";");
2505: }
2506: } else {
2507: for (int i = 0; i < tagVarInfos.length; i++) {
2508: if (tagVarInfos[i].getScope() != scope)
2509: continue;
2510: // If the scripting variable has been declared, skip codes
2511: // for saving and restoring it.
2512: if (n.getScriptingVars(scope).contains(
2513: tagVarInfos[i]))
2514: continue;
2515: String varName = tagVarInfos[i].getNameGiven();
2516: if (varName == null) {
2517: varName = n.getTagData().getAttributeString(
2518: tagVarInfos[i].getNameFromAttribute());
2519: } else if (tagVarInfos[i].getNameFromAttribute() != null) {
2520: // alias
2521: continue;
2522: }
2523: String tmpVarName = "_jspx_" + varName + "_"
2524: + n.getCustomNestingLevel();
2525: out.printin(varName);
2526: out.print(" = ");
2527: out.print(tmpVarName);
2528: out.println(";");
2529: }
2530: }
2531: }
2532:
2533: /*
2534: * Synchronizes the scripting variables of the given custom tag for
2535: * the given scope.
2536: */
2537: private void syncScriptingVars(Node.CustomTag n, int scope) {
2538: TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
2539: VariableInfo[] varInfos = n.getVariableInfos();
2540:
2541: if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
2542: return;
2543: }
2544:
2545: if (varInfos.length > 0) {
2546: for (int i = 0; i < varInfos.length; i++) {
2547: if (varInfos[i].getScope() == scope) {
2548: out.printin(varInfos[i].getVarName());
2549: out.print(" = (");
2550: out.print(varInfos[i].getClassName());
2551: out
2552: .print(") _jspx_page_context.findAttribute(");
2553: out.print(quote(varInfos[i].getVarName()));
2554: out.println(");");
2555: }
2556: }
2557: } else {
2558: for (int i = 0; i < tagVarInfos.length; i++) {
2559: if (tagVarInfos[i].getScope() == scope) {
2560: String name = tagVarInfos[i].getNameGiven();
2561: if (name == null) {
2562: name = n.getTagData().getAttributeString(
2563: tagVarInfos[i]
2564: .getNameFromAttribute());
2565: } else if (tagVarInfos[i]
2566: .getNameFromAttribute() != null) {
2567: // alias
2568: continue;
2569: }
2570: out.printin(name);
2571: out.print(" = (");
2572: out.print(tagVarInfos[i].getClassName());
2573: out
2574: .print(") _jspx_page_context.findAttribute(");
2575: out.print(quote(name));
2576: out.println(");");
2577: }
2578: }
2579: }
2580: }
2581:
2582: /*
2583: * Creates a tag variable name by concatenating the given prefix and
2584: * shortName and endcoded to make the resultant string a valid Java
2585: * Identifier.
2586: */
2587: private String createTagVarName(String fullName, String prefix,
2588: String shortName) {
2589:
2590: String varName;
2591: synchronized (tagVarNumbers) {
2592: varName = prefix + "_" + shortName + "_";
2593: if (tagVarNumbers.get(fullName) != null) {
2594: Integer i = (Integer) tagVarNumbers.get(fullName);
2595: varName = varName + i.intValue();
2596: tagVarNumbers.put(fullName, new Integer(i
2597: .intValue() + 1));
2598: } else {
2599: tagVarNumbers.put(fullName, new Integer(1));
2600: varName = varName + "0";
2601: }
2602: }
2603: return JspUtil.makeXmlJavaIdentifier(varName);
2604: }
2605:
2606: private String evaluateAttribute(TagHandlerInfo handlerInfo,
2607: Node.JspAttribute attr, Node.CustomTag n,
2608: String tagHandlerVar) throws JasperException {
2609:
2610: String attrValue = attr.getValue();
2611: if (attrValue == null) {
2612: if (attr.isNamedAttribute()) {
2613: if (n.checkIfAttributeIsJspFragment(attr.getName())) {
2614: // XXX - no need to generate temporary variable here
2615: attrValue = generateNamedAttributeJspFragment(
2616: attr.getNamedAttributeNode(),
2617: tagHandlerVar);
2618: } else {
2619: attrValue = generateNamedAttributeValue(attr
2620: .getNamedAttributeNode());
2621: }
2622: } else {
2623: return null;
2624: }
2625: }
2626:
2627: String localName = attr.getLocalName();
2628:
2629: Method m = null;
2630: Class[] c = null;
2631: if (attr.isDynamic()) {
2632: c = OBJECT_CLASS;
2633: } else {
2634: m = handlerInfo.getSetterMethod(localName);
2635: if (m == null) {
2636: err.jspError(n, "jsp.error.unable.to_find_method",
2637: attr.getName());
2638: }
2639: c = m.getParameterTypes();
2640: // XXX assert(c.length > 0)
2641: }
2642:
2643: if (attr.isExpression()) {
2644: // Do nothing
2645: } else if (attr.isNamedAttribute()) {
2646: if (!n.checkIfAttributeIsJspFragment(attr.getName())
2647: && !attr.isDynamic()) {
2648: attrValue = convertString(c[0], attrValue,
2649: localName, handlerInfo
2650: .getPropertyEditorClass(localName),
2651: true);
2652: }
2653: } else if (attr.isELInterpreterInput()) {
2654: // run attrValue through the expression interpreter
2655: boolean replaceESC = attrValue.indexOf(Constants.ESC) > 0;
2656: attrValue = JspUtil.interpreterCall(this .isTagFile,
2657: attrValue, c[0], attr.getEL().getMapName(),
2658: false);
2659: // XXX hack: Replace ESC with '$'
2660: if (replaceESC) {
2661: attrValue = "(" + attrValue + ").replace("
2662: + Constants.ESCStr + ", '$')";
2663: }
2664: } else {
2665: attrValue = convertString(c[0], attrValue, localName,
2666: handlerInfo.getPropertyEditorClass(localName),
2667: false);
2668: }
2669: return attrValue;
2670: }
2671:
2672: /**
2673: * Generate code to create a map for the alias variables
2674: * @return the name of the map
2675: */
2676: private String generateAliasMap(Node.CustomTag n,
2677: String tagHandlerVar) throws JasperException {
2678:
2679: TagVariableInfo[] tagVars = n.getTagVariableInfos();
2680: String aliasMapVar = null;
2681:
2682: boolean aliasSeen = false;
2683: for (int i = 0; i < tagVars.length; i++) {
2684:
2685: String nameFrom = tagVars[i].getNameFromAttribute();
2686: if (nameFrom != null) {
2687: String aliasedName = n.getAttributeValue(nameFrom);
2688: if (aliasedName == null)
2689: continue;
2690:
2691: if (!aliasSeen) {
2692: out.printin("java.util.HashMap ");
2693: aliasMapVar = tagHandlerVar + "_aliasMap";
2694: out.print(aliasMapVar);
2695: out.println(" = new java.util.HashMap();");
2696: aliasSeen = true;
2697: }
2698: out.printin(aliasMapVar);
2699: out.print(".put(");
2700: out.print(quote(tagVars[i].getNameGiven()));
2701: out.print(", ");
2702: out.print(quote(aliasedName));
2703: out.println(");");
2704: }
2705: }
2706: return aliasMapVar;
2707: }
2708:
2709: private void generateSetters(Node.CustomTag n,
2710: String tagHandlerVar, TagHandlerInfo handlerInfo,
2711: boolean simpleTag) throws JasperException {
2712:
2713: // Set context
2714: if (simpleTag) {
2715: // Generate alias map
2716: String aliasMapVar = null;
2717: if (n.isTagFile()) {
2718: aliasMapVar = generateAliasMap(n, tagHandlerVar);
2719: }
2720: out.printin(tagHandlerVar);
2721: if (aliasMapVar == null) {
2722: out.println(".setJspContext(_jspx_page_context);");
2723: } else {
2724: out.print(".setJspContext(_jspx_page_context, ");
2725: out.print(aliasMapVar);
2726: out.println(");");
2727: }
2728: } else {
2729: out.printin(tagHandlerVar);
2730: out.println(".setPageContext(_jspx_page_context);");
2731: }
2732:
2733: // Set parent
2734: if (!simpleTag) {
2735: out.printin(tagHandlerVar);
2736: out.print(".setParent(");
2737: if (parent != null) {
2738: if (isSimpleTagParent) {
2739: out
2740: .print("new javax.servlet.jsp.tagext.TagAdapter(");
2741: out
2742: .print("(javax.servlet.jsp.tagext.SimpleTag) ");
2743: out.print(parent);
2744: out.println("));");
2745: } else {
2746: out.print("(javax.servlet.jsp.tagext.Tag) ");
2747: out.print(parent);
2748: out.println(");");
2749: }
2750: } else {
2751: out.println("null);");
2752: }
2753: } else {
2754: // The setParent() method need not be called if the value being
2755: // passed is null, since SimpleTag instances are not reused
2756: if (parent != null) {
2757: out.printin(tagHandlerVar);
2758: out.print(".setParent(");
2759: out.print(parent);
2760: out.println(");");
2761: }
2762: }
2763:
2764: Node.JspAttribute[] attrs = n.getJspAttributes();
2765: for (int i = 0; attrs != null && i < attrs.length; i++) {
2766: String attrValue = evaluateAttribute(handlerInfo,
2767: attrs[i], n, tagHandlerVar);
2768:
2769: if (attrs[i].isDynamic()) {
2770: out.printin(tagHandlerVar);
2771: out.print(".");
2772: out.print("setDynamicAttribute(");
2773: String uri = attrs[i].getURI();
2774: if ("".equals(uri) || (uri == null)) {
2775: out.print("null");
2776: } else {
2777: out.print("\"" + attrs[i].getURI() + "\"");
2778: }
2779: out.print(", \"");
2780: out.print(attrs[i].getLocalName());
2781: out.print("\", ");
2782: out.print(attrValue);
2783: out.println(");");
2784: } else {
2785: out.printin(tagHandlerVar);
2786: out.print(".");
2787: out.print(handlerInfo.getSetterMethod(
2788: attrs[i].getLocalName()).getName());
2789: out.print("(");
2790: out.print(attrValue);
2791: out.println(");");
2792: }
2793: }
2794: }
2795:
2796: /*
2797: * @param c The target class to which to coerce the given string
2798: * @param s The string value
2799: * @param attrName The name of the attribute whose value is being
2800: * supplied
2801: * @param propEditorClass The property editor for the given attribute
2802: * @param isNamedAttribute true if the given attribute is a named
2803: * attribute (that is, specified using the jsp:attribute standard
2804: * action), and false otherwise
2805: */
2806: private String convertString(Class c, String s,
2807: String attrName, Class propEditorClass,
2808: boolean isNamedAttribute) throws JasperException {
2809:
2810: String quoted = s;
2811: if (!isNamedAttribute) {
2812: quoted = quote(s);
2813: }
2814:
2815: if (propEditorClass != null) {
2816: String className = JspUtil.getCanonicalName(c);
2817: return "("
2818: + className
2819: + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor("
2820: + className + ".class, \"" + attrName + "\", "
2821: + quoted + ", "
2822: + JspUtil.getCanonicalName(propEditorClass)
2823: + ".class)";
2824: } else if (c == String.class) {
2825: return quoted;
2826: } else if (c == boolean.class) {
2827: return JspUtil.coerceToPrimitiveBoolean(s,
2828: isNamedAttribute);
2829: } else if (c == Boolean.class) {
2830: return JspUtil.coerceToBoolean(s, isNamedAttribute);
2831: } else if (c == byte.class) {
2832: return JspUtil.coerceToPrimitiveByte(s,
2833: isNamedAttribute);
2834: } else if (c == Byte.class) {
2835: return JspUtil.coerceToByte(s, isNamedAttribute);
2836: } else if (c == char.class) {
2837: return JspUtil.coerceToChar(s, isNamedAttribute);
2838: } else if (c == Character.class) {
2839: return JspUtil.coerceToCharacter(s, isNamedAttribute);
2840: } else if (c == double.class) {
2841: return JspUtil.coerceToPrimitiveDouble(s,
2842: isNamedAttribute);
2843: } else if (c == Double.class) {
2844: return JspUtil.coerceToDouble(s, isNamedAttribute);
2845: } else if (c == float.class) {
2846: return JspUtil.coerceToPrimitiveFloat(s,
2847: isNamedAttribute);
2848: } else if (c == Float.class) {
2849: return JspUtil.coerceToFloat(s, isNamedAttribute);
2850: } else if (c == int.class) {
2851: return JspUtil.coerceToInt(s, isNamedAttribute);
2852: } else if (c == Integer.class) {
2853: return JspUtil.coerceToInteger(s, isNamedAttribute);
2854: } else if (c == short.class) {
2855: return JspUtil.coerceToPrimitiveShort(s,
2856: isNamedAttribute);
2857: } else if (c == Short.class) {
2858: return JspUtil.coerceToShort(s, isNamedAttribute);
2859: } else if (c == long.class) {
2860: return JspUtil.coerceToPrimitiveLong(s,
2861: isNamedAttribute);
2862: } else if (c == Long.class) {
2863: return JspUtil.coerceToLong(s, isNamedAttribute);
2864: } else if (c == Object.class) {
2865: return "new String(" + quoted + ")";
2866: } else {
2867: String className = JspUtil.getCanonicalName(c);
2868: return "("
2869: + className
2870: + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager("
2871: + className + ".class, \"" + attrName + "\", "
2872: + quoted + ")";
2873: }
2874: }
2875:
2876: /*
2877: * Converts the scope string representation, whose possible values
2878: * are "page", "request", "session", and "application", to the
2879: * corresponding scope constant.
2880: */
2881: private String getScopeConstant(String scope) {
2882: String scopeName = "PageContext.PAGE_SCOPE"; // Default to page
2883:
2884: if ("request".equals(scope)) {
2885: scopeName = "PageContext.REQUEST_SCOPE";
2886: } else if ("session".equals(scope)) {
2887: scopeName = "PageContext.SESSION_SCOPE";
2888: } else if ("application".equals(scope)) {
2889: scopeName = "PageContext.APPLICATION_SCOPE";
2890: }
2891:
2892: return scopeName;
2893: }
2894:
2895: /**
2896: * Generates anonymous JspFragment inner class which is passed as an
2897: * argument to SimpleTag.setJspBody().
2898: */
2899: private void generateJspFragment(Node n, String tagHandlerVar)
2900: throws JasperException {
2901: // XXX - A possible optimization here would be to check to see
2902: // if the only child of the parent node is TemplateText. If so,
2903: // we know there won't be any parameters, etc, so we can
2904: // generate a low-overhead JspFragment that just echoes its
2905: // body. The implementation of this fragment can come from
2906: // the org.apache.jasper.runtime package as a support class.
2907: FragmentHelperClass.Fragment fragment = fragmentHelperClass
2908: .openFragment(n, tagHandlerVar, methodNesting);
2909: ServletWriter outSave = out;
2910: out = fragment.getGenBuffer().getOut();
2911: String tmpParent = parent;
2912: parent = "_jspx_parent";
2913: boolean isSimpleTagParentSave = isSimpleTagParent;
2914: isSimpleTagParent = true;
2915: boolean tmpIsFragment = isFragment;
2916: isFragment = true;
2917: String pushBodyCountVarSave = pushBodyCountVar;
2918: if (pushBodyCountVar != null) {
2919: // Use a fixed name for push body count, to simplify code gen
2920: pushBodyCountVar = "_jspx_push_body_count";
2921: }
2922: visitBody(n);
2923: out = outSave;
2924: parent = tmpParent;
2925: isSimpleTagParent = isSimpleTagParentSave;
2926: isFragment = tmpIsFragment;
2927: pushBodyCountVar = pushBodyCountVarSave;
2928: fragmentHelperClass.closeFragment(fragment, methodNesting);
2929: // XXX - Need to change pageContext to jspContext if
2930: // we're not in a place where pageContext is defined (e.g.
2931: // in a fragment or in a tag file.
2932: out.print("new " + fragmentHelperClass.getClassName()
2933: + "( " + fragment.getId()
2934: + ", _jspx_page_context, " + tagHandlerVar + ", "
2935: + pushBodyCountVar + ")");
2936: }
2937:
2938: /**
2939: * Generate the code required to obtain the runtime value of the
2940: * given named attribute.
2941: *
2942: * @return The name of the temporary variable the result is stored in.
2943: */
2944: public String generateNamedAttributeValue(Node.NamedAttribute n)
2945: throws JasperException {
2946:
2947: String varName = n.getTemporaryVariableName();
2948:
2949: // If the only body element for this named attribute node is
2950: // template text, we need not generate an extra call to
2951: // pushBody and popBody. Maybe we can further optimize
2952: // here by getting rid of the temporary variable, but in
2953: // reality it looks like javac does this for us.
2954: Node.Nodes body = n.getBody();
2955: if (body != null) {
2956: boolean templateTextOptimization = false;
2957: if (body.size() == 1) {
2958: Node bodyElement = body.getNode(0);
2959: if (bodyElement instanceof Node.TemplateText) {
2960: templateTextOptimization = true;
2961: out
2962: .printil("String "
2963: + varName
2964: + " = "
2965: + quote(new String(
2966: ((Node.TemplateText) bodyElement)
2967: .getText()))
2968: + ";");
2969: }
2970: }
2971:
2972: // XXX - Another possible optimization would be for
2973: // lone EL expressions (no need to pushBody here either).
2974:
2975: if (!templateTextOptimization) {
2976: out.printil("out = _jspx_page_context.pushBody();");
2977: visitBody(n);
2978: out.printil("String " + varName + " = "
2979: + "((javax.servlet.jsp.tagext.BodyContent)"
2980: + "out).getString();");
2981: out.printil("out = _jspx_page_context.popBody();");
2982: }
2983: } else {
2984: // Empty body must be treated as ""
2985: out.printil("String " + varName + " = \"\";");
2986: }
2987:
2988: return varName;
2989: }
2990:
2991: /**
2992: * Similar to generateNamedAttributeValue, but create a JspFragment
2993: * instead.
2994: *
2995: * @param n The parent node of the named attribute
2996: * @param tagHandlerVar The variable the tag handler is stored in,
2997: * so the fragment knows its parent tag.
2998: * @return The name of the temporary variable the fragment
2999: * is stored in.
3000: */
3001: public String generateNamedAttributeJspFragment(
3002: Node.NamedAttribute n, String tagHandlerVar)
3003: throws JasperException {
3004: String varName = n.getTemporaryVariableName();
3005:
3006: out.printin("javax.servlet.jsp.tagext.JspFragment "
3007: + varName + " = ");
3008: generateJspFragment(n, tagHandlerVar);
3009: out.println(";");
3010:
3011: return varName;
3012: }
3013: }
3014:
3015: private static void generateLocalVariables(ServletWriter out, Node n)
3016: throws JasperException {
3017: Node.ChildInfo ci;
3018: if (n instanceof Node.CustomTag) {
3019: ci = ((Node.CustomTag) n).getChildInfo();
3020: } else if (n instanceof Node.JspBody) {
3021: ci = ((Node.JspBody) n).getChildInfo();
3022: } else if (n instanceof Node.NamedAttribute) {
3023: ci = ((Node.NamedAttribute) n).getChildInfo();
3024: } else {
3025: // Cannot access err since this method is static, but at
3026: // least flag an error.
3027: throw new JasperException("Unexpected Node Type");
3028: //err.getString(
3029: // "jsp.error.internal.unexpected_node_type" ) );
3030: }
3031:
3032: if (ci.hasUseBean()) {
3033: out
3034: .printil("HttpSession session = _jspx_page_context.getSession();");
3035: out
3036: .printil("ServletContext application = _jspx_page_context.getServletContext();");
3037: }
3038: if (ci.hasUseBean() || ci.hasIncludeAction()
3039: || ci.hasSetProperty() || ci.hasParamAction()) {
3040: out
3041: .printil("HttpServletRequest request = (HttpServletRequest)_jspx_page_context.getRequest();");
3042: }
3043: if (ci.hasIncludeAction()) {
3044: out
3045: .printil("HttpServletResponse response = (HttpServletResponse)_jspx_page_context.getResponse();");
3046: }
3047: }
3048:
3049: /**
3050: * Common part of postamble, shared by both servlets and tag files.
3051: */
3052: private void genCommonPostamble() {
3053: // Append any methods that were generated in the buffer.
3054: for (int i = 0; i < methodsBuffered.size(); i++) {
3055: GenBuffer methodBuffer = (GenBuffer) methodsBuffered.get(i);
3056: methodBuffer.adjustJavaLines(out.getJavaLine() - 1);
3057: out.printMultiLn(methodBuffer.toString());
3058: }
3059:
3060: // Append the helper class
3061: if (fragmentHelperClass.isUsed()) {
3062: fragmentHelperClass.generatePostamble();
3063: fragmentHelperClass.adjustJavaLines(out.getJavaLine() - 1);
3064: out.printMultiLn(fragmentHelperClass.toString());
3065: }
3066:
3067: // Append char array declarations
3068: if (charArrayBuffer != null) {
3069: out.printMultiLn(charArrayBuffer.toString());
3070: }
3071:
3072: // Close the class definition
3073: out.popIndent();
3074: out.printil("}");
3075: }
3076:
3077: /**
3078: * Generates the ending part of the static portion of the servlet.
3079: */
3080: private void generatePostamble(Node.Nodes page) {
3081: out.popIndent();
3082: out.printil("} catch (Throwable t) {");
3083: out.pushIndent();
3084: out.printil("if (!(t instanceof SkipPageException)){");
3085: out.pushIndent();
3086: out.printil("out = _jspx_out;");
3087: out.printil("if (out != null && out.getBufferSize() != 0)");
3088: out.pushIndent();
3089: out.printil("out.clearBuffer();");
3090: out.popIndent();
3091:
3092: out
3093: .printil("if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);");
3094: out.popIndent();
3095: out.printil("}");
3096: out.popIndent();
3097: out.printil("} finally {");
3098: out.pushIndent();
3099:
3100: out
3101: .printil("if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);");
3102:
3103: out.popIndent();
3104: out.printil("}");
3105:
3106: // Close the service method
3107: out.popIndent();
3108: out.printil("}");
3109:
3110: // Generated methods, helper classes, etc.
3111: genCommonPostamble();
3112: }
3113:
3114: /**
3115: * Constructor.
3116: */
3117: Generator(ServletWriter out, Compiler compiler) {
3118: this .out = out;
3119: methodsBuffered = new ArrayList();
3120: charArrayBuffer = null;
3121: err = compiler.getErrorDispatcher();
3122: ctxt = compiler.getCompilationContext();
3123: fragmentHelperClass = new FragmentHelperClass(ctxt
3124: .getServletClassName()
3125: + "Helper");
3126: pageInfo = compiler.getPageInfo();
3127:
3128: /*
3129: * Temporary hack. If a JSP page uses the "extends" attribute of the
3130: * page directive, the _jspInit() method of the generated servlet class
3131: * will not be called (it is only called for those generated servlets
3132: * that extend HttpJspBase, the default), causing the tag handler pools
3133: * not to be initialized and resulting in a NPE.
3134: * The JSP spec needs to clarify whether containers can override
3135: * init() and destroy(). For now, we just disable tag pooling for pages
3136: * that use "extends".
3137: */
3138: if (pageInfo.getExtends(false) == null) {
3139: isPoolingEnabled = ctxt.getOptions().isPoolingEnabled();
3140: } else {
3141: isPoolingEnabled = false;
3142: }
3143: beanInfo = pageInfo.getBeanRepository();
3144: breakAtLF = ctxt.getOptions().getMappedFile();
3145: if (isPoolingEnabled) {
3146: tagHandlerPoolNames = new Vector();
3147: }
3148: }
3149:
3150: /**
3151: * The main entry for Generator.
3152: * @param out The servlet output writer
3153: * @param compiler The compiler
3154: * @param page The input page
3155: */
3156: public static void generate(ServletWriter out, Compiler compiler,
3157: Node.Nodes page) throws JasperException {
3158:
3159: Generator gen = new Generator(out, compiler);
3160:
3161: if (gen.isPoolingEnabled) {
3162: gen.compileTagHandlerPoolList(page);
3163: }
3164: if (gen.ctxt.isTagFile()) {
3165: JasperTagInfo tagInfo = (JasperTagInfo) gen.ctxt
3166: .getTagInfo();
3167: gen.generateTagHandlerPreamble(tagInfo, page);
3168:
3169: if (gen.ctxt.isPrototypeMode()) {
3170: return;
3171: }
3172:
3173: gen.generateXmlProlog(page);
3174: gen.fragmentHelperClass.generatePreamble();
3175: page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(),
3176: out, gen.methodsBuffered, gen.fragmentHelperClass,
3177: gen.ctxt.getClassLoader(), tagInfo));
3178: gen.generateTagHandlerPostamble(tagInfo);
3179: } else {
3180: gen.generatePreamble(page);
3181: gen.generateXmlProlog(page);
3182: gen.fragmentHelperClass.generatePreamble();
3183: page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(),
3184: out, gen.methodsBuffered, gen.fragmentHelperClass,
3185: gen.ctxt.getClassLoader(), null));
3186: gen.generatePostamble(page);
3187: }
3188: }
3189:
3190: /*
3191: * Generates tag handler preamble.
3192: */
3193: private void generateTagHandlerPreamble(JasperTagInfo tagInfo,
3194: Node.Nodes tag) throws JasperException {
3195:
3196: // Generate package declaration
3197: String className = tagInfo.getTagClassName();
3198: int lastIndex = className.lastIndexOf('.');
3199: if (lastIndex != -1) {
3200: String pkgName = className.substring(0, lastIndex);
3201: genPreamblePackage(pkgName);
3202: className = className.substring(lastIndex + 1);
3203: }
3204:
3205: // Generate imports
3206: genPreambleImports();
3207:
3208: // Generate class declaration
3209: out.printin("public final class ");
3210: out.println(className);
3211: out
3212: .printil(" extends javax.servlet.jsp.tagext.SimpleTagSupport");
3213: out
3214: .printin(" implements org.apache.jasper.runtime.JspSourceDependent");
3215: if (tagInfo.hasDynamicAttributes()) {
3216: out.println(",");
3217: out
3218: .printin(" javax.servlet.jsp.tagext.DynamicAttributes");
3219: }
3220: out.println(" {");
3221: out.println();
3222: out.pushIndent();
3223:
3224: /*
3225: * Class body begins here
3226: */
3227:
3228: generateDeclarations(tag);
3229:
3230: // Static initializations here
3231: genPreambleStaticInitializers();
3232:
3233: out.printil("private JspContext jspContext;");
3234:
3235: // Declare writer used for storing result of fragment/body invocation
3236: // if 'varReader' or 'var' attribute is specified
3237: out.printil("private java.io.Writer _jspx_sout;");
3238:
3239: // Class variable declarations
3240: genPreambleClassVariableDeclarations(tagInfo.getTagName());
3241:
3242: generateSetJspContext(tagInfo);
3243:
3244: // Tag-handler specific declarations
3245: generateTagHandlerAttributes(tagInfo);
3246: if (tagInfo.hasDynamicAttributes())
3247: generateSetDynamicAttribute();
3248:
3249: // Methods here
3250: genPreambleMethods();
3251:
3252: // Now the doTag() method
3253: out
3254: .printil("public void doTag() throws JspException, java.io.IOException {");
3255:
3256: if (ctxt.isPrototypeMode()) {
3257: out.printil("}");
3258: out.popIndent();
3259: out.printil("}");
3260: return;
3261: }
3262:
3263: out.pushIndent();
3264:
3265: /*
3266: * According to the spec, 'pageContext' must not be made available as
3267: * an implicit object in tag files.
3268: * Declare _jspx_page_context, so we can share the code generator with
3269: * JSPs.
3270: */
3271: out
3272: .printil("PageContext _jspx_page_context = (PageContext)jspContext;");
3273:
3274: // Declare implicit objects.
3275: out
3276: .printil("HttpServletRequest request = "
3277: + "(HttpServletRequest) _jspx_page_context.getRequest();");
3278: out
3279: .printil("HttpServletResponse response = "
3280: + "(HttpServletResponse) _jspx_page_context.getResponse();");
3281: out
3282: .printil("HttpSession session = _jspx_page_context.getSession();");
3283: out
3284: .printil("ServletContext application = _jspx_page_context.getServletContext();");
3285: out
3286: .printil("ServletConfig config = _jspx_page_context.getServletConfig();");
3287: out.printil("JspWriter out = jspContext.getOut();");
3288: if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
3289: out.printil("_jspInit(config);");
3290: }
3291: generatePageScopedVariables(tagInfo);
3292:
3293: // Number of tag object that need to be popped
3294: // XXX TODO: use a better criteria
3295: maxTagNesting = pageInfo.getMaxTagNesting();
3296:
3297: declareTemporaryScriptingVars(tag);
3298: out.println();
3299:
3300: out.printil("try {");
3301: out.pushIndent();
3302: }
3303:
3304: private void generateTagHandlerPostamble(TagInfo tagInfo) {
3305: out.popIndent();
3306:
3307: // Have to catch Throwable because a classic tag handler
3308: // helper method is declared to throw Throwable.
3309: out.printil("} catch( Throwable t ) {");
3310: out.pushIndent();
3311: out.printil("if( t instanceof SkipPageException )");
3312: out.printil(" throw (SkipPageException) t;");
3313: out.printil("if( t instanceof java.io.IOException )");
3314: out.printil(" throw (java.io.IOException) t;");
3315: out.printil("if( t instanceof IllegalStateException )");
3316: out.printil(" throw (IllegalStateException) t;");
3317: out.printil("if( t instanceof JspException )");
3318: out.printil(" throw (JspException) t;");
3319: out.printil("throw new JspException(t);");
3320: out.popIndent();
3321: out.printil("} finally {");
3322: out.pushIndent();
3323: out
3324: .printil("((org.apache.jasper.runtime.JspContextWrapper) jspContext).syncEndTagFile();");
3325: if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
3326: out.printil("_jspDestroy();");
3327: }
3328: out.popIndent();
3329: out.printil("}");
3330:
3331: // Close the doTag method
3332: out.popIndent();
3333: out.printil("}");
3334:
3335: // Generated methods, helper classes, etc.
3336: genCommonPostamble();
3337: }
3338:
3339: /**
3340: * Generates declarations for tag handler attributes, and defines the
3341: * getter and setter methods for each.
3342: */
3343: private void generateTagHandlerAttributes(TagInfo tagInfo)
3344: throws JasperException {
3345:
3346: if (tagInfo.hasDynamicAttributes()) {
3347: out
3348: .printil("private java.util.HashMap _jspx_dynamic_attrs = new java.util.HashMap();");
3349: }
3350:
3351: // Declare attributes
3352: TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
3353: for (int i = 0; i < attrInfos.length; i++) {
3354: out.printin("private ");
3355: if (attrInfos[i].isFragment()) {
3356: out.print("javax.servlet.jsp.tagext.JspFragment ");
3357: } else {
3358: out.print(JspUtil.toJavaSourceType(attrInfos[i]
3359: .getTypeName()));
3360: out.print(" ");
3361: }
3362: out.print(attrInfos[i].getName());
3363: out.println(";");
3364: }
3365: out.println();
3366:
3367: // Define attribute getter and setter methods
3368: if (attrInfos != null) {
3369: for (int i = 0; i < attrInfos.length; i++) {
3370: // getter method
3371: out.printin("public ");
3372: if (attrInfos[i].isFragment()) {
3373: out.print("javax.servlet.jsp.tagext.JspFragment ");
3374: } else {
3375: out.print(JspUtil.toJavaSourceType(attrInfos[i]
3376: .getTypeName()));
3377: out.print(" ");
3378: }
3379: out.print(toGetterMethod(attrInfos[i].getName()));
3380: out.println(" {");
3381: out.pushIndent();
3382: out.printin("return this.");
3383: out.print(attrInfos[i].getName());
3384: out.println(";");
3385: out.popIndent();
3386: out.printil("}");
3387: out.println();
3388:
3389: // setter method
3390: out.printin("public void ");
3391: out.print(toSetterMethodName(attrInfos[i].getName()));
3392: if (attrInfos[i].isFragment()) {
3393: out.print("(javax.servlet.jsp.tagext.JspFragment ");
3394: } else {
3395: out.print("(");
3396: out.print(JspUtil.toJavaSourceType(attrInfos[i]
3397: .getTypeName()));
3398: out.print(" ");
3399: }
3400: out.print(attrInfos[i].getName());
3401: out.println(") {");
3402: out.pushIndent();
3403: out.printin("this.");
3404: out.print(attrInfos[i].getName());
3405: out.print(" = ");
3406: out.print(attrInfos[i].getName());
3407: out.println(";");
3408: out.popIndent();
3409: out.printil("}");
3410: out.println();
3411: }
3412: }
3413: }
3414:
3415: /*
3416: * Generate setter for JspContext so we can create a wrapper and
3417: * store both the original and the wrapper. We need the wrapper
3418: * to mask the page context from the tag file and simulate a
3419: * fresh page context. We need the original to do things like
3420: * sync AT_BEGIN and AT_END scripting variables.
3421: */
3422: private void generateSetJspContext(TagInfo tagInfo) {
3423:
3424: boolean nestedSeen = false;
3425: boolean atBeginSeen = false;
3426: boolean atEndSeen = false;
3427:
3428: // Determine if there are any aliases
3429: boolean aliasSeen = false;
3430: TagVariableInfo[] tagVars = tagInfo.getTagVariableInfos();
3431: for (int i = 0; i < tagVars.length; i++) {
3432: if (tagVars[i].getNameFromAttribute() != null
3433: && tagVars[i].getNameGiven() != null) {
3434: aliasSeen = true;
3435: break;
3436: }
3437: }
3438:
3439: if (aliasSeen) {
3440: out
3441: .printil("public void setJspContext(JspContext ctx, java.util.Map aliasMap) {");
3442: } else {
3443: out.printil("public void setJspContext(JspContext ctx) {");
3444: }
3445: out.pushIndent();
3446: out.printil("super.setJspContext(ctx);");
3447: out.printil("java.util.ArrayList _jspx_nested = null;");
3448: out.printil("java.util.ArrayList _jspx_at_begin = null;");
3449: out.printil("java.util.ArrayList _jspx_at_end = null;");
3450:
3451: for (int i = 0; i < tagVars.length; i++) {
3452:
3453: switch (tagVars[i].getScope()) {
3454: case VariableInfo.NESTED:
3455: if (!nestedSeen) {
3456: out
3457: .printil("_jspx_nested = new java.util.ArrayList();");
3458: nestedSeen = true;
3459: }
3460: out.printin("_jspx_nested.add(");
3461: break;
3462:
3463: case VariableInfo.AT_BEGIN:
3464: if (!atBeginSeen) {
3465: out
3466: .printil("_jspx_at_begin = new java.util.ArrayList();");
3467: atBeginSeen = true;
3468: }
3469: out.printin("_jspx_at_begin.add(");
3470: break;
3471:
3472: case VariableInfo.AT_END:
3473: if (!atEndSeen) {
3474: out
3475: .printil("_jspx_at_end = new java.util.ArrayList();");
3476: atEndSeen = true;
3477: }
3478: out.printin("_jspx_at_end.add(");
3479: break;
3480: } // switch
3481:
3482: out.print(quote(tagVars[i].getNameGiven()));
3483: out.println(");");
3484: }
3485: if (aliasSeen) {
3486: out
3487: .printil("this.jspContext = new org.apache.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, aliasMap);");
3488: } else {
3489: out
3490: .printil("this.jspContext = new org.apache.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, null);");
3491: }
3492: out.popIndent();
3493: out.printil("}");
3494: out.println();
3495: out.printil("public JspContext getJspContext() {");
3496: out.pushIndent();
3497: out.printil("return this.jspContext;");
3498: out.popIndent();
3499: out.printil("}");
3500: }
3501:
3502: /*
3503: * Generates implementation of
3504: * javax.servlet.jsp.tagext.DynamicAttributes.setDynamicAttribute() method,
3505: * which saves each dynamic attribute that is passed in so that a scoped
3506: * variable can later be created for it.
3507: */
3508: public void generateSetDynamicAttribute() {
3509: out
3510: .printil("public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {");
3511: out.pushIndent();
3512: /*
3513: * According to the spec, only dynamic attributes with no uri are to
3514: * be present in the Map; all other dynamic attributes are ignored.
3515: */
3516: out.printil("if (uri == null)");
3517: out.pushIndent();
3518: out.printil("_jspx_dynamic_attrs.put(localName, value);");
3519: out.popIndent();
3520: out.popIndent();
3521: out.printil("}");
3522: }
3523:
3524: /*
3525: * Creates a page-scoped variable for each declared tag attribute.
3526: * Also, if the tag accepts dynamic attributes, a page-scoped variable
3527: * is made available for each dynamic attribute that was passed in.
3528: */
3529: private void generatePageScopedVariables(JasperTagInfo tagInfo) {
3530:
3531: // "normal" attributes
3532: TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
3533: for (int i = 0; i < attrInfos.length; i++) {
3534: String attrName = attrInfos[i].getName();
3535: out.printil("if( " + toGetterMethod(attrName)
3536: + " != null ) ");
3537: out.pushIndent();
3538: out.printin("_jspx_page_context.setAttribute(");
3539: out.print(quote(attrName));
3540: out.print(", ");
3541: out.print(toGetterMethod(attrName));
3542: out.println(");");
3543: out.popIndent();
3544: }
3545:
3546: // Expose the Map containing dynamic attributes as a page-scoped var
3547: if (tagInfo.hasDynamicAttributes()) {
3548: out.printin("_jspx_page_context.setAttribute(\"");
3549: out.print(tagInfo.getDynamicAttributesMapName());
3550: out.print("\", _jspx_dynamic_attrs);");
3551: }
3552: }
3553:
3554: /*
3555: * Generates the getter method for the given attribute name.
3556: */
3557: private String toGetterMethod(String attrName) {
3558: char[] attrChars = attrName.toCharArray();
3559: attrChars[0] = Character.toUpperCase(attrChars[0]);
3560: return "get" + new String(attrChars) + "()";
3561: }
3562:
3563: /*
3564: * Generates the setter method name for the given attribute name.
3565: */
3566: private String toSetterMethodName(String attrName) {
3567: char[] attrChars = attrName.toCharArray();
3568: attrChars[0] = Character.toUpperCase(attrChars[0]);
3569: return "set" + new String(attrChars);
3570: }
3571:
3572: /**
3573: * Class storing the result of introspecting a custom tag handler.
3574: */
3575: private static class TagHandlerInfo {
3576:
3577: private Hashtable methodMaps;
3578: private Hashtable propertyEditorMaps;
3579: private Class tagHandlerClass;
3580:
3581: /**
3582: * Constructor.
3583: *
3584: * @param n The custom tag whose tag handler class is to be
3585: * introspected
3586: * @param tagHandlerClass Tag handler class
3587: * @param err Error dispatcher
3588: */
3589: TagHandlerInfo(Node n, Class tagHandlerClass,
3590: ErrorDispatcher err) throws JasperException {
3591: this .tagHandlerClass = tagHandlerClass;
3592: this .methodMaps = new Hashtable();
3593: this .propertyEditorMaps = new Hashtable();
3594:
3595: try {
3596: BeanInfo tagClassInfo = Introspector
3597: .getBeanInfo(tagHandlerClass);
3598: PropertyDescriptor[] pd = tagClassInfo
3599: .getPropertyDescriptors();
3600: for (int i = 0; i < pd.length; i++) {
3601: /*
3602: * FIXME: should probably be checking for things like
3603: * pageContext, bodyContent, and parent here -akv
3604: */
3605: if (pd[i].getWriteMethod() != null) {
3606: methodMaps.put(pd[i].getName(), pd[i]
3607: .getWriteMethod());
3608: }
3609: if (pd[i].getPropertyEditorClass() != null)
3610: propertyEditorMaps.put(pd[i].getName(), pd[i]
3611: .getPropertyEditorClass());
3612: }
3613: } catch (IntrospectionException ie) {
3614: err.jspError(n, "jsp.error.introspect.taghandler",
3615: tagHandlerClass.getName(), ie);
3616: }
3617: }
3618:
3619: /**
3620: * XXX
3621: */
3622: public Method getSetterMethod(String attrName) {
3623: return (Method) methodMaps.get(attrName);
3624: }
3625:
3626: /**
3627: * XXX
3628: */
3629: public Class getPropertyEditorClass(String attrName) {
3630: return (Class) propertyEditorMaps.get(attrName);
3631: }
3632:
3633: /**
3634: * XXX
3635: */
3636: public Class getTagHandlerClass() {
3637: return tagHandlerClass;
3638: }
3639: }
3640:
3641: /**
3642: * A class for generating codes to a buffer. Included here are some
3643: * support for tracking source to Java lines mapping.
3644: */
3645: private static class GenBuffer {
3646:
3647: /*
3648: * For a CustomTag, the codes that are generated at the beginning of
3649: * the tag may not be in the same buffer as those for the body of the
3650: * tag. Two fields are used here to keep this straight. For codes
3651: * that do not corresponds to any JSP lines, they should be null.
3652: */
3653: private Node node;
3654: private Node.Nodes body;
3655: private java.io.CharArrayWriter charWriter;
3656: protected ServletWriter out;
3657:
3658: GenBuffer() {
3659: this (null, null);
3660: }
3661:
3662: GenBuffer(Node n, Node.Nodes b) {
3663: node = n;
3664: body = b;
3665: if (body != null) {
3666: body.setGeneratedInBuffer(true);
3667: }
3668: charWriter = new java.io.CharArrayWriter();
3669: out = new ServletWriter(new java.io.PrintWriter(charWriter));
3670: }
3671:
3672: public ServletWriter getOut() {
3673: return out;
3674: }
3675:
3676: public String toString() {
3677: return charWriter.toString();
3678: }
3679:
3680: /**
3681: * Adjust the Java Lines. This is necessary because the Java lines
3682: * stored with the nodes are relative the beginning of this buffer
3683: * and need to be adjusted when this buffer is inserted into the
3684: * source.
3685: */
3686: public void adjustJavaLines(final int offset) {
3687:
3688: if (node != null) {
3689: adjustJavaLine(node, offset);
3690: }
3691:
3692: if (body != null) {
3693: try {
3694: body.visit(new Node.Visitor() {
3695:
3696: public void doVisit(Node n) {
3697: adjustJavaLine(n, offset);
3698: }
3699:
3700: public void visit(Node.CustomTag n)
3701: throws JasperException {
3702: Node.Nodes b = n.getBody();
3703: if (b != null && !b.isGeneratedInBuffer()) {
3704: // Don't adjust lines for the nested tags that
3705: // are also generated in buffers, because the
3706: // adjustments will be done elsewhere.
3707: b.visit(this );
3708: }
3709: }
3710: });
3711: } catch (JasperException ex) {
3712: }
3713: }
3714: }
3715:
3716: private static void adjustJavaLine(Node n, int offset) {
3717: if (n.getBeginJavaLine() > 0) {
3718: n.setBeginJavaLine(n.getBeginJavaLine() + offset);
3719: n.setEndJavaLine(n.getEndJavaLine() + offset);
3720: }
3721: }
3722: }
3723:
3724: /**
3725: * Keeps track of the generated Fragment Helper Class
3726: */
3727: private static class FragmentHelperClass {
3728:
3729: private static class Fragment {
3730: private GenBuffer genBuffer;
3731: private int id;
3732:
3733: public Fragment(int id, Node node) {
3734: this .id = id;
3735: genBuffer = new GenBuffer(null, node.getBody());
3736: }
3737:
3738: public GenBuffer getGenBuffer() {
3739: return this .genBuffer;
3740: }
3741:
3742: public int getId() {
3743: return this .id;
3744: }
3745: }
3746:
3747: // True if the helper class should be generated.
3748: private boolean used = false;
3749:
3750: private ArrayList fragments = new ArrayList();
3751:
3752: private String className;
3753:
3754: // Buffer for entire helper class
3755: private GenBuffer classBuffer = new GenBuffer();
3756:
3757: public FragmentHelperClass(String className) {
3758: this .className = className;
3759: }
3760:
3761: public String getClassName() {
3762: return this .className;
3763: }
3764:
3765: public boolean isUsed() {
3766: return this .used;
3767: }
3768:
3769: public void generatePreamble() {
3770: ServletWriter out = this .classBuffer.getOut();
3771: out.println();
3772: out.pushIndent();
3773: // Note: cannot be static, as we need to reference things like
3774: // _jspx_meth_*
3775: out.printil("private class " + className);
3776: out.printil(" extends "
3777: + "org.apache.jasper.runtime.JspFragmentHelper");
3778: out.printil("{");
3779: out.pushIndent();
3780: out
3781: .printil("private javax.servlet.jsp.tagext.JspTag _jspx_parent;");
3782: out.printil("private int[] _jspx_push_body_count;");
3783: out.println();
3784: out.printil("public " + className
3785: + "( int discriminator, JspContext jspContext, "
3786: + "javax.servlet.jsp.tagext.JspTag _jspx_parent, "
3787: + "int[] _jspx_push_body_count ) {");
3788: out.pushIndent();
3789: out
3790: .printil("super( discriminator, jspContext, _jspx_parent );");
3791: out.printil("this._jspx_parent = _jspx_parent;");
3792: out
3793: .printil("this._jspx_push_body_count = _jspx_push_body_count;");
3794: out.popIndent();
3795: out.printil("}");
3796: }
3797:
3798: public Fragment openFragment(Node parent, String tagHandlerVar,
3799: int methodNesting) throws JasperException {
3800: Fragment result = new Fragment(fragments.size(), parent);
3801: fragments.add(result);
3802: this .used = true;
3803: parent.setInnerClassName(className);
3804:
3805: ServletWriter out = result.getGenBuffer().getOut();
3806: out.pushIndent();
3807: out.pushIndent();
3808: // XXX - Returns boolean because if a tag is invoked from
3809: // within this fragment, the Generator sometimes might
3810: // generate code like "return true". This is ignored for now,
3811: // meaning only the fragment is skipped. The JSR-152
3812: // expert group is currently discussing what to do in this case.
3813: // See comment in closeFragment()
3814: if (methodNesting > 0) {
3815: out.printin("public boolean invoke");
3816: } else {
3817: out.printin("public void invoke");
3818: }
3819: out.println(result.getId() + "( " + "JspWriter out ) ");
3820: out.pushIndent();
3821: // Note: Throwable required because methods like _jspx_meth_*
3822: // throw Throwable.
3823: out.printil("throws Throwable");
3824: out.popIndent();
3825: out.printil("{");
3826: out.pushIndent();
3827: generateLocalVariables(out, parent);
3828:
3829: return result;
3830: }
3831:
3832: public void closeFragment(Fragment fragment, int methodNesting) {
3833: ServletWriter out = fragment.getGenBuffer().getOut();
3834: // XXX - See comment in openFragment()
3835: if (methodNesting > 0) {
3836: out.printil("return false;");
3837: } else {
3838: out.printil("return;");
3839: }
3840: out.popIndent();
3841: out.printil("}");
3842: }
3843:
3844: public void generatePostamble() {
3845: ServletWriter out = this .classBuffer.getOut();
3846: // Generate all fragment methods:
3847: for (int i = 0; i < fragments.size(); i++) {
3848: Fragment fragment = (Fragment) fragments.get(i);
3849: fragment.getGenBuffer().adjustJavaLines(
3850: out.getJavaLine() - 1);
3851: out.printMultiLn(fragment.getGenBuffer().toString());
3852: }
3853:
3854: // Generate postamble:
3855: out.printil("public void invoke( java.io.Writer writer )");
3856: out.pushIndent();
3857: out.printil("throws JspException");
3858: out.popIndent();
3859: out.printil("{");
3860: out.pushIndent();
3861: out.printil("JspWriter out = null;");
3862: out.printil("if( writer != null ) {");
3863: out.pushIndent();
3864: out.printil("out = this.jspContext.pushBody(writer);");
3865: out.popIndent();
3866: out.printil("} else {");
3867: out.pushIndent();
3868: out.printil("out = this.jspContext.getOut();");
3869: out.popIndent();
3870: out.printil("}");
3871: out.printil("try {");
3872: out.pushIndent();
3873: out.printil("switch( this.discriminator ) {");
3874: out.pushIndent();
3875: for (int i = 0; i < fragments.size(); i++) {
3876: out.printil("case " + i + ":");
3877: out.pushIndent();
3878: out.printil("invoke" + i + "( out );");
3879: out.printil("break;");
3880: out.popIndent();
3881: }
3882: out.popIndent();
3883: out.printil("}"); // switch
3884: out.popIndent();
3885: out.printil("}"); // try
3886: out.printil("catch( Throwable e ) {");
3887: out.pushIndent();
3888: out.printil("if (e instanceof SkipPageException)");
3889: out.printil(" throw (SkipPageException) e;");
3890: out.printil("throw new JspException( e );");
3891: out.popIndent();
3892: out.printil("}"); // catch
3893: out.printil("finally {");
3894: out.pushIndent();
3895:
3896: out.printil("if( writer != null ) {");
3897: out.pushIndent();
3898: out.printil("this.jspContext.popBody();");
3899: out.popIndent();
3900: out.printil("}");
3901:
3902: out.popIndent();
3903: out.printil("}"); // finally
3904: out.popIndent();
3905: out.printil("}"); // invoke method
3906: out.popIndent();
3907: out.printil("}"); // helper class
3908: out.popIndent();
3909: }
3910:
3911: public String toString() {
3912: return classBuffer.toString();
3913: }
3914:
3915: public void adjustJavaLines(int offset) {
3916: for (int i = 0; i < fragments.size(); i++) {
3917: Fragment fragment = (Fragment) fragments.get(i);
3918: fragment.getGenBuffer().adjustJavaLines(offset);
3919: }
3920: }
3921: }
3922: }
|