tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From r...@apache.org
Subject svn commit: r379417 [4/7] - in /tomcat/jasper/tc6.0.x/src/share/org/apache: el/lang/ el/parser/ el/util/ jasper/compiler/ jasper/el/ jasper/resources/ jasper/runtime/
Date Tue, 21 Feb 2006 10:57:38 GMT
Modified: tomcat/jasper/tc6.0.x/src/share/org/apache/jasper/compiler/Parser.java
URL: http://svn.apache.org/viewcvs/tomcat/jasper/tc6.0.x/src/share/org/apache/jasper/compiler/Parser.java?rev=379417&r1=379416&r2=379417&view=diff
==============================================================================
--- tomcat/jasper/tc6.0.x/src/share/org/apache/jasper/compiler/Parser.java (original)
+++ tomcat/jasper/tc6.0.x/src/share/org/apache/jasper/compiler/Parser.java Tue Feb 21 02:57:35 2006
@@ -33,10 +33,9 @@
 import org.xml.sax.helpers.AttributesImpl;
 
 /**
- * This class implements a parser for a JSP page (non-xml view).
- * JSP page grammar is included here for reference.  The token '#'
- * that appears in the production indicates the current input token
- * location in the production.
+ * This class implements a parser for a JSP page (non-xml view). JSP page
+ * grammar is included here for reference. The token '#' that appears in the
+ * production indicates the current input token location in the production.
  * 
  * @author Kin-man Chung
  * @author Shawn Bayern
@@ -46,338 +45,332 @@
 class Parser implements TagConstants {
 
     private ParserController parserController;
+
     private JspCompilationContext ctxt;
+
     private JspReader reader;
+
     private String currentFile;
+
     private Mark start;
+
     private ErrorDispatcher err;
+
     private int scriptlessCount;
+
     private boolean isTagFile;
+
     private boolean directivesOnly;
+
     private URL jarFileUrl;
+
     private PageInfo pageInfo;
 
     // Virtual body content types, to make parsing a little easier.
     // These are not accessible from outside the parser.
-    private static final String JAVAX_BODY_CONTENT_PARAM = 
-        "JAVAX_BODY_CONTENT_PARAM";
-    private static final String JAVAX_BODY_CONTENT_PLUGIN = 
-        "JAVAX_BODY_CONTENT_PLUGIN";
-    private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT = 
-        "JAVAX_BODY_CONTENT_TEMPLATE_TEXT";
+    private static final String JAVAX_BODY_CONTENT_PARAM = "JAVAX_BODY_CONTENT_PARAM";
+
+    private static final String JAVAX_BODY_CONTENT_PLUGIN = "JAVAX_BODY_CONTENT_PLUGIN";
+
+    private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT = "JAVAX_BODY_CONTENT_TEMPLATE_TEXT";
 
     /**
      * The constructor
      */
     private Parser(ParserController pc, JspReader reader, boolean isTagFile,
-		   boolean directivesOnly, URL jarFileUrl) {
-	this.parserController = pc;
-	this.ctxt = pc.getJspCompilationContext();
-	this.pageInfo = pc.getCompiler().getPageInfo();
-	this.err = pc.getCompiler().getErrorDispatcher();
-	this.reader = reader;
-	this.currentFile = reader.mark().getFile();
+            boolean directivesOnly, URL jarFileUrl) {
+        this.parserController = pc;
+        this.ctxt = pc.getJspCompilationContext();
+        this.pageInfo = pc.getCompiler().getPageInfo();
+        this.err = pc.getCompiler().getErrorDispatcher();
+        this.reader = reader;
+        this.currentFile = reader.mark().getFile();
         this.scriptlessCount = 0;
-	this.isTagFile = isTagFile;
-	this.directivesOnly = directivesOnly;
-	this.jarFileUrl = jarFileUrl;
+        this.isTagFile = isTagFile;
+        this.directivesOnly = directivesOnly;
+        this.jarFileUrl = jarFileUrl;
         start = reader.mark();
     }
 
     /**
      * The main entry for Parser
      * 
-     * @param pc The ParseController, use for getting other objects in compiler
-     *		 and for parsing included pages
-     * @param reader To read the page
-     * @param parent The parent node to this page, null for top level page
+     * @param pc
+     *            The ParseController, use for getting other objects in compiler
+     *            and for parsing included pages
+     * @param reader
+     *            To read the page
+     * @param parent
+     *            The parent node to this page, null for top level page
      * @return list of nodes representing the parsed page
      */
