Attached is a second patch to move us towards JSP 2.0 PFD
feature-complete.  More to come in the next few days...

Files Removed (PLEASE REMOVE MANUALLY):
-------------
jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorManager.java


Files Changed (IN PATCH)
-------------
jasper2/src/share/org/apache/jasper/Constants.java
jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java
jasper2/src/share/org/apache/jasper/compiler/Generator.java
jasper2/src/share/org/apache/jasper/compiler/JspUtil.java
jasper2/src/share/org/apache/jasper/compiler/Validator.java
jasper2/src/share/org/apache/jasper/resources/messages.properties
jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorImpl.java
jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java
jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java
jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java


Summary of changes:
    - Removed ExpressionEvaluatorManager
    - Changed EL validation to use
      ExpressionEvaluatorManager.parseExpression
    - PageContextImpl now implements VariableResolver
    - Generated Servlet / Tag Handler now implements FunctionMapper
    - Implemented getExpressionEvaluator() API - Current implementation
      is very inefficient with all sorts of nasty wrappers, but will 
      get much better once the EL evaluator moves into its 
      own subproject.
    - Reworked internal interpreter call to be a proprietary call 
      for the time being, until the EL evaluator moves out of JSTL 
      and into its own project.  Relying on the standard machinery 
      would be too inefficient for the common case.
    - Removed prefix map generation, as it's not being used anywhere.
    - For tag files, moved creation of JspContextWrapper
      to setJspContext.  Now stores both the original and the 
      wrapped JspContext.  Still need to make sure scripting vars 
      are synchronized with the original JspContext.

--
Mark Roth, Java Software
JSP 2.0 Specification Co-Lead
Sun Microsystems, Inc.


Index: jasper2/src/share/org/apache/jasper/Constants.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Constants.java,v
retrieving revision 1.6
diff -u -r1.6 Constants.java
--- jasper2/src/share/org/apache/jasper/Constants.java	6 Aug 2002 00:11:36 -0000	1.6
+++ jasper2/src/share/org/apache/jasper/Constants.java	21 Aug 2002 05:04:23 -0000
@@ -219,16 +219,6 @@
         "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 =
Index: jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java,v
retrieving revision 1.3
diff -u -r1.3 ErrorDispatcher.java
--- jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java	24 Jul 2002 19:58:57 -0000	1.3
+++ jasper2/src/share/org/apache/jasper/compiler/ErrorDispatcher.java	21 Aug 2002 05:04:23 -0000
@@ -345,6 +345,24 @@
     String getString(String errCode, String arg1, String arg2) {
 	return getString(errCode, new Object[] {arg1, arg2});
     }
+    
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    String getString(String errCode, String arg1, String arg2, String arg3) {
+	return getString(errCode, new Object[] {arg1, arg2, arg3});
+    }
 
     /*
      * Returns the localized error message corresponding to the given error
Index: jasper2/src/share/org/apache/jasper/compiler/Generator.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java,v
retrieving revision 1.73
diff -u -r1.73 Generator.java
--- jasper2/src/share/org/apache/jasper/compiler/Generator.java	21 Aug 2002 01:53:28 -0000	1.73
+++ jasper2/src/share/org/apache/jasper/compiler/Generator.java	21 Aug 2002 05:04:26 -0000
@@ -389,7 +389,7 @@
         
         // Static data for EL function and prefix maps:
         generateELFunctionMap();
-	generatePrefixMap();
+        generateFunctionMapper();
     }
 
     /**
@@ -458,7 +458,8 @@
 	out.print  (" extends ");
 	out.print  (pageInfo.getExtends());
 	if (!pageInfo.isThreadSafe()) {
-	    out.print("implements SingleThreadModel");
+	    out.print("implements SingleThreadModel, " +
+                "javax.servlet.jsp.el.FunctionMapper");
 	}
 	out.println(" {");
 	out.pushIndent();
@@ -583,86 +584,19 @@
                     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 );
+                    JspUtil.FunctionSignature functionSignature = 
+                        new JspUtil.FunctionSignature( fnSignature, 
+                        tli.getShortName(), err );
+                    out.print( quote( functionSignature.getMethodName() ) );
+                    out.print( ", new Class[] {" );
+                    Class[] args = functionSignature.getParameterTypes();
+                    for( int j = 0; j < args.length; j++ ) {
+                        out.print( args[j].getName() + ".class" );
+                        if( j < (args.length - 1) ) {
+                            out.print( ", " );
                         }
-                        
-                        out.println( "} ) );" );
-                    }
-                    catch( NoSuchElementException e ) {
-                        throw new JasperException( err.getString(
-                            "jsp.error.tld.fn.invalid.signature",
-                            tli.getShortName(), fnName ) );
                     }
+                    out.println( "} ) );" );
                 }
             }
             out.popIndent();
@@ -679,35 +613,24 @@
             out.println();
         }
     }
-
-    /*
-     * Generates prefix map section.
-     * The prefix map is a map with keys containing prefixes and values being
-     * the URI corresponding to that prefix in the taglib machinery.
+    
+    /**
+     * Generates the method needed to implement FunctionMapper
      */
