/*
 * Copyright (c) 1998-2012 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 SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jsp.java;

import com.caucho.VersionFactory;
import com.caucho.config.types.Signature;
import com.caucho.jsp.JspParseException;
import com.caucho.jsp.ParseTagManager;
import com.caucho.jsp.TempTagInfo;
import com.caucho.jsp.cfg.TldAttribute;
import com.caucho.jsp.cfg.TldTag;
import com.caucho.jsp.cfg.TldVariable;
import com.caucho.util.L10N;

import javax.servlet.jsp.tagext.TagInfo;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Generates JSP code.  JavaGenerator, JavaScriptGenerator, and
 * StaticGenerator specialize the JspGenerator for language-specific
 * requirements.
 *
 * <p>JspParser parses the JSP file into an XML-DOM tree.  JspGenerator
 * generates code from that tree.
 */
public class JavaTagGenerator extends JavaJspGenerator {
  static final L10N L = new L10N(JavaTagGenerator.class);
  static final Logger log
    = Logger.getLogger(JavaTagGenerator.class.getName());

  private static HashSet<String> _reserved
    = new HashSet<String>();

  private String _description = null;
  private String _displayName = null;
  private String _smallIcon = null;
  private String _largeIcon = null;
  private String _example = null;
  private String _bodyContent = null;
  private String _dynamicAttributes = null;

  private ArrayList<TldAttribute> _attributes = new ArrayList<TldAttribute>();
  private ArrayList<TldVariable> _variables = new ArrayList<TldVariable>();

  private TempTagInfo _tagInfo = new TempTagInfo();

  public JavaTagGenerator(ParseTagManager tagManager)
  {
    super(tagManager);

    setOmitXmlDeclaration(true);
  }

  public void init()
  {
    super.init();
    
    setOmitXmlDeclaration(true);
  }

  /**
   * Returns true if the XML declaration should be ignored.
   */
  /*
  boolean isOmitXmlDeclaration()
  {
    // tags always omit the declaration
    return true;
  }
  */

  public void setDescription(String description)
  {
    _description = description;
  }

  public String getDescription()
  {
    return _description;
  }

  public void setDisplayName(String displayName)
  {
    _displayName = displayName;
  }

  public String getDisplayName()
  {
    return _displayName;
  }

  public void setSmallIcon(String smallIcon)
  {
    _smallIcon = smallIcon;
  }

  public String getSmallIcon()
  {
    return _smallIcon;
  }

  public void setLargeIcon(String largeIcon)
  {
    _largeIcon = largeIcon;
  }

  public String getLargeIcon()
  {
    return _largeIcon;
  }

  public void setExample(String example)
  {
    _example = example;
  }

  public String getExample()
  {
    return _example;
  }

  /**
   * Sets the body content.
   */
  public void setBodyContent(String bodyContent)
  {
    _bodyContent = bodyContent;
  }

  /**
   * Gets the body content.
   */
  public String getBodyContent()
  {
    return _bodyContent;
  }

  /**
   * Sets the name of the dynamic attributes map
   */
  public void setDynamicAttributes(String dynamicAttributes)
  {
    _dynamicAttributes = dynamicAttributes;
  }

  /**
   * Gets the body content.
   */
  public String getDynamicAttributes()
  {
    return _dynamicAttributes;
  }

  /**
   * Adds an attribute.
   */
  public void addAttribute(TldAttribute attribute)
  {
    _attributes.add(attribute);
  }

  /**
   * Returns the attributes.
   */
  public ArrayList<TldAttribute> getAttributes()
  {
    return _attributes;
  }

  /**
   * Finds an attribute.
   */
  public TldAttribute findAttribute(String name)
  {
    for (int i = 0; i < _attributes.size(); i++) {
      TldAttribute attr = _attributes.get(i);

      if (name.equals(attr.getName()))
        return attr;
    }

    return null;
  }

  /**
   * Adds a variable.
   */
  public void addVariable(TldVariable var)
  {
    _variables.add(var);
  }

  /**
   * Finds a variable.
   */
  public TldVariable findVariable(String name)
  {
    for (int i = 0; i < _variables.size(); i++) {
      TldVariable var = _variables.get(i);

      // jsp/1071, jsp/106g (tck)
      if (name.equals(var.getNameGiven())
          || name.equals(var.getAlias()))
        return var;
    }

    return null;
  }

