Attached is a patch to move us towards JSP 2.0 PFD feature-complete. More to come in the next few days...
- Implemented the value attribute of jsp:doBody for classic tag handlers. - Stubbed out JspC with getJspConfig() so it compiles. - Added null check for addInclude() to handle the case where there are no preludes or codas. - Now accepts /WEB-INF/tags as well as /WEB-INF/tags/ for tag file default directory. - ParserController now uses path name to determine if the given element is a tag file instead of searching for tag directive. - In a tag file, an attribute directive with a fragment attribute must not allow a rtexprvalue attribute, and must fix its value to "true". Fixed implementation to comply with spec. - Fixed preamble and postamble generator for Tag Files. Was not generating declarations, tag handler pools, methods buffer, helper fragment, etc. Generator now shares code between servlet and tag handler pre and post ambles. - Even though spec is not clear that they're required, added implicit objects to doTag() so they are available in tag files. -- Mark Roth, Java Software JSP 2.0 Specification Co-Lead Sun Microsystems, Inc.
Index: jasper2/src/share/org/apache/jasper/JspC.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/JspC.java,v retrieving revision 1.12 diff -u -r1.12 JspC.java --- jasper2/src/share/org/apache/jasper/JspC.java 26 Jun 2002 16:50:38 -0000 1.12 +++ jasper2/src/share/org/apache/jasper/JspC.java 20 Aug 2002 05:22:50 -0000 @@ -75,6 +75,7 @@ import org.apache.jasper.logging.Logger; import org.apache.jasper.logging.JasperLogger; +import org.apache.jasper.compiler.JspConfig; /** * Shell for the jspc compiler. Handles all options associated with the @@ -912,6 +913,15 @@ Constants.jasperLog.setVerbosityLevel(verbosityLevel); } + + /** + * Obtain JSP configuration informantion specified in web.xml. + */ + public JspConfig getJspConfig() { + // XXX - Stubbed out so Jasper compiles. + initServletContext(); + return new JspConfig( context ); + } } 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.71 diff -u -r1.71 Generator.java --- jasper2/src/share/org/apache/jasper/compiler/Generator.java 20 Aug 2002 01:42:38 -0000 1.71 +++ jasper2/src/share/org/apache/jasper/compiler/Generator.java 20 Aug 2002 05:22:53 -0000 @@ -170,6 +170,7 @@ out.println(); page.visit(new DeclarationVisitor()); + out.println(); } /** @@ -329,23 +330,25 @@ } /** - * Generates the beginning of the static portion of the servelet. + * Generate preamble package name + * (shared by servlet and tag handler preamble generation) */ - private void generatePreamble(Node.Nodes page) throws JasperException { - - String servletPackageName = ctxt.getServletPackageName(); - String servletClassName = ctxt.getServletClassName(); - String serviceMethodName = Constants.SERVICE_METHOD_NAME; - - // First the package name: - - if (! "".equals(servletPackageName) && servletPackageName != null) { - out.printil("package " + servletPackageName + ";"); + private void genPreamblePackage( String packageName ) + throws JasperException + { + if (! "".equals(packageName) && packageName != null) { + out.printil("package " + packageName + ";"); out.println(); } - - // Generate imports - + } + + /** + * Generate preamble imports + * (shared by servlet and tag handler preamble generation) + */ + private void genPreambleImports() + throws JasperException + { Iterator iter = pageInfo.getImports().iterator(); while (iter.hasNext()) { out.printin("import "); @@ -353,31 +356,21 @@ out.println(";"); } out.println(); + } - // Generate class declaration - - out.printin("public class "); - out.print (servletClassName); - out.print (" extends "); - out.print (pageInfo.getExtends()); - if (!pageInfo.isThreadSafe()) { - out.print("implements SingleThreadModel"); - } - out.println(" {"); - out.pushIndent(); - - // Class body begins here - - generateDeclarations(page); - out.println(); - - // Static initializations here - + /** + * Generation of static initializers in preamble. + * For example, include list, el function map, prefix map. + * (shared by servlet and tag handler preamble generation) + */ + private void genPreambleStaticInitializers() + throws JasperException + { // Static data for getIncludes() out.printil("private static java.util.Vector _jspx_includes;"); out.println(); List includes = pageInfo.getIncludes(); - iter = includes.iterator(); + Iterator iter = includes.iterator(); if( !includes.isEmpty() ) { out.printil("static {"); out.pushIndent(); @@ -397,13 +390,16 @@ // Static data for EL function and prefix maps: generateELFunctionMap(); generatePrefixMap(); + } - // Class variable declarations - - /* - * Declare tag handler pools (tags of the same type and with the same - * attribute set share the same tag handler pool) - */ + /** + * Declare tag handler pools (tags of the same type and with the same + * attribute set share the same tag handler pool) + * (shared by servlet and tag handler preamble generation) + */ + private void genPreambleClassVariableDeclarations( String className ) + throws JasperException + { if (ctxt.getOptions().isPoolingEnabled() && !tagHandlerPoolNames.isEmpty()) { for (int i=0; i<tagHandlerPoolNames.size(); i++) { @@ -416,11 +412,17 @@ // Constructor if (ctxt.getOptions().isPoolingEnabled() && !tagHandlerPoolNames.isEmpty()) { - generateServletConstructor(servletClassName); + generateConstructor(className); } - - // Methods here + } + /** + * Declare general-purpose methods + * (shared by servlet and tag handler preamble generation) + */ + private void genPreambleMethods() + throws JasperException + { // Method used to get compile time include file dependencies out.printil("public java.util.List getIncludes() {"); out.pushIndent(); @@ -433,6 +435,45 @@ && !tagHandlerPoolNames.isEmpty()) { generateDestroy(); } + } + + /** + * Generates the beginning of the static portion of the servelet. + */ + private void generatePreamble(Node.Nodes page) throws JasperException { + + String servletPackageName = ctxt.getServletPackageName(); + String servletClassName = ctxt.getServletClassName(); + String serviceMethodName = Constants.SERVICE_METHOD_NAME; + + // First the package name: + genPreamblePackage( servletPackageName ); + + // Generate imports + genPreambleImports(); + + // Generate class declaration + out.printin("public class "); + out.print (servletClassName); + out.print (" extends "); + out.print (pageInfo.getExtends()); + if (!pageInfo.isThreadSafe()) { + out.print("implements SingleThreadModel"); + } + out.println(" {"); + out.pushIndent(); + + // Class body begins here + generateDeclarations(page); + + // Static initializations here + genPreambleStaticInitializers(); + + // Class variable declarations + genPreambleClassVariableDeclarations( servletClassName ); + + // Methods here + genPreambleMethods(); // Now the service method out.printin("public void "); @@ -668,10 +709,11 @@ } /* - * Generates the servlet constructor. + * Generates the constructor. + * (shared by servlet and tag handler preamble generation) */ - private void generateServletConstructor(String servletClassName) { - out.printil("public " + servletClassName + "() {"); + private void generateConstructor(String className) { + out.printil("public " + className + "() {"); out.pushIndent(); for (int i=0; i<tagHandlerPoolNames.size(); i++) { out.printin((String) tagHandlerPoolNames.elementAt(i)); @@ -1699,11 +1741,11 @@ } public void visit(Node.JspBody n) throws JasperException { + Node.JspAttribute value = n.getValue(); if (isSimpleTagHandler) { out.printin(simpleTagHandlerVar); out.print(".setJspBody("); - Node.JspAttribute value = n.getValue(); if (value != null) { out.print(attributeValue(value, false, JspFragment.class, "null" )); @@ -1712,7 +1754,63 @@ } out.println(");"); } else { - visitBody(n); + Node parent = n.getParent(); + if( (parent instanceof Node.CustomTag) && (value != null) ) { + Node.CustomTag customTag = (Node.CustomTag)parent; + + // Classic tag handler invoked with <jsp:body value="..."> + // Generate a tag body that evaluates the given + // fragment. + + // First, generate a Map with all the AT_BEGIN and + // NESTED variables so the body can access them. + VariableInfo[] varInfos = customTag.getVariableInfos(); + TagVariableInfo[] tagVarInfos = + customTag.getTagVariableInfos(); + + String var = JspUtil.nextTemporaryVariableName(); + out.printil( "java.util.HashMap " + var + + " = new java.util.HashMap();" ); + + if( varInfos != null ) { + for( int i = 0; i < varInfos.length; i++ ) { + if( (varInfos[i].getScope() == + VariableInfo.AT_BEGIN) || + (varInfos[i].getScope() == + VariableInfo.NESTED) ) + { + out.printil( var + ".put( \"" + + varInfos[i].getVarName() + "\", " + + varInfos[i].getVarName() + " );" ); + } + } + } + + if( tagVarInfos != null ) { + for( int i = 0; i < tagVarInfos.length; i++ ) { + if( (tagVarInfos[i].getScope() == + VariableInfo.AT_BEGIN) || + (tagVarInfos[i].getScope() == + VariableInfo.NESTED) ) + { + out.printin( var + ".put( \"" ); + String name = tagVarInfos[i].getNameGiven(); + if( name == null ) { + name = customTag.getTagData(). + getAttributeString( + tagVarInfos[i].getNameFromAttribute()); + } + out.println( name + "\", " + name + " );" ); + } + } + } + + out.printil("(" + + attributeValue(value, false, JspFragment.class, + "null" ) + ").invoke( out, " + var + " );" ); + } else { + visitBody(n); + } } } @@ -2675,6 +2773,29 @@ } /** + * Common part of postamble, shared by both servlets and tag files. + */ + private void genCommonPostamble() { + // Append any methods that were generated + out.print(methodsBuffer.toString()); + + // Append the helper class + if( fragmentHelperClass.isUsed() ) { + fragmentHelperClass.generatePostamble(); + out.printMultiLn(fragmentHelperClass.toString()); + } + + // generate class definition for JspxState + if (maxTagNesting > 0) { + generateJspState(); + } + + // Close the class definition + out.popIndent(); + out.printil("}"); + } + + /** * Generates the ending part of the static portion of the servlet. */ private void generatePostamble(Node.Nodes page) { @@ -2703,23 +2824,8 @@ out.popIndent(); out.printil("}"); - // Append any methods that were generated - out.print(methodsBuffer.toString()); - - // Append the helper class - if( fragmentHelperClass.isUsed() ) { - fragmentHelperClass.generatePostamble(); - out.printMultiLn(fragmentHelperClass.toString()); - } - - // generate class definition for JspxState - if (maxTagNesting > 0) { - generateJspState(); - } - - // Close the class definition - out.popIndent(); - out.printil("}"); + // Generated methods, helper classes, etc. + genCommonPostamble(); } /** @@ -2751,17 +2857,17 @@ Generator gen = new Generator(out, compiler); + if (gen.ctxt.getOptions().isPoolingEnabled()) { + gen.compileTagHandlerPoolList(page); + } if (gen.ctxt.isTagFile()) { TagInfo tagInfo = gen.ctxt.getTagInfo(); - gen.generateTagHandlerPreamble(tagInfo); + gen.generateTagHandlerPreamble(tagInfo, page); page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out, gen.methodsBuffer, null, tagInfo)); gen.generateTagHandlerPostamble(); } else { - if (gen.ctxt.getOptions().isPoolingEnabled()) { - gen.compileTagHandlerPoolList(page); - } gen.generatePreamble(page); gen.fragmentHelperClass.generatePreamble(); page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out, @@ -2774,28 +2880,20 @@ /* * XXX */ - private void generateTagHandlerPreamble(TagInfo tagInfo) - throws JasperException { + private void generateTagHandlerPreamble(TagInfo tagInfo, Node.Nodes tag ) + throws JasperException + { // Generate package declaration String className = tagInfo.getTagClassName(); if (className.indexOf('.') != -1) { String pkgName = className.substring(0, className.lastIndexOf(".")); - out.printin("package "); - out.print(pkgName); - out.println(";"); - out.println(); + genPreamblePackage( pkgName ); } // Generate imports - Iterator iter = pageInfo.getImports().iterator(); - while (iter.hasNext()) { - out.printin("import "); - out.print ((String)iter.next()); - out.println(";"); - } - out.println(); + genPreambleImports(); // Generate class declaration out.printin("public class "); @@ -2808,19 +2906,45 @@ out.pushIndent(); // Class body begins here + generateDeclarations(tag); - // Static data for EL function and prefix maps: - generateELFunctionMap(); - generatePrefixMap(); + // Static initializations here + genPreambleStaticInitializers(); + // Class variable declarations + genPreambleClassVariableDeclarations( tagInfo.getTagName() ); + + // Tag-handler specific declarations: generateTagHandlerDeclarations(tagInfo); if (tagInfo.hasDynamicAttributes()) generateSetDynamicAttribute(); + // Methods here + genPreambleMethods(); + + // Now the doTag() method out.printil("public void doTag() throws javax.servlet.jsp.JspException {"); out.pushIndent(); out.printil("PageContext pageContext = new JspContextWrapper(getJspContext());"); + + // Declare implicit objects. + // XXX - Note that the current JSP 2.0 PFD + // spec is unclear about whether these are required + // XXX - Optimization: Check scriptlets and expressions for the + // use of any of these. They're not likely to be used. If they're + // not used, get rid of them. + out.printil( "javax.servlet.ServletRequest request = " + + "pageContext.getRequest();" ); + out.printil( "javax.servlet.ServletResponse response = " + + "pageContext.getResponse();" ); + out.printil( "javax.servlet.http.HttpSession session = " + + "pageContext.getSession();" ); + out.printil( "javax.servlet.ServletContext application = " + + "pageContext.getServletContext();" ); + out.printil("javax.servlet.ServletConfig config = " + + "pageContext.getServletConfig();"); + // Declare parameter map for fragment/body invocation out.printil("java.util.Map _jspx_params = null;"); @@ -2830,21 +2954,40 @@ out.printil("javax.servlet.jsp.JspWriter out = pageContext.getOut();"); generatePageScopedVariables(tagInfo); + + // Number of tag object that need to be popped + // XXX TODO: use a better criteria + maxTagNesting = pageInfo.getMaxTagNesting(); + + declareTemporaryScriptingVars(tag); + out.println(); + out.printil("try {"); out.pushIndent(); } 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("}"); - out.popIndent(); - out.printil("}"); - out.popIndent(); - out.printil("}"); + //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("throw new javax.servlet.jsp.JspException(t);" ); + out.popIndent(); + out.printil( "}" ); + + // Close the doTag method + out.popIndent(); + out.printil("}"); + + // Generated methods, helper classes, etc. + genCommonPostamble(); } /** Index: jasper2/src/share/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java,v retrieving revision 1.6 diff -u -r1.6 ImplicitTagLibraryInfo.java --- jasper2/src/share/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java 19 Aug 2002 23:06:01 -0000 1.6 +++ jasper2/src/share/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java 20 Aug 2002 05:22:53 -0000 @@ -76,7 +76,7 @@ */ public class ImplicitTagLibraryInfo extends TagLibraryInfo { - private static final String WEB_INF_TAGS = "/WEB-INF/tags/"; + private static final String WEB_INF_TAGS = "/WEB-INF/tags"; private static final String TAG_FILE_SUFFIX = ".tag"; private static final String TAGS_SHORTNAME = "tags"; private static final String TLIB_VERSION = "1.0"; @@ -101,7 +101,9 @@ // Determine the value of the <short-name> subelement of the // "imaginary" <taglib> element - if (tagdir.equals(WEB_INF_TAGS)) { + if (tagdir.equals(WEB_INF_TAGS) || + tagdir.equals( WEB_INF_TAGS + "/" ) ) + { shortname = TAGS_SHORTNAME; } else { shortname = tagdir.substring(WEB_INF_TAGS.length()); Index: jasper2/src/share/org/apache/jasper/compiler/Parser.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java,v retrieving revision 1.22 diff -u -r1.22 Parser.java --- jasper2/src/share/org/apache/jasper/compiler/Parser.java 20 Aug 2002 03:52:18 -0000 1.22 +++ jasper2/src/share/org/apache/jasper/compiler/Parser.java 20 Aug 2002 05:22:55 -0000 @@ -372,17 +372,19 @@ * and include-coda of jsp-config element in web.xml */ private void addInclude(Node parent, List files) throws JasperException { - Iterator iter = files.iterator(); - while (iter.hasNext()) { - String file = (String) iter.next(); - AttributesImpl attrs = new AttributesImpl(); - attrs.addAttribute("", "file", "file", "CDATA", file); + if( files != null ) { + Iterator iter = files.iterator(); + while (iter.hasNext()) { + String file = (String) iter.next(); + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "file", "file", "CDATA", file); - // Create a dummy Include directive node - Node includeNode = new Node.IncludeDirective(attrs, reader.mark(), - parent); - processIncludeDirective(file, includeNode); - } + // Create a dummy Include directive node + Node includeNode = new Node.IncludeDirective(attrs, + reader.mark(), parent); + processIncludeDirective(file, includeNode); + } + } } /* Index: jasper2/src/share/org/apache/jasper/compiler/ParserController.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/ParserController.java,v retrieving revision 1.11 diff -u -r1.11 ParserController.java --- jasper2/src/share/org/apache/jasper/compiler/ParserController.java 20 Aug 2002 03:52:18 -0000 1.11 +++ jasper2/src/share/org/apache/jasper/compiler/ParserController.java 20 Aug 2002 05:22:55 -0000 @@ -278,12 +278,15 @@ // FIXME: We assume xml parser will take care of // encoding for page in XML syntax. Correct? if (!isXml) { - jspReader.reset(startMark); - while (jspReader.skipUntil("<%@") != null) { + // Note: this currently assumes there is no XML syntax for tag + // files (as of PFD of the JSP 2.0 spec there is an XML view, + // but no XML syntax). + isTagFile = file.startsWith( "/WEB-INF/tags" ) || + file.startsWith( "/META-INF/tags" ); + jspReader.reset(startMark); + while (jspReader.skipUntil("<%@") != null) { jspReader.skipSpaces(); - boolean tIsTagFile = jspReader.matches("tag "); - if (tIsTagFile || jspReader.matches("page")) { - isTagFile = tIsTagFile; + if (jspReader.matches( "tag " ) || jspReader.matches("page")) { jspReader.skipSpaces(); Attributes attrs = Parser.parseAttributes(this, jspReader); String attribute = "pageEncoding"; Index: jasper2/src/share/org/apache/jasper/compiler/TagFileProcessor.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagFileProcessor.java,v retrieving revision 1.13 diff -u -r1.13 TagFileProcessor.java --- jasper2/src/share/org/apache/jasper/compiler/TagFileProcessor.java 19 Aug 2002 23:06:01 -0000 1.13 +++ jasper2/src/share/org/apache/jasper/compiler/TagFileProcessor.java 20 Aug 2002 05:22:56 -0000 @@ -181,15 +181,23 @@ String attrName = n.getAttributeValue("name"); boolean required = JspUtil.booleanValue( n.getAttributeValue("required")); - boolean rtexprvalue = JspUtil.booleanValue( - n.getAttributeValue("rtexprvalue")); + String rtexprvalueString = n.getAttributeValue("rtexprvalue"); + boolean rtexprvalue = JspUtil.booleanValue( rtexprvalueString ); boolean fragment = JspUtil.booleanValue( n.getAttributeValue("fragment")); String type = n.getAttributeValue("type"); if (fragment) { fragmentAttributesMap.put(attrName, n); + // type is fixed to "JspFragment" and a translation error + // must occur if specified. if (type != null) { err.jspError("jsp.error.fragmentwithtype"); + } + // rtexprvalue is fixed to "true" and a translation error + // must occur if specified. + rtexprvalue = true; + if( rtexprvalueString != null ) { + err.jspError("jsp.error.frgmentwithrtexprvalue" ); } } else { if (type == null) 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.24 diff -u -r1.24 messages.properties --- jasper2/src/share/org/apache/jasper/resources/messages.properties 20 Aug 2002 03:52:18 -0000 1.24 +++ jasper2/src/share/org/apache/jasper/resources/messages.properties 20 Aug 2002 05:22:56 -0000 @@ -281,6 +281,7 @@ jsp.error.attribute.noquote=quote symbol expected jsp.error.attribute.unterminated=attribute for {0} is not properly terminated jsp.error.missing.tagInfo=TagInfo object for {0} is missing from TLD -jsp.error.fragmentwithtype=Both 'fragment' and 'type' attributes specified in tag directive +jsp.error.fragmentwithtype=Cannot specify both 'fragment' and 'type' attributes. If 'fragment' is present, 'type' is fixed as 'javax.servlet.jsp.tagext.JspFragment' +jsp.error.fragmentwithrtexprvalue=Cannot specify both 'fragment' and 'rtexprvalue' attributes. If 'fragment' is present, 'rtexprvalue' is fixed as 'true' jsp.error.fragmentWithDeclareOrScope=Both 'fragment' and 'declare' or 'scope' attributes specified in variable directive jsp.warning.bad.urlpattern.propertygroup=Bad value {0} in the url-pattern subelement in web.xml
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>