sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r598331 [12/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/j...
Date Mon, 26 Nov 2007 16:15:53 GMT
Added: incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Validator.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Validator.java?rev=598331&view=auto
==============================================================================
--- incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Validator.java (added)
+++ incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/Validator.java Mon Nov 26 08:15:43 2007
@@ -0,0 +1,1553 @@
+/*
+ * 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.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javax.servlet.jsp.el.FunctionMapper;
+import javax.servlet.jsp.tagext.FunctionInfo;
+import javax.servlet.jsp.tagext.JspFragment;
+import javax.servlet.jsp.tagext.PageData;
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagData;
+import javax.servlet.jsp.tagext.TagExtraInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.ValidationMessage;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.xml.sax.Attributes;
+
+/**
+ * Performs validation on the page elements.  Attributes are checked for
+ * mandatory presence, entry value validity, and consistency.  As a
+ * side effect, some page global value (such as those from page direcitves)
+ * are stored, for later use.
+ *
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+class Validator {
+
+    /**
+     * A visitor to validate and extract page directive info
+     */
+    static class DirectiveVisitor extends Node.Visitor {
+
+	private PageInfo pageInfo;
+	private ErrorDispatcher err;
+
+	private static final JspUtil.ValidAttribute[] pageDirectiveAttrs = {
+	    new JspUtil.ValidAttribute("language"),
+	    new JspUtil.ValidAttribute("extends"),
+	    new JspUtil.ValidAttribute("import"),
+	    new JspUtil.ValidAttribute("session"),
+	    new JspUtil.ValidAttribute("buffer"),
+	    new JspUtil.ValidAttribute("autoFlush"),
+	    new JspUtil.ValidAttribute("isThreadSafe"),
+	    new JspUtil.ValidAttribute("info"),
+	    new JspUtil.ValidAttribute("errorPage"),
+	    new JspUtil.ValidAttribute("isErrorPage"),
+	    new JspUtil.ValidAttribute("contentType"),
+	    new JspUtil.ValidAttribute("pageEncoding"),
+	    new JspUtil.ValidAttribute("isELIgnored")
+	};
+
+	private boolean pageEncodingSeen = false;
+
+	/*
+	 * Constructor
+	 */
+	DirectiveVisitor(Compiler compiler) throws JasperException {
+	    this.pageInfo = compiler.getPageInfo();
+	    this.err = compiler.getErrorDispatcher();
+	    JspCompilationContext ctxt = compiler.getCompilationContext();
+	}
+
+	public void visit(Node.IncludeDirective n) throws JasperException {
+            // Since pageDirectiveSeen flag only applies to the Current page
+            // save it here and restore it after the file is included.
+            boolean pageEncodingSeenSave = pageEncodingSeen;
+            pageEncodingSeen = false;
+            visitBody(n);
+            pageEncodingSeen = pageEncodingSeenSave;
+        }
+
+	public void visit(Node.PageDirective n) throws JasperException {    
+
+            JspUtil.checkAttributes("Page directive", n,
+                                    pageDirectiveAttrs, err);
+
+	    // JSP.2.10.1
+	    Attributes attrs = n.getAttributes();
+	    for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
+		String attr = attrs.getQName(i);
+		String value = attrs.getValue(i);
+
+		if ("language".equals(attr)) {
+		    if (pageInfo.getLanguage(false) == null) {
+			pageInfo.setLanguage(value, n, err, true);
+		    } else if (!pageInfo.getLanguage(false).equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.language",
+				     pageInfo.getLanguage(false), value);
+		    }
+		} else if ("extends".equals(attr)) {
+		    if (pageInfo.getExtends(false) == null) {
+			pageInfo.setExtends(value, n);
+		    } else if (!pageInfo.getExtends(false).equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.extends",
+				     pageInfo.getExtends(false), value);
+		    }
+		} else if ("contentType".equals(attr)) {
+		    if (pageInfo.getContentType() == null) {
+			pageInfo.setContentType(value);
+		    } else if (!pageInfo.getContentType().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.contenttype",
+				     pageInfo.getContentType(), value);
+		    }
+		} else if ("session".equals(attr)) {
+		    if (pageInfo.getSession() == null) {
+			pageInfo.setSession(value, n, err);
+		    } else if (!pageInfo.getSession().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.session",
+				     pageInfo.getSession(), value);
+		    }
+		} else if ("buffer".equals(attr)) {
+		    if (pageInfo.getBufferValue() == null) {
+			pageInfo.setBufferValue(value, n, err);
+		    } else if (!pageInfo.getBufferValue().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.buffer",
+				     pageInfo.getBufferValue(), value);
+		    }
+		} else if ("autoFlush".equals(attr)) {
+		    if (pageInfo.getAutoFlush() == null) {
+			pageInfo.setAutoFlush(value, n, err);
+		    } else if (!pageInfo.getAutoFlush().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.autoflush",
+				     pageInfo.getAutoFlush(), value);
+		    }
+		} else if ("isThreadSafe".equals(attr)) {
+		    if (pageInfo.getIsThreadSafe() == null) {
+			pageInfo.setIsThreadSafe(value, n, err);
+		    } else if (!pageInfo.getIsThreadSafe().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.isthreadsafe",
+				     pageInfo.getIsThreadSafe(), value);
+		    }
+		} else if ("isELIgnored".equals(attr)) {
+		    if (pageInfo.getIsELIgnored() == null) {
+                        pageInfo.setIsELIgnored(value, n, err, true);
+		    } else if (!pageInfo.getIsELIgnored().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.iselignored",
+				     pageInfo.getIsELIgnored(), value);
+		    }
+		} else if ("isErrorPage".equals(attr)) {
+		    if (pageInfo.getIsErrorPage() == null) {
+			pageInfo.setIsErrorPage(value, n, err);
+		    } else if (!pageInfo.getIsErrorPage().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.iserrorpage",
+				     pageInfo.getIsErrorPage(), value);
+		    }
+		} else if ("errorPage".equals(attr)) {
+		    if (pageInfo.getErrorPage() == null) {
+			pageInfo.setErrorPage(value);
+		    } else if (!pageInfo.getErrorPage().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.errorpage",
+				     pageInfo.getErrorPage(), value);
+		    }
+		} else if ("info".equals(attr)) {
+		    if (pageInfo.getInfo() == null) {
+			pageInfo.setInfo(value);
+		    } else if (!pageInfo.getInfo().equals(value)) {
+			err.jspError(n, "jsp.error.page.conflict.info",
+				     pageInfo.getInfo(), value);
+		    }
+		} else if ("pageEncoding".equals(attr)) {
+		    if (pageEncodingSeen) 
+			err.jspError(n, "jsp.error.page.multi.pageencoding");
+		    // 'pageEncoding' can occur at most once per file
+		    pageEncodingSeen = true;
+		    comparePageEncodings(value, n);
+		}
+	    }
+
+	    // Check for bad combinations
+	    if (pageInfo.getBuffer() == 0 && !pageInfo.isAutoFlush())
+		err.jspError(n, "jsp.error.page.badCombo");
+
+	    // Attributes for imports for this node have been processed by
+	    // the parsers, just add them to pageInfo.
+	    pageInfo.addImports(n.getImports());
+	}
+
+	public void visit(Node.TagDirective n) throws JasperException {
+            // Note: Most of the validation is done in TagFileProcessor
+            // when it created a TagInfo object from the
+            // tag file in which the directive appeared.
+        
+            // This method does additional processing to collect page info
+            
+	    Attributes attrs = n.getAttributes();
+	    for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
+		String attr = attrs.getQName(i);
+		String value = attrs.getValue(i);
+
+		if ("language".equals(attr)) {
+		    if (pageInfo.getLanguage(false) == null) {
+			pageInfo.setLanguage(value, n, err, false);
+		    } else if (!pageInfo.getLanguage(false).equals(value)) {
+			err.jspError(n, "jsp.error.tag.conflict.language",
+				     pageInfo.getLanguage(false), value);
+		    }
+		} else if ("isELIgnored".equals(attr)) {
+		    if (pageInfo.getIsELIgnored() == null) {
+                        pageInfo.setIsELIgnored(value, n, err, false);
+		    } else if (!pageInfo.getIsELIgnored().equals(value)) {
+			err.jspError(n, "jsp.error.tag.conflict.iselignored",
+				     pageInfo.getIsELIgnored(), value);
+		    }
+		} else if ("pageEncoding".equals(attr)) {
+		    if (pageEncodingSeen) 
+			err.jspError(n, "jsp.error.tag.multi.pageencoding");
+		    pageEncodingSeen = true;
+		    n.getRoot().setPageEncoding(value);
+		}
+	    }
+
+	    // Attributes for imports for this node have been processed by
+	    // the parsers, just add them to pageInfo.
+	    pageInfo.addImports(n.getImports());
+	}
+
+	public void visit(Node.AttributeDirective n) throws JasperException {
+	    // Do nothing, since this attribute directive has already been
+	    // validated by TagFileProcessor when it created a TagInfo object
+	    // from the tag file in which the directive appeared
+	}
+
+	public void visit(Node.VariableDirective n) throws JasperException {
+	    // Do nothing, since this variable directive has already been
+	    // validated by TagFileProcessor when it created a TagInfo object
+	    // from the tag file in which the directive appeared
+	}
+
+        /*
+         * Compares page encodings specified in various places, and throws
+         * exception in case of page encoding mismatch.
+         *
+         * @param pageDirEnc The value of the pageEncoding attribute of the
+         * page directive
+         * @param pageDir The page directive node
+         *
+         * @throws JasperException in case of page encoding mismatch
+         */
+        private void comparePageEncodings(String pageDirEnc,
+                                          Node.PageDirective pageDir)
+                throws JasperException {
+
+            Node.Root root = pageDir.getRoot();
+            String configEnc = root.getJspConfigPageEncoding();
+
+            /*
+             * Compare the 'pageEncoding' attribute of the page directive with
+             * the encoding specified in the JSP config element whose URL
+             * pattern matches this page.
+             * Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as identical.
+             */
+            if (configEnc != null && !pageDirEnc.equals(configEnc) 
+                    && (!pageDirEnc.startsWith("UTF-16")
+                        || !configEnc.startsWith("UTF-16"))) {
+                err.jspError(pageDir,
+                             "jsp.error.config_pagedir_encoding_mismatch",
+                             configEnc, pageDirEnc);
+            }
+
+            /*
+             * Compare the 'pageEncoding' attribute of the page directive with
+             * the encoding specified in the XML prolog (only for XML syntax,
+             * and only if JSP document contains XML prolog with encoding
+             * declaration).
+             * Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as identical.
+             */
+	    if (root.isXmlSyntax() && root.isEncodingSpecifiedInProlog()) {
+		String pageEnc = root.getPageEncoding();
+                if (!pageDirEnc.equals(pageEnc) 
+                        && (!pageDirEnc.startsWith("UTF-16")
+                            || !pageEnc.startsWith("UTF-16"))) {
+                    err.jspError(pageDir,
+                                 "jsp.error.prolog_pagedir_encoding_mismatch",
+                                 pageEnc, pageDirEnc);
+                }
+            }
+        }
+    }
+
+    /**
+     * A visitor for validating nodes other than page directives
+     */
+    static class ValidateVisitor extends Node.Visitor {
+
+	private PageInfo pageInfo;
+	private ErrorDispatcher err;
+	private TagInfo tagInfo;
+        private ClassLoader loader;
+
+	private static final JspUtil.ValidAttribute[] jspRootAttrs = {
+            new JspUtil.ValidAttribute("xsi:schemaLocation"),
+	    new JspUtil.ValidAttribute("version", true) };
+
+	private static final JspUtil.ValidAttribute[] includeDirectiveAttrs = {
+	    new JspUtil.ValidAttribute("file", true) };
+
+	private static final JspUtil.ValidAttribute[] taglibDirectiveAttrs = {
+	    new JspUtil.ValidAttribute("uri"),
+	    new JspUtil.ValidAttribute("tagdir"),
+	    new JspUtil.ValidAttribute("prefix", true) };
+
+	private static final JspUtil.ValidAttribute[] includeActionAttrs = {
+	    new JspUtil.ValidAttribute("page", true, true),
+	    new JspUtil.ValidAttribute("flush") };
+
+	private static final JspUtil.ValidAttribute[] paramActionAttrs = {
+	    new JspUtil.ValidAttribute("name", true),
+	    new JspUtil.ValidAttribute("value", true, true) };
+
+	private static final JspUtil.ValidAttribute[] forwardActionAttrs = {
+	    new JspUtil.ValidAttribute("page", true, true) };
+
+	private static final JspUtil.ValidAttribute[] getPropertyAttrs = {
+	    new JspUtil.ValidAttribute("name", true),
+	    new JspUtil.ValidAttribute("property", true) };
+
+	private static final JspUtil.ValidAttribute[] setPropertyAttrs = {
+	    new JspUtil.ValidAttribute("name", true),
+	    new JspUtil.ValidAttribute("property", true),
+	    new JspUtil.ValidAttribute("value", false, true),
+	    new JspUtil.ValidAttribute("param") };
+
+	private static final JspUtil.ValidAttribute[] useBeanAttrs = {
+	    new JspUtil.ValidAttribute("id", true),
+	    new JspUtil.ValidAttribute("scope"),
+	    new JspUtil.ValidAttribute("class"),
+	    new JspUtil.ValidAttribute("type"),
+	    new JspUtil.ValidAttribute("beanName", false, true) };
+
+	private static final JspUtil.ValidAttribute[] plugInAttrs = {
+	    new JspUtil.ValidAttribute("type",true),
+	    new JspUtil.ValidAttribute("code", true),
+	    new JspUtil.ValidAttribute("codebase"),
+	    new JspUtil.ValidAttribute("align"),
+	    new JspUtil.ValidAttribute("archive"),
+	    new JspUtil.ValidAttribute("height", false, true),
+	    new JspUtil.ValidAttribute("hspace"),
+	    new JspUtil.ValidAttribute("jreversion"),
+	    new JspUtil.ValidAttribute("name"),
+	    new JspUtil.ValidAttribute("vspace"),
+	    new JspUtil.ValidAttribute("width", false, true),
+	    new JspUtil.ValidAttribute("nspluginurl"),
+	    new JspUtil.ValidAttribute("iepluginurl") };
+            
+        private static final JspUtil.ValidAttribute[] attributeAttrs = {
+            new JspUtil.ValidAttribute("name", true),
+            new JspUtil.ValidAttribute("trim") };
+            
+        private static final JspUtil.ValidAttribute[] invokeAttrs = {
+            new JspUtil.ValidAttribute("fragment", true),
+	    new JspUtil.ValidAttribute("var"),
+	    new JspUtil.ValidAttribute("varReader"),
+	    new JspUtil.ValidAttribute("scope") };
+
+        private static final JspUtil.ValidAttribute[] doBodyAttrs = {
+            new JspUtil.ValidAttribute("var"),
+	    new JspUtil.ValidAttribute("varReader"),
+	    new JspUtil.ValidAttribute("scope") };
+
+	private static final JspUtil.ValidAttribute[] jspOutputAttrs = {
+	    new JspUtil.ValidAttribute("omit-xml-declaration"),
+	    new JspUtil.ValidAttribute("doctype-root-element"),
+	    new JspUtil.ValidAttribute("doctype-public"),
+	    new JspUtil.ValidAttribute("doctype-system") };
+
+	/*
+	 * Constructor
+	 */
+	ValidateVisitor(Compiler compiler) {
+	    this.pageInfo = compiler.getPageInfo();
+	    this.err = compiler.getErrorDispatcher();
+	    this.tagInfo = compiler.getCompilationContext().getTagInfo();
+	    this.loader = compiler.getCompilationContext().getClassLoader();
+	}
+
+	public void visit(Node.JspRoot n) throws JasperException {
+	    JspUtil.checkAttributes("Jsp:root", n,
+				    jspRootAttrs, err);
+	    String version = n.getTextAttribute("version");
+	    if (!version.equals("1.2") && !version.equals("2.0")) {
+		err.jspError(n, "jsp.error.jsproot.version.invalid", version);
+	    }
+	    visitBody(n);
+	}
+
+	public void visit(Node.IncludeDirective n) throws JasperException {
+            JspUtil.checkAttributes("Include directive", n,
+                                    includeDirectiveAttrs, err);
+	    visitBody(n);
+	}
+
+	public void visit(Node.TaglibDirective n) throws JasperException {
+            JspUtil.checkAttributes("Taglib directive", n,
+                                    taglibDirectiveAttrs, err);
+	    // Either 'uri' or 'tagdir' attribute must be specified
+	    String uri = n.getAttributeValue("uri");
+	    String tagdir = n.getAttributeValue("tagdir");
+	    if (uri == null && tagdir == null) {
+		err.jspError(n, "jsp.error.taglibDirective.missing.location");
+	    }
+	    if (uri != null && tagdir != null) {
+		err.jspError(n, "jsp.error.taglibDirective.both_uri_and_tagdir");
+	    }
+	}
+
+	public void visit(Node.ParamAction n) throws JasperException {
+            JspUtil.checkAttributes("Param action", n,
+                                    paramActionAttrs, err);
+	    // make sure the value of the 'name' attribute is not a
+	    // request-time expression
+	    throwErrorIfExpression(n, "name", "jsp:param");
+	    n.setValue(getJspAttribute("value", null, null,
+				       n.getAttributeValue("value"),
+                                       java.lang.String.class,
+				       n, false));
+            visitBody(n);
+	}
+
+	public void visit(Node.ParamsAction n) throws JasperException {
+	    // Make sure we've got at least one nested jsp:param
+            Node.Nodes subElems = n.getBody();
+            if (subElems == null) {
+		err.jspError(n, "jsp.error.params.emptyBody");
+	    }
+            visitBody(n);
+	}
+
+	public void visit(Node.IncludeAction n) throws JasperException {
+            JspUtil.checkAttributes("Include action", n,
+                                    includeActionAttrs, err);
+	    n.setPage(getJspAttribute("page", null, null,
+				      n.getAttributeValue("page"), 
+                                      java.lang.String.class, n, false));
+	    visitBody(n);
+        };
+
+	public void visit(Node.ForwardAction n) throws JasperException {
+            JspUtil.checkAttributes("Forward", n,
+                                    forwardActionAttrs, err);
+	    n.setPage(getJspAttribute("page", null, null,
+				      n.getAttributeValue("page"), 
+                                      java.lang.String.class, n, false));
+	    visitBody(n);
+	}
+
+	public void visit(Node.GetProperty n) throws JasperException {
+            JspUtil.checkAttributes("GetProperty", n,
+                                    getPropertyAttrs, err);
+	}
+
+	public void visit(Node.SetProperty n) throws JasperException {
+            JspUtil.checkAttributes("SetProperty", n,
+                                    setPropertyAttrs, err);
+	    String name = n.getTextAttribute("name");
+	    String property = n.getTextAttribute("property");
+	    String param = n.getTextAttribute("param");
+	    String value = n.getAttributeValue("value");
+
+            n.setValue(getJspAttribute("value", null, null, value, 
+                java.lang.Object.class, n, false));
+
+            boolean valueSpecified = n.getValue() != null;
+
+	    if ("*".equals(property)) { 
+                if (param != null || valueSpecified)
+		    err.jspError(n, "jsp.error.setProperty.invalid");
+		
+            } else if (param != null && valueSpecified) {
+		err.jspError(n, "jsp.error.setProperty.invalid");
+	    }
+            
+            visitBody(n);
+	}
+
+	public void visit(Node.UseBean n) throws JasperException {
+            JspUtil.checkAttributes("UseBean", n,
+                                    useBeanAttrs, err);
+
+	    String name = n.getTextAttribute ("id");
+	    String scope = n.getTextAttribute ("scope");
+	    JspUtil.checkScope(scope, n, err);
+	    String className = n.getTextAttribute ("class");
+	    String type = n.getTextAttribute ("type");
+	    BeanRepository beanInfo = pageInfo.getBeanRepository();
+
+	    if (className == null && type == null)
+		err.jspError(n, "jsp.error.usebean.missingType");
+
+	    if (beanInfo.checkVariable(name))
+		err.jspError(n, "jsp.error.usebean.duplicate");
+
+	    if ("session".equals(scope) && !pageInfo.isSession())
+		err.jspError(n, "jsp.error.usebean.noSession");
+
+	    Node.JspAttribute jattr
+		= getJspAttribute("beanName", null, null,
+				  n.getAttributeValue("beanName"),
+				  java.lang.String.class, n, false);
+	    n.setBeanName(jattr);
+	    if (className != null && jattr != null)
+		err.jspError(n, "jsp.error.usebean.notBoth");
+
+	    if (className == null)
+		className = type;
+
+	    beanInfo.addBean(n, name, className, scope);
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.PlugIn n) throws JasperException {
+            JspUtil.checkAttributes("Plugin", n, plugInAttrs, err);
+
+	    throwErrorIfExpression(n, "type", "jsp:plugin");
+	    throwErrorIfExpression(n, "code", "jsp:plugin");
+	    throwErrorIfExpression(n, "codebase", "jsp:plugin");
+	    throwErrorIfExpression(n, "align", "jsp:plugin");
+	    throwErrorIfExpression(n, "archive", "jsp:plugin");
+	    throwErrorIfExpression(n, "hspace", "jsp:plugin");
+	    throwErrorIfExpression(n, "jreversion", "jsp:plugin");
+	    throwErrorIfExpression(n, "name", "jsp:plugin");
+	    throwErrorIfExpression(n, "vspace", "jsp:plugin");
+	    throwErrorIfExpression(n, "nspluginurl", "jsp:plugin");
+	    throwErrorIfExpression(n, "iepluginurl", "jsp:plugin");
+
+	    String type = n.getTextAttribute("type");
+	    if (type == null)
+		err.jspError(n, "jsp.error.plugin.notype");
+	    if (!type.equals("bean") && !type.equals("applet"))
+		err.jspError(n, "jsp.error.plugin.badtype");
+	    if (n.getTextAttribute("code") == null)
+		err.jspError(n, "jsp.error.plugin.nocode");
+            
+	    Node.JspAttribute width
+		= getJspAttribute("width", null, null,
+				  n.getAttributeValue("width"), 
+                                  java.lang.String.class, n, false);
+	    n.setWidth( width );
+            
+	    Node.JspAttribute height
+		= getJspAttribute("height", null, null,
+				  n.getAttributeValue("height"), 
+                                  java.lang.String.class, n, false);
+	    n.setHeight( height );
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.NamedAttribute n) throws JasperException {
+	    JspUtil.checkAttributes("Attribute", n,
+				    attributeAttrs, err);
+            visitBody(n);
+	}
+        
+	public void visit(Node.JspBody n) throws JasperException {
+            visitBody(n);
+	}
+        
+	public void visit(Node.Declaration n) throws JasperException {
+	    if (pageInfo.isScriptingInvalid()) {
+		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+	    }
+	}
+
+        public void visit(Node.Expression n) throws JasperException {
+	    if (pageInfo.isScriptingInvalid()) {
+		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+	    }
+	}
+
+        public void visit(Node.Scriptlet n) throws JasperException {
+	    if (pageInfo.isScriptingInvalid()) {
+		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+	    }
+	}
+
+	public void visit(Node.ELExpression n) throws JasperException {
+            if ( !pageInfo.isELIgnored() ) {
+		String expressions = "${" + new String(n.getText()) + "}";
+		ELNode.Nodes el = ELParser.parse(expressions);
+		validateFunctions(el, n);
+                JspUtil.validateExpressions(
+                    n.getStart(),
+		    expressions,
+                    java.lang.String.class, // XXX - Should template text 
+                                            // always evaluate to String?
+                    getFunctionMapper(el),
+                    err);
+		n.setEL(el);
+            }
+        }
+
+	public void visit(Node.UninterpretedTag n) throws JasperException {
+            if (n.getNamedAttributeNodes().size() != 0) {
+		err.jspError(n, "jsp.error.namedAttribute.invalidUse");
+            }
+
+	    Attributes attrs = n.getAttributes();
+	    if (attrs != null) {
+		int attrSize = attrs.getLength();
+		Node.JspAttribute[] jspAttrs = new Node.JspAttribute[attrSize];
+		for (int i=0; i < attrSize; i++) {
+		    jspAttrs[i] = getJspAttribute(attrs.getQName(i),
+						  attrs.getURI(i),
+						  attrs.getLocalName(i),
+						  attrs.getValue(i),
+						  java.lang.Object.class,
+						  n,
+						  false);
+		}
+		n.setJspAttributes(jspAttrs);
+	    }
+
+	    visitBody(n);
+        }
+
+	public void visit(Node.CustomTag n) throws JasperException {
+
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+
+	    /*
+	     * The bodyconet of a SimpleTag cannot be JSP.
+	     */
+	    if (n.implementsSimpleTag() &&
+                tagInfo.getBodyContent().equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) {
+                err.jspError(n, "jsp.error.simpletag.badbodycontent",
+                        tagInfo.getTagClassName());
+	    }
+
+	    /*
+	     * If the tag handler declares in the TLD that it supports dynamic
+	     * attributes, it also must implement the DynamicAttributes
+	     * interface.
+	     */
+	    if (tagInfo.hasDynamicAttributes()
+		    && !n.implementsDynamicAttributes()) {
+		err.jspError(n, "jsp.error.dynamic.attributes.not.implemented",
+			     n.getQName());
+	    }
+
+	    /*
+	     * Make sure all required attributes are present, either as
+             * attributes or named attributes (<jsp:attribute>).
+ 	     * Also make sure that the same attribute is not specified in
+	     * both attributes or named attributes.
+	     */
+	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
+	    String customActionUri = n.getURI();
+	    Attributes attrs = n.getAttributes();
+	    int attrsSize = (attrs == null) ? 0 : attrs.getLength();
+	    for (int i=0; i<tldAttrs.length; i++) {
+		String attr = null;
+		if (attrs != null) {
+		    attr = attrs.getValue(tldAttrs[i].getName());
+		    if (attr == null) {
+			attr = attrs.getValue(customActionUri,
+					      tldAttrs[i].getName());
+		    }
+		}
+		Node.NamedAttribute na =
+			n.getNamedAttributeNode(tldAttrs[i].getName());
+		
+		if (tldAttrs[i].isRequired() && attr == null && na == null) {
+		    err.jspError(n, "jsp.error.missing_attribute",
+				 tldAttrs[i].getName(), n.getLocalName());
+		}
+		if (attr != null && na != null) {
+		    err.jspError(n, "jsp.error.duplicate.name.jspattribute",
+			tldAttrs[i].getName());
+		}
+	    }
+
+            Node.Nodes naNodes = n.getNamedAttributeNodes();
+	    int jspAttrsSize = naNodes.size() + attrsSize;
+	    Node.JspAttribute[] jspAttrs = null;
+	    if (jspAttrsSize > 0) {
+		jspAttrs = new Node.JspAttribute[jspAttrsSize];
+	    }
+	    Hashtable tagDataAttrs = new Hashtable(attrsSize);
+
+	    checkXmlAttributes(n, jspAttrs, tagDataAttrs);
+            checkNamedAttributes(n, jspAttrs, attrsSize, tagDataAttrs);
+
+	    TagData tagData = new TagData(tagDataAttrs);
+
+	    // JSP.C1: It is a (translation time) error for an action that
+	    // has one or more variable subelements to have a TagExtraInfo
+	    // class that returns a non-null object.
+	    TagExtraInfo tei = tagInfo.getTagExtraInfo();
+	    if (tei != null
+		    && tei.getVariableInfo(tagData) != null
+		    && tei.getVariableInfo(tagData).length > 0
+		    && tagInfo.getTagVariableInfos().length > 0) {
+		err.jspError("jsp.error.non_null_tei_and_var_subelems",
+			     n.getQName());
+	    }
+
+	    n.setTagData(tagData);
+	    n.setJspAttributes(jspAttrs);
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.JspElement n) throws JasperException {
+
+	    Attributes attrs = n.getAttributes();
+	    if (attrs == null) {
+		err.jspError(n, "jsp.error.jspelement.missing.name");
+	    }
+	    int xmlAttrLen = attrs.getLength();
+
+            Node.Nodes namedAttrs = n.getNamedAttributeNodes();
+
+	    // XML-style 'name' attribute, which is mandatory, must not be
+	    // included in JspAttribute array
+	    int jspAttrSize = xmlAttrLen-1 + namedAttrs.size();
+
+	    Node.JspAttribute[] jspAttrs = new Node.JspAttribute[jspAttrSize];
+	    int jspAttrIndex = 0;
+
+	    // Process XML-style attributes
+	    for (int i=0; i<xmlAttrLen; i++) {
+		if ("name".equals(attrs.getLocalName(i))) {
+		    n.setNameAttribute(getJspAttribute(attrs.getQName(i),
+						       attrs.getURI(i),
+						       attrs.getLocalName(i),
+						       attrs.getValue(i),
+						       java.lang.String.class,
+						       n,
+						       false));
+		} else {
+		    if (jspAttrIndex<jspAttrSize) {
+			jspAttrs[jspAttrIndex++]
+			    = getJspAttribute(attrs.getQName(i),
+					      attrs.getURI(i),
+					      attrs.getLocalName(i),
+					      attrs.getValue(i),
+					      java.lang.Object.class,
+					      n,
+					      false);
+		    }
+		}
+	    }
+	    if (n.getNameAttribute() == null) {
+		err.jspError(n, "jsp.error.jspelement.missing.name");
+	    }
+
+	    // Process named attributes
+	    for (int i=0; i<namedAttrs.size(); i++) {
+                Node.NamedAttribute na = (Node.NamedAttribute) namedAttrs.getNode(i);
+		jspAttrs[jspAttrIndex++] = new Node.JspAttribute(na, false);
+	    }
+
+	    n.setJspAttributes(jspAttrs);
+
+	    visitBody(n);
+	}
+
+	public void visit(Node.JspOutput n) throws JasperException {
+            JspUtil.checkAttributes("jsp:output", n, jspOutputAttrs, err);
+
+	    if (n.getBody() != null) {
+                err.jspError(n, "jsp.error.jspoutput.nonemptybody");
+	    }
+
+	    String omitXmlDecl = n.getAttributeValue("omit-xml-declaration");
+	    String doctypeName = n.getAttributeValue("doctype-root-element");
+	    String doctypePublic = n.getAttributeValue("doctype-public");
+	    String doctypeSystem = n.getAttributeValue("doctype-system");
+
+	    String omitXmlDeclOld = pageInfo.getOmitXmlDecl();
+	    String doctypeNameOld = pageInfo.getDoctypeName();
+	    String doctypePublicOld = pageInfo.getDoctypePublic();
+	    String doctypeSystemOld = pageInfo.getDoctypeSystem();
+
+	    if (omitXmlDecl != null && omitXmlDeclOld != null &&
+			!omitXmlDecl.equals(omitXmlDeclOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"omit-xml-declaration", omitXmlDeclOld, omitXmlDecl);
+	    }
+
+	    if (doctypeName != null && doctypeNameOld != null &&
+			!doctypeName.equals(doctypeNameOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"doctype-root-element", doctypeNameOld, doctypeName);
+	    }
+
+	    if (doctypePublic != null && doctypePublicOld != null &&
+			!doctypePublic.equals(doctypePublicOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"doctype-public", doctypePublicOld, doctypePublic);
+	    }
+
+	    if (doctypeSystem != null && doctypeSystemOld != null &&
+			!doctypeSystem.equals(doctypeSystemOld) ) {
+                err.jspError(n, "jsp.error.jspoutput.conflict",
+			"doctype-system", doctypeSystemOld, doctypeSystem);
+	    }
+
+	    if (doctypeName == null && doctypeSystem != null ||
+		doctypeName != null && doctypeSystem == null) {
+		err.jspError(n, "jsp.error.jspoutput.doctypenamesystem");
+	    }
+
+	    if (doctypePublic != null && doctypeSystem == null) {
+		err.jspError(n, "jsp.error.jspoutput.doctypepulicsystem");
+	    }
+
+	    if (omitXmlDecl != null) {
+		pageInfo.setOmitXmlDecl(omitXmlDecl);
+	    }
+	    if (doctypeName != null) {
+		pageInfo.setDoctypeName(doctypeName);
+	    }
+	    if (doctypeSystem != null) {
+		pageInfo.setDoctypeSystem(doctypeSystem);
+	    }
+	    if (doctypePublic != null) {
+		pageInfo.setDoctypePublic(doctypePublic);
+	    }
+	}
+
+	public void visit(Node.InvokeAction n) throws JasperException {
+
+            JspUtil.checkAttributes("Invoke", n, invokeAttrs, err);
+
+	    String scope = n.getTextAttribute ("scope");
+	    JspUtil.checkScope(scope, n, err);
+
+	    String var = n.getTextAttribute("var");
+	    String varReader = n.getTextAttribute("varReader");
+	    if (scope != null && var == null && varReader == null) {
+		err.jspError(n, "jsp.error.missing_var_or_varReader");
+	    }
+	    if (var != null && varReader != null) {
+		err.jspError(n, "jsp.error.var_and_varReader");
+	    }
+	}
+
+	public void visit(Node.DoBodyAction n) throws JasperException {
+
+            JspUtil.checkAttributes("DoBody", n, doBodyAttrs, err);
+
+	    String scope = n.getTextAttribute ("scope");
+	    JspUtil.checkScope(scope, n, err);
+
+	    String var = n.getTextAttribute("var");
+	    String varReader = n.getTextAttribute("varReader");
+	    if (scope != null && var == null && varReader == null) {
+		err.jspError(n, "jsp.error.missing_var_or_varReader");
+	    }
+	    if (var != null && varReader != null) {
+		err.jspError(n, "jsp.error.var_and_varReader");
+	    }
+	}
+
+	/*
+	 * Make sure the given custom action does not have any invalid
+	 * attributes.
+	 *
+	 * A custom action and its declared attributes always belong to the
+	 * same namespace, which is identified by the prefix name of the
+	 * custom tag invocation. For example, in this invocation:
+	 *
+	 *     <my:test a="1" b="2" c="3"/>, the action
+	 *
+	 * "test" and its attributes "a", "b", and "c" all belong to the
+	 * namespace identified by the prefix "my". The above invocation would
+	 * be equivalent to:
+	 *
+	 *     <my:test my:a="1" my:b="2" my:c="3"/>
+	 *
+	 * An action attribute may have a prefix different from that of the
+	 * action invocation only if the underlying tag handler supports
+	 * dynamic attributes, in which case the attribute with the different
+	 * prefix is considered a dynamic attribute.
+	 */
+	private void checkXmlAttributes(Node.CustomTag n,
+					Node.JspAttribute[] jspAttrs,
+					Hashtable tagDataAttrs)
+	        throws JasperException {
+
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
+	    Attributes attrs = n.getAttributes();
+
+	    for (int i=0; attrs != null && i<attrs.getLength(); i++) {
+		boolean found = false;
+		for (int j=0; tldAttrs != null && j<tldAttrs.length; j++) {
+		    if (attrs.getLocalName(i).equals(tldAttrs[j].getName())
+			    && (attrs.getURI(i) == null
+				|| attrs.getURI(i).length() == 0
+				|| attrs.getURI(i).equals(n.getURI()))) {
+			if (tldAttrs[j].canBeRequestTime()) {
+                            Class expectedType = String.class;
+                            try {
+                                String typeStr = tldAttrs[j].getTypeName();
+                                if( tldAttrs[j].isFragment() ) {
+                                    expectedType = JspFragment.class;
+                                }
+                                else if( typeStr != null ) {
+                                    expectedType = JspUtil.toClass(typeStr,
+								   loader);
+                                }
+                                jspAttrs[i]
+                                    = getJspAttribute(attrs.getQName(i),
+                                                      attrs.getURI(i),
+                                                      attrs.getLocalName(i),
+                                                      attrs.getValue(i),
+                                                      expectedType,
+                                                      n,
+                                                      false);
+                            } catch (ClassNotFoundException e) {
+                                err.jspError(n, 
+                                    "jsp.error.unknown_attribute_type",
+                                    tldAttrs[j].getName(), 
+                                    tldAttrs[j].getTypeName() );
+                            }
+			} else {
+			    // Attribute does not accept any expressions.
+			    // Make sure its value does not contain any.
+			    if (isExpression(n, attrs.getValue(i))) {
+                                err.jspError(n,
+				        "jsp.error.attribute.custom.non_rt_with_expr",
+					     tldAttrs[j].getName());
+			    }
+			    jspAttrs[i]
+				= new Node.JspAttribute(attrs.getQName(i),
+							attrs.getURI(i),
+							attrs.getLocalName(i),
+							attrs.getValue(i),
+							false,
+							null,
+							false);
+			}
+			if (jspAttrs[i].isExpression()) {
+			    tagDataAttrs.put(attrs.getQName(i),
+					     TagData.REQUEST_TIME_VALUE);
+			} else {
+			    tagDataAttrs.put(attrs.getQName(i),
+					     attrs.getValue(i));
+			}
+			found = true;
+			break;
+		    }
+		}
+		if (!found) {
+		    if (tagInfo.hasDynamicAttributes()) {
+			jspAttrs[i] = getJspAttribute(attrs.getQName(i),
+						      attrs.getURI(i),
+						      attrs.getLocalName(i),
+						      attrs.getValue(i),
+						      java.lang.Object.class,
+                                                      n,
+						      true);
+		    } else {
+			err.jspError(n, "jsp.error.bad_attribute",
+				     attrs.getQName(i), n.getLocalName());
+		    }
+		}
+	    }
+	}
+
+	/*
+	 * Make sure the given custom action does not have any invalid named
+	 * attributes
+	 */
+	private void checkNamedAttributes(Node.CustomTag n,
+					  Node.JspAttribute[] jspAttrs,
+					  int start,
+					  Hashtable tagDataAttrs)
+	        throws JasperException {
+
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
+            Node.Nodes naNodes = n.getNamedAttributeNodes();
+
+	    for (int i=0; i<naNodes.size(); i++) {
+                Node.NamedAttribute na = (Node.NamedAttribute)
+		    naNodes.getNode(i);
+		boolean found = false;
+		for (int j=0; j<tldAttrs.length; j++) {
+		    /*
+		     * See above comment about namespace matches. For named
+		     * attributes, we use the prefix instead of URI as the
+		     * match criterion, because in the case of a JSP document,
+		     * we'd have to keep track of which namespaces are in scope
+		     * when parsing a named attribute, in order to determine
+		     * the URI that the prefix of the named attribute's name
+		     * matches to.
+		     */
+		    String attrPrefix = na.getPrefix();
+		    if (na.getLocalName().equals(tldAttrs[j].getName())
+			    && (attrPrefix == null || attrPrefix.length() == 0
+				|| attrPrefix.equals(n.getPrefix()))) {
+			jspAttrs[start + i] = new Node.JspAttribute(na, false);
+			NamedAttributeVisitor nav = null;
+			if (na.getBody() != null) {
+			    nav = new NamedAttributeVisitor();
+			    na.getBody().visit(nav);
+			}
+			if (nav != null && nav.hasDynamicContent()) {
+			    tagDataAttrs.put(na.getName(),
+					     TagData.REQUEST_TIME_VALUE);
+			} else {
+			    tagDataAttrs.put(na.getName(), na.getText());    
+			}
+			found = true;
+			break;
+		    }
+		}
+		if (!found) {
+		    if (tagInfo.hasDynamicAttributes()) {
+			jspAttrs[start + i] = new Node.JspAttribute(na, true);
+		    } else {
+			err.jspError(n, "jsp.error.bad_attribute",
+				     na.getName(), n.getLocalName());
+		    }
+		}
+	    }
+	}
+
+	/**
+	 * Preprocess attributes that can be expressions.  Expression
+	 * delimiters are stripped.
+         * <p>
+         * If value is null, checks if there are any
+         * NamedAttribute subelements in the tree node, and if so,
+         * constructs a JspAttribute out of a child NamedAttribute node.
+	 */
+	private Node.JspAttribute getJspAttribute(String qName,
+						  String uri,
+						  String localName,
+						  String value,
+                                                  Class expectedType,
+                                                  Node n,
+						  boolean dynamic)
+                throws JasperException {
+
+            Node.JspAttribute result = null;
+
+	    // XXX Is it an error to see "%=foo%" in non-Xml page?
+	    // (We won't see "<%=foo%> in xml page because '<' is not a
+	    // valid attribute value in xml).
+
+            if (value != null) {
+                if (n.getRoot().isXmlSyntax() && value.startsWith("%=")) {
+                    result = new Node.JspAttribute(
+                                        qName,
+					uri,
+					localName,
+					value.substring(2, value.length()-1),
+					true,
+					null,
+					dynamic);
+                }
+                else if(!n.getRoot().isXmlSyntax() && value.startsWith("<%=")) {
+                    result = new Node.JspAttribute(
+                                        qName,
+					uri,
+					localName,
+					value.substring(3, value.length()-2),
+					true,
+					null,
+					dynamic);
+                }
+                else {
+                    // The attribute can contain expressions but is not a
+                    // scriptlet expression; thus, we want to run it through 
+                    // the expression interpreter
+
+                    // validate expression syntax if string contains
+                    // expression(s)
+                    ELNode.Nodes el = ELParser.parse(value);
+                    if (el.containsEL() && !pageInfo.isELIgnored()) {
+	                validateFunctions(el, n);
+                        JspUtil.validateExpressions(
+                            n.getStart(),
+                            value, 
+                            expectedType, 
+                            getFunctionMapper(el),
+                            this.err);
+
+                        
+                        result = new Node.JspAttribute(qName, uri, localName,
+						       value, false, el,
+						       dynamic);
+                    } else {
+			value = value.replace(Constants.ESC, '$');
+                        result = new Node.JspAttribute(qName, uri, localName,
+						       value, false, null,
+						       dynamic);
+                    }
+                }
+            }
+            else {
+                // Value is null.  Check for any NamedAttribute subnodes
+                // that might contain the value for this attribute.
+                // Otherwise, the attribute wasn't found so we return null.
+
+                Node.NamedAttribute namedAttributeNode =
+                    n.getNamedAttributeNode( qName );
+                if( namedAttributeNode != null ) {
+                    result = new Node.JspAttribute(namedAttributeNode,
+						   dynamic);
+                }
+            }
+
+            return result;
+        }
+
+	/*
+	 * Checks to see if the given attribute value represents a runtime or
+	 * EL expression.
+	 */
+	private boolean isExpression(Node n, String value) {
+	    if ((n.getRoot().isXmlSyntax() && value.startsWith("%="))
+		    || (!n.getRoot().isXmlSyntax() && value.startsWith("<%="))
+   		    || (value.indexOf("${") != -1 && !pageInfo.isELIgnored()))
+		return true;
+	    else
+		return false;
+	}
+
+	/*
+	 * Throws exception if the value of the attribute with the given
+	 * name in the given node is given as an RT or EL expression, but the
+	 * spec requires a static value.
+	 */
+	private void throwErrorIfExpression(Node n, String attrName,
+					    String actionName)
+	            throws JasperException {
+	    if (n.getAttributes() != null
+		    && n.getAttributes().getValue(attrName) != null
+		    && isExpression(n, n.getAttributes().getValue(attrName))) {
+		err.jspError(n,
+			     "jsp.error.attribute.standard.non_rt_with_expr",
+			     attrName, actionName);
+	    }
+	}
+
+	private static class NamedAttributeVisitor extends Node.Visitor {
+	    private boolean hasDynamicContent;
+
+	    public void doVisit(Node n) throws JasperException {
+		if (!(n instanceof Node.JspText)
+		        && !(n instanceof Node.TemplateText)) {
+		    hasDynamicContent = true;
+		}
+		visitBody(n);
+	    }
+	    
+	    public boolean hasDynamicContent() {
+		return hasDynamicContent;
+	    }
+	}
+
+	private String findUri(String prefix, Node n) {
+
+	    for (Node p = n; p != null; p = p.getParent()) {
+		Attributes attrs = p.getTaglibAttributes();
+		if (attrs == null) {
+		    continue;
+		}
+		for (int i = 0; i < attrs.getLength(); i++) {
+		    String name = attrs.getQName(i);
+		    int k = name.indexOf(':');
+		    if (prefix == null && k < 0) {
+			// prefix not specified and a default ns found
+			return attrs.getValue(i);
+		    }   
+		    if (prefix != null && k >= 0 &&
+				prefix.equals(name.substring(k+1))) {
+			return attrs.getValue(i);
+		    }
+		}
+	    }
+	    return null;
+	}
+
+	/**
+	 * Validate functions in EL expressions
+	 */
+	private void validateFunctions(ELNode.Nodes el, Node n) 
+		throws JasperException {
+
+	    class FVVisitor extends ELNode.Visitor {
+
+		Node n;
+
+		FVVisitor(Node n) {
+		    this.n = n;
+		}
+
+		public void visit(ELNode.Function func) throws JasperException {
+		    String prefix = func.getPrefix();
+		    String function = func.getName();
+		    String uri = null;
+
+		    if (n.getRoot().isXmlSyntax()) {
+		        uri = findUri(prefix, n);
+		    } else if (prefix != null) {
+			uri = pageInfo.getURI(prefix);
+		    }
+
+		    if (uri == null) {
+			if (prefix == null) {
+			    err.jspError(n, "jsp.error.noFunctionPrefix",
+				function);
+			}
+			else {
+			    err.jspError(n,
+				"jsp.error.attribute.invalidPrefix", prefix);
+			}
+		    }
+		    TagLibraryInfo taglib = pageInfo.getTaglib(uri);
+		    FunctionInfo funcInfo = null;
+		    if (taglib != null) {
+			funcInfo = taglib.getFunction(function);
+		    }
+		    if (funcInfo == null) {
+			err.jspError(n, "jsp.error.noFunction", function);
+		    }
+		    // Skip TLD function uniqueness check.  Done by Schema ?
+		    func.setUri(uri);
+		    func.setFunctionInfo(funcInfo);
+		    processSignature(func);
+		}
+	    }
+
+	    el.visit(new FVVisitor(n));
+	}
+
+	private void processSignature(ELNode.Function func)
+		throws JasperException {
+	    func.setMethodName(getMethod(func));
+	    func.setParameters(getParameters(func));
+	}
+
+	/**
+	 * Get the method name from the signature.
+	 */
+	private String getMethod(ELNode.Function func)
+		throws JasperException {
+	    FunctionInfo funcInfo = func.getFunctionInfo();
+	    String signature = funcInfo.getFunctionSignature();
+	    
+	    int start = signature.indexOf(' ');
+	    if (start < 0) {
+		err.jspError("jsp.error.tld.fn.invalid.signature",
+			     func.getPrefix(), func.getName());
+	    }
+	    int end = signature.indexOf('(');
+	    if (end < 0) {
+		err.jspError("jsp.error.tld.fn.invalid.signature.parenexpected",
+			     func.getPrefix(), func.getName());
+	    }
+	    return signature.substring(start+1, end).trim();
+	}
+
+	/**
+	 * Get the parameters types from the function signature.
+	 * @return An array of parameter class names
+	 */
+	private String[] getParameters(ELNode.Function func) 
+		throws JasperException {
+	    FunctionInfo funcInfo = func.getFunctionInfo();
+	    String signature = funcInfo.getFunctionSignature();
+	    ArrayList params = new ArrayList();
+	    // Signature is of the form
+	    // <return-type> S <method-name S? '('
+	    // < <arg-type> ( ',' <arg-type> )* )? ')'
+	    int start = signature.indexOf('(') + 1;
+	    boolean lastArg = false;
+	    while (true) {
+		int p = signature.indexOf(',', start);
+		if (p < 0) {
+		    p = signature.indexOf(')', start);
+		    if (p < 0) {
+			err.jspError("jsp.error.tld.fn.invalid.signature",
+				     func.getPrefix(), func.getName());
+		    }
+		    lastArg = true;
+		}
+                String arg = signature.substring(start, p).trim();
+                if (!"".equals(arg)) {
+                    params.add(arg);
+                }
+		if (lastArg) {
+		    break;
+		}
+		start = p+1;
+	    }
+	    return (String[]) params.toArray(new String[params.size()]);
+	}
+
+	private FunctionMapper getFunctionMapper(ELNode.Nodes el)
+		throws JasperException {
+
+	    class ValidateFunctionMapper implements FunctionMapper {
+
+		private HashMap fnmap = new java.util.HashMap();
+		public void mapFunction(String fnQName, Method method) {
+		    fnmap.put(fnQName, method);
+		}
+
+		public Method resolveFunction(String prefix,
+					      String localName) {
+		    return (Method) this.fnmap.get(prefix + ":" + localName);
+		}
+	    }
+
+	    class MapperELVisitor extends ELNode.Visitor {
+		ValidateFunctionMapper fmapper;
+
+		MapperELVisitor(ValidateFunctionMapper fmapper) {
+		    this.fmapper = fmapper;
+		}
+
+		public void visit(ELNode.Function n) throws JasperException {
+
+		    Class c = null;
+		    Method method = null;
+		    try {
+			c = loader.loadClass(
+				n.getFunctionInfo().getFunctionClass());
+		    } catch (ClassNotFoundException e) {
+			err.jspError("jsp.error.function.classnotfound",
+				     n.getFunctionInfo().getFunctionClass(),
+				     n.getPrefix() + ':' + n.getName(),
+				     e.getMessage());
+		    }
+		    String paramTypes[] = n.getParameters();
+		    int size = paramTypes.length;
+		    Class params[] = new Class[size];
+		    int i = 0;
+		    try {
+			for (i = 0; i < size; i++) {
+			    params[i] = JspUtil.toClass(paramTypes[i], loader);
+			}
+			method = c.getDeclaredMethod(n.getMethodName(),
+						     params);
+		    } catch (ClassNotFoundException e) {
+			err.jspError("jsp.error.signature.classnotfound",
+				     paramTypes[i],
+				     n.getPrefix() + ':' + n.getName(),
+				     e.getMessage());
+		    } catch (NoSuchMethodException e ) {
+			err.jspError("jsp.error.noFunctionMethod",
+				     n.getMethodName(), n.getName(),
+				     c.getName());
+		    }
+		    fmapper.mapFunction(n.getPrefix() + ':' + n.getName(),
+					method);
+		}
+	    }
+
+	    ValidateFunctionMapper fmapper = new ValidateFunctionMapper();
+	    el.visit(new MapperELVisitor(fmapper));
+	    return fmapper;
+	}
+    } // End of ValidateVisitor
+
+    /**
+     * A visitor for validating TagExtraInfo classes of all tags
+     */
+    static class TagExtraInfoVisitor extends Node.Visitor {
+
+	private PageInfo pageInfo;
+	private ErrorDispatcher err;
+
+	/*
+	 * Constructor
+	 */
+	TagExtraInfoVisitor(Compiler compiler) {
+	    this.pageInfo = compiler.getPageInfo();
+	    this.err = compiler.getErrorDispatcher();
+	}
+
+	public void visit(Node.CustomTag n) throws JasperException {
+	    TagInfo tagInfo = n.getTagInfo();
+	    if (tagInfo == null) {
+		err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
+	    }
+
+	    ValidationMessage[] errors = tagInfo.validate(n.getTagData());
+            if (errors != null && errors.length != 0) {
+		StringBuffer errMsg = new StringBuffer();
+                errMsg.append("<h3>");
+                errMsg.append(Localizer.getMessage("jsp.error.tei.invalid.attributes",
+						   n.getQName()));
+                errMsg.append("</h3>");
+                for (int i=0; i<errors.length; i++) {
+                    errMsg.append("<p>");
+		    if (errors[i].getId() != null) {
+			errMsg.append(errors[i].getId());
+			errMsg.append(": ");
+		    }
+                    errMsg.append(errors[i].getMessage());
+                    errMsg.append("</p>");
+                }
+
+		err.jspError(n, errMsg.toString());
+            }
+
+	    visitBody(n);
+	}
+    }
+
+    public static void validate(Compiler compiler,
+				Node.Nodes page) throws JasperException {
+
+	/*
+	 * Visit the page/tag directives first, as they are global to the page
+	 * and are position independent.
+	 */
+	page.visit(new DirectiveVisitor(compiler));
+
+	// Determine the default output content type
+	PageInfo pageInfo = compiler.getPageInfo();
+	String contentType = pageInfo.getContentType();
+
+	if (contentType == null || contentType.indexOf("charset=") < 0) {
+	    boolean isXml = page.getRoot().isXmlSyntax();
+	    String defaultType;
+	    if (contentType == null) {
+		defaultType = isXml? "text/xml": "text/html";
+	    } else {
+		defaultType = contentType;
+	    }
+
+	    String charset = null;
+	    if (isXml) {
+		charset = "UTF-8";
+	    } else {
+		if (!page.getRoot().isDefaultPageEncoding()) {
+		    charset = page.getRoot().getPageEncoding();
+		}
+	    }
+
+	    if (charset != null) {
+		pageInfo.setContentType(defaultType + ";charset=" + charset);
+	    } else {
+		pageInfo.setContentType(defaultType);
+	    }
+	}
+
+	/*
+	 * Validate all other nodes.
+	 * This validation step includes checking a custom tag's mandatory and
+	 * optional attributes against information in the TLD (first validation
+	 * step for custom tags according to JSP.10.5).
+	 */
+	page.visit(new ValidateVisitor(compiler));
+
+	/*
+	 * Invoke TagLibraryValidator classes of all imported tags
+	 * (second validation step for custom tags according to JSP.10.5).
+	 */
+	validateXmlView(new PageDataImpl(page, compiler), compiler);
+
+	/*
+	 * Invoke TagExtraInfo method isValid() for all imported tags 
+	 * (third validation step for custom tags according to JSP.10.5).
+	 */
+	page.visit(new TagExtraInfoVisitor(compiler));
+
+    }
+
+
+    //*********************************************************************
+    // Private (utility) methods
+
+    /**
+     * Validate XML view against the TagLibraryValidator classes of all
+     * imported tag libraries.
+     */
+    private static void validateXmlView(PageData xmlView, Compiler compiler)
+	        throws JasperException {
+
+	StringBuffer errMsg = null;
+	ErrorDispatcher errDisp = compiler.getErrorDispatcher();
+
+	for (Iterator iter=compiler.getPageInfo().getTaglibs().iterator();
+	         iter.hasNext(); ) {
+
+	    Object o = iter.next();
+	    if (!(o instanceof TagLibraryInfoImpl))
+		continue;
+	    TagLibraryInfoImpl tli = (TagLibraryInfoImpl) o;
+
+	    ValidationMessage[] errors = tli.validate(xmlView);
+            if ((errors != null) && (errors.length != 0)) {
+                if (errMsg == null) {
+		    errMsg = new StringBuffer();
+		}
+                errMsg.append("<h3>");
+                errMsg.append(Localizer.getMessage("jsp.error.tlv.invalid.page",
+						   tli.getShortName(), compiler.getPageInfo().getJspFile()));
+                errMsg.append("</h3>");
+                for (int i=0; i<errors.length; i++) {
+		    if (errors[i] != null) {
+			errMsg.append("<p>");
+			errMsg.append(errors[i].getId());
+			errMsg.append(": ");
+			errMsg.append(errors[i].getMessage());
+			errMsg.append("</p>");
+		    }
+                }
+            }
+        }
+
+	if (errMsg != null) {
+            errDisp.jspError(errMsg.toString());
+	}
+    }
+}
+