  /**
   * Finds a variable.
   */
  public TldVariable findNameFromAttributeVariable(String name)
  {
    for (int i = 0; i < _variables.size(); i++) {
      TldVariable var = _variables.get(i);

      // jsp/106g (tck)
      if (name.equals(var.getNameFromAttribute()))
        return var;
    }

    return null;
  }

  /**
   * Returns the variables.
   */
  public ArrayList<TldVariable> getVariables()
  {
    return _variables;
  }

  public boolean isTag()
  {
    return true;
  }

  /**
   * Returns true for XML.
   */
  boolean isXml()
  {
    return _parseState.isXml();
  }

  /**
   * Generates the Java code.
   */
  protected void generate(JspJavaWriter out)
    throws Exception
  {
    out.setLineMap(_lineMap);
    
    generateClassHeader(out);

    generateAttributes(out);

    if (_dynamicAttributes != null)
      generateDynamicAttributes(out);

    generateDoTag(out, _rootNode);

    // if (isStaticDoTag())
    //  generateStaticDoTag(out, _rootNode);
    
    generateDoTagImpl(out, _rootNode);

    generateTagInfo(out);
    
    generateClassFooter(out);
  }

  protected boolean isStaticDoTag()
  {
    // return ! hasScripting();
    return false;
  }

  /**
   * Generates the class header.
   *
   * @param doc the XML document representing the JSP page.
   */
  protected void generateClassHeader(JspJavaWriter out)
    throws IOException, JspParseException
  {
    out.println("/*");
    out.println(" * JSP-Tag generated by " + VersionFactory.getFullVersion());
    out.println(" */" );
    out.println();

    if (_pkg != null && ! _pkg.equals(""))
      out.println("package " + _pkg + ";");

    out.println("import javax.servlet.*;");
    out.println("import javax.servlet.jsp.*;");
    out.println("import javax.servlet.http.*;");

    fillSingleTaglibImports();

    ArrayList<String> imports = _parseState.getImportList();
    for (int i = 0; i < imports.size(); i++) {
      String name = imports.get(i);
      out.print("import ");
      out.print(name);
      out.println(";");
    }
    _parseState.addImport("javax.servlet.*");
    _parseState.addImport("javax.servlet.jsp.*");
    _parseState.addImport("javax.servlet.http.*");
    _parseState.addImport("java.lang.*");
    out.println();

    out.print("public class ");
    out.print(_className);

    if (hasScripting())
      out.print(" extends com.caucho.jsp.java.JspTagSupport");
    else
      out.print(" extends com.caucho.jsp.java.JspTagFileSupport");

    if (_dynamicAttributes != null)
      out.print(" implements javax.servlet.jsp.tagext.DynamicAttributes");

    out.println(" {");
    out.pushDepth();

    // jsp/102e, jsp/102g
    if (_rootNode != null && _rootNode.hasCustomTag()) {
      out.println("public static final boolean _caucho_hasCustomTag = true;");
    } else {
      out.println("public static final boolean _caucho_hasCustomTag = false;");
    }
    
    out.print("static "); // XXX: this shouldn't be created each tag
    out.println("private final java.util.HashMap<String,java.lang.reflect.Method> _jsp_functionMap = new java.util.HashMap<String,java.lang.reflect.Method>();");
    // jsp/107{0,1}
    out.println("private static com.caucho.jsp.PageManager _jsp_pageManager;");

    out.println("private boolean _caucho_isDead;");
    out.println("private boolean _caucho_isNotModified;");

    for (int i = 0; i < _declarations.size(); i++) {
      JspDeclaration decl = _declarations.get(i);

      out.println();
      decl.generateDeclaration(out);
    }
  }

  /**
   * Generates the attribute definitions.
   *
   * @param out the writer to the .java source
   */
  protected void generateAttributes(JspJavaWriter out)
    throws IOException, JspParseException
  {
    for (int i = 0; i < _attributes.size(); i++) {
      TldAttribute attr = _attributes.get(i);

      String name = attr.getName();

      String upperName;
      char ch = name.charAt(0);
      upperName = Character.toUpperCase(ch) + name.substring(1);

      Class cl = attr.getType();
      if (cl == null)
        cl = String.class;
      String type = cl.getName();

      String isSetName = "_jsp_" + name + "_isSet";

      String fieldName = toFieldName(name);

      out.println();
      out.print("private ");
      out.printClass(cl);
      out.println(" " + fieldName + ";");
      out.println("private boolean " + isSetName + ";");
      
      out.println();
      out.print("public void set" + upperName + "(");
      out.printClass(cl);
      out.println(" value)");
      out.println("{");
      out.pushDepth();
      out.println("this." + isSetName + " = true;");
      out.println("this." + fieldName + " = value;");
      out.popDepth();
      out.println("}");

      /*
      // jsp/101f
      out.println();
      out.println("public " + type + " get" + upperName + "()");
      out.println("{");
      out.pushDepth();
      out.println("return " + fieldName + ";");
      out.popDepth();
      out.println("}");
      */
    }
  }

