blob: bd5530915503c85af9c2553eaad5f60dae728728 [file] [log] [blame]
/*
* 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.sling.scripting.jsp.jasper.compiler;
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.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.servlet.jsp.tagext.TagAttributeInfo;
import javax.servlet.jsp.tagext.TagInfo;
import javax.servlet.jsp.tagext.TagVariableInfo;
import javax.servlet.jsp.tagext.VariableInfo;
import org.apache.sling.scripting.jsp.jasper.Constants;
import org.apache.sling.scripting.jsp.jasper.JasperException;
import org.apache.sling.scripting.jsp.jasper.JspCompilationContext;
import org.apache.sling.scripting.jsp.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("org.apache.sling.scripting.jsp.jasper.compiler.Generator.VAR_EXPRESSIONFACTORY", "_el_expressionfactory");
private static final String VAR_ANNOTATIONPROCESSOR =
System.getProperty("org.apache.sling.scripting.jsp.jasper.compiler.Generator.VAR_ANNOTATIONPROCESSOR", "_jsp_annotationprocessor");
private ServletWriter out;
private ArrayList methodsBuffered;
private FragmentHelperClass fragmentHelperClass;
private ErrorDispatcher err;
private BeanRepository beanInfo;
private JspCompilationContext ctxt;
private boolean isPoolingEnabled;
private boolean breakAtLF;
private String jspIdPrefix;
private int jspId;
private PageInfo pageInfo;
private Vector<String> tagHandlerPoolNames;
private GenBuffer charArrayBuffer;
/**
* @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 "";
}
StringBuffer b = new StringBuffer();
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) {
StringBuffer b = new StringBuffer();
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() throws JasperException {
if (this.jspIdPrefix == null) {
StringBuffer sb = new StringBuffer(32);
String name = ctxt.getServletJavaFileName();
sb.append("jsp_").append(Math.abs(name.hashCode())).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(new String(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 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
.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, boolean hasEmptyBody) {
String poolName = null;
poolName = "_jspx_tagPool_" + prefix + "_" + shortName;
if (attrs != null) {
String[] attrNames = new String[attrs.getLength()];
for (int i = 0; i < attrNames.length; i++) {
attrNames[i] = attrs.getQName(i);
}
Arrays.sort(attrNames, Collections.reverseOrder());
for (int i = 0; i < attrNames.length; i++) {
poolName = poolName + "_" + attrNames[i];
}
}
if (hasEmptyBody) {
poolName = poolName + "_nobody";
}
return JspUtil.makeJavaIdentifier(poolName);
}
}
page.visit(new TagHandlerPoolVisitor(tagHandlerPoolNames));
}
private void declareTemporaryScriptingVars(Node.Nodes page)
throws JasperException {
class ScriptingVarVisitor extends Node.Visitor {
private Vector vars;
ScriptingVarVisitor() {
vars = new Vector();
}
@Override
public void visit(Node.CustomTag n) throws JasperException {
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()) {
out.printil("private void _jspInit(ServletConfig 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(" = org.apache.sling.scripting.jsp.jasper.runtime.TagHandlerPool.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_ANNOTATIONPROCESSOR);
out.print(" = (org.apache.sling.scripting.jsp.jasper.runtime.AnnotationProcessor) ");
if (ctxt.isTagFile()) {
out.print("config");
} else {
out.print("getServletConfig()");
}
out.println(".getServletContext().getAttribute(org.apache.sling.scripting.jsp.jasper.runtime.AnnotationProcessor.class.getName());");
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) throws JasperException {
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() throws JasperException {
Iterator iter = pageInfo.getImports().iterator();
while (iter.hasNext()) {
out.printin("import ");
out.print((String) iter.next());
out.println(";");
}
out.println();
}
/**
* Generation of static initializers in preamble. For example, dependant
* list, el function map, prefix map. (shared by servlet and tag handler
* preamble generation)
*/
private void genPreambleStaticInitializers() throws JasperException {
out.printil("private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();");
out.println();
// Static data for getDependants()
out.printil("private static java.util.List _jspx_dependants;");
out.println();
List dependants = pageInfo.getDependants();
Iterator iter = dependants.iterator();
if (!dependants.isEmpty()) {
out.printil("static {");
out.pushIndent();
out.printin("_jspx_dependants = new java.util.ArrayList(");
out.print("" + dependants.size());
out.println(");");
while (iter.hasNext()) {
out.printin("_jspx_dependants.add(\"");
out.print((String) iter.next());
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(String className)
throws JasperException {
if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
for (int i = 0; i < tagHandlerPoolNames.size(); i++) {
out.printil("private org.apache.sling.scripting.jsp.jasper.runtime.TagHandlerPool "
+ tagHandlerPoolNames.elementAt(i) + ";");
}
out.println();
}
out.printin("private javax.el.ExpressionFactory ");
out.print(VAR_EXPRESSIONFACTORY);
out.println(";");
out.printin("private org.apache.sling.scripting.jsp.jasper.runtime.AnnotationProcessor ");
out.print(VAR_ANNOTATIONPROCESSOR);
out.println(";");
out.println();
}
/**
* Declare general-purpose methods (shared by servlet and tag handler
* preamble generation)
*/
private void genPreambleMethods() throws JasperException {
// Method used to get compile time file dependencies
out.printil("public Object getDependants() {");
out.pushIndent();
out.printil("return _jspx_dependants;");
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 org.apache.sling.scripting.jsp.jasper.runtime.JspSourceDependent");
if (!pageInfo.isThreadSafe()) {
out.println(",");
out.printin(" SingleThreadModel");
}
out.println(" {");
out.pushIndent();
// Class body begins here
generateDeclarations(page);
// Static initializations here
genPreambleStaticInitializers();
// Class variable declarations
genPreambleClassVariableDeclarations(servletClassName);
// Constructor
// generateConstructor(className);
// Methods here
genPreambleMethods();
// Now the service method
out.printin("public void ");
out.print(serviceMethodName);
out.println("(HttpServletRequest request, HttpServletResponse response)");
out.println(" throws java.io.IOException, ServletException {");
out.pushIndent();
out.println();
// Local variable declarations
out.printil("PageContext pageContext = null;");
if (pageInfo.isSession()) {
out.printil("HttpSession session = null;");
}
if (pageInfo.isErrorPage()) {
out.printil("Throwable exception = org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.getThrowable(request);");
// Removed the following block because of SLING-2094
// If we leave it in, an error handler will always set the status code to 500!
/* out.printil("if (exception != null) {");
out.pushIndent();
out.printil("response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);");
out.popIndent();
out.printil("}");*/
}
out.printil("ServletContext application = null;");
out.printil("ServletConfig config = null;");
out.printil("JspWriter out = null;");
out.printil("Object page = this;");
out.printil("JspWriter _jspx_out = null;");
out.printil("PageContext _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.1\");");
}
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 <jsp:output> action is set to
* "no" or "false" - JSP document without a <jsp:root>
*/
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(\"<?xml version=\\\"1.0\\\" encoding=\\\""
+ charSet + "\\\"?>\\n\");");
}
/*
* Output a DOCTYPE declaration if the doctype-root-element appears. If
* doctype-public appears: <!DOCTYPE name PUBLIC "doctypePublic"
* "doctypeSystem"> else <!DOCTYPE name SYSTEM "doctypeSystem" >
*/
String doctypeName = pageInfo.getDoctypeName();
if (doctypeName != null) {
String doctypePublic = pageInfo.getDoctypePublic();
String doctypeSystem = pageInfo.getDoctypeSystem();
out.printin("out.write(\"<!DOCTYPE ");
out.print(doctypeName);
if (doctypePublic == null) {
out.print(" SYSTEM \\\"");
} else {
out.print(" PUBLIC \\\"");
out.print(doctypePublic);
out.print("\\\" \\\"");
}
out.print(doctypeSystem);
out.println("\\\">\\n\");");
}
}
/*
* Generates the constructor. (shared by servlet and tag handler preamble
* generation)
*/
private void generateConstructor(String className) {
out.printil("public " + className + "() {");
out.printil("}");
out.println();
}
/**
* A visitor that generates codes for the elements in the page.
*/
class GenerateVisitor extends Node.Visitor {
/*
* Hashtable containing introspection information on tag handlers:
* <key>: tag prefix <value>: hashtable containing introspection on tag
* handlers: <key>: tag short name <value>: introspection info of tag
* handler for <prefix:shortName> tag
*/
private Hashtable handlerInfos;
private 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 boolean isTagFile;
private ServletWriter out;
private ArrayList methodsBuffered;
private FragmentHelperClass fragmentHelperClass;
private int methodNesting;
private TagInfo tagInfo;
private ClassLoader loader;
private int charArrayCount;
private HashMap textMap;
/**
* Constructor.
*/
public GenerateVisitor(boolean isTagFile, ServletWriter out,
ArrayList methodsBuffered,
FragmentHelperClass fragmentHelperClass, ClassLoader loader,
TagInfo tagInfo) {
this.isTagFile = isTagFile;
this.out = out;
this.methodsBuffered = methodsBuffered;
this.fragmentHelperClass = fragmentHelperClass;
this.loader = loader;
this.tagInfo = tagInfo;
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 "org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf("
+ v + "), request.getCharacterEncoding())";
}
return v;
} else if (attr.isELInterpreterInput()) {
boolean replaceESC = v.indexOf(Constants.ESC) > 0;
v = JspUtil.interpreterCall(this.isTagFile, v, expectedType,
attr.getEL().getMapName(), false);
// XXX ESC replacement hack
if (replaceESC) {
v = "(" + v + ").replace(" + Constants.ESCStr + ", '$')";
}
if (encode) {
return "org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.URLEncode("
+ v + ", request.getCharacterEncoding())";
}
return v;
} else if (attr.isNamedAttribute()) {
return attr.getNamedAttributeNode().getTemporaryVariableName();
} else {
if (encode) {
return "org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.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 {
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("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary."
+ "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("
+ JspUtil.interpreterCall(this.isTagFile, n.getType() + "{"
+ new String(n.getText()) + "}", String.class,
n.getEL().getMapName(), false) + ");");
} else {
out.printil("out.write("
+ quote(n.getType() + "{" + new String(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("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.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.
* <p>
* 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) throws JasperException {
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) {
out.printil("throw new SkipPageException();");
} 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 = JspUtil.getCanonicalName(bean);
java.lang.reflect.Method meth = JspRuntimeLibrary
.getReadMethod(bean, property);
String methodName = meth.getName();
out
.printil("out.write(org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.toString("
+ "((("
+ beanName
+ ")_jspx_page_context.findAttribute("
+ "\""
+ name + "\"))." + methodName + "())));");
} else {
// The object could be a custom action with an associated
// VariableInfo entry for this name.
// Get the class name and then introspect at runtime.
out
.printil("out.write(org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.toString"
+ "(org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
+ "(_jspx_page_context.getAttribute(\""
+ name
+ "\", PageContext.PAGE_SCOPE), \""
+ property
+ "\")));");
}
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("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.introspect("
+ "_jspx_page_context.findAttribute("
+ "\""
+ name + "\"), request);");
} else if (value == null) {
if (param == null) {
param = property; // default to same as property
}
out
.printil("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+ "_jspx_page_context.findAttribute(\""
+ name
+ "\"), \""
+ property
+ "\", request.getParameter(\""
+ param
+ "\"), "
+ "request, \""
+ param
+ "\", false);");
} else if (value.isExpression()) {
out
.printil("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.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
// <jsp:useBean>, 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("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.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("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.introspecthelper("
+ "_jspx_page_context.findAttribute(\""
+ name
+ "\"), \""
+ property
+ "\", "
+ valueVarName
+ ", null, null, false);");
} else {
out
.printin("org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.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 = JspUtil.getCanonicalName(bean);
} 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, "jsp.error.invalid.bean", 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;
}
}
String scopename = "PageContext.PAGE_SCOPE"; // Default to page
String lock = "_jspx_page_context";
if ("request".equals(scope)) {
scopename = "PageContext.REQUEST_SCOPE";
lock = "request";
} else if ("session".equals(scope)) {
scopename = "PageContext.SESSION_SCOPE";
lock = "session";
} else if ("application".equals(scope)) {
scopename = "PageContext.APPLICATION_SCOPE";
lock = "application";
}
n.setBeginJavaLine(out.getJavaLine());
// Declare bean
out.printin(type);
out.print(' ');
out.print(name);
out.println(" = null;");
// Lock while getting or creating bean
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 alredy 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 java.lang.InstantiationException(\"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(") java.beans.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 (ClassNotFoundException exc) {");
out.pushIndent();
out
.printil("throw new InstantiationException(exc.getMessage());");
out.popIndent();
out.printil("} catch (Exception exc) {");
out.pushIndent();
out.printin("throw new ServletException(");
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
out.popIndent();
out.printil("}");
n.setEndJavaLine(out.getJavaLine());
}
/**
* @return a string for the form 'attr = "value"'
*/
private String makeAttr(String attr, String value) {
if (value == null) {
return "";
}
return " " + attr + "=\"" + value + '\"';
}
@Override
public void visit(Node.PlugIn n) throws JasperException {
/**
* A visitor to handle <jsp:param> in a plugin
*/
class ParamVisitor extends Node.Visitor {
private boolean ie;
ParamVisitor(boolean ie) {
this.ie = ie;
}
@Override
public void visit(Node.ParamAction n) throws JasperException {
String name = n.getTextAttribute("name");
if (name.equalsIgnoreCase("object")) {
name = "java_object";
} else if (name.equalsIgnoreCase("type")) {
name = "java_type";
}
n.setBeginJavaLine(out.getJavaLine());
// XXX - Fixed a bug here - value used to be output
// inline, which is only okay if value is not an EL
// expression. Also, key/value pairs for the
// embed tag were not being generated correctly.
// Double check that this is now the correct behavior.
if (ie) {
// We want something of the form
// out.println( "<param name=\"blah\"
// value=\"" + ... + "\">" );
out.printil("out.write( \"<param name=\\\""
+ escape(name)
+ "\\\" value=\\\"\" + "
+ attributeValue(n.getValue(), false,
String.class) + " + \"\\\">\" );");
out.printil("out.write(\"\\n\");");
} else {
// We want something of the form
// out.print( " blah=\"" + ... + "\"" );
out.printil("out.write( \" "
+ escape(name)
+ "=\\\"\" + "
+ attributeValue(n.getValue(), false,
String.class) + " + \"\\\"\" );");
}
n.setEndJavaLine(out.getJavaLine());
}
}
String type = n.getTextAttribute("type");
String code = n.getTextAttribute("code");
String name = n.getTextAttribute("name");
Node.JspAttribute height = n.getHeight();
Node.JspAttribute width = n.getWidth();
String hspace = n.getTextAttribute("hspace");
String vspace = n.getTextAttribute("vspace");
String align = n.getTextAttribute("align");
String iepluginurl = n.getTextAttribute("iepluginurl");
String nspluginurl = n.getTextAttribute("nspluginurl");
String codebase = n.getTextAttribute("codebase");
String archive = n.getTextAttribute("archive");
String jreversion = n.getTextAttribute("jreversion");
String widthStr = null;
if (width != null) {
if (width.isNamedAttribute()) {
widthStr = generateNamedAttributeValue(width
.getNamedAttributeNode());
} else {
widthStr = attributeValue(width, false, String.class);
}
}
String heightStr = null;
if (height != null) {
if (height.isNamedAttribute()) {
heightStr = generateNamedAttributeValue(height
.getNamedAttributeNode());
} else {
heightStr = attributeValue(height, false, String.class);
}
}
if (iepluginurl == null) {
iepluginurl = Constants.IE_PLUGIN_URL;
}
if (nspluginurl == null) {
nspluginurl = Constants.NS_PLUGIN_URL;
}
n.setBeginJavaLine(out.getJavaLine());
// If any of the params have their values specified by
// jsp:attribute, prepare those values first.
// Look for a params node and prepare its param subelements:
Node.JspBody jspBody = findJspBody(n);
if (jspBody != null) {
Node.Nodes subelements = jspBody.getBody();
if (subelements != null) {
for (int i = 0; i < subelements.size(); i++) {
Node m = subelements.getNode(i);
if (m instanceof Node.ParamsAction) {
prepareParams(m);
break;
}
}
}
}
// XXX - Fixed a bug here - width and height can be set
// dynamically. Double-check if this generation is correct.
// IE style plugin
// <object ...>
// First compose the runtime output string
String s0 = "<object"
+ makeAttr("classid", ctxt.getOptions().getIeClassId())
+ makeAttr("name", name);
String s1 = "";
if (width != null) {
s1 = " + \" width=\\\"\" + " + widthStr + " + \"\\\"\"";
}
String s2 = "";
if (height != null) {
s2 = " + \" height=\\\"\" + " + heightStr + " + \"\\\"\"";
}
String s3 = makeAttr("hspace", hspace) + makeAttr("vspace", vspace)
+ makeAttr("align", align)
+ makeAttr("codebase", iepluginurl) + '>';
// Then print the output string to the java file
out.printil("out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3)
+ ");");
out.printil("out.write(\"\\n\");");
// <param > for java_code
s0 = "<param name=\"java_code\"" + makeAttr("value", code) + '>';
out.printil("out.write(" + quote(s0) + ");");
out.printil("out.write(\"\\n\");");
// <param > for java_codebase
if (codebase != null) {
s0 = "<param name=\"java_codebase\""
+ makeAttr("value", codebase) + '>';
out.printil("out.write(" + quote(s0) + ");");
out.printil("out.write(\"\\n\");");
}
// <param > for java_archive
if (archive != null) {
s0 = "<param name=\"java_archive\""
+ makeAttr("value", archive) + '>';
out.printil("out.write(" + quote(s0) + ");");
out.printil("out.write(\"\\n\");");
}
// <param > for type
s0 = "<param name=\"type\""
+ makeAttr("value", "application/x-java-"
+ type
+ ";"
+ ((jreversion == null) ? "" : "version="
+ jreversion)) + '>';
out.printil("out.write(" + quote(s0) + ");");
out.printil("out.write(\"\\n\");");
/*
* generate a <param> for each <jsp:param> in the plugin body
*/
if (n.getBody() != null) {
n.getBody().visit(new ParamVisitor(true));
}
/*
* Netscape style plugin part
*/
out.printil("out.write(" + quote("<comment>") + ");");
out.printil("out.write(\"\\n\");");
s0 = "<EMBED"
+ makeAttr("type", "application/x-java-"
+ type
+ ";"
+ ((jreversion == null) ? "" : "version="
+ jreversion)) + makeAttr("name", name);
// s1 and s2 are the same as before.
s3 = makeAttr("hspace", hspace) + makeAttr("vspace", vspace)
+ makeAttr("align", align)
+ makeAttr("pluginspage", nspluginurl)
+ makeAttr("java_code", code)
+ makeAttr("java_codebase", codebase)
+ makeAttr("java_archive", archive);
out.printil("out.write(" + quote(s0) + s1 + s2 + " + " + quote(s3)
+ ");");
/*
* Generate a 'attr = "value"' for each <jsp:param> in plugin body
*/
if (n.getBody() != null) {
n.getBody().visit(new ParamVisitor(false));
}
out.printil("out.write(" + quote("/>") + ");");
out.printil("out.write(\"\\n\");");
out.printil("out.write(" + quote("<noembed>") + ");");
out.printil("out.write(\"\\n\");");
/*
* Fallback
*/
if (n.getBody() != null) {
visitBody(n);
out.printil("out.write(\"\\n\");");
}
out.printil("out.write(" + quote("</noembed>") + ");");
out.printil("out.write(\"\\n\");");
out.printil("out.write(" + quote("</comment>") + ");");
out.printil("out.write(\"\\n\");");
out.printil("out.write(" + quote("</object>") + ");");
out.printil("out.write(\"\\n\");");
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("javax.servlet.jsp.tagext.JspTag ");
out.print(parent);
out.print(", ");
}
out.print("PageContext _jspx_page_context");
if (pushBodyCountVar != null) {
out.print(", int[] ");
out.print(pushBodyCountVar);
}
out.println(")");
out.printil(" throws Throwable {");
out.pushIndent();
// Initilaize local variables used in this method.
if (!isTagFile) {
out
.printil("PageContext pageContext = _jspx_page_context;");
}
out.printil("JspWriter out = _jspx_page_context.getOut();");
generateLocalVariables(out, n);
}
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 SINGLE_QUOTE = "'";
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();
int attrsLen = (attrs == null) ? 0 : attrs.getLength();
for (int i = 0; i < attrsLen; i++) {
out.print(" ");
out.print(attrs.getQName(i));
out.print("=");
String quote = DOUBLE_QUOTE;
String value = attrs.getValue(i);
if (value.indexOf('"') != -1) {
quote = SINGLE_QUOTE;
}
out.print(quote);
out.print(value);
out.print(quote);
}
attrs = n.getAttributes();
attrsLen = (attrs == null) ? 0 : attrs.getLength();
Node.JspAttribute[] jspAttrs = n.getJspAttributes();
for (int i = 0; i < attrsLen; i++) {
out.print(" ");
out.print(attrs.getQName(i));
out.print("=");
if (jspAttrs[i].isELInterpreterInput()) {
out.print("\\\"\" + ");
out.print(attributeValue(jspAttrs[i], false, String.class));
out.print(" + \"\\\"");
} else {
String quote = DOUBLE_QUOTE;
String value = attrs.getValue(i);
if (value.indexOf('"') != -1) {
quote = SINGLE_QUOTE;
}
out.print(quote);
out.print(value);
out.print(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 attrStr = null;
if (attrs[i].isNamedAttribute()) {
attrStr = generateNamedAttributeValue(attrs[i]
.getNamedAttributeNode());
} else {
attrStr = attributeValue(attrs[i], false, Object.class);
}
String s = " + \" " + attrs[i].getName() + "=\\\"\" + "
+ attrStr + " + \"\\\"\"";
map.put(attrs[i].getName(), s);
}
// 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 = (String) enumeration.nextElement();
out.print((String) map.get(attrName));
}
// Does the <jsp:element> have nested tags other than
// <jsp:attribute>
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();
}
String charArrayName = (String) textMap.get(text);
if (charArrayName == null) {
charArrayName = "_jspx_char_array_" + charArrayCount++;
textMap.put(text, charArrayName);
caOut.printin("static char[] ");
caOut.print(charArrayName);
caOut.print(" = ");
caOut.print(quote(text));
caOut.println(".toCharArray();");
}
n.setBeginJavaLine(out.getJavaLine());
out.printil("out.write(" + charArrayName + ");");
n.setEndJavaLine(out.getJavaLine());
return;
}
n.setBeginJavaLine(out.getJavaLine());
out.printin();
StringBuffer sb = new StringBuffer("out.write(\"");
int initLength = sb.length();
int count = JspUtil.CHUNKSIZE;
int srcLine = 0; // relative to starting srouce 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("((org.apache.sling.scripting.jsp.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();");
String varReaderAttr = n.getTextAttribute("varReader");
String varAttr = n.getTextAttribute("var");
if (varReaderAttr != null || varAttr != null) {
out.printil("_jspx_sout = new java.io.StringWriter();");
} 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 java.io.StringReader(_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
out.printil("jspContext.getELContext().putContext(JspContext.class,getJspContext());");
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("((org.apache.sling.scripting.jsp.jasper.runtime.JspContextWrapper) 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 java.io.StringWriter();");
} 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 java.io.StringReader(_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
out.printil("jspContext.getELContext().putContext(JspContext.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 = (Hashtable) handlerInfos.get(n
.getPrefix());
if (handlerInfosByShortName == null) {
handlerInfosByShortName = new Hashtable();
handlerInfos.put(n.getPrefix(), handlerInfosByShortName);
}
TagHandlerInfo handlerInfo = (TagHandlerInfo) handlerInfosByShortName
.get(n.getLocalName());
if (handlerInfo == null) {
handlerInfo = new TagHandlerInfo(n, n.getTagHandlerClass(), err, n.getTagHandlerClassName());
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 = handlerInfo.getTagHandlerClassName();
out.printin(tagHandlerClassName);
out.print(" ");
out.print(tagHandlerVar);
out.print(" = ");
if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
out.print("(");
out.print(tagHandlerClassName);
out.print(") ");
out.print(n.getTagHandlerPoolName());
out.print(".get(");
out.print(tagHandlerClassName);
out.println(".class);");
} else {
out.print("new ");
out.print(tagHandlerClassName);
out.println("();");
out.printin("org.apache.sling.scripting.jsp.jasper.runtime.AnnotationHelper.postConstruct(");
out.print(VAR_ANNOTATIONPROCESSOR);
out.print(", ");
out.print(tagHandlerVar);
out.println(");");
}
// 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);
out.println(" != javax.servlet.jsp.tagext.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);
out
.println(" != javax.servlet.jsp.tagext.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);
out
.println(".setBodyContent((javax.servlet.jsp.tagext.BodyContent) 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 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);
out
.printil("if (evalDoAfterBody != javax.servlet.jsp.tagext.BodyTag.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);
out
.println(" != javax.servlet.jsp.tagext.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);
out
.println(".doEndTag() == javax.servlet.jsp.tagext.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();");
out.printin("org.apache.sling.scripting.jsp.jasper.runtime.AnnotationHelper.preDestroy(");
out.print(VAR_ANNOTATIONPROCESSOR);
out.print(", ");
out.print(tagHandlerVar);
out.println(");");
}
}
if (isTagFile || isFragment) {
out.printil("throw new SkipPageException();");
} 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) {
out.printin(n.getTagHandlerPoolName());
out.print(".reuse(");
out.print(tagHandlerVar);
out.println(");");
} else {
out.printin(tagHandlerVar);
out.println(".release();");
out.printin("org.apache.sling.scripting.jsp.jasper.runtime.AnnotationHelper.preDestroy(");
out.print(VAR_ANNOTATIONPROCESSOR);
out.print(", ");
out.print(tagHandlerVar);
out.println(");");
}
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 = handlerInfo.getTagHandlerClassName();
out.printin(tagHandlerClassName);
out.print(" ");
out.print(tagHandlerVar);
out.print(" = ");
out.print("new ");
out.print(tagHandlerClassName);
out.println("();");
// Resource injection
out.printin("org.apache.sling.scripting.jsp.jasper.runtime.AnnotationHelper.postConstruct(");
out.print(VAR_ANNOTATIONPROCESSOR);
out.print(", ");
out.print(tagHandlerVar);
out.println(");");
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 <jsp:body> 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
out.printin("org.apache.sling.scripting.jsp.jasper.runtime.AnnotationHelper.preDestroy(");
out.print(VAR_ANNOTATIONPROCESSOR);
out.print(", ");
out.print(tagHandlerVar);
out.println(");");
n.setEndJavaLine(out.getJavaLine());
}
private void declareScriptingVars(Node.CustomTag n, int scope) {
Vector vec = n.getScriptingVars(scope);
if (vec != null) {
for (int i = 0; i < vec.size(); i++) {
Object elem = vec.elementAt(i);
if (elem instanceof VariableInfo) {
VariableInfo varInfo = (VariableInfo) elem;
if (varInfo.getDeclare()) {
out.printin(varInfo.getClassName());
out.print(" ");
out.print(varInfo.getVarName());
out.println(" = null;");
}
} else {
TagVariableInfo tagVarInfo = (TagVariableInfo) elem;
if (tagVarInfo.getDeclare()) {
String varName = tagVarInfo.getNameGiven();
if (varName == null) {
varName = n.getTagData().getAttributeString(
tagVarInfo.getNameFromAttribute());
} else if (tagVarInfo.getNameFromAttribute() != null) {
// alias
continue;
}
out.printin(tagVarInfo.getClassName());
out.print(" ");
out.print(varName);
out.println(" = null;");
}
}
}
}
}
/*
* This method is called as part of the custom tag's start element.
*
* If the given custom tag has a custom nesting level greater than 0,
* save the current values of its scripting variables to temporary
* variables, so those values may be restored in the tag's end element.
* This way, the scripting variables may be synchronized by the given
* tag without affecting their original values.
*/
private void saveScriptingVars(Node.CustomTag n, int scope) {
if (n.getCustomNestingLevel() == 0) {
return;
}
TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
VariableInfo[] varInfos = n.getVariableInfos();
if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
return;
}
if (varInfos.length > 0) {
for (int i = 0; i < varInfos.length; i++) {
if (varInfos[i].getScope() != scope) {
continue;
}
// If the scripting variable has been declared, skip codes
// for saving and restoring it.
if (n.getScriptingVars(scope).contains(varInfos[i])) {
continue;
}
String varName = varInfos[i].getVarName();
String tmpVarName = "_jspx_" + varName + "_"
+ n.getCustomNestingLevel();
out.printin(tmpVarName);
out.print(" = ");
out.print(varName);
out.println(";");
}
} else {
for (int i = 0; i < tagVarInfos.length; i++) {
if (tagVarInfos[i].getScope() != scope) {
continue;
}
// If the scripting variable has been declared, skip codes
// for saving and restoring it.
if (n.getScriptingVars(scope).contains(tagVarInfos[i])) {
continue;
}
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();
out.printin(tmpVarName);
out.print(" = ");
out.print(varName);
out.println(";");
}
}
}
/*
* This method is called as part of the custom tag's end element.
*
* If the given custom tag has a custom nesting level greater than 0,
* restore its scripting variables to their original values that were
* saved in the tag's start element.
*/
private void restoreScriptingVars(Node.CustomTag n, int scope) {
if (n.getCustomNestingLevel() == 0) {
return;
}
TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
VariableInfo[] varInfos = n.getVariableInfos();
if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
return;
}
if (varInfos.length > 0) {
for (int i = 0; i < varInfos.length; i++) {
if (varInfos[i].getScope() != scope) {
continue;
}
// If the scripting variable has been declared, skip codes
// for saving and restoring it.
if (n.getScriptingVars(scope).contains(varInfos[i])) {
continue;
}
String varName = varInfos[i].getVarName();
String tmpVarName = "_jspx_" + varName + "_"
+ n.getCustomNestingLevel();
out.printin(varName);
out.print(" = ");
out.print(tmpVarName);
out.println(";");
}
} else {
for (int i = 0; i < tagVarInfos.length; i++) {
if (tagVarInfos[i].getScope() != scope) {
continue;
}
// If the scripting variable has been declared, skip codes
// for saving and restoring it.
if (n.getScriptingVars(scope).contains(tagVarInfos[i])) {
continue;
}
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();
out.printin(varName);
out.print(" = ");
out.print(tmpVarName);
out.println(";");
}
}
}
/*
* Synchronizes the scripting variables of the given custom tag for the
* given scope.
*/
private void syncScriptingVars(Node.CustomTag n, int scope) {
TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
VariableInfo[] varInfos = n.getVariableInfos();
if ((varInfos.length == 0) && (tagVarInfos.length == 0)) {
return;
}
if (varInfos.length > 0) {
for (int i = 0; i < varInfos.length; i++) {
if (varInfos[i].getScope() == scope) {
out.printin(varInfos[i].getVarName());
out.print(" = (");
out.print(varInfos[i].getClassName());
out.print(") _jspx_page_context.findAttribute(");
out.print(quote(varInfos[i].getVarName()));
out.println(");");
}
}
} else {
for (int i = 0; i < tagVarInfos.length; i++) {
if (tagVarInfos[i].getScope() == scope) {
String name = tagVarInfos[i].getNameGiven();
if (name == null) {
name = n.getTagData().getAttributeString(
tagVarInfos[i].getNameFromAttribute());
} else if (tagVarInfos[i].getNameFromAttribute() != null) {
// alias
continue;
}
out.printin(name);
out.print(" = (");
out.print(tagVarInfos[i].getClassName());
out.print(") _jspx_page_context.findAttribute(");
out.print(quote(name));
out.println(");");
}
}
}
}
private String getJspContextVar() {
if (this.isTagFile) {
return "this.getJspContext()";
} else {
return "_jspx_page_context";
}
}
private String getExpressionFactoryVar() {
return VAR_EXPRESSIONFACTORY;
}
/*
* Creates a tag variable name by concatenating the given prefix and
* shortName and endcoded to make the resultant string a valid Java
* Identifier.
*/
private String createTagVarName(String fullName, String prefix,
String shortName) {
String varName;
synchronized (tagVarNumbers) {
varName = prefix + "_" + shortName + "_";
if (tagVarNumbers.get(fullName) != null) {
Integer i = (Integer) tagVarNumbers.get(fullName);
varName = varName + i.intValue();
tagVarNumbers.put(fullName, new Integer(i.intValue() + 1));
} else {
tagVarNumbers.put(fullName, new Integer(1));
varName = varName + "0";
}
}
return JspUtil.makeJavaIdentifier(varName);
}
private String evaluateAttribute(TagHandlerInfo handlerInfo,
Node.JspAttribute attr, Node.CustomTag n, String tagHandlerVar)
throws JasperException {
String attrValue = attr.getValue();
if (attrValue == null) {
if (attr.isNamedAttribute()) {
if (n.checkIfAttributeIsJspFragment(attr.getName())) {
// XXX - no need to generate temporary variable here
attrValue = generateNamedAttributeJspFragment(attr
.getNamedAttributeNode(), tagHandlerVar);
} else {
attrValue = generateNamedAttributeValue(attr
.getNamedAttributeNode());
}
} else {
return null;
}
}
String localName = attr.getLocalName();
Method m = null;
Class[] c = null;
if (attr.isDynamic()) {
c = OBJECT_CLASS;
} else {
m = handlerInfo.getSetterMethod(localName);
if (m == null) {
err.jspError(n, "jsp.error.unable.to_find_method", attr
.getName());
}
c = m.getParameterTypes();
// XXX assert(c.length > 0)
}
if (attr.isExpression()) {
// Do nothing
} else if (attr.isNamedAttribute()) {
if (!n.checkIfAttributeIsJspFragment(attr.getName())
&& !attr.isDynamic()) {
attrValue = convertString(c[0], attrValue, localName,
handlerInfo.getPropertyEditorClass(localName), true);
}
} else if (attr.isELInterpreterInput()) {
// results buffer
StringBuffer sb = new StringBuffer(64);
TagAttributeInfo tai = attr.getTagAttributeInfo();
// generate elContext reference
sb.append(getJspContextVar());
sb.append(".getELContext()");
String elContext = sb.toString();
if (attr.getEL() != null && attr.getEL().getMapName() != null) {
sb.setLength(0);
sb.append("new org.apache.sling.scripting.jsp.jasper.el.ELContextWrapper(");
sb.append(elContext);
sb.append(',');
sb.append(attr.getEL().getMapName());
sb.append(')');
elContext = sb.toString();
}
// reset buffer
sb.setLength(0);
// create our mark
sb.append(n.getStart().toString());
sb.append(" '");
sb.append(attrValue);
sb.append('\'');
String mark = sb.toString();
// reset buffer
sb.setLength(0);
// depending on type
if (attr.isDeferredInput()
|| ((tai != null) && ValueExpression.class.getName().equals(tai.getTypeName()))) {
sb.append("new org.apache.sling.scripting.jsp.jasper.el.JspValueExpression(");
sb.append(quote(mark));
sb.append(',');
sb.append(getExpressionFactoryVar());
sb.append(".createValueExpression(");
if (attr.getEL() != null) { // optimize
sb.append(elContext);
sb.append(',');
}
sb.append(quote(attrValue));
sb.append(',');
sb.append(JspUtil.toJavaSourceTypeFromTld(attr.getExpectedTypeName()));
sb.append("))");
// should the expression be evaluated before passing to
// the setter?
boolean evaluate = false;
if (tai.canBeRequestTime()) {
evaluate = true; // JSP.2.3.2
}
if (attr.isDeferredInput()) {
evaluate = false; // JSP.2.3.3
}
if (attr.isDeferredInput() && tai.canBeRequestTime()) {
evaluate = !attrValue.contains("#{"); // JSP.2.3.5
}
if (evaluate) {
sb.append(".getValue(");
sb.append(getJspContextVar());
sb.append(".getELContext()");
sb.append(")");
}
attrValue = sb.toString();
} else if (attr.isDeferredMethodInput()
|| ((tai != null) && MethodExpression.class.getName().equals(tai.getTypeName()))) {
sb.append("new org.apache.sling.scripting.jsp.jasper.el.JspMethodExpression(");
sb.append(quote(mark));
sb.append(',');
sb.append(getExpressionFactoryVar());
sb.append(".createMethodExpression(");
sb.append(elContext);
sb.append(',');
sb.append(quote(attrValue));
sb.append(',');
sb.append(JspUtil.toJavaSourceTypeFromTld(attr.getExpectedTypeName()));
sb.append(',');
sb.append("new Class[] {");
String[] p = attr.getParameterTypeNames();
for (int i = 0; i < p.length; i++) {
sb.append(JspUtil.toJavaSourceTypeFromTld(p[i]));
sb.append(',');
}
if (p.length > 0) {
sb.setLength(sb.length() - 1);
}
sb.append("}))");
attrValue = sb.toString();
} else {
// run attrValue through the expression interpreter
boolean replaceESC = attrValue.indexOf(Constants.ESC) > 0;
String mapName = (attr.getEL() != null) ? attr.getEL()
.getMapName() : null;
attrValue = JspUtil.interpreterCall(this.isTagFile,
attrValue, c[0], mapName, false);
// XXX hack: Replace ESC with '$'
if (replaceESC) {
attrValue = "(" + attrValue + ").replace("
+ Constants.ESCStr + ", '$')";
}
}
} else {
attrValue = convertString(c[0], attrValue, localName,
handlerInfo.getPropertyEditorClass(localName), false);
}
return attrValue;
}
/**
* Generate code to create a map for the alias variables
*
* @return the name of the map
*/
private String generateAliasMap(Node.CustomTag n, String tagHandlerVar)
throws JasperException {
TagVariableInfo[] tagVars = n.getTagVariableInfos();
String aliasMapVar = null;
boolean aliasSeen = false;
for (int i = 0; i < tagVars.length; i++) {
String nameFrom = tagVars[i].getNameFromAttribute();
if (nameFrom != null) {
String aliasedName = n.getAttributeValue(nameFrom);
if (aliasedName == null) {
continue;
}
if (!aliasSeen) {
out.printin("java.util.HashMap ");
aliasMapVar = tagHandlerVar + "_aliasMap";
out.print(aliasMapVar);
out.println(" = new java.util.HashMap();");
aliasSeen = true;
}
out.printin(aliasMapVar);
out.print(".put(");
out.print(quote(tagVars[i].getNameGiven()));
out.print(", ");
out.print(quote(aliasedName));
out.println(");");
}
}
return aliasMapVar;
}
private void generateSetters(Node.CustomTag n, String tagHandlerVar,
TagHandlerInfo handlerInfo, boolean simpleTag)
throws JasperException {
// Set context
if (simpleTag) {
// Generate alias map
String aliasMapVar = null;
if (n.isTagFile()) {
aliasMapVar = generateAliasMap(n, tagHandlerVar);
}
out.printin(tagHandlerVar);
if (aliasMapVar == null) {
out.println(".setJspContext(_jspx_page_context);");
} else {
out.print(".setJspContext(_jspx_page_context, ");
out.print(aliasMapVar);
out.println(");");
}
} else {
out.printin(tagHandlerVar);
out.println(".setPageContext(_jspx_page_context);");
}
// Set parent
if (isTagFile && parent == null) {
out.printin(tagHandlerVar);
out.print(".setParent(");
out.print("new javax.servlet.jsp.tagext.TagAdapter(");
out.print("(javax.servlet.jsp.tagext.SimpleTag) this ));");
} else if (!simpleTag) {
out.printin(tagHandlerVar);
out.print(".setParent(");
if (parent != null) {
if (isSimpleTagParent) {
out.print("new javax.servlet.jsp.tagext.TagAdapter(");
out.print("(javax.servlet.jsp.tagext.SimpleTag) ");
out.print(parent);
out.println("));");
} else {
out.print("(javax.servlet.jsp.tagext.Tag) ");
out.print(parent);
out.println(");");
}
} else {
out.println("null);");
}
} else {
// The setParent() method need not be called if the value being
// passed is null, since SimpleTag instances are not reused
if (parent != null) {
out.printin(tagHandlerVar);
out.print(".setParent(");
out.print(parent);
out.println(");");
}
}
// need to handle deferred values and methods
Node.JspAttribute[] attrs = n.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
String attrValue = evaluateAttribute(handlerInfo, attrs[i], n,
tagHandlerVar);
Mark m = n.getStart();
out.printil("// "+m.getFile()+"("+m.getLineNumber()+","+m.getColumnNumber()+") "+ attrs[i].getTagAttributeInfo());
if (attrs[i].isDynamic()) {
out.printin(tagHandlerVar);
out.print(".");
out.print("setDynamicAttribute(");
String uri = attrs[i].getURI();
if ("".equals(uri) || (uri == null)) {
out.print("null");
} else {
out.print("\"" + attrs[i].getURI() + "\"");
}
out.print(", \"");
out.print(attrs[i].getLocalName());
out.print("\", ");
out.print(attrValue);
out.println(");");
} else {
out.printin(tagHandlerVar);
out.print(".");
out.print(handlerInfo.getSetterMethod(
attrs[i].getLocalName()).getName());
out.print("(");
out.print(attrValue);
out.println(");");
}
}
}
/*
* @param c The target class to which to coerce the given string @param
* s The string value @param attrName The name of the attribute whose
* value is being supplied @param propEditorClass The property editor
* for the given attribute @param isNamedAttribute true if the given
* attribute is a named attribute (that is, specified using the
* jsp:attribute standard action), and false otherwise
*/
private String convertString(Class c, String s, String attrName,
Class propEditorClass, boolean isNamedAttribute)
throws JasperException {
String quoted = s;
if (!isNamedAttribute) {
quoted = quote(s);
}
if (propEditorClass != null) {
String className = JspUtil.getCanonicalName(c);
return "("
+ className
+ ")org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor("
+ className + ".class, \"" + attrName + "\", " + quoted
+ ", " + JspUtil.getCanonicalName(propEditorClass)
+ ".class)";
} else if (c == String.class) {
return quoted;
} else if (c == boolean.class) {
return JspUtil.coerceToPrimitiveBoolean(s, isNamedAttribute);
} else if (c == Boolean.class) {
return JspUtil.coerceToBoolean(s, isNamedAttribute);
} else if (c == byte.class) {
return JspUtil.coerceToPrimitiveByte(s, isNamedAttribute);
} else if (c == Byte.class) {
return JspUtil.coerceToByte(s, isNamedAttribute);
} else if (c == char.class) {
return JspUtil.coerceToChar(s, isNamedAttribute);
} else if (c == Character.class) {
return JspUtil.coerceToCharacter(s, isNamedAttribute);
} else if (c == double.class) {
return JspUtil.coerceToPrimitiveDouble(s, isNamedAttribute);
} else if (c == Double.class) {
return JspUtil.coerceToDouble(s, isNamedAttribute);
} else if (c == float.class) {
return JspUtil.coerceToPrimitiveFloat(s, isNamedAttribute);
} else if (c == Float.class) {
return JspUtil.coerceToFloat(s, isNamedAttribute);
} else if (c == int.class) {
return JspUtil.coerceToInt(s, isNamedAttribute);
} else if (c == Integer.class) {
return JspUtil.coerceToInteger(s, isNamedAttribute);
} else if (c == short.class) {
return JspUtil.coerceToPrimitiveShort(s, isNamedAttribute);
} else if (c == Short.class) {
return JspUtil.coerceToShort(s, isNamedAttribute);
} else if (c == long.class) {
return JspUtil.coerceToPrimitiveLong(s, isNamedAttribute);
} else if (c == Long.class) {
return JspUtil.coerceToLong(s, isNamedAttribute);
} else if (c == Object.class) {
return "new String(" + quoted + ")";
} else {
String className = JspUtil.getCanonicalName(c);
return "("
+ className
+ ")org.apache.sling.scripting.jsp.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager("
+ className + ".class, \"" + attrName + "\", " + quoted
+ ")";
}
}
/*
* Converts the scope string representation, whose possible values are
* "page", "request", "session", and "application", to the corresponding
* scope constant.
*/
private String getScopeConstant(String scope) {
String scopeName = "PageContext.PAGE_SCOPE"; // Default to page
if ("request".equals(scope)) {
scopeName = "PageContext.REQUEST_SCOPE";
} else if ("session".equals(scope)) {
scopeName = "PageContext.SESSION_SCOPE";
} else if ("application".equals(scope)) {
scopeName = "PageContext.APPLICATION_SCOPE";
}
return scopeName;
}
/**
* Generates anonymous JspFragment inner class which is passed as an
* argument to SimpleTag.setJspBody().
*/
private void generateJspFragment(Node n, String tagHandlerVar)
throws JasperException {
// XXX - A possible optimization here would be to check to see
// if the only child of the parent node is TemplateText. If so,
// we know there won't be any parameters, etc, so we can
// generate a low-overhead JspFragment that just echoes its
// body. The implementation of this fragment can come from
// the org.apache.sling.scripting.jsp.jasper.runtime package as a support class.
FragmentHelperClass.Fragment fragment = fragmentHelperClass
.openFragment(n, tagHandlerVar, methodNesting);
ServletWriter outSave = out;
out = fragment.getGenBuffer().getOut();
String tmpParent = parent;
parent = "_jspx_parent";
boolean isSimpleTagParentSave = isSimpleTagParent;
isSimpleTagParent = true;
boolean tmpIsFragment = isFragment;
isFragment = true;
String pushBodyCountVarSave = pushBodyCountVar;
if (pushBodyCountVar != null) {
// Use a fixed name for push body count, to simplify code gen
pushBodyCountVar = "_jspx_push_body_count";
}
visitBody(n);
out = outSave;
parent = tmpParent;
isSimpleTagParent = isSimpleTagParentSave;
isFragment = tmpIsFragment;
pushBodyCountVar = pushBodyCountVarSave;
fragmentHelperClass.closeFragment(fragment, methodNesting);
// XXX - Need to change pageContext to jspContext if
// we're not in a place where pageContext is defined (e.g.
// in a fragment or in a tag file.
out.print("new " + fragmentHelperClass.getClassName() + "( "
+ fragment.getId() + ", _jspx_page_context, "
+ tagHandlerVar + ", " + pushBodyCountVar + ")");
}
/**
* Generate the code required to obtain the runtime value of the given
* named attribute.
*
* @return The name of the temporary variable the result is stored in.
*/
public String generateNamedAttributeValue(Node.NamedAttribute n)
throws JasperException {
String varName = n.getTemporaryVariableName();
// If the only body element for this named attribute node is
// template text, we need not generate an extra call to
// pushBody and popBody. Maybe we can further optimize
// here by getting rid of the temporary variable, but in
// reality it looks like javac does this for us.
Node.Nodes body = n.getBody();
if (body != null) {
boolean templateTextOptimization = false;
if (body.size() == 1) {
Node bodyElement = body.getNode(0);
if (bodyElement instanceof Node.TemplateText) {
templateTextOptimization = true;
out.printil("String "
+ varName
+ " = "
+ quote(new String(
((Node.TemplateText) bodyElement)
.getText())) + ";");
}
}
// XXX - Another possible optimization would be for
// lone EL expressions (no need to pushBody here either).
if (!templateTextOptimization) {
out.printil("out = _jspx_page_context.pushBody();");
visitBody(n);
out.printil("String " + varName + " = "
+ "((javax.servlet.jsp.tagext.BodyContent)"
+ "out).getString();");
out.printil("out = _jspx_page_context.popBody();");
}
} else {
// Empty body must be treated as ""
out.printil("String " + varName + " = \"\";");
}
return varName;
}
/**
* Similar to generateNamedAttributeValue, but create a JspFragment
* instead.
*
* @param n
* The parent node of the named attribute
* @param tagHandlerVar
* The variable the tag handler is stored in, so the fragment
* knows its parent tag.
* @return The name of the temporary variable the fragment is stored in.
*/
public String generateNamedAttributeJspFragment(Node.NamedAttribute n,
String tagHandlerVar) throws JasperException {
String varName = n.getTemporaryVariableName();
out.printin("javax.servlet.jsp.tagext.JspFragment " + varName
+ " = ");
generateJspFragment(n, tagHandlerVar);
out.println(";");
return varName;
}
}
private static void generateLocalVariables(ServletWriter out, Node n)
throws JasperException {
Node.ChildInfo ci;
if (n instanceof Node.CustomTag) {
ci = ((Node.CustomTag) n).getChildInfo();
} else if (n instanceof Node.JspBody) {
ci = ((Node.JspBody) n).getChildInfo();
} else if (n instanceof Node.NamedAttribute) {
ci = ((Node.NamedAttribute) n).getChildInfo();
} else {
// Cannot access err since this method is static, but at
// least flag an error.
throw new JasperException("Unexpected Node Type");
// err.getString(
// "jsp.error.internal.unexpected_node_type" ) );
}
if (ci.hasUseBean()) {
out
.printil("HttpSession session = _jspx_page_context.getSession();");
out
.printil("ServletContext application = _jspx_page_context.getServletContext();");
}
if (ci.hasUseBean() || ci.hasIncludeAction() || ci.hasSetProperty()
|| ci.hasParamAction()) {
out
.printil("HttpServletRequest request = (HttpServletRequest)_jspx_page_context.getRequest();");
}
if (ci.hasIncludeAction()) {
out
.printil("HttpServletResponse response = (HttpServletResponse)_jspx_page_context.getResponse();");
}
}
/**
* Common part of postamble, shared by both servlets and tag files.
*/
private void genCommonPostamble() {
// Append any methods that were generated in the buffer.
for (int i = 0; i < methodsBuffered.size(); i++) {
GenBuffer methodBuffer = (GenBuffer) methodsBuffered.get(i);
methodBuffer.adjustJavaLines(out.getJavaLine() - 1);
out.printMultiLn(methodBuffer.toString());
}
// Append the helper class
if (fragmentHelperClass.isUsed()) {
fragmentHelperClass.generatePostamble();
fragmentHelperClass.adjustJavaLines(out.getJavaLine() - 1);
out.printMultiLn(fragmentHelperClass.toString());
}
// Append char array declarations
if (charArrayBuffer != null) {
out.printMultiLn(charArrayBuffer.toString());
}
// Close the class definition
out.popIndent();
out.printil("}");
}
/**
* Generates the ending part of the static portion of the servlet.
*/
private void generatePostamble(Node.Nodes page) {
out.popIndent();
out.printil("} catch (Throwable t) {");
out.pushIndent();
out.printil("if (!(t instanceof SkipPageException)){");
out.pushIndent();
out.printil("out = _jspx_out;");
out.printil("if (out != null && out.getBufferSize() != 0)");
out.pushIndent();
out.printil("try { out.clearBuffer(); } catch (java.io.IOException e) {}");
out.popIndent();
out
.printil("if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);");
out.popIndent();
out.printil("}");
out.popIndent();
out.printil("} finally {");
out.pushIndent();
out
.printil("_jspxFactory.releasePageContext(_jspx_page_context);");
out.popIndent();
out.printil("}");
// Close the service method
out.popIndent();
out.printil("}");
// Generated methods, helper classes, etc.
genCommonPostamble();
}
/**
* Constructor.
*/
Generator(ServletWriter out, Compiler compiler) {
this.out = out;
methodsBuffered = new ArrayList();
charArrayBuffer = null;
err = compiler.getErrorDispatcher();
ctxt = compiler.getCompilationContext();
fragmentHelperClass = new FragmentHelperClass(ctxt
.getServletClassName()
+ "Helper");
pageInfo = compiler.getPageInfo();
/*
* Temporary hack. If a JSP page uses the "extends" attribute of the
* page directive, the _jspInit() method of the generated servlet class
* will not be called (it is only called for those generated servlets
* that extend HttpJspBase, the default), causing the tag handler pools
* not to be initialized and resulting in a NPE. The JSP spec needs to
* clarify whether containers can override init() and destroy(). For
* now, we just disable tag pooling for pages that use "extends".
*/
if (pageInfo.getExtends(false) == null) {
isPoolingEnabled = ctxt.getOptions().isPoolingEnabled();
} else {
isPoolingEnabled = false;
}
beanInfo = pageInfo.getBeanRepository();
breakAtLF = ctxt.getOptions().getMappedFile();
if (isPoolingEnabled) {
tagHandlerPoolNames = new Vector();
}
}
/**
* The main entry for Generator.
*
* @param out
* The servlet output writer
* @param compiler
* The compiler
* @param page
* The input page
*/
public static void generate(ServletWriter out, Compiler compiler,
Node.Nodes page) throws JasperException {
Generator gen = new Generator(out, compiler);
if (gen.isPoolingEnabled) {
gen.compileTagHandlerPoolList(page);
}
if (gen.ctxt.isTagFile()) {
JasperTagInfo tagInfo = (JasperTagInfo) gen.ctxt.getTagInfo();
gen.generateTagHandlerPreamble(tagInfo, page);
if (gen.ctxt.isPrototypeMode()) {
return;
}
gen.generateXmlProlog(page);
gen.fragmentHelperClass.generatePreamble();
page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out,
gen.methodsBuffered, gen.fragmentHelperClass, gen.ctxt
.getClassLoader(), tagInfo));
gen.generateTagHandlerPostamble(tagInfo);
} else {
gen.generatePreamble(page);
gen.generateXmlProlog(page);
gen.fragmentHelperClass.generatePreamble();
page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out,
gen.methodsBuffered, gen.fragmentHelperClass, gen.ctxt
.getClassLoader(), null));
gen.generatePostamble(page);
}
}
/*
* Generates tag handler preamble.
*/
private void generateTagHandlerPreamble(JasperTagInfo tagInfo,
Node.Nodes tag) throws JasperException {
// Generate package declaration
String className = tagInfo.getTagClassName();
int lastIndex = className.lastIndexOf('.');
if (lastIndex != -1) {
String pkgName = className.substring(0, lastIndex);
genPreamblePackage(pkgName);
className = className.substring(lastIndex + 1);
}
// Generate imports
genPreambleImports();
// Generate class declaration
out.printin("public final class ");
out.println(className);
out.printil(" extends javax.servlet.jsp.tagext.SimpleTagSupport");
out.printin(" implements org.apache.sling.scripting.jsp.jasper.runtime.JspSourceDependent");
if (tagInfo.hasDynamicAttributes()) {
out.println(",");
out.printin(" javax.servlet.jsp.tagext.DynamicAttributes");
}
out.println(" {");
out.println();
out.pushIndent();
/*
* Class body begins here
*/
generateDeclarations(tag);
// Static initializations here
genPreambleStaticInitializers();
out.printil("private JspContext jspContext;");
// Declare writer used for storing result of fragment/body invocation
// if 'varReader' or 'var' attribute is specified
out.printil("private java.io.Writer _jspx_sout;");
// Class variable declarations
genPreambleClassVariableDeclarations(tagInfo.getTagName());
generateSetJspContext(tagInfo);
// Tag-handler specific declarations
generateTagHandlerAttributes(tagInfo);
if (tagInfo.hasDynamicAttributes()) {
generateSetDynamicAttribute();
}
// Methods here
genPreambleMethods();
// Now the doTag() method
out.printil("public void doTag() throws JspException, java.io.IOException {");
if (ctxt.isPrototypeMode()) {
out.printil("}");
out.popIndent();
out.printil("}");
return;
}
out.pushIndent();
/*
* According to the spec, 'pageContext' must not be made available as an
* implicit object in tag files. Declare _jspx_page_context, so we can
* share the code generator with JSPs.
*/
out.printil("PageContext _jspx_page_context = (PageContext)jspContext;");
// Declare implicit objects.
out.printil("HttpServletRequest request = "
+ "(HttpServletRequest) _jspx_page_context.getRequest();");
out.printil("HttpServletResponse response = "
+ "(HttpServletResponse) _jspx_page_context.getResponse();");
out.printil("HttpSession session = _jspx_page_context.getSession();");
out.printil("ServletContext application = _jspx_page_context.getServletContext();");
out.printil("ServletConfig config = _jspx_page_context.getServletConfig();");
out.printil("JspWriter out = jspContext.getOut();");
out.printil("_jspInit(config);");
// set current JspContext on ELContext
out.printil("jspContext.getELContext().putContext(JspContext.class,jspContext);");
generatePageScopedVariables(tagInfo);
declareTemporaryScriptingVars(tag);
out.println();
out.printil("try {");
out.pushIndent();
}
private void generateTagHandlerPostamble(TagInfo tagInfo) {
out.popIndent();
// Have to catch Throwable because a classic tag handler
// helper method is declared to throw Throwable.
out.printil("} catch( Throwable t ) {");
out.pushIndent();
out.printil("if( t instanceof SkipPageException )");
out.printil(" throw (SkipPageException) t;");
out.printil("if( t instanceof java.io.IOException )");
out.printil(" throw (java.io.IOException) t;");
out.printil("if( t instanceof IllegalStateException )");
out.printil(" throw (IllegalStateException) t;");
out.printil("if( t instanceof JspException )");
out.printil(" throw (JspException) t;");
out.printil("throw new JspException(t);");
out.popIndent();
out.printil("} finally {");
out.pushIndent();
// handle restoring VariableMapper
TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
for (int i = 0; i < attrInfos.length; i++) {
if (attrInfos[i].isDeferredMethod() || attrInfos[i].isDeferredValue()) {
out.printin("_el_variablemapper.setVariable(");
out.print(quote(attrInfos[i].getName()));
out.print(",_el_ve");
out.print(i);
out.println(");");
}
}
// restore nested JspContext on ELContext
out.printil("jspContext.getELContext().putContext(JspContext.class,super.getJspContext());");
out.printil("((org.apache.sling.scripting.jsp.jasper.runtime.JspContextWrapper) jspContext).syncEndTagFile();");
if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) {
out.printil("_jspDestroy();");
}
out.popIndent();
out.printil("}");
// Close the doTag method
out.popIndent();
out.printil("}");
// Generated methods, helper classes, etc.
genCommonPostamble();
}
/**
* Generates declarations for tag handler attributes, and defines the getter
* and setter methods for each.
*/
private void generateTagHandlerAttributes(TagInfo tagInfo)
throws JasperException {
if (tagInfo.hasDynamicAttributes()) {
out.printil("private java.util.HashMap _jspx_dynamic_attrs = new java.util.HashMap();");
}
// Declare attributes
TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
for (int i = 0; i < attrInfos.length; i++) {
out.printin("private ");
if (attrInfos[i].isFragment()) {
out.print("javax.servlet.jsp.tagext.JspFragment ");
} else {
out.print(JspUtil.toJavaSourceType(attrInfos[i].getTypeName()));
out.print(" ");
}
out.print(attrInfos[i].getName());
out.println(";");
}
out.println();
// Define attribute getter and setter methods
if (attrInfos != null) {
for (int i = 0; i < attrInfos.length; i++) {
// getter method
out.printin("public ");
if (attrInfos[i].isFragment()) {
out.print("javax.servlet.jsp.tagext.JspFragment ");
} else {
out.print(JspUtil.toJavaSourceType(attrInfos[i]
.getTypeName()));
out.print(" ");
}
out.print(toGetterMethod(attrInfos[i].getName()));
out.println(" {");
out.pushIndent();
out.printin("return this.");
out.print(attrInfos[i].getName());
out.println(";");
out.popIndent();
out.printil("}");
out.println();
// setter method
out.printin("public void ");
out.print(toSetterMethodName(attrInfos[i].getName()));
if (attrInfos[i].isFragment()) {
out.print("(javax.servlet.jsp.tagext.JspFragment ");
} else {
out.print("(");
out.print(JspUtil.toJavaSourceType(attrInfos[i]
.getTypeName()));
out.print(" ");
}
out.print(attrInfos[i].getName());
out.println(") {");
out.pushIndent();
out.printin("this.");
out.print(attrInfos[i].getName());
out.print(" = ");
out.print(attrInfos[i].getName());
out.println(";");
if (ctxt.isTagFile()) {
// Tag files should also set jspContext attributes
out.printin("jspContext.setAttribute(\"");
out.print(attrInfos[i].getName());
out.print("\", ");
out.print(attrInfos[i].getName());
out.println(");");
}
out.popIndent();
out.printil("}");
out.println();
}
}
}
/*
* Generate setter for JspContext so we can create a wrapper and store both
* the original and the wrapper. We need the wrapper to mask the page
* context from the tag file and simulate a fresh page context. We need the
* original to do things like sync AT_BEGIN and AT_END scripting variables.
*/
private void generateSetJspContext(TagInfo tagInfo) {
boolean nestedSeen = false;
boolean atBeginSeen = false;
boolean atEndSeen = false;
// Determine if there are any aliases
boolean aliasSeen = false;
TagVariableInfo[] tagVars = tagInfo.getTagVariableInfos();
for (int i = 0; i < tagVars.length; i++) {
if (tagVars[i].getNameFromAttribute() != null
&& tagVars[i].getNameGiven() != null) {
aliasSeen = true;
break;
}
}
if (aliasSeen) {
out
.printil("public void setJspContext(JspContext ctx, java.util.Map aliasMap) {");
} else {
out.printil("public void setJspContext(JspContext ctx) {");
}
out.pushIndent();
out.printil("super.setJspContext(ctx);");
out.printil("java.util.ArrayList _jspx_nested = null;");
out.printil("java.util.ArrayList _jspx_at_begin = null;");
out.printil("java.util.ArrayList _jspx_at_end = null;");
for (int i = 0; i < tagVars.length; i++) {
switch (tagVars[i].getScope()) {
case VariableInfo.NESTED:
if (!nestedSeen) {
out.printil("_jspx_nested = new java.util.ArrayList();");
nestedSeen = true;
}
out.printin("_jspx_nested.add(");
break;
case VariableInfo.AT_BEGIN:
if (!atBeginSeen) {
out.printil("_jspx_at_begin = new java.util.ArrayList();");
atBeginSeen = true;
}
out.printin("_jspx_at_begin.add(");
break;
case VariableInfo.AT_END:
if (!atEndSeen) {
out.printil("_jspx_at_end = new java.util.ArrayList();");
atEndSeen = true;
}
out.printin("_jspx_at_end.add(");
break;
} // switch
out.print(quote(tagVars[i].getNameGiven()));
out.println(");");
}
if (aliasSeen) {
out
.printil("this.jspContext = new org.apache.sling.scripting.jsp.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, aliasMap);");
} else {
out
.printil("this.jspContext = new org.apache.sling.scripting.jsp.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, null);");
}
out.popIndent();
out.printil("}");
out.println();
out.printil("public JspContext getJspContext() {");
out.pushIndent();
out.printil("return this.jspContext;");
out.popIndent();
out.printil("}");
}
/*
* Generates implementation of
* javax.servlet.jsp.tagext.DynamicAttributes.setDynamicAttribute() method,
* which saves each dynamic attribute that is passed in so that a scoped
* variable can later be created for it.
*/
public void generateSetDynamicAttribute() {
out
.printil("public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {");
out.pushIndent();
/*
* According to the spec, only dynamic attributes with no uri are to be
* present in the Map; all other dynamic attributes are ignored.
*/
out.printil("if (uri == null)");
out.pushIndent();
out.printil("_jspx_dynamic_attrs.put(localName, value);");
out.popIndent();
out.popIndent();
out.printil("}");
}
/*
* Creates a page-scoped variable for each declared tag attribute. Also, if
* the tag accepts dynamic attributes, a page-scoped variable is made
* available for each dynamic attribute that was passed in.
*/
private void generatePageScopedVariables(JasperTagInfo tagInfo) {
// "normal" attributes
TagAttributeInfo[] attrInfos = tagInfo.getAttributes();
boolean variableMapperVar = false;
for (int i = 0; i < attrInfos.length; i++) {
String attrName = attrInfos[i].getName();
// handle assigning deferred vars to VariableMapper, storing
// previous values under '_el_ve[i]' for later re-assignment
if (attrInfos[i].isDeferredValue() || attrInfos[i].isDeferredMethod()) {
// we need to scope the modified VariableMapper for consistency and performance
if (!variableMapperVar) {
out.printil("javax.el.VariableMapper _el_variablemapper = jspContext.getELContext().getVariableMapper();");
variableMapperVar = true;
}
out.printin("javax.el.ValueExpression _el_ve");
out.print(i);
out.print(" = _el_variablemapper.setVariable(");
out.print(quote(attrName));
out.print(',');
if (attrInfos[i].isDeferredMethod()) {
out.print(VAR_EXPRESSIONFACTORY);
out.print(".createValueExpression(");
out.print(toGetterMethod(attrName));
out.print(",javax.el.MethodExpression.class)");
} else {
out.print(toGetterMethod(attrName));
}
out.println(");");
} else {
out.printil("if( " + toGetterMethod(attrName) + " != null ) ");
out.pushIndent();
out.printin("_jspx_page_context.setAttribute(");
out.print(quote(attrName));
out.print(", ");
out.print(toGetterMethod(attrName));
out.println(");");
out.popIndent();
}
}
// Expose the Map containing dynamic attributes as a page-scoped var
if (tagInfo.hasDynamicAttributes()) {
out.printin("_jspx_page_context.setAttribute(\"");
out.print(tagInfo.getDynamicAttributesMapName());
out.print("\", _jspx_dynamic_attrs);");
}
}
/*
* Generates the getter method for the given attribute name.
*/
private String toGetterMethod(String attrName) {
char[] attrChars = attrName.toCharArray();
attrChars[0] = Character.toUpperCase(attrChars[0]);
return "get" + new String(attrChars) + "()";
}
/*
* Generates the setter method name for the given attribute name.
*/
private String toSetterMethodName(String attrName) {
char[] attrChars = attrName.toCharArray();
attrChars[0] = Character.toUpperCase(attrChars[0]);
return "set" + new String(attrChars);
}
/**
* Class storing the result of introspecting a custom tag handler.
*/
private static class TagHandlerInfo {
private Hashtable methodMaps;
private Hashtable propertyEditorMaps;
private String tagHandlerClassName;
/**
* Constructor.
*
* @param n
* The custom tag whose tag handler class is to be
* introspected
* @param tagHandlerClass
* Tag handler class
* @param err
* Error dispatcher
*/
TagHandlerInfo(Node n, Class tagHandlerClass, ErrorDispatcher err, String tagHandlerClassName)
throws JasperException {
this.tagHandlerClassName = tagHandlerClassName;
this.methodMaps = new Hashtable();
this.propertyEditorMaps = new Hashtable();
try {
BeanInfo tagClassInfo = Introspector
.getBeanInfo(tagHandlerClass);
PropertyDescriptor[] pd = tagClassInfo.getPropertyDescriptors();
for (int i = 0; i < pd.length; i++) {
/*
* FIXME: should probably be checking for things like
* pageContext, bodyContent, and parent here -akv
*/
if (pd[i].getWriteMethod() != null) {
methodMaps.put(pd[i].getName(), pd[i].getWriteMethod());
}
if (pd[i].getPropertyEditorClass() != null) {
propertyEditorMaps.put(pd[i].getName(), pd[i]
.getPropertyEditorClass());
}
}
} catch (IntrospectionException ie) {
err.jspError(n, "jsp.error.introspect.taghandler",
tagHandlerClass.getName(), ie);
}
}
/**
* XXX
*/
public Method getSetterMethod(String attrName) {
return (Method) methodMaps.get(attrName);
}
/**
* XXX
*/
public Class getPropertyEditorClass(String attrName) {
return (Class) propertyEditorMaps.get(attrName);
}
/**
* XXX
*/
public String getTagHandlerClassName() {
return tagHandlerClassName;
}
}
/**
* A class for generating codes to a buffer. Included here are some support
* for tracking source to Java lines mapping.
*/
private static class GenBuffer {
/*
* For a CustomTag, the codes that are generated at the beginning of the
* tag may not be in the same buffer as those for the body of the tag.
* Two fields are used here to keep this straight. For codes that do not
* corresponds to any JSP lines, they should be null.
*/
private Node node;
private Node.Nodes body;
private java.io.CharArrayWriter charWriter;
protected ServletWriter out;
GenBuffer() {
this(null, null);
}
GenBuffer(Node n, Node.Nodes b) {
node = n;
body = b;
if (body != null) {
body.setGeneratedInBuffer(true);
}
charWriter = new java.io.CharArrayWriter();
out = new ServletWriter(new java.io.PrintWriter(charWriter));
}
public ServletWriter getOut() {
return out;
}
@Override
public String toString() {
return charWriter.toString();
}
/**
* Adjust the Java Lines. This is necessary because the Java lines
* stored with the nodes are relative the beginning of this buffer and
* need to be adjusted when this buffer is inserted into the source.
*/
public void adjustJavaLines(final int offset) {
if (node != null) {
adjustJavaLine(node, offset);
}
if (body != null) {
try {
body.visit(new Node.Visitor() {
@Override
public void doVisit(Node n) {
adjustJavaLine(n, offset);
}
@Override
public void visit(Node.CustomTag n)
throws JasperException {
Node.Nodes b = n.getBody();
if (b != null && !b.isGeneratedInBuffer()) {
// Don't adjust lines for the nested tags that
// are also generated in buffers, because the
// adjustments will be done elsewhere.
b.visit(this);
}
}
});
} catch (JasperException ex) {
}
}
}
private static void adjustJavaLine(Node n, int offset) {
if (n.getBeginJavaLine() > 0) {
n.setBeginJavaLine(n.getBeginJavaLine() + offset);
n.setEndJavaLine(n.getEndJavaLine() + offset);
}
}
}
/**
* Keeps track of the generated Fragment Helper Class
*/
private static class FragmentHelperClass {
private static class Fragment {
private GenBuffer genBuffer;
private int id;
public Fragment(int id, Node node) {
this.id = id;
genBuffer = new GenBuffer(null, node.getBody());
}
public GenBuffer getGenBuffer() {
return this.genBuffer;
}
public int getId() {
return this.id;
}
}
// True if the helper class should be generated.
private boolean used = false;
private ArrayList fragments = new ArrayList();
private String className;
// Buffer for entire helper class
private GenBuffer classBuffer = new GenBuffer();
public FragmentHelperClass(String className) {
this.className = className;
}
public String getClassName() {
return this.className;
}
public boolean isUsed() {
return this.used;
}
public void generatePreamble() {
ServletWriter out = this.classBuffer.getOut();
out.println();
out.pushIndent();
// Note: cannot be static, as we need to reference things like
// _jspx_meth_*
out.printil("private class " + className);
out.printil(" extends "
+ "org.apache.sling.scripting.jsp.jasper.runtime.JspFragmentHelper");
out.printil("{");
out.pushIndent();
out
.printil("private javax.servlet.jsp.tagext.JspTag _jspx_parent;");
out.printil("private int[] _jspx_push_body_count;");
out.println();
out.printil("public " + className
+ "( int discriminator, JspContext jspContext, "
+ "javax.servlet.jsp.tagext.JspTag _jspx_parent, "
+ "int[] _jspx_push_body_count ) {");
out.pushIndent();
out.printil("super( discriminator, jspContext, _jspx_parent );");
out.printil("this._jspx_parent = _jspx_parent;");
out.printil("this._jspx_push_body_count = _jspx_push_body_count;");
out.popIndent();
out.printil("}");
}
public Fragment openFragment(Node parent, String tagHandlerVar,
int methodNesting) throws JasperException {
Fragment result = new Fragment(fragments.size(), parent);
fragments.add(result);
this.used = true;
parent.setInnerClassName(className);
ServletWriter out = result.getGenBuffer().getOut();
out.pushIndent();
out.pushIndent();
// XXX - Returns boolean because if a tag is invoked from
// within this fragment, the Generator sometimes might
// generate code like "return true". This is ignored for now,
// meaning only the fragment is skipped. The JSR-152
// expert group is currently discussing what to do in this case.
// See comment in closeFragment()
if (methodNesting > 0) {
out.printin("public boolean invoke");
} else {
out.printin("public void invoke");
}
out.println(result.getId() + "( " + "JspWriter out ) ");
out.pushIndent();
// Note: Throwable required because methods like _jspx_meth_*
// throw Throwable.
out.printil("throws Throwable");
out.popIndent();
out.printil("{");
out.pushIndent();
generateLocalVariables(out, parent);
return result;
}
public void closeFragment(Fragment fragment, int methodNesting) {
ServletWriter out = fragment.getGenBuffer().getOut();
// XXX - See comment in openFragment()
if (methodNesting > 0) {
out.printil("return false;");
} else {
out.printil("return;");
}
out.popIndent();
out.printil("}");
}
public void generatePostamble() {
ServletWriter out = this.classBuffer.getOut();
// Generate all fragment methods:
for (int i = 0; i < fragments.size(); i++) {
Fragment fragment = (Fragment) fragments.get(i);
fragment.getGenBuffer().adjustJavaLines(out.getJavaLine() - 1);
out.printMultiLn(fragment.getGenBuffer().toString());
}
// Generate postamble:
out.printil("public void invoke( java.io.Writer writer )");
out.pushIndent();
out.printil("throws JspException");
out.popIndent();
out.printil("{");
out.pushIndent();
out.printil("JspWriter out = null;");
out.printil("if( writer != null ) {");
out.pushIndent();
out.printil("out = this.jspContext.pushBody(writer);");
out.popIndent();
out.printil("} else {");
out.pushIndent();
out.printil("out = this.jspContext.getOut();");
out.popIndent();
out.printil("}");
out.printil("try {");
out.pushIndent();
out.printil("this.jspContext.getELContext().putContext(JspContext.class,this.jspContext);");
out.printil("switch( this.discriminator ) {");
out.pushIndent();
for (int i = 0; i < fragments.size(); i++) {
out.printil("case " + i + ":");
out.pushIndent();
out.printil("invoke" + i + "( out );");
out.printil("break;");
out.popIndent();
}
out.popIndent();
out.printil("}"); // switch
out.popIndent();
out.printil("}"); // try
out.printil("catch( Throwable e ) {");
out.pushIndent();
out.printil("if (e instanceof SkipPageException)");
out.printil(" throw (SkipPageException) e;");
out.printil("throw new JspException( e );");
out.popIndent();
out.printil("}"); // catch
out.printil("finally {");
out.pushIndent();
out.printil("if( writer != null ) {");
out.pushIndent();
out.printil("this.jspContext.popBody();");
out.popIndent();
out.printil("}");
out.popIndent();
out.printil("}"); // finally
out.popIndent();
out.printil("}"); // invoke method
out.popIndent();
out.printil("}"); // helper class
out.popIndent();
}
@Override
public String toString() {
return classBuffer.toString();
}
public void adjustJavaLines(int offset) {
for (int i = 0; i < fragments.size(); i++) {
Fragment fragment = (Fragment) fragments.get(i);
fragment.getGenBuffer().adjustJavaLines(offset);
}
}
}
}