-    public static Node.Nodes parse(ParserController pc,
-				   JspReader reader,
-				   Node parent,
-				   boolean isTagFile,
-				   boolean directivesOnly,
-				   URL jarFileUrl,
-				   String pageEnc,
-				   String jspConfigPageEnc,
-				   boolean isDefaultPageEncoding)
-		throws JasperException {
-
-	Parser parser = new Parser(pc, reader, isTagFile, directivesOnly,
-				   jarFileUrl);
-
-	Node.Root root = new Node.Root(reader.mark(), parent, false);
-	root.setPageEncoding(pageEnc);
-	root.setJspConfigPageEncoding(jspConfigPageEnc);
-	root.setIsDefaultPageEncoding(isDefaultPageEncoding);
-
-	if (directivesOnly) {
-	    parser.parseTagFileDirectives(root);
-	    return new Node.Nodes(root);
-	}
-
-	// For the Top level page, add inlcude-prelude and include-coda
-	PageInfo pageInfo = pc.getCompiler().getPageInfo();
-	if (parent == null) {
-	    parser.addInclude(root, pageInfo.getIncludePrelude());
-	}
-	while (reader.hasMoreInput()) {
-	    parser.parseElements(root);
-	}
-	if (parent == null) {
-	    parser.addInclude(root, pageInfo.getIncludeCoda());
-	}
+    public static Node.Nodes parse(ParserController pc, JspReader reader,
+            Node parent, boolean isTagFile, boolean directivesOnly,
+            URL jarFileUrl, String pageEnc, String jspConfigPageEnc,
+            boolean isDefaultPageEncoding) throws JasperException {
+
+        Parser parser = new Parser(pc, reader, isTagFile, directivesOnly,
+                jarFileUrl);
+
+        Node.Root root = new Node.Root(reader.mark(), parent, false);
+        root.setPageEncoding(pageEnc);
+        root.setJspConfigPageEncoding(jspConfigPageEnc);
+        root.setIsDefaultPageEncoding(isDefaultPageEncoding);
+
+        if (directivesOnly) {
+            parser.parseTagFileDirectives(root);
+            return new Node.Nodes(root);
+        }
+
+        // For the Top level page, add inlcude-prelude and include-coda
+        PageInfo pageInfo = pc.getCompiler().getPageInfo();
+        if (parent == null) {
+            parser.addInclude(root, pageInfo.getIncludePrelude());
+        }
+        while (reader.hasMoreInput()) {
+            parser.parseElements(root);
+        }
+        if (parent == null) {
+            parser.addInclude(root, pageInfo.getIncludeCoda());
+        }
 
-	Node.Nodes page = new Node.Nodes(root);
-	return page;
+        Node.Nodes page = new Node.Nodes(root);
+        return page;
     }
 
     /**
      * Attributes ::= (S Attribute)* S?
      */
     Attributes parseAttributes() throws JasperException {
-	AttributesImpl attrs = new AttributesImpl();
+        AttributesImpl attrs = new AttributesImpl();
 
-	reader.skipSpaces();
-	while (parseAttribute(attrs))
-	    reader.skipSpaces();
+        reader.skipSpaces();
+        while (parseAttribute(attrs))
+            reader.skipSpaces();
 
-	return attrs;
+        return attrs;
     }
 
     /**
      * Parse Attributes for a reader, provided for external use
      */
     public static Attributes parseAttributes(ParserController pc,
-					     JspReader reader)
-		throws JasperException {
-	Parser tmpParser = new Parser(pc, reader, false, false, null);
-	return tmpParser.parseAttributes();
+            JspReader reader) throws JasperException {
+        Parser tmpParser = new Parser(pc, reader, false, false, null);
+        return tmpParser.parseAttributes();
     }
 
     /**
-     * Attribute ::= Name S? Eq S?
-     *               (   '"<%=' RTAttributeValueDouble
-     *                 | '"' AttributeValueDouble
-     *                 | "'<%=" RTAttributeValueSingle
-     *                 | "'" AttributeValueSingle
-     *               }
-     * Note: JSP and XML spec does not allow while spaces around Eq.  It is
-     * added to be backward compatible with Tomcat, and with other xml parsers.
-     */
-    private boolean parseAttribute(AttributesImpl attrs)
-	        throws JasperException {
-
-	// Get the qualified name
-	String qName = parseName();
-	if (qName == null)
-	    return false;
-
-	// Determine prefix and local name components
-	String localName = qName;
-	String uri = "";
-	int index = qName.indexOf(':');
-	if (index != -1) {
-	    String prefix = qName.substring(0, index);
-	    uri = pageInfo.getURI(prefix);
-	    if (uri == null) {
-		err.jspError(reader.mark(),
-			     "jsp.error.attribute.invalidPrefix", prefix);
-	    }
-	    localName = qName.substring(index+1);
-	}
-
- 	reader.skipSpaces();
-	if (!reader.matches("="))
-	    err.jspError(reader.mark(), "jsp.error.attribute.noequal");
-
- 	reader.skipSpaces();
-	char quote = (char) reader.nextChar();
-	if (quote != '\'' && quote != '"')
-	    err.jspError(reader.mark(), "jsp.error.attribute.noquote");
-
- 	String watchString = "";
-	if (reader.matches("<%="))
-	    watchString = "%>";
-	watchString = watchString + quote;
-	
-	String attrValue = parseAttributeValue(watchString);
-	attrs.addAttribute(uri, localName, qName, "CDATA", attrValue);
-	return true;
+     * Attribute ::= Name S? Eq S? ( '"<%=' RTAttributeValueDouble | '"'
+     * AttributeValueDouble | "'<%=" RTAttributeValueSingle | "'"
+     * AttributeValueSingle } Note: JSP and XML spec does not allow while spaces
+     * around Eq. It is added to be backward compatible with Tomcat, and with
+     * other xml parsers.
+     */
+    private boolean parseAttribute(AttributesImpl attrs) throws JasperException {
+
+        // Get the qualified name
+        String qName = parseName();
+        if (qName == null)
+            return false;
+
+        // Determine prefix and local name components
+        String localName = qName;
+        String uri = "";
+        int index = qName.indexOf(':');
+        if (index != -1) {
+            String prefix = qName.substring(0, index);
+            uri = pageInfo.getURI(prefix);
+            if (uri == null) {
+                err.jspError(reader.mark(),
+                        "jsp.error.attribute.invalidPrefix", prefix);
+            }
+            localName = qName.substring(index + 1);
+        }
+
+        reader.skipSpaces();
+        if (!reader.matches("="))
+            err.jspError(reader.mark(), "jsp.error.attribute.noequal");
+
+        reader.skipSpaces();
+        char quote = (char) reader.nextChar();
+        if (quote != '\'' && quote != '"')
+            err.jspError(reader.mark(), "jsp.error.attribute.noquote");
+
+        String watchString = "";
+        if (reader.matches("<%="))
+            watchString = "%>";
+        watchString = watchString + quote;
+
+        String attrValue = parseAttributeValue(watchString);
+        attrs.addAttribute(uri, localName, qName, "CDATA", attrValue);
+        return true;
     }
 
     /**
      * Name ::= (Letter | '_' | ':') (Letter | Digit | '.' | '_' | '-' | ':')*
      */
     private String parseName() throws JasperException {
-	char ch = (char)reader.peekChar();
-	if (Character.isLetter(ch) || ch == '_' || ch == ':') {
-	    StringBuffer buf = new StringBuffer();
-	    buf.append(ch);
-	    reader.nextChar();
-	    ch = (char)reader.peekChar();
-	    while (Character.isLetter(ch) || Character.isDigit(ch) ||
-			ch == '.' || ch == '_' || ch == '-' || ch == ':') {
-		buf.append(ch);
-		reader.nextChar();
-		ch = (char) reader.peekChar();
-	    }
-	    return buf.toString();
-	}
-	return null;
+        char ch = (char) reader.peekChar();
+        if (Character.isLetter(ch) || ch == '_' || ch == ':') {
+            StringBuffer buf = new StringBuffer();
+            buf.append(ch);
+            reader.nextChar();
+            ch = (char) reader.peekChar();
+            while (Character.isLetter(ch) || Character.isDigit(ch) || ch == '.'
+                    || ch == '_' || ch == '-' || ch == ':') {
+                buf.append(ch);
+                reader.nextChar();
+                ch = (char) reader.peekChar();
+            }
+            return buf.toString();
+        }
+        return null;
     }
 
     /**
-     * AttributeValueDouble ::= (QuotedChar - '"')*
-     *				('"' | <TRANSLATION_ERROR>)
+     * AttributeValueDouble ::= (QuotedChar - '"')* ('"' | <TRANSLATION_ERROR>)
      * RTAttributeValueDouble ::= ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"')
-     *				  ('%>"' | TRANSLATION_ERROR)
+     * ('%>"' | TRANSLATION_ERROR)
      */
     private String parseAttributeValue(String watch) throws JasperException {
-	Mark start = reader.mark();
-	Mark stop = reader.skipUntilIgnoreEsc(watch);
-	if (stop == null) {
-	    err.jspError(start, "jsp.error.attribute.unterminated", watch);
-	}
-
-	String ret = parseQuoted(reader.getText(start, stop));
-	if (watch.length() == 1)	// quote
-	    return ret;
-
-	// putback delimiter '<%=' and '%>', since they are needed if the
-	// attribute does not allow RTexpression.
-	return "<%=" + ret + "%>";
+        Mark start = reader.mark();
+        Mark stop = reader.skipUntilIgnoreEsc(watch);
+        if (stop == null) {
+            err.jspError(start, "jsp.error.attribute.unterminated", watch);
+        }
+
+        String ret = parseQuoted(reader.getText(start, stop));
+        if (watch.length() == 1) // quote
+            return ret;
+
+        // putback delimiter '<%=' and '%>', since they are needed if the
+        // attribute does not allow RTexpression.
+        return "<%=" + ret + "%>";
     }
 
     /**
-     * QuotedChar ::=   '&apos;'
-     *	              | '&quot;'
-     *                | '\\'
-     *                | '\"'
-     *                | "\'"
-     *                | '\>'
-     *                | '\$'
-     *                | Char
+     * QuotedChar ::= '&apos;' | '&quot;' | '\\' | '\"' | "\'" | '\>' | '\$' |
+     * Char
      */
     private String parseQuoted(String tx) {
-	StringBuffer buf = new StringBuffer();
-	int size = tx.length();
-	int i = 0;
-	while (i < size) {
-	    char ch = tx.charAt(i);
-	    if (ch == '&') {
-		if (i+5 < size && tx.charAt(i+1) == 'a'
-		        && tx.charAt(i+2) == 'p' && tx.charAt(i+3) == 'o'
-		        && tx.charAt(i+4) == 's' && tx.charAt(i+5) == ';') {
-		    buf.append('\'');
-		    i += 6;
-		} else if (i+5 < size && tx.charAt(i+1) == 'q'
-			   && tx.charAt(i+2) == 'u' && tx.charAt(i+3) == 'o'
-			   && tx.charAt(i+4) == 't' && tx.charAt(i+5) == ';') {
-		    buf.append('"');
-		    i += 6;
-		} else {
-		    buf.append(ch);
-		    ++i;
-		}
-	    } else if (ch == '\\' && i+1 < size) {
-		ch = tx.charAt(i+1);
-		if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') {
-		    buf.append(ch);
-		    i += 2;
-		} else if (ch == '$') {
-		    // Replace "\$" with some special char.  XXX hack!
-		    buf.append(Constants.ESC);
-		    i += 2;
-		} else {
-		    buf.append('\\');
-		    ++i;
-		}
-	    } else {
-		buf.append(ch);
-		++i;
-	    }
-	}
-	return buf.toString();
+        StringBuffer buf = new StringBuffer();
+        int size = tx.length();
+        int i = 0;
+        while (i < size) {
+            char ch = tx.charAt(i);
+            if (ch == '&') {
+                if (i + 5 < size && tx.charAt(i + 1) == 'a'
+                        && tx.charAt(i + 2) == 'p' && tx.charAt(i + 3) == 'o'
+                        && tx.charAt(i + 4) == 's' && tx.charAt(i + 5) == ';') {
+                    buf.append('\'');
+                    i += 6;
+                } else if (i + 5 < size && tx.charAt(i + 1) == 'q'
+                        && tx.charAt(i + 2) == 'u' && tx.charAt(i + 3) == 'o'
+                        && tx.charAt(i + 4) == 't' && tx.charAt(i + 5) == ';') {
+                    buf.append('"');
+                    i += 6;
+                } else {
+                    buf.append(ch);
+                    ++i;
+                }
+            } else if (ch == '\\' && i + 1 < size) {
+                ch = tx.charAt(i + 1);
+                if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') {
+                    buf.append(ch);
+                    i += 2;
+                } else if (ch == '$') {
+                    // Replace "\$" with some special char. XXX hack!
+                    buf.append(Constants.ESC);
+                    i += 2;
+                } else {
+                    buf.append('\\');
+                    ++i;
+                }
+            } else {
+                buf.append(ch);
+                ++i;
+            }
+        }
+        return buf.toString();
     }
 
     private String parseScriptText(String tx) {
-	CharArrayWriter cw = new CharArrayWriter();
-	int size = tx.length();
-	int i = 0;
-	while (i < size) {
-	    char ch = tx.charAt(i);
-	    if (i+2 < size && ch == '%' && tx.charAt(i+1) == '\\'
-		    && tx.charAt(i+2) == '>') {
-		cw.write('%');
-		cw.write('>');
-		i += 3;
-	    } else {
-		cw.write(ch);
-		++i;
-	    }
-	}
-	cw.close();
-	return cw.toString();
+        CharArrayWriter cw = new CharArrayWriter();
+        int size = tx.length();
+        int i = 0;
+        while (i < size) {
+            char ch = tx.charAt(i);
+            if (i + 2 < size && ch == '%' && tx.charAt(i + 1) == '\\'
+                    && tx.charAt(i + 2) == '>') {
+                cw.write('%');
+                cw.write('>');
+                i += 3;
+            } else {
+                cw.write(ch);
+                ++i;
+            }
+        }
+        cw.close();
+        return cw.toString();
     }
 
     /*
      * Invokes parserController to parse the included page
      */