  /**
   * Generates the attribute definitions.
   *
   * @param out the writer to the .java source
   */
  protected void generateDynamicAttributes(JspJavaWriter out)
    throws IOException, JspParseException
  {
    String dyn = toFieldName(_dynamicAttributes);

    out.println();
    out.println("java.util.HashMap " + dyn + " = new java.util.HashMap();");
    out.println();
    out.println("public void setDynamicAttribute(String uri, String localName, Object value)");
    out.println("  throws javax.servlet.jsp.JspException");
    out.println("{");
    out.println("  if (uri == null || \"\".equals(uri))");
    out.println("    " + dyn + ".put(localName, value);");
    out.println("}");
  }

  /**
   * Prints the _jspService header
   */
  protected void generateDoTag(JspJavaWriter out, JspNode node)
    throws Exception
  {
    out.println();
    out.println("public void doTag()");
    out.println("  throws javax.servlet.jsp.JspException, java.io.IOException");
    out.println("{");
    out.pushDepth();

    out.println("javax.servlet.jsp.JspContext _jsp_parentContext = getJspContext();");
    out.println("com.caucho.jsp.PageContextWrapper pageContext = _jsp_pageManager.createPageContextWrapper(_jsp_parentContext);");
    // jsp/1056
    out.println("setJspContext(pageContext);");

    if (false && hasScripting()) {
      out.println("javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) pageContext.getRequest();");
      out.println("javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) pageContext.getResponse();");
      out.println("javax.servlet.http.HttpSession session = pageContext.getSession();");
      out.println("javax.servlet.ServletContext application = pageContext.getServletContext();");
      out.println("javax.servlet.ServletConfig config = pageContext.getServletConfig();");
    }

    out.println("com.caucho.jsp.PageContextWrapper jspContext = pageContext;");
    out.println("javax.el.ELContext _jsp_env = pageContext.getELContext();");
    out.println("javax.servlet.jsp.JspWriter out = pageContext.getOut();");
    
    // generateTagAttributes(out);
    
    //if (hasScripting())
    //  generatePrologue(out);

    out.println("try {");
    out.pushDepth();
     
    // jsp/10a2
    if (false && hasScripting()) {
      out.println("TagState _jsp_state = new TagState();");
      // jsp/100h
      out.println("javax.servlet.jsp.tagext.JspTag _jsp_parent_tag");
      out.println("  = new javax.servlet.jsp.tagext.TagAdapter(this);");

      node.generate(out);
      
      generateTagVariablesAtEnd(out);
    } else {
      out.println("doTag(_jsp_parentContext, pageContext, out, null, this);");
    }
    
    out.popDepth();
    out.println("} catch (Throwable e) {");
    out.println("  if (e instanceof java.io.IOException)");
    out.println("    throw (java.io.IOException) e;");
    out.println("  throw com.caucho.jsp.QJspException.createJspException(e);");
    out.println("}");

    if (hasScripting() && _variables.size() > 0) {
      out.println("finally {");
      out.pushDepth();
      
      // generateTagVariablesAtEnd(out);
      
      out.println("setJspContext(_jsp_parentContext);");
      out.println("_jsp_pageManager.freePageContextWrapper(pageContext);");
      
      out.popDepth();
      out.println("}");
    }
  
    out.popDepth();
    out.println("}");
  }