-    private void generatePrefixMap() throws JasperException {
-        Hashtable taglibs = pageInfo.getTagLibraries();
-        Iterator iter = taglibs.keySet().iterator();
-        
-        out.printil("private static java.util.HashMap _jspx_prefix_map = null;");
-	iter = taglibs.keySet().iterator();
-	out.println();
-	out.printil("static {");
-	out.pushIndent();
-	out.printil("_jspx_prefix_map = new java.util.HashMap();");
-	while (iter.hasNext()) {
-	    String key = (String) iter.next();
-	    TagLibraryInfo tli = (TagLibraryInfo) taglibs.get(key);
-	    out.printin("_jspx_prefix_map.put(");
-	    out.print(quote(tli.getPrefixString()));
-	    out.print(", ");
-	    out.print(quote(tli.getURI()));
-	    out.println(");");
-        }
-	out.popIndent();
-	out.printil("}");
+    private void generateFunctionMapper() 
+        throws JasperException 
+    {
+        out.printil( "public java.lang.reflect.Method resolveFunction(" );
+        out.printil( "    String prefix, String localName )" );
+        out.printil( "{" );
+        out.pushIndent();
+        out.printil( "return (java.lang.reflect.Method)_jspx_fnmap.get( " );
+        out.printil( "    prefix + \":\" + localName );" );
+        out.popIndent();
+        out.printil( "}" );
     }
 
+
     /*
      * Generates the constructor.
      * (shared by servlet and tag handler preamble generation)
@@ -814,8 +737,8 @@
             if (attr.isExpression() || attr.isELInterpreterInput()) {
 		if (attr.isELInterpreterInput()) {
 		    v = JspUtil.interpreterCall(this.isTagFile,
-		        attr.getValue(), expectedType, "_jspx_prefix_map",
-			"_jspx_fnmap", defaultPrefix );
+		        attr.getValue(), expectedType, defaultPrefix,
+			"_jspx_fnmap" );
 		}
 		if (encode) {
 		    return "java.net.URLEncoder.encode(" + v + ")";
@@ -893,7 +816,7 @@
                     "out.write("
 		    + JspUtil.interpreterCall(this.isTagFile,
                         "${" + new String(n.getText()) + "}", String.class,
-			"_jspx_prefix_map", "_jspx_fnmap", "null" )
+			null, "_jspx_fnmap" )
                     + ");");
             } else {
                 out.printil("out.write(" +
@@ -1108,7 +1031,11 @@
                     "pageContext.findAttribute(\""  + name + "\"), \""
                     + property + "\", "
                     + quote(value.getValue()) + ", "
-                    + "pageContext, _jspx_prefix_map, _jspx_fnmap);");
+                    + "pageContext, "
+                    + "pageContext, "   // pageContext is a VariableResolver.
+                    + "this );");       // this (either the generated Servlet 
+                                        // or the generated tag handler for 
+                                        // Tag files) is a FunctionMapper.
             } else if( value.isNamedAttribute() ) {
                 // If the value for setProperty was specified via
                 // jsp:attribute, first generate code to evaluate
@@ -2522,8 +2449,7 @@
 		} else if (attrs[i].isELInterpreterInput()) {
                     // run attrValue through the expression interpreter
                     attrValue = JspUtil.interpreterCall(this.isTagFile,
-                        attrValue, c[0], "_jspx_prefix_map", "_jspx_fnmap",
-                        n.getPrefix() );
+                        attrValue, c[0], n.getPrefix(), "_jspx_fnmap" );
                 } else {
 		    attrValue = convertString(
                                 c[0], attrValue, attrName,
@@ -2904,7 +2830,8 @@
 	out.print(tagInfo.getTagName());
 	out.print(" extends javax.servlet.jsp.tagext.SimpleTagSupport");
 	if (tagInfo.hasDynamicAttributes())
-	    out.print(" implements javax.servlet.jsp.tagext.DynamicAttributes");
+	    out.print(" implements javax.servlet.jsp.tagext.DynamicAttributes, " +
+                "javax.servlet.jsp.el.FunctionMapper" );
 	out.println(" {");
 	out.println();
 	out.pushIndent();
@@ -2928,9 +2855,9 @@
         genPreambleMethods();
         
         // Now the doTag() method
-	out.printil("public void doTag() throws javax.servlet.jsp.JspException {");
+	out.printil("public void doTag() throws javax.servlet.jsp.JspException, java.io.IOException {");
 	out.pushIndent();
-	out.printil("PageContext pageContext = new JspContextWrapper(getJspContext());");
+	out.printil("PageContext pageContext = (PageContext)getJspContext();");
         
         // Declare implicit objects.  
         // XXX - Note that the current JSP 2.0 PFD 
@@ -2972,16 +2899,15 @@
 
     private void generateTagHandlerPostamble() {
         out.popIndent();
-        //out.printil("} catch (java.io.IOException ioe) {");
-	//out.pushIndent();
-	//out.printil("throw new javax.servlet.jsp.JspException(ioe);");
-	//out.popIndent();
-	//out.printil("}");
         
         // Have to catch Throwable because a classic tag handler
         // helper method is declared to throw Throwable.
         out.printil( "} catch( Throwable t ) {" );
         out.pushIndent();
+        out.printil( "if( t instanceof java.io.IOException )" );
+        out.printil( "    throw (java.io.IOException)t;" );
+        out.printil( "if( t instanceof javax.servlet.jsp.JspException )" );
+        out.printil( "    throw (javax.servlet.jsp.JspException)t;" );
         out.printil("throw new javax.servlet.jsp.JspException(t);" );
         out.popIndent();
         out.printil( "}" );
@@ -3063,6 +2989,26 @@
 		out.println();
 	    }
 	}
+        
+        // Define setter for JspContext so we can create a wrapper and
+        // store both the original and the wrapper.  We need the wrapper
+        // to maask the page context from the tag file and simulate a 
+        // fresh page context.  We need the original to do things like
+        // sync AT_BEGIN and AT_END scripting variables.
+        out.printil( "protected JspContext jspContext;" );
+        out.println();
+        out.printil( "public void setJspContext( JspContext ctx ) {" );
+        out.pushIndent();
+        out.printil( "super.setJspContext( ctx );" );
+        out.printil( "this.jspContext = new JspContextWrapper( ctx );" );
+        out.popIndent();
+        out.printil( "}" );
+        out.println();
+        out.printil( "public JspContext getJspContext() {" );
+        out.pushIndent();
+        out.printil( "return this.jspContext;" );
+        out.popIndent();
+        out.printil( "}" );
     }
 
     /*
Index: jasper2/src/share/org/apache/jasper/compiler/JspUtil.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java,v
retrieving revision 1.11
diff -u -r1.11 JspUtil.java
--- jasper2/src/share/org/apache/jasper/compiler/JspUtil.java	19 Aug 2002 16:54:16 -0000	1.11
+++ jasper2/src/share/org/apache/jasper/compiler/JspUtil.java	21 Aug 2002 05:04:30 -0000
@@ -66,9 +66,12 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.NoSuchElementException;
 import java.util.Vector;
-import java.util.Enumeration;
+import java.util.StringTokenizer;
 
 import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
@@ -88,7 +91,10 @@
 
 // EL interpreter (subject to change)
 import javax.servlet.jsp.el.ExpressionEvaluator;
-import org.apache.jasper.runtime.ExpressionEvaluatorManager;
+import javax.servlet.jsp.el.FunctionMapper;
+import javax.servlet.jsp.el.ELException;
+import javax.servlet.jsp.el.ELParseException;
+import org.apache.jasper.runtime.ExpressionEvaluatorImpl;
 
 /** 
  * This class has all the utility method(s).
@@ -112,6 +118,8 @@
     private static ErrorHandler errorHandler = new MyErrorHandler();
     private static EntityResolver entityResolver = new MyEntityResolver();
     private static int tempSequenceNumber = 0;
+    private static ExpressionEvaluatorImpl expressionEvaluator = 
+        new ExpressionEvaluatorImpl( null );
 
     public static char[] removeQuotes(char []chars) {
 	CharArrayWriter caw = new CharArrayWriter();
@@ -537,16 +545,15 @@
      * 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"
+     * @param fnmapvar Variable pointing to a function map.
      * @return a String representing a call to the EL interpreter.
      */
     public static String interpreterCall(boolean isTagFile,
 					 String expression,
                                          Class expectedType,
-					 String prefixMap,
-                                         String fnMap,
-                                         String defaultPrefix) 
+                                         String defaultPrefix,
+                                         String fnmapvar ) 
     {
         /*
          * Determine which context object to use.
@@ -595,16 +602,27 @@
  	/*
          * Build up the base call to the interpreter.
          */
+        // XXX - We use a proprietary call to the interpreter for now
+        // as the current standard machinery is inefficient and requires
+        // lots of wrappers and adapters.  This should all clear up once
+        // the EL interpreter moves out of JSTL and into its own project.
+        // In the future, this should be replaced by code that calls
+        // ExpressionEvaluator.parseExpression() and then cache the resulting
+        // expression objects.  The interpreterCall would simply select
+        // one of the pre-cached expressions and evaluate it.
+        // Note that PageContextImpl implements VariableResolver and
+        // the generated Servlet/SimpleTag implements FunctionMapper, so
+        // that machinery is already in place (mroth).
  	StringBuffer call = new StringBuffer(
              "(" + targetType + ") "
-               + Constants.EL_INTERPRETER_CONDUIT_CLASS + "."
-               + Constants.EL_INTERPRETER_CONDUIT_METHOD
+               + "org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate"
                + "(" + Generator.quote(expression) + ", "
                +       targetType + ".class, "
-	       +       jspCtxt + ", "
-               +       prefixMap + ", "
-               +       fnMap + ", "
-               +       Generator.quote( defaultPrefix ) + ")");
+	       +       "(PageContext)" + jspCtxt 
+               +       ", " + fnmapvar + ", "
+               +       ((defaultPrefix == null) ? 
+                            "null" : Generator.quote( defaultPrefix )) 
+               + ")");
  
  	/*
          * Add the primitive converter method if we need to.
@@ -625,23 +643,24 @@
      */
     public static void validateExpressions(Mark where,
                                            String expressions,
+                                           Class expectedType,
+                                           FunctionMapper functionMapper,
+                                           String defaultPrefix,
                                            ErrorDispatcher err)
             throws JasperException {
-        ExpressionEvaluator el = null;
+        // Just parse and check if any exceptions are thrown.
         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 = null; // XXX EL
-        // String errMsg = el.validate(expressions);
-        if (errMsg != null)
+            JspUtil.expressionEvaluator.parseExpression( expressions, 
+                expectedType, functionMapper, defaultPrefix );
+        }
+        catch( ELParseException e ) {
+            err.jspError(where, "jsp.error.invalid.expression", expressions,
+                e.toString() );
+        }
+        catch( ELException e ) {
             err.jspError(where, "jsp.error.invalid.expression", expressions,
-                errMsg);
+                e.toString() );
+        }
     }
 
     /**
@@ -659,6 +678,123 @@
     public static String nextTemporaryVariableName() {
         return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
     }
+    
+    /**
+     * Parses and encapsulates a function signature, as would appear in
+     * a TLD.
+     */
+    public static class FunctionSignature {
+        private String returnType;
+        private String methodName;
+        private Class[] parameterTypes;
+        
+        /**
+         * Parses a function signature, as would appear in the TLD
+         *
+         * @param signature The signature to parse
+         * @param tagName Name of tag, for error messages.
+         * @throws JasperException If an error occurred while parsing the 
+         *     signature.
+         */
+        public FunctionSignature( String signature, String tagName,
+            ErrorDispatcher err )
+            throws JasperException
+        {
+            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( 
+                    signature, ws + "(),", true);
+
+                // Return type:
+                this.returnType = sigTokenizer.nextToken();
+
+                // Skip whitespace and read <method-name>:
+                do {
+                    this.methodName = sigTokenizer.nextToken();
+                } while( ws.indexOf( this.methodName ) != -1 );
+
+                // 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.parenexpected",
+                        tagName, this.methodName ) );
+                }
+
+                // ( <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( ")" ) ) {
+                    ArrayList parameterTypes = new ArrayList();
+                    do {
+                        if( ",(".indexOf( argType ) != -1 ) {
+                            throw new JasperException( err.getString(
+                                "jsp.error.tld.fn.invalid.signature",
+                                tagName, this.methodName ) );
+                        }
+
+                        parameterTypes.add( Class.forName( argType ) );
+
+                        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.commaexpected",
+                                tagName, this.methodName ) );
+                        }
+
+                        // <arg-type>
+                        do {
+                            argType = sigTokenizer.nextToken();
+                        } while( ws.indexOf( argType ) != -1 );
+                    } while( true );
+                    this.parameterTypes = (Class[])parameterTypes.toArray( 
+                        new Class[parameterTypes.size()] );
+                }
+            }
+            catch( NoSuchElementException e ) {
+                throw new JasperException( err.getString(
+                    "jsp.error.tld.fn.invalid.signature",
+                    tagName, this.methodName ) );
+            }
+            catch( ClassNotFoundException e ) {
+                throw new JasperException( err.getString(
+                    "jsp.error.tld.fn.invalid.signature.classnotfound",
+                    e.getMessage(), tagName, this.methodName ) );
+            }
+        }
+        
+        public String getReturnType() {
+            return this.returnType;
+        }
+        
+        public String getMethodName() {
+            return this.methodName;
+        }
+        
+        public Class[] getParameterTypes() {
+            return this.parameterTypes;
+        }
+    }
+    
 }
 
 
