You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ki...@apache.org on 2002/07/16 21:30:53 UTC

cvs commit: jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/xmlparser ParserUtils.java

kinman      2002/07/16 12:30:53

  Modified:    jasper2  BUILDING.txt build.xml
               jasper2/src/share/org/apache/jasper Constants.java
                        EmbededServletOptions.java Options.java
               jasper2/src/share/org/apache/jasper/compiler Collector.java
                        Compiler.java Dumper.java Generator.java
                        JspDocumentParser.java JspReader.java JspUtil.java
                        Node.java PageDataImpl.java PageInfo.java
                        Parser.java TagConstants.java
                        TagLibraryInfoImpl.java Validator.java
               jasper2/src/share/org/apache/jasper/resources
                        messages.properties
               jasper2/src/share/org/apache/jasper/runtime
                        JspRuntimeLibrary.java PageContextImpl.java
               jasper2/src/share/org/apache/jasper/xmlparser
                        ParserUtils.java
  Added:       jasper2/src/share/org/apache/jasper/compiler
                        SmapGenerator.java SmapStratum.java SmapUtil.java
               jasper2/src/share/org/apache/jasper/runtime
                        ExpressionEvaluatorImpl.java
                        ExpressionEvaluatorManager.java
                        JspFragmentHelper.java
  Log:
  - Merged sources with jsr152 EG repository.
  
    This is a JSP 2.0 early access 1 release code base.  Supproted features
  
    * New JSP 2.0 APIs
    * Expression Language
    * JSR-45 Debugging
    * JSP 2.0 Tag Library Descriptors
    * Support for <jsp:attribute>, <jsp:body> Standard Actions
    * SimpleTag Handler Support
    * JSP Fragments
  
    JSP 2.0 features not yet supported:
  
    * Tag Files
    * Attributes with Dynamic Names
    * JSP Configuration
  
  Revision  Changes    Path
  1.3       +67 -7     jakarta-tomcat-jasper/jasper2/BUILDING.txt
  
  Index: BUILDING.txt
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/BUILDING.txt,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- BUILDING.txt	19 Apr 2002 20:35:01 -0000	1.2
  +++ BUILDING.txt	16 Jul 2002 19:30:51 -0000	1.3
  @@ -1,13 +1,73 @@
  -1. Go to a tomcat 4.0/4.1 repository
  +$Id$
   
  -    cd jakarta-tomcat-4.0
  +         Building The Tomcat 4.0 Servlet/JSP Container With Jasper2
  +         ==========================================================
   
  -2. Edit jakarta-tomcat-4.0/build.properties to include this line
  +Jasper2 is a reimplementation of the JSP Container for Tomcat 4.0 that is
  +cleaner and easier to extend.  It also forms the basis of the JSP 2.0 
  +implementation.  Jasper2 will eventually replace the Jasper module
  +built into the Tomcat distribution.  In the meanwhile, you can build Jasper2
  +alongside Tomcat 4.0 and replace Jasper.
   
  -    jasper.home=../jakarta-tomcat-jasper/jasper2
  +To build Jasper2, first make sure you have a working build environment for
  +Tomcat 4.0.  You can do so by following the instructions in the BUILDING.txt
  +file in the Tomcat 4.0 workspace.
   
  -3. Build tomcat 4
  +Once you have a working build environment for Tomcat 4.0, do the following:
   
  -    ant clean
  -    ant
  +(0) Modify your build.properties file in Tomcat 4.0
   
  +* Add a line to your build.properties file for Tomcat 4.0 that
  +  points the build script to Jasper2 instead of Jasper, as follows:
  +
  +      # ----- Jakarta Tomcat Jasper source path -----
  +      jasper.home=${base.path}/jakarta-tomcat-jasper/jasper2
  +
  +  This directory is relative to the location of build.xml for Tomcat.
  +
  +(1) Use anonymous CVS (as described on the Jakarta web site at
  +    <http://jakarta.apache.org/site/cvsindex.html>, or download a source
  +    distribution of the "jakarta-taglibs" repository (7/15/2002 or later).
  +
  +    http://jakarta.apache.org/builds/jakarta-taglibs/nightly/projects/standard/
  +
  +* Unpack the package into a convenient location so that
  +  it resides in its own subdirectory.
  +
  +* Follow the instructions in BUILDING.txt to set up the correct 
  +  build environment.  Make sure you have a valid build.properties file.
  +
  +* Change directory to "standard" and build the special JSP 2.0 build target
  +  for producing the JSP 2.0 Expression Language Evaluator.  The ant target
  +  is called jsp20el.dist.
  +
  +        cd standard
  +        ant jsp20el.dist
  +
  +(2) Customize Build Properties for this subproject
  +
  +Most Jakarta subprojects allow you to customize Ant properties (with default
  +values defined in the "build.xml" file.  This is done by creating a text file
  +named "build.properties" in the source distribution directory (for property
  +definitions local to this subproject) and/or your user home directory (for
  +property definitions shared across subprojects).  You can use the included
  +"build.properties.sample" file as a starting point for this.
  +
  +Jasper2 has external dependencies that are satisfied by configuring
  +appropriate values in your build.properties file.  The easiest
  +way to satisfy these dependencies is to copy the "build.properties.sample"
  +file (in the top-level Tomcat source directory) to "build.properties", and
  +then edit it to suit your environment.  On Unix, this would be done as:
  +
  +  cd ${jasper2.source}
  +  cp build.properties.sample build.properties
  +  emacs build.properties
  +
  +NOTE:  Be *sure* that you do not check "build.properties" in to the CVS
  +repository.  This file is local to your own development environment, and
  +each developer will have their own version.
  +
  +(3) Build A Binary Distribution
  +
  +Jasper2 is built as part of Tomcat.  Follow the instructions in 
  +BUILDING.txt in the Tomcat project to build Tomcat with Jasper2.
  
  
  
  1.8       +3 -0      jakarta-tomcat-jasper/jasper2/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/build.xml,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- build.xml	12 Jun 2002 23:15:27 -0000	1.7
  +++ build.xml	16 Jul 2002 19:30:51 -0000	1.8
  @@ -28,6 +28,7 @@
       <pathelement location="${xmlParserAPIs.jar}"/>
       <pathelement location="${commons-collections.jar}"/>
       <pathelement location="${jasper.build}/shared/classes"/>
  +    <pathelement location="${jsp20el.jar}"/>
     </path>
   
     <!-- Construct unit tests classpath -->
  @@ -116,6 +117,8 @@
         </fileset>
       </jar>
   
  +    <!-- Supporting JAR Files -->
  +    <copy todir="${jasper.deploy}/common/lib" file="${jsp20el.jar}"/>
   
     </target>
   
  
  
  
  1.5       +18 -0     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Constants.java
  
  Index: Constants.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Constants.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- Constants.java	22 Jun 2002 10:19:03 -0000	1.4
  +++ Constants.java	16 Jul 2002 19:30:51 -0000	1.5
  @@ -67,6 +67,8 @@
    *
    * @author Anil K. Vijendran
    * @author Harish Prabandham
  + * @author Shawn Bayern
  + * @author Mark Roth
    */
   public class Constants {
       /**
  @@ -209,6 +211,22 @@
   
       public static final String IE_PLUGIN_URL = 
           "http://java.sun.com/products/plugin/1.2.2/jinstall-1_2_2-win.cab#Version=1,2,2,0";
  +
  +    /**
  +     * Information about conduit to EL interpreter.
  +     *  EL_INTERPRETER_CONDUIT_CLASS: name of class
  +     *  EL_INTERPRETER_CONDUIT_METHOD: name of static method within class
  +     */
  +    public static final String EL_INTERPRETER_CONDUIT_CLASS =
  +        "org.apache.jasper.runtime.ExpressionEvaluatorManager";
  +    public static final String EL_INTERPRETER_CONDUIT_METHOD =
  +        "evaluate";
  +
  +    /**
  +     * Prefix to use for generated temporary variable names
  +     */
  +    public static final String TEMP_VARIABLE_NAME_PREFIX =
  +        "_jspx_temp";
   
       /**
        * This is where all our error messages and such are stored. 
  
  
  
  1.9       +16 -18    jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/EmbededServletOptions.java
  
  Index: EmbededServletOptions.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/EmbededServletOptions.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- EmbededServletOptions.java	26 Jun 2002 16:50:37 -0000	1.8
  +++ EmbededServletOptions.java	16 Jul 2002 19:30:51 -0000	1.9
  @@ -105,7 +105,7 @@
        * Determines whether tag handler pooling is enabled.
        */
       public boolean poolingEnabled = true;
  -
  +    
       /**
        * Do you want support for "mapped" files? This will generate
        * servlet that has a print statement per line of the JSP file.
  @@ -190,11 +190,11 @@
       public boolean getLargeFile() {
           return largeFile;
       }
  -
  +    
       public boolean isPoolingEnabled() {
   	return poolingEnabled;
       }
  -    
  +
       /**
        * Are we supporting HTML mapped servlets?
        */
  @@ -283,9 +283,7 @@
        * Create an EmbededServletOptions object using data available from
        * ServletConfig and ServletContext. 
        */
  -    public EmbededServletOptions(ServletConfig config,
  -				 ServletContext context) {
  -
  +    public EmbededServletOptions(ServletConfig config, ServletContext context) {
           Enumeration enum=config.getInitParameterNames();
           while( enum.hasMoreElements() ) {
               String k=(String)enum.nextElement();
  @@ -317,15 +315,6 @@
               else Constants.message ("jsp.warning.largeFile", Logger.WARNING);
           }
   	
  -        String mapFile = config.getInitParameter("mappedfile"); 
  -        if (mapFile != null) {
  -            if (mapFile.equalsIgnoreCase("true"))
  -                this.mappedFile = true;
  -            else if (mapFile.equalsIgnoreCase("false"))
  -                this.mappedFile = false;
  -            else Constants.message ("jsp.warning.mappedFile", Logger.WARNING);
  -        }
  -
   	poolingEnabled = true;
           String poolingEnabledParam
   	    = config.getInitParameter("enablePooling"); 
  @@ -335,6 +324,15 @@
                   this.poolingEnabled = false;
               else Constants.message("jsp.warning.enablePooling",
   				   Logger.WARNING);
  +        }
  +
  +        String mapFile = config.getInitParameter("mappedfile"); 
  +        if (mapFile != null) {
  +            if (mapFile.equalsIgnoreCase("true"))
  +                this.mappedFile = true;
  +            else if (mapFile.equalsIgnoreCase("false"))
  +                this.mappedFile = false;
  +            else Constants.message ("jsp.warning.mappedFile", Logger.WARNING);
           }
   	
           String senderr = config.getInitParameter("sendErrToClient");
  
  
  
  1.7       +3 -18     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Options.java
  
  Index: Options.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Options.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- Options.java	26 Jun 2002 16:50:38 -0000	1.6
  +++ Options.java	16 Jul 2002 19:30:51 -0000	1.7
  @@ -77,13 +77,11 @@
    */
   public interface Options {
   
  -
       /**
        * Are we keeping generated code around?
        */
       public boolean getKeepGenerated();
   
  -
       /**
        * Are we supporting large files?
        */
  @@ -99,62 +97,52 @@
        */
       public boolean getMappedFile();
   
  -
       /**
        * Should errors be sent to client or thrown into stderr?
        */
       public boolean getSendErrorToClient();
    
  -
       /**
        * Should we include debug information in compiled class?
        */
       public boolean getClassDebugInfo();
   
  -
       /**
        * Background compile thread check interval in seconds
        */
       public int getCheckInterval();
   
  -
       /**
        * Is Jasper being used in development mode?
        */
       public boolean getDevelopment();
   
  -
       /**
        * JSP reloading check ?
        */
       public boolean getReloading();
   
  -
       /**
        * Class ID for use in the plugin tag when the browser is IE. 
        */
       public String getIeClassId();
   
  -
       /**
        * What is my scratch dir?
        */
       public File getScratchDir();
   
  -
       /**
        * What classpath should I use while compiling the servlets
        * generated from JSP files?
        */
       public String getClassPath();
   
  -
       /**
        * Compiler to use.
        */
       public String getCompiler();
   
  -
       /**
        * The cache for the location of the TLD's
        * for the various tag libraries 'exposed'
  @@ -168,12 +156,9 @@
        */
       public TldLocationsCache getTldLocationsCache();
   
  -
       /**
        * Java platform encoding to generate the JSP
        * page servlet.
        */
       public String getJavaEncoding();
  -
  -
   }
  
  
  
  1.3       +44 -21    jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Collector.java
  
  Index: Collector.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Collector.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Collector.java	13 Jun 2002 22:56:11 -0000	1.2
  +++ Collector.java	16 Jul 2002 19:30:51 -0000	1.3
  @@ -69,6 +69,7 @@
    * the PageInfo object.
    *
    * @author Kin-man Chung
  + * @author Mark Roth
    */
   
   public class Collector {
  @@ -137,12 +138,24 @@
   	}
   
           public void visit(Node.CustomTag n) throws JasperException {
  -
               curTagNesting++;
               if (curTagNesting > maxTagNesting) {
                   maxTagNesting = curTagNesting;
               }
  +            
  +            // Check to see what kinds of element we see as child elements
  +            checkSeen( n.getChildInfo(), n );
   
  +            curTagNesting--;
  +        }
  +
  +        /**
  +         * Check all child nodes for various elements and update the given
  +         * ChildInfo object accordingly.  Visits body in the process.
  +         */
  +        private void checkSeen( Node.ChildInfo ci, Node n ) 
  +            throws JasperException
  +        {
   	    // save values collected so far
   	    boolean scriptingElementSeenSave = scriptingElementSeen;
   	    scriptingElementSeen = false;
  @@ -156,31 +169,35 @@
   	    hasScriptingVars = false;
   
   	    // Scan attribute list for expressions
  -	    Node.JspAttribute[] attrs = n.getJspAttributes();
  -	    for (int i = 0; i < attrs.length; i++) {
  -		if (attrs[i].isExpression()) {
  -		    scriptingElementSeen = true;
  -		    break;
  -		}
  -	    }
  +            if( n instanceof Node.CustomTag ) {
  +                Node.CustomTag ct = (Node.CustomTag)n;
  +                Node.JspAttribute[] attrs = ct.getJspAttributes();
  +                for (int i = 0; i < attrs.length; i++) {
  +                    if (attrs[i].isExpression()) {
  +                        scriptingElementSeen = true;
  +                        break;
  +                    }
  +                }
  +            }
   
               visitBody(n);
   
  -	    if (!hasScriptingVars) {
  +            if( (n instanceof Node.CustomTag) && !hasScriptingVars) {
  +                Node.CustomTag ct = (Node.CustomTag)n;
   		// For some reason, varInfos is null when var is not defined
   		// in TEI, but tagVarInfos is empty array when var is not
   		// defined in tld.
  -		hasScriptingVars = n.getVariableInfos() != null || 
  -			(n.getTagVariableInfos() != null
  -			 && n.getTagVariableInfos().length > 0);
  +		hasScriptingVars = ct.getVariableInfos() != null || 
  +			(ct.getTagVariableInfos() != null
  +			 && ct.getTagVariableInfos().length > 0);
   	    }
   
   	    // Record if the tag element and its body contains any scriptlet.
  -	    n.setScriptless(! scriptingElementSeen);
  -	    n.setHasUsebean(usebeanSeen);
  -	    n.setHasIncludeAction(includeActionSeen);
  -	    n.setHasSetProperty(setPropertySeen);
  -	    n.setHasScriptingVars(hasScriptingVars);
  +	    ci.setScriptless(! scriptingElementSeen);
  +	    ci.setHasUsebean(usebeanSeen);
  +	    ci.setHasIncludeAction(includeActionSeen);
  +	    ci.setHasSetProperty(setPropertySeen);
  +	    ci.setHasScriptingVars(hasScriptingVars);
   
   	    // Propagate value of scriptingElementSeen up.
   	    scriptingElementSeen = scriptingElementSeen || scriptingElementSeenSave;
  @@ -188,10 +205,16 @@
   	    setPropertySeen = setPropertySeen || setPropertySeenSave;
   	    includeActionSeen = includeActionSeen || includeActionSeenSave;
   	    hasScriptingVars = hasScriptingVars || hasScriptingVarsSave;
  -
  -            curTagNesting--;
           }
   
  +        public void visit(Node.JspBody n) throws JasperException {
  +            checkSeen( n.getChildInfo(), n );
  +        }
  +        
  +        public void visit(Node.NamedAttribute n) throws JasperException {
  +            checkSeen( n.getChildInfo(), n );
  +        }
  +        
   	public void visit(Node.Declaration n) throws JasperException {
   	    scriptingElementSeen = true;
   	}
  
  
  
  1.19      +13 -3     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Compiler.java
  
  Index: Compiler.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Compiler.java,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- Compiler.java	21 Jun 2002 02:28:47 -0000	1.18
  +++ Compiler.java	16 Jul 2002 19:30:51 -0000	1.19
  @@ -94,6 +94,7 @@
    * @author Pierre Delisle
    * @author Kin-man Chung
    * @author Remy Maucherat
  + * @author Mark Roth
    */
   public class Compiler {
   
  @@ -223,6 +224,9 @@
   	ServletWriter writer = new ServletWriter(new PrintWriter(osw));
           ctxt.setWriter(writer);
   
  +        // Reset the temporary variable counter for the generator.
  +        JspUtil.resetTemporaryVariableName();
  +
   	// Parse the file
   	ParserController parserCtl = new ParserController(ctxt, this);
   	pageNodes = parserCtl.parse(ctxt.getJspFile());
  @@ -239,6 +243,9 @@
   	// generate servlet .java file
   	Generator.generate(writer, this, pageNodes);
           writer.close();
  +
  +        //JSR45 Support - note this needs to be checked by a JSR45 guru
  +	SmapUtil.generateSmap(ctxt, pageNodes, true);
       }
   
       /** 
  @@ -310,6 +317,9 @@
               if(errorReport!=null ) 
                   errDispatcher.javacError(errorReport, javaFileName, pageNodes);
           }
  +
  +        //JSR45 Support - note this needs to be checked by a JSR45 guru
  +	SmapUtil.installSmap(ctxt);
       }
   
       /** 
  
  
  
  1.2       +36 -5     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Dumper.java
  
  Index: Dumper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Dumper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Dumper.java	28 Mar 2002 18:46:15 -0000	1.1
  +++ Dumper.java	16 Jul 2002 19:30:51 -0000	1.2
  @@ -147,8 +147,9 @@
           }
   
           public void visit(Node.IncludeAction n) throws JasperException {
  -	    printAttributes("<jsp:include", n.getAttributes(), "/>");
  +	    printAttributes("<jsp:include", n.getAttributes(), ">");
   	    dumpBody(n);
  +            printString("</jsp:include>");
           }
   
           public void visit(Node.ForwardAction n) throws JasperException {
  @@ -162,7 +163,9 @@
           }
   
           public void visit(Node.SetProperty n) throws JasperException {
  -	    printAttributes("<jsp:setProperty", n.getAttributes(), "/>");
  +	    printAttributes("<jsp:setProperty", n.getAttributes(), ">");
  +            dumpBody(n);
  +            printString("</jsp:setProperty>");
           }
   
           public void visit(Node.UseBean n) throws JasperException {
  @@ -176,6 +179,34 @@
   	    dumpBody(n);
   	    printString("</jsp:plugin>");
   	}
  +        
  +        public void visit(Node.ParamsAction n) throws JasperException {
  +	    printAttributes("<jsp:params", n.getAttributes(), ">");
  +	    dumpBody(n);
  +	    printString("</jsp:params>");
  +        }
  +        
  +        public void visit(Node.ParamAction n) throws JasperException {
  +	    printAttributes("<jsp:param", n.getAttributes(), ">");
  +	    dumpBody(n);
  +	    printString("</jsp:param>");
  +        }
  +        
  +        public void visit(Node.NamedAttribute n) throws JasperException {
  +	    printAttributes("<jsp:attribute", n.getAttributes(), ">");
  +	    dumpBody(n);
  +	    printString("</jsp:attribute>");
  +        }
  +
  +        public void visit(Node.JspBody n) throws JasperException {
  +	    printAttributes("<jsp:body", n.getAttributes(), ">");
  +	    dumpBody(n);
  +	    printString("</jsp:body>");
  +        }
  +        
  +        public void visit(Node.ELExpression n) throws JasperException {
  +	    printString( "${" + new String( n.getText() ) + "}" );
  +        }
   
           public void visit(Node.CustomTag n) throws JasperException {
   	    printAttributes("<" + n.getName(), n.getAttributes(), ">");
  
  
  
  1.36      +951 -165  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java
  
  Index: Generator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java,v
  retrieving revision 1.35
  retrieving revision 1.36
  diff -u -r1.35 -r1.36
  --- Generator.java	27 Jun 2002 17:32:17 -0000	1.35
  +++ Generator.java	16 Jul 2002 19:30:51 -0000	1.36
  @@ -83,6 +83,8 @@
    * @author Pierre Delisle
    * @author Kin-man Chung
    * @author Jan Luehe
  + * @author Shawn Bayern
  + * @author Mark Roth
    * @author Denis Benoit
    */
   
  @@ -90,6 +92,7 @@
   
       private ServletWriter out;
       private MethodsBuffer methodsBuffer;
  +    private HelperClassBuffer helperClassBuffer;
       private ErrorDispatcher err;
       private BeanRepository beanInfo;
       private JspCompilationContext ctxt;
  @@ -102,13 +105,24 @@
        * @param s the input string
        * @return quoted and escaped string, per Java rule
        */
  -    private static String quote(String s) {
  +    static String quote(String s) {
   
   	if (s == null)
   	    return "null";
  +        
  +        return '"' + escape( s ) + '"';
  +    }
  +    
  +    /**
  +     * @param s the input string
  +     * @return escaped string, per Java rule
  +     */
  +    static String escape(String s) {
  +
  +	if (s == null)
  +	    return "";
   
   	StringBuffer b = new StringBuffer();
  -	b.append('"');
   	for (int i = 0; i < s.length(); i++) {
   	    char c = s.charAt(i);
   	    if (c == '"') 
  @@ -122,7 +136,6 @@
   	    else 
   		b.append(c);
   	}
  -	b.append('"');
   	return b.toString();
       }
   
  @@ -391,6 +404,9 @@
               out.printil("}");
               out.println();
           }
  +        
  +        // Static data for EL function maps:
  +        generateELFunctionMap();
   
    	// Class variable declarations
        	
  @@ -439,7 +455,7 @@
   
   	// Local variable declarations
   	out.printil("JspFactory _jspxFactory = null;");
  -	out.printil("javax.servlet.jsp.PageContext pageContext = null;");
  +	out.printil("PageContext pageContext = null;");
   	if (pageInfo.isSession())
   	    out.printil("HttpSession session = null;");
   
  @@ -492,6 +508,147 @@
   	out.println();
       }
   
  +    /**
  +     * Generates EL Function map section
  +     */
  +    private void generateELFunctionMap() 
  +        throws JasperException
  +    {
  +        Hashtable taglibs = pageInfo.getTagLibraries();
  +        Iterator iter = taglibs.keySet().iterator();
  +        boolean fnPresent = false;
  +        
  +        // Check to see if at least one function is present.
  +        while( iter.hasNext() ) {
  +            String key = (String)iter.next();
  +            TagLibraryInfo tli = (TagLibraryInfo)taglibs.get( key );
  +            if( tli.getFunctions().length > 0 ) {
  +                fnPresent = true;
  +                break;
  +            }
  +        }
  +        
  +        out.printil("private static java.util.HashMap _jspx_fnmap = null;");
  +        if( fnPresent ) {
  +            iter = taglibs.keySet().iterator();
  +            out.println();
  +            out.printil("static {");
  +            out.pushIndent();
  +            out.printil("_jspx_fnmap = new java.util.HashMap();");
  +            out.printil( "try {" );
  +            out.pushIndent();
  +            while( iter.hasNext() ) {
  +                String key = (String)iter.next();
  +                TagLibraryInfo tli = (TagLibraryInfo)taglibs.get( key );
  +                FunctionInfo[] fnInfo = tli.getFunctions();
  +                String fnPrefix = tli.getPrefixString();
  +                out.printil( "// Functions for " + tli.getShortName() );
  +                for( int i = 0; i < fnInfo.length; i++ ) {
  +                    String fnName = fnPrefix + ":" + fnInfo[i].getName();
  +                    String fnSignature = fnInfo[i].getFunctionSignature();
  +                    out.printin("_jspx_fnmap.put(");
  +                    out.print(quote(fnName));
  +                    out.print(", ");
  +                    out.print(fnInfo[i].getFunctionClass() + 
  +                        ".class.getDeclaredMethod(");
  +                    
  +                    try {
  +                        // Parse function signature, assuming syntax:
  +                        // <return-type> S <method-name> S? '('
  +                        // ( <arg-type> ( ',' <arg-type> )* )? ')'
  +                        String ws = " \t\n\r";
  +                        StringTokenizer sigTokenizer = new StringTokenizer( 
  +                            fnSignature, ws + "(),", true);
  +
  +                        // Skip <arg-type>:
  +                        sigTokenizer.nextToken();
  +
  +                        // Skip whitespace and read <method-name>:
  +                        String methodName;
  +                        do {
  +                            methodName = sigTokenizer.nextToken();
  +                        } while( ws.indexOf( methodName ) != -1 );
  +
  +                        out.print( quote( methodName ) );
  +                        out.print( ", new Class[] {" );
  +                        
  +                        // Skip whitespace and read '(':
  +                        String paren;
  +                        do {
  +                            paren = sigTokenizer.nextToken();
  +                        } while( ws.indexOf( paren ) != -1 );
  +
  +                        if( !paren.equals( "(" ) ) {
  +                            throw new JasperException( err.getString(
  +                                "jsp.error.tld.fn.invalid.signature",
  +                                tli.getShortName(), fnName ) );
  +                        }
  +
  +                        // ( <arg-type> S? ( ',' S? <arg-type> S? )* )? ')'
  +                        
  +                        // Skip whitespace and read <arg-type>:
  +                        String argType;
  +                        do {
  +                            argType = sigTokenizer.nextToken();
  +                        } while( ws.indexOf( argType ) != -1 );
  +
  +                        if( !argType.equals( ")" ) ) {
  +                            do {
  +                                if( ",(".indexOf( argType ) != -1 ) {
  +                                    throw new JasperException( err.getString(
  +                                        "jsp.error.tld.fn.invalid.signature",
  +                                        tli.getShortName(), fnName ) );
  +                                }
  +
  +                                out.print( argType + ".class" );
  +
  +                                String comma;
  +                                do {
  +                                    comma = sigTokenizer.nextToken();
  +                                } while( ws.indexOf( comma ) != -1 );
  +
  +                                if( comma.equals( ")" ) ) {
  +                                    break;
  +                                }
  +                                if( !comma.equals( "," ) ) {
  +                                    throw new JasperException( err.getString(
  +                                        "jsp.error.tld.fn.invalid.signature",
  +                                        tli.getShortName(), fnName ) );
  +                                }
  +
  +                                out.print( ", " );
  +
  +                                // <arg-type>
  +                                do {
  +                                    argType = sigTokenizer.nextToken();
  +                                } while( ws.indexOf( argType ) != -1 );
  +                            } while( true );
  +                        }
  +                        
  +                        out.println( "} ) );" );
  +                    }
  +                    catch( NoSuchElementException e ) {
  +                        throw new JasperException( err.getString(
  +                            "jsp.error.tld.fn.invalid.signature",
  +                            tli.getShortName(), fnName ) );
  +                    }
  +                }
  +            }
  +            out.popIndent();
  +            out.printil( "}" );
  +            out.printil( "catch( NoSuchMethodException e ) {" );
  +            out.pushIndent();
  +            out.printil( "throw new RuntimeException( \"" +
  +                "Invalid function mapping - no such method: \" + " +
  +                "e.getMessage(), e );" );
  +            out.popIndent();
  +            out.printil( "}" );
  +            out.popIndent();
  +            out.printil("}");
  +            out.println();
  +        }
  +    }
  +
       /*
        * Generates the servlet constructor.
        */
  @@ -544,18 +701,24 @@
   
   	private Hashtable tagVarNumbers;
   	private String parent;
  +	private String simpleTagHandlerVar;
  +	private boolean isSimpleTagHandler;
   
   	private ServletWriter out;
   	private MethodsBuffer methodsBuffer;
  +        private HelperClassBuffer helperClassBuffer;
   	private int methodNesting;
   
   	/**
   	 * Constructor.
   	 */
  -	public GenerateVisitor(ServletWriter out,
  -			       MethodsBuffer methodsBuffer) {
  +	public GenerateVisitor(ServletWriter out, 
  +            MethodsBuffer methodsBuffer, 
  +            HelperClassBuffer helperClassBuffer ) 
  +        {
   	    this.out = out;
   	    this.methodsBuffer = methodsBuffer;
  +            this.helperClassBuffer = helperClassBuffer;
   	    methodNesting = 0;
   	    handlerInfos = new Hashtable();
   	    tagVarNumbers = new Hashtable();
  @@ -563,22 +726,38 @@
   
   	/**
   	 * Returns an attribute value, optionally URL encoded.  If
  -	 * the value is a runtime expression, the result is the string for
  -	 * the expression, otherwise the result is the string literal,
  -	 * quoted and escaped.
  +         * the value is a runtime expression, the result is the expression
  +         * itself, as a string.  If the result is an EL expression, we insert
  +         * a call to the interpreter.  If the result is a Named Attribute
  +         * we insert the generated variable name.  Otherwise the result is a
  +         * string literal, quoted and escaped.
  +         *
   	 * @param attr An JspAttribute object
   	 * @param encode true if to be URL encoded
  +         * @param expectedType the expected type for an EL evaluation
  +         *        (ignored for attributes that aren't EL expressions)
  +         * @param defaultPrefix the default prefix for any EL functions
   	 */
  -	private String attributeValue(Node.JspAttribute attr, boolean encode) {
  +        private String attributeValue(Node.JspAttribute attr,
  +                                      boolean encode,
  +                                      Class expectedType,
  +                                      String defaultPrefix ) 
  +        {
   	    String v = attr.getValue();
  -	    if (v == null)
  +	    if (!attr.isNamedAttribute() && (v == null))
   		return "";
   
  -	    if (attr.isExpression()) {
  +            if (attr.isExpression() || attr.isELInterpreterInput()) {
  +		if (attr.isELInterpreterInput()) {
  +		    v = JspUtil.interpreterCall( attr.getValue(), 
  +                        expectedType, "_jspx_fnmap", defaultPrefix );
  +		}
   		if (encode) {
   		    return "java.net.URLEncoder.encode(" + v + ")";
   		}
   		return v;
  +            } else if( attr.isNamedAttribute() ) {
  +                return attr.getNamedAttributeNode().getTemporaryVariableName();
   	    } else {
   		if (encode) {
   		    v = URLEncoder.encode(v);
  @@ -593,7 +772,7 @@
   	 *
   	 * @param n the parent node for the param action nodes.
   	 */
  -	private void printParams(Node n, Node.JspAttribute page)
  +	private void printParams(Node n, String pageParam, boolean literal)
   						throws JasperException {
   
   	    class ParamVisitor extends Node.Visitor {
  @@ -610,19 +789,19 @@
   		    out.print(" + \"");
   		    out.print(n.getAttributeValue("name"));
   		    out.print("=\" + ");
  -		    out.print(attributeValue(n.getValue(), true));
  +		    out.print(attributeValue(n.getValue(), true, String.class,
  +                        "null" ));
   
   		    // The separator is '&' after the second use
   		    separator = "\"&\"";
   		}
   	    }
   
  -	    String pValue = page.getValue();
   	    String sep;
  -	    if (page.isExpression()) {
  -		sep = "((" + pValue + ").indexOf('?')>0? '&': '?')";
  +	    if (literal) {
  +		sep = pageParam.indexOf('?')>0? "\"&\"": "\"?\"";
   	    } else {
  -		sep = pValue.indexOf('?')>0? "\"&\"": "\"?\"";
  +		sep = "((" + pageParam + ").indexOf('?')>0? '&': '?')";
   	    }
   	    if (n.getBody() != null) {
   		n.getBody().visit(new ParamVisitor(sep));
  @@ -631,7 +810,7 @@
   
           public void visit(Node.Expression n) throws JasperException {
   	    n.setBeginJavaLine(out.getJavaLine());
  -	    out.printil("out.print(" + new String(n.getText()) + ");");
  +	    out.printil("out.write(" + new String(n.getText()) + ");");
   	    n.setEndJavaLine(out.getJavaLine());
           }
   
  @@ -641,34 +820,139 @@
   	    n.setEndJavaLine(out.getJavaLine());
   	}
   
  +        public void visit(Node.ELExpression n) throws JasperException {
  +            n.setBeginJavaLine(out.getJavaLine());
  +            if ( true /*isELEnabled*/ ) {
  +                out.printil(
  +                    "out.write("
  +                      + JspUtil.interpreterCall(
  +                      "${" + new String(n.getText()) + "}", String.class,
  +                      "_jspx_fnmap", "null" )
  +                    + ");");
  +            } else {
  +                out.printil("out.write(" +
  +                    quote("${" + new String(n.getText()) + "}") +
  +                    ");");
  +            }
  +            n.setEndJavaLine(out.getJavaLine());
  +        }
  +
   	public void visit(Node.IncludeAction n) throws JasperException {
   
   	    String flush = n.getAttributeValue("flush");
  +	    Node.JspAttribute page = n.getPage();
   
   	    boolean isFlush = false;	// default to false;
   	    if ("true".equals(flush))
   		isFlush = true;
   
   	    n.setBeginJavaLine(out.getJavaLine());
  -
  -	    out.printin("JspRuntimeLibrary.include(request, response, ");
  -	    out.print(attributeValue(n.getPage(), false));
  -	    printParams(n, n.getPage());
  +            
  +            String pageParam;
  +            if( page.isNamedAttribute() ) {
  +                // If the page for jsp:include was specified via
  +                // jsp:attribute, first generate code to evaluate
  +                // that body.
  +                pageParam = generateNamedAttributeValue( 
  +                    page.getNamedAttributeNode() );
  +            }
  +            else {
  +                pageParam = attributeValue(page, false, String.class, "null");
  +            }
  +            
  +            // If any of the params have their values specified by
  +            // jsp:attribute, prepare those values first.
  +            prepareParams( findJspBody( n ) );
  +            
  +            out.printin("JspRuntimeLibrary.include(request, response, " +
  +                pageParam );
  +	    printParams(n, pageParam, page.isLiteral());
   	    out.println(", out, " + isFlush + ");");
   
   	    n.setEndJavaLine(out.getJavaLine());
           }
  +        
  +        /**
  +         * Scans through all child nodes of the given parent for
  +         * <param> subelements.  For each <param> element, if its value
  +         * is specified via a Named Attribute (<jsp:attribute>), 
  +         * generate the code to evaluate those bodies first.
  +         * <p>
  +         * If parent is null, simply returns.
  +         */
  +        private void prepareParams( Node parent ) throws JasperException {
  +            if( parent == null ) return;
  +            
  +            Node.Nodes subelements = parent.getBody();
  +            if( subelements != null ) {
  +                for( int i = 0; i < subelements.size(); i++ ) {
  +                    Node n = subelements.getNode( i );
  +                    if( n instanceof Node.ParamAction ) {
  +                        Node.Nodes paramSubElements = n.getBody();
  +                        for( int j = 0; (paramSubElements != null) &&
  +                            (j < paramSubElements.size()); j++ ) 
  +                        {
  +                            Node m = paramSubElements.getNode( j );
  +                            if( m instanceof Node.NamedAttribute ) {
  +                                generateNamedAttributeValue( 
  +                                    (Node.NamedAttribute)m );
  +                            }
  +                        }
  +                    }
  +                }
  +            }
  +        }
  +        
  +        /**
  +         * Finds the <jsp:body> subelement of the given parent node.
  +         * If not found, null is returned.
  +         */
  +        private Node.JspBody findJspBody( Node parent ) 
  +            throws JasperException 
  +        {
  +            Node.JspBody result = null;
  +            
  +            Node.Nodes subelements = parent.getBody();
  +            for( int i = 0; (subelements != null) &&
  +                (i < subelements.size()); i++ )
  +            {
  +                Node n = subelements.getNode( i );
  +                if( n instanceof Node.JspBody ) {
  +                    result = (Node.JspBody)n;
  +                    break;
  +                }
  +            }
  +            
  +            return result;
  +        }
   
   	public void visit(Node.ForwardAction n) throws JasperException {
  -	    String page = n.getAttributeValue("page");
  +	    Node.JspAttribute page = n.getPage();
   
   	    n.setBeginJavaLine(out.getJavaLine());
   
   	    out.printil("if (true) {");	// So that javac won't complain about
   	    out.pushIndent();		// codes after "return"
  +
  +            String pageParam;
  +            if( page.isNamedAttribute() ) {
  +                // If the page for jsp:forward was specified via
  +                // jsp:attribute, first generate code to evaluate
  +                // that body.
  +                pageParam = generateNamedAttributeValue(
  +                    page.getNamedAttributeNode() );
  +            }
  +            else {
  +                pageParam = attributeValue(page, false, String.class, "null");
  +            }
  +            
  +            // If any of the params have their values specified by
  +            // jsp:attribute, prepare those values first.
  +            prepareParams( findJspBody( n ) );
  +            
   	    out.printin("pageContext.forward(");
  -	    out.print(attributeValue(n.getPage(), false));
  -	    printParams(n, n.getPage());
  +	    out.print( pageParam );
  +	    printParams(n, pageParam, page.isLiteral());
   	    out.println(");");
   	    out.printil((methodNesting > 0)? "return true;": "return;");
   	    out.popIndent();
  @@ -691,14 +975,14 @@
   		java.lang.reflect.Method meth =
   		    JspRuntimeLibrary.getReadMethod(bean, property);
   		String methodName = meth.getName();
  -		out.printil("out.print(JspRuntimeLibrary.toString(" +
  +		out.printil("out.write(JspRuntimeLibrary.toString(" +
   			    "(((" + beanName + ")pageContext.findAttribute(" +
   			    "\"" + name + "\"))." + methodName + "())));");
   	    } else {
   		// The object could be a custom action with an associated
   		// VariableInfo entry for this name.
   		// Get the class name and then introspect at runtime.
  -		out.printil("out.print(JspRuntimeLibrary.toString" +
  +		out.printil("out.write(JspRuntimeLibrary.toString" +
   			    "(JspRuntimeLibrary.handleGetProperty" +
   			    "(pageContext.findAttribute(\"" +
   			    name + "\"), \"" + property + "\")));");
  @@ -730,13 +1014,36 @@
   		out.printil("JspRuntimeLibrary.handleSetProperty(" + 
   			    "pageContext.findAttribute(\""  + name + "\"), \""
   			    + property + "\","); 
  -		out.print(attributeValue(value, false));
  +		out.print(attributeValue(value, false, null, "null"));
   		out.println(");");
  +            } else if (value.isELInterpreterInput()) {
  +                // We've got to resolve the very call to the interpreter
  +                // at runtime since we don't know what type to expect
  +                // in the general case; we thus can't hard-wire the call
  +                // into the generated code.  (XXX We could, however,
  +                // optimize the case where the bean is exposed with
  +                // <jsp:useBean>, much as the code here does for
  +                // getProperty.)
  +                out.printil("JspRuntimeLibrary.handleSetPropertyExpression(" +
  +                    "pageContext.findAttribute(\""  + name + "\"), \""
  +                    + property + "\", "
  +                    + quote(value.getValue()) + ", "
  +                    + "pageContext, _jspx_fnmap);");
  +            } else if( value.isNamedAttribute() ) {
  +                // If the value for setProperty was specified via
  +                // jsp:attribute, first generate code to evaluate
  +                // that body.
  +                String valueVarName = generateNamedAttributeValue(
  +                    value.getNamedAttributeNode() );
  +                out.println("JspRuntimeLibrary.handleSetProperty(" +
  +                            "pageContext.findAttribute(\""  + name + "\"), \""
  +                            + property + "\", " + valueVarName + " );" );
  +
   	    } else {
   		out.printil("JspRuntimeLibrary.introspecthelper(" +
   			    "pageContext.findAttribute(\"" + name + "\"), \"" +
  -			    property + "\",");
  -		out.print(attributeValue(value, false));
  +    			    property + "\",");
  +		out.print(attributeValue(value, false, null, "null"));
   		out.println(",null, null, false);");
   	    }
   
  @@ -814,7 +1121,17 @@
   		 */
   		String className;
   		if (beanName != null) {
  -		    className = attributeValue(beanName, false);
  +                    if( beanName.isNamedAttribute() ) {
  +                        // If the value for beanName was specified via
  +                        // jsp:attribute, first generate code to evaluate
  +                        // that body.
  +                        className = generateNamedAttributeValue(
  +                            beanName.getNamedAttributeNode() );
  +                    }
  +                    else {
  +                        className = attributeValue(beanName, false,
  +                            String.class, "null");
  +                    }
   		}
   		else {
   		    // Implies klass is not null
  @@ -901,15 +1218,31 @@
   		    else if (name.equalsIgnoreCase ("type"))
   			name = "java_type";
   
  -		    String s0 = makeAttr("name", name) + " value=" +
  -			        attributeValue(n.getValue(), false);
  -
  -		    if (ie) {
  -			s0 = "<PARAM" + s0 + '>';
  -		    }
  -
   		    n.setBeginJavaLine(out.getJavaLine());
  -		    out.printil("out.println(" + quote(s0) + ");");
  +                    // XXX - Fixed a bug here - value used to be output
  +                    // inline, which is only okay if value is not an EL
  +                    // expression.  Also, key/value pairs for the
  +                    // embed tag were not being generated correctly.
  +                    // Double check that this is now the correct behavior.
  +                    if( ie ) {
  +                        // We want something of the form
  +                        // out.println( "<PARAM name=\"blah\" 
  +                        //     value=\"" + ... + "\">" );
  +                        out.printil( "out.write( \"<PARAM name=\\\"" +
  +                            escape( name ) + "\\\" value=\\\"\" + " +
  +                            attributeValue( n.getValue(), false, String.class, 
  +                            "null" ) + " + \"\\\">\" );" );
  +                        out.printil( "out.write(\"\n\");" );
  +                    }
  +                    else {
  +                        // We want something of the form
  +                        // out.print( " blah=\"" + ... + "\"" );
  +                        out.printil( "out.write( \" " + escape( name ) +
  +                            "=\\\"\" + " + 
  +                            attributeValue( n.getValue(), false, String.class,
  +                            "null" ) + " + \"\\\"\" );" );
  +                    }
  +                    
   		    n.setEndJavaLine(out.getJavaLine());
   		}
   	    }
  @@ -927,6 +1260,30 @@
   	    String codebase = n.getAttributeValue("codebase");
   	    String archive = n.getAttributeValue("archive");
   	    String jreversion = n.getAttributeValue("jreversion");
  +            
  +            String widthStr = null;
  +            if( width != null ) {
  +                if( width.isNamedAttribute() ) {
  +                    widthStr = generateNamedAttributeValue(
  +                        width.getNamedAttributeNode() );
  +                }
  +                else {
  +                    widthStr = attributeValue( width, false, String.class,
  +                        "null" );
  +                }
  +            }
  +            
  +            String heightStr = null;
  +            if( height != null ) {
  +                if( height.isNamedAttribute() ) {
  +                    heightStr = generateNamedAttributeValue(
  +                        height.getNamedAttributeNode() );
  +                }
  +                else {
  +                    heightStr = attributeValue( height, false, String.class,
  +                        "null" );
  +                }
  +            }
   
   	    if (iepluginurl == null)
   		iepluginurl = Constants.IE_PLUGIN_URL;
  @@ -935,50 +1292,66 @@
   
   
   	    n.setBeginJavaLine(out.getJavaLine());
  +            
  +            // If any of the params have their values specified by
  +            // jsp:attribute, prepare those values first.
  +            // Look for a params node and prepare its param subelements:
  +            Node.JspBody jspBody = findJspBody( n );
  +            if( jspBody != null ) {
  +                Node.Nodes subelements = jspBody.getBody();
  +                if( subelements != null ) {
  +                    for( int i = 0; i < subelements.size(); i++ ) {
  +                        Node m = subelements.getNode( i );
  +                        if( m instanceof Node.ParamsAction ) {
  +                            prepareParams( m );
  +                            break;
  +                        }
  +                    }
  +                }
  +            }
  +            
  +            // XXX - Fixed a bug here - width and height can be set 
  +            // dynamically.  Double-check if this generation is correct.
  +            
   	    // IE style plugin
   	    // <OBJECT ...>
   	    // First compose the runtime output string 
  -	    String s0 = "<OBJECT classid=\"" + ctxt.getOptions().getIeClassId()+
  -			"\"" + makeAttr("name", name);
  -	    String s1, s2;
  -	    if (width != null) {
  -		if (width.isExpression()) {
  -		    s1 = quote(s0 + " width=\"") + " + " + width.getValue() +
  -			" + " + quote("\"");
  -		} else {
  -		    s1 = quote(s0 + makeAttr("width", width.getValue()));
  -		}
  -	    } else {
  -		s1 = quote(s0);
  -	    }
  -	    if (height != null) {
  -		if (height.isExpression()) {
  -		    s2 = quote(" height=\"") + " + " + height.getValue() +
  -			" + " + quote("\"");
  -		} else {
  -		    s2 = quote(makeAttr("height", height.getValue()));
  -		}
  -	    } else {
  -		s2 = "\"\"";
  -	    }
  -	    String s3 = quote(makeAttr("hspace", hspace) +
  -				makeAttr("vspace", vspace) +
  -				makeAttr("align", align) +
  -				makeAttr("codebase", iepluginurl) +
  -				'>');
  +	    String s0 = "<OBJECT classid=" + ctxt.getOptions().getIeClassId() +
  +			makeAttr("name", name);
  +            
  +            String s1 = "";
  +            if( width != null ) {
  +                s1 = " + \" width=\\\"\" + " + widthStr + " + \"\\\"\"";
  +            }
  +            
  +            String s2 = "";
  +            if( height != null ) {
  +                s2 = " + \" height=\\\"\" + " + heightStr + " + \"\\\"\"";
  +            }
  +                        
  +            String s3 = makeAttr("hspace", hspace) +
  +			makeAttr("vspace", vspace) +
  +			makeAttr("align", align) +
  +			makeAttr("codebase", iepluginurl) +
  +			'>';
  +            
   	    // Then print the output string to the java file
  -	    out.printil("out.println(" + s1 + " + " + s2 + " + " + s3 + ");");
  +	    out.printil("out.write(" + 
  +                quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
  +	    out.printil("out.write(\"\\n\");");
   
   	    // <PARAM > for java_code
   	    s0 = "<PARAM name=\"java_code\"" + makeAttr("value", code) + '>';
  -	    out.printil("out.println(" + quote(s0) + ");");
  +	    out.printil("out.write(" + quote(s0) + ");");
  +	    out.printil("out.write(\"\\n\");");
   
   	    // <PARAM > for java_codebase
   	    if (codebase != null) {
   		s0 = "<PARAM name=\"java_codebase\"" + 
   		     makeAttr("value", codebase) +
   		     '>';
  -		out.printil("out.println(" + quote(s0) + ");");
  +		out.printil("out.write(" + quote(s0) + ");");
  +		out.printil("out.write(\"\\n\");");
   	    }
   
   	    // <PARAM > for java_archive
  @@ -986,7 +1359,8 @@
   		s0 = "<PARAM name=\"java_archive\"" +
   		     makeAttr("value", archive) +
   		     '>';
  -		out.printil("out.println(" + quote(s0) + ");");
  +		out.printil("out.write(" + quote(s0) + ");");
  +		out.printil("out.write(\"\\n\");");
   	    }
   
   	    // <PARAM > for type
  @@ -994,7 +1368,8 @@
   		 makeAttr("value", "application/x-java-" + type + ";" +
   			  ((jreversion==null)? "": "version=" + jreversion)) +
   		 '>';
  -	    out.printil("out.println(" + quote(s0) + ");");
  +	    out.printil("out.write(" + quote(s0) + ");");
  +	    out.printil("out.write(\"\\n\");");
   
   	    /*
   	     * generate a <PARAM> for each <jsp:param> in the plugin body
  @@ -1005,41 +1380,25 @@
   	    /*
   	     * Netscape style plugin part
   	     */
  -	    out.printil("out.println(" + quote("<COMMENT>") + ");");
  +	    out.printil("out.write(" + quote("<COMMENT>") + ");");
  +	    out.printil("out.write(\"\\n\");");
   	    s0 = "<EMBED" +
   		 makeAttr("type", "application/x-java-" + type + ";" +
   			  ((jreversion==null)? "": "version=" + jreversion)) +
   		 makeAttr("name", name);
  -
  -	    if (width != null) {
  -		if (width.isExpression()) {
  -		    s1 = quote(s0 + " width=\"") + " + " + width.getValue() +
  -			" + " + quote("\"");
  -		} else {
  -		    s1 = quote(s0 + makeAttr("width", width.getValue()));
  -		}
  -	    } else {
  -		s1 = quote(s0);
  -	    }
  -	    if (height != null) {
  -		if (height.isExpression()) {
  -		    s2 = quote(" height=\"") + " + " + height.getValue() +
  -		       " + " + quote("\"");
  -		} else {
  -		    s2 = quote(makeAttr("height", height.getValue()));
  -		}
  -	    } else {
  -		s2 = "\"\"";
  -	    }
  -
  -	    s3 = quote(makeAttr("hspace", hspace) +
  -			 makeAttr("vspace", vspace) +
  -			 makeAttr("align", align) +
  -			 makeAttr("pluginspage", nspluginurl) +
  -			 makeAttr("java_code", code) +
  -			 makeAttr("java_codebase", codebase) +
  -			 makeAttr("java_archive", archive));
  -	    out.printil("out.println(" + s1 + " + " + s2 + " + " + s3 + ");");
  +                         
  +            // s1 and s2 are the same as before.
  +            
  +	    s3 = makeAttr("hspace", hspace) +
  +		 makeAttr("vspace", vspace) +
  +		 makeAttr("align", align) +
  +		 makeAttr("pluginspage", nspluginurl) +
  +                 makeAttr("java_code", code) +
  +		 makeAttr("java_codebase", codebase) +
  +		 makeAttr("java_archive", archive);
  +	    out.printil("out.write(" + 
  +                quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
  +	    out.printil("out.write(\"\\n\");");
   		 
   	    /*
   	     * Generate a 'attr = "value"' for each <jsp:param> in plugin body
  @@ -1047,10 +1406,13 @@
   	    if (n.getBody() != null)
   		n.getBody().visit(new ParamVisitor(false)); 
   
  -	    out.printil("out.println(" + quote(">") + ");");
  +	    out.printil("out.write(" + quote(">") + ");");
  +	    out.printil("out.write(\"\\n\");");
   
  -	    out.printil("out.println(" + quote("<NOEMBED>") + ");");
  -	    out.printil("out.println(" + quote("</COMMENT>") + ");");
  +	    out.printil("out.write(" + quote("<NOEMBED>") + ");");
  +	    out.printil("out.write(\"\\n\");");
  +	    out.printil("out.write(" + quote("</COMMENT>") + ");");
  +	    out.printil("out.write(\"\\n\");");
   
   	    /*
   	     * Fallback
  @@ -1059,19 +1421,26 @@
   		n.getBody().visit(new Node.Visitor() {
   		    public void visit(Node.FallBackAction n) {
   			n.setBeginJavaLine(out.getJavaLine());
  -			out.printil("out.println(" +
  +			out.printil("out.write(" +
   				    quote(new String(n.getText())) + ");");
  +			out.printil("out.write(\"\\n\");");
   			n.setEndJavaLine(out.getJavaLine());
   		    }
   		});
   	    }
   
  -	    out.printil("out.println(" + quote("</NOEMBED></EMBED>") + ");");
  -	    out.printil("out.println(" + quote("</OBJECT>") + ");");
  +	    out.printil("out.write(" + quote("</NOEMBED></EMBED>") + ");");
  +	    out.printil("out.write(\"\\n\");");
  +	    out.printil("out.write(" + quote("</OBJECT>") + ");");
  +	    out.printil("out.write(\"\\n\");");
   
   	    n.setEndJavaLine(out.getJavaLine());
   	}
   
  +        public void visit(Node.NamedAttribute n) throws JasperException {
  +            // Don't visit body of this tag - we already did earlier.
  +        }
  +
           public void visit(Node.CustomTag n) throws JasperException {
   
   	    Hashtable handlerInfosByShortName = (Hashtable)
  @@ -1097,11 +1466,23 @@
   	    String tagEvalVar = "_jspx_eval_" + baseVar;
   	    String tagHandlerVar = "_jspx_th_" + baseVar;
   
  +	    Class tagHandlerClass = handlerInfo.getTagHandlerClass();
  +	    boolean implementsIterationTag = 
  +		IterationTag.class.isAssignableFrom(tagHandlerClass);
  +	    boolean implementsBodyTag = 
  +		BodyTag.class.isAssignableFrom(tagHandlerClass);
  +	    boolean implementsTryCatchFinally = 
  +		TryCatchFinally.class.isAssignableFrom(tagHandlerClass);
  +	    boolean implementsSimpleTag = 
  +		SimpleTag.class.isAssignableFrom(tagHandlerClass);
  +
   	    // If the tag contains no scripting element, generate its codes
   	    // to a method.
   	    ServletWriter outSave = null;
   	    MethodsBuffer methodsBufferSave = null;
  -	    if (n.isScriptless() && !n.hasScriptingVars()) {
  +            Node.ChildInfo ci = n.getChildInfo();
  +	    if (implementsSimpleTag
  +		    || (ci.isScriptless() && !ci.hasScriptingVars())) {
   		// The tag handler and its body code can reside in a separate
   		// method if it is scriptless and does not have any scripting
   		// variable defined.
  @@ -1140,48 +1521,44 @@
   		    out.print(parent);
   		    out.print(", ");
   		}
  -//		out.println("javax.servlet.jsp.PageContext pageContext, JspxState _jspxState)");
  -		out.println("javax.servlet.jsp.PageContext pageContext)");
  +//		out.println("PageContext pageContext, JspxState _jspxState)");
  +		out.println("PageContext pageContext)");
   		out.printil("        throws Throwable {");
   		out.pushIndent();
   
   		// Initilaize local variables used in this method.
   		out.printil("JspWriter out = pageContext.getOut();");
  -		if (n.isHasUsebean()) {
  -		    out.println("HttpSession session = pageContext.getSession();");
  -		    out.println("ServletContext application = pageContext.getServletContext();");
  -		}
  -		if (n.isHasUsebean() || n.isHasIncludeAction() || n.isHasSetProperty()) {
  -		    out.println("HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();");
  -		}
  -		if (n.isHasIncludeAction()) {
  -		    out.println("ServletResponse response = pageContext.getResponse();");
  -		}
  -	    }
  +                generateLocalVariables( out, n );
  +            }
   
  -	    Class tagHandlerClass = handlerInfo.getTagHandlerClass();
  -	    boolean implementsIterationTag = 
  -		IterationTag.class.isAssignableFrom(tagHandlerClass);
  -	    boolean implementsBodyTag = 
  -		BodyTag.class.isAssignableFrom(tagHandlerClass);
  -	    boolean implementsTryCatchFinally = 
  -		TryCatchFinally.class.isAssignableFrom(tagHandlerClass);
  +	    if (implementsSimpleTag) {
  +		generateCustomDoTag(n, handlerInfo, tagHandlerVar,
  +				    implementsTryCatchFinally);
  +	    } else {
  +		/*
  +		 * Classic tag handler: Generate code for start element, body,
  +		 * and end element
  +		 */
  +		generateCustomStart(n, handlerInfo, tagHandlerVar, tagEvalVar,
  +				    implementsIterationTag, implementsBodyTag,
  +				    implementsTryCatchFinally);
  +
  +		// visit body
  +		String tmpParent = parent;
  +		parent = tagHandlerVar;
  +		boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
  +		isSimpleTagHandler = false;
  +		visitBody(n);
  +		parent = tmpParent;
  +		isSimpleTagHandler = tmpIsSimpleTagHandler;
   
  -	    // Generate code for start tag, body, and end tag
  -	    generateCustomStart(n, handlerInfo, tagHandlerVar, tagEvalVar,
  -			      implementsIterationTag, implementsBodyTag,
  -			      implementsTryCatchFinally);
  -
  -	    String tmpParent = parent;
  -	    parent = tagHandlerVar;
  -	    visitBody(n);
  -
  -	    parent = tmpParent;
  -	    generateCustomEnd(n, tagHandlerVar, tagEvalVar,
  -			      implementsIterationTag, implementsBodyTag,
  -			      implementsTryCatchFinally);
  +		generateCustomEnd(n, tagHandlerVar, tagEvalVar,
  +				  implementsIterationTag, implementsBodyTag,
  +				  implementsTryCatchFinally);
  +	    }
   
  -	    if (n.isScriptless() && !n.hasScriptingVars()) {
  +	    if (implementsSimpleTag
  +		    || (ci.isScriptless() && !ci.hasScriptingVars())) {
   		// Generate end of method
   		if (methodNesting > 0) {
   		    out.printil("return false;");
  @@ -1301,6 +1678,27 @@
   	    n.setEndJavaLine(out.getJavaLine());
   	}
   
  +	public void visit(Node.JspBody n) throws JasperException {
  +	    if (isSimpleTagHandler) {
  +		out.printin(simpleTagHandlerVar);
  +		out.print(".setJspBody(");
  +
  +		Node.JspAttribute value = n.getValue();
  +		if (value != null) {
  +		    out.print(attributeValue(value, false, JspFragment.class,
  +                        "null" ));
  +		} else {
  +                    // Parent node must be a CustomTag since 
  +                    // isSimpleTagHandler is set to true.
  +		    generateJspFragment((Node.CustomTag)n.getParent(), 
  +                        simpleTagHandlerVar);
  +		}
  +		out.println(");");
  +	    } else {
  +		visitBody(n);
  +	    }
  +	}
  +
   	private void generateCustomStart(Node.CustomTag n,
   					 TagHandlerInfo handlerInfo,
   					 String tagHandlerVar,
  @@ -1347,7 +1745,7 @@
   		out.println("();");
   	    }
   
  -	    generateSetters(n, tagHandlerVar, handlerInfo);
  +	    generateSetters(n, tagHandlerVar, handlerInfo, false);
   	    
               if (implementsTryCatchFinally) {
                   out.printil("try {");
  @@ -1377,12 +1775,9 @@
   		    out.println(" != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
   		    // Assume EVAL_BODY_BUFFERED
   		    out.pushIndent();
  -                    out.printil("javax.servlet.jsp.tagext.BodyContent _bc = pageContext.pushBody();");
  -                    out.printil("_bc.clear();");
  -                    out.printil("out = _bc;");
  -
  +                    out.printil("out = pageContext.pushBody();");
   		    out.printin(tagHandlerVar);
  -		    out.println(".setBodyContent(_bc);");
  +		    out.println(".setBodyContent((javax.servlet.jsp.tagext.BodyContent) out);");
   		    out.printin(tagHandlerVar);
   		    out.println(".doInitBody();");
   
  @@ -1468,7 +1863,7 @@
                   out.pushIndent();
   		out.printin(tagHandlerVar);
   		out.println(".doFinally();");
  -            }
  +	    }
   
   	    if (ctxt.getOptions().isPoolingEnabled()) {
                   out.printin(n.getTagHandlerPoolName());
  @@ -1489,6 +1884,103 @@
   	    n.setEndJavaLine(out.getJavaLine());
   	}
   
  +	private void generateCustomDoTag(Node.CustomTag n,
  +					 TagHandlerInfo handlerInfo,
  +					 String tagHandlerVar,
  +					 boolean implementsTryCatchFinally)
  +	                    throws JasperException {
  +
  +	    Class tagHandlerClass = handlerInfo.getTagHandlerClass();
  +
  +	    n.setBeginJavaLine(out.getJavaLine());
  +	    out.printin("/* ----  ");
  +	    out.print(n.getName());
  +	    out.println(" ---- */");
  +	    out.printil("{");
  +	    out.pushIndent();
  +
  +	    /*
  +	     * Save current values of scripting variables, so that the 
  +	     * scripting variables may be synchronized without affecting their
  +	     * original values
  +	     */
  +	    saveScriptingVariables(n);
  +
  +	    out.printin(tagHandlerClass.getName());
  +	    out.print(" ");
  +	    out.print(tagHandlerVar);
  +	    out.print(" = ");
  +	    out.print("new ");
  +	    out.print(tagHandlerClass.getName());
  +	    out.println("();");
  +
  +	    generateSetters(n, tagHandlerVar, handlerInfo, true);
  +
  +            if (implementsTryCatchFinally) {
  +                out.printil("try {");
  +                out.pushIndent();
  +            }
  +	    
  +	    // Set the body
  +	    if (findJspBody(n) == null) {
  +		/*
  +		 * Encapsulate body of custom tag invocation in JspFragment
  +		 * and pass it to tag handler's setJspBody()
  +		 */
  +		out.printin(tagHandlerVar);
  +		out.print(".setJspBody(");
  +		generateJspFragment(n, tagHandlerVar);
  +		out.println(");");
  +	    } else {
  +		/*
  +		 * Body of tag is the body of the <jsp:body> element.
  +		 * The visit method for that element is going to encapsulate
  +		 * that element's body in a JspFragment and pass it to
  +		 * the tag handler's setJspBody()
  +		 */
  +		String tmpTagHandlerVar = simpleTagHandlerVar;
  +		simpleTagHandlerVar = tagHandlerVar;
  +		boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
  +		isSimpleTagHandler = true;
  +		visitBody(n);
  +		simpleTagHandlerVar = tmpTagHandlerVar;
  +		isSimpleTagHandler = tmpIsSimpleTagHandler;
  +	    }
  +
  +	    out.printin("if (");
  +	    out.print(tagHandlerVar);
  +	    out.println(".doTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE)");
  +	    out.pushIndent();
  +	    out.printil((methodNesting > 0)? "return true;": "return;");
  +	    out.popIndent();
  +
  +	    // Synchronize AT_BEGIN and AT_END scripting variables
  +	    syncScriptingVariables(n, VariableInfo.AT_BEGIN);
  +	    syncScriptingVariables(n, VariableInfo.AT_END);
  +
  +	    // TryCatchFinally
  +	    if (implementsTryCatchFinally) {
  +                out.popIndent(); // try
  +		out.printil("} catch (Throwable _jspx_exception) {");
  +		out.pushIndent();
  +		out.printin(tagHandlerVar);
  +		out.println(".doCatch(_jspx_exception);");
  +		out.popIndent();
  +                out.printil("} finally {");
  +                out.pushIndent();
  +		out.printin(tagHandlerVar);
  +		out.println(".doFinally();");
  +                out.popIndent();
  +                out.println("}");
  +	    }
  +
  +	    restoreScriptingVariables(n);
  +	    out.popIndent();
  +	    out.printil("}");
  +
  +	    n.setEndJavaLine(out.getJavaLine());
  +	}
  +
   	/*
   	 * Declares any NESTED scripting variables of the given custom tag,
   	 * if the given custom tag is not nested inside itself (i.e, has a
  @@ -1738,12 +2230,18 @@
   	    }
   	}
   
  -	private void generateSetters(Node.CustomTag n, String tagHandlerVar,
  -				     TagHandlerInfo handlerInfo)
  +	private void generateSetters(Node.CustomTag n,
  +				     String tagHandlerVar,
  +				     TagHandlerInfo handlerInfo,
  +				     boolean simpleTag)
   	            throws JasperException {
   
   	    out.printin(tagHandlerVar);
  -	    out.println(".setPageContext(pageContext);");
  +	    if (simpleTag) {
  +		out.println(".setJspContext(pageContext);");
  +	    } else {
  +		out.println(".setPageContext(pageContext);");
  +	    }
   	    out.printin(tagHandlerVar);
   	    out.print(".setParent(");
   	    out.print(parent);
  @@ -1753,7 +2251,24 @@
   	    for (int i=0; i<attrs.length; i++) {
   		String attrValue = attrs[i].getValue();
   		if (attrValue == null) {
  -		    continue;
  +                    if( attrs[i].isNamedAttribute() ) {
  +                        if( n.checkIfAttributeIsJspFragment( 
  +                            attrs[i].getName() ) ) 
  +                        {
  +                            // XXX - no need to generate temporary variable 
  +                            // here
  +                            attrValue = generateNamedAttributeJspFragment( 
  +                                attrs[i].getNamedAttributeNode(),
  +                                tagHandlerVar );
  +                        }
  +                        else {
  +                            attrValue = generateNamedAttributeValue( 
  +                                attrs[i].getNamedAttributeNode() );
  +                        }
  +                    } 
  +                    else {
  +                        continue;
  +                    }
   		}
   		String attrName = attrs[i].getName();
   		Method m = handlerInfo.getSetterMethod(attrName);
  @@ -1765,7 +2280,15 @@
   		Class c[] = m.getParameterTypes();
   		// XXX assert(c.length > 0)
   
  -		if (!attrs[i].isExpression()) {
  +		if (attrs[i].isExpression() || attrs[i].isNamedAttribute()) {
  +		    // Do nothing
  +		}
  +		else if (attrs[i].isELInterpreterInput()) {
  +                    // run attrValue through the expression interpreter
  +                    attrValue = JspUtil.interpreterCall( attrValue,
  +                        c[0], "_jspx_fnmap", n.getPrefix() );
  +                }
  +                else {
   		    attrValue = convertString(c[0], attrValue, attrName,
   					      handlerInfo.getPropertyEditorClass(attrName));
   		}
  @@ -1848,10 +2371,137 @@
   		    + quote(s) + ")";
   	    }
   	}   
  -    }
   
  +	/**
  +	 * Generates anonymous JspFragment inner class which is passed as an
  +	 * argument to SimpleTag.setJspBody().
  +	 */
  +	private void generateJspFragment(Node parent, String tagHandlerVar) 
  +            throws JasperException
  +        {
  +            // XXX - A possible optimization here would be to check to see
  +            // if the old child of the parent node is TemplateText.  If so,
  +            // we know there won't be any parameters, etc, so we can 
  +            // generate a low-overhead JspFragment that just echoes its
  +            // body.  The implementation of this fragment can come from
  +            // the org.apache.jasper.runtime package as a support class.
  +            int id = helperClassBuffer.getFragmentId();
  +            helperClassBuffer.openFragment( parent );
  +            ServletWriter outSave = out;
  +            out = helperClassBuffer.getOut();
  +            visitBody( parent );
  +            out = outSave;
  +            helperClassBuffer.closeFragment();
  +            // XXX - Need to change pageContext to jspContext if
  +            // we're not in a place where pageContext is defined (e.g.
  +            // in a fragment or in a tag file.
  +	    out.print( "new " + helperClassBuffer.getClassName() + 
  +                "( " + id + ", pageContext, " + tagHandlerVar + ")" );
  +	}
  +        
  +        /**
  +         * Generate the code required to obtain the runtime value of the
  +         * given named attribute.
  +         *
  +         * @return The name of the temporary variable the result is stored in.
  +         */
  +        public String generateNamedAttributeValue( Node.NamedAttribute n )
  +            throws JasperException
  +        {
  +            String varName = n.getTemporaryVariableName();
  +
  +            // If the only body element for this named attribute node is
  +            // template text, we need not generate an extra call to
  +            // pushBody and popBody.  Maybe we can further optimize
  +            // here by getting rid of the temporary variable, but in
  +            // reality it looks like javac does this for us.
  +            boolean templateTextOptimization = false;
  +            Node.Nodes body = n.getBody();
  +            if( body.size() == 1 ) {
  +                Node bodyElement = body.getNode( 0 );
  +                if( bodyElement instanceof Node.TemplateText ) {
  +                    templateTextOptimization = true;
  +                    out.printil( "String " + varName + " = " +
  +                        quote( new String(
  +                        ((Node.TemplateText)bodyElement).getText() ) ) + ";" );
  +                }
  +            }
  +
  +            // XXX - Another possible optimization would be for
  +            // lone EL expressions (no need to pushBody here either).
  +
  +            if( !templateTextOptimization ) {
  +                out.printil( "out = pageContext.pushBody();" );
  +                visitBody( n );
  +                out.printil( "String " + varName + " = " +
  +			     "((javax.servlet.jsp.tagext.BodyContent)" +
  +			     "out).getString();" );
  +                out.printil( "out = pageContext.popBody();" );
  +            }
  +
  +            return varName;
  +        }
  +
  +        /**
  +         * Similar to generateNamedAttributeValue, but create a JspFragment
  +         * instead.
  +         *
  +         * @param n The parent node of the named attribute
  +         * @param tagHandlerVar The variable the tag handler is stored in,
  +         *     so the fragment knows its parent tag.
  +         * @return The name of the temporary variable the fragment 
  +         *     is stored in.
  +         */
  +        public String generateNamedAttributeJspFragment( 
  +            Node.NamedAttribute n, String tagHandlerVar )
  +            throws JasperException
  +        {
  +            String varName = n.getTemporaryVariableName();
  +            
  +            out.printin( "javax.servlet.jsp.tagext.JspFragment " + varName + 
  +                " = " );
  +            generateJspFragment( n, tagHandlerVar );
  +            out.println( ";" );
  +            
  +            return varName;
  +        }
  +    }
  +    
  +    private static void generateLocalVariables( ServletWriter out, Node n ) 
  +        throws JasperException
  +    {
  +        Node.ChildInfo ci;
  +        if( n instanceof Node.CustomTag ) {
  +            ci = ((Node.CustomTag)n).getChildInfo();
  +        }
  +        else if( n instanceof Node.JspBody ) {
  +            ci = ((Node.JspBody)n).getChildInfo();
  +        }
  +        else if( n instanceof Node.NamedAttribute ) {
  +            ci = ((Node.NamedAttribute)n).getChildInfo();
  +        }
  +        else {
  +            // Cannot access err since this method is static, but at
  +            // least flag an error.
  +            throw new JasperException( "Unexpected Node Type" );
  +            //err.getString( 
  +            //    "jsp.error.internal.unexpected_node_type" ) );
  +        }
  +        
  +        if (ci.isHasUsebean()) {
  +            out.println("HttpSession session = pageContext.getSession();");
  +            out.println("ServletContext application = pageContext.getServletContext();");
  +        }
  +        if (ci.isHasUsebean() || ci.isHasIncludeAction() || ci.isHasSetProperty()) {
  +            out.println("HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();");
  +        }
  +        if (ci.isHasIncludeAction()) {
  +            out.println("ServletResponse response = pageContext.getResponse();");
  +        }
  +    }
  +    
       /**
  -     * Generates the ending part of the static portion of the servelet.
  +     * Generates the ending part of the static portion of the servlet.
        */
       private void generatePostamble(Node.Nodes page) {
           out.popIndent();
  @@ -1881,6 +2531,12 @@
   
   	// Append any methods that were generated
   	out.print(methodsBuffer.toString());
  +        
  +        // Append the helper class
  +        if( helperClassBuffer.isUsed() ) {
  +            helperClassBuffer.generatePostamble();
  +            out.print(helperClassBuffer.toString());
  +        }
   
   	// generate class definition for JspxState
           if (maxTagNesting > 0) {
  @@ -1900,7 +2556,9 @@
   	methodsBuffer = new MethodsBuffer();
   	err = compiler.getErrorDispatcher();
   	ctxt = compiler.getCompilationContext();
  -	pageInfo = compiler.getPageInfo();
  +        helperClassBuffer = new HelperClassBuffer( 
  +            ctxt.getServletClassName() + "Helper" );
  +        pageInfo = compiler.getPageInfo();
   	beanInfo = pageInfo.getBeanRepository();
   	breakAtLF = ctxt.getOptions().getMappedFile();
   	if (ctxt.getOptions().isPoolingEnabled()) {
  @@ -1922,7 +2580,9 @@
   	    gen.compileTagHandlerPoolList(page);
   	}
   	gen.generatePreamble(page);
  -	page.visit(gen.new GenerateVisitor(out, gen.methodsBuffer));
  +        gen.helperClassBuffer.generatePreamble();
  +	page.visit(gen.new GenerateVisitor(out, gen.methodsBuffer, 
  +            gen.helperClassBuffer));
   	gen.generatePostamble(page);
       }
   
  @@ -2007,7 +2667,7 @@
       private static class MethodsBuffer {
   
   	private java.io.CharArrayWriter charWriter;
  -	private ServletWriter out;
  +	protected ServletWriter out;
   
   	MethodsBuffer() {
   	    charWriter = new java.io.CharArrayWriter();
  @@ -2021,6 +2681,132 @@
   	public String toString() {
   	    return charWriter.toString();
   	}
  +    }
  +
  +    /**
  +     * Keeps a separate buffer for helper classes.
  +     */
  +    private static class HelperClassBuffer 
  +        extends MethodsBuffer 
  +    {
  +        // True if the helper class should be generated.
  +        private boolean used = false;
  +        
  +        private int numFragments = 0;
  +        
  +        private String className;
  +        
  +	public HelperClassBuffer( String className ) {
  +            this.className = className;
  +	}
  +        
  +        public String getClassName() {
  +            return this.className;
  +        }
  +        
  +        public boolean isUsed() {
  +            return this.used;
  +        }
  +        
  +        public int getFragmentId() {
  +            return this.numFragments;
  +        }
  +        
  +        public void generatePreamble() {
  +            out.println();
  +            out.pushIndent();
  +            out.printil( "static class " + className );
  +            out.printil( "    extends " +
  +                "org.apache.jasper.runtime.JspFragmentHelper" );
  +            out.printil( "{" );
  +            out.pushIndent();
  +            out.printil( "public " + className + 
  +                "( int discriminator, JspContext jspContext, " +
  +                "Object parentTag ) {" );
  +            out.pushIndent();
  +            out.printil( "super( discriminator, jspContext, parentTag );" );
  +            out.popIndent();
  +            out.printil( "}" );
  +        }
  +        
  +        public void openFragment( Node parent ) 
  +            throws JasperException 
  +        {
  +            this.used = true;
  +            out.printil( "public void invoke" + numFragments + "( " +
  +                "java.io.Writer out, java.util.Map params ) " );
  +            out.pushIndent();
  +            out.printil( "throws javax.servlet.jsp.JspException, " +
  +                "java.io.IOException" );
  +            out.popIndent();
  +            out.printil( "{" );
  +            out.pushIndent();
  +            generateLocalVariables( out, parent );
  +        }
  +        
  +        public void closeFragment() {
  +            out.popIndent();
  +            out.printil( "}" );
  +            this.numFragments++;
  +        }
  +        
  +        public void generatePostamble() {
  +            out.printil( "public void invoke( java.io.Writer out, " +
  +			 "java.util.Map params )" );
  +            out.pushIndent();
  +            out.printil( "throws javax.servlet.jsp.JspException" );
  +            out.popIndent();
  +            out.printil( "{" );
  +            out.pushIndent();
  +            out.printil( 
  +                "this.jspContext.pushPageScope( this.originalPageScope );" );
  +            out.printil( "java.util.Map _jspx_originalValues = null;" );
  +            out.printil( "if( params != null ) {" );
  +            out.pushIndent();
  +            out.printil( "_jspx_originalValues = preparePageScope( params );");
  +            out.popIndent();
  +            out.printil( "}" );
  +            out.printil( "if( out == null ) {" );
  +            out.pushIndent();
  +            out.printil( "out = this.jspContext.getOut();" );
  +            out.popIndent();
  +            out.printil( "}" );
  +            out.printil( "try {" );
  +            out.pushIndent();
  +            out.printil( "switch( this.discriminator ) {" );
  +            out.pushIndent();
  +            for( int i = 0; i < numFragments; i++ ) {
  +                out.printil( "case " + i + ":" );
  +                out.pushIndent();
  +                out.printil( "invoke" + i + "( out, params );" );
  +                out.printil( "break;" );
  +                out.popIndent();
  +            }
  +            out.popIndent();
  +            out.printil( "}" ); // switch
  +            out.popIndent();
  +            out.printil( "}" ); // try
  +            out.printil( "catch( java.io.IOException e ) {" );
  +            out.pushIndent();
  +            out.printil( "throw new javax.servlet.jsp.JspException( e );" );
  +            out.popIndent();
  +            out.printil( "}" ); // catch
  +            out.printil( "finally {" );
  +            out.pushIndent();
  +            out.printil( "if( params != null ) {" );
  +            out.pushIndent();
  +            out.printil( "restorePageScope( _jspx_originalValues );");
  +            out.popIndent();
  +            out.printil( "}" );
  +            out.printil( "this.jspContext.popPageScope();" );
  +            out.popIndent();
  +            out.printil( "}" ); // finally
  +            out.popIndent();
  +            out.printil( "}" ); // invoke method
  +            out.popIndent();
  +            out.printil( "}" ); // helper class
  +            out.popIndent();
  +        }
       }
   }
   
  
  
  
  1.5       +24 -12    jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java
  
  Index: JspDocumentParser.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- JspDocumentParser.java	8 Jul 2002 17:28:58 -0000	1.4
  +++ JspDocumentParser.java	16 Jul 2002 19:30:51 -0000	1.5
  @@ -233,6 +233,10 @@
   	    node = new Node.PlugIn(attrsCopy, start, current);
   	} else if (qName.equals(JSP_TEXT_TAG)) {
   	    node = new Node.JspText(start, current);
  +	} else if (qName.equals(JSP_BODY_TAG)) {
  +	    node = new Node.JspBody(attrsCopy, start, current);
  +	} else if (qName.equals(JSP_ATTRIBUTE_TAG)) {
  +	    node = new Node.NamedAttribute(attrsCopy, start, current);
   	} else {
   	    node = getCustomTag(qName, attrsCopy, start, current);
   	    if (node == null) {
  @@ -285,9 +289,26 @@
       public void endElement(String uri,
   			   String localName,
   			   String qName) throws SAXException {
  +	if (current instanceof Node.NamedAttribute
  +	        && ((Node.NamedAttribute) current).isTrim()) {
  +	    // Ignore any whitespace (including spaces, carriage returns,
  +	    // line feeds, and tabs, that appear at the beginning and at the
  +	    // end of the body of the <jsp:attribute> action.
  +	    Node.Nodes subelems = ((Node.NamedAttribute) current).getBody();
  +	    Node firstNode = subelems.getNode(0);
  +	    if (firstNode instanceof Node.TemplateText) {
  +		((Node.TemplateText) firstNode).ltrim();
  +	    }
  +	    Node lastNode = subelems.getNode(subelems.size() - 1);
  +	    if (lastNode instanceof Node.TemplateText) {
  +		((Node.TemplateText) lastNode).rtrim();
  +	    }
  +	}
  +
   	if (current instanceof Node.ScriptingElement) {
   	    checkScriptingBody(current.getBody());
   	}
  +
   	if (current.getParent() != null) {
   	    current = current.getParent();
   	}
  @@ -434,15 +455,6 @@
   
   		// get the uri
   		String uri = attrs.getValue(i);
  -		/*
  -		 * make sure that a relative path is prefixed with
  -		 * urn:jsptld:path
  -		 */
  -		if (uri.charAt(0) == '/') {
  -		    throw new JasperException(
  -	                err.getString("jsp.error.xml.taglib.uri.not_prefixed",
  -				      uri));
  -		}
   		if (uri.startsWith(URN_JSPTLD)) {
   		    // uri value is of the form "urn:jsptld:path"
   		    uri = uri.substring(URN_JSPTLD.length());
  
  
  
  1.6       +32 -2     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspReader.java
  
  Index: JspReader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspReader.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- JspReader.java	7 Jun 2002 20:04:27 -0000	1.5
  +++ JspReader.java	16 Jul 2002 19:30:51 -0000	1.6
  @@ -75,6 +75,9 @@
    * @author Rajiv Mordani
    * @author Mandar Raje
    * @author Danno Ferrin
  + * @author Kin-man Chung
  + * @author Shawn Bayern
  + * @author Mark Roth
    */
   
   public class JspReader {
  @@ -254,7 +257,13 @@
           while ((++current.cursor < len) && 
   	    ((ch = current.stream[current.cursor]) != '<')) {
   
  -	    if (ch == '\n') {
  +	    if (ch == '$') {
  +		// XXX Make this backward compatible with JSP1.2.
  +		if ((current.cursor+1 < len) &&
  +			current.stream[current.cursor+1] == '{') {
  +		    break;
  +		}
  +	    } else if (ch == '\n') {
   		current.line++;
   		current.col = 0;
   	    } else {
  @@ -340,7 +349,27 @@
   	reset(mark);
   	return false;
       }
  -    
  +
  +    /**
  +     * Looks ahead to see if there are optional spaces followed by
  +     * the given String.  If so, true is returned and those spaces and
  +     * characters are skipped.  If not, false is returned and the
  +     * position is restored to where we were before.
  +     */
  +    public boolean matchesOptionalSpacesFollowedBy( String s )
  +        throws JasperException
  +    {
  +        Mark mark = mark();
  +
  +        skipSpaces();
  +        boolean result = matches( s );
  +        if( !result ) {
  +            reset( mark );
  +        }
  +
  +        return result;
  +    }
  +
       public void advance(int n) throws JasperException {
   	while (--n >= 0)
   	    nextChar();
  @@ -402,6 +431,7 @@
       }
   
       final boolean isSpace() {
  +        // Note: If this logic changes, also update Node.TemplateText.rtrim()
   	return peekChar() <= ' ';
       }
   
  
  
  
  1.5       +134 -7    jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java
  
  Index: JspUtil.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- JspUtil.java	8 Jun 2002 00:14:35 -0000	1.4
  +++ JspUtil.java	16 Jul 2002 19:30:51 -0000	1.5
  @@ -86,6 +86,10 @@
   import org.xml.sax.InputSource;
   import org.xml.sax.helpers.AttributesImpl;
   
  +// EL interpreter (subject to change)
  +import javax.servlet.jsp.el.ExpressionEvaluator;
  +import org.apache.jasper.runtime.ExpressionEvaluatorManager;
  +
   /** 
    * This class has all the utility method(s).
    * Ideally should move all the bean containers here.
  @@ -94,6 +98,8 @@
    * @author Rajiv Mordani.
    * @author Danno Ferrin
    * @author Pierre Delisle
  + * @author Shawn Bayern
  + * @author Mark Roth
    */
   public class JspUtil {
   
  @@ -105,6 +111,7 @@
   
       private static ErrorHandler errorHandler = new MyErrorHandler();
       private static EntityResolver entityResolver = new MyEntityResolver();
  +    private static int tempSequenceNumber = 0;
   
       public static char[] removeQuotes(char []chars) {
   	CharArrayWriter caw = new CharArrayWriter();
  @@ -273,15 +280,24 @@
   	}
       }
   
  +    /**
  +     * Checks if all mandatory attributes are present and if all attributes
  +     * present have valid names.  Checks attributes specified as XML-style
  +     * attributes as well as attributes specified using the jsp:attribute
  +     * standard action.  Also verifies that any attributes specified
  +     * via jsp:attribute are rtexprvalue attributes.
  +     */
       public static void checkAttributes(String typeOfTag,
  -				       Attributes attrs,
  +				       Node n,
   				       ValidAttribute[] validAttributes,
  -				       Mark start,
   				       ErrorDispatcher err)
   	                        throws JasperException {
  +        Attributes attrs = n.getAttributes();
  +        Mark start = n.getStart();
   	boolean valid = true;
  +
           // AttributesImpl.removeAttribute is broken, so we do this...
  -        int tempLength = attrs.getLength();
  +        int tempLength = (attrs == null) ? 0 : attrs.getLength();
   	Vector temp = new Vector(tempLength, 1);
           for (int i = 0; i < tempLength; i++) {
               String qName = attrs.getQName(i);
  @@ -289,6 +305,40 @@
                   temp.addElement(qName);
           }
   
  +        // Add names of attributes specified using jsp:attribute and
  +        // check that they are rtexprvalues while we're at it.
  +        Node.Nodes tagBody = n.getBody();
  +        if( tagBody != null ) {
  +            int numSubElements = tagBody.size();
  +            for( int i = 0; i < numSubElements; i++ ) {
  +                Node node = tagBody.getNode( i );
  +                if( node instanceof Node.NamedAttribute ) {
  +                    String attrName = node.getAttributeValue( "name" );
  +                    // Verify that this node is an rtexprvalue.
  +                    for( int j = 0; j < validAttributes.length; j++ ) {
  +                        if( validAttributes[j].name.equals( attrName ) &&
  +                            !validAttributes[j].rtexprvalue )
  +                        {
  +                            valid = false;
  +                            err.jspError(start,
  +                                         "jsp.error.named.attribute.not.rt",
  +                                         attrName );
  +                            break;
  +                        }
  +                    }
  +                    if( valid ) {
  +                        temp.addElement( attrName );
  +                    }
  +                    valid = true;
  +                }
  +                else {
  +                    // Nothing can come before jsp:attribute, and only
  +                    // jsp:body can come after it.
  +                    break;
  +                }
  +            }
  +        }
  +
   	/*
   	 * First check to see if all the mandatory attributes are present.
   	 * If so only then proceed to see if the other attributes are valid
  @@ -337,6 +387,7 @@
   	        err.jspError(start, "jsp.error.invalid.attribute", typeOfTag,
   			     attribute);
   	}
  +	// XXX *could* move EL-syntax validation here... (sb)
       }
       
       public static String escapeQueryString(String unescString) {
  @@ -411,10 +462,18 @@
       public static class ValidAttribute {
      	String name;
   	boolean mandatory;
  +	boolean rtexprvalue;
   
  -	public ValidAttribute (String name, boolean mandatory) {
  +	public ValidAttribute (String name, boolean mandatory,
  +            boolean rtexprvalue )
  +        {
   	    this.name = name;
   	    this.mandatory = mandatory;
  +            this.rtexprvalue = rtexprvalue;
  +        }
  +
  +       public ValidAttribute (String name, boolean mandatory) {
  +            this( name, mandatory, false );
   	}
   
   	public ValidAttribute (String name) {
  @@ -473,7 +532,75 @@
   	}
   	return b;
       }
  +
  +    /**
  +     * Produces a String representing a call to the EL interpreter.
  +     * @param expression a String containing zero or more "${}" expressions
  +     * @param expectedType the expected type of the interpreted result
  +     * @param fnMap Variable name containing function map, or literal "null"
  +     * @param defaultPrefix Default prefix, or literal "null"
  +     * @return a String representing a call to the EL interpreter.
  +     */
  +    public static String interpreterCall(String expression,
  +                                         Class expectedType,
  +                                         String fnMap,
  +                                         String defaultPrefix) 
  +    {
  +        return "(" + expectedType.getName() + ") "
  +               + Constants.EL_INTERPRETER_CONDUIT_CLASS + "."
  +               + Constants.EL_INTERPRETER_CONDUIT_METHOD
  +               + "(" + Generator.quote(expression) + ", "
  +               +       expectedType.getName() + ".class, "
  +               +       "pageContext,"
  +               +       fnMap + ", "
  +               +       Generator.quote( defaultPrefix ) + ")";
  +    }
  +
  +    /**
  +     * Validates the syntax of all ${} expressions within the given string.
  +     * @param where the approximate location of the expressions in the JSP page
  +     * @param expressions a string containing zero or more "${}" expressions
  +     * @param err an error dispatcher to use
  +     * @param extraInfo info (such as the name of the current attribute)
  +     *        to be included in any error messages
  +     */
  +    public static void validateExpressions(Mark where,
  +                                           String expressions,
  +                                           ErrorDispatcher err)
  +            throws JasperException {
  +        ExpressionEvaluator el = null;
  +        try {
  +            // XXX when the EL moves to Jakarta Commons, this can
  +            //     be replaced with a *much* cleaner interface.  I apologize
  +            //     for it for the moment! (SB)
  +            el = ExpressionEvaluatorManager.getEvaluatorByName(
  +                    ExpressionEvaluatorManager.EVALUATOR_CLASS);
  +        } catch (javax.servlet.jsp.JspException uglyEx) {
  +            err.jspError(where, "jsp.error.internal.evaluator_not_found");
  +        }
  +        String errMsg = el.validate(expressions);
  +        if (errMsg != null)
  +            err.jspError(where, "jsp.error.invalid.expression", expressions,
  +                errMsg);
  +    }
  +
  +    /**
  +     * Resets the temporary variable name.
  +     * (not thread-safe)
  +     */
  +    public static void resetTemporaryVariableName() {
  +        tempSequenceNumber = 0;
  +    }
  +
  +    /**
  +     * Generates a new temporary variable name.
  +     * (not thread-safe)
  +     */
  +    public static String nextTemporaryVariableName() {
  +        return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
  +    }
   }
  +
   
   class MyEntityResolver implements EntityResolver {
       public InputSource resolveEntity(String publicId, String systemId)
  
  
  
  1.16      +362 -56   jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java
  
  Index: Node.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- Node.java	21 Jun 2002 00:59:56 -0000	1.15
  +++ Node.java	16 Jul 2002 19:30:51 -0000	1.16
  @@ -72,6 +72,8 @@
    *
    * @author Kin-man Chung
    * @author Jan Luehe
  + * @author Shawn Bayern
  + * @author Mark Roth
    */
   
   public abstract class Node {
  @@ -130,6 +132,58 @@
   	return (attrs == null) ? null : attrs.getValue(name);
       }
   
  +    /**
  +     * Searches all subnodes of this node for jsp:attribute standard
  +     * actions with the given name, and returns the NamedAttribute node
  +     * of the matching named attribute, nor null if no such node is found.
  +     * <p>
  +     * This should always be called and only be called for nodes that
  +     * accept dynamic runtime attribute expressions.
  +     */
  +    public NamedAttribute getNamedAttributeNode( String name ) {
  +        NamedAttribute result = null;
  +        
  +        // Look for the attribute in NamedAttribute children
  +        Nodes nodes = getNamedAttributeNodes();
  +        int numChildNodes = nodes.size();
  +        for( int i = 0; i < numChildNodes; i++ ) {
  +            NamedAttribute na = (NamedAttribute)nodes.getNode( i );
  +            if( na.getName().equals( name ) ) {
  +                result = na;
  +                break;
  +            }
  +        }
  +        
  +        return result;
  +    }
  +
  +    /**
  +     * Searches all subnodes of this node for jsp:attribute standard
  +     * actions, and returns that set of nodes as a Node.Nodes object.
  +     */
  +    public Node.Nodes getNamedAttributeNodes() {
  +        Node.Nodes result = new Node.Nodes();
  +        
  +        // Look for the attribute in NamedAttribute children
  +        Nodes nodes = getBody();
  +        if( nodes != null ) {
  +            int numChildNodes = nodes.size();
  +            for( int i = 0; i < numChildNodes; i++ ) {
  +                Node n = nodes.getNode( i );
  +                if( n instanceof NamedAttribute ) {
  +                    result.add( n );
  +                }
  +                else {
  +                    // Nothing can come before jsp:attribute, and only
  +                    // jsp:body can come after it.
  +                    break;
  +                }
  +            }
  +        }
  +
  +        return result;
  +    }
  +    
       public Nodes getBody() {
   	return body;
       }
  @@ -440,6 +494,21 @@
       }
   
       /**
  +     * Represents an EL expression.  Expressions in attributes are embedded
  +     * in the attribute string and not here.
  +     */
  +    public static class ELExpression extends Node {
  +
  +        public ELExpression(char[] text, Mark start, Node parent) {
  +            super(text, start, parent);
  +        }
  +
  +        public void accept(Visitor v) throws JasperException {
  +            v.visit(this);
  +        }
  +    }
  +
  +    /**
        * Represents a param action
        */
       public static class ParamAction extends Node {
  @@ -606,9 +675,9 @@
        */
       public static class PlugIn extends Node {
   
  -	JspAttribute height;
  -	JspAttribute width;
  -
  +        private JspAttribute width;
  +        private JspAttribute height;
  +        
   	public PlugIn(Attributes attrs, Mark start, Node parent) {
   	    super(attrs, start, parent);
   	}
  @@ -654,6 +723,60 @@
   	    return tagName;
   	}
       }
  +    
  +    /**
  +     * Collected information about child elements.  Used by nodes like
  +     * CustomTag, JspBody, and NamedAttribute.  The information is 
  +     * set in the Collector.
  +     */
  +    public static class ChildInfo {
  +	private boolean scriptless;	// true if the tag and its body
  +					// contians no scripting elements.
  +	private boolean hasUsebean;
  +	private boolean hasIncludeAction;
  +	private boolean hasSetProperty;
  +	private boolean hasScriptingVars;
  +
  +	public void setScriptless(boolean s) {
  +	    scriptless = s;
  +	}
  +
  +	public boolean isScriptless() {
  +	    return scriptless;
  +	}
  +
  +	public void setHasUsebean(boolean u) {
  +	    hasUsebean = u;
  +	}
  +
  +	public boolean isHasUsebean() {
  +	    return hasUsebean;
  +	}
  +
  +	public void setHasIncludeAction(boolean i) {
  +	    hasIncludeAction = i;
  +	}
  +
  +	public boolean isHasIncludeAction() {
  +	    return hasIncludeAction;
  +	}
  +
  +	public void setHasSetProperty(boolean s) {
  +	    hasSetProperty = s;
  +	}
  +
  +	public boolean isHasSetProperty() {
  +	    return hasSetProperty;
  +	}
  +        
  +	public void setHasScriptingVars(boolean s) {
  +	    hasScriptingVars = s;
  +	}
  +
  +	public boolean hasScriptingVars() {
  +	    return hasScriptingVars;
  +	}
  +    }
   
       /**
        * Represents a custom tag
  @@ -664,17 +787,12 @@
   	private String shortName;
   	private JspAttribute[] jspAttrs;
   	private TagData tagData;
  -	private boolean scriptless;	// true if the tag and its body
  -					// contians no scripting elements.
  -	private boolean hasUsebean;
  -	private boolean hasIncludeAction;
  -	private boolean hasSetProperty;
  -	private boolean hasScriptingVars;
   	private String tagHandlerPoolName;
   	private TagInfo tagInfo;
   	private VariableInfo[] varInfos;
   	private int customNestingLevel;
   	private boolean hasUnnestedIdAttribute;
  +        private ChildInfo childInfo;
   
   	public CustomTag(Attributes attrs, Mark start, String name,
   			 String prefix, String shortName,
  @@ -686,6 +804,7 @@
   	    this.tagInfo = tagInfo;
   	    this.customNestingLevel = computeCustomNestingLevel();
   	    this.hasUnnestedIdAttribute = determineHasUnnestedIdAttribute();
  +            this.childInfo = new ChildInfo();
   	}
   
   	public void accept(Visitor v) throws JasperException {
  @@ -720,6 +839,10 @@
   	public JspAttribute[] getJspAttributes() {
   	    return jspAttrs;
   	}
  +        
  +        public ChildInfo getChildInfo() {
  +            return childInfo;
  +        }
   	
   	public void setTagData(TagData tagData) {
   	    this.tagData = tagData;
  @@ -730,46 +853,6 @@
   	    return tagData;
   	}
   
  -	public void setScriptless(boolean s) {
  -	    scriptless = s;
  -	}
  -
  -	public boolean isScriptless() {
  -	    return scriptless;
  -	}
  -
  -	public void setHasUsebean(boolean u) {
  -	    hasUsebean = u;
  -	}
  -
  -	public boolean isHasUsebean() {
  -	    return hasUsebean;
  -	}
  -
  -	public void setHasIncludeAction(boolean i) {
  -	    hasIncludeAction = i;
  -	}
  -
  -	public boolean isHasIncludeAction() {
  -	    return hasIncludeAction;
  -	}
  -
  -	public void setHasSetProperty(boolean s) {
  -	    hasSetProperty = s;
  -	}
  -
  -	public boolean isHasSetProperty() {
  -	    return hasSetProperty;
  -	}
  -
  -	public void setHasScriptingVars(boolean s) {
  -	    hasScriptingVars = s;
  -	}
  -
  -	public boolean hasScriptingVars() {
  -	    return hasScriptingVars;
  -	}
  -
   	public void setTagHandlerPoolName(String s) {
   	    tagHandlerPoolName = s;
   	}
  @@ -807,6 +890,42 @@
   	    return customNestingLevel;
   	}
   
  +        /**
  +         * Checks to see if the attribute or fragment attribute of the
  +         * given name is of type JspFragment.
  +         */
  +        public boolean checkIfAttributeIsJspFragment( String name ) {
  +            boolean result = false;
  +
  +            // The attribute is of type JspFragment if it appears in
  +            // the TagInfo Fragment Attributes list, or if it appears in the
  +            // Attributes list and is of the right type.
  +            
  +            TagFragmentAttributeInfo[] fragmentAttributes = 
  +                tagInfo.getFragmentAttributes();
  +            for( int i = 0; i < fragmentAttributes.length; i++ ) {
  +                if( fragmentAttributes[i].getName().equals( name ) ) {
  +                    result = true;
  +                    break;
  +                }
  +            }
  +            
  +            if( !result ) {
  +                TagAttributeInfo[] attributes = tagInfo.getAttributes();
  +                for( int i = 0; i < attributes.length; i++ ) {
  +                    if( attributes[i].getName().equals( name ) &&
  +                        "javax.servlet.jsp.tagext.JspFragment".equals(
  +                        attributes[i].getTypeName() ) )
  +                    {
  +                        result = true;
  +                        break;
  +                    }
  +                }
  +            }
  +            
  +            return result;
  +        }
  +        
   	/*
   	 * Computes this custom tag's custom nesting level, which corresponds
   	 * to the number of times this custom tag is nested inside itself.
  @@ -877,7 +996,7 @@
       }
   
       /**
  -     * Represents the body of a <jsp:text> element
  +     * Represents the body of a &lt;jsp:text&gt; element
        */
       public static class JspText extends Node {
   
  @@ -891,6 +1010,84 @@
       }
   
       /**
  +     * Represents a Named Attribute (&lt;jsp:attribute&gt;)
  +     */
  +    public static class NamedAttribute extends Node {
  +
  +        // A unique temporary variable name suitable for code generation
  +        private String temporaryVariableName;
  +
  +        // True if this node is to be trimmed, or false otherwise
  +        private boolean trim = true;
  +        
  +        private ChildInfo childInfo;
  +
  +        public NamedAttribute( Attributes attrs, Mark start, Node parent) {
  +            super( attrs, start, parent );
  +            this.temporaryVariableName = JspUtil.nextTemporaryVariableName();
  +            if( "false".equals( this.getAttributeValue( "trim" ) ) ) {
  +                // (if null or true, leave default of true)
  +                trim = false;
  +            }
  +            this.childInfo = new ChildInfo();
  +        }
  +
  +        public void accept(Visitor v) throws JasperException {
  +            v.visit(this);
  +        }
  +
  +        public String getName() {
  +            return this.getAttributeValue( "name" );
  +        }
  +        
  +        public ChildInfo getChildInfo() {
  +            return this.childInfo;
  +        }
  +
  +        public boolean isTrim() {
  +            return trim;
  +        }
  +
  +        /**
  +         * @return A unique temporary variable name to store the result in.
  +         *      (this probably could go elsewhere, but it's convenient here)
  +         */
  +        public String getTemporaryVariableName() {
  +            return temporaryVariableName;
  +        }
  +    }
  +
  +    /**
  +     * Represents a JspBody node (&lt;jsp:body&gt;)
  +     */
  +    public static class JspBody extends Node {
  +
  +	private JspAttribute value;
  +        private ChildInfo childInfo;
  +
  +        public JspBody( Attributes attrs, Mark start, Node parent) {
  +            super( attrs, start, parent );
  +            this.childInfo = new ChildInfo();
  +        }
  +
  +        public void accept(Visitor v) throws JasperException {
  +            v.visit(this);
  +        }
  +
  +	public void setValue(JspAttribute value) {
  +	    this.value = value;
  +	}
  +
  +	public JspAttribute getValue() {
  +	    return value;
  +	}
  +        
  +        public ChildInfo getChildInfo() {
  +            return childInfo;
  +        }
  +    }
  +
  +    /**
        * Represents a template text string
        */
       public static class TemplateText extends Node {
  @@ -902,6 +1099,35 @@
   	public void accept(Visitor v) throws JasperException {
   	    v.visit(this);
   	}
  +
  +        /**
  +         * Trim all whitespace from the left of the template text
  +         */
  +        public void ltrim() {
  +            // Whitespace logic borrowed from JspReader.isSpace
  +	    int index = 0;
  +            while ((index < text.length) && (text[index] <= ' ')) {
  +		index++;
  +            }
  +	    int size = text.length - index;
  +            char[] newText = new char[size];
  +            System.arraycopy(text, index, newText, 0, size);
  +            text = newText;
  +        }
  +
  +        /**
  +         * Trim all whitespace from the right of the template text
  +         */
  +        public void rtrim() {
  +            // Whitespace logic borrowed from JspReader.isSpace
  +            int size = text.length;
  +            while( (size > 0) && (text[size-1] <= ' ') ) {
  +                size--;
  +            }
  +            char[] newText = new char[size];
  +            System.arraycopy( text, 0, newText, 0, size );
  +            text = newText;
  +        }
       }
   
       /*********************************************************************
  @@ -910,6 +1136,10 @@
   
       /**
        * Represents attributes that can be request time expressions.
  +     *
  +     * Can either be a plain attribute, an attribute that represents a
  +     * request time expression value, or a named attribute (specified using
  +     * the jsp:attribute standard action).
        */
   
       public static class JspAttribute {
  @@ -917,13 +1147,36 @@
   	private String name;
   	private String value;
   	private boolean expression;
  +        private boolean el;
   
  -	JspAttribute(String name, String value, boolean expr) {
  +        // If true, this JapAttribute represents a <jsp:attribute>
  +        private boolean namedAttribute;
  +        // The node in the parse tree for the NamedAttribute
  +        private NamedAttribute namedAttributeNode;
  +
  +        JspAttribute(String name, String value, boolean expr, boolean el ) {
   	    this.name = name;
   	    this.value = value;
  +            this.namedAttributeNode = null;
   	    this.expression = expr;
  +            this.el = el;
  +            this.namedAttribute = false;
   	}
   
  +        /**
  +         * Use this constructor if the JspAttribute represents a
  +         * named attribute.  In this case, we have to store the nodes of
  +         * the body of the attribute.
  +         */
  +        JspAttribute( String name, NamedAttribute namedAttributeNode ) {
  +            this.name = name;
  +            this.value = null;
  +            this.namedAttributeNode = namedAttributeNode;
  +            this.expression = false;
  +            this.el = false;
  +            this.namedAttribute = true;
  +        }
  +
   	/**
    	 * @return The name of the attribute
   	 */
  @@ -932,19 +1185,56 @@
   	}
   
   	/**
  -	 * @return the value for the attribute, or the expression string
  -	 *	   (stripped of "<%=", "%>", "%=", or "%")
  +         * Only makes sense if namedAttribute is false.
  +         *
  +         * @return the value for the attribute, or the expression string
  +         *         (stripped of "<%=", "%>", "%=", or "%"
  +         *          but containing "${" and "}" for EL expressions)
   	 */
   	public String getValue() {
   	    return value;
   	}
   
  +        /**
  +         * Only makes sense if namedAttribute is true.
  +         *
  +         * @return the nodes that evaluate to the body of this attribute.
  +         */
  +        public NamedAttribute getNamedAttributeNode() {
  +            return namedAttributeNode;
  +        }
  +
   	/**
  -	 * @return true is the value represents an expression
  +         * @return true if the value represents a traditional rtexprvalue
   	 */
   	public boolean isExpression() {
   	    return expression;
   	}
  +
  +        /**
  +         * @return true if the value represents a NamedAttribute value.
  +         */
  +        public boolean isNamedAttribute() {
  +            return namedAttribute;
  +        }
  +
  +        /**
  +         * @return true if the value represents an expression that should
  +         * be fed to the expression interpreter
  +         * @return false for string literals or rtexprvalues that should
  +         * not be interpreter or reevaluated
  +         */
  +        public boolean isELInterpreterInput() {
  +            return el;
  +        }
  +
  +	/**
  +	 * @return true if the value is a string literal know at translation
  +	 * time.
  +	 */
  +	public boolean isLiteral() {
  +	    return !expression && !el && !namedAttribute;
  +	}
       }
   
       /**
  @@ -1068,6 +1358,10 @@
   	    doVisit(n);
   	}
   
  +        public void visit(ELExpression n) throws JasperException {
  +            doVisit(n);
  +        }
  +
   	public void visit(IncludeAction n) throws JasperException {
   	    doVisit(n);
   	    visitBody(n);
  @@ -1084,10 +1378,12 @@
   
   	public void visit(SetProperty n) throws JasperException {
   	    doVisit(n);
  +	    visitBody(n);
   	}
   
   	public void visit(ParamAction n) throws JasperException {
   	    doVisit(n);
  +	    visitBody(n);
   	}
   
   	public void visit(ParamsAction n) throws JasperException {
  @@ -1124,6 +1420,16 @@
   	    doVisit(n);
   	    visitBody(n);
   	}
  +
  +        public void visit(NamedAttribute n) throws JasperException {
  +            doVisit(n);
  +            visitBody(n);
  +        }
  +
  +        public void visit(JspBody n) throws JasperException {
  +            doVisit(n);
  +            visitBody(n);
  +        }
   
   	public void visit(TemplateText n) throws JasperException {
   	    doVisit(n);
  
  
  
  1.4       +12 -4     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageDataImpl.java
  
  Index: PageDataImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageDataImpl.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PageDataImpl.java	17 May 2002 23:44:00 -0000	1.3
  +++ PageDataImpl.java	16 Jul 2002 19:30:51 -0000	1.4
  @@ -325,6 +325,14 @@
   	    appendTag(JSP_PLUGIN_TAG, n.getAttributes(), n.getBody());
   	}
   
  +        public void visit(Node.NamedAttribute n) throws JasperException {
  +            appendTag(JSP_ATTRIBUTE_TAG, n.getAttributes(), n.getBody());
  +        }
  +        
  +        public void visit(Node.JspBody n) throws JasperException {
  +            appendTag(JSP_BODY_TAG, n.getAttributes(), n.getBody());
  +        }
  +
   	public void visit(Node.CustomTag n) throws JasperException {
   	    appendTag(n.getName(), n.getAttributes(), n.getBody());
   	}
  @@ -337,7 +345,7 @@
   	    // jsp:text has no attributes, except for jsp:id
   	    appendTag(JSP_TEXT_TAG, n.getAttributes(), n.getBody());
   	}
  -
  +        
   	public void visit(Node.TemplateText n) throws JasperException {
   	    /*
   	     * If the template text came from a JSP page written in JSP syntax,
  
  
  
  1.6       +10 -2     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageInfo.java
  
  Index: PageInfo.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageInfo.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- PageInfo.java	5 Jun 2002 22:01:33 -0000	1.5
  +++ PageInfo.java	16 Jul 2002 19:30:51 -0000	1.6
  @@ -90,6 +90,7 @@
       private String pageEncoding = null;
       private int maxTagNesting = 0;
       private boolean scriptless = false;
  +    private boolean scriptingEnabled = true;
   
       PageInfo(BeanRepository beanRepository) {
   	this.beanRepository = beanRepository;
  @@ -222,4 +223,11 @@
   	return scriptless;
       }
   
  +    public void setScriptingEnabled(boolean s) {
  +	scriptingEnabled = s;
  +    }
  +
  +    public boolean isScriptingEnabled() {
  +	return scriptingEnabled;
  +    }
   }
  
  
  
  1.8       +553 -189  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java
  
  Index: Parser.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- Parser.java	8 Jul 2002 23:47:55 -0000	1.7
  +++ Parser.java	16 Jul 2002 19:30:51 -0000	1.8
  @@ -67,6 +67,7 @@
   import javax.servlet.jsp.tagext.TagInfo;
   import org.xml.sax.Attributes;
   import org.xml.sax.helpers.AttributesImpl;
  +import org.apache.jasper.Constants;
   import org.apache.jasper.JspCompilationContext;
   import org.apache.jasper.JasperException;
   
  @@ -77,6 +78,8 @@
    * location in the production.
    * 
    * @author Kin-man Chung
  + * @author Shawn Bayern
  + * @author Mark Roth
    */
   
   public class Parser {
  @@ -88,6 +91,14 @@
       private Mark start;
       private Hashtable taglibs;
       private ErrorDispatcher err;
  +    private int scriptlessCount;
  +    
  +    // 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";
   
       /**
        * The constructor
  @@ -99,6 +110,7 @@
   	this.err = pc.getCompiler().getErrorDispatcher();
   	this.reader = reader;
   	this.currentFile = reader.mark().getFile();
  +        this.scriptlessCount = 0;
           start = reader.mark();
       }
   
  @@ -151,9 +163,9 @@
   
       /**
        * Attribute ::= Name S? Eq S?
  -     *               (   '"<%= RTAttributeValueDouble
  +     *               (   '"<%=' RTAttributeValueDouble
        *                 | '"' AttributeValueDouble
  -     *                 | "'<%= RTAttributeValueSingle
  +     *                 | "'<%=" RTAttributeValueSingle
        *                 | "'" AttributeValueSingle
        *               }
        * Note: JSP and XML spec does not allow while spaces around Eq.  It is
  @@ -352,7 +364,7 @@
   	    // Errors to be checked in Validator
   	    String[] location = ctxt.getTldLocation(uri);
   	    TagLibraryInfo tl = new TagLibraryInfoImpl(ctxt, prefix, uri,
  -						       location, err);
  +						   location, err);
   	    taglibs.put(prefix, tl);
   	}
   
  @@ -429,7 +441,38 @@
   	new Node.Expression(parseScriptText(reader.getText(start, stop)),
   				start, parent);
       }
  -	
  +
  +    /*
  +     * 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 {
  +        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
  +            currentChar = reader.nextChar();
  +            if (currentChar == '\\' && (singleQuoted || doubleQuoted)) {
  +                // skip character following '\' within quotes
  +                reader.nextChar();
  +                currentChar = reader.nextChar();
  +            }
  +            if (currentChar == -1)
  +                err.jspError(start, "jsp.error.unterminated", "${");
  +            if (currentChar == '"')
  +                doubleQuoted = !doubleQuoted;
  +            if (currentChar == '\'')
  +                singleQuoted = !singleQuoted;
  +        } while (currentChar != '}' || (singleQuoted || doubleQuoted));
  +
  +        new Node.ELExpression(reader.getText(start, last), start, parent);
  +    }
  +
       /*
        * Scriptlet ::= (Char* - (char* '%>')) '%>'
        */
  @@ -445,217 +488,338 @@
       }
   	
       /**
  -     *  Param ::= '<jsp:param' Attributes* '/>'
  +     * 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.paramexpectedonly");
   	}
   	Attributes attrs = parseAttributes();
   	reader.skipSpaces();
  -
  -	if (!reader.matches("/>")) {
  -	    err.jspError(reader.mark(), "jsp.error.unterminated",
  -			 "<jsp:param");
  -	}
  -
  -	new Node.ParamAction(attrs, start, parent);
  -    }
  -
  -    /**
  -     *  Params ::= (Param S?)*
  -     */
  -    private void parseParams(Node parent, String tag) throws JasperException {
  -
  -	Mark start = reader.mark();
  -
  -	while (reader.hasMoreInput()) {
  -	    if (reader.matchesETag(tag)) {
  -		break;
  -	    }
  -
  -	    parseParam(parent);
  -	    reader.skipSpaces();
  -	}
  +        
  +        Node paramActionNode = new Node.ParamAction( attrs, start, parent );
  +        
  +        parseEmptyBody( paramActionNode, "jsp:param" );
  +        
  +        reader.skipSpaces();
       }
   
       /*
  -     * IncludeAction ::= Attributes
  -     *			 (   '/>'
  -     *			   | '>' S? Params '</jsp:include' S? '>'
  -     *			 )
  +     * 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();
   
  -	if (reader.matches("/>")) {
  -	    // No body
  -	    new Node.IncludeAction(attrs, start, parent);
  -	    return;
  -	}
  -	    
  -	if (!reader.matches(">")) {
  -	    err.jspError(reader.mark(), "jsp.error.unterminated",
  -			 "<jsp:include");
  -	}
  -
  -	reader.skipSpaces();
  -	Node includeNode = new Node.IncludeAction(attrs, start, parent);
  -	parseParams(includeNode, "jsp:include");
  +        Node includeNode = new Node.IncludeAction( attrs, start, parent );
  +        
  +        parseOptionalBody( includeNode, "jsp:include", 
  +            JAVAX_BODY_CONTENT_PARAM );
       }
  -   
  +
       /*
  -     * ForwardAction ::= Attributes
  -     *			 (  '/>'
  -     *			  | '>' S? Params '<jsp:forward' S? '>'
  -     *			 )
  +     * For Forward:
  +     * StdActionContent ::= Attributes ParamBody
        */
       private void parseForward(Node parent) throws JasperException {
  -
   	Attributes attrs = parseAttributes();
   	reader.skipSpaces();
   
  -	if (reader.matches("/>")) {
  -	    // No body
  -	    new Node.ForwardAction(attrs, start, parent);
  -	    return;
  -	}
  -
  -	if (!reader.matches(">")) {
  -	    err.jspError(reader.mark(), "jsp.error.unterminated",
  -			 "<jsp:forward");
  -	}
  -
  -	reader.skipSpaces();
  -	Node forwardNode = new Node.ForwardAction(attrs, start, parent);
  -	parseParams(forwardNode, "jsp:forward");
  +        Node forwardNode = new Node.ForwardAction( attrs, start, parent );
  +        
  +        parseOptionalBody( forwardNode, "jsp:forward", 
  +            JAVAX_BODY_CONTENT_PARAM );
       }
   
       /*
  -     * GetProperty ::= (S? Attribute)* S? '/>/
  +     * For GetProperty:
  +     * StdActionContent ::= Attributes EmptyBody
        */
       private void parseGetProperty(Node parent) throws JasperException {
   	Attributes attrs = parseAttributes();
   	reader.skipSpaces();
   
  -	if (!reader.matches("/>")) {
  -	    err.jspError(reader.mark(), "jsp.error.unterminated",
  -			 "<jsp:getProperty");
  -	}
  -
  -	new Node.GetProperty(attrs, start, parent);
  +        Node getPropertyNode = new Node.GetProperty( attrs, start, parent );
  +        
  +        parseEmptyBody( getPropertyNode, "jsp:getProperty" );
       }
   
       /*
  -     * SetProperty ::= (S Attribute)* S? '/>'
  +     * For SetProperty:
  +     * StdActionContent ::= Attributes EmptyBody
        */
       private void parseSetProperty(Node parent) throws JasperException {
   	Attributes attrs = parseAttributes();
   	reader.skipSpaces();
   
  -	if (!reader.matches("/>")) {
  -	    err.jspError(reader.mark(), "jsp.error.unterminated",
  -			 "<jsp:setProperty");
  -	}
  +        Node setPropertyNode = new Node.SetProperty( attrs, start, parent );
  +        
  +        parseEmptyBody( setPropertyNode, "jsp:setProperty" );
  +    }
   
  -	new Node.SetProperty(attrs, start, parent);
  +    /*
  +     * EmptyBody ::=   '/>'
  +     *               | ( '>' ETag )
  +     *               | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
  +     */
  +    private void parseEmptyBody( Node parent, String tag ) 
  +        throws JasperException
  +    {
  +	if( reader.matches("/>") ) {
  +            // Done
  +        }
  +        else if( reader.matches( ">" ) ) {
  +            if( reader.matchesETag( tag ) ) {
  +                // Done
  +            }
  +            else if( reader.matchesOptionalSpacesFollowedBy(
  +                "<jsp:attribute" ) )
  +            {
  +                // Parse the one or more named attribute nodes
  +                parseNamedAttributes( parent );
  +                if( !reader.matchesETag( tag ) ) {
  +                    // Body not allowed
  +                    err.jspError(reader.mark(),
  +                        "jsp.error.jspbody.emptybody.only",
  +                        "<" + tag );
  +                }
  +            }
  +            else {
  +                err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only",
  +                    "<" + tag );
  +            }
  +        }
  +        else {
  +	    err.jspError(reader.mark(), "jsp.error.unterminated",
  +                "<" + tag );
  +        }
       }
   
       /*
  -     * UseBean ::= (S Attribute)* S?
  -     *		   ('/>' | ( '>' Body '</jsp:useBean' S? '>' ))
  +     * 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 );
  +    }
   
  +    /*
  +     * 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("/>")) {
  -	    new Node.UseBean(attrs, start, parent);
  +	    // EmptyBody
   	    return;
   	}
   
   	if (!reader.matches(">")) {
   	    err.jspError(reader.mark(), "jsp.error.unterminated",
  -			 "<jsp:useBean");
  +			 "<" + tag );
   	}
  -
  -	Node beanNode = new Node.UseBean(attrs, start, parent);
  -	parseBody(beanNode, "jsp:useBean");
  +        
  +        if( reader.matchesETag( tag ) ) {
  +            // EmptyBody
  +            return;
  +        }
  +        
  +        if( !parseJspAttributeAndBody( parent, tag, bodyType ) ) {
  +            // Must be ( '>' # Body ETag )
  +            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
  +    {
  +        boolean result = false;
  +        
  +        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 );
  +            
  +            result = true;
  +        }
  +        
  +        if( reader.matchesOptionalSpacesFollowedBy( "<jsp:body" ) ) {
  +            // ActionBody
  +            parseJspBody( parent, bodyType );
  +            reader.skipSpaces();
  +            if( !reader.matchesETag( tag ) ) {
  +                err.jspError(reader.mark(), "jsp.error.unterminated", 
  +                    "<" + tag );
  +            }
  +            
  +            result = true;
  +        }
  +        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", 
  +                "<" + tag );
  +        }
  +        
  +        return result;
       }
   
       /*
  -     * JspParams ::=  S? '>' S? Params+ </jsp:params' S? '>'
  +     * Params ::=   '/>'
  +     *            | ( '>' S? Param* '</jsp:params>' )
        */
       private void parseJspParams(Node parent) throws JasperException {
  -
  -	reader.skipSpaces();
  -	if (!reader.matches(">")) {
  -	    err.jspError(reader.mark(), "jsp.error.params.notclosed");
  -	}
  -
  -	reader.skipSpaces();
  -	Node jspParamsNode = new Node.ParamsAction(start, parent);
  -	parseParams(jspParamsNode, "jsp:params");
  +        if( reader.matches( "/>" ) ) {
  +            // No elements, don't create node.
  +        }
  +        else if( reader.matches( ">" ) ) {
  +            reader.skipSpaces();
  +            Node jspParamsNode = new Node.ParamsAction(start, parent);
  +            parseBody(jspParamsNode, "jsp:params", JAVAX_BODY_CONTENT_PARAM );
  +        }
  +        else {
  +            err.jspError(reader.mark(), "jsp.error.unterminated",
  +                "<jsp:params" );
  +        }
       }
   
       /*
  -     * FallBack ::=  S? '>' Char* '</jsp:fallback' S? '>'
  +     * Fallback ::=   '/>'
  +     *              | ( '>'
  +     *                  ( Char* - ( Char* '</jsp:fallback>' ) )
  +     *                  '</jsp:fallback>'
  +     *                )
        */
       private void parseFallBack(Node parent) throws JasperException {
  -
  -	reader.skipSpaces();
  -	if (!reader.matches(">")) {
  -	    err.jspError(reader.mark(), "jsp.error.fallback.notclosed");
  -	}
  -
  -	Mark bodyStart = reader.mark();
  -        Mark bodyEnd = reader.skipUntilETag("jsp:fallback");
  -        if (bodyEnd == null) {
  -            err.jspError(start, "jsp.error.unterminated", "<jsp:fallback>");
  -	}
  -	char[] text = reader.getText(bodyStart, bodyEnd);
  -        new Node.FallBackAction(start, text, parent);
  +        if( reader.matches( "/>" ) ) {
  +            // No elements, don't create node.
  +        }
  +        else if( reader.matches( ">" ) ) {
  +            Mark bodyStart = reader.mark();
  +            Mark bodyEnd = reader.skipUntilETag("jsp:fallback");
  +            if (bodyEnd == null) {
  +                err.jspError(start, "jsp.error.unterminated", 
  +                    "<jsp:fallback");
  +            }
  +            char[] text = reader.getText(bodyStart, bodyEnd);
  +            new Node.FallBackAction(start, text, parent);
  +        }
  +        else {
  +            err.jspError( reader.mark(), "jsp.error.unterminated",
  +                "<jsp:fallback" );
  +        }
       }
   
       /*
  -     * PlugIn ::= Attributes '>' PlugInBody '</jsp:plugin' S? '>'
  -     * PlugBody ::= S? ('<jsp:params' JspParams S?)?
  -     *			('<jsp:fallback' JspFallack S?)?
  +     * 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();
  -
  -	if (!reader.matches(">")) {
  -	    err.jspError(reader.mark(), "jsp.error.plugin.notclosed");
  -	}
  -
  -	reader.skipSpaces();
  +        
   	Node pluginNode = new Node.PlugIn(attrs, start, parent);
  -	if (reader.matches("<jsp:params")) {
  -	    parseJspParams(pluginNode);
  -	    reader.skipSpaces();
  -	}
  -
  -	if (reader.matches("<jsp:fallback")) {
  -	    parseFallBack(pluginNode);
  -	    reader.skipSpaces();
  -	}
  -
  -	if (!reader.matchesETag("jsp:plugin")) {
  -	    err.jspError(reader.mark(), "jsp.error.plugin.notclosed");
  -	}
  +        
  +        parseOptionalBody( pluginNode, "jsp:plugin", 
  +            JAVAX_BODY_CONTENT_PLUGIN );
       }
   
       /*
  -     * StandardAction ::=   'include' IncludeAction
  -     *			  | 'forward' ForwardAction
  -     *			  | 'getProperty' GetPropertyAction
  -     *			  | 'setProperty' SetPropertyAction
  -     *			  | 'useBean' UseBeanAction
  -     *			  | 'plugin' PlugInAction
  +     * PluginTags ::= ( '<jsp:params' Params S? )?
  +     *                ( '<jsp:fallback' Fallback? S? )?
  +     */
  +    private void parsePluginTags( Node parent ) throws JasperException {
  +        reader.skipSpaces();
  +        
  +        if( reader.matches( "<jsp:params" ) ) {
  +            parseJspParams( parent );
  +            reader.skipSpaces();
  +        }
  +        
  +        if( reader.matches( "<jsp:fallback" ) ) {
  +            parseFallBack( parent );
  +            reader.skipSpaces();
  +        }
  +    }
  +        
  +    /*
  +     * StandardAction ::=   'include'       StdActionContent
  +     *                    | 'forward'       StdActionContent
  +     *                    | 'getProperty'   StdActionContent
  +     *                    | 'setProperty'   StdActionContent
  +     *                    | 'useBean'       StdActionContent
  +     *                    | 'plugin'        StdActionContent
        */
       private void parseAction(Node parent) throws JasperException {
   	Mark start = reader.mark();
  @@ -678,16 +842,35 @@
       }
   
       /*
  -     * ActionElement ::= EmptyElemTag | Stag Body Etag
  -     * EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'
  -     * Stag ::= '<' Name ( S Attribute)* S? '>'
  -     * Etag ::= '</' Name S? '>'
  +     * # '<' CustomAction CustomActionBody
  +     *
  +     * CustomAction ::= TagPrefix ':' CustomActionName
  +     *
  +     * TagPrefix ::= Name
  +     *
  +     * CustomActionName ::= Name
  +     *
  +     * CustomActionBody ::=   ( Attributes CustomActionEnd )
  +     *                      | <TRANSLATION_ERROR>
  +     *
  +     * Attributes ::= ( S Attribute )* S?
  +     *
  +     * CustomActionEnd ::=   CustomActionTagDependent
  +     *                     | CustomActionJSPContent
  +     *                     | CustomActionScriptlessContent
  +     *
  +     * CustomActionTagDependent ::= TagDependentOptionalBody
  +     *
  +     * CustomActionJSPContent ::= OptionalBody
  +     *
  +     * CustomActionScriptlessContent ::= ScriptlessOptionalBody
        */
       private boolean parseCustomTag(Node parent) throws JasperException {
   	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(':');
  @@ -709,46 +892,35 @@
   	if (tagInfo == null) {
   	    err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
   	}
  +        
  +        // Parse 'CustomActionBody' production:
  +        // At this point we are committed - if anything fails, we produce
  +        // a translation error.
   
  -	// EmptyElemTag ::= '<' Name ( #S Attribute )* S? '/>'
  -	// or Stag ::= '<' Name ( #S Attribute)* S? '>'
  +        // Parse 'Attributes' production:
   	Attributes attrs = parseAttributes();
   	reader.skipSpaces();
   	
  +        // Parse 'CustomActionEnd' production:
   	if (reader.matches("/>")) {
  -	    // EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'#
   	    new Node.CustomTag(attrs, start, tagName, prefix, shortTagName,
   			       tagInfo, parent);
   	    return true;
   	}
   	
  -	if (!reader.matches(">")) {
  -	    err.jspError(start, "jsp.error.unterminated.tag");
  -	}
  +        // Now we parse one of 'CustomActionTagDependent', 
  +        // 'CustomActionJSPContent', or 'CustomActionScriptlessContent'.
  +        // depending on body-content in TLD.
   
  -	// ActionElement ::= Stag #Body Etag
  -	
   	// 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 TLD.
  -	String bc = tagInfo.getBodyContent();
  +	String bc = ((TagLibraryInfo)taglibs.get(prefix)).getTag(
  +            shortTagName).getBodyContent();
   
   	Node tagNode = new Node.CustomTag(attrs, start, tagName, prefix,
   					  shortTagName, tagInfo, parent);
  -	// There are 3 body content types: empty, jsp, or tag-dependent.
  -	if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)) {
  -	    if (!reader.matchesETag(tagName)) {
  -		err.jspError(start, "jasper.error.emptybodycontent.nonempty");
  -	    }
  -	} else if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT)) {
  -	    // parse the body as text
  -	    parseBodyText(tagNode, tagName);
  -	} else if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) {
  -	    // parse body as JSP page
  -	    parseBody(tagNode, tagName);
  -	} else {
  -	    err.jspError(start, "jasper.error.bad.bodycontent.type");
  -	}
  +	parseOptionalBody( tagNode, tagName, bc );
   
   	return true;
       }
  @@ -771,18 +943,29 @@
   	    new Node.TemplateText(reader.nextContent(), start, parent);
   	}
       }
  -	
  +    
       /*
  -     * BodyElement ::=	  '<%--' JSPCommentBody
  -     *			| '<%@' DirectiveBody
  -     *			| '<%!' DeclarationBody
  -     *			| '<%=' ExpressionBody
  -     *			| '<%' ScriptletBody
  -     *			| '<jsp:' StandardAction
  -     *			| '<' CustomAction
  +     * AllBody ::=	  ( '<%--' JSPCommentBody )
  +     *			| ( '<%@' DirectiveBody )
  +     *			| ( '<%!' DeclarationBody )
  +     *			| ( '<%=' ExpressionBody )
  +     *                  | ( '${' ELExpressionBody )
  +     *			| ( '<%' ScriptletBody )
  +     *			| ( '<jsp:' StandardAction )
  +     *			| ( '<' CustomAction CustomActionBody )
        *			| TemplateText
        */
  -    private void parseElements(Node parent) throws JasperException {
  +    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 );
  +            return;
  +        }
  +        
   	start = reader.mark();
   	if (reader.matches("<%--")) {
   	    parseComment(parent);
  @@ -794,6 +977,8 @@
   	    parseExpression(parent);
   	} else if (reader.matches("<%")) {
   	    parseScriptlet(parent);
  +        } else if (reader.matches("${")) {
  +            parseELExpression(parent);
   	} else if (reader.matches("<jsp:")) {
   	    parseAction(parent);
   	} else if (!parseCustomTag(parent)) {
  @@ -802,33 +987,212 @@
       }
   
       /*
  +     * ScriptlessBody ::=   ( '<%--' JSPCommentBody )
  +     *			  | ( '<%@' <TRANSLATION_ERROR> )
  +     *			  | ( '<%!' <TRANSLATION_ERROR> )
  +     *			  | ( '<%=' <TRANSLATION_ERROR> )
  +     *			  | ( '<%' <TRANSLATION_ERROR> )
  +     *                    | ( '${' ELExpressionBody )
  +     *	  		  | ( '<jsp:' StandardAction )
  +     *			  | ( '<' CustomAction CustomActionBody )
  +     *			  | TemplateText
  +     */
  +    private void parseElementsScriptless(Node parent) 
  +        throws JasperException 
  +    {
  +        // Keep track of how many scriptless nodes we've encountered
  +        // so we know whether our child nodes are forced scriptless
  +        scriptlessCount++;
  +        
  +	start = reader.mark();
  +	if (reader.matches("<%--")) {
  +	    parseComment(parent);
  +	} else if (reader.matches("<%@")) {
  +	    parseDirective(parent);
  +	} else if (reader.matches("<%!")) {
  +	    err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
  +	} else if (reader.matches("<%=")) {
  +	    err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
  +	} else if (reader.matches("<%")) {
  +	    err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
  +	} else if (reader.matches("${")) {
  +	    parseELExpression(parent);
  +	} else if (reader.matches("<jsp:")) {
  +	    parseAction(parent);
  +	} else if (!parseCustomTag(parent)) {
  +	    parseTemplateText(parent);
  +	}
  +        
  +        scriptlessCount--;
  +    }
  +    
  +    /*
        *
        */
       private void parseBodyText(Node parent, String tag) throws JasperException{
   	Mark bodyStart = reader.mark();
   	Mark bodyEnd = reader.skipUntilETag(tag);
   	if (bodyEnd == null) {
  -	    err.jspError(start, "jsp.error.unterminated", "<"+tag+">");
  +	    err.jspError(start, "jsp.error.unterminated", "<"+tag );
   	}
   	new Node.TemplateText(reader.getText(bodyStart, bodyEnd), bodyStart,
   			      parent);
       }
   
       /*
  +     * JspBodyBody ::=      ( S? '>' 
  +     *                        ( (   ScriptlessBody 
  +     *                            | Body
  +     *                            | TagDependentBody
  +     *                          ) - ''
  +     *                        ) '</jsp:body>' 
  +     *                      )
  +     *                  |   ( ATTR[ !value ] S? JspBodyEmptyBody )
  +     *
  +     * JspBodyEmptyBody ::=     '/>'
  +     *                      |   '></jsp:body>'
  +     *                      |   <TRANSLATION_ERROR>
  +     *
  +     */
  +    private void parseJspBody(Node parent, String bodyType) 
  +        throws JasperException 
  +    {
  +        Mark start = reader.mark();
  +        reader.skipSpaces();
  +
  +        if( reader.matches( ">" ) ) {
  +            Node bodyNode = new Node.JspBody( null, start, parent );
  +            if( reader.matches( "</jsp:body>" ) ) {
  +                // Body was empty.  This is illegal, according to the grammar.
  +                err.jspError(reader.mark(),
  +                    "jsp.error.empty.body.not.allowed",
  +                    "<jsp:body>" );
  +            }
  +            else {
  +                parseBody( bodyNode, "jsp:body", bodyType );
  +            }
  +        }
  +        else {
  +            Attributes attrs = parseAttributes();
  +            reader.skipSpaces();
  +            new Node.JspBody( attrs, start, parent );
  +
  +            if( !reader.matches( "/>" ) &&
  +                !reader.matches( "></jsp:body>" ) )
  +            {
  +                err.jspError(reader.mark(), 
  +                    "jsp.error.jspbody.body.not.allowed.with.value",
  +                    "<jsp:body>" );
  +            }
  +        }
  +    }
  +
  +    /*
        * Parse the body as JSP content.
        * @param tag The name of the tag whose end tag would terminate the body
  +     * @param bodyType One of the TagInfo body types
        */
  -    private void parseBody(Node parent, String tag) throws JasperException {
  +    private void parseBody(Node parent, String tag, String bodyType) 
  +        throws JasperException 
  +    {
  +        if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_TAG_DEPENDENT ) ) {
  +            parseBodyText( parent, tag );
  +        }
  +        else if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_EMPTY ) ) {
  +            if( !reader.matchesETag( tag ) ) {
  +		err.jspError(start, "jasper.error.emptybodycontent.nonempty");
  +            }
  +        }
  +        else if( bodyType == JAVAX_BODY_CONTENT_PLUGIN ) {
  +            // (note the == since we won't recognize JAVAX_* 
  +            // from outside this module).
  +            parsePluginTags(parent);
  +            if( !reader.matchesETag( tag ) ) {
  +                err.jspError( reader.mark(), "jsp.error.unterminated",
  +                    "<" + tag  );
  +            }
  +        }
  +        else if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_JSP ) ||
  +            bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_SCRIPTLESS ) ||
  +            (bodyType == JAVAX_BODY_CONTENT_PARAM) )
  +        {
  +            while (reader.hasMoreInput()) {
  +                if (reader.matchesETag(tag)) {
  +                    return;
  +                }
  +                
  +                if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_JSP ) ) {
  +                    parseElements( parent );
  +                }
  +                else if( bodyType.equalsIgnoreCase( 
  +                    TagInfo.BODY_CONTENT_SCRIPTLESS ) ) 
  +                {
  +                    parseElementsScriptless( parent );
  +                }
  +                else if( bodyType == JAVAX_BODY_CONTENT_PARAM ) {
  +                    // (note the == since we won't recognize JAVAX_* 
  +                    // from outside this module).
  +                    reader.skipSpaces();
  +                    parseParam( parent );
  +                }
  +            }
  +            err.jspError(start, "jsp.error.unterminated", "<"+tag );
  +        }
  +        else {
  +	    err.jspError(start, "jasper.error.bad.bodycontent.type");
  +        }
  +    }
   
  -	while (reader.hasMoreInput()) {
  -	    if (reader.matchesETag(tag)) {
  -		return;
  -	    }
  -	    parseElements(parent);
  -	}
  -	err.jspError(start, "jsp.error.unterminated", "<"+tag+">");
  +    /*
  +     * NamedAttributes ::= AttributeBody S?
  +     *                     ( '<jsp:attribute' AttributeBody S? )*
  +     *
  +     * AttributeBody   ::= ATTR[ !name, trim ] S? AttributeBodyRequired
  +     *
  +     * AttributeBodyRequired ::= '>' ( ScriptlessBody - '' ) '</jsp:attribute>'
  +     */
  +    private void parseNamedAttributes(Node parent) throws JasperException {
  +        do {
  +            Mark start = reader.mark();
  +            Attributes attrs = parseAttributes();
  +            reader.skipSpaces();
  +
  +            Node.NamedAttribute namedAttributeNode =
  +                new Node.NamedAttribute( attrs, start, parent );
  +
  +            if( reader.matches( "/>" ) ) {
  +                // Body was empty.  This is illegal, according to the grammar.
  +                err.jspError(reader.mark(), "jsp.error.empty.body.not.allowed", 
  +                    "<jsp:attribute" );
  +            }
  +            else if( !reader.matches( ">" ) ) {
  +                err.jspError(reader.mark(), "jsp.error.unterminated",
  +                    "<jsp:attribute");
  +            }
  +
  +            if( reader.matches( "</jsp:attribute" ) ) {
  +                // Body was empty.  This is illegal, according to the grammar.
  +                err.jspError(reader.mark(), "jsp.error.empty.body.not.allowed", 
  +                    "<jsp:attribute" );
  +            }
  +            else {
  +                if( namedAttributeNode.isTrim() ) {
  +                    reader.skipSpaces();
  +                }
  +                parseBody( namedAttributeNode, "jsp:attribute", 
  +                    TagInfo.BODY_CONTENT_SCRIPTLESS );
  +                if( namedAttributeNode.isTrim() ) {
  +                    Node.Nodes subelements = namedAttributeNode.getBody();
  +                    Node lastNode = subelements.getNode(
  +                        subelements.size() - 1 );
  +                    if( lastNode instanceof Node.TemplateText ) {
  +                        ((Node.TemplateText)lastNode).rtrim();
  +                    }
  +                }
  +            }
  +            reader.skipSpaces();
  +        } while( reader.matches( "<jsp:attribute" ) );
       }
   
  -    
   }
   
  
  
  
  1.2       +5 -3      jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagConstants.java
  
  Index: TagConstants.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagConstants.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TagConstants.java	28 Mar 2002 18:46:16 -0000	1.1
  +++ TagConstants.java	16 Jul 2002 19:30:51 -0000	1.2
  @@ -87,4 +87,6 @@
       public static final String JSP_TEXT_TAG = "jsp:text";
       public static final String JSP_TEXT_TAG_START = "<jsp:text>";
       public static final String JSP_TEXT_TAG_END = "</jsp:text>";
  +    public static final String JSP_ATTRIBUTE_TAG = "jsp:attribute";
  +    public static final String JSP_BODY_TAG = "jsp:body";
   }
  
  
  
  1.3       +142 -23   jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagLibraryInfoImpl.java
  
  Index: TagLibraryInfoImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagLibraryInfoImpl.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TagLibraryInfoImpl.java	16 May 2002 20:59:21 -0000	1.2
  +++ TagLibraryInfoImpl.java	16 Jul 2002 19:30:51 -0000	1.3
  @@ -78,6 +78,7 @@
    * @author Anil K. Vijendran
    * @author Mandar Raje
    * @author Pierre Delisle
  + * @author Kin-man Chung
    */
   public class TagLibraryInfoImpl extends TagLibraryInfo {
       static private final String TAGLIB_TLD = "META-INF/taglib.tld";
  @@ -110,6 +111,9 @@
           for(int i = 0; i < tags.length; i++)
               out.println(tags[i].toString());
           
  +        for(int i = 0; i < functions.length; i++)
  +            out.println(functions[i].toString());
  +        
           return sw.toString();
       }
       
  @@ -240,6 +244,7 @@
           throws JasperException
       {
           Vector tagVector = new Vector();
  +        Vector functionVector = new Vector();
   
           // Create an iterator over the child elements of our <taglib> element
           ClassLoader cl = ctxt.getClassLoader();
  @@ -271,6 +276,8 @@
                   this.tagLibraryValidator = createValidator(element);
               else if ("tag".equals(tname))
                   tagVector.addElement(createTagInfo(element));
  +            else if ("function".equals(tname))          // JSP2.0
  +                functionVector.addElement(createFunctionInfo(element));
               else if ("display-name".equals(tname) ||    // Ignored elements
                        "small-icon".equals(tname) ||
                        "large-icon".equals(tname) ||
  @@ -287,6 +294,9 @@
   
           this.tags = new TagInfo[tagVector.size()];
           tagVector.copyInto (this.tags);
  +
  +        this.functions = new FunctionInfo[functionVector.size()];
  +        functionVector.copyInto (this.functions);
       }
   
       private TagInfo createTagInfo(TreeNode elem) throws JasperException {
  @@ -298,42 +308,48 @@
   	String displayName = null;
   	String smallIcon = null;
   	String largeIcon = null;
  +        boolean dynamicAttributes = false;
           
           Vector attributeVector = new Vector();
           Vector variableVector = new Vector();
  +        Vector fragmentAttributeVector = new Vector();
           Iterator list = elem.findChildren();
           while (list.hasNext()) {
               TreeNode element = (TreeNode) list.next();
               String tname = element.getName();
   
  -            if ("name".equals(tname))
  +            if ("name".equals(tname)) {
                   name = element.getBody();
  -            else if ("tagclass".equals(tname) ||
  -                     "tag-class".equals(tname))
  +            } else if ("tagclass".equals(tname) ||
  +                     "tag-class".equals(tname)) {
                   tagclass = element.getBody();
  -            else if ("teiclass".equals(tname) ||
  -                     "tei-class".equals(tname))
  +            } else if ("teiclass".equals(tname) ||
  +                     "tei-class".equals(tname)) {
                   teiclass = element.getBody();
  -            else if ("bodycontent".equals(tname) ||
  -                     "body-content".equals(tname))
  +            } else if ("bodycontent".equals(tname) ||
  +                     "body-content".equals(tname)) {
                   bodycontent = element.getBody();
  -            else if ("display-name".equals(tname))
  +            } else if ("display-name".equals(tname)) {
                   displayName = element.getBody();
  -            else if ("small-icon".equals(tname))
  +            } else if ("small-icon".equals(tname)) {
                   smallIcon = element.getBody();
  -            else if ("large-icon".equals(tname))
  +            } else if ("large-icon".equals(tname)) {
                   largeIcon = element.getBody();
  -            else if ("info".equals(tname) ||
  -                     "description".equals(tname))
  +            } else if ("info".equals(tname) ||
  +                     "description".equals(tname)) {
                   info = element.getBody();
  -            else if ("variable".equals(tname)) {
  +            } else if ("variable".equals(tname)) {
                   variableVector.addElement(createVariable(element));
  -            } else if ("attribute".equals(tname))
  +            } else if ("attribute".equals(tname)) {
                   attributeVector.addElement(createAttribute(element));
  -            else if ("example".equals(tname) ||   // Ignored elements
  -		     false)
  -                ;
  -            else {
  +            } else if ("fragment-attribute".equals(tname)) {
  +                fragmentAttributeVector.addElement(
  +                                        createFragmentAttribute(element));
  +            } else if ("dynamic-attributes".equals(tname)) {
  +                dynamicAttributes = JspUtil.booleanValue(element.getBody());
  +            } else if ("example".equals(tname)) {
  +                // Ignored elements
  +            } else {
                   Constants.message("jsp.warning.unknown.element.in.tag", 
                                     new Object[] {tname},
                                     Logger.WARNING
  @@ -342,12 +358,15 @@
   	}
   	TagAttributeInfo[] tagAttributeInfo 
               = new TagAttributeInfo[attributeVector.size()];
  -	attributeVector.copyInto (tagAttributeInfo);
  +	attributeVector.copyInto(tagAttributeInfo);
   
   	TagVariableInfo[] tagVariableInfos
               = new TagVariableInfo[variableVector.size()];
   	variableVector.copyInto(tagVariableInfos);
   
  +        TagFragmentAttributeInfo [] fragmentAttributes
  +            = new TagFragmentAttributeInfo[fragmentAttributeVector.size()];
  +        fragmentAttributeVector.copyInto(fragmentAttributes);
   
           TagExtraInfo tei = null;
   
  @@ -393,7 +412,9 @@
   				      displayName,
   				      smallIcon,
   				      largeIcon,
  -				      tagVariableInfos);
  +				      tagVariableInfos,
  +                                      fragmentAttributes,
  +                                      dynamicAttributes);
           return taginfo;
       }
   
  @@ -436,6 +457,70 @@
           return new TagAttributeInfo(name, required, type, rtexprvalue);
       }
   
  +    TagFragmentAttributeInfo createFragmentAttribute(TreeNode elem) {
  +
  +        Vector fragmentInputVector = new Vector();
  +        String name = null;
  +        String description = null;
  +        boolean required = false;
  +
  +        Iterator list = elem.findChildren();
  +        while (list.hasNext()) {
  +            TreeNode element = (TreeNode) list.next();
  +            String tname = element.getName();
  +
  +            if ("name".equals(tname)) {
  +                name = element.getBody();
  +            } else if ("required".equals(tname)) {
  +                required = JspUtil.booleanValue(element.getBody());
  +            } else if ("fragment-input".equals(tname)) {
  +                fragmentInputVector.addElement(createFragmentInput(element));
  +            } else if ("description".equals(tname)) {
  +                description = element.getBody();
  +            } else {
  +                Constants.message("jsp.warning.unknown.element.in.attribute",
  +                                  new Object[] {tname},
  +                                  Logger.WARNING
  +                                  );
  +            }
  +        }
  +
  +        TagFragmentAttributeInfo.FragmentInput[] fragmentInputs =
  +            new TagFragmentAttributeInfo.FragmentInput[fragmentInputVector.size()];
  +        fragmentInputVector.copyInto(fragmentInputs);
  +
  +        return new TagFragmentAttributeInfo(name, required, description,
  +                                            fragmentInputs);
  +    }
  +
  +    TagFragmentAttributeInfo.FragmentInput createFragmentInput(TreeNode elem) {
  +
  +        String name = null;
  +        String type = null;
  +        String description = null;
  +
  +        Iterator list = elem.findChildren();
  +        while (list.hasNext()) {
  +            TreeNode element = (TreeNode) list.next();
  +            String tname = element.getName();
  +
  +            if ("name".equals(tname)) {
  +                name = element.getBody();
  +            } else if ("type".equals(tname)) {
  +                type = element.getBody();
  +            } else if ("description".equals(tname)) {
  +                description = element.getBody();
  +            } else {
  +                Constants.message("jsp.warning.unknown.element.in.attribute", 
  +                                  new Object[] {tname},
  +                                  Logger.WARNING
  +                                  );
  +            }
  +        }
  +        return new TagFragmentAttributeInfo.FragmentInput(name, type,
  +                                                          description);
  +    }
  +
       TagVariableInfo createVariable(TreeNode elem) {
           String nameGiven = null;
           String nameFromAttribute = null;
  @@ -547,6 +632,39 @@
   	return initParam;
       }
   
  +    FunctionInfo createFunctionInfo(TreeNode elem) {
  +
  +        String name = null;
  +        String klass = null;
  +        String signature = null;
  +
  +        Iterator list = elem.findChildren();
  +        while (list.hasNext()) {
  +            TreeNode element = (TreeNode) list.next();
  +            String tname = element.getName();
  +
  +            if ("name".equals(tname)) {
  +                name = element.getBody();
  +            } else if ("function-class".equals(tname)) {
  +                klass = element.getBody();
  +            } else if ("function-signature".equals(tname)) {
  +                signature = element.getBody();
  +            } else if ("display-name".equals(tname) ||    // Ignored elements
  +                     "small-icon".equals(tname) ||
  +                     "large-icon".equals(tname) ||
  +                     "description".equals(tname) || 
  +                     "example".equals(tname)) {
  +            } else {
  +                Constants.message("jsp.warning.unknown.element.in.function",
  +                                  new Object[] {tname},
  +                                  Logger.WARNING);
  +	    }
  +        }
  +
  +        return new FunctionInfo(name, klass, signature);
  +    }
  +
  +/*
       static void copy(InputStream in, String fileName) 
           throws IOException, FileNotFoundException 
       {
  @@ -557,6 +675,7 @@
           while ((nRead = in.read(buf, 0, buf.length)) != -1)
               out.write(buf, 0, nRead);
       }
  +*/
   
       //*********************************************************************
       // Until javax.servlet.jsp.tagext.TagLibraryInfo is fixed
  
  
  
  1.12      +229 -69   jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java
  
  Index: Validator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- Validator.java	8 Jul 2002 17:28:58 -0000	1.11
  +++ Validator.java	16 Jul 2002 19:30:51 -0000	1.12
  @@ -62,14 +62,19 @@
   
   import java.util.Hashtable;
   import java.util.Enumeration;
  +
   import javax.servlet.jsp.tagext.PageData;
   import javax.servlet.jsp.tagext.TagData;
   import javax.servlet.jsp.tagext.TagInfo;
   import javax.servlet.jsp.tagext.TagAttributeInfo;
  +import javax.servlet.jsp.tagext.TagFragmentAttributeInfo;
   import javax.servlet.jsp.tagext.TagLibraryInfo;
   import javax.servlet.jsp.tagext.TagLibraryInfo;
   import javax.servlet.jsp.tagext.ValidationMessage;
  +
  +import org.apache.jasper.Constants;
   import org.apache.jasper.JasperException;
  +
   import org.xml.sax.Attributes;
   
   /**
  @@ -80,6 +85,8 @@
    *
    * @author Kin-man Chung
    * @author Jan Luehe
  + * @author Shawn Bayern
  + * @author Mark Roth
    */
   public class Validator {
   
  @@ -103,7 +110,8 @@
   	    new JspUtil.ValidAttribute("errorPage"),
   	    new JspUtil.ValidAttribute("isErrorPage"),
   	    new JspUtil.ValidAttribute("contentType"),
  -	    new JspUtil.ValidAttribute("pageEncoding") };
  +	    new JspUtil.ValidAttribute("pageEncoding"),
  +	    new JspUtil.ValidAttribute("isScriptingEnabled") };
   
   	private boolean languageSeen = false;
   	private boolean extendsSeen = false;
  @@ -127,8 +135,8 @@
   
   	public void visit(Node.PageDirective n) throws JasperException {    
   
  -	    JspUtil.checkAttributes("Page directive", n.getAttributes(),
  -				    pageDirectiveAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("Page directive", n,
  +                                    pageDirectiveAttrs, err);
   
   	    // JSP.2.10.1
   	    Attributes attrs = n.getAttributes();
  @@ -210,6 +218,14 @@
   			pageInfo.setThreadSafe(false);
   		    else
   			err.jspError(n, "jsp.error.isThreadSafe.invalid");
  +		} else if ("isScriptingEnabled".equals(attr)) {
  +		    // XXX Test for multiple occurrence?
  +		    if ("true".equalsIgnoreCase(value))
  +			pageInfo.setScriptingEnabled(true);
  +		    else if ("false".equalsIgnoreCase(value))
  +			pageInfo.setScriptingEnabled(false);
  +		    else
  +			err.jspError(n, "jsp.error.isScriptingEnabled.invalid");
   		} else if ("isErrorPage".equals(attr)) {
   		    if (isErrorPageSeen)
   			err.jspError(n, "jsp.error.page.multiple.iserrorpage");
  @@ -266,15 +282,15 @@
   	    new JspUtil.ValidAttribute("prefix", true) };
   
   	private static final JspUtil.ValidAttribute[] includeActionAttrs = {
  -	    new JspUtil.ValidAttribute("page", true),
  +	    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) };
  +	    new JspUtil.ValidAttribute("value", true, true) };
   
   	private static final JspUtil.ValidAttribute[] forwardActionAttrs = {
  -	    new JspUtil.ValidAttribute("page", true) };
  +	    new JspUtil.ValidAttribute("page", true, true) };
   
   	private static final JspUtil.ValidAttribute[] getPropertyAttrs = {
   	    new JspUtil.ValidAttribute("name", true),
  @@ -283,7 +299,7 @@
   	private static final JspUtil.ValidAttribute[] setPropertyAttrs = {
   	    new JspUtil.ValidAttribute("name", true),
   	    new JspUtil.ValidAttribute("property", true),
  -	    new JspUtil.ValidAttribute("value"),
  +	    new JspUtil.ValidAttribute("value", false, true),
   	    new JspUtil.ValidAttribute("param") };
   
   	private static final JspUtil.ValidAttribute[] useBeanAttrs = {
  @@ -291,7 +307,7 @@
   	    new JspUtil.ValidAttribute("scope"),
   	    new JspUtil.ValidAttribute("class"),
   	    new JspUtil.ValidAttribute("type"),
  -	    new JspUtil.ValidAttribute("beanName") };
  +	    new JspUtil.ValidAttribute("beanName", false, true) };
   
   	private static final JspUtil.ValidAttribute[] plugInAttrs = {
   	    new JspUtil.ValidAttribute("type",true),
  @@ -299,14 +315,21 @@
   	    new JspUtil.ValidAttribute("codebase"),
   	    new JspUtil.ValidAttribute("align"),
   	    new JspUtil.ValidAttribute("archive"),
  -	    new JspUtil.ValidAttribute("height"),
  +	    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"),
  +	    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[] bodyAttrs = {
  +            new JspUtil.ValidAttribute("value") };
   
   	/*
   	 * Constructor
  @@ -317,71 +340,75 @@
   	}
   
   	public void visit(Node.JspRoot n) throws JasperException {
  -	    JspUtil.checkAttributes("Jsp:root", n.getAttributes(),
  -				    jspRootAttrs, n.getStart(), err);
  +	    JspUtil.checkAttributes("Jsp:root", n,
  +				    jspRootAttrs, err);
   	    visitBody(n);
   	}
   
   	public void visit(Node.IncludeDirective n) throws JasperException {
  -	    JspUtil.checkAttributes("Include directive", n.getAttributes(),
  -				    includeDirectiveAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("Include directive", n,
  +                                    includeDirectiveAttrs, err);
   	    visitBody(n);
   	}
   
   	public void visit(Node.TaglibDirective n) throws JasperException {
  -	    JspUtil.checkAttributes("Taglib directive", n.getAttributes(),
  -				    taglibDirectiveAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("Taglib directive", n,
  +                                    taglibDirectiveAttrs, err);
   	}
   
   	public void visit(Node.ParamAction n) throws JasperException {
  -	    JspUtil.checkAttributes("Param action", n.getAttributes(),
  -				    paramActionAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("Param action", n,
  +                                    paramActionAttrs, err);
   	    n.setValue(getJspAttribute("value", n.getAttributeValue("value"),
  -				       n.isXmlSyntax()));
  +				       n));
  +            visitBody(n);
   	}
   
   	public void visit(Node.IncludeAction n) throws JasperException {
  -	    JspUtil.checkAttributes("Include action", n.getAttributes(),
  -				    includeActionAttrs, n.getStart(), err);
  -	    n.setPage(getJspAttribute("page", n.getAttributeValue("page"),
  -				      n.isXmlSyntax()));
  +            JspUtil.checkAttributes("Include action", n,
  +                                    includeActionAttrs, err);
  +	    n.setPage(getJspAttribute("page", n.getAttributeValue("page"), n));
   	    visitBody(n);
           };
   
   	public void visit(Node.ForwardAction n) throws JasperException {
  -            JspUtil.checkAttributes("Forward", n.getAttributes(),
  -				    forwardActionAttrs, n.getStart(), err);
  -	    n.setPage(getJspAttribute("page", n.getAttributeValue("page"),
  -				      n.isXmlSyntax()));
  +            JspUtil.checkAttributes("Forward", n,
  +                                    forwardActionAttrs, err);
  +	    n.setPage(getJspAttribute("page", n.getAttributeValue("page"), n));
   	    visitBody(n);
   	}
   
   	public void visit(Node.GetProperty n) throws JasperException {
  -	    JspUtil.checkAttributes("GetProperty", n.getAttributes(),
  -				    getPropertyAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("GetProperty", n,
  +                                    getPropertyAttrs, err);
   	}
   
   	public void visit(Node.SetProperty n) throws JasperException {
  -	    JspUtil.checkAttributes("SetProperty", n.getAttributes(),
  -				    setPropertyAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("SetProperty", n,
  +                                    setPropertyAttrs, err);
   	    String name = n.getAttributeValue("name");
   	    String property = n.getAttributeValue("property");
   	    String param = n.getAttributeValue("param");
   	    String value = n.getAttributeValue("value");
   
  +            n.setValue(getJspAttribute("value", value, n));
  +
  +            boolean valueSpecified = n.getValue() != null;
  +
   	    if ("*".equals(property)) { 
  -		if (param != null || value != null)
  +                if (param != null || valueSpecified)
   		    err.jspError(n, "jsp.error.setProperty.invalid");
   		
  -	    } else if (param != null && value != null) {
  +            } else if (param != null && valueSpecified) {
   		err.jspError(n, "jsp.error.setProperty.invalid");
   	    }
  -	    n.setValue(getJspAttribute("value", value, n.isXmlSyntax()));
  +            
  +            visitBody(n);
   	}
   
   	public void visit(Node.UseBean n) throws JasperException {
  -	    JspUtil.checkAttributes("UseBean", n.getAttributes(),
  -				    useBeanAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("UseBean", n,
  +                                    useBeanAttrs, err);
   
   	    String name = n.getAttributeValue ("id");
   	    String scope = n.getAttributeValue ("scope");
  @@ -400,7 +427,7 @@
   
   	    Node.JspAttribute jattr
   		= getJspAttribute("beanName", n.getAttributeValue("beanName"),
  -				  n.isXmlSyntax());
  +				  n);
   	    n.setBeanName(jattr);
   	    if (className != null && jattr != null)
   		err.jspError(n, "jsp.error.useBean.notBoth");
  @@ -423,8 +450,7 @@
   	}
   
   	public void visit(Node.PlugIn n) throws JasperException {
  -	    JspUtil.checkAttributes("Plugin", n.getAttributes(),
  -				    plugInAttrs, n.getStart(), err);
  +            JspUtil.checkAttributes("Plugin", n, plugInAttrs, err);
   
   	    String type = n.getAttributeValue("type");
   	    if (type == null)
  @@ -433,14 +459,61 @@
   		err.jspError(n, "jsp.error.plugin.badtype");
   	    if (n.getAttributeValue("code") == null)
   		err.jspError(n, "jsp.error.plugin.nocode");
  +            
  +	    Node.JspAttribute width = getJspAttribute("width", 
  +                n.getAttributeValue("width"), n);
  +	    n.setWidth( width );
  +            
  +	    Node.JspAttribute height = getJspAttribute("height", 
  +                n.getAttributeValue("height"), n);
  +	    n.setHeight( height );
   
   	    n.setHeight(getJspAttribute("height", n.getAttributeValue("height"),
  -				      n.isXmlSyntax()));
  +					n));
   	    n.setWidth(getJspAttribute("width", n.getAttributeValue("width"),
  -				      n.isXmlSyntax()));
  +					n));
   	    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 {
  +	    JspUtil.checkAttributes("Body", n,
  +				    bodyAttrs, err);
  +	    n.setValue(getJspAttribute("value", n.getAttributeValue("value"),
  +				       n));
  +            visitBody(n);
  +	}
  +        
  +	public void visit(Node.Declaration n) throws JasperException {
  +	    if (! pageInfo.isScriptingEnabled()) {
  +		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
  +	    }
  +	}
  +
  +        public void visit(Node.Expression n) throws JasperException {
  +	    if (! pageInfo.isScriptingEnabled()) {
  +		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
  +	    }
  +	}
  +
  +        public void visit(Node.Scriptlet n) throws JasperException {
  +	    if (! pageInfo.isScriptingEnabled()) {
  +		err.jspError(n.getStart(), "jsp.error.no.scriptlets");
  +	    }
  +	}
  +
  +	public void visit(Node.ELExpression n) throws JasperException {
  +            if ( true /*isELEnabled*/ ) {
  +                JspUtil.validateExpressions(n.getStart(),
  +                    "${" + new String(n.getText()) + "}", err);
  +            }
  +        }
  +
   	public void visit(Node.CustomTag n) throws JasperException {
   	    TagLibraryInfo tagLibInfo = (TagLibraryInfo)
   		pageInfo.getTagLibraries().get(n.getPrefix());
  @@ -450,13 +523,16 @@
   	    }
   
   	    /*
  -	     * Make sure all required attributes are present
  +	     * Make sure all required attributes are present, either as
  +             * attributes or named attributes (<jsp:attribute>).
   	     */
   	    TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
   	    Attributes attrs = n.getAttributes();
   	    for (int i=0; i<tldAttrs.length; i++) {
  -		if (tldAttrs[i].isRequired()
  -		    && attrs.getValue(tldAttrs[i].getName()) == null) {
  +		if (tldAttrs[i].isRequired() &&
  +		    (attrs.getValue(tldAttrs[i].getName()) == null) &&
  +                    (n.getNamedAttributeNode(tldAttrs[i].getName()) == null) )
  +                {
   		    err.jspError(n, "jsp.error.missing_attribute",
   				 tldAttrs[i].getName(), n.getShortName());
   		}
  @@ -465,9 +541,11 @@
   	    /*
   	     * Make sure there are no invalid attributes
   	     */
  +            Node.Nodes namedAttributeNodes = n.getNamedAttributeNodes();
   	    Hashtable tagDataAttrs = new Hashtable(attrs.getLength());
   	    Node.JspAttribute[] jspAttrs
  -		= new Node.JspAttribute[attrs.getLength()];
  +		= new Node.JspAttribute[attrs.getLength() + 
  +                namedAttributeNodes.size()];
   	    for (int i=0; i<attrs.getLength(); i++) {
   		boolean found = false;
   		for (int j=0; j<tldAttrs.length; j++) {
  @@ -476,12 +554,12 @@
   			    jspAttrs[i]
   				= getJspAttribute(attrs.getQName(i),
   						  attrs.getValue(i),
  -						  n.isXmlSyntax());
  +						  n);
   			} else {
   			    jspAttrs[i]
   				= new Node.JspAttribute(attrs.getQName(i),
   							attrs.getValue(i),
  -							false);
  +							false, false);
   			}
   			if (jspAttrs[i].isExpression()) {
   			    tagDataAttrs.put(attrs.getQName(i),
  @@ -499,6 +577,50 @@
   				 attrs.getQName(i));
   		}
   	    }
  +            
  +	    /*
  +	     * Make sure there are no invalid named attributes
  +	     */
  +	    TagFragmentAttributeInfo[] tfais = tagInfo.getFragmentAttributes();
  +	    for (int i=0; i<namedAttributeNodes.size(); i++) {
  +                Node.NamedAttribute na = 
  +                    (Node.NamedAttribute)namedAttributeNodes.getNode( i );
  +		boolean found = false;
  +		for (int j=0; j<tldAttrs.length; j++) {
  +		    if (na.getName().equals(tldAttrs[j].getName())) {
  +			if (tldAttrs[j].canBeRequestTime()) {
  +			    jspAttrs[attrs.getLength() + i]
  +				= getJspAttribute(na.getName(), null, n);
  +			} else {
  +                            err.jspError( n, 
  +                                "jsp.error.named.attribute.not.rt",
  +                                na.getName() );
  +			}
  +                        tagDataAttrs.put(na.getName(),
  +                                         TagData.REQUEST_TIME_VALUE);
  +			found = true;
  +			break;
  +		    }
  +		}
  +		if (!found && (tfais != null)) {
  +		    // check given named attribute against attributes of type
  +		    // JspFragment
  +		    for (int j=0; j<tfais.length; j++) {
  +			if (na.getName().equals(tfais[j].getName())) {
  +			    jspAttrs[attrs.getLength() + i]
  +				= getJspAttribute(na.getName(), null, n);
  +			    tagDataAttrs.put(na.getName(),
  +					     TagData.REQUEST_TIME_VALUE);
  +			    found = true;
  +			    break;
  +			}
  +		    }
  +		}
  +		if (!found) {
  +		    err.jspError(n, "jsp.error.bad_attribute",
  +				 na.getName());
  +		}
  +	    }
   
   	    TagData tagData = new TagData(tagDataAttrs);
   	    n.setTagData(tagData);
  @@ -510,33 +632,71 @@
   	/**
   	 * 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 name,
   						  String value,
  -						  boolean isXml) {
  +                                                  Node n)
  +                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)
  -		return null;
  +            if (value != null) {
  +                if (n.isXmlSyntax() && value.startsWith("%=")) {
  +                    result = new Node.JspAttribute(
  +                                             name,
  +                                             value.substring(2,
  +                                                             value.length()-1),
  +                                             true, false);
  +                }
  +                else if(!n.isXmlSyntax() && value.startsWith("<%=")) {
  +                    result = new Node.JspAttribute(
  +                                             name,
  +                                             value.substring(3,
  +                                                             value.length()-2),
  +                                             true, false);
  +                }
  +                else {
  +                    // The attribute can contain expressions but is not an
  +                    // rtexprvalue; thus, we want to run it through the
  +                    // expression interpreter (final argument "true" in
  +                    // Node.JspAttribute constructor).
  +                    // XXX Optimize by directing generator to pass expressions
  +                    //     through interpreter only if they contain at least
  +                    //     one "${"?  (But ensure consistent type conversions
  +                    //     in JSP 1.3!)
  +
  +                    // validate expression syntax if string contains
  +                    // expression(s)
  +                    if (value.indexOf("${") != -1 /* && isELEnabled */) {
  +                        JspUtil.validateExpressions(n.getStart(), value, err);
  +                        result = new Node.JspAttribute(name, value, false, true);
  +                    } else {
  +                        result = new Node.JspAttribute(name, value, false, false);
  +                    }
  +                }
  +            }
  +            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( name );
  +                if( namedAttributeNode != null ) {
  +                    result = new Node.JspAttribute(name, namedAttributeNode);
  +                }
  +            }
   
  -	    if (isXml && value.startsWith("%=")) {
  -		return new Node.JspAttribute(name,
  -					     value.substring(2,
  -							     value.length()-1),
  -					     true);
  -	    }
  -
  -	    if (!isXml && value.startsWith("<%=")) {
  -		return new Node.JspAttribute(name,
  -					     value.substring(3,
  -							     value.length()-2),
  -					     true);
  -	    }
  -
  -	    return new Node.JspAttribute(name, value, false);
  -	}
  +            return result;
  +        }
       }
   
       /**
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/SmapGenerator.java
  
  Index: SmapGenerator.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  
  package org.apache.jasper.compiler;
  
  import java.util.List;
  import java.util.ArrayList;
  
  /**
   * Represents a source map (SMAP), which serves to associate lines
   * of the input JSP file(s) to lines in the generated servlet in the
   * final .class file, according to the JSR-045 spec.
   * 
   * @author Shawn Bayern
   */
  public class SmapGenerator {
  
      //*********************************************************************
      // Overview
  
      /*
       * The SMAP syntax is reasonably straightforward.  The purpose of this
       * class is currently twofold:
       *  - to provide a simple but low-level Java interface to build
       *    a logical SMAP
       *  - to serialize this logical SMAP for eventual inclusion directly
       *    into a .class file.
       */
  
  
      //*********************************************************************
      // Private state
  
      private String outputFileName;
      private String defaultStratum = "Java";
      private List strata = new ArrayList();
      private List embedded = new ArrayList();
      private boolean doEmbedded = true;
  
      //*********************************************************************
      // Methods for adding mapping data
  
      /**
       * Sets the filename (without path information) for the generated
       * source file.  E.g., "foo$jsp.java".
       */
      public synchronized void setOutputFileName(String x) {
  	outputFileName = x;
      }
  
      /**
       * Adds the given SmapStratum object, representing a Stratum with
       * logically associated FileSection and LineSection blocks, to
       * the current SmapGenerator.  If <tt>default</tt> is true, this
       * stratum is made the default stratum, overriding any previously
       * set default.
       *
       * @param stratum the SmapStratum object to add
       * @param defaultStratum if <tt>true</tt>, this SmapStratum is considered
       *                to represent the default SMAP stratum unless
       *                overwritten
       */
      public synchronized void addStratum(SmapStratum stratum,
  					boolean defaultStratum) {
  	strata.add(stratum);
  	if (defaultStratum)
  	    this.defaultStratum = stratum.getStratumName();
      }
  
      /**
       * Adds the given string as an embedded SMAP with the given stratum name.
       *
       * @param smap the SMAP to embed
       * @param stratumName the name of the stratum output by the compilation
       *                    that produced the <tt>smap</tt> to be embedded
       */
      public synchronized void addSmap(String smap, String stratumName) {
  	embedded.add("*O " + stratumName + "\n"
  		   + smap
  		   + "*C " + stratumName + "\n");
      }
  
      /**
       * Instructs the SmapGenerator whether to actually print any embedded
       * SMAPs or not.  Intended for situations without an SMAP resolver.
       *
       * @param status If <tt>false</tt>, ignore any embedded SMAPs.
       */
      public void setDoEmbedded(boolean status) {
  	doEmbedded = status;
      }
  
      //*********************************************************************
      // Methods for serializing the logical SMAP
  
      public synchronized String getString() {
  	// check state and initialize buffer
  	if (outputFileName == null)
  	    throw new IllegalStateException();
          StringBuffer out = new StringBuffer();
  
  	// start the SMAP
  	out.append("SMAP\n");
  	out.append(outputFileName + '\n');
  	out.append(defaultStratum + '\n');
  
  	// include embedded SMAPs
  	if (doEmbedded) {
  	    int nEmbedded = embedded.size();
  	    for (int i = 0; i < nEmbedded; i++) {
  	        out.append(embedded.get(i));
  	    }
  	}
  
  	// print our StratumSections, FileSections, and LineSections
  	int nStrata = strata.size();
  	for (int i = 0; i < nStrata; i++) {
  	    SmapStratum s = (SmapStratum) strata.get(i);
  	    out.append(s.getString());
  	}
  
  	// end the SMAP
  	out.append("*E\n");
  
  	return out.toString();
      }
  
      public String toString() { return getString(); }
  
      //*********************************************************************
      // For testing (and as an example of use)...
  
      public static void main(String args[]) {
  	SmapGenerator g = new SmapGenerator();
  	g.setOutputFileName("foo.java");
  	SmapStratum s = new SmapStratum("JSP");
  	s.addFile("foo.jsp");
  	s.addFile("bar.jsp", "/foo/foo/bar.jsp");
  	s.addLineData(1, "foo.jsp", 1, 1, 1);
  	s.addLineData(2, "foo.jsp", 1, 6, 1);
  	s.addLineData(3, "foo.jsp", 2, 10, 5);
  	s.addLineData(20, "bar.jsp", 1, 30, 1);
  	g.addStratum(s, true);
  	System.out.print(g);
  
  	System.out.println("---");
  
  	SmapGenerator embedded = new SmapGenerator();
  	embedded.setOutputFileName("blargh.tier2");
  	s = new SmapStratum("Tier2");
  	s.addFile("1.tier2");
  	s.addLineData(1, "1.tier2", 1, 1, 1);
  	embedded.addStratum(s, true);
  	g.addSmap(embedded.toString(), "JSP");
  	System.out.println(g);
      }
  }
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/SmapStratum.java
  
  Index: SmapStratum.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  
  package org.apache.jasper.compiler;
  
  import java.util.List;
  import java.util.ArrayList;
  
  /**
   * Represents the line and file mappings associated with a JSR-045
   * "stratum".
   *
   * @author Jayson Falkner
   * @author Shawn Bayern
   */
  public class SmapStratum {
  
      //*********************************************************************
      // Class for storing LineInfo data
  
      /**
       * Represents a single LineSection in an SMAP, associated with
       * a particular stratum.
       */
      public static class LineInfo {
  	private int inputStartLine = -1;
  	private int outputStartLine = -1;
  	private int lineFileID = 0;
  	private int inputLineCount = 1;
  	private int outputLineIncrement = 1;
  	private boolean lineFileIDSet = false;
  
  	/** Sets InputStartLine. */
  	public void setInputStartLine(int inputStartLine) {
  	    if (inputStartLine < 0)
  		throw new IllegalArgumentException("" + inputStartLine);
  	    this.inputStartLine = inputStartLine;
  	}
  
  	/** Sets OutputStartLine. */
  	public void setOutputStartLine(int outputStartLine) {
  	    if (outputStartLine < 0)
  		throw new IllegalArgumentException("" + outputStartLine);
  	    this.outputStartLine = outputStartLine;
  	}
  
  	/**
           * Sets lineFileID.  Should be called only when different from
           * that of prior LineInfo object (in any given context) or 0
           * if the current LineInfo has no (logical) predecessor.
           * <tt>LineInfo</tt> will print this file number no matter what.
           */
  	public void setLineFileID(int lineFileID) {
  	    if (lineFileID < 0)
  		throw new IllegalArgumentException("" + lineFileID);
  	    this.lineFileID = lineFileID;
  	    this.lineFileIDSet = true;
  	}
  
  	/** Sets InputLineCount. */
  	public void setInputLineCount(int inputLineCount) {
  	    if (inputLineCount < 0)
  		throw new IllegalArgumentException("" + inputLineCount);
  	    this.inputLineCount = inputLineCount;
  	}
  
  	/** Sets OutputLineIncrement. */
  	public void setOutputLineIncrement(int outputLineIncrement) {
  	    if (outputLineIncrement < 0)
  		throw new IllegalArgumentException("" + outputLineIncrement);
  	    this.outputLineIncrement = outputLineIncrement;
  	}
  
          /**
           * Retrieves the current LineInfo as a String, print all values
           * only when appropriate (but LineInfoID if and only if it's been
           * specified, as its necessity is sensitive to context).
           */
  	public String getString() {
  	    if (inputStartLine == -1 || outputStartLine == -1)
  		throw new IllegalStateException();
  	    StringBuffer out = new StringBuffer();
  	    out.append(inputStartLine);
  	    if (lineFileIDSet)
  		out.append("#" + lineFileID);
  	    if (inputLineCount != 1)
  		out.append("," + inputLineCount);
  	    out.append(":" + outputStartLine);
  	    if (outputLineIncrement != 1)
  		out.append("," + outputLineIncrement);
  	    out.append('\n');
  	    return out.toString();
  	}
  
  	public String toString() { return getString(); }
      }
  
      //*********************************************************************
      // Private state
  
      private String stratumName;
      private List fileNameList;
      private List filePathList;
      private List lineData;
      private int lastFileID;
  
      //*********************************************************************
      // Constructor
  
      /**
       * Constructs a new SmapStratum object for the given stratum name
       * (e.g., JSP).
       *
       * @param stratumName the name of the stratum (e.g., JSP)
       */
      public SmapStratum(String stratumName) {
  	this.stratumName = stratumName;
  	fileNameList = new ArrayList();
  	filePathList = new ArrayList();
  	lineData = new ArrayList();
  	lastFileID = 0;
      }
  
  
      //*********************************************************************
      // Methods to add mapping information
  
      /**
       * Adds record of a new file, by filename.
       *
       * @param fileName the filename to add, unqualified by path.
       */
      public void addFile(String filename) {
  	addFile(filename, null);
      }
  
      /**
       * Adds record of a new file, by filename and path.  The path
       * may be relative to a source compilation path.
       *
       * @param fileName the filename to add, unqualified by path
       * @param filePath the path for the filename, potentially relative
       *                 to a source compilation path
       */
      public synchronized void addFile(String filename, String filePath) {
        // fix this to check if duplicate name exists.
        int fileIndex = fileNameList.indexOf(filename);
        if (fileIndex == -1) {
          fileNameList.add(filename);
          filePathList.add(filePath);
        }
      }
   
      /**
       * Adds complete information about a simple line mapping.  Specify
       * all the fields in this method; the back-end machinery takes care
       * of printing only those that are necessary in the final SMAP.
       * (My view is that fields are optional primarily for spatial efficiency,
       * not for programmer convenience.  Could always add utility methods
       * later.)
       *
       * @param inputStartLine starting line in the source file
       *        (SMAP <tt>InputStartLine</tt>)
       * @param inputFileName the filepath (or name) from which the input comes
       *        (yields SMAP <tt>LineFileID</tt>)  Use unqualified names
       *        carefully, and only when they uniquely identify a file.
       * @param inputLineCount the number of lines in the input to map
       *        (SMAP <tt>LineFileCount</tt>)
       * @param outputStartLine starting line in the output file 
       *        (SMAP <tt>OutputStartLine</tt>)
       * @param outputLineIncrement number of output lines to map to each
       *        input line (SMAP <tt>OutputLineIncrement</tt>).  <i>Given the
       *        fact that the name starts with "output", I continuously have
       *        the subconscious urge to call this field
       *        <tt>OutputLineExcrement</tt>.</i>
       */
      public synchronized void addLineData(int inputStartLine,
  				    String inputFileName,
  				    int inputLineCount,
  				    int outputStartLine,
  				    int outputLineIncrement) {
  	// check the input - what are you doing here??
  //	int fileIndex = filePathList.indexOf(inputFileName);
  //	if (fileIndex == -1)
  //        fileNameList.indexOf(inputFileName);
  	int fileIndex = fileNameList.indexOf(inputFileName);
  	if (fileIndex == -1)					// still
  	    throw new IllegalArgumentException(
  		"inputFileName: " + inputFileName);
  
  	// build the LineInfo
  	LineInfo li = new LineInfo();
  	li.setInputStartLine(inputStartLine);
  	li.setInputLineCount(inputLineCount);
  	li.setOutputStartLine(outputStartLine);
  	li.setOutputLineIncrement(outputLineIncrement);
  	if (fileIndex != lastFileID)
  	    li.setLineFileID(fileIndex);
  	lastFileID = fileIndex;
  
  	// save it
  	lineData.add(li);
      }
  
      //*********************************************************************
      // Methods to retrieve information
  
      /**
       * Returns the name of the stratum.
       */
      public String getStratumName() {
  	return stratumName;
      }
  
      /**
       * Returns the given stratum as a String:  a StratumSection,
       * followed by at least one FileSection and at least one LineSection.
       */
      public synchronized String getString() {
  	// check state and initialize buffer
  	if (fileNameList.size() == 0 || lineData.size() == 0)
  	    throw new IllegalStateException();
  	StringBuffer out = new StringBuffer();
  
  	// print StratumSection
  	out.append("*S " + stratumName + "\n");
  
  	// print FileSection
  	out.append("*F\n");
  	int bound = fileNameList.size();
  	for (int i = 0; i < bound; i++) {
  	    if (filePathList.get(i) != null) {
  		out.append("+ " + i + " " + fileNameList.get(i) + "\n");
  		out.append(filePathList.get(i) + "\n");
  	    } else {
  		out.append(i + " " + fileNameList.get(i) + "\n");
  	    }
  	}
  
  	// print LineSection
  	out.append("*L\n");
  	bound = lineData.size();
  	for (int i = 0; i < bound; i++) {
  	    LineInfo li = (LineInfo) lineData.get(i);
  	    out.append(li.getString());
  	}
  
  	return out.toString();
      }
  
      public String toString() { return getString(); }
  
  }
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/SmapUtil.java
  
  Index: SmapUtil.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  
  package org.apache.jasper.compiler;
  
  import java.io.*;
  import java.util.*;
  import org.apache.jasper.JspCompilationContext;
  import org.apache.jasper.compiler.Node;
  
  /**
   * Contains static utilities for generating SMAP data based on the
   * current version of Jasper.
   * 
   * @author Jayson Falkner
   * @author Shawn Bayern
   * @author Robert Field (inner SDEInstaller class)
   * @author Mark Roth
   */
  public class SmapUtil {
  
      //*********************************************************************
      // Constants
  
      public static final String SMAP_ENCODING = "UTF-8";
  
      //*********************************************************************
      // Public entry points
  
      /**
       * Generates an appropriate SMAP representing the current compilation
       * context and optionally installs in the target .class file.  (JSR-045.)
       * If the keepGenerated flag is set in the compilation context, the
       * generated .smap file will remain.  Otherwise, it will be deleted.
       *
       * @param install True if the SourceDebugExtension is to be installed
       *     in the generated .class file, or false if not.
       */
      public static void generateSmap(JspCompilationContext ctxt,
                                                Node.Nodes pageNodes,
  					      boolean install )
          throws IOException 
      {
  	// set up our SMAP generator
  	SmapGenerator g = new SmapGenerator();
  
  	// determine if we have an input SMAP
  	String smapPath = inputSmapPath(ctxt.getRealPath(ctxt.getJspFile()));
          File inputSmap = new File(smapPath);
          if (inputSmap.exists()) {
          byte[] embeddedSmap = null;
  	    byte[] subSmap = SDEInstaller.readWhole(inputSmap);
  	    String subSmapString = new String(subSmap, SMAP_ENCODING);
  	    g.addSmap(subSmapString, "JSP");
  	}
  
          // now, assemble info about our own stratum (JSP) using JspLineMap
          SmapStratum s = new SmapStratum("JSP");
  
          g.setOutputFileName(unqualify(ctxt.getServletJavaFileName()));
          // recursively map out Node.Nodes, it seems ugly..but its the only way??
          evaluateNodes(pageNodes, s);
          g.addStratum(s, true);
  
  	/*
           * Save the output to a temporary file (just so as to use
           * Robert's interface -- so that I don't have to keep changing
           * this code if he updates it).  TODO:  We could do this more
           * gracefully later.  But writing out the SMAP to a known
  	 * filename (servlet.class.smap) will also make troubleshooting
           * easier.
  	 */
  
          File outSmap = new File(ctxt.getClassFileName() + ".smap");
  	PrintWriter so = new PrintWriter(
  	    new OutputStreamWriter(new FileOutputStream(outSmap),
  				   SMAP_ENCODING));
  	so.print(g.getString());
  	so.close();
      }
      
      public static void installSmap(JspCompilationContext ctxt) throws IOException {
          File outSmap = new File(ctxt.getClassFileName() + ".smap");
          File outServlet = new File(ctxt.getClassFileName());
          SDEInstaller.install(outServlet, outSmap);
          if( !ctxt.keepGenerated() ) {
              outSmap.delete();
          }
        
      }
  
  
      //*********************************************************************
      // Private utilities
  
      /**
       * Returns an unqualified version of the given file path.
       */
      private static String unqualify(String path) {
  	return path.substring(path.lastIndexOf("/") + 1);
      }
  
      /**
       * Returns a file path corresponding to a potential SMAP input
       * for the given compilation input (JSP file).
       */
      private static String inputSmapPath(String path) {
          return path.substring(0, path.lastIndexOf('.') + 1) + "smap";
      }
  
  
      //*********************************************************************
      // Installation logic (from Robert Field, JSR-045 spec lead)
      private static class SDEInstaller {
  
  	static final boolean verbose = true; 
          static final String nameSDE = "SourceDebugExtension";
  
          byte[] orig;
          byte[] sdeAttr;
          byte[] gen;
  
          int origPos = 0;
          int genPos = 0;
  
          int sdeIndex;
  
          public static void main(String[] args) throws IOException {
              if (args.length == 2) {
                  install(new File(args[0]), new File(args[1]));
              } else if (args.length == 3) {
                  install(new File(args[0]), new File(args[1]), new File(args[2]));
              } else {
                  abort("Usage: <command> <input class file> " + 
                                     "<attribute file> <output class file name>\n" +
                        "<command> <input/output class file> <attribute file>");
              }
          }
  
          static void install(File inClassFile, File attrFile, File outClassFile)
                                                                  throws IOException {
              new SDEInstaller(inClassFile, attrFile, outClassFile);
          }
  
          static void install(File inOutClassFile, File attrFile) throws IOException {
              File tmpFile = new File(inOutClassFile.getPath() + "tmp");
              new SDEInstaller(inOutClassFile, attrFile, tmpFile);
              if (!inOutClassFile.delete()) {
                  throw new IOException("inOutClassFile.delete() failed");
              }
              if (!tmpFile.renameTo(inOutClassFile)) {
                  throw new IOException("tmpFile.renameTo(inOutClassFile) failed");
              }
          }
  
          static void abort(String msg) {
              System.err.println(msg);
              System.exit(1);
          }
  
          SDEInstaller(File inClassFile, File attrFile, File outClassFile) throws IOException {
              if (!inClassFile.exists()) {
                  abort("no such file: " + inClassFile);
              }
              if (!attrFile.exists()) {
                  abort("no such file: " + attrFile);
              }
   
              // get the bytes
              orig = readWhole(inClassFile);
              sdeAttr = readWhole(attrFile);
              gen = new byte[orig.length + sdeAttr.length + 100];
      
              // do it
              addSDE();
          
              // write result
              FileOutputStream outStream = new FileOutputStream(outClassFile);
              outStream.write(gen, 0, genPos);
              outStream.close();
          }
  
          static byte[] readWhole(File input) throws IOException {
              FileInputStream inStream = new FileInputStream(input);
              int len = (int)input.length();
              byte[] bytes = new byte[len];
              if (inStream.read(bytes, 0, len) != len) {
                  abort("expected size: " + len);
              }
              inStream.close();
              return bytes;
          }
  
          void addSDE() throws UnsupportedEncodingException {
              int i;
              copy(4 + 2 + 2); // magic min/maj version
              int constantPoolCountPos = genPos;
              int constantPoolCount = readU2();
              writeU2(constantPoolCount);
              // copy old constant pool return index of SDE symbol, if found
              sdeIndex = copyConstantPool(constantPoolCount);
              if (sdeIndex < 0) {
                  // if "SourceDebugExtension" symbol not there add it
                  writeUtf8ForSDE();
                  
                  // increment the countantPoolCount
                  sdeIndex = constantPoolCount;
                  ++constantPoolCount;
                  randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
              
                  if (verbose) {
                      System.out.println("SourceDebugExtension not found, installed at: " +
                                         sdeIndex);
                  }
              } else {
                  if (verbose) {
                      System.out.println("SourceDebugExtension found at: " +
                                         sdeIndex);
                  }
              }
              copy(2 + 2 + 2);  // access, this, super
              int interfaceCount = readU2();
              writeU2(interfaceCount);
              if (verbose) {
                  System.out.println("interfaceCount: " + interfaceCount);
              }
              copy(interfaceCount * 2);
              copyMembers(); // fields
              copyMembers(); // methods
              int attrCountPos = genPos;
              int attrCount = readU2();
              writeU2(attrCount);
              if (verbose) {
                  System.out.println("class attrCount: " + attrCount);
              }
              // copy the class attributes, return true if SDE attr found (not copied)
              if (!copyAttrs(attrCount)) {
                  // we will be adding SDE and it isn't already counted
                  ++attrCount;
                  randomAccessWriteU2(attrCountPos, attrCount);
                  if (verbose) {
                      System.out.println("class attrCount incremented");
                  }
              }
              writeAttrForSDE(sdeIndex);
          }
  
          void copyMembers() {
              int count = readU2();
              writeU2(count);
              if (verbose) {
                  System.out.println("members count: " + count);
              }
              for (int i = 0; i < count; ++i) {
                  copy(6); // access, name, descriptor
                  int attrCount = readU2();
                  writeU2(attrCount);
                  if (verbose) {
                      System.out.println("member attr count: " + attrCount);
                  }
                  copyAttrs(attrCount);
              }
          }
  
          boolean copyAttrs(int attrCount) {
              boolean sdeFound = false;
              for (int i = 0; i < attrCount; ++i) {
                  int nameIndex = readU2();
                  // don't write old SDE
                  if (nameIndex == sdeIndex) {
                      sdeFound = true;
                      if (verbose) {
                          System.out.println("SDE attr found");
                      }
                  } else {
                      writeU2(nameIndex);  // name
                      int len = readU4();
                      writeU4(len);
                      copy(len);
                      if (verbose) {
                          System.out.println("attr len: " + len);
                      }
                  }
              }
              return sdeFound;
          }
  
          void writeAttrForSDE(int index) {
              writeU2(index);
              writeU4(sdeAttr.length);
              for (int i = 0; i < sdeAttr.length; ++i) {
                  writeU1(sdeAttr[i]);
              }
          }
  
          void randomAccessWriteU2(int pos, int val) {
              int savePos = genPos;
              genPos = pos;
              writeU2(val);
              genPos = savePos;
          }
  
          int readU1() {
              return ((int)orig[origPos++]) & 0xFF;
          }
  
          int readU2() {
              int res = readU1();
              return (res << 8) + readU1();
         }
       
          int readU4() {
              int res = readU2();
              return (res << 16) + readU2();
          }
  
          void writeU1(int val) {
              gen[genPos++] = (byte)val;
          }
  
          void writeU2(int val) {
              writeU1(val >> 8);
              writeU1(val & 0xFF);
          }
  
          void writeU4(int val) {
              writeU2(val >> 16);
              writeU2(val & 0xFFFF);
          }
      
          void copy(int count) {
              for (int i = 0; i < count; ++i) {
                  gen[genPos++] = orig[origPos++];
              }
          }
      
          byte[] readBytes(int count) {
              byte[] bytes = new byte[count];
              for (int i = 0; i < count; ++i) {
                  bytes[i] = orig[origPos++];
              }
              return bytes;
          }
      
          void writeBytes(byte[] bytes) {
              for (int i = 0; i < bytes.length; ++i) {
                  gen[genPos++] = bytes[i];
              }
          }
      
          int copyConstantPool(int constantPoolCount) throws UnsupportedEncodingException {
              int sdeIndex = -1;
              // copy const pool index zero not in class file
              for (int i = 1; i < constantPoolCount; ++i) {
                  int tag = readU1();
                  writeU1(tag);
                  switch (tag) {
                      case 7:  // Class
                      case 8:  // String
                          copy(2); 
                          break;
                      case 9:  // Field
                      case 10: // Method
                      case 11: // InterfaceMethod
                      case 3:  // Integer
                      case 4:  // Float
                      case 12: // NameAndType
                          copy(4); 
                          break;
                      case 5:  // Long
                      case 6:  // Double
                          copy(8); 
                          break;
                      case 1:  // Utf8
                          int len = readU2(); 
                          writeU2(len);
                          byte[] utf8 = readBytes(len);
                          String str = new String(utf8, "UTF-8");
                          if (verbose) {
                              System.out.println(i + " read class attr -- '" + str + "'");
                          }
                          if (str.equals(nameSDE)) {
                              sdeIndex = i;
                          }
                          writeBytes(utf8);
                          break;
                      default: 
                          abort("unexpected tag: " + tag); 
                          break;
                  }
              }
              return sdeIndex;
          }
  
          void writeUtf8ForSDE() {
              int len = nameSDE.length();
              writeU1(1); // Utf8 tag
              writeU2(len);
              for (int i = 0; i < len; ++i) {
                  writeU1(nameSDE.charAt(i));
              }
          }
      }
      public static void evaluateNodes(Node.Nodes nodes, SmapStratum s) {
        if( nodes != null && nodes.size()>0) {
          int numChildNodes = nodes.size();
          for( int i = 0; i < numChildNodes; i++ ) {
            Node n = nodes.getNode( i );
            Mark mark = n.getStart();
            System.out.println("Mark(start): line="+ mark.getLineNumber() + " col="+mark.getColumnNumber() +"Node: begLine="+n.getBeginJavaLine() +" endLine="+n.getEndJavaLine());
            String unqualifiedName = unqualify(mark.getFile());
            s.addFile(unqualifiedName);
            s.addLineData(mark.getLineNumber(),
                          unqualifiedName,
                          1,
                          n.getBeginJavaLine(),
                          n.getEndJavaLine() - n.getBeginJavaLine());
            evaluateNodes(nodes.getNode(i).getBody(), s);
  
  /*
  int inputStartLine,
  String inputFileName,
  int inputLineCount,
  int outputStartLine,
  int outputLineIncrement
  */
          }
        }
      }
  
  }
  
  
  
  1.10      +19 -2     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/resources/messages.properties
  
  Index: messages.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/resources/messages.properties,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- messages.properties	8 Jul 2002 17:28:58 -0000	1.9
  +++ messages.properties	16 Jul 2002 19:30:52 -0000	1.10
  @@ -73,6 +73,7 @@
   jsp.error.unable.rename=Unable to rename class file {0} to {1}
   jsp.error.mandatory.attribute={0}: Mandatory attribute {1} missing
   jsp.engine.info=Jasper JSP 1.1 Engine
  +jsp.error.invalid.expression="{0}" contains invalid expression(s): {1}
   jsp.error.invalid.attribute={0}: Invalid attribute, {1}
   jsp.error.usebean.class.notfound=Class: {0} not found
   jsp.error.file.cannot.read=Cannot read file: {0}
  @@ -125,7 +126,6 @@
   jsp.warning.unknown.element.in.TLD=Warning: Unknown element {0} in TLD
   jsp.warning.unknown.element.in.tag=Warning: Unknown element {0} in tag
   jsp.warning.unknown.element.in.attribute=Warning: Unknown element {0} in attribute
  -jsp.error.xml.taglib.uri.not_prefixed=The relative URI \"{0}\" must be prefixed by \"urn:jsptld:\"
   jsp.error.more.than.one.taglib=More than one taglib in the TLD: {0}
   jsp.warning.teiclass.is.null=Could not load TagExtraInfo class {0}: {1}
   jsp.warning.teiclass.is.nonnull=variable subelement defined in tld when TagExtraInfo class {0} is non-null
  @@ -144,6 +144,7 @@
   jsp.message.accepted=Accepted {0} at {1}
   jsp.message.adding_jar=Adding jar {0} to my classpath
   jsp.message.compiling_with=Compiling with: {0}
  +jsp.message.template_text=template text
   jsp.error.missing_attribute=According to the TLD attribute {0} is mandatory for tag {1}
   jsp.error.bad_attribute=Attribute {0} invalid according to the specified TLD
   jsp.error.tld_not_found=Could not locate TLD {0}
  @@ -230,6 +231,7 @@
   jsp.error.parse.xml.line=XML parsing error on file {0}: (line {1}, col {2}): {3}
   jsp.error.internal.tldinit=Exception initializing TldLocationsCache: {0}
   jsp.error.internal.filenotfound=Internal Error: File {0} not found
  +jsp.error.internal.evaluator_not_found=Internal error: unable to load expression evaluator
   jsp.error.parse.xml.invalidPublicId=Invalid PUBLIC ID: {0}
   jsp.error.include.flush.invalid.value=Invalid value for the flush attribute: {0}
   jsp.error.page.invalid.pageencoding=Page directive: invalid value for pageEncoding
  @@ -250,3 +252,18 @@
   jsp.error.single.line.number=\n\nAn error occurred at line: {0} in the jsp file: {1}\n\n
   jsp.error.multiple.line.number=\n\nAn error occurred between lines: {0} and {1} in the jsp file: {2}\n\n
   jsp.error.corresponding.servlet=Generated servlet error:\n
  +
  +jsp.error.empty.body.not.allowed=Empty body not allowed for {0}
  +jsp.error.jspbody.body.not.allowed.with.value=Body not allowed for jsp:body if any attributes (such as value) are specified.
  +
  +jsp.error.named.attribute.not.rt=Cannot use jsp:attribute to specify value for non-request-time-attribute {0}.
  +
  +jsp.error.jspbody.required=Must use jsp:body to specify tag body for {0} if jsp:attribute is used.
  +
  +jsp.error.jspbody.emptybody.only=The {0} tag can only have jsp:attribute in its body.
  +
  +jsp.error.no.scriptlets=Scripting elements ( <%@, <%!, <%=, <% ) are disallowed here.
  +
  +jsp.error.internal.unexpected_node_type=Internal Error: Unexpected node type encountered
  +
  +jsp.error.tld.fn.invalid.signature=Invalid syntax for function signature in TLD.  Tag Library: {0}, Function: {1}
  
  
  
  1.2       +29 -3     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java
  
  Index: JspRuntimeLibrary.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- JspRuntimeLibrary.java	28 Mar 2002 18:46:19 -0000	1.1
  +++ JspRuntimeLibrary.java	16 Jul 2002 19:30:52 -0000	1.2
  @@ -94,6 +94,8 @@
   import org.apache.jasper.JasperException;
   import org.apache.jasper.Constants;
   
  +// for JSTL expression interpreter
  +import javax.servlet.jsp.PageContext;
   
   /**
    * Bunch of util methods that are used by code generated for useBean,
  @@ -105,6 +107,7 @@
    * works so well right now. It got forgotten at some point. -akv
    *
    * @author Mandar Raje
  + * @author Shawn Bayern
    */
   public class JspRuntimeLibrary {
   
  @@ -522,6 +525,29 @@
           return value;
       }
       // __end lookupReadMethodMethod
  +
  +    // handles <jsp:setProperty> with EL expression for 'value' attribute
  +    public static void handleSetPropertyExpression(Object bean,
  +						   String prop,
  +						   String expression,
  +						   PageContext pageContext,
  +                                                   java.util.Map fnMap )
  +	throws JasperException
  +    {
  +	try {
  +            Method method = getWriteMethod(bean.getClass(), prop);
  +	    method.invoke(bean, new Object[] { 
  +		ExpressionEvaluatorManager.evaluate(
  +		    expression,
  +		    method.getParameterTypes()[0],
  +		    pageContext,
  +                    fnMap,
  +                    "null" )
  +	    });
  +	} catch (Exception ex) {
  +	    throw new JasperException(ex);
  +	}
  +    }
   
       public static void handleSetProperty(Object bean, String prop,
   					 Object value)
  
  
  
  1.12      +87 -3     jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java
  
  Index: PageContextImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- PageContextImpl.java	20 Jun 2002 09:36:10 -0000	1.11
  +++ PageContextImpl.java	16 Jul 2002 19:30:52 -0000	1.12
  @@ -64,9 +64,11 @@
   import java.io.IOException;
   import java.io.InputStreamReader;
   
  +import java.util.EmptyStackException;
   import java.util.Enumeration;
   import java.util.Hashtable;
   import java.util.NoSuchElementException;
  +import java.util.Stack;
   
   import javax.servlet.Servlet;
   import javax.servlet.ServletConfig;
  @@ -85,6 +87,7 @@
   import javax.servlet.jsp.JspWriter;
   import javax.servlet.jsp.tagext.BodyContent;
   import javax.servlet.jsp.JspException;
  +import javax.servlet.jsp.el.ExpressionEvaluator;
   
   import org.apache.jasper.Constants;
   import org.apache.jasper.logging.Logger;
  @@ -96,6 +99,7 @@
    * @author Larry Cable
    * @author Hans Bergsten
    * @author Pierre Delisle
  + * @author Mark Roth
    */
   public class PageContextImpl extends PageContext {
   
  @@ -444,6 +448,7 @@
               outs = newOuts;
           }
           out = outs[depth];
  +	outs[depth].clearBody();
           return outs[depth];
       }
   
  @@ -457,6 +462,82 @@
           return out;
       }
   
  +    /**
  +     * Pops the page scope from the stack. After calling this method, the
  +     * PageScope will appear the same as it was before the last call to
  +     * pushPageScope.
  +     *
  +     * @return A Map representing the state of the page scope just before
  +     *     it was popped.  This object can be passed to pushPageScope to
  +     *     restore this state.  The keys of the returned Map are Strings
  +     *     representing attribute names.  The values are the values of
  +     *     those attributes.
  +     */
  +    public java.util.Map popPageScope()
  +        throws EmptyStackException
  +    {
  +        if( this.attributesStack == null ) {
  +            throw new EmptyStackException();
  +        }
  +        // Remember pop() may throw EmptyStackException.
  +        this.attributes = (Hashtable)this.attributesStack.pop();
  +        return this.attributes;
  +    }
  +
  +    /**
  +     * Pushes a new page scope on the stack.
  +     *
  +     * @param scopeState If null, a new, empty, page scope is pushed.
  +     *     Otherwise, the state of the page scope is restored to the
  +     *     contents of the provided Map.
  +     */
  +    public void pushPageScope( java.util.Map scopeState ) {
  +        // Lazily create page scope stack
  +        if( this.attributesStack == null ) {
  +            this.attributesStack = new Stack();
  +        }
  +
  +        // Push the old page scope on the stack:
  +        this.attributesStack.push( this.attributes );
  +
  +        // Set the new page scope, depending on the input.
  +        if( scopeState == null ) {
  +            // Create a fresh page scope:
  +            this.attributes = new Hashtable( 16 );
  +        }
  +        else if( scopeState instanceof Hashtable ) {
  +            // Compatible Map.
  +            this.attributes = (Hashtable)scopeState;
  +        }
  +        else {
  +            // Incompatible Map.  Only Maps returned by popPageScope()
  +            // or peekPageScope() can be passed in to this method.
  +            // Therefore, the scopeState MUST be an instance of Hashtable.
  +            throw new IllegalArgumentException(
  +                "Attempt to pass PageContext.pushPageScope() a Map " +
  +                "that was not created by this container." );
  +        }
  +    }
  +
  +    public java.util.Map peekPageScope() {
  +        return this.attributes;
  +    }
  +
  +    /**
  +     * Provides programmatic access to the ExpressionEvaluator.
  +     * The JSP Container must return a valid instance of an
  +     * ExpressionEvaluator that can parse EL expressions.
  +     */
  +    public ExpressionEvaluator getExpressionEvaluator() {
  +        try {
  +            return ExpressionEvaluatorManager.getEvaluatorByName( 
  +                ExpressionEvaluatorManager.EVALUATOR_CLASS );
  +        }
  +        catch( JspException e ) {
  +            throw new RuntimeException( e );
  +        }
  +    }
  +
       public void handlePageException(Exception ex)
           throws IOException, ServletException 
       {
  @@ -529,6 +610,9 @@
   
       protected transient Hashtable	attributes = new Hashtable(16);
   
  +    // Page scope attribute stack, to implement {push|pop|peek}PageScope:
  +    // Lazily initialized.
  +    protected transient Stack           attributesStack = null;
       // per request state
   
       protected transient ServletRequest	request;
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorImpl.java
  
  Index: ExpressionEvaluatorImpl.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  
  package org.apache.jasper.runtime;
  
  import java.util.Map;
  import javax.servlet.jsp.JspContext;
  import javax.servlet.jsp.JspException;
  import javax.servlet.jsp.PageContext;
  import javax.servlet.jsp.el.ExpressionEvaluator;
  import org.apache.taglibs.standard.lang.jstl.Evaluator;
  
  /**
   * <p>An adapter for the JSTL Expression Evaluator.</p>
   * 
   * <p>Encapsulates and delegates to the JSTL evaluator, until the
   * JSTL evaluator APIs are up to date with JSP 2.0.</p>
   *
   * @author Mark Roth
   */
  
  public class ExpressionEvaluatorImpl 
      implements ExpressionEvaluator
  {
      private Evaluator delegate;
  
      /**
       * Create a new expression evaluator that delegates to the 
       * given evaluator.
       */
      public ExpressionEvaluatorImpl() {
          this.delegate = new Evaluator();
      }
  
      /**
       * @see javax.servlet.jsp.el.ExpressionEvaluator#validate
       */
      public String validate( String expression ) {
          return delegate.validate( "", expression );
      }
      
      /**
       * @see javax.servlet.jsp.el.ExpressionEvaluator#evaluate
       */
      public Object evaluate( String expression, 
                              Class expectedType, 
                              JspContext jspContext,
                              Map elFunctions,
                              String defaultPrefix ) 
         throws JspException
      {
          // XXX - Assume PageContext for now, until JSTL APIs are updated.
          // change back to JspContext later.
          return delegate.evaluate( "", expression, expectedType, null,
              (PageContext)jspContext, elFunctions, defaultPrefix );
      }
  }
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorManager.java
  
  Index: ExpressionEvaluatorManager.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  
  package org.apache.jasper.runtime;
  
  import java.util.*;
  import javax.servlet.*;
  import javax.servlet.jsp.*;
  import javax.servlet.jsp.el.*;
  import javax.servlet.jsp.tagext.*;
  
  /**
   * <p>A conduit to the JSTL EL.</p>
   * 
   * <p>This is a stripped-down version of the expression evaluator manager
   * found in the JSTL implementation.</p>
   *
   * @author Shawn Bayern
   * @author Mark Roth
   */
  public class ExpressionEvaluatorManager { 
  
      //*********************************************************************
      // Constants
  
      public static final String EVALUATOR_CLASS =
          "org.apache.jasper.runtime.ExpressionEvaluatorImpl";
  
      //*********************************************************************
      // Internal, static state
  
      private static HashMap nameMap = new HashMap();
  
      //*********************************************************************
      // Public static methods
  
      /** 
       * Invokes the evaluate() method on the "active" ExpressionEvaluator
       * for the given pageContext.
       */ 
      public static Object evaluate( String expression, 
                                     Class expectedType, 
                                     JspContext jspContext,
                                     Map elFunctions,
                                     String defaultPrefix ) 
             throws JspException
      {
  
          // the evaluator we'll use
          ExpressionEvaluator target = getEvaluatorByName(EVALUATOR_CLASS);
  
          // delegate the call
          return (target.evaluate(
              expression, expectedType, jspContext, elFunctions, defaultPrefix));
      }
  
      /**
       * Gets an ExpressionEvaluator from the cache, or seeds the cache
       * if we haven't seen a particular ExpressionEvaluator before.
       */
      public static synchronized
  	    ExpressionEvaluator getEvaluatorByName(String name)
              throws JspException {
          try {
  
              Object oEvaluator = nameMap.get(name);
              if (oEvaluator == null) {
                  ExpressionEvaluator e = (ExpressionEvaluator)
                      Class.forName(name).newInstance();
                  nameMap.put(name, e);
                  return (e);
              } else
                  return ((ExpressionEvaluator) oEvaluator);
  
          } catch (ClassCastException ex) {
              // just to display a better error message
              throw new JspException("invalid ExpressionEvaluator: " +
                  ex.getMessage(), ex);
          } catch (ClassNotFoundException ex) {
              throw new JspException("couldn't find ExpressionEvaluator: " +
                  ex.getMessage(), ex);
          } catch (IllegalAccessException ex) {
              throw new JspException("couldn't access ExpressionEvaluator: " +
                  ex.getMessage(), ex);
          } catch (InstantiationException ex) {
              throw new JspException(
                  "couldn't instantiate ExpressionEvaluator: " +
                  ex.getMessage(), ex);
          }
      }
  
  } 
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java
  
  Index: JspFragmentHelper.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java,v 1.1 2002/07/16 19:30:52 kinman Exp $
   * $Revision: 1.1 $
   * $Date: 2002/07/16 19:30:52 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.jasper.runtime;
  
  import javax.servlet.jsp.JspContext;
  import javax.servlet.jsp.PageContext;
  
  import javax.servlet.jsp.tagext.JspFragment;
  
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  
  /**
   * Helper class from which all Jsp Fragment helper classes extend.
   * This class allows for the emulation of numerous fragments within
   * a single class, which in turn reduces the load on the class loader
   * since there are potentially many JspFragments in a single page.
   * <p>
   * The class also provides various utility methods for JspFragment
   * implementations.
   *
   * @author Mark Roth
   */
  public abstract class JspFragmentHelper 
      implements JspFragment 
  {
      
      protected int discriminator;
      protected JspContext jspContext;
      protected PageContext pageContext;
      protected Object parentTag;
      protected Map originalPageScope;
  
      public JspFragmentHelper( int discriminator, JspContext jspContext, 
          Object parentTag ) 
      {
          this.discriminator = discriminator;
          this.jspContext = jspContext;
          this.pageContext = null;
          if( jspContext instanceof PageContext ) {
              pageContext = (PageContext)jspContext;
          }
          this.parentTag = parentTag;
          this.originalPageScope = jspContext.peekPageScope();
      }
      
      public JspContext getJspContext() {
          return this.jspContext;
      }
      
      public Object getParentTag() {
          return this.parentTag;
      }
      
      /**
       * Takes a snapshot of the current JspContext and stores
       * the results in a Map for later restoration.  Also sets the
       * new values in the page context, given the provided parameters.
       *
       * @param params the parameters to set in the page scope
       * @return A map that contains a snapshot of the old page scope.
       */
      protected Map preparePageScope( Map params ) {
          Map originalValues = new HashMap();
          Iterator keys = params.keySet().iterator();
          while( keys.hasNext() ) {
              String key = (String)keys.next();
              // Remember original values to restore later
              originalValues.put( key, originalPageScope.get( key ) );
              // Set new values, based on params
              jspContext.setAttribute( key, params.get( key ) );
          }
          return originalValues;
      }
      
      /**
       * Restores the state of the page scope in the current page context,
       * from the given map.
       *
       * @param originalValues the values to restore in the page context.
       */
      protected void restorePageScope( Map originalValues ) {
          Iterator keys = originalValues.keySet().iterator();
          while( keys.hasNext() ) {
              String key = (String)keys.next();
              Object value = originalValues.get( key );
              if( value == null ) {
                  // Value to be cleared:
                  jspContext.removeAttribute( key );
              }
              else {
                  // Value to be restored:
                  jspContext.setAttribute( key, value );
              }
          }
      }
      
  }
  
  
  
  1.4       +3 -2      jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/xmlparser/ParserUtils.java
  
  Index: ParserUtils.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/xmlparser/ParserUtils.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ParserUtils.java	20 Jun 2002 22:31:23 -0000	1.3
  +++ ParserUtils.java	16 Jul 2002 19:30:53 -0000	1.4
  @@ -119,7 +119,8 @@
        */
       static EntityResolver entityResolver = new MyEntityResolver();
   
  -    public static boolean validating=true;
  +    // Turn off for JSP 2.0 until switch over to using xschema.
  +    public static boolean validating=false;
   
       // --------------------------------------------------------- Public Methods
   
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>