  /**
   * Generates the attribute definitions.
   *
   * @param out the writer to the .java source
   */
  protected void generateTagAttributes(JspJavaWriter out)
    throws IOException, JspParseException
  {
    for (int i = 0; i < _attributes.size(); i++) {
      TldAttribute attr = _attributes.get(i);

      String name = attr.getName();

      String upperName;
      char ch = name.charAt(0);
      upperName = Character.toUpperCase(ch) + name.substring(1);

      Class cl = attr.getType();
      if (cl == null)
        cl = String.class;
      String type = cl.getName();

      String isSetName = "_jsp_" + name + "_isSet";
      String fieldName = toFieldName(name);
      
      out.println("if (" + isSetName + ")");
      out.println("  pageContext.setAttribute(\"" + name + "\", " +
                  JspNode.toELObject(fieldName, cl) + ");");
    }

    // jsp/10a1
    if (_dynamicAttributes != null) {
      out.println("pageContext.setAttribute(\"" + _dynamicAttributes + "\"," +
                  toFieldName(_dynamicAttributes) + ");");
    }
  }

  /**
   * Prints the _jspService header
   */
  protected void generateDoTagImpl(JspJavaWriter out, JspNode node)
    throws Exception
  {
    out.println();
    out.println("public void doTag(javax.servlet.jsp.JspContext _jsp_parentContext,");
    out.println("                         com.caucho.jsp.PageContextWrapper pageContext,");
    out.println("                         javax.servlet.jsp.JspWriter out,");
    out.println("                         javax.servlet.jsp.tagext.JspFragment _jspBody,");
    out.println("                         javax.servlet.jsp.tagext.JspTag jsp_parent_tag)");
    out.println("  throws Throwable");
    out.println("{");
    out.pushDepth();

    out.println("javax.el.ELContext _jsp_env = pageContext.getELContext();");

    if (node.hasScripting()) {
      out.println("javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) pageContext.getRequest();");
      out.println("javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) pageContext.getResponse();");
      out.println("javax.servlet.http.HttpSession session = pageContext.getSession();");
      out.println("javax.servlet.ServletContext application = pageContext.getServletContext();");
      out.println("javax.servlet.ServletConfig config = pageContext.getServletConfig();");
      out.println("com.caucho.jsp.PageContextWrapper jspContext = pageContext;");
    }
    
    /*
    out.println("if (_jspBody != null)");
    out.println("  setJspBody(_jspBody);"); // jsp/1025 vs jsp/102h
    */
    
    out.println("TagState _jsp_state = new TagState();");
    out.println("javax.servlet.jsp.tagext.JspTag _jsp_parent_tag = jsp_parent_tag;");

    generateTagAttributes(out);
    
    generatePrologue(out);

    //out.println("try {");
    //out.pushDepth();
    
    node.generate(out);

    //out.popDepth();
    //out.println("} finally {");
    //out.pushDepth();
      
    generateTagVariablesAtEnd(out);
      
    //out.println("_jsp_pageManager.freePageContextWrapper(pageContext);");
      
    //out.popDepth();
    //out.println("}");

    out.popDepth();
    out.println("}");
  }

  /**
   * Generates prologue stuff
   *
   * @param out the writer to the .java source
   */
  protected void generatePrologue(JspJavaWriter out)
    throws Exception
  {
    _rootNode.generatePrologue(out);
    
    for (int i = 0; i < _variables.size(); i++) {
      TldVariable var = _variables.get(i);

      if (var.getNameFromAttribute() != null) {
        out.print("String _jsp_var_from_attribute_" + i + " = (String) ");
        out.println("pageContext.getAttribute(\"" +
                    var.getNameFromAttribute() + "\");");
      }
      
      if ("AT_END".equals(var.getScope()))
        continue;

      String srcName = var.getNameGiven();
      if (srcName == null)
        srcName = var.getAlias();
      
      String dstName;
      if (var.getNameGiven() != null)
        dstName = "\"" + var.getNameGiven() + "\"";
      else
        dstName = "_jsp_var_from_attribute_" + i;

      if ("NESTED".equals(var.getScope())) {
        out.print("Object _jsp_nested_var_" + i + " = ");
        out.println("_jsp_parentContext.getAttribute(" + dstName + ");");
      }
      /*
      else {
        out.print("pageContext.setAttribute(\"" + srcName + "\",");
        out.println("_jsp_parentContext.getAttribute(" + dstName + "));");
      }
      */
    }
  }