Index: jasper2/src/share/org/apache/jasper/compiler/Validator.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java,v
retrieving revision 1.23
diff -u -r1.23 Validator.java
--- jasper2/src/share/org/apache/jasper/compiler/Validator.java	20 Aug 2002 03:52:18 -0000	1.23
+++ jasper2/src/share/org/apache/jasper/compiler/Validator.java	21 Aug 2002 05:04:30 -0000
@@ -60,15 +60,21 @@
  */ 
 package org.apache.jasper.compiler;
 
+import java.lang.reflect.Method;
+
 import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.Enumeration;
 
+import javax.servlet.jsp.el.FunctionMapper;
+
+import javax.servlet.jsp.tagext.FunctionInfo;
 import javax.servlet.jsp.tagext.PageData;
+import javax.servlet.jsp.tagext.JspFragment;
 import javax.servlet.jsp.tagext.TagData;
 import javax.servlet.jsp.tagext.TagInfo;
 import javax.servlet.jsp.tagext.TagAttributeInfo;
 import javax.servlet.jsp.tagext.TagLibraryInfo;
-import javax.servlet.jsp.tagext.TagLibraryInfo;
 import javax.servlet.jsp.tagext.ValidationMessage;
 
 import org.apache.jasper.Constants;
@@ -297,6 +303,11 @@
 
 	private PageInfo pageInfo;
 	private ErrorDispatcher err;