Added: incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPlugin.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPlugin.java?rev=598331&view=auto
==============================================================================
--- incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPlugin.java (added)
+++ incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPlugin.java Mon Nov 26 08:15:43 2007
@@ -0,0 +1,36 @@
+/*
+ * 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.tagplugin;
+
+/**
+ * This interface is to be implemented by the plugin author, to supply
+ * an alternate implementation of the tag handlers.  It can be used to
+ * specify the Java codes to be generated when a tag is invoked.
+ *
+ * An implementation of this interface must be registered in a file
+ * named "tagPlugins.xml" under WEB-INF.
+ */
+
+public interface TagPlugin {
+
+    /**
+     * Generate codes for a custom tag.
+     * @param ctxt a TagPluginContext for accessing Jasper functions
+     */
+    void doTag(TagPluginContext ctxt);
+}
+

Added: incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java?rev=598331&view=auto
==============================================================================
--- incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java (added)
+++ incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java Mon Nov 26 08:15:43 2007
@@ -0,0 +1,123 @@
+/*
+ * 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.tagplugin;
+
+
+/**
+ * This interface allows the plugin author to make inqueries about the
+ * properties of the current tag, and to use Jasper resources to generate
+ * direct Java codes in place of tag handler invocations.
+ */
+
+public interface TagPluginContext {
+    /**
+     * @return true if the body of the tag is scriptless.
+     */
+    boolean isScriptless();
+
+    /**
+     * @param attribute Name of the attribute
+     * @return true if the attribute is specified in the tag
+     */
+    boolean isAttributeSpecified(String attribute);
+
+    /**
+     * @return An unique temporary variable name that the plugin can use.
+     */
+    String getTemporaryVariableName();
+
+    /**
+     * Generate an import statement
+     * @param s Name of the import class, '*' allowed.
+     */
+    void generateImport(String s);
+
+    /**
+     * Generate a declaration in the of the generated class.  This can be
+     * used to declare an innter class, a method, or a class variable.
+     * @param id An unique ID identifying the declaration.  It is not
+     *           part of the declaration, and is used to ensure that the
+     *           declaration will only appear once.  If this method is
+     *           invoked with the same id more than once in the translation
+     *           unit, only the first declaration will be taken.
+     * @param text The text of the declaration.
+     **/
+    void generateDeclaration(String id, String text);
+
+    /**
+     * Generate Java source codes
+     */
+    void generateJavaSource(String s);
+
+    /**
+     * @return true if the attribute is specified and its value is a
+     *         translation-time constant.
+     */
+    boolean isConstantAttribute(String attribute);
+
+    /**
+     * @return A string that is the value of a constant attribute.  Undefined
+     *         if the attribute is not a (translation-time) constant.
+     *         null if the attribute is not specified.
+     */
+    String getConstantAttribute(String attribute);
+
+    /**
+     * Generate codesto evaluate value of a attribute in the custom tag
+     * The codes is a Java expression.
+     * NOTE: Currently cannot handle attributes that are fragments.
+     * @param attribute The specified attribute
+     */
+    void generateAttribute(String attribute);
+
+    /**
+     * Generate codes for the body of the custom tag
+     */
+    void generateBody();
+
+    /**
+     * Abandon optimization for this tag handler, and instruct
+     * Jasper to generate the tag handler calls, as usual.
+     * Should be invoked if errors are detected, or when the tag body
+     * is deemed too compilicated for optimization.
+     */
+    void dontUseTagPlugin();
+
+    /**
+     * Get the PluginContext for the parent of this custom tag.  NOTE:
+     * The operations available for PluginContext so obtained is limited
+     * to getPluginAttribute and setPluginAttribute, and queries (e.g.
+     * isScriptless().  There should be no calls to generate*().
+     * @return The pluginContext for the parent node.
+     *         null if the parent is not a custom tag, or if the pluginConxt
+     *         if not available (because useTagPlugin is false, e.g).
+     */
+    TagPluginContext getParentContext();
+
+    /**
+     * Associate the attribute with a value in the current tagplugin context.
+     * The plugin attributes can be used for communication among tags that
+     * must work together as a group.  See <c:when> for an example.
+     */
+    void setPluginAttribute(String attr, Object value);
+
+    /**
+     * Get the value of an attribute in the current tagplugin context.
+     */
+    Object getPluginAttribute(String attr);
+}
+