-    private void processIncludeDirective(String file, Node parent) 
-		throws JasperException {
-	if (file == null) {
-	    return;
-	}
-
-	try {
-	    parserController.parse(file, parent, jarFileUrl);
-	} catch (FileNotFoundException ex) {
-	    err.jspError(start, "jsp.error.file.not.found", file);
-	} catch (Exception ex) {
-	    err.jspError(start, ex.getMessage());
-	}
+    private void processIncludeDirective(String file, Node parent)
+            throws JasperException {
+        if (file == null) {
+            return;
+        }
+
+        try {
+            parserController.parse(file, parent, jarFileUrl);
+        } catch (FileNotFoundException ex) {
+            err.jspError(start, "jsp.error.file.not.found", file);
+        } catch (Exception ex) {
+            err.jspError(start, ex.getMessage());
+        }
     }
 
     /*
-     * Parses a page directive with the following syntax:
-     *   PageDirective ::= ( S Attribute)*
+     * Parses a page directive with the following syntax: PageDirective ::= ( S
+     * Attribute)*
      */
     private void parsePageDirective(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	Node.PageDirective n = new Node.PageDirective(attrs, start, parent);
+        Attributes attrs = parseAttributes();
+        Node.PageDirective n = new Node.PageDirective(attrs, start, parent);
 
-	/*
-	 * A page directive may contain multiple 'import' attributes, each of
-	 * which consists of a comma-separated list of package names.
-	 * Store each list with the node, where it is parsed.
-	 */
-	for (int i = 0; i < attrs.getLength(); i++) {
-	    if ("import".equals(attrs.getQName(i))) {
-		n.addImport(attrs.getValue(i));
-	    }
-	}
+        /*
+         * A page directive may contain multiple 'import' attributes, each of
+         * which consists of a comma-separated list of package names. Store each
+         * list with the node, where it is parsed.
+         */
+        for (int i = 0; i < attrs.getLength(); i++) {
+            if ("import".equals(attrs.getQName(i))) {
+                n.addImport(attrs.getValue(i));
+            }
+        }
     }
 
     /*
-     * Parses an include directive with the following syntax:
-     *   IncludeDirective ::= ( S Attribute)*
+     * Parses an include directive with the following syntax: IncludeDirective
+     * ::= ( S Attribute)*
      */
     private void parseIncludeDirective(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
+        Attributes attrs = parseAttributes();
 
-	// Included file expanded here
-	Node includeNode = new Node.IncludeDirective(attrs, start, parent);
-	processIncludeDirective(attrs.getValue("file"), includeNode);
+        // Included file expanded here
+        Node includeNode = new Node.IncludeDirective(attrs, start, parent);
+        processIncludeDirective(attrs.getValue("file"), includeNode);
     }
 
     /**
-     * Add a list of files.  This is used for implementing include-prelude
-     * and include-coda of jsp-config element in web.xml
+     * Add a list of files. This is used for implementing include-prelude and
+     * include-coda of jsp-config element in web.xml
      */
     private void addInclude(Node parent, List files) throws JasperException {
-        if( files != null ) {
+        if (files != null) {
             Iterator iter = files.iterator();
             while (iter.hasNext()) {
                 String file = (String) iter.next();
@@ -385,225 +378,206 @@
                 attrs.addAttribute("", "file", "file", "CDATA", file);
 
                 // Create a dummy Include directive node
-                Node includeNode = new Node.IncludeDirective(attrs, 
-                    reader.mark(), parent);
+                Node includeNode = new Node.IncludeDirective(attrs, reader
+                        .mark(), parent);
                 processIncludeDirective(file, includeNode);
             }
         }
     }
 
     /*
-     * Parses a taglib directive with the following syntax:
-     *   Directive ::= ( S Attribute)*
+     * Parses a taglib directive with the following syntax: Directive ::= ( S
+     * Attribute)*
      */
     private void parseTaglibDirective(Node parent) throws JasperException {
 
-	Attributes attrs = parseAttributes();
-	String uri = attrs.getValue("uri");
-	String prefix = attrs.getValue("prefix");
-	if (prefix != null) {
+        Attributes attrs = parseAttributes();
+        String uri = attrs.getValue("uri");
+        String prefix = attrs.getValue("prefix");
+        if (prefix != null) {
             Mark prevMark = pageInfo.getNonCustomTagPrefix(prefix);
             if (prevMark != null) {
                 err.jspError(reader.mark(), "jsp.error.prefix.use_before_dcl",
-                    prefix, prevMark.getFile(), "" + prevMark.getLineNumber());
+                        prefix, prevMark.getFile(), ""
+                                + prevMark.getLineNumber());
             }
-	    if (uri != null) {
-		String uriPrev = pageInfo.getURI(prefix);
-		if (uriPrev != null && !uriPrev.equals(uri)) {
-		    err.jspError(reader.mark(), "jsp.error.prefix.refined",
-			prefix, uri, uriPrev);
-		}
-		if (pageInfo.getTaglib(uri) == null) {
-            TagLibraryInfoImpl impl = null;
-            if (ctxt.getOptions().isCaching()) {
-                impl = (TagLibraryInfoImpl) ctxt.getOptions().getCache().get(uri);
-            }
-            if (impl == null) {
-                String[] location = ctxt.getTldLocation(uri);
-                impl = new TagLibraryInfoImpl(ctxt,
-                        parserController,
-                        prefix,
-                        uri,
-                        location,
-                        err);
-                if (ctxt.getOptions().isCaching()) {
-                    ctxt.getOptions().getCache().put(uri, impl);
-                }
-            }
-		    pageInfo.addTaglib(uri, impl);
-		}
-		pageInfo.addPrefixMapping(prefix, uri);
-	    } else {
-		String tagdir = attrs.getValue("tagdir");
-		if (tagdir != null) {
-		    String urnTagdir = URN_JSPTAGDIR + tagdir;
-		    if (pageInfo.getTaglib(urnTagdir) == null) {
-			pageInfo.addTaglib(urnTagdir,
-					   new ImplicitTagLibraryInfo(
-                                                   ctxt,
-						   parserController,
-						   prefix, 
-						   tagdir,
-						   err));
-		    }
-		    pageInfo.addPrefixMapping(prefix, urnTagdir);
-		}
-	    }
-	}
+            if (uri != null) {
+                String uriPrev = pageInfo.getURI(prefix);
+                if (uriPrev != null && !uriPrev.equals(uri)) {
+                    err.jspError(reader.mark(), "jsp.error.prefix.refined",
+                            prefix, uri, uriPrev);
+                }
+                if (pageInfo.getTaglib(uri) == null) {
+                    TagLibraryInfoImpl impl = null;
+                    if (ctxt.getOptions().isCaching()) {
+                        impl = (TagLibraryInfoImpl) ctxt.getOptions()
+                                .getCache().get(uri);
+                    }
+                    if (impl == null) {
+                        String[] location = ctxt.getTldLocation(uri);
+                        impl = new TagLibraryInfoImpl(ctxt, parserController,
+                                prefix, uri, location, err);
+                        if (ctxt.getOptions().isCaching()) {
+                            ctxt.getOptions().getCache().put(uri, impl);
+                        }
+                    }
+                    pageInfo.addTaglib(uri, impl);
+                }
+                pageInfo.addPrefixMapping(prefix, uri);
+            } else {
+                String tagdir = attrs.getValue("tagdir");
+                if (tagdir != null) {
+                    String urnTagdir = URN_JSPTAGDIR + tagdir;
+                    if (pageInfo.getTaglib(urnTagdir) == null) {
+                        pageInfo.addTaglib(urnTagdir,
+                                new ImplicitTagLibraryInfo(ctxt,
+                                        parserController, prefix, tagdir, err));
+                    }
+                    pageInfo.addPrefixMapping(prefix, urnTagdir);
+                }
+            }
+        }
 
-	new Node.TaglibDirective(attrs, start, parent);
+        new Node.TaglibDirective(attrs, start, parent);
     }
 
     /*
-     * Parses a directive with the following syntax:
-     *   Directive ::= S? (   'page' PageDirective
-     *			    | 'include' IncludeDirective
-     *			    | 'taglib' TagLibDirective)
-     *		       S? '%>'
-     *
-     *   TagDirective ::= S? ('tag' PageDirective
-     *			    | 'include' IncludeDirective
-     *			    | 'taglib' TagLibDirective)
-     *                      | 'attribute AttributeDirective
-     *                      | 'variable VariableDirective
-     *		       S? '%>'
+     * Parses a directive with the following syntax: Directive ::= S? ( 'page'
+     * PageDirective | 'include' IncludeDirective | 'taglib' TagLibDirective) S?
+     * '%>'
+     * 
+     * TagDirective ::= S? ('tag' PageDirective | 'include' IncludeDirective |
+     * 'taglib' TagLibDirective) | 'attribute AttributeDirective | 'variable
+     * VariableDirective S? '%>'
      */
     private void parseDirective(Node parent) throws JasperException {
-	reader.skipSpaces();
+        reader.skipSpaces();
 
-	String directive = null;
-	if (reader.matches("page")) {
-	    directive = "&lt;%@ page";
-	    if (isTagFile) {
-		err.jspError(reader.mark(), "jsp.error.directive.istagfile",
-					    directive);
-	    }
-	    parsePageDirective(parent);
-	} else if (reader.matches("include")) {
-	    directive = "&lt;%@ include";
-	    parseIncludeDirective(parent);
-	} else if (reader.matches("taglib")) {
-	    if (directivesOnly) {
-	        // No need to get the tagLibInfo objects.  This alos suppresses
-	        // parsing of any tag files used in this tag file.
-	        return;
-	    }
-	    directive = "&lt;%@ taglib";
-	    parseTaglibDirective(parent);
-	} else if (reader.matches("tag")) {
-	    directive = "&lt;%@ tag";
-	    if (!isTagFile) {
-		err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
-					    directive);
-	    }
-	    parseTagDirective(parent);
-	} else if (reader.matches("attribute")) {
-	    directive = "&lt;%@ attribute";
-	    if (!isTagFile) {
-		err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
-					    directive);
-	    }
-	    parseAttributeDirective(parent);
-	} else if (reader.matches("variable")) {
-	    directive = "&lt;%@ variable";
-	    if (!isTagFile) {
-		err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
-					    directive);
-	    }
-	    parseVariableDirective(parent);
-	} else {
-	    err.jspError(reader.mark(), "jsp.error.invalid.directive");
-	}
-
-	reader.skipSpaces();
-	if (!reader.matches("%>")) {
-	    err.jspError(start, "jsp.error.unterminated", directive);
-	}
+        String directive = null;
+        if (reader.matches("page")) {
+            directive = "&lt;%@ page";
+            if (isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.istagfile",
+                        directive);
+            }
+            parsePageDirective(parent);
+        } else if (reader.matches("include")) {
+            directive = "&lt;%@ include";
+            parseIncludeDirective(parent);
+        } else if (reader.matches("taglib")) {
+            if (directivesOnly) {
+                // No need to get the tagLibInfo objects. This alos suppresses
+                // parsing of any tag files used in this tag file.
+                return;
+            }
+            directive = "&lt;%@ taglib";
+            parseTaglibDirective(parent);
+        } else if (reader.matches("tag")) {
+            directive = "&lt;%@ tag";
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                        directive);
+            }
+            parseTagDirective(parent);
+        } else if (reader.matches("attribute")) {
+            directive = "&lt;%@ attribute";
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                        directive);
+            }
+            parseAttributeDirective(parent);
+        } else if (reader.matches("variable")) {
+            directive = "&lt;%@ variable";
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                        directive);
+            }
+            parseVariableDirective(parent);
+        } else {
+            err.jspError(reader.mark(), "jsp.error.invalid.directive");
+        }
+
+        reader.skipSpaces();
+        if (!reader.matches("%>")) {
+            err.jspError(start, "jsp.error.unterminated", directive);
+        }
     }
-	
+
     /*
      * Parses a directive with the following syntax:
-     *
-     *   XMLJSPDirectiveBody ::= S? (   ( 'page' PageDirectiveAttrList
-     *                                    S? ( '/>' | ( '>' S? ETag ) )
-     *                               | ( 'include' IncludeDirectiveAttrList
-     *                                    S? ( '/>' | ( '>' S? ETag ) )
-     *                           | <TRANSLATION_ERROR>
-     *
-     *   XMLTagDefDirectiveBody ::= (   ( 'tag' TagDirectiveAttrList
-     *                                    S? ( '/>' | ( '>' S? ETag ) )
-     *                                | ( 'include' IncludeDirectiveAttrList
-     *                                    S? ( '/>' | ( '>' S? ETag ) )
-     *                                | ( 'attribute' AttributeDirectiveAttrList
-     *                                    S? ( '/>' | ( '>' S? ETag ) )
-     *                                | ( 'variable' VariableDirectiveAttrList
-     *                                    S? ( '/>' | ( '>' S? ETag ) )
-     *                              )
-     *                            | <TRANSLATION_ERROR>
+     * 
+     * XMLJSPDirectiveBody ::= S? ( ( 'page' PageDirectiveAttrList S? ( '/>' | (
+     * '>' S? ETag ) ) | ( 'include' IncludeDirectiveAttrList S? ( '/>' | ( '>'
+     * S? ETag ) ) | <TRANSLATION_ERROR>
+     * 
+     * XMLTagDefDirectiveBody ::= ( ( 'tag' TagDirectiveAttrList S? ( '/>' | (
+     * '>' S? ETag ) ) | ( 'include' IncludeDirectiveAttrList S? ( '/>' | ( '>'
+     * S? ETag ) ) | ( 'attribute' AttributeDirectiveAttrList S? ( '/>' | ( '>'
+     * S? ETag ) ) | ( 'variable' VariableDirectiveAttrList S? ( '/>' | ( '>' S?
+     * ETag ) ) ) | <TRANSLATION_ERROR>
      */
     private void parseXMLDirective(Node parent) throws JasperException {
-       reader.skipSpaces();
+        reader.skipSpaces();
 
         String eTag = null;
-       if (reader.matches("page")) {
+        if (reader.matches("page")) {
             eTag = "jsp:directive.page";
-           if (isTagFile) {
-               err.jspError(reader.mark(), "jsp.error.directive.istagfile",
-                                           "&lt;" + eTag);
-           }
-           parsePageDirective(parent);
-       } else if (reader.matches("include")) {
+            if (isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.istagfile",
+                        "&lt;" + eTag);
+            }
+            parsePageDirective(parent);
+        } else if (reader.matches("include")) {
             eTag = "jsp:directive.include";
-           parseIncludeDirective(parent);
-       } else if (reader.matches("tag")) {
+            parseIncludeDirective(parent);
+        } else if (reader.matches("tag")) {
             eTag = "jsp:directive.tag";
-           if (!isTagFile) {
-               err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
-                                           "&lt;" + eTag);
-           }
-           parseTagDirective(parent);
-       } else if (reader.matches("attribute")) {
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                        "&lt;" + eTag);
+            }
+            parseTagDirective(parent);
+        } else if (reader.matches("attribute")) {
             eTag = "jsp:directive.attribute";
-           if (!isTagFile) {
-               err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
-                                           "&lt;" + eTag);
-           }
-           parseAttributeDirective(parent);
-       } else if (reader.matches("variable")) {
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                        "&lt;" + eTag);
+            }
+            parseAttributeDirective(parent);
+        } else if (reader.matches("variable")) {
             eTag = "jsp:directive.variable";
-           if (!isTagFile) {
-               err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
-                                           "&lt;" + eTag);
-           }
-           parseVariableDirective(parent);
-       } else {
-           err.jspError(reader.mark(), "jsp.error.invalid.directive");
-       }
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
+                        "&lt;" + eTag);
+            }
+            parseVariableDirective(parent);
+        } else {
+            err.jspError(reader.mark(), "jsp.error.invalid.directive");
+        }
 