+        
+        /**
+         * A FunctionMapper, used to validate EL expressions.
+         */
+        private FunctionMapper functionMapper;
 
 	private static final JspUtil.ValidAttribute[] jspRootAttrs = {
 	    new JspUtil.ValidAttribute("version", true) };
@@ -376,6 +387,8 @@
 	ValidateVisitor(Compiler compiler) {
 	    this.pageInfo = compiler.getPageInfo();
 	    this.err = compiler.getErrorDispatcher();
+            this.functionMapper = new ValidatorFunctionMapper( this.pageInfo, 
+                this.err );
 	}
 
 	public void visit(Node.JspRoot n) throws JasperException {
@@ -405,6 +418,7 @@
                                     paramActionAttrs, err);
 	    n.setValue(getJspAttribute("value", null, null,
 				       n.getAttributeValue("value"),
+                                       java.lang.String.class, null,
 				       n, false));
             visitBody(n);
 	}
@@ -413,7 +427,8 @@
             JspUtil.checkAttributes("Include action", n,
                                     includeActionAttrs, err);
 	    n.setPage(getJspAttribute("page", null, null,
-				      n.getAttributeValue("page"), n, false));
+				      n.getAttributeValue("page"), 
+                                      java.lang.String.class, null, n, false));
 	    visitBody(n);
         };
 
@@ -421,7 +436,8 @@
             JspUtil.checkAttributes("Forward", n,
                                     forwardActionAttrs, err);
 	    n.setPage(getJspAttribute("page", null, null,
-				      n.getAttributeValue("page"), n, false));
+				      n.getAttributeValue("page"), 
+                                      java.lang.String.class, null, n, false));
 	    visitBody(n);
 	}
 
@@ -438,7 +454,8 @@
 	    String param = n.getAttributeValue("param");
 	    String value = n.getAttributeValue("value");
 
-            n.setValue(getJspAttribute("value", null, null, value, n, false));
+            n.setValue(getJspAttribute("value", null, null, value, 
+                java.lang.Object.class, null, n, false));
 
             boolean valueSpecified = n.getValue() != null;
 
@@ -475,7 +492,7 @@
 	    Node.JspAttribute jattr
 		= getJspAttribute("beanName", null, null,
 				  n.getAttributeValue("beanName"),
-				  n, false);
+				  java.lang.String.class, null, n, false);
 	    n.setBeanName(jattr);
 	    if (className != null && jattr != null)
 		err.jspError(n, "jsp.error.useBean.notBoth");
@@ -510,20 +527,16 @@
             
 	    Node.JspAttribute width
 		= getJspAttribute("width", null, null,
-				  n.getAttributeValue("width"), n, false);
+				  n.getAttributeValue("width"), 
+                                  java.lang.String.class, null, n, false);
 	    n.setWidth( width );
             
 	    Node.JspAttribute height
 		= getJspAttribute("height", null, null,
-				  n.getAttributeValue("height"), n, false);
+				  n.getAttributeValue("height"), 
+                                  java.lang.String.class, null, n, false);
 	    n.setHeight( height );
 
-	    n.setHeight(getJspAttribute("height", null, null,
-					n.getAttributeValue("height"), n,
-					false));
-	    n.setWidth(getJspAttribute("width", null, null,
-				       n.getAttributeValue("width"), n,
-				       false));
 	    visitBody(n);
 	}
 
@@ -534,11 +547,19 @@
 	}
         
 	public void visit(Node.JspBody n) throws JasperException {
-	    JspUtil.checkAttributes("Body", n,
+	    String defaultPrefix = null;
+            JspUtil.checkAttributes("Body", n,
 				    bodyAttrs, err);
+            Node parent = n.getParent();
+            if( parent instanceof Node.CustomTag ) {
+                // Default prefix comes from parent custom tag's prefix.
+                Node.CustomTag customTag = (Node.CustomTag)parent;
+                defaultPrefix = customTag.getPrefix();
+            }
 	    n.setValue(getJspAttribute("value", null, null,
-				       n.getAttributeValue("value"), n,
-				       false));
+				       n.getAttributeValue("value"), 
+                                       JspFragment.class, defaultPrefix, 
+                                       n, false));
             visitBody(n);
 	}
         
