You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/07/31 20:12:51 UTC

svn commit: r799681 [7/24] - in /struts/sandbox/trunk/struts2-jsp-plugin: ./ src/main/java/org/apache/struts/ src/main/java/org/apache/struts2/ src/main/java/org/apache/struts2/compiler/ src/main/java/org/apache/struts2/jasper/ src/main/java/org/apache...

Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Mark.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Mark.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Mark.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Mark.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.jasper.compiler;
+
+import java.util.Stack;
+import java.net.URL;
+import java.net.MalformedURLException;
+import org.apache.struts2.jasper.JspCompilationContext;
+
+/**
+ * Mark represents a point in the JSP input. 
+ *
+ * @author Anil K. Vijendran
+ */
+final class Mark {
+
+    // position within current stream
+    int cursor, line, col;
+
+    // directory of file for current stream
+    String baseDir;
+
+    // current stream
+    char[] stream = null;
+
+    // fileid of current stream
+    private int fileId;
+
+    // name of the current file
+    private String fileName;
+
+    /*
+     * stack of stream and stream state of streams that have included
+     * current stream
+     */
+    private Stack includeStack = null;
+
+    // encoding of current file
+    private String encoding = null;
+
+    // reader that owns this mark (so we can look up fileid's)
+    private JspReader reader;
+
+    private JspCompilationContext ctxt;
+
+    /**
+     * Constructor
+     *
+     * @param reader JspReader this mark belongs to
+     * @param inStream current stream for this mark
+     * @param fileId id of requested jsp file
+     * @param name JSP file name
+     * @param inBaseDir base directory of requested jsp file
+     * @param inEncoding encoding of current file
+     */
+    Mark(JspReader reader, char[] inStream, int fileId, String name,
+         String inBaseDir, String inEncoding) {
+
+        this.reader = reader;
+        this.ctxt = reader.getJspCompilationContext();
+        this.stream = inStream;
+        this.cursor = 0;
+        this.line = 1;
+        this.col = 1;
+        this.fileId = fileId;
+        this.fileName = name;
+        this.baseDir = inBaseDir;
+        this.encoding = inEncoding;
+        this.includeStack = new Stack();
+    }
+
+
+    /**
+     * Constructor
+     */
+    Mark(Mark other) {
+
+        this.reader = other.reader;
+        this.ctxt = other.reader.getJspCompilationContext();
+        this.stream = other.stream;
+        this.fileId = other.fileId;
+        this.fileName = other.fileName;
+        this.cursor = other.cursor;
+        this.line = other.line;
+        this.col = other.col;
+        this.baseDir = other.baseDir;
+        this.encoding = other.encoding;
+
+        // clone includeStack without cloning contents
+        includeStack = new Stack();
+        for ( int i=0; i < other.includeStack.size(); i++ ) {
+            includeStack.addElement( other.includeStack.elementAt(i) );
+        }
+    }
+
+
+    /**
+     * Constructor
+     */    
+    Mark(JspCompilationContext ctxt, String filename, int line, int col) {
+
+        this.reader = null;
+        this.ctxt = ctxt;
+        this.stream = null;
+        this.cursor = 0;
+        this.line = line;
+        this.col = col;
+        this.fileId = -1;
+        this.fileName = filename;
+        this.baseDir = "le-basedir";
+        this.encoding = "le-endocing";
+        this.includeStack = null;
+    }
+
+
+    /**
+     * Sets this mark's state to a new stream.
+     * It will store the current stream in it's includeStack.
+     *
+     * @param inStream new stream for mark
+     * @param inFileId id of new file from which stream comes from
+     * @param inBaseDir directory of file
+     * @param inEncoding encoding of new file
+     */
+    public void pushStream(char[] inStream, int inFileId, String name,
+                           String inBaseDir, String inEncoding) 
+    {
+        // store current state in stack
+        includeStack.push(new IncludeState(cursor, line, col, fileId,
+                                           fileName, baseDir, 
+					   encoding, stream) );
+
+        // set new variables
+        cursor = 0;
+        line = 1;
+        col = 1;
+        fileId = inFileId;
+        fileName = name;
+        baseDir = inBaseDir;
+        encoding = inEncoding;
+        stream = inStream;
+    }
+
+
+    /**
+     * Restores this mark's state to a previously stored stream.
+     * @return The previous Mark instance when the stream was pushed, or null
+     * if there is no previous stream
+     */
+    public Mark popStream() {
+        // make sure we have something to pop
+        if ( includeStack.size() <= 0 ) {
+            return null;
+        }
+
+        // get previous state in stack
+        IncludeState state = (IncludeState) includeStack.pop( );
+
+        // set new variables
+        cursor = state.cursor;
+        line = state.line;
+        col = state.col;
+        fileId = state.fileId;
+        fileName = state.fileName;
+        baseDir = state.baseDir;
+        stream = state.stream;
+        return this;
+    }
+
+
+    // -------------------- Locator interface --------------------
+
+    public int getLineNumber() {
+        return line;
+    }
+
+    public int getColumnNumber() {
+        return col;
+    }
+
+    public String getSystemId() {
+        return getFile();
+    }
+
+    public String getPublicId() {
+        return null;
+    }
+
+    public String toString() {
+	return getFile()+"("+line+","+col+")";
+    }
+
+    public String getFile() {
+        return this.fileName;
+    }
+
+    /**
+     * Gets the URL of the resource with which this Mark is associated
+     *
+     * @return URL of the resource with which this Mark is associated
+     *
+     * @exception MalformedURLException if the resource pathname is incorrect
+     */
+    public URL getURL() throws MalformedURLException {
+        return ctxt.getResource(getFile());
+    }
+
+    public String toShortString() {
+        return "("+line+","+col+")";
+    }
+
+    public boolean equals(Object other) {
+	if (other instanceof Mark) {
+	    Mark m = (Mark) other;
+	    return this.reader == m.reader && this.fileId == m.fileId 
+		&& this.cursor == m.cursor && this.line == m.line 
+		&& this.col == m.col;
+	} 
+	return false;
+    }
+
+    /**
+     * @return true if this Mark is greather than the <code>other</code>
+     * Mark, false otherwise.
+     */
+    public boolean isGreater(Mark other) {
+
+        boolean greater = false;
+
+        if (this.line > other.line) {
+            greater = true;
+        } else if (this.line == other.line && this.col > other.col) {
+            greater = true;
+        }
+
+        return greater;
+    }
+
+    /**
+     * Keep track of parser before parsing an included file.
+     * This class keeps track of the parser before we switch to parsing an
+     * included file. In other words, it's the parser's continuation to be
+     * reinstalled after the included file parsing is done.
+     */
+    class IncludeState {
+        int cursor, line, col;
+        int fileId;
+        String fileName;
+        String baseDir;
+        String encoding;
+        char[] stream = null;
+
+        IncludeState(int inCursor, int inLine, int inCol, int inFileId, 
+                     String name, String inBaseDir, String inEncoding,
+                     char[] inStream) {
+            cursor = inCursor;
+            line = inLine;
+            col = inCol;
+            fileId = inFileId;
+            fileName = name;
+            baseDir = inBaseDir;
+            encoding = inEncoding;
+            stream = inStream;
+        }
+    }
+
+}
+

Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Node.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Node.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Node.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Node.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,2372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import java.util.ArrayList;
+
+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.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.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;
+
+        // 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;
+        }
+
+        /**
+         * @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 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;
+
+        /**
+         * 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 qName, String prefix, String localName,
+                         String uri, Attributes attrs, Mark start, Node parent,
+                         TagInfo tagInfo, Class tagHandlerClass) {
+            this(qName, prefix, localName, uri, attrs, null, null, start,
+                 parent, tagInfo, tagHandlerClass);
+        }
+
+        /*
+         * Constructor for custom action implemented by tag handler.
+         */
+        public CustomTag(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.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 qName, String prefix, String localName,
+                         String uri, Attributes attrs, Mark start, Node parent,
+                         TagFileInfo tagFileInfo) {
+            this(qName, prefix, localName, uri, attrs, null, null, start,
+                 parent, tagFileInfo);
+        }
+
+        /*
+         * Constructor for custom action implemented by tag file.
+         */
+        public CustomTag(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.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 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;
+        }
+        
+        /*
+         * 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;
+         }
+    }
+
+    /**
+     * Used as a placeholder for the evaluation code of a custom action
+     * attribute (used by the tag plugin machinery only).
+     */
+    public static class AttributeGenerator extends Node {
+        String name;        // name of the attribute
+        CustomTag tag;        // The tag this attribute belongs to
+
+        public AttributeGenerator(Mark start, String name, CustomTag tag) {
+            super(start, null);
+            this.name = name;
+            this.tag = tag;
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public CustomTag getTag() {
+            return tag;
+        }
+    }
+
+    /**
+     * Represents the body of a &lt;jsp:text&gt; element
+     */
+    public static class JspText extends Node {
+
+        public JspText(String qName, Attributes nonTaglibXmlnsAttrs,
+                       Attributes taglibAttrs, Mark start, Node parent) {
+            super(qName, TEXT_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs,
+                  start, parent);
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+    }
+
+    /**
+     * Represents a Named Attribute (&lt;jsp:attribute&gt;)
+     */
+    public static class NamedAttribute extends Node {
+
+        // A unique temporary variable name suitable for code generation
+        private String temporaryVariableName;
+
+        // True if this node is to be trimmed, or false otherwise
+        private boolean trim = true;
+        
+        private ChildInfo childInfo;
+        private String name;
+        private String localName;
+        private String prefix;
+
+        public NamedAttribute(Attributes attrs, Mark start, Node parent) {
+            this(JSP_ATTRIBUTE_ACTION, attrs, null, null, start, parent);
+        }
+
+        public NamedAttribute(String qName, Attributes attrs,
+                              Attributes nonTaglibXmlnsAttrs,
+                              Attributes taglibAttrs,
+                              Mark start, Node parent) {
+
+            super(qName, ATTRIBUTE_ACTION, attrs, nonTaglibXmlnsAttrs,
+                  taglibAttrs, start, parent);
+            if( "false".equals( this.getAttributeValue( "trim" ) ) ) {
+                // (if null or true, leave default of true)
+                trim = false;
+            }
+            childInfo = new ChildInfo();
+            name = this.getAttributeValue("name");
+            if (name != null) {
+                // Mandatary attribute "name" will be checked in Validator
+                localName = name;
+                int index = name.indexOf(':');
+                if (index != -1) {
+                    prefix = name.substring(0, index);
+                    localName = name.substring(index+1);
+                }
+            }
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+
+        public String getName() {
+            return this.name;
+        }
+
+        public String getLocalName() {
+            return this.localName;
+        }
+
+        public String getPrefix() {
+            return this.prefix;
+        }
+        
+        public ChildInfo getChildInfo() {
+            return this.childInfo;
+        }
+
+        public boolean isTrim() {
+            return trim;
+        }
+
+        /**
+         * @return A unique temporary variable name to store the result in.
+         *      (this probably could go elsewhere, but it's convenient here)
+         */
+        public String getTemporaryVariableName() {
+            if (temporaryVariableName == null) {
+                temporaryVariableName = JspUtil.nextTemporaryVariableName();
+            }
+            return temporaryVariableName;
+        }
+
+        /*
+         * Get the attribute value from this named attribute (<jsp:attribute>).
+         * Since this method is only for attributes that are not rtexpr,
+         * we can assume the body of the jsp:attribute is a template text.
+         */
+        public String getText() {
+
+            class AttributeVisitor extends Visitor {
+                String attrValue = null;
+                public void visit(TemplateText txt) {
+                    attrValue = new String(txt.getText());
+                }
+                
+                public String getAttrValue() {
+                    return attrValue;
+                }
+            }
+
+            // According to JSP 2.0, if the body of the <jsp:attribute>
+            // action is empty, it is equivalent of specifying "" as the value
+            // of the attribute.
+            String text = "";
+            if (getBody() != null) {
+                AttributeVisitor attributeVisitor = new AttributeVisitor();
+                try {
+                    getBody().visit(attributeVisitor);
+                } catch (JasperException e) {
+                }
+                text = attributeVisitor.getAttrValue();
+            }
+            
+            return text;
+        }
+    }
+
+    /**
+     * Represents a JspBody node (&lt;jsp:body&gt;)
+     */
+    public static class JspBody extends Node {
+
+        private ChildInfo childInfo;
+
+        public JspBody(Mark start, Node parent) {
+            this(JSP_BODY_ACTION, null, null, start, parent);
+        }
+
+        public JspBody(String qName, Attributes nonTaglibXmlnsAttrs,
+                       Attributes taglibAttrs, Mark start, Node parent) {
+            super(qName, BODY_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs,
+                  start, parent);
+            this.childInfo = new ChildInfo();
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+
+        public ChildInfo getChildInfo() {
+            return childInfo;
+        }
+    }
+
+    /**
+     * Represents a template text string
+     */
+    public static class TemplateText extends Node {
+
+        private ArrayList extraSmap = null;
+
+        public TemplateText(String text, Mark start, Node parent) {
+            super(null, null, text, start, parent);
+        }
+
+        public void accept(Visitor v) throws JasperException {
+            v.visit(this);
+        }
+
+        /**
+         * Trim all whitespace from the left of the template text
+         */
+        public void ltrim() {
+            int index = 0;
+            while ((index < text.length()) && (text.charAt(index) <= ' ')) {
+                index++;
+            }
+            text = text.substring(index);
+        }
+
+        public void setText(String text) {
+            this.text = text;
+        }
+
+        /**
+         * Trim all whitespace from the right of the template text
+         */
+        public void rtrim() {
+            int index = text.length();
+            while( (index > 0) && (text.charAt(index-1) <= ' ') ) {
+                index--;
+            }
+            text = text.substring(0, index);
+        }
+
+        /**
+         * Returns true if this template text contains whitespace only.
+         */
+        public boolean isAllSpace() {
+            boolean isAllSpace = true;
+            for (int i=0; i<text.length(); i++) {
+                if (!Character.isWhitespace(text.charAt(i))) {
+                    isAllSpace = false;
+                    break;
+                }
+            }
+            return isAllSpace;
+        }
+
+        /**
+         * Add a source to Java line mapping
+         * @param srcLine The postion of the source line, relative to the line
+         *        at the start of this node.  The corresponding java line is
+         *        assumed to be consecutive, i.e. one more than the last.
+         */
+        public void addSmap(int srcLine) {
+            if (extraSmap == null) {
+                extraSmap = new ArrayList();
+            }
+            extraSmap.add(new Integer(srcLine));
+        }
+
+        public ArrayList getExtraSmap() {
+            return extraSmap;
+        }
+    }
+
+    /*********************************************************************
+     * Auxillary classes used in Node
+     */
+
+    /**
+     * Represents attributes that can be request time expressions.
+     *
+     * Can either be a plain attribute, an attribute that represents a
+     * request time expression value, or a named attribute (specified using
+     * the jsp:attribute standard action).
+     */
+
+    public static class JspAttribute {
+
+        private String qName;
+        private String uri;
+        private String localName;
+        private String value;
+        private boolean expression;
+        private boolean dynamic;
+        private ELNode.Nodes el;
+
+        // If true, this JspAttribute represents a <jsp:attribute>
+        private boolean namedAttribute;
+        // The node in the parse tree for the NamedAttribute
+        private NamedAttribute namedAttributeNode;
+
+        JspAttribute(String qName, String uri, String localName, String value,
+                     boolean expr, ELNode.Nodes el, boolean dyn ) {
+            this.qName = qName;
+            this.uri = uri;
+            this.localName = localName;
+            this.value = value;
+            this.namedAttributeNode = null;
+            this.expression = expr;
+            this.el = el;
+            this.dynamic = dyn;
+            this.namedAttribute = false;
+        }
+
+        /**
+         * Use this constructor if the JspAttribute represents a
+         * named attribute.  In this case, we have to store the nodes of
+         * the body of the attribute.
+         */
+        JspAttribute(NamedAttribute na, boolean dyn) {
+            this.qName = na.getName();
+            this.localName = na.getLocalName();
+            this.value = null;
+            this.namedAttributeNode = na;
+            this.expression = false;
+            this.el = null;
+            this.dynamic = dyn;
+            this.namedAttribute = true;
+        }
+
+        /**
+          * @return The name of the attribute
+         */
+        public String getName() {
+            return qName;
+        }
+
+        /**
+          * @return The local name of the attribute
+         */
+        public String getLocalName() {
+            return localName;
+        }
+
+        /**
+          * @return The namespace of the attribute, or null if in the default
+         * namespace
+         */
+        public String getURI() {
+            return uri;
+        }
+
+        /**
+         * Only makes sense if namedAttribute is false.
+         *
+         * @return the value for the attribute, or the expression string
+         *         (stripped of "<%=", "%>", "%=", or "%"
+         *          but containing "${" and "}" for EL expressions)
+         */
+        public String getValue() {
+            return value;
+        }
+
+        /**
+         * Only makes sense if namedAttribute is true.
+         *
+         * @return the nodes that evaluate to the body of this attribute.
+         */
+        public NamedAttribute getNamedAttributeNode() {
+            return namedAttributeNode;
+        }
+
+        /**
+         * @return true if the value represents a traditional rtexprvalue
+         */
+        public boolean isExpression() {
+            return expression;
+        }
+
+        /**
+         * @return true if the value represents a NamedAttribute value.
+         */
+        public boolean isNamedAttribute() {
+            return namedAttribute;
+        }
+
+        /**
+         * @return true if the value represents an expression that should
+         * be fed to the expression interpreter
+         * @return false for string literals or rtexprvalues that should
+         * not be interpreted or reevaluated
+         */
+        public boolean isELInterpreterInput() {
+            return el != null;
+        }
+
+        /**
+         * @return true if the value is a string literal known at translation
+         * time.
+         */
+        public boolean isLiteral() {
+            return !expression && (el != null) && !namedAttribute;
+        }
+
+        /**
+         * XXX
+         */
+        public boolean isDynamic() {
+            return dynamic;
+        }
+
+        public ELNode.Nodes getEL() {
+            return el;
+        }
+    }
+
+    /**
+     * An ordered list of Node, used to represent the body of an element, or
+     * a jsp page of jsp document.
+     */
+    public static class Nodes {
+
+        private List list;
+        private Node.Root root;                // null if this is not a page
+        private boolean generatedInBuffer;
+
+        public Nodes() {
+            list = new Vector();
+        }
+
+        public Nodes(Node.Root root) {
+            this.root = root;
+            list = new Vector();
+            list.add(root);
+        }
+
+        /**
+         * Appends a node to the list
+         * @param n The node to add
+         */
+        public void add(Node n) {
+            list.add(n);
+            root = null;
+        }
+
+        /**
+         * Removes the given node from the list.
+         * @param n The node to be removed
+         */
+        public void remove(Node n) {
+            list.remove(n);
+        }
+
+        /**
+         * Visit the nodes in the list with the supplied visitor
+         * @param v The visitor used
+         */
+        public void visit(Visitor v) throws JasperException {
+            Iterator iter = list.iterator();
+            while (iter.hasNext()) {
+                Node n = (Node) iter.next();
+                n.accept(v);
+            }
+        }
+
+        public int size() {
+            return list.size();
+        }
+
+        public Node getNode(int index) {
+            Node n = null;
+            try {
+                n = (Node) list.get(index);
+            } catch (ArrayIndexOutOfBoundsException e) {
+            }
+            return n;
+        }
+        
+        public Node.Root getRoot() {
+            return root;
+        }
+
+        public boolean isGeneratedInBuffer() {
+            return generatedInBuffer;
+        }
+
+        public void setGeneratedInBuffer(boolean g) {
+            generatedInBuffer = g;
+        }
+    }
+
+    /**
+     * A visitor class for visiting the node.  This class also provides the
+     * default action (i.e. nop) for each of the child class of the Node.
+     * An actual visitor should extend this class and supply the visit
+     * method for the nodes that it cares.
+     */
+    public static class Visitor {
+
+        /**
+         * This method provides a place to put actions that are common to
+         * all nodes. Override this in the child visitor class if need to.
+         */
+        protected void doVisit(Node n) throws JasperException {
+        }
+
+        /**
+         * Visit the body of a node, using the current visitor
+         */
+        protected void visitBody(Node n) throws JasperException {
+            if (n.getBody() != null) {
+                n.getBody().visit(this);
+            }
+        }
+
+        public void visit(Root n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(JspRoot n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(PageDirective n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(TagDirective n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(IncludeDirective n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(TaglibDirective n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(AttributeDirective n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(VariableDirective n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(Comment n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(Declaration n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(Expression n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(Scriptlet n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(ELExpression n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(IncludeAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(ForwardAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(GetProperty n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(SetProperty n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(ParamAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(ParamsAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(FallBackAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(UseBean n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(PlugIn n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(CustomTag n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(UninterpretedTag n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(JspElement n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(JspText n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(NamedAttribute n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(JspBody n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(InvokeAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(DoBodyAction n) throws JasperException {
+            doVisit(n);
+            visitBody(n);
+        }
+
+        public void visit(TemplateText n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(JspOutput n) throws JasperException {
+            doVisit(n);
+        }
+
+        public void visit(AttributeGenerator n) throws JasperException {
+            doVisit(n);
+        }
+    }
+}