/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/jsp/java/JspNode.java
/*
 * 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("&lt;");
        break;
      case '>':
        cb.append("&gt;");
        break;
      case '&':
        cb.append("&amp;");
        break;
      case '"':
        cb.append("&quot;");
        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("&amp;");
        break;
      case '<':
        cb.append("&lt;");
        break;
      case '>':
        cb.append("&gt;");
        break;
      case '"':
        cb.append("&quot;");
        break;
      case '\'':
        cb.append("&apos;");
        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() + ">";
  }
}