@@ -562,8 +583,14 @@
 
 	public void visit(Node.ELExpression n) throws JasperException {
             if ( pageInfo.isELEnabled() ) {
-                JspUtil.validateExpressions(n.getStart(),
-                    "${" + new String(n.getText()) + "}", err);
+                JspUtil.validateExpressions(
+                    n.getStart(),
+                    "${" + new String(n.getText()) + "}", 
+                    java.lang.String.class, // XXX - Should template text 
+                                            // always evaluate to String?
+                    this.functionMapper,
+                    null,
+                    err);
             }
         }
 
@@ -613,13 +640,31 @@
 		for (int j=0; j<tldAttrs.length; j++) {
 		    if (attrs.getQName(i).equals(tldAttrs[j].getName())) {
 			if (tldAttrs[j].canBeRequestTime()) {
-			    jspAttrs[i]
-				= getJspAttribute(attrs.getQName(i),
-						  attrs.getURI(i),
-						  attrs.getLocalName(i),
-						  attrs.getValue(i),
-						  n,
-						  false);
+                            Class expectedType;
+                            try {
+                                if( tldAttrs[j].isFragment() ) {
+                                    expectedType = JspFragment.class;
+                                }
+                                else {
+                                    expectedType = Class.forName(
+                                        tldAttrs[j].getTypeName() );
+                                }
+                                jspAttrs[i]
+                                    = getJspAttribute(attrs.getQName(i),
+                                                      attrs.getURI(i),
+                                                      attrs.getLocalName(i),
+                                                      attrs.getValue(i),
+                                                      expectedType,
+                                                      n.getPrefix(),
+                                                      n,
+                                                      false);
+                            }
+                            catch( ClassNotFoundException e ) {
+                                err.jspError(n, 
+                                    "jsp.error.unknown_attribute_type",
+                                    tldAttrs[j].getName(), 
+                                    tldAttrs[j].getTypeName() );
+                            }
 			} else {
 			    jspAttrs[i]
 				= new Node.JspAttribute(attrs.getQName(i),
@@ -647,7 +692,9 @@
 						      attrs.getURI(i),
 						      attrs.getLocalName(i),
 						      attrs.getValue(i),
-						      n,
+						      java.lang.Object.class,
+                                                      n.getPrefix(),
+                                                      n,
 						      true);
 		    } else {
 			err.jspError(n, "jsp.error.bad_attribute",
@@ -666,9 +713,26 @@
 		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, null,
-						  null, n, false);
+                            Class expectedType;
+                            try {
+                                if( tldAttrs[j].isFragment() ) {
+                                    expectedType = JspFragment.class;
+                                }
+                                else {
+                                    expectedType = Class.forName(
+                                        tldAttrs[j].getTypeName() );
+                                }
+                                jspAttrs[attrs.getLength() + i]
+                                    = getJspAttribute(na.getName(), null, null,
+                                                      null, expectedType, 
+                                                      n.getPrefix(), n, false);
+                            }
+                            catch( ClassNotFoundException e ) {
+                                err.jspError(n, 
+                                    "jsp.error.unknown_attribute_type",
+                                    tldAttrs[j].getName(), 
+                                    tldAttrs[j].getTypeName() );
+                            }
 			} else {
                             err.jspError( n, 
                                 "jsp.error.named.attribute.not.rt",
@@ -684,7 +748,8 @@
 		    if (tagInfo.hasDynamicAttributes()) {
 			jspAttrs[attrs.getLength() + i]
 			    = getJspAttribute(na.getName(), null, null, null,
-					      n, true);
+					      java.lang.Object.class, 
+                                              n.getPrefix(), n, true);
 		    } else {
 			err.jspError(n, "jsp.error.bad_attribute",
 				     na.getName());
@@ -711,6 +776,8 @@
 						  String uri,
 						  String localName,
 						  String value,
+                                                  Class expectedType,
+                                                  String defaultPrefix,
                                                   Node n,
 						  boolean dynamic)
                 throws JasperException {
@@ -743,19 +810,22 @@
 					dynamic);
                 }
                 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
+                    // The attribute can contain expressions but is not a
+                    // scriptlet expression; 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 && pageInfo.isELEnabled()) {
-                        JspUtil.validateExpressions(n.getStart(), value, err);
+                        JspUtil.validateExpressions(
+                            n.getStart(),
+                            value, 
+                            expectedType, 
+                            this.functionMapper,
+                            defaultPrefix,
+                            this.err);
+                        
                         result = new Node.JspAttribute(qName, uri, localName,
 						       value, false, true,
 						       dynamic);
@@ -939,6 +1009,94 @@
 	if (errMsg != null) {
             errDisp.jspError(errMsg.toString());
 	}
+    }
+   
+    /**
+     * A Function Mapper to be used by the validator to help in parsing
+     * EL Expressions.  
+     */
+    private static class ValidatorFunctionMapper 
+        implements FunctionMapper
+    {
+        private PageInfo pageInfo;
+        private ErrorDispatcher err;
+        
+        /**
+         * HashMap of cached functions that we already looked up.
+         * Key = prefix, value = HashMap, where key = localName,
+         * value = Method.
+         */
+        private HashMap cachedFunctions = new HashMap();
+        
+        public ValidatorFunctionMapper( PageInfo pageInfo, 
+            ErrorDispatcher err ) 
+        {
+            this.pageInfo = pageInfo;
+            this.err = err;
+        }
+        
+        public Method resolveFunction( String prefix, String localName ) {
+            boolean cached = false;
+            Method result = null;
+            
+            // Look up entry in cache:
+            HashMap cachedMethods = (HashMap)this.cachedFunctions.get( prefix );
+            if( cachedMethods != null ) {
+                if( cachedMethods.containsKey( localName ) ) {
+                    result = (Method)cachedMethods.get( localName );
+                    cached = true;
+                }
+            }
+            
+            // If not cached, look it up:
+            if( !cached ) {
+                Hashtable taglibraries = pageInfo.getTagLibraries();
+                TagLibraryInfo info = 
+                    (TagLibraryInfo)taglibraries.get( prefix );
+                if( info != null ) {
+                    FunctionInfo fnInfo = 
+                        (FunctionInfo)info.getFunction( localName );
+                    if( fnInfo != null ) {
+                        String clazz = fnInfo.getFunctionClass();
+                        try {
+                            JspUtil.FunctionSignature functionSignature = 
+                                new JspUtil.FunctionSignature( 
+                                fnInfo.getFunctionSignature(),
+                                info.getShortName(), this.err );
+                            Class c = Class.forName( clazz );
+                            result = c.getDeclaredMethod( 
+                                functionSignature.getMethodName(),
+                                functionSignature.getParameterTypes() );
+                        }
+                        catch( JasperException e ) {
+                            // return null.
+                            // XXX - If the EL API evolves to allow for
+                            // an exception to be thrown, we should provide
+                            // details here.
+                        }
+                        catch( ClassNotFoundException e ) {
+                            // return null.
+                            // XXX - See above comment regarding detailed
+                            // error reporting.
+                        }
+                        catch( NoSuchMethodException e ) {
+                            // return null.
+                            // XXX - See above comment regarding detailed
+                            // error reporting.
+                        }
+                    }
+                }
+                
+                // Store result in cache:
+                if( cachedMethods == null ) {
+                    cachedMethods = new HashMap();
+                    this.cachedFunctions.put( prefix, cachedMethods );
+                }
+                cachedMethods.put( localName, result );
+            }
+            
+            return result;
+        }
     }
 }
 
