org.apache.jasper.compiler.Generator Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jasper.compiler;
import static org.apache.jasper.JasperMessages.MESSAGES;
import static org.apache.jasper.compiler.Constants.ARRAY_LIST;
import static org.apache.jasper.compiler.Constants.BEANS;
import static org.apache.jasper.compiler.Constants.BODY_CONTENT;
import static org.apache.jasper.compiler.Constants.BODY_TAG;
import static org.apache.jasper.compiler.Constants.BOOLEAN;
import static org.apache.jasper.compiler.Constants.CLASS;
import static org.apache.jasper.compiler.Constants.CLASS_NOT_FOUND_EXCEPTION;
import static org.apache.jasper.compiler.Constants.DISPATCHER_TYPE;
import static org.apache.jasper.compiler.Constants.DYNAMIC_ATTRIBUTES;
import static org.apache.jasper.compiler.Constants.EL_CONTEXT_WRAPPER;
import static org.apache.jasper.compiler.Constants.EXCEPTION;
import static org.apache.jasper.compiler.Constants.EXPRESSION_FACTORY;
import static org.apache.jasper.compiler.Constants.GENERATOR;
import static org.apache.jasper.compiler.Constants.HASH_MAP;
import static org.apache.jasper.compiler.Constants.HASH_SET;
import static org.apache.jasper.compiler.Constants.HTTP_SERVLET_REQUEST;
import static org.apache.jasper.compiler.Constants.HTTP_SERVLET_RESPONSE;
import static org.apache.jasper.compiler.Constants.HTTP_SESSION;
import static org.apache.jasper.compiler.Constants.ILLEGAL_STATE_EXCEPTION;
import static org.apache.jasper.compiler.Constants.INSTANCE_MANAGER;
import static org.apache.jasper.compiler.Constants.INSTANCE_MANAGER_FACTORY;
import static org.apache.jasper.compiler.Constants.INSTANTIATION_EXCEPTION;
import static org.apache.jasper.compiler.Constants.IO_EXCEPTION;
import static org.apache.jasper.compiler.Constants.JSP_CONTEXT;
import static org.apache.jasper.compiler.Constants.JSP_CONTEXT_WRAPPER;
import static org.apache.jasper.compiler.Constants.JSP_EXCEPTION;
import static org.apache.jasper.compiler.Constants.JSP_FACTORY;
import static org.apache.jasper.compiler.Constants.JSP_FRAGMENT;
import static org.apache.jasper.compiler.Constants.JSP_FRAGMENT_HELPER;
import static org.apache.jasper.compiler.Constants.JSP_METHOD_EXPRESSION;
import static org.apache.jasper.compiler.Constants.JSP_RUNTIME_LIBRARY;
import static org.apache.jasper.compiler.Constants.JSP_SOURCE_DEPENDENT;
import static org.apache.jasper.compiler.Constants.JSP_SOURCE_DIRECTIVES;
import static org.apache.jasper.compiler.Constants.JSP_SOURCE_IMPORTS;
import static org.apache.jasper.compiler.Constants.JSP_TAG;
import static org.apache.jasper.compiler.Constants.JSP_VALUE_EXPRESSION;
import static org.apache.jasper.compiler.Constants.JSP_WRITER;
import static org.apache.jasper.compiler.Constants.LONG;
import static org.apache.jasper.compiler.Constants.MAP;
import static org.apache.jasper.compiler.Constants.METHOD_EXPRESSION;
import static org.apache.jasper.compiler.Constants.OBJECT;
import static org.apache.jasper.compiler.Constants.PAGE_CONTEXT;
import static org.apache.jasper.compiler.Constants.SERVLET_CONFIG;
import static org.apache.jasper.compiler.Constants.SERVLET_CONTEXT;
import static org.apache.jasper.compiler.Constants.SERVLET_EXCEPTION;
import static org.apache.jasper.compiler.Constants.SET;
import static org.apache.jasper.compiler.Constants.SIMPLE_TAG;
import static org.apache.jasper.compiler.Constants.SIMPLE_TAG_SUPPORT;
import static org.apache.jasper.compiler.Constants.SKIP_PAGE_EXCEPTION;
import static org.apache.jasper.compiler.Constants.STRING;
import static org.apache.jasper.compiler.Constants.STRING_READER;
import static org.apache.jasper.compiler.Constants.STRING_WRITER;
import static org.apache.jasper.compiler.Constants.TAG;
import static org.apache.jasper.compiler.Constants.TAG_ADAPTER;
import static org.apache.jasper.compiler.Constants.TAG_HANDLER_POOL;
import static org.apache.jasper.compiler.Constants.THROWABLE;
import static org.apache.jasper.compiler.Constants.VALUE_EXPRESSION;
import static org.apache.jasper.compiler.Constants.VARIABLE_MAPPER;
import static org.apache.jasper.compiler.Constants.WRITER;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.Vector;
import jakarta.el.MethodExpression;
import jakarta.el.ValueExpression;
import jakarta.servlet.jsp.tagext.TagAttributeInfo;
import jakarta.servlet.jsp.tagext.TagInfo;
import jakarta.servlet.jsp.tagext.TagVariableInfo;
import jakarta.servlet.jsp.tagext.VariableInfo;
import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.jasper.JasperLogger;
import org.apache.jasper.JasperMessages;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.compiler.Node.NamedAttribute;
import org.apache.jasper.runtime.JspRuntimeLibrary;
import org.xml.sax.Attributes;
/**
* Generate Java source from Nodes
*
* @author Anil K. Vijendran
* @author Danno Ferrin
* @author Mandar Raje
* @author Rajiv Mordani
* @author Pierre Delisle
*
* Tomcat 4.1.x and Tomcat 5:
* @author Kin-man Chung
* @author Jan Luehe
* @author Shawn Bayern
* @author Mark Roth
* @author Denis Benoit
*
* Tomcat 6.x
* @author Jacob Hookom
* @author Remy Maucherat
*/
class Generator {
private static final Class>[] OBJECT_CLASS = { Object.class };
private static final String VAR_EXPRESSIONFACTORY =
System.getProperty(GENERATOR + ".VAR_EXPRESSIONFACTORY", "_el_expressionfactory");
private static final String VAR_INSTANCEMANAGER =
System.getProperty(GENERATOR + ".VAR_INSTANCEMANAGER", "_jsp_instancemanager");
private static final boolean POOL_TAGS_WITH_EXTENDS =
Boolean.getBoolean(GENERATOR + ".POOL_TAGS_WITH_EXTENDS");
/* System property that controls if the requirement to have the object
* used in jsp:getProperty action to be previously "introduced"
* to the JSP processor (see JSP.5.3) is enforced.
*/
private static final boolean STRICT_GET_PROPERTY = Boolean.valueOf(
System.getProperty(
GENERATOR + ".STRICT_GET_PROPERTY",
"true")).booleanValue();
private final ServletWriter out;
private final ArrayList methodsBuffered;
private final FragmentHelperClass fragmentHelperClass;
private final ErrorDispatcher err;
private final BeanRepository beanInfo;
private final Set varInfoNames;
private final JspCompilationContext ctxt;
private final boolean isPoolingEnabled;
private final boolean breakAtLF;
private String jspIdPrefix;
private int jspId;
private final PageInfo pageInfo;
private final Vector tagHandlerPoolNames;
private GenBuffer charArrayBuffer;
private final DateFormat timestampFormat;
private final ELInterpreter elInterpreter;
/**
* @param s
* the input string
* @return quoted and escaped string, per Java rule
*/
static String quote(String s) {
if (s == null)
return "null";
return '"' + escape(s) + '"';
}
/**
* @param s
* the input string
* @return escaped string, per Java rule
*/
static String escape(String s) {
if (s == null)
return "";
StringBuilder b = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '"')
b.append('\\').append('"');
else if (c == '\\')
b.append('\\').append('\\');
else if (c == '\n')
b.append('\\').append('n');
else if (c == '\r')
b.append('\\').append('r');
else
b.append(c);
}
return b.toString();
}
/**
* Single quote and escape a character
*/
static String quote(char c) {
StringBuilder b = new StringBuilder();
b.append('\'');
if (c == '\'')
b.append('\\').append('\'');
else if (c == '\\')
b.append('\\').append('\\');
else if (c == '\n')
b.append('\\').append('n');
else if (c == '\r')
b.append('\\').append('r');
else
b.append(c);
b.append('\'');
return b.toString();
}
private String createJspId() {
if (this.jspIdPrefix == null) {
StringBuilder sb = new StringBuilder(32);
String name = ctxt.getServletJavaFileName();
sb.append("jsp_");
// Cast to long to avoid issue with Integer.MIN_VALUE
sb.append(Math.abs((long) name.hashCode()));
sb.append('_');
this.jspIdPrefix = sb.toString();
}
return this.jspIdPrefix + (this.jspId++);
}
/**
* Generates declarations. This includes "info" of the page directive, and
* scriptlet declarations.
*/
private void generateDeclarations(Node.Nodes page) throws JasperException {
class DeclarationVisitor extends Node.Visitor {
private boolean getServletInfoGenerated = false;
/*
* Generates getServletInfo() method that returns the value of the
* page directive's 'info' attribute, if present.
*
* The Validator has already ensured that if the translation unit
* contains more than one page directive with an 'info' attribute,
* their values match.
*/
@Override
public void visit(Node.PageDirective n) throws JasperException {
if (getServletInfoGenerated) {
return;
}
String info = n.getAttributeValue("info");
if (info == null)
return;
getServletInfoGenerated = true;
out.printil("public " + STRING + " getServletInfo() {");
out.pushIndent();
out.printin("return ");
out.print(quote(info));
out.println(";");
out.popIndent();
out.printil("}");
out.println();
}
@Override
public void visit(Node.Declaration n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
out.printMultiLn(n.getText());
out.println();
n.setEndJavaLine(out.getJavaLine());
}
// Custom Tags may contain declarations from tag plugins.
@Override
public void visit(Node.CustomTag n) throws JasperException {
if (n.useTagPlugin()) {
if (n.getAtSTag() != null) {
n.getAtSTag().visit(this);
}
visitBody(n);
if (n.getAtETag() != null) {
n.getAtETag().visit(this);
}
} else {
visitBody(n);
}
}
}
out.println();
page.visit(new DeclarationVisitor());
}
/**
* Compiles list of tag handler pool names.
*/
private void compileTagHandlerPoolList(Node.Nodes page)
throws JasperException {
class TagHandlerPoolVisitor extends Node.Visitor {
private final Vector names;
/*
* Constructor
*
* @param v Vector of tag handler pool names to populate
*/
TagHandlerPoolVisitor(Vector v) {
names = v;
}
/*
* Gets the name of the tag handler pool for the given custom tag
* and adds it to the list of tag handler pool names unless it is
* already contained in it.
*/
@Override
public void visit(Node.CustomTag n) throws JasperException {
if (!n.implementsSimpleTag()) {
String name = createTagHandlerPoolName(n.getPrefix(), n
.getLocalName(), n.getAttributes(),
n.getNamedAttributeNodes(), n.hasEmptyBody());
n.setTagHandlerPoolName(name);
if (!names.contains(name)) {
names.add(name);
}
}
visitBody(n);
}
/*
* Creates the name of the tag handler pool whose tag handlers may
* be (re)used to service this action.
*
* @return The name of the tag handler pool
*/
private String createTagHandlerPoolName(String prefix,
String shortName, Attributes attrs, Node.Nodes namedAttrs,
boolean hasEmptyBody) {
StringBuilder poolName = new StringBuilder(64);
poolName.append("_jspx_tagPool_").append(prefix).append('_')
.append(shortName);
if (attrs != null) {
String[] attrNames =
new String[attrs.getLength() + namedAttrs.size()];
for (int i = 0; i < attrNames.length; i++) {
attrNames[i] = attrs.getQName(i);
}
for (int i = 0; i < namedAttrs.size(); i++) {
attrNames[attrs.getLength() + i] =
((NamedAttribute) namedAttrs.getNode(i)).getQName();
}
Arrays.sort(attrNames, Collections.reverseOrder());
if (attrNames.length > 0) {
poolName.append('&');
}
for (int i = 0; i < attrNames.length; i++) {
poolName.append('_');
poolName.append(attrNames[i]);
}
}
if (hasEmptyBody) {
poolName.append("_nobody");
}
return JspUtil.makeJavaIdentifier(poolName.toString());
}
}
page.visit(new TagHandlerPoolVisitor(tagHandlerPoolNames));
}
private void declareTemporaryScriptingVars(Node.Nodes page)
throws JasperException {
class ScriptingVarVisitor extends Node.Visitor {
private final Vector vars;
ScriptingVarVisitor() {
vars = new Vector<>();
}
@Override
public void visit(Node.CustomTag n) throws JasperException {
// XXX - Actually there is no need to declare those
// "_jspx_" + varName + "_" + nestingLevel variables when we are
// inside a JspFragment.
if (n.getCustomNestingLevel() > 0) {
TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
VariableInfo[] varInfos = n.getVariableInfos();
if (varInfos.length > 0) {
for (int i = 0; i < varInfos.length; i++) {
String varName = varInfos[i].getVarName();
String tmpVarName = "_jspx_" + varName + "_"
+ n.getCustomNestingLevel();
if (!vars.contains(tmpVarName)) {
vars.add(tmpVarName);
out.printin(varInfos[i].getClassName());
out.print(" ");
out.print(tmpVarName);
out.print(" = ");
out.print(null);
out.println(";");
}
}
} else {
for (int i = 0; i < tagVarInfos.length; i++) {
String varName = tagVarInfos[i].getNameGiven();
if (varName == null) {
varName = n.getTagData().getAttributeString(
tagVarInfos[i].getNameFromAttribute());
} else if (tagVarInfos[i].getNameFromAttribute() != null) {
// alias
continue;
}
String tmpVarName = "_jspx_" + varName + "_"
+ n.getCustomNestingLevel();
if (!vars.contains(tmpVarName)) {
vars.add(tmpVarName);
out.printin(tagVarInfos[i].getClassName());
out.print(" ");
out.print(tmpVarName);
out.print(" = ");
out.print(null);
out.println(";");
}
}
}
}
visitBody(n);
}
}
page.visit(new ScriptingVarVisitor());
}
/**
* Generates the _jspInit() method for instantiating the tag handler pools.
* For tag file, _jspInit has to be invoked manually, and the ServletConfig
* object explicitly passed.
*
* In JSP 2.1, we also instantiate an ExpressionFactory
*/
private void generateInit() {
if (ctxt.isTagFile()) {
printilThreePart(out, "private void _jspInit(", SERVLET_CONFIG, " config) {");
} else {
out.printil("public void _jspInit() {");
}
out.pushIndent();
if (isPoolingEnabled) {
for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
out.printin(tagHandlerPoolNames.elementAt(i));
out.print(" = " + TAG_HANDLER_POOL + ".getTagHandlerPool(");
if (ctxt.isTagFile()) {
out.print("config");
} else {
out.print("getServletConfig()");
}
out.println(");");
}
}
out.printin(VAR_EXPRESSIONFACTORY);
out.print(" = _jspxFactory.getJspApplicationContext(");
if (ctxt.isTagFile()) {
out.print("config");
} else {
out.print("getServletConfig()");
}
out.println(".getServletContext()).getExpressionFactory();");
out.printin(VAR_INSTANCEMANAGER);
out.print(" = " + INSTANCE_MANAGER_FACTORY + ".getInstanceManager(");
if (ctxt.isTagFile()) {
out.print("config");
} else {
out.print("getServletConfig()");
}
out.println(");");
out.popIndent();
out.printil("}");
out.println();
}
/**
* Generates the _jspDestroy() method which is responsible for calling the
* release() method on every tag handler in any of the tag handler pools.
*/
private void generateDestroy() {
out.printil("public void _jspDestroy() {");
out.pushIndent();
if (isPoolingEnabled) {
for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
out.printin(tagHandlerPoolNames.elementAt(i));
out.println(".release();");
}
}
out.popIndent();
out.printil("}");
out.println();
}
/**
* Generate preamble package name (shared by servlet and tag handler
* preamble generation)
*/
private void genPreamblePackage(String packageName) {
if (!"".equals(packageName) && packageName != null) {
out.printil("package " + packageName + ";");
out.println();
}
}
/**
* Generate preamble imports (shared by servlet and tag handler preamble
* generation)
*/
private void genPreambleImports() {
Iterator iter = pageInfo.getImports().iterator();
while (iter.hasNext()) {
out.printin("import ");
out.print(iter.next());
out.println(";");
}
out.println();
}
/**
* Generation of static initializers in preamble. For example, dependent
* list, el function map, prefix map. (shared by servlet and tag handler
* preamble generation)
*/
private void genPreambleStaticInitializers() {
printilThreePart(out, "private static final ", JSP_FACTORY, " _jspxFactory =");
printilThreePart(out, " ", JSP_FACTORY, ".getDefaultFactory();");
out.println();
// Static data for getDependants()
out.printil("private static " + MAP + "<" + STRING + "," + LONG + "> _jspx_dependants;");
out.println();
Map dependants = pageInfo.getDependants();
if (!dependants.isEmpty()) {
out.printil("static {");
out.pushIndent();
out.printin("_jspx_dependants = new " + HASH_MAP + "<" + STRING + "," + LONG + ">(");
out.print("" + dependants.size());
out.println(");");
Iterator> iter = dependants.entrySet().iterator();
while (iter.hasNext()) {
Entry entry = iter.next();
out.printin("_jspx_dependants.put(\"");
out.print(entry.getKey());
out.print("\", Long.valueOf(");
out.print(entry.getValue().toString());
out.println("L));");
}
out.popIndent();
out.printil("}");
out.println();
}
// Static data for getImports()
List imports = pageInfo.getImports();
Set packages = new HashSet<>();
Set classes = new HashSet<>();
for (String importName : imports) {
if (importName == null) {
continue;
}
String trimmed = importName.trim();
if (trimmed.endsWith(".*")) {
packages.add(trimmed.substring(0, trimmed.length() - 2));
} else {
classes.add(trimmed);
}
}
out.printil("private static final " + SET + "<" + STRING + "> _jspx_imports_packages;");
out.println();
out.printil("private static final " + SET + "<" + STRING + "> _jspx_imports_classes;");
out.println();
out.printil("static {");
out.pushIndent();
if (packages.size() == 0) {
out.printin("_jspx_imports_packages = null;");
out.println();
} else {
out.printin("_jspx_imports_packages = new " + HASH_SET + "<>();");
out.println();
for (String packageName : packages) {
out.printin("_jspx_imports_packages.add(\"");
out.print(packageName);
out.println("\");");
}
}
if (classes.size() == 0) {
out.printin("_jspx_imports_classes = null;");
out.println();
} else {
out.printin("_jspx_imports_classes = new " + HASH_SET + "<>();");
out.println();
for (String className : classes) {
out.printin("_jspx_imports_classes.add(\"");
out.print(className);
out.println("\");");
}
}
out.popIndent();
out.printil("}");
out.println();
}
/**
* Declare tag handler pools (tags of the same type and with the same
* attribute set share the same tag handler pool) (shared by servlet and tag
* handler preamble generation)
*
* In JSP 2.1, we also scope an instance of ExpressionFactory
*/
private void genPreambleClassVariableDeclarations() {
if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
out.printil("private " + TAG_HANDLER_POOL + " "
+ tagHandlerPoolNames.elementAt(i) + ";");
}
out.println();
}
printinThreePart(out, "private ", EXPRESSION_FACTORY, " ");
out.print(VAR_EXPRESSIONFACTORY);
out.println(";");
out.printin("private " + INSTANCE_MANAGER + " ");
out.print(VAR_INSTANCEMANAGER);
out.println(";");
out.println();
}
/**
* Declare general-purpose methods (shared by servlet and tag handler
* preamble generation)
*/
private void genPreambleMethods() {
// Implement JspSourceDependent
out.printil("public " + MAP + "<" + STRING + "," + LONG + "> getDependants() {");
out.pushIndent();
out.printil("return _jspx_dependants;");
out.popIndent();
out.printil("}");
out.println();
// Implement JspSourceImports
out.printil("public " + SET + "<" + STRING + "> getPackageImports() {");
out.pushIndent();
out.printil("return _jspx_imports_packages;");
out.popIndent();
out.printil("}");
out.println();
out.printil("public " + SET + "<" + STRING + "> getClassImports() {");
out.pushIndent();
out.printil("return _jspx_imports_classes;");
out.popIndent();
out.printil("}");
out.println();
// Implement JspSourceDirectives
out.printil("public boolean getErrorOnELNotFound() {");
out.pushIndent();
if (pageInfo.isErrorOnELNotFound()) {
out.printil("return true;");
} else {
out.printil("return false;");
}
out.popIndent();
out.printil("}");
out.println();
generateInit();
generateDestroy();
}
/**
* Generates the beginning of the static portion of the servlet.
*/
private void generatePreamble(Node.Nodes page) throws JasperException {
String servletPackageName = ctxt.getServletPackageName();
String servletClassName = ctxt.getServletClassName();
String serviceMethodName = Constants.SERVICE_METHOD_NAME;
// First the package name:
genPreamblePackage(servletPackageName);
// Generate imports
genPreambleImports();
// Generate class declaration
out.printin("public final class ");
out.print(servletClassName);
out.print(" extends ");
out.println(pageInfo.getExtends());
out.printin(" implements " + JSP_SOURCE_DEPENDENT + ",");
out.println();
out.printin(" " + JSP_SOURCE_IMPORTS + ",");
out.println();
out.printin(" " + JSP_SOURCE_DIRECTIVES);
out.println(" {");
out.pushIndent();
// Class body begins here
generateDeclarations(page);
// Static initializations here
genPreambleStaticInitializers();
// Class variable declarations
genPreambleClassVariableDeclarations();
// Methods here
genPreambleMethods();
// Now the service method
// Now the service method
if (pageInfo.isThreadSafe()) {
out.printin("public void ");
} else {
// This is unlikely to perform well.
out.printin("public synchronized void ");
// As required by JSP 3.1, log a warning
JasperLogger.ROOT_LOGGER.deprecatedIsThreadSafe(ctxt.getJspFile());
}
out.print(serviceMethodName);
printlnMultiPart(out, "(final ", HTTP_SERVLET_REQUEST, " request, final ", HTTP_SERVLET_RESPONSE, " response)");
printlnThreePart(out, " throws " + IO_EXCEPTION + ", ", SERVLET_EXCEPTION, " {");
out.pushIndent();
out.println();
// Method check
if (!pageInfo.isErrorPage()) {
out.println("final " + STRING + " _jspx_method = request.getMethod();");
out.print("if (!\"GET\".equals(_jspx_method) && !\"POST\".equals(_jspx_method) && !\"HEAD\".equals(_jspx_method) && ");
printlnThreePart(out, "!", DISPATCHER_TYPE, ".ERROR.equals(request.getDispatcherType())) {");
out.pushIndent();
out.print("response.sendError(" + HTTP_SERVLET_RESPONSE + ".SC_METHOD_NOT_ALLOWED, ");
out.println("\"" + JasperMessages.MESSAGES.forbiddenHttpMethod()+ "\");");
out.println("return;");
out.popIndent();
out.println("}");
out.println();
}
// Local variable declarations
printilThreePart(out, "final ", PAGE_CONTEXT, " pageContext;");
if (pageInfo.isSession())
printilTwoPart(out, HTTP_SESSION, " session = null;");
if (pageInfo.isErrorPage()) {
out.printil(THROWABLE + " exception = " + JSP_RUNTIME_LIBRARY + ".getThrowable(request);");
out.printil("if (exception != null) {");
out.pushIndent();
printilThreePart(out, "response.setStatus(", HTTP_SERVLET_RESPONSE, ".SC_INTERNAL_SERVER_ERROR);");
out.popIndent();
out.printil("}");
}
printilThreePart(out, "final ", SERVLET_CONTEXT, " application;");
printilThreePart(out, "final ", SERVLET_CONFIG, " config;");
printilTwoPart(out, JSP_WRITER, " out = null;");
out.printil("final " + OBJECT + " page = this;");
printilTwoPart(out, JSP_WRITER, " _jspx_out = null;");
printilTwoPart(out, PAGE_CONTEXT, " _jspx_page_context = null;");
out.println();
declareTemporaryScriptingVars(page);
out.println();
out.printil("try {");
out.pushIndent();
out.printin("response.setContentType(");
out.print(quote(pageInfo.getContentType()));
out.println(");");
if (ctxt.getOptions().isXpoweredBy()) {
out.printil("response.addHeader(\"X-Powered-By\", \"JSP/2.3\");");
}
out.printil("pageContext = _jspxFactory.getPageContext(this, request, response,");
out.printin("\t\t\t");
out.print(quote(pageInfo.getErrorPage()));
out.print(", " + pageInfo.isSession());
out.print(", " + pageInfo.getBuffer());
out.print(", " + pageInfo.isAutoFlush());
out.println(");");
out.printil("_jspx_page_context = pageContext;");
out.printil("application = pageContext.getServletContext();");
out.printil("config = pageContext.getServletConfig();");
if (pageInfo.isSession())
out.printil("session = pageContext.getSession();");
out.printil("out = pageContext.getOut();");
out.printil("_jspx_out = out;");
out.println();
}
/**
* Generates an XML Prolog, which includes an XML declaration and an XML
* doctype declaration.
*/
private void generateXmlProlog(Node.Nodes page) {
/*
* An XML declaration is generated under the following conditions: -
* 'omit-xml-declaration' attribute of action is set to
* "no" or "false" - JSP document without a
*/
String omitXmlDecl = pageInfo.getOmitXmlDecl();
if ((omitXmlDecl != null && !JspUtil.booleanValue(omitXmlDecl))
|| (omitXmlDecl == null && page.getRoot().isXmlSyntax()
&& !pageInfo.hasJspRoot() && !ctxt.isTagFile())) {
String cType = pageInfo.getContentType();
String charSet = cType.substring(cType.indexOf("charset=") + 8);
out.printil("out.write(\"\\n\");");
}
/*
* Output a DOCTYPE declaration if the doctype-root-element appears. If
* doctype-public appears: else
*/
String doctypeName = pageInfo.getDoctypeName();
if (doctypeName != null) {
String doctypePublic = pageInfo.getDoctypePublic();
String doctypeSystem = pageInfo.getDoctypeSystem();
out.printin("out.write(\"\\n\");");
}
}
/**
* A visitor that generates codes for the elements in the page.
*/
private class GenerateVisitor extends Node.Visitor {
/*
* Hashtable containing introspection information on tag handlers:
* : tag prefix : hashtable containing introspection on tag
* handlers: : tag short name : introspection info of tag
* handler for tag
*/
private final Hashtable> handlerInfos;
private final Hashtable tagVarNumbers;
private String parent;
private boolean isSimpleTagParent; // Is parent a SimpleTag?
private String pushBodyCountVar;
private String simpleTagHandlerVar;
private boolean isSimpleTagHandler;
private boolean isFragment;
private final boolean isTagFile;
private ServletWriter out;
private final ArrayList methodsBuffered;
private final FragmentHelperClass fragmentHelperClass;
private int methodNesting;
private int charArrayCount;
private HashMap textMap;
/**
* Constructor.
*/
public GenerateVisitor(boolean isTagFile, ServletWriter out,
ArrayList methodsBuffered,
FragmentHelperClass fragmentHelperClass) {
this.isTagFile = isTagFile;
this.out = out;
this.methodsBuffered = methodsBuffered;
this.fragmentHelperClass = fragmentHelperClass;
methodNesting = 0;
handlerInfos = new Hashtable<>();
tagVarNumbers = new Hashtable<>();
textMap = new HashMap<>();
}
/**
* Returns an attribute value, optionally URL encoded. If the value is a
* runtime expression, the result is the expression itself, as a string.
* If the result is an EL expression, we insert a call to the
* interpreter. If the result is a Named Attribute we insert the
* generated variable name. Otherwise the result is a string literal,
* quoted and escaped.
*
* @param attr
* An JspAttribute object
* @param encode
* true if to be URL encoded
* @param expectedType
* the expected type for an EL evaluation (ignored for
* attributes that aren't EL expressions)
*/
private String attributeValue(Node.JspAttribute attr, boolean encode,
Class> expectedType) {
String v = attr.getValue();
if (!attr.isNamedAttribute() && (v == null))
return "";
if (attr.isExpression()) {
if (encode) {
return JSP_RUNTIME_LIBRARY + ".URLEncode(String.valueOf("
+ v + "), request.getCharacterEncoding())";
}
return v;
} else if (attr.isELInterpreterInput()) {
v = elInterpreter.interpreterCall(ctxt, this.isTagFile, v,
expectedType, attr.getEL().getMapName());
if (encode) {
return JSP_RUNTIME_LIBRARY + ".URLEncode("
+ v + ", request.getCharacterEncoding())";
}
return v;
} else if (attr.isNamedAttribute()) {
return attr.getNamedAttributeNode().getTemporaryVariableName();
} else {
if (encode) {
return JSP_RUNTIME_LIBRARY + ".URLEncode("
+ quote(v) + ", request.getCharacterEncoding())";
}
return quote(v);
}
}
/**
* Prints the attribute value specified in the param action, in the form
* of name=value string.
*
* @param n
* the parent node for the param action nodes.
*/
private void printParams(Node n, String pageParam, boolean literal)
throws JasperException {
class ParamVisitor extends Node.Visitor {
private String separator;
ParamVisitor(String separator) {
this.separator = separator;
}
@Override
public void visit(Node.ParamAction n) throws JasperException {
out.print(" + ");
out.print(separator);
out.print(" + ");
out.print(JSP_RUNTIME_LIBRARY
+ ".URLEncode(" + quote(n.getTextAttribute("name"))
+ ", request.getCharacterEncoding())");
out.print("+ \"=\" + ");
out.print(attributeValue(n.getValue(), true, String.class));
// The separator is '&' after the second use
separator = "\"&\"";
}
}
String sep;
if (literal) {
sep = pageParam.indexOf('?') > 0 ? "\"&\"" : "\"?\"";
} else {
sep = "((" + pageParam + ").indexOf('?')>0? '&': '?')";
}
if (n.getBody() != null) {
n.getBody().visit(new ParamVisitor(sep));
}
}
@Override
public void visit(Node.Expression n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
out.printin("out.print(");
out.printMultiLn(n.getText());
out.println(");");
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.Scriptlet n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
out.printMultiLn(n.getText());
out.println();
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.ELExpression n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
if (!pageInfo.isELIgnored() && (n.getEL() != null)) {
out.printil("out.write("
+ elInterpreter.interpreterCall(ctxt, this.isTagFile,
n.getType() + "{" + n.getText() + "}",
String.class, n.getEL().getMapName()) +
");");
} else {
out.printil("out.write("
+ quote(n.getType() + "{" + n.getText() + "}") + ");");
}
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.IncludeAction n) throws JasperException {
String flush = n.getTextAttribute("flush");
Node.JspAttribute page = n.getPage();
boolean isFlush = false; // default to false;
if ("true".equals(flush))
isFlush = true;
n.setBeginJavaLine(out.getJavaLine());
String pageParam;
if (page.isNamedAttribute()) {
// If the page for jsp:include was specified via
// jsp:attribute, first generate code to evaluate
// that body.
pageParam = generateNamedAttributeValue(page
.getNamedAttributeNode());
} else {
pageParam = attributeValue(page, false, String.class);
}
// If any of the params have their values specified by
// jsp:attribute, prepare those values first.
Node jspBody = findJspBody(n);
if (jspBody != null) {
prepareParams(jspBody);
} else {
prepareParams(n);
}
out.printin(JSP_RUNTIME_LIBRARY + ".include(request, response, "
+ pageParam);
printParams(n, pageParam, page.isLiteral());
out.println(", out, " + isFlush + ");");
n.setEndJavaLine(out.getJavaLine());
}
/**
* Scans through all child nodes of the given parent for <param>
* subelements. For each <param> element, if its value is specified via
* a Named Attribute (<jsp:attribute>), generate the code to evaluate
* those bodies first.
*
* If parent is null, simply returns.
*/
private void prepareParams(Node parent) throws JasperException {
if (parent == null)
return;
Node.Nodes subelements = parent.getBody();
if (subelements != null) {
for (int i = 0; i < subelements.size(); i++) {
Node n = subelements.getNode(i);
if (n instanceof Node.ParamAction) {
Node.Nodes paramSubElements = n.getBody();
for (int j = 0; (paramSubElements != null)
&& (j < paramSubElements.size()); j++) {
Node m = paramSubElements.getNode(j);
if (m instanceof Node.NamedAttribute) {
generateNamedAttributeValue((Node.NamedAttribute) m);
}
}
}
}
}
}
/**
* Finds the <jsp:body> subelement of the given parent node. If not
* found, null is returned.
*/
private Node.JspBody findJspBody(Node parent) {
Node.JspBody result = null;
Node.Nodes subelements = parent.getBody();
for (int i = 0; (subelements != null) && (i < subelements.size()); i++) {
Node n = subelements.getNode(i);
if (n instanceof Node.JspBody) {
result = (Node.JspBody) n;
break;
}
}
return result;
}
@Override
public void visit(Node.ForwardAction n) throws JasperException {
Node.JspAttribute page = n.getPage();
n.setBeginJavaLine(out.getJavaLine());
out.printil("if (true) {"); // So that javac won't complain about
out.pushIndent(); // codes after "return"
String pageParam;
if (page.isNamedAttribute()) {
// If the page for jsp:forward was specified via
// jsp:attribute, first generate code to evaluate
// that body.
pageParam = generateNamedAttributeValue(page
.getNamedAttributeNode());
} else {
pageParam = attributeValue(page, false, String.class);
}
// If any of the params have their values specified by
// jsp:attribute, prepare those values first.
Node jspBody = findJspBody(n);
if (jspBody != null) {
prepareParams(jspBody);
} else {
prepareParams(n);
}
out.printin("_jspx_page_context.forward(");
out.print(pageParam);
printParams(n, pageParam, page.isLiteral());
out.println(");");
if (isTagFile || isFragment) {
printilThreePart(out, "throw new ", SKIP_PAGE_EXCEPTION , "();");
} else {
out.printil((methodNesting > 0) ? "return true;" : "return;");
}
out.popIndent();
out.printil("}");
n.setEndJavaLine(out.getJavaLine());
// XXX Not sure if we can eliminate dead codes after this.
}
@Override
public void visit(Node.GetProperty n) throws JasperException {
String name = n.getTextAttribute("name");
String property = n.getTextAttribute("property");
n.setBeginJavaLine(out.getJavaLine());
if (beanInfo.checkVariable(name)) {
// Bean is defined using useBean, introspect at compile time
Class> bean = beanInfo.getBeanType(name);
String beanName = bean.getCanonicalName();
java.lang.reflect.Method meth = JspRuntimeLibrary
.getReadMethod(bean, property);
String methodName = meth.getName();
out.printil("out.write(" + JSP_RUNTIME_LIBRARY + ".toString("
+ "((("
+ beanName
+ ")_jspx_page_context.findAttribute("
+ "\""
+ name + "\"))." + methodName + "())));");
} else if (!STRICT_GET_PROPERTY || varInfoNames.contains(name)) {
// The object is a custom action with an associated
// VariableInfo entry for this name.
// Get the class name and then introspect at runtime.
out.printil("out.write(" + JSP_RUNTIME_LIBRARY + ".toString"
+ "(" + JSP_RUNTIME_LIBRARY + ".handleGetProperty"
+ "(_jspx_page_context.findAttribute(\""
+ name
+ "\"), \""
+ property
+ "\")));");
} else {
StringBuilder msg = new StringBuilder();
msg.append("file:");
msg.append(n.getStart());
msg.append(" jsp:getProperty for bean with name '");
msg.append(name);
msg.append(
"'. Name was not previously introduced as per JSP.5.3");
throw new JasperException(msg.toString());
}
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.SetProperty n) throws JasperException {
String name = n.getTextAttribute("name");
String property = n.getTextAttribute("property");
String param = n.getTextAttribute("param");
Node.JspAttribute value = n.getValue();
n.setBeginJavaLine(out.getJavaLine());
if ("*".equals(property)) {
out.printil(JSP_RUNTIME_LIBRARY + ".introspect("
+ "_jspx_page_context.findAttribute("
+ "\""
+ name + "\"), request);");
} else if (value == null) {
if (param == null)
param = property; // default to same as property
out.printil(JSP_RUNTIME_LIBRARY + ".introspecthelper("
+ "_jspx_page_context.findAttribute(\""
+ name
+ "\"), \""
+ property
+ "\", request.getParameter(\""
+ param
+ "\"), "
+ "request, \""
+ param
+ "\", false);");
} else if (value.isExpression()) {
out.printil(JSP_RUNTIME_LIBRARY + ".handleSetProperty("
+ "_jspx_page_context.findAttribute(\""
+ name
+ "\"), \"" + property + "\",");
out.print(attributeValue(value, false, null));
out.println(");");
} else if (value.isELInterpreterInput()) {
// We've got to resolve the very call to the interpreter
// at runtime since we don't know what type to expect
// in the general case; we thus can't hard-wire the call
// into the generated code. (XXX We could, however,
// optimize the case where the bean is exposed with
// , much as the code here does for
// getProperty.)
// The following holds true for the arguments passed to
// JspRuntimeLibrary.handleSetPropertyExpression():
// - 'pageContext' is a VariableResolver.
// - 'this' (either the generated Servlet or the generated tag
// handler for Tag files) is a FunctionMapper.
out.printil(JSP_RUNTIME_LIBRARY + ".handleSetPropertyExpression("
+ "_jspx_page_context.findAttribute(\""
+ name
+ "\"), \""
+ property
+ "\", "
+ quote(value.getValue())
+ ", "
+ "_jspx_page_context, "
+ value.getEL().getMapName() + ");");
} else if (value.isNamedAttribute()) {
// If the value for setProperty was specified via
// jsp:attribute, first generate code to evaluate
// that body.
String valueVarName = generateNamedAttributeValue(value
.getNamedAttributeNode());
out.printil(JSP_RUNTIME_LIBRARY + ".introspecthelper("
+ "_jspx_page_context.findAttribute(\""
+ name
+ "\"), \""
+ property
+ "\", "
+ valueVarName
+ ", null, null, false);");
} else {
out.printin(JSP_RUNTIME_LIBRARY + ".introspecthelper("
+ "_jspx_page_context.findAttribute(\""
+ name
+ "\"), \"" + property + "\", ");
out.print(attributeValue(value, false, null));
out.println(", null, null, false);");
}
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.UseBean n) throws JasperException {
String name = n.getTextAttribute("id");
String scope = n.getTextAttribute("scope");
String klass = n.getTextAttribute("class");
String type = n.getTextAttribute("type");
Node.JspAttribute beanName = n.getBeanName();
// If "class" is specified, try an instantiation at compile time
boolean generateNew = false;
String canonicalName = null; // Canonical name for klass
if (klass != null) {
try {
Class> bean = ctxt.getClassLoader().loadClass(klass);
if (klass.indexOf('$') >= 0) {
// Obtain the canonical type name
canonicalName = bean.getCanonicalName();
} else {
canonicalName = klass;
}
int modifiers = bean.getModifiers();
if (!Modifier.isPublic(modifiers)
|| Modifier.isInterface(modifiers)
|| Modifier.isAbstract(modifiers)) {
throw new Exception("Invalid bean class modifier");
}
// Check that there is a 0 arg constructor
bean.getConstructor(new Class[] {});
// At compile time, we have determined that the bean class
// exists, with a public zero constructor, new() can be
// used for bean instantiation.
generateNew = true;
} catch (Exception e) {
// Cannot instantiate the specified class, either a
// compilation error or a runtime error will be raised,
// depending on a compiler flag.
if (ctxt.getOptions()
.getErrorOnUseBeanInvalidClassAttribute()) {
err.jspError(n, MESSAGES.invalidUseBeanAttributeClass(klass));
}
if (canonicalName == null) {
// Doing our best here to get a canonical name
// from the binary name, should work 99.99% of time.
canonicalName = klass.replace('$', '.');
}
}
if (type == null) {
// if type is unspecified, use "class" as type of bean
type = canonicalName;
}
}
// JSP.5.1, Sematics, para 1 - lock not required for request or
// page scope
String scopename = PAGE_CONTEXT + ".PAGE_SCOPE"; // Default to page
String lock = null;
if ("request".equals(scope)) {
scopename = PAGE_CONTEXT + ".REQUEST_SCOPE";
} else if ("session".equals(scope)) {
scopename = PAGE_CONTEXT + ".SESSION_SCOPE";
lock = "session";
} else if ("application".equals(scope)) {
scopename = PAGE_CONTEXT + ".APPLICATION_SCOPE";
lock = "application";
}
n.setBeginJavaLine(out.getJavaLine());
// Declare bean
out.printin(type);
out.print(' ');
out.print(name);
out.println(" = null;");
// Lock (if required) while getting or creating bean
if (lock != null) {
out.printin("synchronized (");
out.print(lock);
out.println(") {");
out.pushIndent();
}
// Locate bean from context
out.printin(name);
out.print(" = (");
out.print(type);
out.print(") _jspx_page_context.getAttribute(");
out.print(quote(name));
out.print(", ");
out.print(scopename);
out.println(");");
// Create bean
/*
* Check if bean is already there
*/
out.printin("if (");
out.print(name);
out.println(" == null){");
out.pushIndent();
if (klass == null && beanName == null) {
/*
* If both class name and beanName is not specified, the bean
* must be found locally, otherwise it's an error
*/
out.printin("throw new " + INSTANTIATION_EXCEPTION + "(\"bean ");
out.print(name);
out.println(" not found within scope\");");
} else {
/*
* Instantiate the bean if it is not in the specified scope.
*/
if (!generateNew) {
String binaryName;
if (beanName != null) {
if (beanName.isNamedAttribute()) {
// If the value for beanName was specified via
// jsp:attribute, first generate code to evaluate
// that body.
binaryName = generateNamedAttributeValue(beanName
.getNamedAttributeNode());
} else {
binaryName = attributeValue(beanName, false, String.class);
}
} else {
// Implies klass is not null
binaryName = quote(klass);
}
out.printil("try {");
out.pushIndent();
out.printin(name);
out.print(" = (");
out.print(type);
out.print(") " + BEANS + ".instantiate(");
out.print("this.getClass().getClassLoader(), ");
out.print(binaryName);
out.println(");");
out.popIndent();
/*
* Note: Beans.instantiate throws ClassNotFoundException if
* the bean class is abstract.
*/
out.printil("} catch (" + CLASS_NOT_FOUND_EXCEPTION + " exc) {");
out.pushIndent();
out.printil("throw new " + INSTANTIATION_EXCEPTION + "(exc.getMessage());");
out.popIndent();
out.printil("} catch (" + EXCEPTION + " exc) {");
out.pushIndent();
printinThreePart(out,"throw new ", SERVLET_EXCEPTION, "(");
out.print("\"Cannot create bean of class \" + ");
out.print(binaryName);
out.println(", exc);");
out.popIndent();
out.printil("}"); // close of try
} else {
// Implies klass is not null
// Generate codes to instantiate the bean class
out.printin(name);
out.print(" = new ");
out.print(canonicalName);
out.println("();");
}
/*
* Set attribute for bean in the specified scope
*/
out.printin("_jspx_page_context.setAttribute(");
out.print(quote(name));
out.print(", ");
out.print(name);
out.print(", ");
out.print(scopename);
out.println(");");
// Only visit the body when bean is instantiated
visitBody(n);
}
out.popIndent();
out.printil("}");
// End of lock block
if (lock != null) {
out.popIndent();
out.printil("}");
}
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.PlugIn n) throws JasperException {
// As of JSP 3.1, jsp:plugin must not generate any output
n.setBeginJavaLine(out.getJavaLine());
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.NamedAttribute n) throws JasperException {
// Don't visit body of this tag - we already did earlier.
}
@Override
public void visit(Node.CustomTag n) throws JasperException {
// Use plugin to generate more efficient code if there is one.
if (n.useTagPlugin()) {
generateTagPlugin(n);
return;
}
TagHandlerInfo handlerInfo = getTagHandlerInfo(n);
// Create variable names
String baseVar = createTagVarName(n.getQName(), n.getPrefix(), n
.getLocalName());
String tagEvalVar = "_jspx_eval_" + baseVar;
String tagHandlerVar = "_jspx_th_" + baseVar;
String tagPushBodyCountVar = "_jspx_push_body_count_" + baseVar;
// If the tag contains no scripting element, generate its codes
// to a method.
ServletWriter outSave = null;
Node.ChildInfo ci = n.getChildInfo();
if (ci.isScriptless() && !ci.hasScriptingVars()) {
// The tag handler and its body code can reside in a separate
// method if it is scriptless and does not have any scripting
// variable defined.
String tagMethod = "_jspx_meth_" + baseVar;
// Generate a call to this method
out.printin("if (");
out.print(tagMethod);
out.print("(");
if (parent != null) {
out.print(parent);
out.print(", ");
}
out.print("_jspx_page_context");
if (pushBodyCountVar != null) {
out.print(", ");
out.print(pushBodyCountVar);
}
out.println("))");
out.pushIndent();
out.printil((methodNesting > 0) ? "return true;" : "return;");
out.popIndent();
// Set up new buffer for the method
outSave = out;
/*
* For fragments, their bodies will be generated in fragment
* helper classes, and the Java line adjustments will be done
* there, hence they are set to null here to avoid double
* adjustments.
*/
GenBuffer genBuffer = new GenBuffer(n,
n.implementsSimpleTag() ? null : n.getBody());
methodsBuffered.add(genBuffer);
out = genBuffer.getOut();
methodNesting++;
// Generate code for method declaration
out.println();
out.pushIndent();
out.printin("private boolean ");
out.print(tagMethod);
out.print("(");
if (parent != null) {
out.print(JSP_TAG);
out.print(" ");
out.print(parent);
out.print(", ");
}
out.print(PAGE_CONTEXT);
out.print(" _jspx_page_context");
if (pushBodyCountVar != null) {
out.print(", int[] ");
out.print(pushBodyCountVar);
}
out.println(")");
out.printil(" throws " + THROWABLE + " {");
out.pushIndent();
// Initialize local variables used in this method.
if (!isTagFile) {
printilTwoPart(out, PAGE_CONTEXT, " pageContext = _jspx_page_context;");
}
printilTwoPart(out, JSP_WRITER, " out = _jspx_page_context.getOut();");
generateLocalVariables(out, n);
}
// Add the named objects to the list of 'introduced' names to enable
// a later test as per JSP.5.3
VariableInfo[] infos = n.getVariableInfos();
if (infos != null && infos.length > 0) {
for (int i = 0; i < infos.length; i++) {
VariableInfo info = infos[i];
if (info != null && info.getVarName() != null)
pageInfo.getVarInfoNames().add(info.getVarName());
}
}
TagVariableInfo[] tagInfos = n.getTagVariableInfos();
if (tagInfos != null && tagInfos.length > 0) {
for (int i = 0; i < tagInfos.length; i++) {
TagVariableInfo tagInfo = tagInfos[i];
if (tagInfo != null) {
String name = tagInfo.getNameGiven();
if (name == null) {
String nameFromAttribute =
tagInfo.getNameFromAttribute();
name = n.getAttributeValue(nameFromAttribute);
}
pageInfo.getVarInfoNames().add(name);
}
}
}
if (n.implementsSimpleTag()) {
generateCustomDoTag(n, handlerInfo, tagHandlerVar);
} else {
/*
* Classic tag handler: Generate code for start element, body,
* and end element
*/
generateCustomStart(n, handlerInfo, tagHandlerVar, tagEvalVar,
tagPushBodyCountVar);
// visit body
String tmpParent = parent;
parent = tagHandlerVar;
boolean isSimpleTagParentSave = isSimpleTagParent;
isSimpleTagParent = false;
String tmpPushBodyCountVar = null;
if (n.implementsTryCatchFinally()) {
tmpPushBodyCountVar = pushBodyCountVar;
pushBodyCountVar = tagPushBodyCountVar;
}
boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
isSimpleTagHandler = false;
visitBody(n);
parent = tmpParent;
isSimpleTagParent = isSimpleTagParentSave;
if (n.implementsTryCatchFinally()) {
pushBodyCountVar = tmpPushBodyCountVar;
}
isSimpleTagHandler = tmpIsSimpleTagHandler;
generateCustomEnd(n, tagHandlerVar, tagEvalVar,
tagPushBodyCountVar);
}
if (ci.isScriptless() && !ci.hasScriptingVars()) {
// Generate end of method
if (methodNesting > 0) {
out.printil("return false;");
}
out.popIndent();
out.printil("}");
out.popIndent();
methodNesting--;
// restore previous writer
out = outSave;
}
}
private static final String DOUBLE_QUOTE = "\\\"";
@Override
public void visit(Node.UninterpretedTag n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
/*
* Write begin tag
*/
out.printin("out.write(\"<");
out.print(n.getQName());
Attributes attrs = n.getNonTaglibXmlnsAttributes();
if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
out.print(" ");
out.print(attrs.getQName(i));
out.print("=");
out.print(DOUBLE_QUOTE);
out.print(escape(attrs.getValue(i).replace("\"", """)));
out.print(DOUBLE_QUOTE);
}
}
attrs = n.getAttributes();
if (attrs != null) {
Node.JspAttribute[] jspAttrs = n.getJspAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
out.print(" ");
out.print(attrs.getQName(i));
out.print("=");
if (jspAttrs[i].isELInterpreterInput()) {
out.print("\\\"\" + ");
String debug = attributeValue(jspAttrs[i], false, String.class);
out.print(debug);
out.print(" + \"\\\"");
} else {
out.print(DOUBLE_QUOTE);
out.print(escape(jspAttrs[i].getValue().replace("\"", """)));
out.print(DOUBLE_QUOTE);
}
}
}
if (n.getBody() != null) {
out.println(">\");");
// Visit tag body
visitBody(n);
/*
* Write end tag
*/
out.printin("out.write(\"");
out.print(n.getQName());
out.println(">\");");
} else {
out.println("/>\");");
}
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.JspElement n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
// Compute attribute value string for XML-style and named
// attributes
Hashtable map = new Hashtable<>();
Node.JspAttribute[] attrs = n.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
String value = null;
String nvp = null;
if (attrs[i].isNamedAttribute()) {
NamedAttribute attr = attrs[i].getNamedAttributeNode();
Node.JspAttribute omitAttr = attr.getOmit();
String omit;
if (omitAttr == null) {
omit = "false";
} else {
omit = attributeValue(omitAttr, false, boolean.class);
if ("true".equals(omit)) {
continue;
}
}
value = generateNamedAttributeValue(
attrs[i].getNamedAttributeNode());
if ("false".equals(omit)) {
nvp = " + \" " + attrs[i].getName() + "=\\\"\" + " +
value + " + \"\\\"\"";
} else {
nvp = " + (" + BOOLEAN + ".valueOf(" + omit + ")?\"\":\" " +
attrs[i].getName() + "=\\\"\" + " + value +
" + \"\\\"\")";
}
} else {
value = attributeValue(attrs[i], false, Object.class);
nvp = " + \" " + attrs[i].getName() + "=\\\"\" + " +
value + " + \"\\\"\"";
}
map.put(attrs[i].getName(), nvp);
}
// Write begin tag, using XML-style 'name' attribute as the
// element name
String elemName = attributeValue(n.getNameAttribute(), false, String.class);
out.printin("out.write(\"<\"");
out.print(" + " + elemName);
// Write remaining attributes
Enumeration enumeration = map.keys();
while (enumeration.hasMoreElements()) {
String attrName = enumeration.nextElement();
out.print(map.get(attrName));
}
// Does the have nested tags other than
//
boolean hasBody = false;
Node.Nodes subelements = n.getBody();
if (subelements != null) {
for (int i = 0; i < subelements.size(); i++) {
Node subelem = subelements.getNode(i);
if (!(subelem instanceof Node.NamedAttribute)) {
hasBody = true;
break;
}
}
}
if (hasBody) {
out.println(" + \">\");");
// Smap should not include the body
n.setEndJavaLine(out.getJavaLine());
// Visit tag body
visitBody(n);
// Write end tag
out.printin("out.write(\"\"");
out.print(" + " + elemName);
out.println(" + \">\");");
} else {
out.println(" + \"/>\");");
n.setEndJavaLine(out.getJavaLine());
}
}
@Override
public void visit(Node.TemplateText n) throws JasperException {
String text = n.getText();
int textSize = text.length();
if (textSize == 0) {
return;
}
if (textSize <= 3) {
// Special case small text strings
n.setBeginJavaLine(out.getJavaLine());
int lineInc = 0;
for (int i = 0; i < textSize; i++) {
char ch = text.charAt(i);
out.printil("out.write(" + quote(ch) + ");");
if (i > 0) {
n.addSmap(lineInc);
}
if (ch == '\n') {
lineInc++;
}
}
n.setEndJavaLine(out.getJavaLine());
return;
}
if (ctxt.getOptions().genStringAsCharArray()) {
// Generate Strings as char arrays, for performance
ServletWriter caOut;
if (charArrayBuffer == null) {
charArrayBuffer = new GenBuffer();
caOut = charArrayBuffer.getOut();
caOut.pushIndent();
textMap = new HashMap<>();
} else {
caOut = charArrayBuffer.getOut();
}
// UTF-8 is up to 4 bytes per character
// String constants are limited to 64k bytes
// Limit string constants here to 16k characters
int textIndex = 0;
int textLength = text.length();
while (textIndex < textLength) {
int len = 0;
if (textLength - textIndex > 16384) {
len = 16384;
} else {
len = textLength - textIndex;
}
String output = text.substring(textIndex, textIndex + len);
String charArrayName = textMap.get(output);
if (charArrayName == null) {
charArrayName = "_jspx_char_array_" + charArrayCount++;
textMap.put(output, charArrayName);
caOut.printin("static char[] ");
caOut.print(charArrayName);
caOut.print(" = ");
caOut.print(quote(output));
caOut.println(".toCharArray();");
}
n.setBeginJavaLine(out.getJavaLine());
out.printil("out.write(" + charArrayName + ");");
n.setEndJavaLine(out.getJavaLine());
textIndex = textIndex + len;
}
return;
}
n.setBeginJavaLine(out.getJavaLine());
out.printin();
StringBuilder sb = new StringBuilder("out.write(\"");
int initLength = sb.length();
int count = JspUtil.CHUNKSIZE;
int srcLine = 0; // relative to starting source line
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
--count;
switch (ch) {
case '"':
sb.append('\\').append('\"');
break;
case '\\':
sb.append('\\').append('\\');
break;
case '\r':
sb.append('\\').append('r');
break;
case '\n':
sb.append('\\').append('n');
srcLine++;
if (breakAtLF || count < 0) {
// Generate an out.write() when see a '\n' in template
sb.append("\");");
out.println(sb.toString());
if (i < text.length() - 1) {
out.printin();
}
sb.setLength(initLength);
count = JspUtil.CHUNKSIZE;
}
// add a Smap for this line
n.addSmap(srcLine);
break;
case '\t': // Not sure we need this
sb.append('\\').append('t');
break;
default:
sb.append(ch);
}
}
if (sb.length() > initLength) {
sb.append("\");");
out.println(sb.toString());
}
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.JspBody n) throws JasperException {
if (n.getBody() != null) {
if (isSimpleTagHandler) {
out.printin(simpleTagHandlerVar);
out.print(".setJspBody(");
generateJspFragment(n, simpleTagHandlerVar);
out.println(");");
} else {
visitBody(n);
}
}
}
@Override
public void visit(Node.InvokeAction n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
// Copy virtual page scope of tag file to page scope of invoking
// page
out.printil("((" + JSP_CONTEXT_WRAPPER + ") this.jspContext).syncBeforeInvoke();");
String varReaderAttr = n.getTextAttribute("varReader");
String varAttr = n.getTextAttribute("var");
if (varReaderAttr != null || varAttr != null) {
out.printil("_jspx_sout = new " + STRING_WRITER + "();");
} else {
out.printil("_jspx_sout = null;");
}
// Invoke fragment, unless fragment is null
out.printin("if (");
out.print(toGetterMethod(n.getTextAttribute("fragment")));
out.println(" != null) {");
out.pushIndent();
out.printin(toGetterMethod(n.getTextAttribute("fragment")));
out.println(".invoke(_jspx_sout);");
out.popIndent();
out.printil("}");
// Store varReader in appropriate scope
if (varReaderAttr != null || varAttr != null) {
String scopeName = n.getTextAttribute("scope");
out.printin("_jspx_page_context.setAttribute(");
if (varReaderAttr != null) {
out.print(quote(varReaderAttr));
out.print(", new " + STRING_READER + "(_jspx_sout.toString())");
} else {
out.print(quote(varAttr));
out.print(", _jspx_sout.toString()");
}
if (scopeName != null) {
out.print(", ");
out.print(getScopeConstant(scopeName));
}
out.println(");");
}
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.DoBodyAction n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
// Copy virtual page scope of tag file to page scope of invoking
// page
out.printil("((" + JSP_CONTEXT_WRAPPER + ") this.jspContext).syncBeforeInvoke();");
// Invoke body
String varReaderAttr = n.getTextAttribute("varReader");
String varAttr = n.getTextAttribute("var");
if (varReaderAttr != null || varAttr != null) {
out.printil("_jspx_sout = new " + STRING_WRITER + "();");
} else {
out.printil("_jspx_sout = null;");
}
out.printil("if (getJspBody() != null)");
out.pushIndent();
out.printil("getJspBody().invoke(_jspx_sout);");
out.popIndent();
// Store varReader in appropriate scope
if (varReaderAttr != null || varAttr != null) {
String scopeName = n.getTextAttribute("scope");
out.printin("_jspx_page_context.setAttribute(");
if (varReaderAttr != null) {
out.print(quote(varReaderAttr));
out.print(", new " + STRING_READER + "(_jspx_sout.toString())");
} else {
out.print(quote(varAttr));
out.print(", _jspx_sout.toString()");
}
if (scopeName != null) {
out.print(", ");
out.print(getScopeConstant(scopeName));
}
out.println(");");
}
// Restore EL context
printilThreePart(out, "jspContext.getELContext().putContext(", JSP_CONTEXT, ".class,getJspContext());");
n.setEndJavaLine(out.getJavaLine());
}
@Override
public void visit(Node.AttributeGenerator n) throws JasperException {
Node.CustomTag tag = n.getTag();
Node.JspAttribute[] attrs = tag.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
if (attrs[i].getName().equals(n.getName())) {
out.print(evaluateAttribute(getTagHandlerInfo(tag),
attrs[i], tag, null));
break;
}
}
}
private TagHandlerInfo getTagHandlerInfo(Node.CustomTag n)
throws JasperException {
Hashtable handlerInfosByShortName =
handlerInfos.get(n.getPrefix());
if (handlerInfosByShortName == null) {
handlerInfosByShortName = new Hashtable<>();
handlerInfos.put(n.getPrefix(), handlerInfosByShortName);
}
TagHandlerInfo handlerInfo =
handlerInfosByShortName.get(n.getLocalName());
if (handlerInfo == null) {
handlerInfo = new TagHandlerInfo(n, n.getTagHandlerClass(), err);
handlerInfosByShortName.put(n.getLocalName(), handlerInfo);
}
return handlerInfo;
}
private void generateTagPlugin(Node.CustomTag n) throws JasperException {
if (n.getAtSTag() != null) {
n.getAtSTag().visit(this);
}
visitBody(n);
if (n.getAtETag() != null) {
n.getAtETag().visit(this);
}
}
private void generateCustomStart(Node.CustomTag n,
TagHandlerInfo handlerInfo, String tagHandlerVar,
String tagEvalVar, String tagPushBodyCountVar)
throws JasperException {
Class> tagHandlerClass =
handlerInfo.getTagHandlerClass();
out.printin("// ");
out.println(n.getQName());
n.setBeginJavaLine(out.getJavaLine());
// Declare AT_BEGIN scripting variables
declareScriptingVars(n, VariableInfo.AT_BEGIN);
saveScriptingVars(n, VariableInfo.AT_BEGIN);
String tagHandlerClassName = tagHandlerClass.getCanonicalName();
if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
out.printin(tagHandlerClassName);
out.print(" ");
out.print(tagHandlerVar);
out.print(" = ");
out.print("(");
out.print(tagHandlerClassName);
out.print(") ");
out.print(n.getTagHandlerPoolName());
out.print(".get(");
out.print(tagHandlerClassName);
out.println(".class);");
} else {
writeNewInstance(tagHandlerVar, tagHandlerClassName);
}
// includes setting the context
generateSetters(n, tagHandlerVar, handlerInfo, false);
// JspIdConsumer (after context has been set)
if (n.implementsJspIdConsumer()) {
out.printin(tagHandlerVar);
out.print(".setJspId(\"");
out.print(createJspId());
out.println("\");");
}
if (n.implementsTryCatchFinally()) {
out.printin("int[] ");
out.print(tagPushBodyCountVar);
out.println(" = new int[] { 0 };");
out.printil("try {");
out.pushIndent();
}
out.printin("int ");
out.print(tagEvalVar);
out.print(" = ");
out.print(tagHandlerVar);
out.println(".doStartTag();");
if (!n.implementsBodyTag()) {
// Synchronize AT_BEGIN scripting variables
syncScriptingVars(n, VariableInfo.AT_BEGIN);
}
if (!n.hasEmptyBody()) {
out.printin("if (");
out.print(tagEvalVar);
printlnThreePart(out, " != ", TAG, ".SKIP_BODY) {");
out.pushIndent();
// Declare NESTED scripting variables
declareScriptingVars(n, VariableInfo.NESTED);
saveScriptingVars(n, VariableInfo.NESTED);
if (n.implementsBodyTag()) {
out.printin("if (");
out.print(tagEvalVar);
printlnThreePart(out, " != ", TAG, ".EVAL_BODY_INCLUDE) {");
// Assume EVAL_BODY_BUFFERED
out.pushIndent();
out.printil("out = _jspx_page_context.pushBody();");
if (n.implementsTryCatchFinally()) {
out.printin(tagPushBodyCountVar);
out.println("[0]++;");
} else if (pushBodyCountVar != null) {
out.printin(pushBodyCountVar);
out.println("[0]++;");
}
out.printin(tagHandlerVar);
printilThreePart(out, ".setBodyContent((", BODY_CONTENT, ") out);");
out.printin(tagHandlerVar);
out.println(".doInitBody();");
out.popIndent();
out.printil("}");
// Synchronize AT_BEGIN and NESTED scripting variables
syncScriptingVars(n, VariableInfo.AT_BEGIN);
syncScriptingVars(n, VariableInfo.NESTED);
} else {
// Synchronize NESTED scripting variables
syncScriptingVars(n, VariableInfo.NESTED);
}
if (n.implementsIterationTag()) {
out.printil("do {");
out.pushIndent();
}
}
// Map the Java lines that handles start of custom tags to the
// JSP line for this tag
n.setEndJavaLine(out.getJavaLine());
}
private void writeNewInstance(String tagHandlerVar, String tagHandlerClassName) {
if (Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
out.printin(tagHandlerClassName);
out.print(" ");
out.print(tagHandlerVar);
out.print(" = (");
out.print(tagHandlerClassName);
out.print(")");
out.print(VAR_INSTANCEMANAGER);
out.print(".newInstance(\"");
out.print(tagHandlerClassName);
out.println("\", this.getClass().getClassLoader());");
} else {
out.printin(tagHandlerClassName);
out.print(" ");
out.print(tagHandlerVar);
out.print(" = (");
out.print("new ");
out.print(tagHandlerClassName);
out.println("());");
out.printin(VAR_INSTANCEMANAGER);
out.print(".newInstance(");
out.print(tagHandlerVar);
out.println(");");
}
}
private void writeDestroyInstance(String tagHandlerVar) {
out.printin(VAR_INSTANCEMANAGER);
out.print(".destroyInstance(");
out.print(tagHandlerVar);
out.println(");");
}
private void generateCustomEnd(Node.CustomTag n, String tagHandlerVar,
String tagEvalVar, String tagPushBodyCountVar) {
if (!n.hasEmptyBody()) {
if (n.implementsIterationTag()) {
out.printin("int evalDoAfterBody = ");
out.print(tagHandlerVar);
out.println(".doAfterBody();");
// Synchronize AT_BEGIN and NESTED scripting variables
syncScriptingVars(n, VariableInfo.AT_BEGIN);
syncScriptingVars(n, VariableInfo.NESTED);
printilThreePart(out, "if (evalDoAfterBody != ", BODY_TAG, ".EVAL_BODY_AGAIN)");
out.pushIndent();
out.printil("break;");
out.popIndent();
out.popIndent();
out.printil("} while (true);");
}
restoreScriptingVars(n, VariableInfo.NESTED);
if (n.implementsBodyTag()) {
out.printin("if (");
out.print(tagEvalVar);
printlnThreePart(out, " != ", TAG, ".EVAL_BODY_INCLUDE) {");
out.pushIndent();
out.printil("out = _jspx_page_context.popBody();");
if (n.implementsTryCatchFinally()) {
out.printin(tagPushBodyCountVar);
out.println("[0]--;");
} else if (pushBodyCountVar != null) {
out.printin(pushBodyCountVar);
out.println("[0]--;");
}
out.popIndent();
out.printil("}");
}
out.popIndent(); // EVAL_BODY
out.printil("}");
}
out.printin("if (");
out.print(tagHandlerVar);
printlnThreePart(out, ".doEndTag() == ", TAG, ".SKIP_PAGE) {");
out.pushIndent();
if (!n.implementsTryCatchFinally()) {
if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
out.printin(n.getTagHandlerPoolName());
out.print(".reuse(");
out.print(tagHandlerVar);
out.println(");");
} else {
out.printin(tagHandlerVar);
out.println(".release();");
writeDestroyInstance(tagHandlerVar);
}
}
if (isTagFile || isFragment) {
printilThreePart(out, "throw new ", SKIP_PAGE_EXCEPTION, "();");
} else {
out.printil((methodNesting > 0) ? "return true;" : "return;");
}
out.popIndent();
out.printil("}");
// Synchronize AT_BEGIN scripting variables
syncScriptingVars(n, VariableInfo.AT_BEGIN);
// TryCatchFinally
if (n.implementsTryCatchFinally()) {
out.popIndent(); // try
out.printil("} catch (" + THROWABLE + " _jspx_exception) {");
out.pushIndent();
out.printin("while (");
out.print(tagPushBodyCountVar);
out.println("[0]-- > 0)");
out.pushIndent();
out.printil("out = _jspx_page_context.popBody();");
out.popIndent();
out.printin(tagHandlerVar);
out.println(".doCatch(_jspx_exception);");
out.popIndent();
out.printil("} finally {");
out.pushIndent();
out.printin(tagHandlerVar);
out.println(".doFinally();");
}
if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
out.printin(n.getTagHandlerPoolName());
out.print(".reuse(");
out.print(tagHandlerVar);
out.println(");");
} else {
out.printin(tagHandlerVar);
out.println(".release();");
writeDestroyInstance(tagHandlerVar);
}
if (n.implementsTryCatchFinally()) {
out.popIndent();
out.printil("}");
}
// Declare and synchronize AT_END scripting variables (must do this
// outside the try/catch/finally block)
declareScriptingVars(n, VariableInfo.AT_END);
syncScriptingVars(n, VariableInfo.AT_END);
restoreScriptingVars(n, VariableInfo.AT_BEGIN);
}
private void generateCustomDoTag(Node.CustomTag n,
TagHandlerInfo handlerInfo, String tagHandlerVar)
throws JasperException {
Class> tagHandlerClass =
handlerInfo.getTagHandlerClass();
n.setBeginJavaLine(out.getJavaLine());
out.printin("// ");
out.println(n.getQName());
// Declare AT_BEGIN scripting variables
declareScriptingVars(n, VariableInfo.AT_BEGIN);
saveScriptingVars(n, VariableInfo.AT_BEGIN);
String tagHandlerClassName = tagHandlerClass.getCanonicalName();
writeNewInstance(tagHandlerVar, tagHandlerClassName);
generateSetters(n, tagHandlerVar, handlerInfo, true);
// JspIdConsumer (after context has been set)
if (n.implementsJspIdConsumer()) {
out.printin(tagHandlerVar);
out.print(".setJspId(\"");
out.print(createJspId());
out.println("\");");
}
// Set the body
if (findJspBody(n) == null) {
/*
* Encapsulate body of custom tag invocation in JspFragment and
* pass it to tag handler's setJspBody(), unless tag body is
* empty
*/
if (!n.hasEmptyBody()) {
out.printin(tagHandlerVar);
out.print(".setJspBody(");
generateJspFragment(n, tagHandlerVar);
out.println(");");
}
} else {
/*
* Body of tag is the body of the element. The visit
* method for that element is going to encapsulate that
* element's body in a JspFragment and pass it to the tag
* handler's setJspBody()
*/
String tmpTagHandlerVar = simpleTagHandlerVar;
simpleTagHandlerVar = tagHandlerVar;
boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
isSimpleTagHandler = true;
visitBody(n);
simpleTagHandlerVar = tmpTagHandlerVar;
isSimpleTagHandler = tmpIsSimpleTagHandler;
}
out.printin(tagHandlerVar);
out.println(".doTag();");
restoreScriptingVars(n, VariableInfo.AT_BEGIN);
// Synchronize AT_BEGIN scripting variables
syncScriptingVars(n, VariableInfo.AT_BEGIN);
// Declare and synchronize AT_END scripting variables
declareScriptingVars(n, VariableInfo.AT_END);
syncScriptingVars(n, VariableInfo.AT_END);
// Resource injection
writeDestroyInstance(tagHandlerVar);
n.setEndJavaLine(out.getJavaLine());
}
private void declareScriptingVars(Node.CustomTag n, int scope) {
if (isFragment) {
// No need to declare Java variables, if we inside a
// JspFragment, because a fragment is always scriptless.
return;
}
List