gt:                  tempVars = new HashMap<String,String>();
context: /**
   *  Copyright (c) 1997-2013, www.tinygroup.org (luo_guo@icloud.com).
   *
   *  Licensed under the GPL, Version 3.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *       http://www.gnu.org/licenses/gpl.html
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  package org.tinygroup.jspengine.compiler;
  
  import java.util.Iterator;
  import java.util.List;
  import java.util.Vector;
  import java.util.ArrayList;
  import java.util.HashMap;
  
  import javax.servlet.jsp.tagext.BodyTag;
  import javax.servlet.jsp.tagext.DynamicAttributes;
  import javax.servlet.jsp.tagext.IterationTag;
  import javax.servlet.jsp.tagext.SimpleTag;
  import javax.servlet.jsp.tagext.TagAttributeInfo;
  import javax.servlet.jsp.tagext.TagData;
  import javax.servlet.jsp.tagext.TagFileInfo;
  import javax.servlet.jsp.tagext.TagInfo;
  import javax.servlet.jsp.tagext.TagVariableInfo;
  import javax.servlet.jsp.tagext.TryCatchFinally;
  import javax.servlet.jsp.tagext.VariableInfo;
  
  import org.tinygroup.jspengine.JasperException;
  import org.tinygroup.jspengine.compiler.tagplugin.TagPluginContext;
  import org.xml.sax.Attributes;
  
  
  /**
   * An internal data representation of a JSP page or a JSP docuement (XML).
   * Also included here is a visitor class for tranversing nodes.
   *
   * @author Kin-man Chung
   * @author Jan Luehe
   * @author Shawn Bayern
   * @author Mark Roth
   */
  
  abstract class Node implements TagConstants {
  
      private static final VariableInfo[] ZERO_VARIABLE_INFO = { };
      
      protected Attributes attrs;
  
      // xmlns attributes that represent tag libraries (only in XML syntax)
      protected Attributes taglibAttrs;
  
      /*
       * xmlns attributes that do not represent tag libraries
       * (only in XML syntax)
       */
      protected Attributes nonTaglibXmlnsAttrs;
  
      protected Nodes body;
      protected String text;
      protected Mark startMark;
      protected int beginJavaLine;
      protected int endJavaLine;
      protected Node parent;
      protected Nodes namedAttributeNodes; // cached for performance
      protected String qName;
      protected String localName;
      /*
       * The name of the inner class to which the codes for this node and
       * its body are generated.  For instance, for <jsp:body> in foo.jsp,
       * this is "foo_jspHelper".  This is primarily used for communicating
       * such info from Generator to Smap generator.
       */
      protected String innerClassName;
  
      private boolean isDummy;
  
      /**
       * Zero-arg Constructor.
       */
      public Node() {
  	this.isDummy = true;
      }
  
      /**
       * Constructor.
       *
       * @param start The location of the jsp page
       * @param parent The enclosing node
       */
      public Node(Mark start, Node parent) {
  	this.startMark = start;
  	this.isDummy = (start == null);
  	addToParent(parent);
      }
  
      /**
       * Constructor.
       *
       * @param qName The action's qualified name
       * @param localName The action's local name
       * @param start The location of the jsp page
       * @param parent The enclosing node
       */
      public Node(String qName, String localName, Mark start, Node parent) {
  	this.qName = qName;
  	this.localName = localName;
  	this.startMark = start;
  	this.isDummy = (start == null);
  	addToParent(parent);
      }
  
      /**
       * Constructor for Nodes parsed from standard syntax.
       *
       * @param qName The action's qualified name
       * @param localName The action's local name
       * @param attrs The attributes for this node
       * @param start The location of the jsp page
       * @param parent The enclosing node
       */
      public Node(String qName, String localName, Attributes attrs, Mark start,
  		Node parent) {
  	this.qName = qName;
  	this.localName = localName;
  	this.attrs = attrs;
  	this.startMark = start;
  	this.isDummy = (start == null);
  	addToParent(parent);
      }
  
      /**
       * Constructor for Nodes parsed from XML syntax.
       *
       * @param qName The action's qualified name
       * @param localName The action's local name
       * @param attrs The action's attributes whose name does not start with
       * xmlns
       * @param nonTaglibXmlnsAttrs The action's xmlns attributes that do not
       * represent tag libraries
       * @param taglibAttrs The action's xmlns attributes that represent tag
       * libraries
       * @param start The location of the jsp page
       * @param parent The enclosing node
       */
      public Node(String qName, String localName, Attributes attrs,
  		Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
  		Mark start, Node parent) {
  	this.qName = qName;
  	this.localName = localName;
  	this.attrs = attrs;
  	this.nonTaglibXmlnsAttrs = nonTaglibXmlnsAttrs;
  	this.taglibAttrs = taglibAttrs;
  	this.startMark = start;
  	this.isDummy = (start == null);
  	addToParent(parent);
      }
  
      /*
       * Constructor.
       *
       * @param qName The action's qualified name
       * @param localName The action's local name
       * @param text The text associated with this node
       * @param start The location of the jsp page
       * @param parent The enclosing node
       */
      public Node(String qName, String localName, String text, Mark start,
  		Node parent) {
  	this.qName = qName;
  	this.localName = localName;
  	this.text = text;
  	this.startMark = start;
  	this.isDummy = (start == null);
  	addToParent(parent);
      }
  
      public String getQName() {
  	return this.qName;
      }
  
      public String getLocalName() {
  	return this.localName;
      }
  
      /*
       * Gets this Node's attributes.
       *
       * In the case of a Node parsed from standard syntax, this method returns
       * all the Node's attributes.
       *
       * In the case of a Node parsed from XML syntax, this method returns only
       * those attributes whose name does not start with xmlns.
       */
      public Attributes getAttributes() {
  	return this.attrs;
      }
  
      /*
       * Gets this Node's xmlns attributes that represent tag libraries
       * (only meaningful for Nodes parsed from XML syntax)
       */
      public Attributes getTaglibAttributes() {
  	return this.taglibAttrs;
      }
  
      /*
       * Gets this Node's xmlns attributes that do not represent tag libraries
       * (only meaningful for Nodes parsed from XML syntax)
       */
      public Attributes getNonTaglibXmlnsAttributes() {
  	return this.nonTaglibXmlnsAttrs;
      }
  
      public void setAttributes(Attributes attrs) {
  	this.attrs = attrs;
      }
  
      public String getAttributeValue(String name) {
  	return (attrs == null) ? null : attrs.getValue(name);
      }
  
      /**
       * Get the attribute that is non request time expression, either
       * from the attribute of the node, or from a jsp:attrbute 
       */
      public String getTextAttribute(String name) {
  
  	String attr = getAttributeValue(name);
  	if (attr != null) {
  	    return attr;
  	}
  
  	NamedAttribute namedAttribute = getNamedAttributeNode(name);
  	if (namedAttribute == null) {
  	    return null;
  	}
  
  	return namedAttribute.getText();
      }
  
      /**
       * Searches all subnodes of this node for jsp:attribute standard
       * actions with the given name, and returns the NamedAttribute node
       * of the matching named attribute, nor null if no such node is found.
       * <p>
       * This should always be called and only be called for nodes that
       * accept dynamic runtime attribute expressions.
       */
      public NamedAttribute getNamedAttributeNode( String name ) {
          NamedAttribute result = null;
          
          // Look for the attribute in NamedAttribute children
          Nodes nodes = getNamedAttributeNodes();
          int numChildNodes = nodes.size();
          for( int i = 0; i < numChildNodes; i++ ) {
              NamedAttribute na = (NamedAttribute)nodes.getNode( i );
  	    boolean found = false;
  	    int index = name.indexOf(':');
  	    if (index != -1) {
  		// qualified name
  		found = na.getName().equals(name);
  	    } else {
  		found = na.getLocalName().equals(name);
  	    }
  	    if (found) {
                  result = na;
                  break;
              }
          }
          
          return result;
      }
  
      /**
       * Searches all subnodes of this node for jsp:attribute standard
       * actions, and returns that set of nodes as a Node.Nodes object.
       *
       * @return Possibly empty Node.Nodes object containing any jsp:attribute
       * subnodes of this Node
       */
      public Node.Nodes getNamedAttributeNodes() {
  
  	if (namedAttributeNodes != null) {
  	    return namedAttributeNodes;
  	}
  
          Node.Nodes result = new Node.Nodes();
          
          // Look for the attribute in NamedAttribute children
          Nodes nodes = getBody();
          if( nodes != null ) {
              int numChildNodes = nodes.size();
              for( int i = 0; i < numChildNodes; i++ ) {
                  Node n = nodes.getNode( i );
                  if( n instanceof NamedAttribute ) {
                      result.add( n );
                  }
                  else if (! (n instanceof Comment)) {
                      // Nothing can come before jsp:attribute, and only
                      // jsp:body can come after it.
                      break;
                  }
              }
          }
  
  	namedAttributeNodes = result;
          return result;
      }
      
      public Nodes getBody() {
  	return body;
      }
  
      public void setBody(Nodes body) {
  	this.body = body;
      }
  
      public String getText() {
  	return text;
      }
  
      public Mark getStart() {
  	return startMark;
      }
  
      public Node getParent() {
  	return parent;
      }
  
      public int getBeginJavaLine() {
  	return beginJavaLine;
      }
  
      public void setBeginJavaLine(int begin) {
  	beginJavaLine = begin;
      }
  
      public int getEndJavaLine() {
  	return endJavaLine;
      }
  
      public void setEndJavaLine(int end) {
  	endJavaLine = end;
      }
  
      public boolean isDummy() {
  	return isDummy;
      }
  
      public Node.Root getRoot() {
  	Node n = this;
  	while (!(n instanceof Node.Root)) {
  	    n = n.getParent();
  	}
  	return (Node.Root) n;
      }
  
      public String getInnerClassName() {
          return innerClassName;
      }
  
      public void setInnerClassName(String icn) {
          innerClassName = icn;
      }
  
      /**
       * Selects and invokes a method in the visitor class based on the node
       * type.  This is abstract and should be overrode by the extending classes.
       * @param v The visitor class
       */
      abstract void accept(Visitor v) throws JasperException;
  
  
      //*********************************************************************
      // Private utility methods
  
      /*
       * Adds this Node to the body of the given parent.
       */
      private void addToParent(Node parent) {
  	if (parent != null) {
  	    this.parent = parent;
  	    Nodes parentBody = parent.getBody();
  	    if (parentBody == null) {
  		parentBody = new Nodes();
  		parent.setBody(parentBody);
  	    }
  	    parentBody.add(this);
  	}
      }
  
  
      /*********************************************************************
       * Child classes
       */
      
      /**
       * Represents the root of a Jsp page or Jsp document
       */
      public static class Root extends Node {
  
  	private Root parentRoot;
  	private boolean isXmlSyntax;
          private boolean hasBom;
  
  	// Source encoding of the page containing this Root
  	private String pageEnc;
  	
  	// Page encoding specified in JSP config element
  	private String jspConfigPageEnc;
  
  	/*
  	 * Flag indicating if the default page encoding is being used (only
  	 * applicable with standard syntax).
  	 *
  	 * True if the page does not provide a page directive with a
  	 * 'contentType' attribute (or the 'contentType' attribute doesn't
  	 * have a CHARSET value), the page does not provide a page directive
  	 * with a 'pageEncoding' attribute, and there is no JSP configuration
  	 * element page-encoding whose URL pattern matches the page.
  	 */
  	private boolean isDefaultPageEncoding;
  
  	/*
  	 * Indicates whether an encoding has been explicitly specified in the
  	 * page's XML prolog (only used for pages in XML syntax).
  	 * This information is used to decide whether a translation error must
  	 * be reported for encoding conflicts.
  	 */
  	private boolean isEncodingSpecifiedInProlog;
  
  	/*
  	 * Constructor.
  	 */
  	Root(Mark start, Node parent, boolean isXmlSyntax) {
  	    super(start, parent);
  	    this.isXmlSyntax = isXmlSyntax;
  	    this.qName = JSP_ROOT_ACTION;
  	    this.localName = ROOT_ACTION;
  
  	    // Figure out and set the parent root
  	    Node r = parent;
  	    while ((r != null) && !(r instanceof Node.Root))
  		r = r.getParent();
  	    parentRoot = (Node.Root) r;
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public boolean isXmlSyntax() {
  	    return isXmlSyntax;
  	}
  
  	/*
  	 * Sets the encoding specified in the JSP config element whose URL
  	 * pattern matches the page containing this Root.
  	 */
  	public void setJspConfigPageEncoding(String enc) {
  	    jspConfigPageEnc = enc;
  	}
  
  	/*
  	 * Gets the encoding specified in the JSP config element whose URL
  	 * pattern matches the page containing this Root.
  	 */
  	public String getJspConfigPageEncoding() {
  	    return jspConfigPageEnc;
  	}
  
  	public void setPageEncoding(String enc) {
  	    pageEnc = enc;
  	}
  
  	public String getPageEncoding() {
  	    return pageEnc;
  	}
  
  	public void setIsDefaultPageEncoding(boolean isDefault) {
  	    isDefaultPageEncoding = isDefault;
  	}
  
  	public boolean isDefaultPageEncoding() {
  	    return isDefaultPageEncoding;
  	}
  	
  	public void setIsEncodingSpecifiedInProlog(boolean isSpecified) {
  	    isEncodingSpecifiedInProlog = isSpecified;
  	}
  
  	public boolean isEncodingSpecifiedInProlog() {
  	    return isEncodingSpecifiedInProlog;
  	}
  
          public void setHasBom(boolean hasBom) {
              this.hasBom = hasBom;
          }
  
          public boolean hasBom() {
              return hasBom;
          }
  
  	/**
  	 * @return The enclosing root to this Root. Usually represents the
  	 * page that includes this one.
  	 */
  	public Root getParentRoot() {
  	    return parentRoot;
  	}
      }
      
      /**
       * Represents the root of a Jsp document (XML syntax)
       */
      public static class JspRoot extends Node {
  
  	public JspRoot(String qName, Attributes attrs,
  		       Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
  		       Mark start, Node parent) {
  	    super(qName, ROOT_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
  		  start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a page directive
       */
      public static class PageDirective extends Node {
  
  	private Vector imports;
  
  	public PageDirective(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_PAGE_DIRECTIVE_ACTION, attrs, null, null, start, parent);
  	}
  
  	public PageDirective(String qName, Attributes attrs,
  			     Attributes nonTaglibXmlnsAttrs,
  			     Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, PAGE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	    imports = new Vector();
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	/**
  	 * Parses the comma-separated list of class or package names in the
  	 * given attribute value and adds each component to this
  	 * PageDirective's vector of imported classes and packages.
  	 * @param value A comma-separated string of imports.
  	 */
  	public void addImport(String value) {
  	    int start = 0;
  	    int index;
  	    while ((index = value.indexOf(',', start)) != -1) {
  		imports.add(value.substring(start, index).trim());
  		start = index + 1;
  	    }
  	    if (start == 0) {
  		// No comma found
  		imports.add(value.trim());
  	    } else {
  		imports.add(value.substring(start).trim());
  	    }
  	}
  
  	public List getImports() {
  	    return imports;
  	}
      }
  
      /**
       * Represents an include directive
       */
      public static class IncludeDirective extends Node {
  
  	public IncludeDirective(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_INCLUDE_DIRECTIVE_ACTION, attrs, null, null, start,
  		 parent);
  	}
  
  	public IncludeDirective(String qName, Attributes attrs,
  				Attributes nonTaglibXmlnsAttrs,
  				Attributes taglibAttrs, Mark start,
  				Node parent) {
  	    super(qName, INCLUDE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a custom taglib directive
       */
      public static class TaglibDirective extends Node {
  
  	public TaglibDirective(Attributes attrs, Mark start, Node parent) {
  	    super(JSP_TAGLIB_DIRECTIVE_ACTION, TAGLIB_DIRECTIVE_ACTION, attrs,
  		  start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a tag directive
       */
      public static class TagDirective extends Node {
          private Vector imports;
  
  	public TagDirective(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_TAG_DIRECTIVE_ACTION, attrs, null, null, start, parent);
  	}
  
  	public TagDirective(String qName, Attributes attrs,
  			    Attributes nonTaglibXmlnsAttrs,
  			    Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, TAG_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
              imports = new Vector();
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
          }
   
          /**
           * Parses the comma-separated list of class or package names in the
           * given attribute value and adds each component to this
           * PageDirective's vector of imported classes and packages.
           * @param value A comma-separated string of imports.
           */
          public void addImport(String value) {
              int start = 0;
              int index;
              while ((index = value.indexOf(',', start)) != -1) {
                  imports.add(value.substring(start, index).trim());
                  start = index + 1;
              }
              if (start == 0) {
                  // No comma found
                  imports.add(value.trim());
              } else {
                  imports.add(value.substring(start).trim());
              }
          }
   
          public List getImports() {
              return imports;
  	}
      }
  
      /**
       * Represents an attribute directive
       */
      public static class AttributeDirective extends Node {
  
  	public AttributeDirective(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_ATTRIBUTE_DIRECTIVE_ACTION, attrs, null, null, start,
  		 parent);
  	}
  
  	public AttributeDirective(String qName, Attributes attrs,
  				  Attributes nonTaglibXmlnsAttrs,
  				  Attributes taglibAttrs, Mark start,
  				  Node parent) {
  	    super(qName, ATTRIBUTE_DIRECTIVE_ACTION, attrs,
  		  nonTaglibXmlnsAttrs, taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a variable directive
       */
      public static class VariableDirective extends Node {
  
  	public VariableDirective(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_VARIABLE_DIRECTIVE_ACTION, attrs, null, null, start,
  		 parent);
  	}
  
  	public VariableDirective(String qName, Attributes attrs,
  				 Attributes nonTaglibXmlnsAttrs,
  				 Attributes taglibAttrs,
  				 Mark start, Node parent) {
  	    super(qName, VARIABLE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a <jsp:invoke> tag file action
       */
      public static class InvokeAction extends Node {
  
  	public InvokeAction(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_INVOKE_ACTION, attrs, null, null, start, parent);
  	}
  
  	public InvokeAction(String qName, Attributes attrs,
  			    Attributes nonTaglibXmlnsAttrs,
  			    Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, INVOKE_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a <jsp:doBody> tag file action
       */
      public static class DoBodyAction extends Node {
  
  	public DoBodyAction(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_DOBODY_ACTION, attrs, null, null, start, parent);
  	}
  
  	public DoBodyAction(String qName, Attributes attrs,
  			    Attributes nonTaglibXmlnsAttrs,
  			    Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, DOBODY_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a Jsp comment
       * Comments are kept for completeness.
       */
      public static class Comment extends Node {
  
  	public Comment(String text, Mark start, Node parent) {
  	    super(null, null, text, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents an expression, declaration, or scriptlet
       */
      public static abstract class ScriptingElement extends Node {
  
  	public ScriptingElement(String qName, String localName, String text,
  				Mark start, Node parent) {
  	    super(qName, localName, text, start, parent);
  	}
  
  	public ScriptingElement(String qName, String localName,
  				Attributes nonTaglibXmlnsAttrs,
  				Attributes taglibAttrs, Mark start,
  				Node parent) {
  	    super(qName, localName, null, nonTaglibXmlnsAttrs, taglibAttrs,
  		  start, parent);
  	}
  
  	/**
  	 * When this node was created from a JSP page in JSP syntax, its text
  	 * was stored as a String in the "text" field, whereas when this node
  	 * was created from a JSP document, its text was stored as one or more
  	 * TemplateText nodes in its body. This method handles either case.
  	 * @return The text string
  	 */
  	public String getText() {
  	    String ret = text;
  	    if ((ret == null) && (body != null)) {
  		StringBuffer buf = new StringBuffer();
  		for (int i=0; i<body.size(); i++) {
  		    buf.append(body.getNode(i).getText());
  		}
  		ret = buf.toString();
  	    }
  	    return ret;
  	}
  
          /**
           * For the same reason as above, the source line information in the
           * contained TemplateText node should be used.
           */
          public Mark getStart() {
              if (text == null && body != null && body.size() > 0) {
                  return body.getNode(0).getStart();
              } else {
                  return super.getStart();
              }
          }
      }
  
      /**
       * Represents a declaration
       */
      public static class Declaration extends ScriptingElement {
  
  	public Declaration(String text, Mark start, Node parent) {
  	    super(JSP_DECLARATION_ACTION, DECLARATION_ACTION, text, start,
  		  parent);
  	}
  
  	public Declaration(String qName, Attributes nonTaglibXmlnsAttrs,
  			   Attributes taglibAttrs, Mark start,
  			   Node parent) {
  	    super(qName, DECLARATION_ACTION, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents an expression.  Expressions in attributes are embedded
       * in the attribute string and not here.
       */
      public static class Expression extends ScriptingElement {
  
  	public Expression(String text, Mark start, Node parent) {
  	    super(JSP_EXPRESSION_ACTION, EXPRESSION_ACTION, text, start,
  		  parent);
  	}
  
  	public Expression(String qName, Attributes nonTaglibXmlnsAttrs,
  			  Attributes taglibAttrs, Mark start,
  			  Node parent) {
  	    super(qName, EXPRESSION_ACTION, nonTaglibXmlnsAttrs, taglibAttrs,
  		  start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a scriptlet
       */
      public static class Scriptlet extends ScriptingElement {
  
  	public Scriptlet(String text, Mark start, Node parent) {
  	    super(JSP_SCRIPTLET_ACTION, SCRIPTLET_ACTION, text, start, parent);
  	}
  
  	public Scriptlet(String qName, Attributes nonTaglibXmlnsAttrs,
  			 Attributes taglibAttrs, Mark start,
  			 Node parent) {
  	    super(qName, SCRIPTLET_ACTION, nonTaglibXmlnsAttrs, taglibAttrs,
  		  start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents an EL expression.  Expressions in attributes are embedded
       * in the attribute string and not here.
       */
      public static class ELExpression extends Node {
  
  	private ELNode.Nodes el;
  
          public ELExpression(String text, Mark start, Node parent) {
              super(null, null, text, start, parent);
          }
  
          public void accept(Visitor v) throws JasperException {
              v.visit(this);
          }
  
  	public void setEL(ELNode.Nodes el) {
  	    this.el = el;
  	}
  
  	public ELNode.Nodes getEL() {
  	    return el;
  	}
      }
  
      /**
       * Represents a param action
       */
      public static class ParamAction extends Node {
  
  	JspAttribute value;
  
  	public ParamAction(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_PARAM_ACTION, attrs, null, null, start, parent);
  	}
  
  	public ParamAction(String qName, Attributes attrs,
  			   Attributes nonTaglibXmlnsAttrs,
  			   Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, PARAM_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setValue(JspAttribute value) {
  	    this.value = value;
  	}
  
  	public JspAttribute getValue() {
  	    return value;
  	}
      }
  
      /**
       * Represents a params action
       */
      public static class ParamsAction extends Node {
  
  	public ParamsAction(Mark start, Node parent) {
  	    this(JSP_PARAMS_ACTION, null, null, start, parent);
  	}
  
  	public ParamsAction(String qName,
  			    Attributes nonTaglibXmlnsAttrs,
  			    Attributes taglibAttrs,
  			    Mark start, Node parent) {
  	    super(qName, PARAMS_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs,
  		  start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a fallback action
       */
      public static class FallBackAction extends Node {
  
  	public FallBackAction(Mark start, Node parent) {
  	    this(JSP_FALLBACK_ACTION, null, null, start, parent);
  	}
  
  	public FallBackAction(String qName,
  			      Attributes nonTaglibXmlnsAttrs,
  			      Attributes taglibAttrs, Mark start,
  			      Node parent) {
  	    super(qName, FALLBACK_ACTION, null, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents an include action
       */
      public static class IncludeAction extends Node {
  
  	private JspAttribute page;
  
  	public IncludeAction(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_INCLUDE_ACTION, attrs, null, null, start, parent);
  	}
  
  	public IncludeAction(String qName, Attributes attrs,
  			     Attributes nonTaglibXmlnsAttrs,
  			     Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, INCLUDE_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setPage(JspAttribute page) {
  	    this.page = page;
  	}
  
  	public JspAttribute getPage() {
  	    return page;
  	}
      }
  
      /**
       * Represents a forward action
       */
      public static class ForwardAction extends Node {
  
  	private JspAttribute page;
  
  	public ForwardAction(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_FORWARD_ACTION, attrs, null, null, start, parent);
  	}
  
  	public ForwardAction(String qName, Attributes attrs,
  			     Attributes nonTaglibXmlnsAttrs,
  			     Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, FORWARD_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setPage(JspAttribute page) {
  	    this.page = page;
  	}
  
  	public JspAttribute getPage() {
  	    return page;
  	}
      }
  
      /**
       * Represents a getProperty action
       */
      public static class GetProperty extends Node {
  
  	public GetProperty(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_GET_PROPERTY_ACTION, attrs, null, null, start, parent);
  	}
  
  	public GetProperty(String qName, Attributes attrs,
  			   Attributes nonTaglibXmlnsAttrs,
  			   Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, GET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start,  parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Represents a setProperty action
       */
      public static class SetProperty extends Node {
  
  	private JspAttribute value;
  
  	public SetProperty(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_SET_PROPERTY_ACTION, attrs, null, null, start, parent);
  	}
  
  	public SetProperty(String qName, Attributes attrs,
  			   Attributes nonTaglibXmlnsAttrs,
  			   Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, SET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setValue(JspAttribute value) {
  	    this.value = value;
  	}
  
  	public JspAttribute getValue() {
  	    return value;
  	}
      }
  
      /**
       * Represents a useBean action
       */
      public static class UseBean extends Node {
  
  	JspAttribute beanName;
  
  	public UseBean(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_USE_BEAN_ACTION, attrs, null, null, start, parent);
  	}
  
  	public UseBean(String qName, Attributes attrs,
  		       Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
  		       Mark start, Node parent) {
  	    super(qName, USE_BEAN_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setBeanName(JspAttribute beanName) {
  	    this.beanName = beanName;
  	}
  
  	public JspAttribute getBeanName() {
  	    return beanName;
  	}
      }
  
      /**
       * Represents a plugin action
       */
      public static class PlugIn extends Node {
  
          private JspAttribute width;
          private JspAttribute height;
          
  	public PlugIn(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_PLUGIN_ACTION, attrs, null, null, start, parent);
  	}
  
  	public PlugIn(String qName, Attributes attrs,
  		      Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs,
  		      Mark start, Node parent) {
  	    super(qName, PLUGIN_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setHeight(JspAttribute height) {
  	    this.height = height;
  	}
  
  	public void setWidth(JspAttribute width) {
  	    this.width = width;
  	}
  
  	public JspAttribute getHeight() {
  	    return height;
  	}
  
  	public JspAttribute getWidth() {
  	    return width;
  	}
      }
  
      /**
       * Represents an uninterpreted tag, from a Jsp document
       */
      public static class UninterpretedTag extends Node {
  
  	private JspAttribute[] jspAttrs;
  
  	public UninterpretedTag(String qName, String localName,
  				Attributes attrs,
  				Attributes nonTaglibXmlnsAttrs,
  				Attributes taglibAttrs,
  				Mark start, Node parent) {
  	    super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
  		  start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setJspAttributes(JspAttribute[] jspAttrs) {
  	    this.jspAttrs = jspAttrs;
  	}
  
  	public JspAttribute[] getJspAttributes() {
  	    return jspAttrs;
  	}
      }
      
      /**
       * Represents a <jsp:element>.
       */
      public static class JspElement extends Node {
  
  	private JspAttribute[] jspAttrs;
  	private JspAttribute nameAttr;
  
  	public JspElement(Attributes attrs, Mark start, Node parent) {
  	    this(JSP_ELEMENT_ACTION, attrs, null, null, start, parent);
  	}
  
  	public JspElement(String qName, Attributes attrs,
  			  Attributes nonTaglibXmlnsAttrs,
  			  Attributes taglibAttrs, Mark start, Node parent) {
  	    super(qName, ELEMENT_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
  	public void setJspAttributes(JspAttribute[] jspAttrs) {
  	    this.jspAttrs = jspAttrs;
  	}
  
  	public JspAttribute[] getJspAttributes() {
  	    return jspAttrs;
  	}
  
  	/*
  	 * Sets the XML-style 'name' attribute
  	 */
  	public void setNameAttribute(JspAttribute nameAttr) {
  	    this.nameAttr = nameAttr;
  	}
  
  	/*
  	 * Gets the XML-style 'name' attribute
  	 */
  	public JspAttribute getNameAttribute() {
  	    return this.nameAttr;
  	}
      }
  
      /**
       * Represents a <jsp:output>.
       */
      public static class JspOutput extends Node {
  
  	public JspOutput(String qName, Attributes attrs,
  			 Attributes nonTaglibXmlnsAttrs,
  			 Attributes taglibAttrs,
  			 Mark start, Node parent) {
  	    super(qName, OUTPUT_ACTION, attrs, nonTaglibXmlnsAttrs,
  		  taglibAttrs, start, parent);
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
      }
  
      /**
       * Collected information about child elements.  Used by nodes like
       * CustomTag, JspBody, and NamedAttribute.  The information is 
       * set in the Collector.
       */
      public static class ChildInfo {
  	private boolean scriptless;	// true if the tag and its body
  					// contain no scripting elements.
  	private boolean hasUseBean;
  	private boolean hasIncludeAction;
  	private boolean hasParamAction;
  	private boolean hasSetProperty;
  	private boolean hasScriptingVars;
  
  	public void setScriptless(boolean s) {
  	    scriptless = s;
  	}
  
  	public boolean isScriptless() {
  	    return scriptless;
  	}
  
  	public void setHasUseBean(boolean u) {
  	    hasUseBean = u;
  	}
  
  	public boolean hasUseBean() {
  	    return hasUseBean;
  	}
  
  	public void setHasIncludeAction(boolean i) {
  	    hasIncludeAction = i;
  	}
  
  	public boolean hasIncludeAction() {
  	    return hasIncludeAction;
  	}
  
  	public void setHasParamAction(boolean i) {
  	    hasParamAction = i;
  	}
  
  	public boolean hasParamAction() {
  	    return hasParamAction;
  	}
  
  	public void setHasSetProperty(boolean s) {
  	    hasSetProperty = s;
  	}
  
  	public boolean hasSetProperty() {
  	    return hasSetProperty;
  	}
          
  	public void setHasScriptingVars(boolean s) {
  	    hasScriptingVars = s;
  	}
  
  	public boolean hasScriptingVars() {
  	    return hasScriptingVars;
  	}
      }
  
      /**
       * Represents a custom tag
       */
      public static class CustomTag extends Node {
  
          private double jspVersion;
  	private String uri;
  	private String prefix;
  	private JspAttribute[] jspAttrs;
  	private TagData tagData;
  	private String tagHandlerPoolName;
  	private TagInfo tagInfo;
  	private TagFileInfo tagFileInfo;
  	private Class tagHandlerClass;
  	private VariableInfo[] varInfos;
  	private int customNestingLevel;
          private ChildInfo childInfo;
  	private boolean implementsIterationTag;
  	private boolean implementsBodyTag;
  	private boolean implementsTryCatchFinally;
  	private boolean implementsSimpleTag;
  	private boolean implementsDynamicAttributes;
  	private Vector atBeginScriptingVars;
  	private Vector atEndScriptingVars;
  	private Vector nestedScriptingVars;
  	private Node.CustomTag customTagParent;
  	private Integer numCount;
  	private boolean useTagPlugin;
  	private TagPluginContext tagPluginContext;
          private int jspId;
          private HashMap<String, String> tempVars;
                  // The temporary name for saving and restoring a scripting var
  
  	/**
  	 * The following two fields are used for holding the Java
  	 * scriptlets that the tag plugins may generate.  Meaningful
  	 * only if useTagPlugin is true;
  	 * Could move them into TagPluginContextImpl, but we'll need
  	 * to cast tagPluginContext to TagPluginContextImpl all the time...
  	 */
  	private Nodes atSTag;
  	private Nodes atETag;
  
  	/*
  	 * Constructor for custom action implemented by tag handler.
  	 */
  	public CustomTag(String jspVersion,
                           String qName, String prefix, String localName,
  			 String uri, Attributes attrs, Mark start, Node parent,
  			 TagInfo tagInfo, Class tagHandlerClass) {
  	    this(jspVersion, qName, prefix, localName, uri, attrs, null, null,
                   start, parent, tagInfo, tagHandlerClass);
  	}
  
  	/*
  	 * Constructor for custom action implemented by tag handler.
  	 */
  	public CustomTag(String jspVersion, String qName, String prefix,
                           String localName,
  			 String uri, Attributes attrs,
  			 Attributes nonTaglibXmlnsAttrs,
  			 Attributes taglibAttrs,
  			 Mark start, Node parent, TagInfo tagInfo,
  			 Class tagHandlerClass) {
              super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
                    start, parent);
  
              this.jspVersion = Double.valueOf(jspVersion).doubleValue();
  	    this.uri = uri;
  	    this.prefix = prefix;
  	    this.tagInfo = tagInfo;
  	    this.tagHandlerClass = tagHandlerClass;
  	    this.customNestingLevel = makeCustomNestingLevel();
              this.childInfo = new ChildInfo();
  
  	    this.implementsIterationTag = 
  		IterationTag.class.isAssignableFrom(tagHandlerClass);
  	    this.implementsBodyTag =
  		BodyTag.class.isAssignableFrom(tagHandlerClass);
  	    this.implementsTryCatchFinally = 
  		TryCatchFinally.class.isAssignableFrom(tagHandlerClass);
  	    this.implementsSimpleTag = 
  		SimpleTag.class.isAssignableFrom(tagHandlerClass);
  	    this.implementsDynamicAttributes = 
  		DynamicAttributes.class.isAssignableFrom(tagHandlerClass);
  	}
  
  	/*
  	 * Constructor for custom action implemented by tag file.
  	 */
  	public CustomTag(String jspVersion,
                           String qName, String prefix, String localName,
  			 String uri, Attributes attrs, Mark start, Node parent,
  			 TagFileInfo tagFileInfo) {
  	    this(jspVersion, qName, prefix, localName, uri, attrs, null, null,
                   start, parent, tagFileInfo);
  	}
  
  	/*
  	 * Constructor for custom action implemented by tag file.
  	 */
  	public CustomTag(String jspVersion,
                           String qName, String prefix, String localName,
  			 String uri, Attributes attrs,
  			 Attributes nonTaglibXmlnsAttrs,
  			 Attributes taglibAttrs,
  			 Mark start, Node parent, TagFileInfo tagFileInfo) {
  
              super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs,
                    start, parent);
  
              this.jspVersion = Double.valueOf(jspVersion).doubleValue();
  	    this.uri = uri;
  	    this.prefix = prefix;
  	    this.tagFileInfo = tagFileInfo;
  	    this.tagInfo = tagFileInfo.getTagInfo();
  	    this.customNestingLevel = makeCustomNestingLevel();
              this.childInfo = new ChildInfo();
  
  	    this.implementsIterationTag = false;
  	    this.implementsBodyTag = false;
  	    this.implementsTryCatchFinally = false;
  	    this.implementsSimpleTag = true;
  	    this.implementsDynamicAttributes = tagInfo.hasDynamicAttributes();
  	}
  
  	public void accept(Visitor v) throws JasperException {
  	    v.visit(this);
  	}
  
          /**
           * @return The jspVersion of the tag
           */
          public double getJspVersion() {
              return jspVersion;
          }
  
  	/**
  	 * @return The URI namespace that this custom action belongs to
  	 */
  	public String getURI() {
  	    return this.uri;
  	}
  
  	/**
  	 * @return The tag prefix
  	 */
  	public String getPrefix() {
  	    return prefix;
  	}
  
  	public void setJspAttributes(JspAttribute[] jspAttrs) {
  	    this.jspAttrs = jspAttrs;
  	}
  
  	public JspAttribute[] getJspAttributes() {
  	    return jspAttrs;
  	}
          
          public ChildInfo getChildInfo() {
              return childInfo;
          }
  	
  	public void setTagData(TagData tagData) {
  	    this.tagData = tagData;
  	    this.varInfos = tagInfo.getVariableInfo(tagData);
  	    if (this.varInfos == null) {
  		this.varInfos = ZERO_VARIABLE_INFO;
  	    }
  	}
  
  	public TagData getTagData() {
  	    return tagData;
  	}
  
  	public void setTagHandlerPoolName(String s) {
  	    tagHandlerPoolName = s;
  	}
  
  	public String getTagHandlerPoolName() {
  	    return tagHandlerPoolName;
  	}
  
  	public TagInfo getTagInfo() {
  	    return tagInfo;
  	}
  
  	public TagFileInfo getTagFileInfo() {
  	    return tagFileInfo;
  	}
  
  	/*
  	 * @return true if this custom action is supported by a tag file,
  	 * false otherwise
  	 */
  	public boolean isTagFile() {
  	    return tagFileInfo != null;
  	}
  
  	public Class getTagHandlerClass() {
  	    return tagHandlerClass;
  	}
  
  	public void setTagHandlerClass(Class hc) {
  	    tagHandlerClass = hc;
  	}
  
  	public boolean implementsIterationTag() {
  	    return implementsIterationTag;
  	}
  
  	public boolean implementsBodyTag() {
  	    return implementsBodyTag;
  	}
  
  	public boolean implementsTryCatchFinally() {
  	    return implementsTryCatchFinally;
  	}
  
  	public boolean implementsSimpleTag() {
  	    return implementsSimpleTag;
  	}
  
  	public boolean implementsDynamicAttributes() {
  	    return implementsDynamicAttributes;
  	}
  
  	public TagVariableInfo[] getTagVariableInfos() {
  	    return tagInfo.getTagVariableInfos();
   	}
   
  	public VariableInfo[] getVariableInfos() {
  	    return varInfos;
  	}
  
  	public void setCustomTagParent(Node.CustomTag n) {
  	    this.customTagParent = n;
  	}
  
  	public Node.CustomTag getCustomTagParent() {
  	    return this.customTagParent;
  	}
  
  	public void setNumCount(Integer count) {
  	    this.numCount = count;
  	}
  
  	public Integer getNumCount() {
  	    return this.numCount;
  	}
  
  	public void setScriptingVars(Vector vec, int scope) {
  	    switch (scope) {
  	    case VariableInfo.AT_BEGIN:
  		this.atBeginScriptingVars = vec;
  		break;
  	    case VariableInfo.AT_END:
  		this.atEndScriptingVars = vec;
  		break;
  	    case VariableInfo.NESTED:
  		this.nestedScriptingVars = vec;
  		break;
  	    }
  	}
  
  	/*
  	 * Gets the scripting variables for the given scope that need to be
  	 * declared.
  	 */
  	public Vector getScriptingVars(int scope) {
  	    Vector vec = null;
  
  	    switch (scope) {
  	    case VariableInfo.AT_BEGIN:
  		vec = this.atBeginScriptingVars;
  		break;
  	    case VariableInfo.AT_END:
  		vec = this.atEndScriptingVars;
  		break;
  	    case VariableInfo.NESTED:
  		vec = this.nestedScriptingVars;
  		break;
  	    }
  
  	    return vec;
  	}
  
  	/*
  	 * Gets this custom tag's custom nesting level, which is given as
  	 * the number of times this custom tag is nested inside itself.
  	 */
  	public int getCustomNestingLevel() {
  	    return customNestingLevel;
  	}
  
          /**
           * Checks to see if the attribute of the given name is of type
  	 * JspFragment.
           */
          public boolean checkIfAttributeIsJspFragment( String name ) {
              boolean result = false;
  
  	    TagAttributeInfo[] attributes = tagInfo.getAttributes();
  	    for (int i = 0; i < attributes.length; i++) {
  		if (attributes[i].getName().equals(name) &&
  		            attributes[i].isFragment()) {
  		    result = true;
  		    break;
  		}
  	    }
              
              return result;
          }
  
  	public void setUseTagPlugin(boolean use) {
  	    useTagPlugin = use;
  	}
  
  	public boolean useTagPlugin() {
  	    return useTagPlugin;
  	}
  
  	public void setTagPluginContext(TagPluginContext tagPluginContext) {
  	    this.tagPluginContext = tagPluginContext;
  	}
  
  	public TagPluginContext getTagPluginContext() {
  	    return tagPluginContext;
  	}
  
  	public void setAtSTag(Nodes sTag) {
  	    atSTag = sTag;
  	}
  
  	public Nodes getAtSTag() {
  	    return atSTag;
  	}
          
  	public void setAtETag(Nodes eTag) {
  	    atETag = eTag;
  	}
  
  	public Nodes getAtETag() {
  	    return atETag;
  	}
  
          public void setJspId(int jspId) {
              this.jspId = jspId;
          }
  
          public int getJspId() {
              return jspId;
          }
          
  	/*
  	 * Computes this custom tag's custom nesting level, which corresponds
  	 * to the number of times this custom tag is nested inside itself.
  	 *
  	 * Example:
  	 * 
  	 *  <g:h>
  	 *    <a:b> -- nesting level 0
  	 *      <c:d>
  	 *        <e:f>
  	 *          <a:b> -- nesting level 1
  	 *            <a:b> -- nesting level 2
  	 *            </a:b>
  	 *          </a:b>
  	 *          <a:b> -- nesting level 1
  	 *          </a:b>
  	 *        </e:f>
  	 *      </c:d>
  	 *    </a:b>
  	 *  </g:h>
  	 * 
  	 * @return Custom tag's nesting level
  	 */
  	private int makeCustomNestingLevel() {
  	    int n = 0;
  	    Node p = parent;
  	    while (p != null) {
  		if ((p instanceof Node.CustomTag)
  		        && qName.equals(((Node.CustomTag) p).qName)) {
  		    n++;
  		}
  		p = p.parent;
  	    }
  	    return n;
  	}
  
  	/**
  	 * Returns true if this custom action has an empty body, and false
  	 * otherwise.
  	 *
  	 * A custom action is considered to have an empty body if the 
  	 * following holds true:
  	 * - getBody() returns null, or
  	 * - all immediate children are jsp:attribute actions, or
  	 * - the action's jsp:body is empty.
  	 */
  	 public boolean hasEmptyBody() {
  	     boolean hasEmptyBody = true;
  	     Nodes nodes = getBody();
  	     if (nodes != null) {
  		 int numChildNodes = nodes.size();
  		 for (int i=0; i<numChildNodes; i++) {
  		     Node n = nodes.getNode(i);
  		     if (!(n instanceof NamedAttribute)) {
  			 if (n instanceof JspBody) {
  			     hasEmptyBody = (n.getBody() == null);
  			 } else {
  			     hasEmptyBody = false;
  			 }
  			 break;
  		     }
  		 }
  	     }
  
  	     return hasEmptyBody;
  	 }
  
           public String getTempScriptingVar(String scriptingVar) {
               if (tempVars == null) {
                   return null;
               }
               return tempVars.get(scriptingVar);
           }
  
           public void setTempScriptingVar(String scriptingVar,
                                           String tempScriptingVar) {
               if (tempVars == null) {
  