Index: jasper2/src/share/org/apache/jasper/resources/messages.properties
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/resources/messages.properties,v
retrieving revision 1.26
diff -u -r1.26 messages.properties
--- jasper2/src/share/org/apache/jasper/resources/messages.properties	20 Aug 2002 23:44:29 -0000	1.26
+++ jasper2/src/share/org/apache/jasper/resources/messages.properties	21 Aug 2002 05:04:30 -0000
@@ -274,6 +274,9 @@
 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}
+jsp.error.tld.fn.invalid.signature.classnotfound=Invalid syntax for function signature in TLD.  Class not found: ${0}.  Tag Library: {1}, Function: {2}.
+jsp.error.tld.fn.invalid.signature.commaexpected=Invalid syntax for function signature in TLD.  Comma ',' expected.  Tag Library: {0}, Function: {1}.
+jsp.error.tld.fn.invalid.signature.parenexpected=Invalid syntax for function signature in TLD.  Parenthesis '(' expected.  Tag Library: {0}, Function: {1}.
 
 jsp.error.dynamic.attributes.not.implemented=The {0} tag declares that it accepts dynamic attributes but does not implement the required interface
 jsp.error.nomatching.fragment=Cannot find an attribute directive (with name={0} and fragment=true) prior to the fragment directive.
@@ -287,3 +290,4 @@
 jsp.error.invoke.varAndVarReader=Both 'var' and 'varReader' specified in jsp:invoke
 jsp.error.doBody.varAndVarReader=Both 'var' and 'varReader' specified in jsp:doBody
 jsp.warning.bad.urlpattern.propertygroup=Bad value {0} in the url-pattern subelement in web.xml
+jsp.error.unknown_attribute_type=Unknown attribute type ({1}) for attribute {0}.
Index: jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorImpl.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorImpl.java,v
retrieving revision 1.3
diff -u -r1.3 ExpressionEvaluatorImpl.java
--- jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorImpl.java	19 Aug 2002 16:54:17 -0000	1.3
+++ jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorImpl.java	21 Aug 2002 05:04:31 -0000
@@ -58,7 +58,7 @@
 import java.util.Map;
 import javax.servlet.jsp.*;
 import javax.servlet.jsp.el.*;