-       reader.skipSpaces();
-        if( reader.matches( ">" ) ) {
+        reader.skipSpaces();
+        if (reader.matches(">")) {
             reader.skipSpaces();
-            if( !reader.matchesETag( eTag ) ) {
-                err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag );
+            if (!reader.matchesETag(eTag)) {
+                err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag);
             }
-        }
-        else if( !reader.matches( "/>" ) ) {
-            err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag );
+        } else if (!reader.matches("/>")) {
+            err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag);
         }
     }
 
     /*
-     * Parses a tag directive with the following syntax:
-     *   PageDirective ::= ( S Attribute)*
+     * Parses a tag directive with the following syntax: PageDirective ::= ( S
+     * Attribute)*
      */
     private void parseTagDirective(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	Node.TagDirective n = new Node.TagDirective(attrs, start, parent);
+        Attributes attrs = parseAttributes();
+        Node.TagDirective n = new Node.TagDirective(attrs, start, parent);
 
         /*
          * A page directive may contain multiple 'import' attributes, each of
-         * which consists of a comma-separated list of package names.
-         * Store each list with the node, where it is parsed.
+         * which consists of a comma-separated list of package names. Store each
+         * list with the node, where it is parsed.
          */
         for (int i = 0; i < attrs.getLength(); i++) {
             if ("import".equals(attrs.getQName(i))) {
@@ -614,77 +588,74 @@
 
     /*
      * Parses a attribute directive with the following syntax:
-     *   AttributeDirective ::= ( S Attribute)*
+     * AttributeDirective ::= ( S Attribute)*
      */
     private void parseAttributeDirective(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	Node.AttributeDirective n =
-		new Node.AttributeDirective(attrs, start, parent);
+        Attributes attrs = parseAttributes();
+        Node.AttributeDirective n = new Node.AttributeDirective(attrs, start,
+                parent);
     }
 
     /*
-     * Parses a variable directive with the following syntax:
-     *   PageDirective ::= ( S Attribute)*
+     * Parses a variable directive with the following syntax: PageDirective ::= (
+     * S Attribute)*
      */
     private void parseVariableDirective(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	Node.VariableDirective n =
-		new Node.VariableDirective(attrs, start, parent);
+        Attributes attrs = parseAttributes();
+        Node.VariableDirective n = new Node.VariableDirective(attrs, start,
+                parent);
     }
 
     /*
      * JSPCommentBody ::= (Char* - (Char* '--%>')) '--%>'
      */
-    private void parseComment(Node parent) throws JasperException {	
-	start = reader.mark();
-	Mark stop = reader.skipUntil("--%>");
-	if (stop == null) {
-	    err.jspError(start, "jsp.error.unterminated", "&lt;%--");
-	}
+    private void parseComment(Node parent) throws JasperException {
+        start = reader.mark();
+        Mark stop = reader.skipUntil("--%>");
+        if (stop == null) {
+            err.jspError(start, "jsp.error.unterminated", "&lt;%--");
+        }
 
-	new Node.Comment(reader.getText(start, stop), start, parent);
+        new Node.Comment(reader.getText(start, stop), start, parent);
     }
 
     /*
      * DeclarationBody ::= (Char* - (char* '%>')) '%>'
      */
     private void parseDeclaration(Node parent) throws JasperException {
-	start = reader.mark();
-	Mark stop = reader.skipUntil("%>");
-	if (stop == null) {
-	    err.jspError(start, "jsp.error.unterminated", "&lt;%!");
-	}
-
-	new Node.Declaration(parseScriptText(reader.getText(start, stop)),
-			     start, parent);
+        start = reader.mark();
+        Mark stop = reader.skipUntil("%>");
+        if (stop == null) {
+            err.jspError(start, "jsp.error.unterminated", "&lt;%!");
+        }
+
+        new Node.Declaration(parseScriptText(reader.getText(start, stop)),
+                start, parent);
     }
 
     /*
-     * XMLDeclarationBody ::=   ( S? '/>' )
-     *                        | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag
-     *                        | <TRANSLATION_ERROR>
-     * CDSect ::= CDStart CData CDEnd
-     * CDStart ::= '<![CDATA['
-     * CData ::= (Char* - (Char* ']]>' Char*))
-     * CDEnd ::= ']]>'
+     * XMLDeclarationBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<'))
+     * CDSect?)* ETag | <TRANSLATION_ERROR> CDSect ::= CDStart CData CDEnd
+     * CDStart ::= '<![CDATA[' CData ::= (Char* - (Char* ']]>' Char*)) CDEnd
+     * ::= ']]>'
      */
     private void parseXMLDeclaration(Node parent) throws JasperException {
         reader.skipSpaces();
-        if( !reader.matches( "/>" ) ) {
-            if( !reader.matches( ">" ) ) {
+        if (!reader.matches("/>")) {
+            if (!reader.matches(">")) {
                 err.jspError(start, "jsp.error.unterminated",
                         "&lt;jsp:declaration&gt;");
             }
-	    Mark stop;
+            Mark stop;
             String text;
             while (true) {
                 start = reader.mark();
                 stop = reader.skipUntil("<");
                 if (stop == null) {
                     err.jspError(start, "jsp.error.unterminated",
-                        "&lt;jsp:declaration&gt;");
+                            "&lt;jsp:declaration&gt;");
                 }
-		text = parseScriptText(reader.getText(start, stop));
+                text = parseScriptText(reader.getText(start, stop));
                 new Node.Declaration(text, start, parent);
                 if (reader.matches("![CDATA[")) {
                     start = reader.mark();
@@ -692,15 +663,14 @@
                     if (stop == null) {
                         err.jspError(start, "jsp.error.unterminated", "CDATA");
                     }
-		    text = parseScriptText(reader.getText(start, stop));
+                    text = parseScriptText(reader.getText(start, stop));
                     new Node.Declaration(text, start, parent);
-                }
-                else {
+                } else {
                     break;
                 }
-	    }
-		
-            if (!reader.matchesETagWithoutLessThan( "jsp:declaration" ) ) {
+            }
+
+            if (!reader.matchesETagWithoutLessThan("jsp:declaration")) {
                 err.jspError(start, "jsp.error.unterminated",
                         "&lt;jsp:declaration&gt;");
             }
@@ -711,27 +681,26 @@
      * ExpressionBody ::= (Char* - (char* '%>')) '%>'
      */
     private void parseExpression(Node parent) throws JasperException {
-	start = reader.mark();
-	Mark stop = reader.skipUntil("%>");
-	if (stop == null) {
-	    err.jspError(start, "jsp.error.unterminated", "&lt;%=");
-	}
+        start = reader.mark();
+        Mark stop = reader.skipUntil("%>");
+        if (stop == null) {
+            err.jspError(start, "jsp.error.unterminated", "&lt;%=");
+        }
 
-	new Node.Expression(parseScriptText(reader.getText(start, stop)),
-			    start, parent);
+        new Node.Expression(parseScriptText(reader.getText(start, stop)),
+                start, parent);
     }
 
     /*
-     * XMLExpressionBody ::=   ( S? '/>' )
-     *                       | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag )
-     *                       | <TRANSLATION_ERROR>
+     * XMLExpressionBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<'))
+     * CDSect?)* ETag ) | <TRANSLATION_ERROR>
      */
     private void parseXMLExpression(Node parent) throws JasperException {
         reader.skipSpaces();
-        if( !reader.matches( "/>" ) ) {
-            if( !reader.matches( ">" ) ) {
+        if (!reader.matches("/>")) {
+            if (!reader.matches(">")) {
                 err.jspError(start, "jsp.error.unterminated",
-                    "&lt;jsp:expression&gt;");
+                        "&lt;jsp:expression&gt;");
             }
             Mark stop;
             String text;
@@ -740,7 +709,7 @@
                 stop = reader.skipUntil("<");
                 if (stop == null) {
                     err.jspError(start, "jsp.error.unterminated",
-                        "&lt;jsp:expression&gt;");
+                            "&lt;jsp:expression&gt;");
                 }
                 text = parseScriptText(reader.getText(start, stop));
                 new Node.Expression(text, start, parent);
@@ -752,32 +721,29 @@
                     }
                     text = parseScriptText(reader.getText(start, stop));
                     new Node.Expression(text, start, parent);
-                }
-                else {
+                } else {
                     break;
                 }
             }
-            if (!reader.matchesETagWithoutLessThan( "jsp:expression" )) {
+            if (!reader.matchesETagWithoutLessThan("jsp:expression")) {
                 err.jspError(start, "jsp.error.unterminated",
-                    "&lt;jsp:expression&gt;");
+                        "&lt;jsp:expression&gt;");
             }
         }
     }
 
     /*
-     * ELExpressionBody
-     * (following "${" to first unquoted "}")
-     * // XXX add formal production and confirm implementation against it,
-     * //     once it's decided
+     * ELExpressionBody (following "${" to first unquoted "}") // XXX add formal
+     * production and confirm implementation against it, // once it's decided
      */
-    private void parseELExpression(Node parent) throws JasperException {
+    private void parseELExpression(Node parent, char type) throws JasperException {
         start = reader.mark();
         Mark last = null;
         boolean singleQuoted = false, doubleQuoted = false;
         int currentChar;
         do {
             // XXX could move this logic to JspReader
-            last = reader.mark();               // XXX somewhat wasteful
+            last = reader.mark(); // XXX somewhat wasteful
             currentChar = reader.nextChar();
             if (currentChar == '\\' && (singleQuoted || doubleQuoted)) {
                 // skip character following '\' within quotes
@@ -785,41 +751,40 @@
                 currentChar = reader.nextChar();
             }
             if (currentChar == -1)
-                err.jspError(start, "jsp.error.unterminated", "${");
+                err.jspError(start, "jsp.error.unterminated", type + "{");
             if (currentChar == '"')
                 doubleQuoted = !doubleQuoted;
             if (currentChar == '\'')
                 singleQuoted = !singleQuoted;
         } while (currentChar != '}' || (singleQuoted || doubleQuoted));
 
-        new Node.ELExpression(reader.getText(start, last), start, parent);
+        new Node.ELExpression(type, reader.getText(start, last), start, parent);
     }
 
     /*
      * ScriptletBody ::= (Char* - (char* '%>')) '%>'
      */
     private void parseScriptlet(Node parent) throws JasperException {
-	start = reader.mark();
-	Mark stop = reader.skipUntil("%>");
-	if (stop == null) {
-	    err.jspError(start, "jsp.error.unterminated", "&lt;%");
-	}
+        start = reader.mark();
+        Mark stop = reader.skipUntil("%>");
+        if (stop == null) {
+            err.jspError(start, "jsp.error.unterminated", "&lt;%");
+        }
 
-	new Node.Scriptlet(parseScriptText(reader.getText(start, stop)),
-			   start, parent);
+        new Node.Scriptlet(parseScriptText(reader.getText(start, stop)), start,
+                parent);
     }
 
     /*
-     * XMLScriptletBody ::=   ( S? '/>' )
-     *                      | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag )
-     *                      | <TRANSLATION_ERROR>
+     * XMLScriptletBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<'))
+     * CDSect?)* ETag ) | <TRANSLATION_ERROR>
      */
     private void parseXMLScriptlet(Node parent) throws JasperException {
         reader.skipSpaces();
-        if( !reader.matches( "/>" ) ) {
-            if( !reader.matches( ">" ) ) {
+        if (!reader.matches("/>")) {
+            if (!reader.matches(">")) {
                 err.jspError(start, "jsp.error.unterminated",
-                    "&lt;jsp:scriptlet&gt;");
+                        "&lt;jsp:scriptlet&gt;");
             }
             Mark stop;
             String text;
@@ -828,7 +793,7 @@
                 stop = reader.skipUntil("<");
                 if (stop == null) {
                     err.jspError(start, "jsp.error.unterminated",
-                        "&lt;jsp:scriptlet&gt;");
+                            "&lt;jsp:scriptlet&gt;");
                 }
                 text = parseScriptText(reader.getText(start, stop));
                 new Node.Scriptlet(text, start, parent);
@@ -840,623 +805,545 @@
                     }
                     text = parseScriptText(reader.getText(start, stop));
                     new Node.Scriptlet(text, start, parent);
-                }
-                else {
+                } else {
                     break;
                 }
             }
 
-            if (!reader.matchesETagWithoutLessThan( "jsp:scriptlet" )) {
+            if (!reader.matchesETagWithoutLessThan("jsp:scriptlet")) {
                 err.jspError(start, "jsp.error.unterminated",
-                    "&lt;jsp:scriptlet&gt;");
+                        "&lt;jsp:scriptlet&gt;");
             }
         }
     }
-	
+
     /**
      * Param ::= '<jsp:param' S Attributes S? EmptyBody S?
      */
     private void parseParam(Node parent) throws JasperException {
-	if (!reader.matches("<jsp:param")) {
-	    err.jspError(reader.mark(), "jsp.error.paramexpected");
-	}
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
-        
-        Node paramActionNode = new Node.ParamAction( attrs, start, parent );
-        
-        parseEmptyBody( paramActionNode, "jsp:param" );
-        
+        if (!reader.matches("<jsp:param")) {
+            err.jspError(reader.mark(), "jsp.error.paramexpected");
+        }
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
+        Node paramActionNode = new Node.ParamAction(attrs, start, parent);
+
+        parseEmptyBody(paramActionNode, "jsp:param");
+
         reader.skipSpaces();
     }
 
     /*
-     * For Include:
-     * StdActionContent ::= Attributes ParamBody
-     *
-     * ParamBody ::=   EmptyBody
-     *               | ( '>' S? ( '<jsp:attribute' NamedAttributes )?
-     *                   '<jsp:body'
-     *                   (JspBodyParam | <TRANSLATION_ERROR> )
-     *                   S? ETag
-     *                 )
-     *               | ( '>' S? Param* ETag )
-     *
-     * EmptyBody ::=   '/>'
-     *               | ( '>' ETag )
-     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
-     *
+     * For Include: StdActionContent ::= Attributes ParamBody
+     * 
+     * ParamBody ::= EmptyBody | ( '>' S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body'
+     * (JspBodyParam | <TRANSLATION_ERROR> ) S? ETag ) | ( '>' S? Param* ETag )
+     * 
+     * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
+     * NamedAttributes ETag )
+     * 
      * JspBodyParam ::= S? '>' Param* '</jsp:body>'
      */
     private void parseInclude(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
+        Node includeNode = new Node.IncludeAction(attrs, start, parent);
 
-        Node includeNode = new Node.IncludeAction( attrs, start, parent );
-        
-        parseOptionalBody(includeNode, "jsp:include", 
-			  JAVAX_BODY_CONTENT_PARAM);
+        parseOptionalBody(includeNode, "jsp:include", JAVAX_BODY_CONTENT_PARAM);
     }
 
     /*
-     * For Forward:
-     * StdActionContent ::= Attributes ParamBody
+     * For Forward: StdActionContent ::= Attributes ParamBody
      */
     private void parseForward(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
+        Node forwardNode = new Node.ForwardAction(attrs, start, parent);
 
-        Node forwardNode = new Node.ForwardAction( attrs, start, parent );
-        
-        parseOptionalBody(forwardNode, "jsp:forward",
-			  JAVAX_BODY_CONTENT_PARAM);
+        parseOptionalBody(forwardNode, "jsp:forward", JAVAX_BODY_CONTENT_PARAM);
     }
 
     private void parseInvoke(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
 
         Node invokeNode = new Node.InvokeAction(attrs, start, parent);
-        
+
         parseEmptyBody(invokeNode, "jsp:invoke");
     }
 
     private void parseDoBody(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
 
         Node doBodyNode = new Node.DoBodyAction(attrs, start, parent);
-        
+
         parseEmptyBody(doBodyNode, "jsp:doBody");
     }
 
     private void parseElement(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
 
         Node elementNode = new Node.JspElement(attrs, start, parent);
-        
-        parseOptionalBody( elementNode, "jsp:element", 
-            TagInfo.BODY_CONTENT_JSP );
+
+        parseOptionalBody(elementNode, "jsp:element", TagInfo.BODY_CONTENT_JSP);
     }
 
     /*
-     * For GetProperty:
-     * StdActionContent ::= Attributes EmptyBody
+     * For GetProperty: StdActionContent ::= Attributes EmptyBody
      */
     private void parseGetProperty(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
+        Node getPropertyNode = new Node.GetProperty(attrs, start, parent);
 
-        Node getPropertyNode = new Node.GetProperty( attrs, start, parent );
-        
         parseOptionalBody(getPropertyNode, "jsp:getProperty",
-			  TagInfo.BODY_CONTENT_EMPTY);
+                TagInfo.BODY_CONTENT_EMPTY);
     }
 
     /*
-     * For SetProperty:
-     * StdActionContent ::= Attributes EmptyBody
+     * For SetProperty: StdActionContent ::= Attributes EmptyBody
      */
     private void parseSetProperty(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
+        Node setPropertyNode = new Node.SetProperty(attrs, start, parent);
 
-        Node setPropertyNode = new Node.SetProperty( attrs, start, parent );
-        
         parseOptionalBody(setPropertyNode, "jsp:setProperty",
-			  TagInfo.BODY_CONTENT_EMPTY);
+                TagInfo.BODY_CONTENT_EMPTY);
     }
 
     /*
-     * EmptyBody ::=   '/>'
-     *               | ( '>' ETag )
-     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+     * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
+     * NamedAttributes ETag )
      */
