/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.jsp.java; import com.caucho.el.Expr; import com.caucho.jsp.JspLineParseException; import com.caucho.jsp.JspParseException; import com.caucho.jsp.JspParser; import com.caucho.jsp.Namespace; import com.caucho.jsp.ParseState; import com.caucho.jsp.TagInstance; import com.caucho.util.CharBuffer; import com.caucho.util.CompileException; import com.caucho.util.L10N; import com.caucho.util.LineCompileException; import com.caucho.vfs.Path; import com.caucho.vfs.WriteStream; import com.caucho.xml.QName; import com.caucho.xml.XmlChar; import com.caucho.xpath.NamespaceContext; import com.caucho.xpath.XPath; import javax.el.MethodExpression; import javax.el.ValueExpression; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.TagAttributeInfo; import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; import java.io.IOException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; public abstract class JspNode { static final L10N L = new L10N(JspNode.class); private static final Logger log = Logger.getLogger(JspNode.class.getName()); static final String JSP_NS = JspParser.JSP_NS; protected Path _sourcePath; protected String _filename; protected int _startLine; protected int _endAttributeLine; protected int _endLine; protected Namespace _ns; protected JavaJspGenerator _gen; protected ParseState _parseState; protected QName _name; protected JspNode _parent; protected JspNode() { } /** * Sets the Java generator. */ public void setGenerator(JavaJspGenerator gen) { _gen = gen; } public JavaJspGenerator getGenerator() { return _gen; } /** * Sets the parse state */ public void setParseState(ParseState parseState) { _parseState = parseState; } /** * Returns the qname of the node. */ public QName getQName() { return _name; } /** * Sets the node's qname */ public void setQName(QName name) { _name = name; } /** * Returns the qname of the node. */ public String getTagName() { if (_name != null) return _name.getName(); else return "jsp:unknown"; } /** * Returns the parent node. */ public JspNode getParent() { return _parent; } /** * Sets the parent node */ public void setParent(JspNode parent) { _parent = parent; } /** * Sets the start location of the node. */ public void setStartLocation(Path sourcePath, String filename, int line) { _sourcePath = sourcePath; _filename = filename; _startLine = line; _endAttributeLine = line; } /** * Sets the end location of the node. */ public void setEndAttributeLocation(String filename, int line) { if (_filename != null && _filename.equals(filename)) _endAttributeLine = line; } /** * Sets the end location of the node. */ public void setEndLocation(String filename, int line) { if (_filename != null && _filename.equals(filename)) _endLine = line; } /** * Gets the filename of the node */ public String getFilename() { return _filename; } /** * Gets the starting line number */ public int getStartLine() { return _startLine; } /** * Gets the attribute ending line number */ public int getEndAttributeLine() { return _endAttributeLine; } /** * Gets the ending line number */ public int getEndLine() { return _endLine; } /** * True if the node only has static text. */ public boolean isStatic() { return false; } /** * True if this is a jstl node. */ public boolean isJstl() { return false; } /** * True for 2.1 or later taglib */ public boolean isJsp21() { return true; } /** * Returns the static text. */ public String getStaticText() { CharBuffer cb = CharBuffer.allocate(); getStaticText(cb); return cb.close(); } /** * Returns the static text. */ public void getStaticText(CharBuffer cb) { } /** * True if the node has scripting (counting rtexpr) */ public boolean hasScripting() { return false; } /** * True if the node has scripting element (i.e. not counting rtexpr values) */ public boolean hasScriptingElement() { return false; } /** * Finds the first scripting node */ public JspNode findScriptingNode() { if (hasScripting()) return this; else return null; } /** * Returns the body content. */ public String getBodyContent() { return "jsp"; } /** * True if the node contains a child tag. */ public boolean hasCustomTag() { return false; } /** * True if the node contains a child tag. */ public boolean hasTag() { return hasCustomTag(); } /** * Returns the tag name for the current tag. */ public String getCustomTagName() { return null; } /** * Returns true for a simple tag. */ public boolean isSimpleTag() { return false; } /** * Returns parent tag node */ public JspNode getParentTagNode() { if (getCustomTagName() != null) return this; else { JspNode parent = getParent(); if (parent != null) return parent.getParentTagNode(); else return null; } } /** * Returns parent tag node */ public String getParentTagName() { if (getCustomTagName() != null) return getCustomTagName(); else { JspNode parent = getParent(); if (parent != null) return parent.getParentTagName(); else return null; } } /** * Returns true if the namespace decl has been printed. */ public boolean hasNamespace(String prefix, String uri) { if (_parent == null) return false; else return _parent.hasNamespace(prefix, uri); } /** * Adds a namespace, e.g. from a prefix declaration. */ public final void addNamespace(String prefix, String value) { addNamespaceRec(prefix, value); } /** * Adds a namespace, e.g. from a prefix declaration. */ public final void setNamespace(Namespace ns) { _ns = ns; } /** * Returns the XPath namespace context. */ public final NamespaceContext getNamespaceContext() { NamespaceContext ns = null; for (Namespace ptr = _ns; ptr != null; ptr = ptr.getNext()) { // jsp/1g58 if (! "".equals(ptr.getPrefix())) ns = new NamespaceContext(ns, ptr.getPrefix(), ptr.getURI()); } return ns; } /** * Adds a namespace, e.g. from a prefix declaration. */ public void addNamespaceRec(String prefix, String value) { if (_parent != null) _parent.addNamespaceRec(prefix, value); } /** * Adds a namespace, e.g. from a prefix declaration. */ public String getNamespacePrefix(String uri) { if (_parent != null) return _parent.getNamespacePrefix(uri); else return null; } /** * Returns true if the namespace decl has been printed. */ public boolean hasNamespace(QName name) { return hasNamespace(name.getPrefix(), name.getNamespaceURI()); } /** * Adds an attribute. */ public void addAttribute(QName name, String value) throws JspParseException { throw error(L.l("attribute '{0}' is not allowed in <{1}>.", name.getName(), getTagName())); } /** * Adds a JspAttribute attribute. * * @param name the name of the attribute. * @param value the value of the attribute. */ public void addAttribute(QName name, JspAttribute value) throws JspParseException { if (value.isStatic()) { addAttribute(name, value.getStaticText().trim()); } else throw error(L.l("attribute '{0}' is not allowed in <{1}>.", name.getName(), getTagName())); } /** * Called after all the attributes from the tag. */ public void endAttributes() throws JspParseException { } /** * Adds text. */ public JspNode addText(String text) throws JspParseException { for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); if (! XmlChar.isWhitespace(ch)) throw error(L.l("Text is not allowed in <{0}> at '{1}'.", _name.getName(), text)); } return null; } /** * Adds a child node. */ public void addChild(JspNode node) throws JspParseException { node.setParent(this); if (node instanceof JspAttribute) { } else if (node instanceof StaticText && ((StaticText) node).isWhitespace()) { } else throw node.error(L.l("<{0}> does not allow any child elements at {1}", getTagName(), node.getTagName())); } /** * Adds a child node after its completely initialized.. */ public void addChildEnd(JspNode node) throws JspParseException { if (node instanceof JspAttribute) { JspAttribute attr = (JspAttribute) node; QName name = attr.getName(); addAttribute(name, attr); } } /** * Called when the tag closes. */ public void endElement() throws Exception { } /** * Returns the children. */ public ArrayList<JspNode> getChildren() { return null; } /** * Returns the TagInstance of the enclosing parent. */ public TagInstance getTag() { JspNode parent = getParent(); if (parent != null) return parent.getTag(); else { return _gen.getRootTag(); } } /** * Return true for pre-21 taglib. */ public boolean isPre21Taglib() { return false; } /** * Generates the XML text representation for the tag validation. * * @param os write stream to the generated XML. */ abstract public void printXml(WriteStream os) throws IOException; /** * Prints the jsp:id */ public void printJspId(WriteStream os) throws IOException { os.print(" jsp:id=\"" + _gen.generateJspId() + "\""); } /** * Generates the XML text representation for the tag validation. * * @param os write stream to the generated XML. */ public void printXmlText(WriteStream os, String text) throws IOException { os.print(xmlText(text)); } /** * Generates the XML text representation for the tag validation. * * @param os write stream to the generated XML. */ public void printXmlAttribute(WriteStream os, String name, String text) throws IOException { os.print(" "); os.print(name); os.print("=\""); if (text.startsWith("<%=") && text.endsWith("%>")) { os.print("%="); os.print(xmlAttrText(text.substring(3, text.length() - 2))); os.print("%"); } else os.print(xmlAttrText(text)); os.print("\""); } /** * Generates the XML text. */ public String xmlText(String text) { if (text == null) return ""; CharBuffer cb = new CharBuffer(); for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); switch (ch) { case '<': cb.append("<"); break; case '>': cb.append(">"); break; case '&': cb.append("&"); break; case '"': cb.append("""); break; default: cb.append(ch); break; } } return cb.toString(); } /** * Generates the XML text. */ public String xmlAttrText(String text) { if (text == null) return ""; CharBuffer cb = new CharBuffer(); for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); switch (ch) { case '&': cb.append("&"); break; case '<': cb.append("<"); break; case '>': cb.append(">"); break; case '"': cb.append("""); break; case '\'': cb.append("'"); break; default: cb.append(ch); break; } } return cb.toString(); } /** * Generates the start location. */ public void generateStartLocation(JspJavaWriter out) throws IOException { out.setLocation(_filename, _startLine); } /** * Generates the start location. */ public void generateEndLocation(JspJavaWriter out) throws IOException { out.setLocation(_filename, _endLine); } /** * generates prologue data. */ public void generatePrologue(JspJavaWriter out) throws Exception { generatePrologueChildren(out); } /** * generates prologue data. */ public void generatePrologueDeclare(JspJavaWriter out) throws Exception { } /** * generates data for prologue children. */ public void generatePrologueChildren(JspJavaWriter out) throws Exception { } /** * generates declaration data. */ public void generateDeclaration(JspJavaWriter out) throws IOException { generateDeclarationChildren(out); } /** * generates data for declaration children. */ public void generateDeclarationChildren(JspJavaWriter out) throws IOException { } /** * generates tag state */ public void generateTagState(JspJavaWriter out) throws Exception { generateTagStateChildren(out); } /** * generates tag state */ public void generateTagStateChildren(JspJavaWriter out) throws Exception { } /** * generates tag state release */ public void generateTagRelease(JspJavaWriter out) throws Exception { generateTagReleaseChildren(out); } /** * generates tag state */ public void generateTagReleaseChildren(JspJavaWriter out) throws Exception { } /** * Generates the code for the tag * * @param out the output writer for the generated java. */ abstract public void generate(JspJavaWriter out) throws Exception; /** * Generates the code for the children. * * @param out the output writer for the generated java. */ public void generateChildren(JspJavaWriter out) throws Exception { } /** * Generates the code for the tag * * @param out the output writer for the generated java. */ public void generateClassEpilogue(JspJavaWriter out) throws IOException { generateClassEpilogueChildren(out); } /** * Generates the code for the tag * * @param out the output writer for the generated java. */ public void generateClassEpilogueChildren(JspJavaWriter out) throws IOException { } /** * Generates the code for the tag * * @param out the output writer for the generated java. */ public void generateStatic(JspJavaWriter out) throws Exception { } /** * Generates the code for the tag * * @param out the output writer for the generated java. */ public void generateEmpty() throws Exception { generateChildrenEmpty(); } /** * Generates the code for the children. * * @param out the output writer for the generated java. */ public void generateChildrenEmpty() throws Exception { } /** * Converts the string to a boolean. */ protected boolean attributeToBoolean(String attr, String value) throws JspParseException { if (value.equals("yes") || value.equals("true")) return true; else if (value.equals("no") || value.equals("false")) return false; else throw error(L.l("'{0}' is an unknown value for {1}. 'true' or 'false' are the expected values.", value, attr)); } /** * Returns true if in a fragment */ public boolean isInFragment() { for (JspNode node = getParent(); node != null; node = node.getParent()) { if (node instanceof JspAttribute || node instanceof CustomSimpleTag) return true; } return false; } void generateSetParameter(JspJavaWriter out, String obj, Object objValue, Method method, boolean allowRtexpr, String contextVar, boolean isParentSimpleTag, boolean isFragment, TagAttributeInfo attrInfo) throws Exception { Class<?> type = method.getParameterTypes()[0]; if (isFragment || JspFragment.class.equals(type)) { generateFragmentParameter(out, obj, objValue, method, allowRtexpr, contextVar, isParentSimpleTag); return; } if (objValue instanceof JspAttribute) { JspAttribute attr = (JspAttribute) objValue; if (attr.isStatic()) objValue = attr.getStaticText(); else { String str = "_jsp_str_" + _gen.uniqueId(); out.printClass(type); out.println(" " + str + " = " + attr.generateValue(type) + ";"); out.println(obj + "." + method.getName() + "(" + str + ");"); return; } } else if (objValue instanceof JspNode) throw error(L.l("jsp:attribute may not set this attribute.")); String strValue = (String) objValue; String convValue = generateParameterValue(type, strValue, allowRtexpr, attrInfo, _parseState.isELIgnored()); PropertyEditor editor; if (convValue != null) out.println(obj + "." + method.getName() + "(" + convValue + ");"); else if ((editor = PropertyEditorManager.findEditor(type)) != null) { generateSetParameter(out, obj, strValue, method, editor.getClass()); } else throw error(L.l("expected '<%= ... %>' at '{0}' for tag attribute setter '{1}'. Tag attributes which can't be converted from strings must use a runtime attribute expression.", strValue, method.getName() + "(" + type.getName() + ")")); } void generateSetParameter(JspJavaWriter out, String obj, String value, Method method, Class<?> editorClass) throws Exception { Class<?> type = method.getParameterTypes()[0]; String name = "_jsp_editor" + _gen.uniqueId(); out.print("java.beans.PropertyEditor " + name + " = new " + editorClass.getName() + "();"); out.println(name + ".setAsText(\"" + escapeJavaString(value) + "\");"); out.println(obj + "." + method.getName() + "((" + type.getName() + ") " + name + ".getValue());"); } void generateFragmentParameter(JspJavaWriter out, Object obj, Object objValue, Method method, boolean allowRtexpr, String contextVar, boolean isParentSimpleTag) throws Exception { out.print(obj + "." + method.getName() + "("); if (objValue instanceof JspFragmentNode) generateFragment(out, (JspFragmentNode) objValue, contextVar, isParentSimpleTag); else if (objValue instanceof String) { String string = (String) objValue; int index = _gen.addExpr(string); out.print("new com.caucho.jsp.ELExprFragment(pageContext, _caucho_expr_" + index + ")"); } else { throw error(L.l("can't handle fragment '{0}' of type {1}", objValue, objValue.getClass())); } out.println(");"); } String generateFragmentParameter(String string, boolean allowRtexpr) throws Exception { int index = _gen.addExpr(string); return ("new com.caucho.jsp.ELExprFragment(pageContext, _caucho_expr_" + index + ")"); } /** * Returns the containing segment. */ public JspSegmentNode getSegment() { JspNode parent = getParent(); if (parent != null) return parent.getSegment(); else return null; } void generateFragment(JspJavaWriter out, JspFragmentNode frag, String contextVar, boolean isParentSimpleTag) throws Exception { out.print(generateFragment(frag, contextVar)); } void generateParentTag(JspJavaWriter out, TagInstance parent) throws IOException { String parentId = parent.getId(); if (parentId == null || parentId.startsWith("top_")) { out.print("null"); } else if (parent.isSimpleTag()) { out.print("(" + parentId + "_adapter != null ? "); out.print(parentId + "_adapter : "); out.print("(" + parentId + "_adapter = new javax.servlet.jsp.tagext.TagAdapter(" + parentId + ")))"); } else out.print(parentId); } String generateRTValue(Class<?> type, Object value) throws Exception { if (value instanceof String) return generateParameterValue(type, (String) value, true, null, _parseState.isELIgnored()); else { JspAttribute attr = (JspAttribute) value; return stringToValue(type, attr.generateValue()); } } /** * Generates the code invoking a fragment to a string. */ protected String invokeFragment(JspFragmentNode frag) throws Exception { return frag.generateValue(); } /** * Generates the code for a fragment. */ protected String generateFragment(JspFragmentNode frag, String contextVar) throws Exception { StringBuffer cb = new StringBuffer(); if (frag.isStatic()) { String fragmentVar = frag.getFragmentName(); cb.append(fragmentVar + " = com.caucho.jsp.StaticJspFragmentSupport.create(" + fragmentVar + ", " + contextVar + ", \""); cb.append(escapeJavaString(frag.getStaticText())); cb.append("\")"); return cb.toString(); } int index = frag.getFragmentCode(); getGenerator().addFragment(frag); String fragmentVar = frag.getFragmentName(); JspNode parentTag = getParentTagNode(); // jsp/0800 boolean isParentSimpleTag = (parentTag instanceof CustomSimpleTag); if (! isParentSimpleTag) { cb.append(fragmentVar + " = createFragment_" + index + "(" + fragmentVar + ", _jsp_parentContext" + ", " + contextVar + ", "); } else { cb.append(fragmentVar + " = createFragment_" + index + "(null" + ", _jsp_parentContext" + ", " + contextVar + ", "); } if (parentTag == null) cb.append("null"); else if (frag.hasCustomTag() && parentTag instanceof CustomSimpleTag) cb.append(parentTag.getCustomTagName() + "_adapter"); else cb.append(parentTag.getCustomTagName()); if (_gen instanceof JavaTagGenerator) { JavaTagGenerator tagGen = (JavaTagGenerator) _gen; if (tagGen.isStaticDoTag()) // jsp/1025 cb.append(", _jspBody"); else cb.append(", getJspBody()"); } else cb.append(", null"); cb.append(", _jsp_state"); cb.append(", _jsp_pageManager"); cb.append(")"); return cb.toString(); } /** * Generates the code for the value of a parent tag. */ protected String generateParentTag(TagInstance parent) throws IOException { String parentId = parent.getId(); if (parent.isTop()) { return "null"; } else if (parent.isSimpleTag()) { CharBuffer cb = CharBuffer.allocate(); cb.append("(" + parentId + "_adapter != null ? "); cb.append(parentId + "_adapter : "); cb.append("(" + parentId + "_adapter = new javax.servlet.jsp.tagext.TagAdapter(" + parentId + ")))"); return cb.close(); } else return parentId; } // // JSF functions // /** * Returns the variable containing the jsf component */ public String getJsfVar() { if (_parent != null) return _parent.getJsfVar(); else return null; } /** * Returns the variable containing the jsf body */ public String getJsfBodyVar() { if (_parent != null) return _parent.getJsfBodyVar(); else return null; } /** * True if the jsf-parent setting is required. */ public boolean isJsfParentRequired() { return false; } // // value generation // /** * Generate include params. */ void generateIncludeParams(JspJavaWriter out, ArrayList<JspParam> params) throws Exception { boolean hasQuery = false; for (int i = 0; i < params.size(); i++) { JspParam param = (JspParam) params.get(i); String value = param.getValue(); if (hasQuery) out.print("+ \"&\" + "); hasQuery = true; out.print("\"" + param.getName() + "=\""); String outValue = generateParameterValue(String.class, value); if (outValue.equals("null")) { } else if (outValue.startsWith("\"")) out.print(" + (" + outValue + ")"); else out.print(" + com.caucho.el.Expr.toString(" + outValue + ", null)"); } } protected void generateIncludeUrl(JspJavaWriter out, String page, ArrayList<JspParam> params) throws Exception { if (params != null) { for (int i = 0; i < params.size(); i++) { out.print("pageContext.encode("); } out.print("pageContext.encode("); } if (hasRuntimeAttribute(page)) { out.print(getRuntimeAttribute(page)); } else { out.print(generateParameterValue(String.class, page)); } if (params != null) { out.print(")"); for (int i = 0; i < params.size(); i++) { if (i > 0) out.print(".append('&')"); out.print(", "); generateIncludeParam(out, params.get(i)); out.print(")"); } out.print(".toString()"); } } /** * Generate include params. */ void generateIncludeParam(JspJavaWriter out, JspParam param) throws Exception { String value = param.getValue(); out.print("\"" + param.getName() + "=\""); String outValue = generateParameterValue(String.class, value); if (outValue.equals("null")) { } else if (outValue.startsWith("\"")) { out.print(" + (" + outValue + ")"); } else out.print(" + com.caucho.el.Expr.toString(" + outValue + ", null)"); } String generateJstlValue(Class<?> type, String value) throws Exception { return generateParameterValue(type, value, true, null, false); } String generateValue(Class<?> type, String value) throws Exception { return generateParameterValue(type, value, true, null, _parseState.isELIgnored()); } String generateParameterValue(Class<?> type, String value) throws Exception { return generateParameterValue(type, value, true, null, _parseState.isELIgnored()); } String generateParameterValue(Class<?> type, String value, boolean rtexpr, TagAttributeInfo attrInfo, boolean isELIgnored) throws Exception { // jsp/1c2m if (isJstl()) isELIgnored = false; boolean isEmpty = value == null || value.equals(""); if (isEmpty) value = "0"; try { String typeName = attrInfo != null ? attrInfo.getExpectedTypeName() : ""; boolean isValueDeferred = (attrInfo != null && attrInfo.isDeferredValue() || typeName != null && ! "".equals(typeName)); boolean isMethodDeferred = (attrInfo != null && attrInfo.isDeferredMethod()); if (JspFragment.class.equals(type)) return generateFragmentParameter(value, rtexpr); else if (type.equals(ValueExpression.class)) { int exprIndex; if (isEmpty) exprIndex = _gen.addValueExpr("", typeName); else exprIndex = _gen.addValueExpr(value, typeName); if (isValueDeferred && value.indexOf("#{") < 0 && value.indexOf("${") >= 0) { throw error(L.l("ValueExpression '{0}' must use deferred syntax '#{...}'", value)); } else if (! isValueDeferred && value.indexOf("#{") >= 0 && value.indexOf("${") < 0) { throw error(L.l("Deferred syntax '#{...}' is not allowed for '{0}'", value)); } if (value.indexOf("#{") < 0 && value.indexOf("${") < 0) { return ("_caucho_value_expr_" + exprIndex); } else { StringBuilder sb = new StringBuilder(); sb.append("pageContext.createExpr(_caucho_value_expr_"); sb.append(exprIndex); sb.append(", \""); sb.append(escapeJavaString(value)); sb.append("\", "); if (null == typeName || "".equals(typeName)) { sb.append("java.lang.Object.class)"); } else { sb.append(escapeJavaString(typeName)); sb.append(".class)"); } return sb.toString(); } } else if (type.equals(MethodExpression.class)) { int exprIndex; String sig = attrInfo != null ? attrInfo.getMethodSignature() : "java.lang.String misc()"; if (isEmpty) exprIndex = _gen.addMethodExpr("", sig); else exprIndex = _gen.addMethodExpr(value, sig); if (value.indexOf("${") >= 0) throw error(L.l("MethodExpression '{0}' must use deferred syntax '$#{...}'", value)); return ("_caucho_method_expr_" + exprIndex); } else if (! isValueDeferred && ! _gen.getParseState().isDeferredSyntaxAllowedAsLiteral() && value.indexOf("#{") >= 0 && value.indexOf("${") < 0 && rtexpr && isJsp21()) { // jsp/10h2, jsp/1cn0, jsp/10h3 throw error(L.l("deferred expression '{0}' is not allowed here", value)); } else if (com.caucho.el.Expr.class.equals(type)) { int exprIndex; if (isEmpty) exprIndex = _gen.addExpr(""); else exprIndex = _gen.addExpr(value); return ("_caucho_expr_" + exprIndex); } else if (com.caucho.xpath.Expr.class.equals(type)) { com.caucho.xpath.Expr expr; if (isEmpty) expr = XPath.parseExpr(""); else expr = XPath.parseExpr(value, getNamespaceContext()); return _gen.addXPathExpr(expr); } else if (rtexpr && hasRuntimeAttribute(value)) { return getRuntimeAttribute(value); } else if (rtexpr && hasELAttribute(value, isELIgnored)) { // jsp/0138, jsp/18s0, jsp/1ce5, jsp/1e0a return generateELValue(type, value); } else if (! rtexpr && hasELAttribute(value, isELIgnored)) { // JSP.2.3.6 says this is an error // jsp/184v vs jsp/18cr vs jsp/18f5 vs jsp/18f7 (tck) // #2112 if (String.class.equals(type) && _gen.isELIgnore()) return '"' + escapeJavaString(value) + '"'; else throw error(L.l("EL expression '{0}' is only allowed for attributes with rtexprvalue='true'.", value)); } else if (rtexpr && hasDeferredAttribute(value, false)) { // jsp/1c2m, jsp/1ce8 if (type.equals(String.class)) return '"' + value + '"'; else return generateELValue(type, value); } else if (! rtexpr && hasDeferredAttribute(value, isELIgnored) && ! _gen.getParseState().isDeferredSyntaxAllowedAsLiteral()) { throw error(L.l("Deferred syntax '{0}' is not allowed as a literal.", value)); } else if (type.equals(boolean.class)) return String.valueOf(Boolean.valueOf(isEmpty ? "false" : value)); else if (type.equals(Boolean.class)) { if (isEmpty) return "java.lang.Boolean.FALSE"; else return "new java.lang.Boolean(" + Boolean.valueOf(value) + ")"; } else if (type.equals(byte.class)) return "(byte) " + Byte.valueOf(value); else if (type.equals(Byte.class)) return "new java.lang.Byte((byte) " + Byte.valueOf(value) + ")"; else if (type.equals(char.class)) { if (isEmpty) return "'\\0'"; else return "'" + value.charAt(0) + "'"; } else if (type.equals(Character.class)) { if (isEmpty) return "new java.lang.Character('\\0')"; else return ("new Character('" + value.charAt(0) + "')"); } else if (type.equals(short.class)) return ("(short) " + Short.valueOf(value)); else if (type.equals(Short.class)) return ("new java.lang.Short((short) " + Short.valueOf(value) + ")"); else if (type.equals(int.class)) return String.valueOf(Integer.valueOf(value)); else if (type.equals(Integer.class)) return ("new java.lang.Integer(" + Integer.valueOf(value) + ")"); else if (type.equals(long.class)) return String.valueOf(Long.valueOf(value)); else if (type.equals(Long.class)) return ("new java.lang.Long(" + Long.valueOf(value) + ")"); else if (type.equals(float.class)) return ("(float) " + Float.valueOf(value)); else if (type.equals(Float.class)) return ("new java.lang.Float((float) " + Float.valueOf(value) + ")"); else if (type.equals(double.class)) return String.valueOf(Double.valueOf(value)); else if (type.equals(Double.class)) { double v = Double.valueOf(value); if (Double.isNaN(v)) return ("new java.lang.Double(Double.NaN)"); else return ("new java.lang.Double(" + v + ")"); } else if (! type.equals(String.class) && ! type.equals(Object.class)) { return null; } else if (! isEmpty) { return '"' + escapeJavaString(value) + '"'; } else return "\"\""; } catch (NumberFormatException e) { throw error(L.l("parameter format error: {0}", e.getMessage()), e); } } protected String generateELValue(Class<?> type, String value) throws Exception { if (type.equals(com.caucho.el.Expr.class)) { int exprIndex; exprIndex = _gen.addExpr(value); return ("_caucho_expr_" + exprIndex); } else if (type.equals(ValueExpression.class)) { int exprIndex; exprIndex = _gen.addValueExpr(value, ""); return ("_caucho_value_expr_" + exprIndex); } else if (type.equals(Object.class) && value.contains("#{") && CustomTag.class.equals(getClass())) { int exprIndex; exprIndex = _gen.addValueExpr(value, ""); return ("_caucho_value_expr_" + exprIndex); } else if (type.equals(com.caucho.xpath.Expr.class)) { com.caucho.xpath.Expr expr; expr = XPath.parseExpr(value, getNamespaceContext()); return _gen.addXPathExpr(expr); } Expr expr = _gen.genExpr(value); if (expr.isConstant()) { try { if (expr.evalObject(null) != null) { } else if (Character.class.isAssignableFrom(type)) { // jsp/18s0 return "new Character((char) 0)"; } else if (Boolean.class.isAssignableFrom(type)) { // jsp/18s1 return "Boolean.FALSE"; } else if (String.class.isAssignableFrom(type)) { // jsp/18s2 return "\"\""; } else if (BigInteger.class.isAssignableFrom(type)) { return "java.math.BigInteger.ZERO"; } else if (BigDecimal.class.isAssignableFrom(type)) { return "java.math.BigDecimal.ZERO"; } else if (Number.class.isAssignableFrom(type)) { // jsp/18s6 return "new " + type.getName() + "((byte) 0)"; } else if (Object.class.isAssignableFrom(type)) return "null"; if (boolean.class.equals(type)) return expr.evalBoolean(null) ? "true" : "false"; else if (Boolean.class.equals(type)) return expr.evalBoolean(null) ? "java.lang.Boolean.TRUE" : "java.lang.Boolean.FALSE"; else if (byte.class.equals(type)) return "(byte) " + expr.evalLong(null); else if (Byte.class.equals(type)) return "new java.lang.Byte((byte) " + expr.evalLong(null) + "L)"; else if (short.class.equals(type)) return "(short) " + expr.evalLong(null); else if (Short.class.equals(type)) return "new java.lang.Short((short) " + expr.evalLong(null) + "L)"; else if (int.class.equals(type)) return "(int) " + expr.evalLong(null); else if (Integer.class.equals(type)) return "new java.lang.Integer((int) " + expr.evalLong(null) + "L)"; else if (long.class.equals(type)) return "" + expr.evalLong(null) + "L"; else if (Long.class.equals(type)) return "new java.lang.Long(" + expr.evalLong(null) + "L)"; else if (float.class.equals(type)) return "(float) " + expr.evalDouble(null); else if (Float.class.equals(type)) return "new java.lang.Float((float) " + expr.evalDouble(null) + ")"; else if (double.class.equals(type)) { double v = expr.evalDouble(null); if (Double.isNaN(v)) return "Double.NaN"; else return "" + v; } else if (Double.class.equals(type)) { double v = expr.evalDouble(null); if (Double.isNaN(v)) return "new Double(Double.NaN)"; else return "new java.lang.Double(" + v + ")"; } else if (char.class.equals(type)) return "((char) " + (int) expr.evalCharacter(null) + ")"; else if (Character.class.equals(type)) { // jsp/18s0 return "new Character((char) " + (int) expr.evalCharacter(null) + ")"; } else if (String.class.equals(type)) return "\"" + escapeJavaString(expr.evalString(null)) + "\""; else if (BigInteger.class.equals(type)) { String v = expr.evalBigInteger(null).toString(); // 18s3 if (v.equals("") || v.equals("0")) return "java.math.BigInteger.ZERO"; else return "new java.math.BigInteger(\"" + v + "\")"; } else if (BigDecimal.class.equals(type)) { String v = expr.evalBigDecimal(null).toString(); // 18s4 if (v.equals("") || v.equals("0.0")) return "java.math.BigDecimal.ZERO"; else return "new java.math.BigDecimal(\"" + v + "\")"; } else if (Object.class.equals(type)) { Object cValue = expr.evalObject(null); String result = generateObject(cValue); if (result != null) return result; } else { Object cValue = expr.evalObject(null); // jsp/184t if ("".equals(cValue)) return "null"; } } catch (Throwable e) { // jsp/18co // exceptions are caught at runtime log.log(Level.FINER, e.toString(), e); log.fine(e.getMessage()); } } int exprIndex = _gen.addExpr(value); String var = "_caucho_expr_" + exprIndex; if (boolean.class.equals(type)) return var + ".evalBoolean(_jsp_env)"; else if (Boolean.class.equals(type)) return var + ".evalBoolean(_jsp_env) ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE"; else if (byte.class.equals(type)) return "(byte) " + var + ".evalLong(_jsp_env)"; else if (Byte.class.equals(type)) return "new java.lang.Byte((byte) " + var + ".evalLong(_jsp_env))"; else if (short.class.equals(type)) return "(short) " + var + ".evalLong(_jsp_env)"; else if (Short.class.equals(type)) return "new java.lang.Short((short) " + var + ".evalLong(_jsp_env))"; else if (int.class.equals(type)) return "(int) " + var + ".evalLong(_jsp_env)"; else if (Integer.class.equals(type)) return "new java.lang.Integer((int) " + var + ".evalLong(_jsp_env))"; else if (long.class.equals(type)) return var + ".evalLong(_jsp_env)"; else if (Long.class.equals(type)) return "new java.lang.Long(" + var + ".evalLong(_jsp_env))"; else if (float.class.equals(type)) return "(float) " + var + ".evalDouble(_jsp_env)"; else if (Float.class.equals(type)) return "new java.lang.Float((float) " + var + ".evalDouble(_jsp_env))"; else if (double.class.equals(type)) return var + ".evalDouble(_jsp_env)"; else if (Double.class.equals(type)) return "new java.lang.Double(" + var + ".evalDouble(_jsp_env))"; else if (java.math.BigDecimal.class.equals(type)) return "" + var + ".evalBigDecimal(_jsp_env)"; else if (java.math.BigInteger.class.equals(type)) return "" + var + ".evalBigInteger(_jsp_env)"; else if (char.class.equals(type)) return var + ".evalCharacter(_jsp_env)"; else if (Character.class.equals(type)) return "new Character(" + var + ".evalCharacter(_jsp_env))"; else if (String.class.equals(type)) return var + ".evalString(_jsp_env)"; else if (BigInteger.class.equals(type)) return var + ".evalBigInteger(_jsp_env)"; else if (BigDecimal.class.equals(type)) return var + ".evalBigDecimal(_jsp_env)"; else if (Object.class.equals(type)) return var + ".evalObject(_jsp_env)"; else { return "(" + classToString(type) + ") " + var + ".evalObject(_jsp_env)"; } } public void convertParameterValue(JspJavaWriter out, String type, String value) throws IOException { if (type.equals("boolean")) out.print("java.lang.Boolean.TRUE.equals(" + value + ")"); else if (type.equals("byte")) out.print("java.lang.Byte.valueOf(" + value + ")"); else if (type.equals("char")) out.print("java.lang.Character.valueOf(" + value + ")"); else if (type.equals("short")) out.print("java.lang.Short.valueOf(" + value + ")"); else if (type.equals("int")) out.print("((java.lang.Integer) " + value + ").intValue()"); else if (type.equals("long")) out.print("((java.lang.Long) " + value + ").longValue()"); else if (type.equals("float")) out.print("((java.lang.Float) " + value + ").floatValue()"); else if (type.equals("double")) out.print("((java.lang.Double) " + value + ").doubleValue()"); else out.print("(" + type + ")" + value); } protected String classToString(Class<?> cl) { if (cl.isArray()) return classToString(cl.getComponentType()) + "[]"; else return cl.getName(); } /** * Returns true if the value is a runtime attribute. */ public boolean hasRuntimeAttribute(String value) throws JspParseException { if (_parseState.isScriptingInvalid()) { // && value.indexOf("<%=") >= 0) { return false; /* throw error(L.l("Runtime expressions are forbidden here. Scripting has been disabled either:\n1) disabled by the web.xml scripting-invalid\n2) disabled in a tag's descriptor\n3) forbidden in <jsp:attribute> or <jsp:body> tags.")); */ } if (value.startsWith("<%=") && value.endsWith("%>")) return true; else if (value.startsWith("%=") && value.endsWith("%")) return true; else if (value.indexOf("<%=") >= 0 && value.indexOf("<%=") < value.indexOf("%>")) throw error(L.l("interpolated runtime values are forbidden by the JSP spec at '{0}'", value)); else return false; } /** * Returns true if the string has scripting. */ public boolean hasScripting(String value) { try { return value != null && hasRuntimeAttribute(value); } catch (Exception e) { throw new RuntimeException(e); } } /** * Returns true if the string has scripting. */ public boolean hasScripting(JspAttribute value) { return value != null && value.hasScripting(); } /** * Returns true if the value is a runtime attribute. */ public boolean hasELAttribute(String value) { return ! _parseState.isELIgnored() && value.indexOf("${") >= 0; } /** * Returns true if the value is a runtime attribute. */ public boolean hasDeferredAttribute(String value) { if (value.indexOf("#{") < 0) return false; else if (isPre21Taglib()) return false; else return ! _parseState.isELIgnored(); } /** * Returns true if the value is a runtime attribute. */ public boolean hasELAttribute(String value, boolean isELIgnored) { return ! isELIgnored && value.indexOf("${") >= 0; } /** * Returns true if the value is a runtime attribute. */ public boolean hasDeferredAttribute(String value, boolean isELIgnored) { if (isELIgnored) return false; else if (value.indexOf("#{") < 0) return false; else if (isPre21Taglib()) return false; else return true; } /** * Returns the runtime attribute of the value. */ public String getRuntimeAttribute(String value) throws Exception { if (value.startsWith("<%=") && value.endsWith("%>")) return value.substring(3, value.length() - 2); else if (value.startsWith("%=") && value.endsWith("%")) return value.substring(2, value.length() - 1); else return value; } /** * Converts a string-valued expression to the given type. */ String stringToValue(Class<?> type, String obj) { if (boolean.class.equals(type)) return "com.caucho.jsp.PageContextImpl.toBoolean(" + obj + ")"; else if (Boolean.class.equals(type)) return "java.lang.Boolean.valueOf(" + obj + ")"; else if (byte.class.equals(type)) return "java.lang.Byte.parseByte(" + obj + ")"; else if (Byte.class.equals(type)) return "java.lang.Byte.valueOf(" + obj + ")"; else if (char.class.equals(type)) return obj + ".charAt(0)"; else if (Character.class.equals(type)) return "new java.lang.Character(" + obj + ".charAt(0))"; else if (short.class.equals(type)) return "java.lang.Short.parseShort(" + obj + ")"; else if (Short.class.equals(type)) return "java.lang.Short.valueOf(" + obj + ")"; else if (int.class.equals(type)) return "java.lang.Integer.parseInt(" + obj + ")"; else if (Integer.class.equals(type)) return "java.lang.Integer.valueOf(" + obj + ")"; else if (long.class.equals(type)) return "java.lang.Long.parseLong(" + obj + ")"; else if (Long.class.equals(type)) return "java.lang.Long.valueOf(" + obj + ")"; else if (float.class.equals(type)) return "java.lang.Float.parseFloat(" + obj + ")"; else if (Float.class.equals(type)) return "java.lang.Float.valueOf(" + obj + ")"; else if (double.class.equals(type)) return "java.lang.Double.parseDouble(" + obj + ")"; else if (Double.class.equals(type)) return "java.lang.Double.valueOf(" + obj + ")"; else if (type.isAssignableFrom(String.class)) return obj; else return null; } /** * Converts a string-valued expression to the given type. */ Object staticStringToValue(Class<?> type, String obj) { if (boolean.class.equals(type) || Boolean.class.equals(type)) return Boolean.valueOf(obj); else if (byte.class.equals(type) || Byte.class.equals(type)) return Byte.parseByte(obj); else if (char.class.equals(type) || Character.class.equals(type)) return obj.charAt(0); else if (short.class.equals(type) || Short.class.equals(type)) return Short.parseShort(obj); else if (int.class.equals(type) || Integer.class.equals(type)) return Integer.parseInt(obj); else if (long.class.equals(type) || Long.class.equals(type)) return Long.parseLong(obj); else if (float.class.equals(type) || Float.class.equals(type)) return Float.parseFloat(obj); else if (double.class.equals(type) || Double.class.equals(type)) return Double.parseDouble(obj); else if (type.isAssignableFrom(String.class)) return obj; else return obj; // XXX: } protected String generateObject(Object obj) { if (obj instanceof String) return "\"" + escapeJavaString((String) obj) + "\""; else if (obj instanceof Long) return "new java.lang.Long(" + obj + "L)"; else if (obj instanceof Integer) return "new java.lang.Integer((int) " + obj + "L)"; else if (obj instanceof Double) { double v = (Double) obj; if (Double.isNaN(v)) return "new java.lang.Double(Double.NaN)"; else return "new java.lang.Double(" + v + ")"; } else if (obj instanceof Boolean) return ((Boolean) obj).booleanValue() ? "java.lang.Boolean.TRUE" : "java.lang.Boolean.FALSE"; else return null; } public static String toELObject(String expr, Class<?> type) { if (boolean.class.equals(type)) return "((" + expr + ") ? Boolean.TRUE : Boolean.FALSE)"; else if (byte.class.equals(type)) return "new Long(" + expr + ")"; else if (short.class.equals(type)) return "new Long(" + expr + ")"; else if (int.class.equals(type)) return "new Long(" + expr + ")"; else if (long.class.equals(type)) return "new Long(" + expr + ")"; else if (float.class.equals(type)) return "new Double(" + expr + ")"; else if (double.class.equals(type)) return "new Double(" + expr + ")"; else if (char.class.equals(type)) return "String.valueOf(" + expr + ")"; else return expr; } /** * Escapes a java string. */ public static String escapeJavaString(String s) { if (s == null) return ""; CharBuffer cb = CharBuffer.allocate(); for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '\\') cb.append("\\\\"); else if (s.charAt(i) == '"') cb.append("\\\""); else if (s.charAt(i) == '\n') cb.append("\\n"); else if (s.charAt(i) == '\r') cb.append("\\r"); else cb.append(s.charAt(i)); } return cb.close(); } protected Class loadClass(String type) throws JspParseException { if (type == null) return null; else { try { return _gen.getBeanClass(type); } catch (Exception e) { throw new JspParseException(e); } } } /** * Creates a parse exception with the proper line information. */ protected JspParseException error(String msg) { return error(msg, null); } /** * Creates a parse exception with the proper line information. */ protected JspParseException error(String msg, Throwable e) { if (_filename != null) { String lines = _gen.getSourceLines(_sourcePath, _startLine); return new JspLineParseException(_filename + ":" + _startLine + ": " + msg + lines, e); } else return new JspParseException(msg, e); } /** * Creates a parse exception with the proper line information. */ protected JspParseException error(Throwable e) { if (e instanceof JspLineParseException) return (JspParseException) e; else if (_filename == null || e instanceof LineCompileException) return new JspLineParseException(e); String msg; if (e instanceof CompileException) msg = e.getMessage(); else msg = String.valueOf(e); String lines = _gen.getSourceLines(_sourcePath, _startLine); return new JspLineParseException(_filename + ":" + _startLine + ": " + msg + lines, e); } /** * Returns a printable version of the node. */ public String toString() { if (_name == null) return "<" + getClass().getName() + ">"; else return "<" + _name.getName() + ">"; } }