-import org.apache.taglibs.standard.lang.jstl.Evaluator;
+import org.apache.taglibs.standard.lang.jstl.ELEvaluator;
 
 /**
  * <p>An adapter for the JSTL Expression Evaluator.</p>
@@ -66,58 +66,230 @@
  * <p>Encapsulates and delegates to the JSTL evaluator, until the
  * JSTL evaluator APIs are up to date with JSP 2.0.</p>
  *
+ * <p>Note: This is quite a hack at the moment.  This entire class needs to
+ * be rewritten (and indeed may be obsoleted) once the EL interpreter moves
+ * out of JSTL and in to its own project.
+ *
  * @author Mark Roth
  */
 
 public class ExpressionEvaluatorImpl 
     implements ExpressionEvaluator
 {
-    private Evaluator delegate;
+    private PageContextImpl pageContext;
 
     /**
      * 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 );
+    public ExpressionEvaluatorImpl( PageContextImpl pageContext ) {
+        this.pageContext = pageContext;
     }
     
-    /**
-     * @see javax.servlet.jsp.el.ExpressionEvaluator#evaluate
-     */
-    public Object evaluate( String expression, 
-                            Class expectedType, 
-                            JspContext jspContext,
-                            Map prefixMap,
-                            Map functionMap,
-                            String defaultURI ) 
-	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, functionMap, defaultURI );
-    }
-
     public Object evaluate(String expression, 
 			   Class expectedType, 
 			   VariableResolver vResolver,
 			   FunctionMapper fMapper,
-			   String defaultPrefix) throws ELException {
-	return null; // XXX EL
+			   String defaultPrefix) 
+        throws ELException 
+    {
+        org.apache.taglibs.standard.lang.jstl.VariableResolver
+            resolver = new JSTLVariableResolverWrapper( vResolver );
+        Map fMapperMap = new FunctionMapperMap( fMapper );
+        
+        // XXX - This is currently inefficient.  A new evaluator,
+        // JSTLVariableResolverWrapper, and FuntionMapperMap is created for 
+        // each evaluate call.  Things should get better once the JSTL 
+        // implementation is moved out of JSTL into its own project.
+        try {
+            return new ELEvaluator( resolver ).evaluate( 
+                expression, this.pageContext, expectedType, fMapperMap,
+                defaultPrefix );
+        }
+        catch( org.apache.taglibs.standard.lang.jstl.ELException e ) {
+            throw new ELException( e );
+        }
     }
 
     public Expression parseExpression(String expression, 
 				      Class expectedType, 
 				      FunctionMapper fMapper,
-				      String defaultPrefix) throws ELException {
-	return null; // XXX EL
+				      String defaultPrefix) 
+        throws ELException 
+    {
+        // Validate and then create an Expression object.
+        String errorMessage =
+            new org.apache.taglibs.standard.lang.jstl.Evaluator().validate( 
+            "<unknown>", expression );
+        if( errorMessage != null ) {
+            // Failed validation.  Tell user why.
+            throw new ELException( errorMessage );
+        }
+        
+        // Create an Expression object that knows how to evaluate this.
+        return new JSTLExpression( expression, expectedType, fMapper, 
+            defaultPrefix );
+    }
+    
+    /**
+     * Exposes a JSP 2.0 VariableResolver using the JSTL VariableResolver
+     * interface.
+     *
+     * XXX - This class should be removed as soon as the EL implementation
+     * can be moved out of JSTL into its own project.
+     */
+    private static class JSTLVariableResolverWrapper 
+        implements org.apache.taglibs.standard.lang.jstl.VariableResolver
+    {
+        private VariableResolver delegate;
+        
+        public JSTLVariableResolverWrapper( VariableResolver delegate ) {
+            this.delegate = delegate;
+        }
+        
+        public Object resolveVariable( String pName, Object pContext ) 
+            throws org.apache.taglibs.standard.lang.jstl.ELException
+        {
+            // pContext parameter is going away in JSP 2.0
+            Object result;
+            try {
+                result = delegate.resolveVariable( pName, null );
+            }
+            catch( ELException e ) {
+                throw new org.apache.taglibs.standard.lang.jstl.ELException( 
+                    e.getMessage() );
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Exposes a FunctionMapper as a read-only Map.
+     * Keys are Strings of the form 'prefix:localName' and values are of 
+     * the type java.lang.reflect.Method.
+     * Only get() and containsKey() are implemented.  The rest of the 
+     * methods throw UnsupportedOperationException.
+     *
+     * XXX - This class should be removed as soon as the EL implementation
+     * can be moved out of JSTL into its own project.
+     * XXX - Strings are not i18n-ed, mainly because this class is going away
+     * ant the Strings are for internal errors only.
+     */
+    private static class FunctionMapperMap
+        implements Map 
+    {
+        private FunctionMapper delegate;
+        
+        public FunctionMapperMap( FunctionMapper delegate ) {
+            this.delegate = delegate;
+        }
+        
+        public void clear() {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.clear not implemented" );
+        }
+        
+        public boolean containsKey(Object obj) {
+            return get( obj ) != null;
+        }
+        
+        public boolean containsValue(Object obj) {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.containsValue not implemented" );
+        }
+        
+        public java.util.Set entrySet() {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.entrySet not implemented" );
+        }
+        
+        public Object get(Object obj) {
+            String key = (String)obj;
+            int index = key.indexOf( ':' );
+            String prefix = key.substring( 0, index );
+            String localName = key.substring( index + 1 );
+            return delegate.resolveFunction( prefix, localName );
+        }
+        
+        public boolean isEmpty() {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.isEmpty not implemented" );
+        }
+        
+        public java.util.Set keySet() {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.keySet not implemented" );
+        }
+        
+        public Object put(Object obj, Object obj1) {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.put not implemented" );
+        }
+        
+        public void putAll(java.util.Map map) {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.putAll not implemented" );
+        }
+        
+        public Object remove(Object obj) {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.remove not implemented" );
+        }
+        
+        public int size() {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.size not implemented" );
+        }
+        
+        public java.util.Collection values() {
+            throw new UnsupportedOperationException( 
+                "FunctionMapperMap.values not implemented" );
+        }
+    }
+    
+    /**
+     * An object that encapsulates an expression to be evaluated by 
+     * the JSTL evaluator.
+     *
+     * XXX - This class should be removed as soon as the EL implementation
+     * can be moved out of JSTL into its own project.
+     */
+    private class JSTLExpression 
+        implements Expression
+    {
+        private String expression;
+        private Class expectedType;
+        private FunctionMapperMap fMapperMap;
+        private String defaultPrefix;
+        
+        public JSTLExpression(String expression, Class expectedType, 
+            FunctionMapper fMapper, String defaultPrefix)         
+        {
+            this.expression = expression;
+            this.expectedType = expectedType;
+            this.fMapperMap = new FunctionMapperMap( fMapper );
+            this.defaultPrefix = defaultPrefix;
+        }
+        
+        public Object evaluate( VariableResolver vResolver )
+            throws ELException
+        {
+            org.apache.taglibs.standard.lang.jstl.VariableResolver
+                resolver = new JSTLVariableResolverWrapper( vResolver );
+
+            // XXX - This is currently inefficient.  A new evaluator and
+            // JSTLVariableResolverWrapper is created for 
+            // each evaluate call.  Things should get better once the JSTL 
+            // implementation is moved out of JSTL into its own project.
+            try {
+                return new ELEvaluator( resolver ).evaluate( 
+                    this.expression, ExpressionEvaluatorImpl.this.pageContext, 
+                    this.expectedType, this.fMapperMap,
+                    this.defaultPrefix );
+            }
+            catch( org.apache.taglibs.standard.lang.jstl.ELException e ) {
+                throw new ELException( e );
+            }
+        }
     }
 }
Index: jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java,v
retrieving revision 1.3
diff -u -r1.3 JspFragmentHelper.java
--- jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java	12 Aug 2002 17:55:45 -0000	1.3
+++ jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java	21 Aug 2002 05:04:31 -0000
@@ -127,7 +127,13 @@
             // Remember original values to restore later
             originalValues.put( key, jspContext.getAttribute( key ) );
             // Set new values, based on params
-            jspContext.setAttribute( key, params.get( key ) );
+            Object newValue = params.get( key );
+            if( newValue != null ) {
+                jspContext.setAttribute( key, newValue );
+            }
+            else {
+                jspContext.removeAttribute( key );
+            }
         }
         return originalValues;
     }