-    private void parseEmptyBody( Node parent, String tag ) 
-        throws JasperException
-    {
-	if( reader.matches("/>") ) {
+    private void parseEmptyBody(Node parent, String tag) throws JasperException {
+        if (reader.matches("/>")) {
             // Done
-        }
-        else if( reader.matches( ">" ) ) {
-            if( reader.matchesETag( tag ) ) {
+        } else if (reader.matches(">")) {
+            if (reader.matchesETag(tag)) {
                 // Done
-            }
-            else if( reader.matchesOptionalSpacesFollowedBy(
-                "<jsp:attribute" ) )
-            {
+            } else if (reader.matchesOptionalSpacesFollowedBy("<jsp:attribute")) {
                 // Parse the one or more named attribute nodes
-                parseNamedAttributes( parent );
-                if( !reader.matchesETag( tag ) ) {
+                parseNamedAttributes(parent);
+                if (!reader.matchesETag(tag)) {
                     // Body not allowed
                     err.jspError(reader.mark(),
-                        "jsp.error.jspbody.emptybody.only",
-                        "&lt;" + tag );
+                            "jsp.error.jspbody.emptybody.only", "&lt;" + tag);
                 }
-            }
-            else {
+            } else {
                 err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only",
-                    "&lt;" + tag );
+                        "&lt;" + tag);
             }
-        }
-        else {
-	    err.jspError(reader.mark(), "jsp.error.unterminated",
-                "&lt;" + tag );
+        } else {
+            err.jspError(reader.mark(), "jsp.error.unterminated", "&lt;" + tag);
         }
     }
 
     /*
-     * For UseBean:
-     * StdActionContent ::= Attributes OptionalBody
+     * For UseBean: StdActionContent ::= Attributes OptionalBody
      */
     private void parseUseBean(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
-        
-        Node useBeanNode = new Node.UseBean( attrs, start, parent );
-        
-        parseOptionalBody( useBeanNode, "jsp:useBean", 
-            TagInfo.BODY_CONTENT_JSP );
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
+        Node useBeanNode = new Node.UseBean(attrs, start, parent);
+
+        parseOptionalBody(useBeanNode, "jsp:useBean", TagInfo.BODY_CONTENT_JSP);
     }
 
     /*
-     * Parses OptionalBody, but also reused to parse bodies for plugin
-     * and param since the syntax is identical (the only thing that
-     * differs substantially is how to process the body, and thus
-     * we accept the body type as a parameter).
-     *
+     * Parses OptionalBody, but also reused to parse bodies for plugin and param
+     * since the syntax is identical (the only thing that differs substantially
+     * is how to process the body, and thus we accept the body type as a
+     * parameter).
+     * 
      * OptionalBody ::= EmptyBody | ActionBody
-     *
+     * 
      * ScriptlessOptionalBody ::= EmptyBody | ScriptlessActionBody
-     *
+     * 
      * TagDependentOptionalBody ::= EmptyBody | TagDependentActionBody
-     *
-     * EmptyBody ::=   '/>'
-     *               | ( '>' ETag )
-     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
-     *
-     * ActionBody ::=   JspAttributeAndBody
-     *                | ( '>' Body ETag )
-     *
-     * ScriptlessActionBody ::=   JspAttributeAndBody 
-     *                          | ( '>' ScriptlessBody ETag )
-     * 
-     * TagDependentActionBody ::=   JspAttributeAndBody
-     *                            | ( '>' TagDependentBody ETag )
-     *
-     */
-    private void parseOptionalBody( Node parent, String tag, String bodyType ) 
-        throws JasperException 
-    {
-	if (reader.matches("/>")) {
-	    // EmptyBody
-	    return;
-	}
-
-	if (!reader.matches(">")) {
-	    err.jspError(reader.mark(), "jsp.error.unterminated",
-			 "&lt;" + tag );
-	}
-        
-        if( reader.matchesETag( tag ) ) {
+     * 
+     * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
+     * NamedAttributes ETag )
+     * 
+     * ActionBody ::= JspAttributeAndBody | ( '>' Body ETag )
+     * 
+     * ScriptlessActionBody ::= JspAttributeAndBody | ( '>' ScriptlessBody ETag )
+     * 
+     * TagDependentActionBody ::= JspAttributeAndBody | ( '>' TagDependentBody
+     * ETag )
+     * 
+     */
+    private void parseOptionalBody(Node parent, String tag, String bodyType)
+            throws JasperException {
+        if (reader.matches("/>")) {
             // EmptyBody
             return;
         }
-        
-        if( !parseJspAttributeAndBody( parent, tag, bodyType ) ) {
+
+        if (!reader.matches(">")) {
+            err.jspError(reader.mark(), "jsp.error.unterminated", "&lt;" + tag);
+        }
+
+        if (reader.matchesETag(tag)) {
+            // EmptyBody
+            return;
+        }
+
+        if (!parseJspAttributeAndBody(parent, tag, bodyType)) {
             // Must be ( '>' # Body ETag )
-            parseBody(parent, tag, bodyType );
+            parseBody(parent, tag, bodyType);
         }
     }
-    
+
     /**
-     * Attempts to parse 'JspAttributeAndBody' production.  Returns true if
-     * it matched, or false if not.  Assumes EmptyBody is okay as well.
-     *
-     * JspAttributeAndBody ::=
-     *                  ( '>' # S? ( '<jsp:attribute' NamedAttributes )?
-     *                    '<jsp:body'
-     *                    ( JspBodyBody | <TRANSLATION_ERROR> )
-     *                    S? ETag
-     *                  )
-     */
-    private boolean parseJspAttributeAndBody( Node parent, String tag, 
-        String bodyType ) 
-        throws JasperException
-    {
+     * Attempts to parse 'JspAttributeAndBody' production. Returns true if it
+     * matched, or false if not. Assumes EmptyBody is okay as well.
+     * 
+     * JspAttributeAndBody ::= ( '>' # S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body' (
+     * JspBodyBody | <TRANSLATION_ERROR> ) S? ETag )
+     */
+    private boolean parseJspAttributeAndBody(Node parent, String tag,
+            String bodyType) throws JasperException {
         boolean result = false;
-        
-        if( reader.matchesOptionalSpacesFollowedBy( "<jsp:attribute" ) ) {
+
+        if (reader.matchesOptionalSpacesFollowedBy("<jsp:attribute")) {
             // May be an EmptyBody, depending on whether
             // There's a "<jsp:body" before the ETag
-            
+
             // First, parse <jsp:attribute> elements:
-            parseNamedAttributes( parent );
-            
+            parseNamedAttributes(parent);
+
             result = true;
         }
-        
-        if( reader.matchesOptionalSpacesFollowedBy( "<jsp:body" ) ) {
+
+        if (reader.matchesOptionalSpacesFollowedBy("<jsp:body")) {
             // ActionBody
-            parseJspBody( parent, bodyType );
+            parseJspBody(parent, bodyType);
             reader.skipSpaces();
-            if( !reader.matchesETag( tag ) ) {
-                err.jspError(reader.mark(), "jsp.error.unterminated", 
-                    "&lt;" + tag );
+            if (!reader.matchesETag(tag)) {
+                err.jspError(reader.mark(), "jsp.error.unterminated", "&lt;"
+                        + tag);
             }
-            
+
             result = true;
-        }
-        else if( result && !reader.matchesETag( tag ) ) {
+        } else if (result && !reader.matchesETag(tag)) {
             // If we have <jsp:attribute> but something other than
             // <jsp:body> or the end tag, translation error.
-            err.jspError(reader.mark(), "jsp.error.jspbody.required", 
-                "&lt;" + tag );
+            err.jspError(reader.mark(), "jsp.error.jspbody.required", "&lt;"
+                    + tag);
         }
-        
+
         return result;
     }
 
     /*
-     * Params ::=  `>' S?
-     *              (   ( `<jsp:body>'
-     *                    ( ( S? Param+ S? `</jsp:body>' )
-     *                      | <TRANSLATION_ERROR>
-     *                    )
-     *                  )
-     *                | Param+
-     *              )
-     *              '</jsp:params>'
+     * Params ::= `>' S? ( ( `<jsp:body>' ( ( S? Param+ S? `</jsp:body>' ) |
+     * <TRANSLATION_ERROR> ) ) | Param+ ) '</jsp:params>'
      */
     private void parseJspParams(Node parent) throws JasperException {
-	Node jspParamsNode = new Node.ParamsAction(start, parent);
-	parseOptionalBody(jspParamsNode, "jsp:params",
-			  JAVAX_BODY_CONTENT_PARAM );
+        Node jspParamsNode = new Node.ParamsAction(start, parent);
+        parseOptionalBody(jspParamsNode, "jsp:params", JAVAX_BODY_CONTENT_PARAM);
     }
 
     /*
-     * Fallback ::=   '/>'
-     *               | ( `>' S? `<jsp:body>'
-     *                   (   ( S?
-     *                         ( Char* - ( Char* `</jsp:body>' ) )
-     *                         `</jsp:body>' S?
-     *                       )
-     *                     | <TRANSLATION_ERROR>
-     *                   )
-     *                   `</jsp:fallback>'
-     *                 )
-     *               | ( '>'
-     *                   ( Char* - ( Char* '</jsp:fallback>' ) )
-     *                   '</jsp:fallback>'
-     *                 )
+     * Fallback ::= '/>' | ( `>' S? `<jsp:body>' ( ( S? ( Char* - ( Char* `</jsp:body>' ) ) `</jsp:body>'
+     * S? ) | <TRANSLATION_ERROR> ) `</jsp:fallback>' ) | ( '>' ( Char* - (
+     * Char* '</jsp:fallback>' ) ) '</jsp:fallback>' )
      */
     private void parseFallBack(Node parent) throws JasperException {
-	Node fallBackNode = new Node.FallBackAction(start, parent);
-	parseOptionalBody(fallBackNode, "jsp:fallback", 
-			  JAVAX_BODY_CONTENT_TEMPLATE_TEXT);
+        Node fallBackNode = new Node.FallBackAction(start, parent);
+        parseOptionalBody(fallBackNode, "jsp:fallback",
+                JAVAX_BODY_CONTENT_TEMPLATE_TEXT);
     }
 
     /*
-     * For Plugin:
-     * StdActionContent ::= Attributes PluginBody
-     *
-     * PluginBody ::=   EmptyBody 
-     *                | ( '>' S? ( '<jsp:attribute' NamedAttributes )?
-     *                    '<jsp:body'
-     *                    ( JspBodyPluginTags | <TRANSLATION_ERROR> )
-     *                    S? ETag
-     *                  )
-     *                | ( '>' S? PluginTags ETag )
-     *
-     * EmptyBody ::=   '/>'
-     *               | ( '>' ETag )
-     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
-     *
+     * For Plugin: StdActionContent ::= Attributes PluginBody
+     * 
+     * PluginBody ::= EmptyBody | ( '>' S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body' (
+     * JspBodyPluginTags | <TRANSLATION_ERROR> ) S? ETag ) | ( '>' S? PluginTags
+     * ETag )
+     * 
+     * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
+     * NamedAttributes ETag )
+     * 
      */
     private void parsePlugin(Node parent) throws JasperException {
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
-        
-	Node pluginNode = new Node.PlugIn(attrs, start, parent);
-        
-        parseOptionalBody( pluginNode, "jsp:plugin", 
-            JAVAX_BODY_CONTENT_PLUGIN );
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
+        Node pluginNode = new Node.PlugIn(attrs, start, parent);
+
+        parseOptionalBody(pluginNode, "jsp:plugin", JAVAX_BODY_CONTENT_PLUGIN);
     }
 
     /*
-     * PluginTags ::= ( '<jsp:params' Params S? )?
-     *                ( '<jsp:fallback' Fallback? S? )?
+     * PluginTags ::= ( '<jsp:params' Params S? )? ( '<jsp:fallback' Fallback?
+     * S? )?
      */
-    private void parsePluginTags( Node parent ) throws JasperException {
+    private void parsePluginTags(Node parent) throws JasperException {
         reader.skipSpaces();
-        
-        if( reader.matches( "<jsp:params" ) ) {
-            parseJspParams( parent );
+
+        if (reader.matches("<jsp:params")) {
+            parseJspParams(parent);
             reader.skipSpaces();
         }
-        
-        if( reader.matches( "<jsp:fallback" ) ) {
-            parseFallBack( parent );
+
+        if (reader.matches("<jsp:fallback")) {
+            parseFallBack(parent);
             reader.skipSpaces();
         }
     }
-        
+
     /*
-     * StandardAction ::=   'include'       StdActionContent
-     *                    | 'forward'       StdActionContent
-     *                    | 'invoke'        StdActionContent
-     *                    | 'doBody'        StdActionContent
-     *                    | 'getProperty'   StdActionContent
-     *                    | 'setProperty'   StdActionContent
-     *                    | 'useBean'       StdActionContent
-     *                    | 'plugin'        StdActionContent
-     *                    | 'element'       StdActionContent
+     * StandardAction ::= 'include' StdActionContent | 'forward'
+     * StdActionContent | 'invoke' StdActionContent | 'doBody' StdActionContent |
+     * 'getProperty' StdActionContent | 'setProperty' StdActionContent |
+     * 'useBean' StdActionContent | 'plugin' StdActionContent | 'element'
+     * StdActionContent
      */
     private void parseStandardAction(Node parent) throws JasperException {
-	Mark start = reader.mark();
+        Mark start = reader.mark();
 
-	if (reader.matches(INCLUDE_ACTION)) {
-	    parseInclude(parent);
-	} else if (reader.matches(FORWARD_ACTION)) {
-	    parseForward(parent);
-	} else if (reader.matches(INVOKE_ACTION)) {
-	    if (!isTagFile) {
-		err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
-			     "&lt;jsp:invoke");
-	    }
-	    parseInvoke(parent);
-	} else if (reader.matches(DOBODY_ACTION)) {
-	    if (!isTagFile) {
-		err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
-			     "&lt;jsp:doBody");
-	    }
-	    parseDoBody(parent);
-	} else if (reader.matches(GET_PROPERTY_ACTION)) {
-	    parseGetProperty(parent);
-	} else if (reader.matches(SET_PROPERTY_ACTION)) {
-	    parseSetProperty(parent);
-	} else if (reader.matches(USE_BEAN_ACTION)) {
-	    parseUseBean(parent);
-	} else if (reader.matches(PLUGIN_ACTION)) {
-	    parsePlugin(parent);
-	} else if (reader.matches(ELEMENT_ACTION)) {
-	    parseElement(parent);
-	} else if (reader.matches(ATTRIBUTE_ACTION)) {
-	    err.jspError(start, "jsp.error.namedAttribute.invalidUse");
-	} else if (reader.matches(BODY_ACTION)) {
-	    err.jspError(start, "jsp.error.jspbody.invalidUse");
-	} else if (reader.matches(FALLBACK_ACTION)) {
-	    err.jspError(start, "jsp.error.fallback.invalidUse");
-	} else if (reader.matches(PARAMS_ACTION)) {
-	    err.jspError(start, "jsp.error.params.invalidUse");
-	} else if (reader.matches(PARAM_ACTION)) {
-	    err.jspError(start, "jsp.error.param.invalidUse");
-	} else if (reader.matches(OUTPUT_ACTION)) {
-	    err.jspError(start, "jsp.error.jspoutput.invalidUse");
-	} else {
-	    err.jspError(start, "jsp.error.badStandardAction");
-	}
+        if (reader.matches(INCLUDE_ACTION)) {
+            parseInclude(parent);
+        } else if (reader.matches(FORWARD_ACTION)) {
+            parseForward(parent);
+        } else if (reader.matches(INVOKE_ACTION)) {
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
+                        "&lt;jsp:invoke");
+            }
+            parseInvoke(parent);
+        } else if (reader.matches(DOBODY_ACTION)) {
+            if (!isTagFile) {
+                err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
+                        "&lt;jsp:doBody");
+            }
+            parseDoBody(parent);
+        } else if (reader.matches(GET_PROPERTY_ACTION)) {
+            parseGetProperty(parent);
+        } else if (reader.matches(SET_PROPERTY_ACTION)) {
+            parseSetProperty(parent);
+        } else if (reader.matches(USE_BEAN_ACTION)) {
+            parseUseBean(parent);
+        } else if (reader.matches(PLUGIN_ACTION)) {
+            parsePlugin(parent);
+        } else if (reader.matches(ELEMENT_ACTION)) {
+            parseElement(parent);
+        } else if (reader.matches(ATTRIBUTE_ACTION)) {
+            err.jspError(start, "jsp.error.namedAttribute.invalidUse");
+        } else if (reader.matches(BODY_ACTION)) {
+            err.jspError(start, "jsp.error.jspbody.invalidUse");
+        } else if (reader.matches(FALLBACK_ACTION)) {
+            err.jspError(start, "jsp.error.fallback.invalidUse");
+        } else if (reader.matches(PARAMS_ACTION)) {
+            err.jspError(start, "jsp.error.params.invalidUse");
+        } else if (reader.matches(PARAM_ACTION)) {
+            err.jspError(start, "jsp.error.param.invalidUse");
+        } else if (reader.matches(OUTPUT_ACTION)) {
+            err.jspError(start, "jsp.error.jspoutput.invalidUse");
+        } else {
+            err.jspError(start, "jsp.error.badStandardAction");
+        }
     }
 
     /*
      * # '<' CustomAction CustomActionBody
-     *
+     * 
      * CustomAction ::= TagPrefix ':' CustomActionName
-     *
+     * 
      * TagPrefix ::= Name
-     *
+     * 
      * CustomActionName ::= Name
-     *
-     * CustomActionBody ::=   ( Attributes CustomActionEnd )
-     *                      | <TRANSLATION_ERROR>
-     *
+     * 
+     * CustomActionBody ::= ( Attributes CustomActionEnd ) | <TRANSLATION_ERROR>
+     * 
      * Attributes ::= ( S Attribute )* S?
-     *
-     * CustomActionEnd ::=   CustomActionTagDependent
-     *                     | CustomActionJSPContent
-     *                     | CustomActionScriptlessContent
-     *
+     * 
+     * CustomActionEnd ::= CustomActionTagDependent | CustomActionJSPContent |
+     * CustomActionScriptlessContent
+     * 
      * CustomActionTagDependent ::= TagDependentOptionalBody
-     *
+     * 
      * CustomActionJSPContent ::= OptionalBody
-     *
+     * 
      * CustomActionScriptlessContent ::= ScriptlessOptionalBody
      */
     private boolean parseCustomTag(Node parent) throws JasperException {
 
-	if (reader.peekChar() != '<') {
-	    return false;
-	}
+        if (reader.peekChar() != '<') {
+            return false;
+        }
 
         // Parse 'CustomAction' production (tag prefix and custom action name)
-	reader.nextChar();	// skip '<'
-	String tagName = reader.parseToken(false);
-	int i = tagName.indexOf(':');
-	if (i == -1) {
-	    reader.reset(start);
-	    return false;
-	}
+        reader.nextChar(); // skip '<'
+        String tagName = reader.parseToken(false);
+        int i = tagName.indexOf(':');
+        if (i == -1) {
+            reader.reset(start);
+            return false;
+        }
 
-	String prefix = tagName.substring(0, i);
-	String shortTagName = tagName.substring(i+1);
+        String prefix = tagName.substring(0, i);
+        String shortTagName = tagName.substring(i + 1);
 
-	// Check if this is a user-defined tag.
-	String uri = pageInfo.getURI(prefix);
+        // Check if this is a user-defined tag.
+        String uri = pageInfo.getURI(prefix);
         if (uri == null) {
-	    reader.reset(start);
+            reader.reset(start);
             // Remember the prefix for later error checking
             pageInfo.putNonCustomTagPrefix(prefix, reader.mark());
-	    return false;
-	}
+            return false;
+        }
 
         TagLibraryInfo tagLibInfo = pageInfo.getTaglib(uri);
-	TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
-	TagFileInfo tagFileInfo = tagLibInfo.getTagFile(shortTagName);
-	if (tagInfo == null && tagFileInfo == null) {
-	    err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
-	}
-	Class tagHandlerClass = null;
-	if (tagInfo != null) {
-	    // Must be a classic tag, load it here.
-	    // tag files will be loaded later, in TagFileProcessor
-	    String handlerClassName = tagInfo.getTagClassName();
-	    try {
-	        tagHandlerClass = ctxt.getClassLoader().loadClass(handlerClassName);
-	    } catch (Exception e) {
-	        err.jspError(start, "jsp.error.loadclass.taghandler",
-			     handlerClassName, tagName);
-	    }
-	}
+        TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
+        TagFileInfo tagFileInfo = tagLibInfo.getTagFile(shortTagName);
+        if (tagInfo == null && tagFileInfo == null) {
+            err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
+        }
+        Class tagHandlerClass = null;
+        if (tagInfo != null) {
+            // Must be a classic tag, load it here.
+            // tag files will be loaded later, in TagFileProcessor
+            String handlerClassName = tagInfo.getTagClassName();
+            try {
+                tagHandlerClass = ctxt.getClassLoader().loadClass(
+                        handlerClassName);
+            } catch (Exception e) {
+                err.jspError(start, "jsp.error.loadclass.taghandler",
+                        handlerClassName, tagName);
+            }
+        }
 
         // Parse 'CustomActionBody' production:
         // At this point we are committed - if anything fails, we produce
         // a translation error.
 
         // Parse 'Attributes' production:
-	Attributes attrs = parseAttributes();
-	reader.skipSpaces();
-	
+        Attributes attrs = parseAttributes();
+        reader.skipSpaces();
+
         // Parse 'CustomActionEnd' production:
-	if (reader.matches("/>")) {
-	    if (tagInfo != null) {
-		new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
-				   start, parent, tagInfo, tagHandlerClass);
-	    } else {
-		new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
-				   start, parent, tagFileInfo);
-	    }
-	    return true;
-	}
-	
-        // Now we parse one of 'CustomActionTagDependent', 
+        if (reader.matches("/>")) {
+            if (tagInfo != null) {
+                new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
+                        start, parent, tagInfo, tagHandlerClass);
+            } else {
+                new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
+                        start, parent, tagFileInfo);
+            }
+            return true;
+        }
+
+        // Now we parse one of 'CustomActionTagDependent',
         // 'CustomActionJSPContent', or 'CustomActionScriptlessContent'.
         // depending on body-content in TLD.
 