Added: incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/BodyContentImpl.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/BodyContentImpl.java?rev=598331&view=auto
==============================================================================
--- incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/BodyContentImpl.java (added)
+++ incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/BodyContentImpl.java Mon Nov 26 08:15:43 2007
@@ -0,0 +1,608 @@
+/*
+ * 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.runtime;
+
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.BodyContent;
+
+import org.apache.jasper.Constants;
+
+/**
+ * Write text to a character-output stream, buffering characters so as
+ * to provide for the efficient writing of single characters, arrays,
+ * and strings. 
+ *
+ * Provide support for discarding for the output that has been buffered. 
+ *
+ * @author Rajiv Mordani
+ * @author Jan Luehe
+ */
+public class BodyContentImpl extends BodyContent {
+    
+    private static final String LINE_SEPARATOR = 
+        System.getProperty("line.separator");
+    private static final boolean LIMIT_BUFFER = 
+        Boolean.valueOf(System.getProperty("org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER", "false")).booleanValue();
+    
+    private char[] cb;
+    private int nextChar;
+    private boolean closed;
+    
+    // Enclosed writer to which any output is written
+    private Writer writer;
+    
+    // See comment in setWriter()
+    private int bufferSizeSave;
+    
+    /**
+     * Constructor.
+     */
+    public BodyContentImpl(JspWriter enclosingWriter) {
+        super(enclosingWriter);
+        bufferSize = Constants.DEFAULT_TAG_BUFFER_SIZE;
+        cb = new char[bufferSize];
+        nextChar = 0;
+        closed = false;
+    }
+    
+    /**
+     * Write a single character.
+     */
+    public void write(int c) throws IOException {
+        if (writer != null) {
+            writer.write(c);
+        } else {
+            ensureOpen();
+            if (nextChar >= bufferSize) {
+                reAllocBuff (1);
+            }
+            cb[nextChar++] = (char) c;
+        }
+    }
+    
+    /**
+     * Write a portion of an array of characters.
+     *
+     * <p> Ordinarily this method stores characters from the given array into
+     * this stream's buffer, flushing the buffer to the underlying stream as
+     * needed.  If the requested length is at least as large as the buffer,
+     * however, then this method will flush the buffer and write the characters
+     * directly to the underlying stream.  Thus redundant
+     * <code>DiscardableBufferedWriter</code>s will not copy data
+     * unnecessarily.
+     *
+     * @param cbuf A character array
+     * @param off Offset from which to start reading characters
+     * @param len Number of characters to write
+     */
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        if (writer != null) {
+            writer.write(cbuf, off, len);
+        } else {
+            ensureOpen();
+            
+            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
+                    ((off + len) > cbuf.length) || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            } else if (len == 0) {
+                return;
+            } 
+            
+            if (len >= bufferSize - nextChar)
+                reAllocBuff (len);
+            
+            System.arraycopy(cbuf, off, cb, nextChar, len);
+            nextChar+=len;
+        }
+    }
+    
+    /**
+     * Write an array of characters.  This method cannot be inherited from the
+     * Writer class because it must suppress I/O exceptions.
+     */
+    public void write(char[] buf) throws IOException {
+        if (writer != null) {
+            writer.write(buf);
+        } else {
+            write(buf, 0, buf.length);
+        }
+    }
+    
+    /**
+     * Write a portion of a String.
+     *
+     * @param s String to be written
+     * @param off Offset from which to start reading characters
+     * @param len Number of characters to be written
+     */
+    public void write(String s, int off, int len) throws IOException {
+        if (writer != null) {
+            writer.write(s, off, len);
+        } else {
+            ensureOpen();
+            if (len >= bufferSize - nextChar)
+                reAllocBuff(len);
+            
+            s.getChars(off, off + len, cb, nextChar);
+            nextChar += len;
+        }
+    }
+    
+    /**
+     * Write a string.  This method cannot be inherited from the Writer class
+     * because it must suppress I/O exceptions.
+     */
+    public void write(String s) throws IOException {
+        if (writer != null) {
+            writer.write(s);
+        } else {
+            write(s, 0, s.length());
+        }
+    }
+    
+    /**
+     * Write a line separator.  The line separator string is defined by the
+     * system property <tt>line.separator</tt>, and is not necessarily a single
+     * newline ('\n') character.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void newLine() throws IOException {
+        if (writer != null) {
+            writer.write(LINE_SEPARATOR);
+        } else {
+            write(LINE_SEPARATOR);
+        }
+    }
+    
+    /**
+     * Print a boolean value.  The string produced by <code>{@link
+     * java.lang.String#valueOf(boolean)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param b The <code>boolean</code> to be printed
+     * @throws IOException
+     */
+    public void print(boolean b) throws IOException {
+        if (writer != null) {
+            writer.write(b ? "true" : "false");
+        } else {
+            write(b ? "true" : "false");
+        }
+    }
+    
+    /**
+     * Print a character.  The character is translated into one or more bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param c The <code>char</code> to be printed
+     * @throws IOException
+     */
+    public void print(char c) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(c));
+        } else {
+            write(String.valueOf(c));
+        }
+    }
+    
+    /**
+     * Print an integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(int)}</code> is translated into bytes according
+     * to the platform's default character encoding, and these bytes are
+     * written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param i The <code>int</code> to be printed
+     * @throws IOException
+     */
+    public void print(int i) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(i));
+        } else {
+            write(String.valueOf(i));
+        }
+    }
+    
+    /**
+     * Print a long integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(long)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param l The <code>long</code> to be printed
+     * @throws IOException
+     */
+    public void print(long l) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(l));
+        } else {
+            write(String.valueOf(l));
+        }
+    }
+    
+    /**
+     * Print a floating-point number.  The string produced by <code>{@link
+     * java.lang.String#valueOf(float)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param f The <code>float</code> to be printed
+     * @throws IOException
+     */
+    public void print(float f) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(f));
+        } else {
+            write(String.valueOf(f));
+        }
+    }
+    
+    /**
+     * Print a double-precision floating-point number.  The string produced by
+     * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
+     * bytes according to the platform's default character encoding, and these
+     * bytes are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param d The <code>double</code> to be printed
+     * @throws IOException
+     */
+    public void print(double d) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(d));
+        } else {
+            write(String.valueOf(d));
+        }
+    }
+    
+    /**
+     * Print an array of characters.  The characters are converted into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param s The array of chars to be printed
+     *
+     * @throws NullPointerException If <code>s</code> is <code>null</code>
+     * @throws IOException
+     */
+    public void print(char[] s) throws IOException {
+        if (writer != null) {
+            writer.write(s);
+        } else {
+            write(s);
+        }
+    }
+    
+    /**
+     * Print a string.  If the argument is <code>null</code> then the string
+     * <code>"null"</code> is printed.  Otherwise, the string's characters are
+     * converted into bytes according to the platform's default character
+     * encoding, and these bytes are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param s The <code>String</code> to be printed
+     * @throws IOException
+     */
+    public void print(String s) throws IOException {
+        if (s == null) s = "null";
+        if (writer != null) {
+            writer.write(s);
+        } else {
+            write(s);
+        }
+    }
+    
+    /**
+     * Print an object.  The string produced by the <code>{@link
+     * java.lang.String#valueOf(Object)}</code> method is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param obj The <code>Object</code> to be printed
+     * @throws IOException
+     */
+    public void print(Object obj) throws IOException {
+        if (writer != null) {
+            writer.write(String.valueOf(obj));
+        } else {
+            write(String.valueOf(obj));
+        }
+    }
+    
+    /**
+     * Terminate the current line by writing the line separator string.  The
+     * line separator string is defined by the system property
+     * <code>line.separator</code>, and is not necessarily a single newline
+     * character (<code>'\n'</code>).
+     *
+     * @throws IOException
+     */
+    public void println() throws IOException {
+        newLine();
+    }
+    
+    /**
+     * Print a boolean value and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(boolean)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(boolean x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a character and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(char)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(char x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an integer and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(int)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(int x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a long integer and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(long)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(long x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a floating-point number and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(float)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(float x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a double-precision floating-point number and then terminate the
+     * line.  This method behaves as though it invokes <code>{@link
+     * #print(double)}</code> and then <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(double x) throws IOException{
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an array of characters and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(char[])}</code> and
+     * then <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(char x[]) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a String and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(String)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(String x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an Object and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(Object)}</code> and then
+     * <code>{@link #println()}</code>.
+     *
+     * @throws IOException
+     */
+    public void println(Object x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Clear the contents of the buffer. If the buffer has been already
+     * been flushed then the clear operation shall throw an IOException
+     * to signal the fact that some data has already been irrevocably 
+     * written to the client response stream.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void clear() throws IOException {
+        if (writer != null) {
+            throw new IOException();
+        } else {
+            nextChar = 0;
+            if (LIMIT_BUFFER && (cb.length > Constants.DEFAULT_TAG_BUFFER_SIZE)) {
+                bufferSize = Constants.DEFAULT_TAG_BUFFER_SIZE;
+                cb = new char[bufferSize];
+            }
+        }
+    }
+    
+    /**
+     * Clears the current contents of the buffer. Unlike clear(), this
+     * mehtod will not throw an IOException if the buffer has already been
+     * flushed. It merely clears the current content of the buffer and
+     * returns.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void clearBuffer() throws IOException {
+        if (writer == null) {
+            this.clear();
+        }
+    }
+    
+    /**
+     * Close the stream, flushing it first.  Once a stream has been closed,
+     * further write() or flush() invocations will cause an IOException to be
+     * thrown.  Closing a previously-closed stream, however, has no effect.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    public void close() throws IOException {
+        if (writer != null) {
+            writer.close();
+        } else {
+            closed = true;
+        }
+    }
+    
+    /**
+     * @return the number of bytes unused in the buffer
+     */
+    public int getRemaining() {
+        return (writer == null) ? bufferSize-nextChar : 0;
+    }
+    
+    /**
+     * Return the value of this BodyJspWriter as a Reader.
+     * Note: this is after evaluation!!  There are no scriptlets,
+     * etc in this stream.
+     *
+     * @return the value of this BodyJspWriter as a Reader
+     */
+    public Reader getReader() {
+        return (writer == null) ? new CharArrayReader (cb, 0, nextChar) : null;
+    }
+    
+    /**
+     * Return the value of the BodyJspWriter as a String.
+     * Note: this is after evaluation!!  There are no scriptlets,
+     * etc in this stream.
+     *
+     * @return the value of the BodyJspWriter as a String
+     */
+    public String getString() {
+        return (writer == null) ? new String(cb, 0, nextChar) : null;
+    }
+    
+    /**
+     * Write the contents of this BodyJspWriter into a Writer.
+     * Subclasses are likely to do interesting things with the
+     * implementation so some things are extra efficient.
+     *
+     * @param out The writer into which to place the contents of this body
+     * evaluation
+     */
+    public void writeOut(Writer out) throws IOException {
+        if (writer == null) {
+            out.write(cb, 0, nextChar);
+            // Flush not called as the writer passed could be a BodyContent and
+            // it doesn't allow to flush.
+        }
+    }
+    
+    /**
+     * Sets the writer to which all output is written.
+     */
+    void setWriter(Writer writer) {
+        this.writer = writer;
+        closed = false;
+        if (writer != null) {
+            // According to the spec, the JspWriter returned by 
+            // JspContext.pushBody(java.io.Writer writer) must behave as
+            // though it were unbuffered. This means that its getBufferSize()
+            // must always return 0. The implementation of
+            // JspWriter.getBufferSize() returns the value of JspWriter's
+            // 'bufferSize' field, which is inherited by this class. 
+            // Therefore, we simply save the current 'bufferSize' (so we can 
+            // later restore it should this BodyContentImpl ever be reused by
+            // a call to PageContext.pushBody()) before setting it to 0.
+            if (bufferSize != 0) {
+                bufferSizeSave = bufferSize;
+                bufferSize = 0;
+            }
+        } else {
+            bufferSize = bufferSizeSave;
+            clearBody();
+        }
+    }
+    
+    private void ensureOpen() throws IOException {
+        if (closed) throw new IOException("Stream closed");
+    }
+    
+    /**
+     * Reallocates buffer since the spec requires it to be unbounded.
+     */
+    private void reAllocBuff(int len) {
+        
+        if (bufferSize + len <= cb.length) {
+            bufferSize = cb.length;
+            return;
+        }
+        
+        if (len < cb.length) {
+            len = cb.length;
+        }
+        
+        bufferSize = cb.length + len;
+        char[] tmp = new char[bufferSize];
+        
+        System.arraycopy(cb, 0, tmp, 0, cb.length);
+        cb = tmp;
+        tmp = null;
+        
+    }
+    
+    
+}