Index: jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java,v
retrieving revision 1.3
diff -u -r1.3 JspRuntimeLibrary.java
--- jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java	30 Jul 2002 22:41:37 -0000	1.3
+++ jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java	21 Aug 2002 05:04:32 -0000
@@ -89,6 +89,8 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.el.VariableResolver;
+import javax.servlet.jsp.el.FunctionMapper;
 import javax.servlet.jsp.JspWriter;
 import javax.servlet.jsp.tagext.BodyContent;
 
@@ -529,23 +531,19 @@
 
     // handles <jsp:setProperty> with EL expression for 'value' attribute
     public static void handleSetPropertyExpression(Object bean,
-						   String prop,
-						   String expression,
-						   PageContext pageContext,
-						   Map prefixMap,
-                                                   Map fnMap )
+        String prop, String expression, PageContext pageContext,
+        VariableResolver variableResolver, FunctionMapper functionMapper )
 	throws JasperException
     {
 	try {
             Method method = getWriteMethod(bean.getClass(), prop);
 	    method.invoke(bean, new Object[] { 
-		ExpressionEvaluatorManager.evaluate(
+		pageContext.getExpressionEvaluator().evaluate(
 		    expression,
 		    method.getParameterTypes()[0],
-		    pageContext,
-		    prefixMap,
-                    fnMap,
-                    "null" )
+                    variableResolver,
+                    functionMapper,
+                    null )
 	    });
 	} catch (Exception ex) {
 	    throw new JasperException(ex);
Index: jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java
===================================================================
RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java,v
retrieving revision 1.17
diff -u -r1.17 PageContextImpl.java
--- jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java	19 Aug 2002 23:25:42 -0000	1.17
+++ jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java	21 Aug 2002 05:04:32 -0000
@@ -67,6 +67,7 @@
 import java.util.EmptyStackException;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Stack;
 
@@ -87,14 +88,17 @@
 import javax.servlet.jsp.JspWriter;
 import javax.servlet.jsp.tagext.BodyContent;
 import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.el.ELException;
 import javax.servlet.jsp.el.ExpressionEvaluator;
 import javax.servlet.jsp.el.VariableResolver;
 
 import org.apache.jasper.Constants;
 import org.apache.jasper.logging.Logger;
+import org.apache.taglibs.standard.lang.jstl.JSTLVariableResolver;
 
 /**
  * Implementation of the PageContext class from the JSP spec.
+ * Also doubles as a VariableResolver for the EL.
  *
  * @author Anil K. Vijendran
  * @author Larry Cable
@@ -102,9 +106,32 @@
  * @author Pierre Delisle
  * @author Mark Roth
  */
-public class PageContextImpl extends PageContext {
+public class PageContextImpl 
+    extends PageContext 
+    implements VariableResolver
+{
 
     Logger.Helper loghelper = new Logger.Helper("JASPER_LOG", "PageContextImpl");
+    
+    /**
+     * The expression evaluator, for evaluating EL expressions.
+     */
+    private ExpressionEvaluatorImpl expressionEvaluator = null;
+    
+    /**
+     * The variable resolver, for evaluating EL expressions.
+     */
+    private static JSTLVariableResolver variableResolver =
+        new JSTLVariableResolver();
+    
+    /**
+     * Expression evaluator for proprietary EL evaluation.
+     * XXX - This should be going away once the EL evaluator moves from
+     * the JSTL implementation to its own project.
+     */
+    private static org.apache.taglibs.standard.lang.jstl.Evaluator
+        proprietaryEvaluator = new 
+        org.apache.taglibs.standard.lang.jstl.Evaluator();
 
     PageContextImpl(JspFactory factory) {
         this.factory = factory;
@@ -415,7 +442,7 @@
     }
 
     public VariableResolver getVariableResolver() {
-	return null; // XXX
+	return this;
     }
 
     public void forward(String relativeUrlPath)
@@ -482,13 +509,13 @@
      * ExpressionEvaluator that can parse EL expressions.
      */
     public ExpressionEvaluator getExpressionEvaluator() {
-        try {
-            return ExpressionEvaluatorManager.getEvaluatorByName( 
-                ExpressionEvaluatorManager.EVALUATOR_CLASS );
-        }
-        catch( JspException e ) {
-            throw new RuntimeException( e.toString() );
+        if( this.expressionEvaluator == null ) {
+            this.expressionEvaluator = new ExpressionEvaluatorImpl( this );
+            // no need to synchronize - not a big deal even if we create 
+            // two of these.
         }
+        
+        return this.expressionEvaluator;
     }
 
     public void handlePageException(Exception ex)
@@ -530,6 +557,50 @@
 	}
     }
 
+    /**
+     * VariableResolver interface
+     */
+    public Object resolveVariable( String pName, Object pContext ) 
+        throws ELException
+    {
+        // Note: pContext will be going away.
+        try {
+            return PageContextImpl.variableResolver.resolveVariable( 
+                pName, this );
+        }
+        catch( org.apache.taglibs.standard.lang.jstl.ELException e ) {
+            throw new ELException( e );
+        }
+    }
+    
+    /**
+     * Proprietary method to evaluate EL expressions.
+     * XXX - This method should go away once the EL interpreter moves
+     * out of JSTL and into its own project.  For now, this is necessary
+     * because the standard machinery is too slow.
+     *
+     * @param expression The expression to be evaluated
+     * @param expectedType The expected resulting type
+     * @param pageContext The page context
+     * @param functionMap Maps prefix and name to Method
+     * @param defaultPrefix Default prefix for this evaluation
+     * @return The result of the evaluation
+     */
+    public static Object proprietaryEvaluate( String expression, 
+        Class expectedType, PageContext pageContext, Map functionMap, 
+        String defaultPrefix )
+        throws ELException
+    {
+        try {
+            return PageContextImpl.proprietaryEvaluator.evaluate( "<unknown>", 
+                expression, expectedType, null, pageContext, functionMap, 
+                defaultPrefix );
+        }
+        catch( JspException e ) {
+            throw new ELException( e );
+        }
+    }
+
     protected JspWriterImpl _createOut(int bufferSize, boolean autoFlush)
         throws IOException, IllegalArgumentException {
         try {
@@ -539,7 +610,7 @@
             return null;
         }
     }
-
+    
     /*
      * fields
      */

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to