  /**
   * Generates the variable setting.
   *
   * @param out the writer to the .java source
   */
  protected void generateTagVariablesAtEnd(JspJavaWriter out)
    throws IOException, JspParseException
  {
    for (int i = 0; i < _variables.size(); i++) {
      TldVariable var = _variables.get(i);
      
      String srcName = var.getNameGiven();
      if (srcName == null)
        srcName = var.getAlias();
      
      String dstName;
      if (var.getNameGiven() != null)
        dstName = "\"" + var.getNameGiven() + "\"";
      else
        dstName = "_jsp_var_from_attribute_" + i;

      if ("NESTED".equals(var.getScope())) {
        out.println("_jsp_parentContext.setAttribute(" + dstName + ", _jsp_nested_var_" + i + ");");
      }
      else {
        out.print("_jsp_parentContext.setAttribute(" + dstName + ",");
        out.println("pageContext.getAttribute(\"" + srcName + "\"));");
      }
    }
  }

  public TagInfo generateTagInfo(String className, TagLibraryInfo taglib)
  {
    init(className);
    
    TldTag tag = new TldTag();
    
    for (int i = 0; i < _attributes.size(); i++) {
      TldAttribute attr = _attributes.get(i);

      tag.addAttribute(attr);
    }
    
    for (int i = 0; i < _variables.size(); i++) {
      TldVariable var = _variables.get(i);

      try {
        tag.addVariable(var);
      } catch (Exception e) {
        log.log(Level.WARNING, e.toString(), e);
      }
    }
    
    String bodyContent = _bodyContent;
    if (bodyContent == null)
      bodyContent = "scriptless";
    
    return new TagInfoExt(tag.getName(),
                          _fullClassName,
                          bodyContent,
                          getDescription(),
                          taglib,
                          null,
                          tag.getAttributes(),
                          getDisplayName(),
                          getSmallIcon(),
                          getLargeIcon(),
                          tag.getVariables(),
                          _dynamicAttributes != null,
                          _dynamicAttributes,
                          null);
  }
  
  /**
   * Generates the attribute definitions.
   *
   * @param out the writer to the .java source
   */
  protected void generateTagInfo(JspJavaWriter out)
    throws IOException, JspParseException
  {
    /*
    out.println();
    out.println("public javax.servlet.jsp.tagext.TagInfo _caucho_getTagInfo()");
    out.println("  throws com.caucho.config.ConfigException");
    out.println("{");
    out.pushDepth();
    out.println("  return _caucho_getTagInfo(_caucho_getTagLibrary());");
    out.popDepth();
    out.println("}");
    */
    
    out.println();
    out.println("public javax.servlet.jsp.tagext.TagInfo _caucho_getTagInfo(javax.servlet.jsp.tagext.TagLibraryInfo taglib)");
    out.println("  throws com.caucho.config.ConfigException");
    out.println("{");
    out.pushDepth();
    out.println("com.caucho.jsp.cfg.TldTag tag = new com.caucho.jsp.cfg.TldTag();");

    out.println("tag.setName(\"test\");");

    out.println("com.caucho.jsp.cfg.TldAttribute attr;");
    for (int i = 0; i < _attributes.size(); i++) {
      TldAttribute attr = _attributes.get(i);

      out.println("attr = new com.caucho.jsp.cfg.TldAttribute();");
      out.println("attr.setName(\"" + attr.getName() + "\");");

      Class type = attr.getType();
      if (type != null) {
        out.print("attr.setType(");
        out.printClass(type);
        out.println(".class);");
      }
      out.println("attr.setRtexprvalue(" + attr.getRtexprvalue() + ");");
      out.println("attr.setRequired(" + attr.getRequired() + ");");

      if (attr.getDeferredValue() != null) {
        out.println("attr.setDeferredValue(new com.caucho.jsp.cfg.TldAttribute.DeferredValue());");

        if (attr.getDeferredValue().getType() != null) {
          out.print("attr.getDeferredValue().setType(\"");
          out.printJavaString(attr.getDeferredValue().getType());
          out.println("\");");
        }
      }

      if (attr.getDeferredMethod() != null) {
        out.println("attr.setDeferredMethod(new com.caucho.jsp.cfg.TldAttribute.DeferredMethod());");

        Signature sig = attr.getDeferredMethod().getMethodSignature();

        if (sig != null) {
          out.print("attr.getDeferredMethod().setMethodSignature(");
          out.print("new com.caucho.config.types.Signature(\"");
          out.printJavaString(sig.getSignature());
          out.println("\"));");
        }
      }

      out.println("tag.addAttribute(attr);");
    }

    out.println("com.caucho.jsp.cfg.TldVariable var;");
    for (int i = 0; i < _variables.size(); i++) {
      TldVariable var = _variables.get(i);

      out.println("var = new com.caucho.jsp.cfg.TldVariable();");

      if (var.getNameGiven() != null)
        out.println("var.setNameGiven(\"" + var.getNameGiven() + "\");");
      
      if (var.getNameFromAttribute() != null)
        out.println("var.setNameFromAttribute(\"" + var.getNameFromAttribute() + "\");");

      String type = var.getVariableClass();
      if (type != null)
        out.println("var.setVariableClass(\"" + type + "\");");
      out.println("var.setDeclare(" + var.getDeclare() + ");");
      if (var.getScope() != null)
        out.println("var.setScope(\"" + var.getScope() + "\");");

      out.println("tag.addVariable(var);");
    }
    
    String bodyContent = _bodyContent;
    if (bodyContent == null)
      bodyContent = "scriptless";

    out.println("return new com.caucho.jsp.java.TagInfoExt(tag.getName(),");
    out.println("                   getClass().getName(),");
    out.println("                   \"" + bodyContent + "\",");
    out.print("                   \"");
    if (_description != null)
      out.printJavaString(_description);
    else
      out.printJavaString("A simple tag");
    out.println("\",");
    out.println("                   taglib,");
    out.println("                   null,");
    out.println("                   tag.getAttributes(),");
    
    if (_displayName != null) {
      out.print("                   \"");
      out.printJavaString(_displayName);
      out.println("\",");
    }
    else {
      out.println("                   null,");
    }
    
    if (_smallIcon != null) {
      out.print("                   \"");
      out.printJavaString(_smallIcon);
      out.println("\",");
    }
    else {
      out.println("                   null,");
    }
    
    if (_largeIcon != null) {
      out.print("                   \"");
      out.printJavaString(_largeIcon);
      out.println("\",");
    }
    else {
      out.println("                   null,");
    }
    
    out.println("                   tag.getVariables(),");
    out.println("                   " + (_dynamicAttributes != null) + ",");
    if (_dynamicAttributes != null)
      out.println("                   \"" + _dynamicAttributes + "\",");
    else
      out.println("                   null,");
    out.println("                   _caucho_depends.getDependencies());");

    out.popDepth();
    out.println("}");
    
    out.println();
    out.println("public String _caucho_getDynamicAttributes()");
    out.println("{");
    out.pushDepth();

    if (_dynamicAttributes != null)
      out.println("return \"" + _dynamicAttributes + "\";");
    else
      out.println("return null;");
    
    out.popDepth();
    out.println("}");

    generateTagLibrary(out);
  }
  