-	// Looking for a body, it still can be empty; but if there is a
-	// a tag body, its syntax would be dependent on the type of
-	// body content declared in the TLD.
-	String bc;
-	if (tagInfo != null) {
-	    bc = tagInfo.getBodyContent();
-	} else {
-	    bc = tagFileInfo.getTagInfo().getBodyContent();
-	}
-
-	Node tagNode = null;
-	if (tagInfo != null) {
-	    tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
-					 attrs, start, parent, tagInfo,
-					 tagHandlerClass);
-	} else {
-	    tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
-					 attrs, start, parent, tagFileInfo);
-	}
+        // Looking for a body, it still can be empty; but if there is a
+        // a tag body, its syntax would be dependent on the type of
+        // body content declared in the TLD.
+        String bc;
+        if (tagInfo != null) {
+            bc = tagInfo.getBodyContent();
+        } else {
+            bc = tagFileInfo.getTagInfo().getBodyContent();
+        }
 
-	parseOptionalBody( tagNode, tagName, bc );
+        Node tagNode = null;
+        if (tagInfo != null) {
+            tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
+                    attrs, start, parent, tagInfo, tagHandlerClass);
+        } else {
+            tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
+                    attrs, start, parent, tagFileInfo);
+        }
 
-	return true;
+        parseOptionalBody(tagNode, tagName, bc);
+
+        return true;
     }
 
     /*
-     * Parse for a template text string until '<' or "${" is encountered, 
-     * recognizing escape sequences "\%" and "\$".
+     * Parse for a template text string until '<' or "${" or "#{" is encountered,
+     * recognizing escape sequences "\%", "\$", and "\#".
      */
     private void parseTemplateText(Node parent) throws JasperException {
 
-	if (!reader.hasMoreInput())
-	    return;
+        if (!reader.hasMoreInput())
+            return;
 
-	CharArrayWriter ttext = new CharArrayWriter();
-	// Output the first character
-	int ch = reader.nextChar();
+        CharArrayWriter ttext = new CharArrayWriter();
+        // Output the first character
+        int ch = reader.nextChar();
         if (ch == '\\') {
             reader.pushChar();
         } else {
             ttext.write(ch);
         }
 
-	while (reader.hasMoreInput()) {
-	    ch = reader.nextChar();
-	    if (ch == '<') {
+        while (reader.hasMoreInput()) {
+            ch = reader.nextChar();
+            if (ch == '<') {
                 reader.pushChar();
                 break;
-            }
-	    else if( ch == '$' ) {
-		if (!reader.hasMoreInput()) {
-		    ttext.write('$');
-		    break;
-                }
-		ch = reader.nextChar();
-		if (ch == '{') {
-		    reader.pushChar();
-		    reader.pushChar();
-		    break;
-		}
-		ttext.write('$');
-		reader.pushChar();
-		continue;
-	    }
-	    else if (ch == '\\') {
-		if (!reader.hasMoreInput()) {
-		    ttext.write('\\');
-		    break;
-		}
-                char next = (char)reader.peekChar();
-                // Looking for \% or \$
-                // TODO: only recognize \$ if isELIgnored is false, but since
+            } else if (ch == '$' || ch == '#') {
+                if (!reader.hasMoreInput()) {
+                    ttext.write(ch);
+                    break;
+                }
+                if (reader.nextChar() == '{') {
+                    reader.pushChar();
+                    reader.pushChar();
+                    break;
+                }
+                ttext.write(ch);
+                reader.pushChar();
+                continue;
+            } else if (ch == '\\') {
+                if (!reader.hasMoreInput()) {
+                    ttext.write('\\');
+                    break;
+                }
+                char next = (char) reader.peekChar();
+                // Looking for \% or \$ or \#
+                // TODO: only recognize \$ or \# if isELIgnored is false, but since
                 // it can be set in a page directive, it cannot be determined
-                // here.  Argh!
-                if (next == '%' || next == '$') {
+                // here. Argh! (which is the way it should be since we shouldn't
+                // convolude multiple steps at once and create confusing parsers...)
+                if (next == '%' || next == '$' || next == '#') {
                     ch = reader.nextChar();
                 }
-	    }
-	    ttext.write(ch);
-	}
-	new Node.TemplateText(ttext.toString(), start, parent);
-    }
-    
-    /*
-     * XMLTemplateText ::=   ( S? '/>' )
-     *                     | ( S? '>'
-     *                         ( ( Char* - ( Char* ( '<' | '${' ) ) )
-     *                           ( '${' ELExpressionBody )?
-     *                           CDSect?
-     *                         )* ETag
-     *                       )
-     *                     | <TRANSLATION_ERROR>
+            }
+            ttext.write(ch);
+        }
+        new Node.TemplateText(ttext.toString(), start, parent);
+    }
+
+    /*
+     * XMLTemplateText ::= ( S? '/>' ) | ( S? '>' ( ( Char* - ( Char* ( '<' |
+     * '${' ) ) ) ( '${' ELExpressionBody )? CDSect? )* ETag ) |
+     * <TRANSLATION_ERROR>
      */
     private void parseXMLTemplateText(Node parent) throws JasperException {
         reader.skipSpaces();
-        if( !reader.matches( "/>" ) ) {
-            if( !reader.matches( ">" ) ) {
+        if (!reader.matches("/>")) {
+            if (!reader.matches(">")) {
                 err.jspError(start, "jsp.error.unterminated",
-                    "&lt;jsp:text&gt;" );
+                        "&lt;jsp:text&gt;");
             }
             CharArrayWriter ttext = new CharArrayWriter();
             while (reader.hasMoreInput()) {
-        	int ch = reader.nextChar();
-                if( ch == '<' ) {
+                int ch = reader.nextChar();
+                if (ch == '<') {
                     // Check for <![CDATA[
                     if (!reader.matches("![CDATA[")) {
                         break;
@@ -1468,189 +1355,159 @@
                     }
                     String text = reader.getText(start, stop);
                     ttext.write(text, 0, text.length());
-                }
-                else if( ch == '\\') {
+                } else if (ch == '\\') {
                     if (!reader.hasMoreInput()) {
                         ttext.write('\\');
                         break;
-		    }
+                    }
                     ch = reader.nextChar();
-                    if (ch != '$' ) {
+                    if (ch != '$' && ch != '#') {
                         ttext.write('\\');
                     }
                     ttext.write(ch);
-                }
-                else if( ch == '$' ) {
+                } else if (ch == '$' || ch == '#') {
                     if (!reader.hasMoreInput()) {
-                        ttext.write('$');
+                        ttext.write(ch);
                         break;
                     }
-                    ch = reader.nextChar();
-                    if (ch != '{') {
-                        ttext.write('$');
+                    if (reader.nextChar() != '{') {
+                        ttext.write(ch);
                         reader.pushChar();
                         continue;
                     }
                     // Create a template text node
-                    new Node.TemplateText( ttext.toString(), start, parent);
+                    new Node.TemplateText(ttext.toString(), start, parent);
 
                     // Mark and parse the EL expression and create its node:
                     start = reader.mark();
-                    parseELExpression(parent);
+                    parseELExpression(parent, (char) ch);
 
                     start = reader.mark();
                     ttext = new CharArrayWriter();
-                }
-                else {
-                    ttext.write( ch );
+                } else {
+                    ttext.write(ch);
                 }
             }
 
-            new Node.TemplateText( ttext.toString(), start, parent );
+            new Node.TemplateText(ttext.toString(), start, parent);
 
-	    if (! reader.hasMoreInput()) {
-                err.jspError( start, "jsp.error.unterminated",
-                    "&lt;jsp:text&gt;" );
-	    } else if( !reader.matchesETagWithoutLessThan( "jsp:text" ) ) {
-                err.jspError( start, "jsp.error.jsptext.badcontent");
+            if (!reader.hasMoreInput()) {
+                err.jspError(start, "jsp.error.unterminated",
+                        "&lt;jsp:text&gt;");
+            } else if (!reader.matchesETagWithoutLessThan("jsp:text")) {
+                err.jspError(start, "jsp.error.jsptext.badcontent");
             }
         }
     }
 
     /*
-     * AllBody ::=       ( '<%--'              JSPCommentBody     )
-     *                 | ( '<%@'               DirectiveBody      )
-     *                 | ( '<jsp:directive.'   XMLDirectiveBody   )
-     *                 | ( '<%!'               DeclarationBody    )
-     *                 | ( '<jsp:declaration'  XMLDeclarationBody )
-     *                 | ( '<%='               ExpressionBody     )
-     *                 | ( '<jsp:expression'   XMLExpressionBody  )
-     *                 | ( '${'                ELExpressionBody   )
-     *                 | ( '<%'                ScriptletBody      )
-     *                 | ( '<jsp:scriptlet'    XMLScriptletBody   )
-     *                 | ( '<jsp:text'         XMLTemplateText    )
-     *                 | ( '<jsp:'             StandardAction     )
-     *                 | ( '<'                 CustomAction
-     *                                         CustomActionBody   )
-     *	               | TemplateText
+     * AllBody ::= ( '<%--' JSPCommentBody ) | ( '<%@' DirectiveBody ) | ( '<jsp:directive.'
+     * XMLDirectiveBody ) | ( '<%!' DeclarationBody ) | ( '<jsp:declaration'
+     * XMLDeclarationBody ) | ( '<%=' ExpressionBody ) | ( '<jsp:expression'
+     * XMLExpressionBody ) | ( '${' ELExpressionBody ) | ( '<%' ScriptletBody ) | ( '<jsp:scriptlet'
+     * XMLScriptletBody ) | ( '<jsp:text' XMLTemplateText ) | ( '<jsp:'
+     * StandardAction ) | ( '<' CustomAction CustomActionBody ) | TemplateText
      */
-    private void parseElements(Node parent) 
-        throws JasperException 
-    {
-        if( scriptlessCount > 0 ) {
+    private void parseElements(Node parent) throws JasperException {
+        if (scriptlessCount > 0) {
             // vc: ScriptlessBody
             // We must follow the ScriptlessBody production if one of
             // our parents is ScriptlessBody.
-            parseElementsScriptless( parent );
+            parseElementsScriptless(parent);
             return;
         }
-        
-	start = reader.mark();
-	if (reader.matches("<%--")) {
-	    parseComment(parent);
-	} else if (reader.matches("<%@")) {
-	    parseDirective(parent);
+
+        start = reader.mark();
+        if (reader.matches("<%--")) {
+            parseComment(parent);
+        } else if (reader.matches("<%@")) {
+            parseDirective(parent);
         } else if (reader.matches("<jsp:directive.")) {
             parseXMLDirective(parent);
-	} else if (reader.matches("<%!")) {
-	    parseDeclaration(parent);
+        } else if (reader.matches("<%!")) {
+            parseDeclaration(parent);
         } else if (reader.matches("<jsp:declaration")) {
             parseXMLDeclaration(parent);
         } else if (reader.matches("<%=")) {
             parseExpression(parent);
         } else if (reader.matches("<jsp:expression")) {
             parseXMLExpression(parent);
-	} else if (reader.matches("<%")) {
-	    parseScriptlet(parent);
+        } else if (reader.matches("<%")) {
+            parseScriptlet(parent);
         } else if (reader.matches("<jsp:scriptlet")) {
             parseXMLScriptlet(parent);
         } else if (reader.matches("<jsp:text")) {
             parseXMLTemplateText(parent);
         } else if (reader.matches("${")) {
-            parseELExpression(parent);
-	} else if (reader.matches("<jsp:")) {
-	    parseStandardAction(parent);
-	} else if (!parseCustomTag(parent)) {
+            parseELExpression(parent, '$');
+        } else if (reader.matches("#{")) {
+            parseELExpression(parent, '#');
+        } else if (reader.matches("<jsp:")) {
+            parseStandardAction(parent);
+        } else if (!parseCustomTag(parent)) {
             checkUnbalancedEndTag();
             parseTemplateText(parent);
-	}
+        }
     }
 
     /*
-     * ScriptlessBody ::=  ( '<%--'              JSPCommentBody      )
-     *                   | ( '<%@'               DirectiveBody       )
-     *                   | ( '<jsp:directive.'   XMLDirectiveBody    )

[... 517 lines stripped ...]


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Mime
View raw message