sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r598331 [7/22] - in /incubator/sling/trunk/scripting/jsp: ./ src/main/java/org/apache/jasper/ src/main/java/org/apache/jasper/compiler/ src/main/java/org/apache/jasper/compiler/tagplugin/ src/main/java/org/apache/jasper/runtime/ src/main/ja...
Date Mon, 26 Nov 2007 16:15:53 GMT
Added: incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Node.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Node.java?rev=598331&view=auto
==============================================================================
--- incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Node.java (added)
+++ incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Node.java Mon Nov 26 08:15:43 2007
@@ -0,0 +1,2369 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.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.jasper.JasperException;
+import org.apache.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);
+            temporaryVariableName = JspUtil.nextTemporaryVariableName();
+            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() {
+            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);
+	}
+    }
+}

Added: incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/PageDataImpl.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/PageDataImpl.java?rev=598331&view=auto
==============================================================================
--- incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/PageDataImpl.java (added)
+++ incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/PageDataImpl.java Mon Nov 26 08:15:43 2007
@@ -0,0 +1,710 @@
+/*
+ * Copyright 1999,2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.jasper.compiler;
+
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ListIterator;
+import javax.servlet.jsp.tagext.PageData;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
+import org.apache.jasper.JasperException;
+
+/**
+ * An implementation of <tt>javax.servlet.jsp.tagext.PageData</tt> which
+ * builds the XML view of a given page.
+ *
+ * The XML view is built in two passes:
+ *
+ * During the first pass, the FirstPassVisitor collects the attributes of the
+ * top-level jsp:root and those of the jsp:root elements of any included
+ * pages, and adds them to the jsp:root element of the XML view.
+ * In addition, any taglib directives are converted into xmlns: attributes and
+ * added to the jsp:root element of the XML view.
+ * This pass ignores any nodes other than JspRoot and TaglibDirective.
+ *
+ * During the second pass, the SecondPassVisitor produces the XML view, using
+ * the combined jsp:root attributes determined in the first pass and any
+ * remaining pages nodes (this pass ignores any JspRoot and TaglibDirective
+ * nodes).
+ *
+ * @author Jan Luehe
+ */
+class PageDataImpl extends PageData implements TagConstants {
+
+    private static final String JSP_VERSION = "2.0";
+    private static final String CDATA_START_SECTION = "<![CDATA[\n";
+    private static final String CDATA_END_SECTION = "]]>\n";
+
+    // string buffer used to build XML view
+    private StringBuffer buf;
+
+    /**
+     * Constructor.
+     *
+     * @param page the page nodes from which to generate the XML view
+     */
+    public PageDataImpl(Node.Nodes page, Compiler compiler)
+	        throws JasperException {
+
+	// First pass
+	FirstPassVisitor firstPass = new FirstPassVisitor(page.getRoot(),
+							  compiler.getPageInfo());
+	page.visit(firstPass);
+
+	// Second pass
+	buf = new StringBuffer();
+	SecondPassVisitor secondPass
+	    = new SecondPassVisitor(page.getRoot(), buf, compiler,
+				    firstPass.getJspIdPrefix());
+	page.visit(secondPass);
+    }
+
+    /**
+     * Returns the input stream of the XML view.
+     *
+     * @return the input stream of the XML view
+     */
+    public InputStream getInputStream() {
+	// Turn StringBuffer into InputStream
+        try {
+            return new ByteArrayInputStream(buf.toString().getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException uee) {
+	    // should never happen
+            throw new RuntimeException(uee.toString());
+        }
+    }
+
+    /*
+     * First-pass Visitor for JspRoot nodes (representing jsp:root elements)
+     * and TablibDirective nodes, ignoring any other nodes.
+     *
+     * The purpose of this Visitor is to collect the attributes of the
+     * top-level jsp:root and those of the jsp:root elements of any included
+     * pages, and add them to the jsp:root element of the XML view.
+     * In addition, this Visitor converts any taglib directives into xmlns:
+     * attributes and adds them to the jsp:root element of the XML view.
+     */
+    static class FirstPassVisitor
+	        extends Node.Visitor implements TagConstants {
+
+	private Node.Root root;
+	private AttributesImpl rootAttrs;
+	private PageInfo pageInfo;
+
+	// Prefix for the 'id' attribute
+	private String jspIdPrefix;
+
+	/*
+	 * Constructor
+	 */
+	public FirstPassVisitor(Node.Root root, PageInfo pageInfo) {
+	    this.root = root;
+	    this.pageInfo = pageInfo;
+	    this.rootAttrs = new AttributesImpl();
+	    this.rootAttrs.addAttribute("", "", "version", "CDATA",
+					JSP_VERSION);
+	    this.jspIdPrefix = "jsp";
+	}
+
+	public void visit(Node.Root n) throws JasperException {
+	    visitBody(n);
+	    if (n == root) {
+		/*
+		 * Top-level page.
+		 *
+		 * Add
+		 *   xmlns:jsp="http://java.sun.com/JSP/Page"
+		 * attribute only if not already present.
+		 */
+		if (!JSP_URI.equals(rootAttrs.getValue("xmlns:jsp"))) {
+		    rootAttrs.addAttribute("", "", "xmlns:jsp", "CDATA",
+					   JSP_URI);
+		}
+
+		if (pageInfo.isJspPrefixHijacked()) {
+		    /*
+		     * 'jsp' prefix has been hijacked, that is, bound to a
+		     * namespace other than the JSP namespace. This means that
+		     * when adding an 'id' attribute to each element, we can't
+		     * use the 'jsp' prefix. Therefore, create a new prefix 
+		     * (one that is unique across the translation unit) for use
+		     * by the 'id' attribute, and bind it to the JSP namespace
+		     */
+		    jspIdPrefix += "jsp";
+		    while (pageInfo.containsPrefix(jspIdPrefix)) {
+			jspIdPrefix += "jsp";
+		    }
+		    rootAttrs.addAttribute("", "", "xmlns:" + jspIdPrefix,
+					   "CDATA", JSP_URI);
+		}
+
+		root.setAttributes(rootAttrs);
+	    }
+	}
+
+	public void visit(Node.JspRoot n) throws JasperException {
+	    addAttributes(n.getTaglibAttributes());
+            addAttributes(n.getNonTaglibXmlnsAttributes());
+	    addAttributes(n.getAttributes());
+
+	    visitBody(n);
+	}
+
+	/*
+	 * Converts taglib directive into "xmlns:..." attribute of jsp:root
+	 * element.
+	 */
+	public void visit(Node.TaglibDirective n) throws JasperException {
+	    Attributes attrs = n.getAttributes();
+	    if (attrs != null) {
+		String qName = "xmlns:" + attrs.getValue("prefix");
+		/*
+		 * According to javadocs of org.xml.sax.helpers.AttributesImpl,
+		 * the addAttribute method does not check to see if the
+		 * specified attribute is already contained in the list: This
+		 * is the application's responsibility!
+		 */
+		if (rootAttrs.getIndex(qName) == -1) {
+		    String location = attrs.getValue("uri");
+		    if (location != null) {
+                        if (location.startsWith("/")) {
+                            location = URN_JSPTLD + location;
+                        }
+			rootAttrs.addAttribute("", "", qName, "CDATA",
+					       location);
+		    } else {
+			location = attrs.getValue("tagdir");
+			rootAttrs.addAttribute("", "", qName, "CDATA",
+					       URN_JSPTAGDIR + location);
+		    }
+		}
+	    }
+	}
+
+	public String getJspIdPrefix() {
+	    return jspIdPrefix;
+	}
+
+	private void addAttributes(Attributes attrs) {
+	    if (attrs != null) {
+		int len = attrs.getLength();
+
+		for (int i=0; i<len; i++) {
+                    String qName = attrs.getQName(i);
+		    if ("version".equals(qName)) {
+			continue;
+		    }
+
+                    // Bugzilla 35252: http://issues.apache.org/bugzilla/show_bug.cgi?id=35252
+                    if(rootAttrs.getIndex(qName) == -1) {
+                        rootAttrs.addAttribute(attrs.getURI(i),
+                                               attrs.getLocalName(i),
+                                               qName,
+                                               attrs.getType(i),
+                                               attrs.getValue(i));
+                    }
+		}
+	    }
+	}
+    }
+
+
+    /*
+     * Second-pass Visitor responsible for producing XML view and assigning
+     * each element a unique jsp:id attribute.
+     */
+    static class SecondPassVisitor extends Node.Visitor
+        	implements TagConstants {
+
+	private Node.Root root;
+	private StringBuffer buf;
+	private Compiler compiler;
+	private String jspIdPrefix;
+	private boolean resetDefaultNS = false;
+
+	// Current value of jsp:id attribute
+	private int jspId;
+
+	/*
+	 * Constructor
+	 */
+	public SecondPassVisitor(Node.Root root, StringBuffer buf,
+				 Compiler compiler, String jspIdPrefix) {
+	    this.root = root;
+	    this.buf = buf;
+	    this.compiler = compiler;
+	    this.jspIdPrefix = jspIdPrefix;
+	}
+
+	/*
+	 * Visits root node.
+	 */
+	public void visit(Node.Root n) throws JasperException {
+	    if (n == this.root) {
+		// top-level page
+		appendXmlProlog();
+		appendTag(n);
+	    } else {
+		boolean resetDefaultNSSave = resetDefaultNS;
+		if (n.isXmlSyntax()) {
+		    resetDefaultNS = true;
+		}
+		visitBody(n);
+		resetDefaultNS = resetDefaultNSSave;
+	    }
+	}
+
+	/*
+	 * Visits jsp:root element of JSP page in XML syntax.
+	 *
+	 * Any nested jsp:root elements (from pages included via an
+	 * include directive) are ignored.
+	 */
+	public void visit(Node.JspRoot n) throws JasperException {
+	    visitBody(n);
+	}
+
+	public void visit(Node.PageDirective n) throws JasperException {
+	    appendPageDirective(n);
+	}
+
+	public void visit(Node.IncludeDirective n) throws JasperException {
+	    // expand in place
+	    visitBody(n);
+	}
+
+	public void visit(Node.Comment n) throws JasperException {
+	    // Comments are ignored in XML view
+	}
+
+	public void visit(Node.Declaration n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.Expression n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.Scriptlet n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.JspElement n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.ELExpression n) throws JasperException {
+	    if (!n.getRoot().isXmlSyntax()) {
+		buf.append("<").append(JSP_TEXT_ACTION);
+		buf.append(" ");
+	        buf.append(jspIdPrefix);
+		buf.append(":id=\"");
+		buf.append(jspId++).append("\">");
+	    }
+	    buf.append("${");
+            buf.append(JspUtil.escapeXml(n.getText()));
+	    buf.append("}");
+	    if (!n.getRoot().isXmlSyntax()) {
+		buf.append(JSP_TEXT_ACTION_END);
+	    }
+	    buf.append("\n");
+	}
+
+	public void visit(Node.IncludeAction n) throws JasperException {
+	    appendTag(n);
+	}
+    
+	public void visit(Node.ForwardAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.GetProperty n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.SetProperty n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.ParamAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.ParamsAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.FallBackAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.UseBean n) throws JasperException {
+	    appendTag(n);
+	}
+	
+	public void visit(Node.PlugIn n) throws JasperException {
+	    appendTag(n);
+	}
+
+        public void visit(Node.NamedAttribute n) throws JasperException {
+            appendTag(n);
+        }
+        
+        public void visit(Node.JspBody n) throws JasperException {
+            appendTag(n);
+        }
+
+	public void visit(Node.CustomTag n) throws JasperException {
+	    boolean resetDefaultNSSave = resetDefaultNS;
+	    appendTag(n, resetDefaultNS);
+	    resetDefaultNS = resetDefaultNSSave;
+	}
+
+	public void visit(Node.UninterpretedTag n) throws JasperException {
+	    boolean resetDefaultNSSave = resetDefaultNS;
+	    appendTag(n, resetDefaultNS);
+	    resetDefaultNS = resetDefaultNSSave;
+	}
+
+	public void visit(Node.JspText n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.DoBodyAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+        public void visit(Node.InvokeAction n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.TagDirective n) throws JasperException {
+	    appendTagDirective(n);
+	}
+
+	public void visit(Node.AttributeDirective n) throws JasperException {
+	    appendTag(n);
+	}
+
+	public void visit(Node.VariableDirective n) throws JasperException {
+	    appendTag(n);
+	}
+        
+	public void visit(Node.TemplateText n) throws JasperException {
+	    /*
+	     * If the template text came from a JSP page written in JSP syntax,
+	     * create a jsp:text element for it (JSP 5.3.2).
+	     */
+	    appendText(n.getText(), !n.getRoot().isXmlSyntax());
+	}
+
+	/*
+	 * Appends the given tag, including its body, to the XML view.
+	 */
+	private void appendTag(Node n) throws JasperException {
+	    appendTag(n, false);
+	}
+
+	/*
+	 * Appends the given tag, including its body, to the XML view,
+	 * and optionally reset default namespace to "", if none specified.
+	 */
+	private void appendTag(Node n, boolean addDefaultNS)
+		throws JasperException {
+
+	    Node.Nodes body = n.getBody();
+	    String text = n.getText();
+
+	    buf.append("<").append(n.getQName());
+	    buf.append("\n");
+
+	    printAttributes(n, addDefaultNS);
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+
+	    if (ROOT_ACTION.equals(n.getLocalName()) || body != null
+		        || text != null) {
+		buf.append(">\n");
+		if (ROOT_ACTION.equals(n.getLocalName())) {
+		    if (compiler.getCompilationContext().isTagFile()) {
+			appendTagDirective();
+		    } else {
+			appendPageDirective();
+		    }
+		}
+		if (body != null) {
+		    body.visit(this);
+		} else {
+		    appendText(text, false);
+		}
+		buf.append("</" + n.getQName() + ">\n");
+	    } else {
+		buf.append("/>\n");
+	    }
+	}
+
+	/*
+	 * Appends the page directive with the given attributes to the XML
+	 * view.
+	 *
+	 * Since the import attribute of the page directive is the only page
+	 * attribute that is allowed to appear multiple times within the same
+	 * document, and since XML allows only single-value attributes,
+	 * the values of multiple import attributes must be combined into one,
+	 * separated by comma.
+	 *
+	 * If the given page directive contains just 'contentType' and/or
+	 * 'pageEncoding' attributes, we ignore it, as we've already appended
+	 * a page directive containing just these two attributes.
+	 */
+	private void appendPageDirective(Node.PageDirective n) {
+	    boolean append = false;
+	    Attributes attrs = n.getAttributes();
+	    int len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String attrName = attrs.getQName(i);
+		if (!"pageEncoding".equals(attrName)
+		        && !"contentType".equals(attrName)) {
+		    append = true;
+		    break;
+		}
+	    }
+	    if (!append) {
+		return;
+	    }
+
+	    buf.append("<").append(n.getQName());
+	    buf.append("\n");
+
+	    // append jsp:id
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+
+	    // append remaining attributes
+	    for (int i=0; i<len; i++) {
+		String attrName = attrs.getQName(i);
+		if ("import".equals(attrName) || "contentType".equals(attrName)
+		        || "pageEncoding".equals(attrName)) {
+		    /*
+		     * Page directive's 'import' attribute is considered
+		     * further down, and its 'pageEncoding' and 'contentType'
+		     * attributes are ignored, since we've already appended
+		     * a new page directive containing just these two
+		     * attributes
+		     */
+		    continue;
+		}
+		String value = attrs.getValue(i);
+		buf.append("  ").append(attrName).append("=\"");
+		buf.append(JspUtil.getExprInXml(value)).append("\"\n");
+	    }
+	    if (n.getImports().size() > 0) {
+		// Concatenate names of imported classes/packages
+		boolean first = true;
+		ListIterator iter = n.getImports().listIterator();
+		while (iter.hasNext()) {
+		    if (first) {
+			first = false;
+			buf.append("  import=\"");
+		    } else {
+			buf.append(",");
+		    }
+		    buf.append(JspUtil.getExprInXml((String) iter.next()));
+		}
+		buf.append("\"\n");
+	    }
+	    buf.append("/>\n");
+	}
+
+	/*
+	 * Appends a page directive with 'pageEncoding' and 'contentType'
+	 * attributes.
+	 *
+	 * The value of the 'pageEncoding' attribute is hard-coded
+	 * to UTF-8, whereas the value of the 'contentType' attribute, which
+	 * is identical to what the container will pass to
+	 * ServletResponse.setContentType(), is derived from the pageInfo.
+	 */
+	private void appendPageDirective() {
+	    buf.append("<").append(JSP_PAGE_DIRECTIVE_ACTION);
+	    buf.append("\n");
+
+	    // append jsp:id
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+	    buf.append("  ").append("pageEncoding").append("=\"UTF-8\"\n");
+	    buf.append("  ").append("contentType").append("=\"");
+	    buf.append(compiler.getPageInfo().getContentType()).append("\"\n");
+	    buf.append("/>\n");	    
+	}
+
+	/*
+	 * Appends the tag directive with the given attributes to the XML
+	 * view.
+	 *
+	 * If the given tag directive contains just a 'pageEncoding'
+	 * attributes, we ignore it, as we've already appended
+	 * a tag directive containing just this attributes.
+	 */
+	private void appendTagDirective(Node.TagDirective n)
+	        throws JasperException {
+
+	    boolean append = false;
+	    Attributes attrs = n.getAttributes();
+	    int len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String attrName = attrs.getQName(i);
+		if (!"pageEncoding".equals(attrName)) {
+		    append = true;
+		    break;
+		}
+	    }
+	    if (!append) {
+		return;
+	    }
+
+	    appendTag(n);
+	}
+
+	/*
+	 * Appends a tag directive containing a single 'pageEncoding'
+	 * attribute whose value is hard-coded to UTF-8.
+	 */
+	private void appendTagDirective() {
+	    buf.append("<").append(JSP_TAG_DIRECTIVE_ACTION);
+	    buf.append("\n");
+
+	    // append jsp:id
+	    buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+	    buf.append(jspId++).append("\"\n");
+	    buf.append("  ").append("pageEncoding").append("=\"UTF-8\"\n");
+	    buf.append("/>\n");	    
+	}
+
+	private void appendText(String text, boolean createJspTextElement) {
+	    if (createJspTextElement) {
+		buf.append("<").append(JSP_TEXT_ACTION);
+		buf.append("\n");
+
+		// append jsp:id
+		buf.append("  ").append(jspIdPrefix).append(":id").append("=\"");
+		buf.append(jspId++).append("\"\n");
+		buf.append(">\n");
+
+		appendCDATA(text);
+		buf.append(JSP_TEXT_ACTION_END);
+		buf.append("\n");
+	    } else {
+		appendCDATA(text);
+	    }
+	}
+	
+	/*
+	 * Appends the given text as a CDATA section to the XML view, unless
+	 * the text has already been marked as CDATA.
+	 */
+	private void appendCDATA(String text) {
+	    buf.append(CDATA_START_SECTION);
+	    buf.append(escapeCDATA(text));
+	    buf.append(CDATA_END_SECTION);
+	}
+
+	/*
+	 * Escapes any occurrences of "]]>" (by replacing them with "]]&gt;")
+	 * within the given text, so it can be included in a CDATA section.
+	 */
+	private String escapeCDATA(String text) {
+            if( text==null ) return "";
+	    int len = text.length();
+	    CharArrayWriter result = new CharArrayWriter(len);
+	    for (int i=0; i<len; i++) {
+		if (((i+2) < len)
+		        && (text.charAt(i) == ']')
+		        && (text.charAt(i+1) == ']')
+		        && (text.charAt(i+2) == '>')) {
+		    // match found
+		    result.write(']');
+		    result.write(']');
+		    result.write('&');
+		    result.write('g');
+		    result.write('t');
+		    result.write(';');
+		    i += 2;
+		} else {
+		    result.write(text.charAt(i));
+		}
+	    }
+	    return result.toString();
+	}
+
+	/*
+	 * Appends the attributes of the given Node to the XML view.
+	 */
+	private void printAttributes(Node n, boolean addDefaultNS) {
+
+	    /*
+	     * Append "xmlns" attributes that represent tag libraries
+	     */
+	    Attributes attrs = n.getTaglibAttributes();
+	    int len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String name = attrs.getQName(i);
+		String value = attrs.getValue(i);
+		buf.append("  ").append(name).append("=\"").append(value).append("\"\n");
+	    }
+
+	    /*
+	     * Append "xmlns" attributes that do not represent tag libraries
+	     */
+	    attrs = n.getNonTaglibXmlnsAttributes();
+	    len = (attrs == null) ? 0 : attrs.getLength();
+	    boolean defaultNSSeen = false;
+	    for (int i=0; i<len; i++) {
+		String name = attrs.getQName(i);
+		String value = attrs.getValue(i);
+		buf.append("  ").append(name).append("=\"").append(value).append("\"\n");
+		defaultNSSeen |= "xmlns".equals(name);
+	    }
+	    if (addDefaultNS && !defaultNSSeen) {
+		buf.append("  xmlns=\"\"\n");
+	    }
+	    resetDefaultNS = false;
+
+	    /*
+	     * Append all other attributes
+	     */
+	    attrs = n.getAttributes();
+	    len = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<len; i++) {
+		String name = attrs.getQName(i);
+		String value = attrs.getValue(i);
+		buf.append("  ").append(name).append("=\"");
+		buf.append(JspUtil.getExprInXml(value)).append("\"\n");
+	    }
+	}
+
+	/*
+	 * Appends XML prolog with encoding declaration.
+	 */
+	private void appendXmlProlog() {
+	    buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+	}
+    }
+}
+



Mime
View raw message