  /**
   * Generates the attribute definitions.
   *
   * @param out the writer to the .java source
   */
  protected void generateTagLibrary(JspJavaWriter out)
    throws IOException, JspParseException
  {
    out.println();
    out.println("private javax.servlet.jsp.tagext.TagLibraryInfo _caucho_getTagLibrary()");
    out.println("  throws com.caucho.config.ConfigException");
    out.println("{");
    out.pushDepth();

    out.println("return new com.caucho.jsp.java.TagTaglib(\"x\", \"http://test.com\");");
    out.popDepth();
    out.println("}");
  }

  private String toFieldName(String name)
  {
    /*n
    if (hasScripting() && ! _reserved.contains(name))
      return name;
    else
      return "_" + name;
      */
    
    if (! _reserved.contains(name))
      return name;
    else
      return "_" + name;
  }

  static {
    _reserved.add("public");
    _reserved.add("private");
    _reserved.add("protected");
    _reserved.add("static");
    _reserved.add("final");
    _reserved.add("class");
    _reserved.add("module");
    _reserved.add("interface");
    _reserved.add("extends");
    _reserved.add("implements");
    _reserved.add("package");
    _reserved.add("import");
    _reserved.add("new");
    _reserved.add("if");
    _reserved.add("else");
    _reserved.add("for");
    _reserved.add("do");
    _reserved.add("while");
    _reserved.add("break");
    _reserved.add("continue");
    _reserved.add("switch");
    _reserved.add("case");
    _reserved.add("default");
    _reserved.add("throw");
    _reserved.add("enum");
    _reserved.add("throws");
    
    _reserved.add("void");
    _reserved.add("boolean");
    _reserved.add("byte");
    _reserved.add("char");
    _reserved.add("short");
    _reserved.add("int");
    _reserved.add("long");
    _reserved.add("float");
    
    _reserved.add("true");
    _reserved.add("false");
    _reserved.add("null");
  }
}