Added: incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/HttpJspBase.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/HttpJspBase.java?rev=598331&view=auto
==============================================================================
--- incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/HttpJspBase.java (added)
+++ incubator/sling/trunk/scripting/jsp/src/main/java/org/apache/jasper/runtime/HttpJspBase.java Mon Nov 26 08:15:43 2007
@@ -0,0 +1,115 @@
+/*
+ * 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.runtime;
+
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.HttpJspPage;
+import javax.servlet.jsp.JspFactory;
+
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * This is the super class of all JSP-generated servlets.
+ *
+ * @author Anil K. Vijendran
+ */
+public abstract class HttpJspBase 
+    extends HttpServlet 
+    implements HttpJspPage 
+        
+    
+{
+    
+    static {
+        if( JspFactory.getDefaultFactory() == null ) {
+            JspFactoryImpl factory = new JspFactoryImpl();
+            if( System.getSecurityManager() != null ) {
+                String basePackage = "org.apache.jasper.";
+                try {
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspFactoryImpl$PrivilegedGetPageContext");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspFactoryImpl$PrivilegedReleasePageContext");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspRuntimeLibrary");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "runtime.ServletResponseWrapperInclude");
+                    factory.getClass().getClassLoader().loadClass( basePackage +
+                                                                   "servlet.JspServletWrapper");
+                } catch (ClassNotFoundException ex) {
+                    org.apache.commons.logging.LogFactory.getLog( HttpJspBase.class )
+                        .error("Jasper JspRuntimeContext preload of class failed: " +
+                                       ex.getMessage(), ex);
+                }
+            }
+            JspFactory.setDefaultFactory(factory);
+        }
+    }
+
+    protected HttpJspBase() {
+    }
+
+    public final void init(ServletConfig config) 
+	throws ServletException 
+    {
+        super.init(config);
+	jspInit();
+        _jspInit();
+    }
+    
+    public String getServletInfo() {
+	return Localizer.getMessage("jsp.engine.info");
+    }
+
+    public final void destroy() {
+	jspDestroy();
+	_jspDestroy();
+    }
+
+    /**
+     * Entry point into service.
+     */
+    public final void service(HttpServletRequest request, HttpServletResponse response) 
+	throws ServletException, IOException 
+    {
+        _jspService(request, response);
+    }
+    
+    public void jspInit() {
+    }
+
+    public void _jspInit() {
+    }
+
+    public void jspDestroy() {
+    }
+
+    protected void _jspDestroy() {
+    }
+
+    public abstract void _jspService(HttpServletRequest request, 
+				     HttpServletResponse response) 
+	throws ServletException, IOException;
+}



Mime
View raw message