Author: markt Date: Mon Oct 27 06:17:35 2008 New Revision: 708165 URL: http://svn.apache.org/viewvc?rev=708165&view=rev Log: Various EL parsing fixes. Note: the behaviour regarding un-escaping of EL in attributes has been confirmed as correct with the EG.
Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/Constants.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Compiler.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/JspDocumentParser.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Parser.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ParserController.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/Constants.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/Constants.java?rev=708165&r1=708164&r2=708165&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/Constants.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/Constants.java Mon Oct 27 06:17:35 2008 @@ -183,8 +183,12 @@ /** * A replacement char for "\$". * XXX This is a hack to avoid changing EL interpreter to recognize "\$" + * @deprecated */ public static final char ESC = '\u001b'; + /** + * @deprecated + */ public static final String ESCStr = "'\\u001b'"; /** Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Compiler.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Compiler.java?rev=708165&r1=708164&r2=708165&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Compiler.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Compiler.java Mon Oct 27 06:17:35 2008 @@ -145,8 +145,28 @@ ServletWriter writer = null; try { + /* + * The setting of isELIgnored changes the behaviour of the parser + * in subtle ways. To add to the 'fun', isELIgnored can be set in + * any file that forms part of the translation unit so setting it + * in a file included towards the end of the translation unit can + * change how the parser should have behaved when parsing content + * up to the point where isELIgnored was set. Arghh! + * Previous attempts to hack around this have only provided partial + * solutions. We now use two passes to parse the translation unit. + * The first just parses the directives and the second parses the + * whole translation unit once we know how isELIgnored has been set. + * TODO There are some possible optimisations of this process. + */ // Parse the file ParserController parserCtl = new ParserController(ctxt, this); + + // Pass 1 - the directives + Node.Nodes directives = + parserCtl.parseDirectives(ctxt.getJspFile()); + Validator.validateDirectives(this, directives); + + // Pass 2 - the whole translation unit pageNodes = parserCtl.parse(ctxt.getJspFile()); if (ctxt.isPrototypeMode()) { @@ -158,8 +178,9 @@ return null; } - // Validate and process attributes - Validator.validate(this, pageNodes); + // Validate and process attributes - don't re-validate the + // directives we validated in pass 1 + Validator.validateExDirectives(this, pageNodes); if (log.isDebugEnabled()) { t2 = System.currentTimeMillis(); Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java?rev=708165&r1=708164&r2=708165&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java Mon Oct 27 06:17:35 2008 @@ -806,13 +806,8 @@ } return v; } else if (attr.isELInterpreterInput()) { - boolean replaceESC = v.indexOf(Constants.ESC) > 0; - v = JspUtil.interpreterCall(this.isTagFile, v, expectedType, - attr.getEL().getMapName(), false); - // XXX ESC replacement hack - if (replaceESC) { - v = "(" + v + ").replace(" + Constants.ESCStr + ", '$')"; - } + v = attributeValueWithEL(this.isTagFile, v, expectedType, + attr.getEL().getMapName()); if (encode) { return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(" + v + ", request.getCharacterEncoding())"; @@ -829,6 +824,68 @@ } } + + /* + * When interpreting the EL attribute value, literals outside the EL + * must not be unescaped but the EL processor will unescape them. + * Therefore, make sure only the EL expressions are processed by the EL + * processor. + */ + private String attributeValueWithEL(boolean isTag, String tx, + Class<?> expectedType, String mapName) { + if (tx==null) return null; + int size = tx.length(); + StringBuffer output = new StringBuffer(size); + boolean el = false; + int i = 0; + int mark = 0; + char ch; + + while(i < size){ + ch = tx.charAt(i); + + // Start of an EL expression + if (!el && i+1 < size && ch == '$' && tx.charAt(i+1)=='{') { + if (mark < i) { + if (output.length() > 0) { + output.append(" + "); + } + output.append(quote(tx.substring(mark, i))); + } + mark = i; + el = true; + i += 2; + } else if (ch=='\\' && i+1 < size && + (tx.charAt(i+1)=='$' || tx.charAt(i+1)=='}')) { + // Skip an escaped $ or } + i += 2; + } else if (el && ch=='}') { + // End of an EL expression + if (output.length() > 0) { + output.append(" + "); + } + output.append( + JspUtil.interpreterCall(isTag, + tx.substring(mark, i+1), expectedType, + mapName, false)); + mark = i + 1; + el = false; + ++i; + } else { + // Nothing to see here - move to next character + ++i; + } + } + if (!el && mark < i) { + if (output.length() > 0) { + output.append(" + "); + } + output.append(quote(tx.substring(mark, i))); + } + return output.toString(); + } + + /** * Prints the attribute value specified in the param action, in the form * of name=value string. @@ -2838,16 +2895,10 @@ attrValue = sb.toString(); } else { // run attrValue through the expression interpreter - boolean replaceESC = attrValue.indexOf(Constants.ESC) > 0; String mapName = (attr.getEL() != null) ? attr.getEL() .getMapName() : null; - attrValue = JspUtil.interpreterCall(this.isTagFile, - attrValue, c[0], mapName, false); - // XXX hack: Replace ESC with '$' - if (replaceESC) { - attrValue = "(" + attrValue + ").replace(" - + Constants.ESCStr + ", '$')"; - } + attrValue = attributeValueWithEL(this.isTagFile, + attrValue, c[0], mapName); } } else { attrValue = convertString(c[0], attrValue, localName, Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/JspDocumentParser.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/JspDocumentParser.java?rev=708165&r1=708164&r2=708165&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/JspDocumentParser.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/JspDocumentParser.java Mon Oct 27 06:17:35 2008 @@ -580,6 +580,9 @@ lastCh = ch; } } else if (lastCh == '\\' && (ch == '$' || ch == '#')) { + if (pageInfo.isELIgnored()) { + ttext.write('\\'); + } ttext.write(ch); ch = 0; // Not start of EL anymore } else { Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Parser.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Parser.java?rev=708165&r1=708164&r2=708165&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Parser.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Parser.java Mon Oct 27 06:17:35 2008 @@ -27,7 +27,6 @@ import javax.servlet.jsp.tagext.TagInfo; import javax.servlet.jsp.tagext.TagLibraryInfo; -import org.apache.jasper.Constants; import org.apache.jasper.JasperException; import org.apache.jasper.JspCompilationContext; import org.xml.sax.Attributes; @@ -286,12 +285,11 @@ } else if (ch == '\\' && i + 1 < size) { ch = tx.charAt(i + 1); if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') { + // \ " and ' are always unescaped regardless of if they are + // inside or outside of an EL expression. JSP.1.6 takes + // precedence over JSP.1.3.10 (confirmed with EG). buf.append(ch); i += 2; - } else if (ch == '$') { - // Replace "\$" with some special char. XXX hack! - buf.append(Constants.ESC); - i += 2; } else { buf.append('\\'); ++i; @@ -1335,11 +1333,8 @@ } char next = (char) reader.peekChar(); // Looking for \% or \$ or \# - // TODO: only recognize \$ or \# if isELIgnored is false, but since - // it can be set in a page directive, it cannot be determined - // here. Argh! (which is the way it should be since we shouldn't - // convolude multiple steps at once and create confusing parsers...) - if (next == '%' || next == '$' || next == '#') { + if (next == '%' || ((next == '$' || next == '#') && + !pageInfo.isELIgnored())) { ch = reader.nextChar(); } } Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ParserController.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ParserController.java?rev=708165&r1=708164&r2=708165&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ParserController.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ParserController.java Mon Oct 27 06:17:35 2008 @@ -104,6 +104,24 @@ } /** + * Parses the directives of a JSP page or tag file. This is invoked by the + * compiler. + * + * @param inFileName The path to the JSP page or tag file to be parsed. + */ + public Node.Nodes parseDirectives(String inFileName) + throws FileNotFoundException, JasperException, IOException { + // If we're parsing a packaged tag file or a resource included by it + // (using an include directive), ctxt.getTagFileJar() returns the + // JAR file from which to read the tag file or included resource, + // respectively. + isTagFile = ctxt.isTagFile(); + directiveOnly = true; + return doParse(inFileName, null, ctxt.getTagFileJarUrl()); + } + + + /** * Processes an include directive with the given path. * * @param inFileName The path to the resource to be included. Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java?rev=708165&r1=708164&r2=708165&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java Mon Oct 27 06:17:35 2008 @@ -1337,7 +1337,6 @@ } } else { - value = value.replace(Constants.ESC, '$'); result = new Node.JspAttribute(tai, qName, uri, localName, value, false, null, dynamic); } @@ -1692,15 +1691,13 @@ } } - public static void validate(Compiler compiler, Node.Nodes page) + public static void validateDirectives(Compiler compiler, Node.Nodes page) throws JasperException { - - /* - * Visit the page/tag directives first, as they are global to the page - * and are position independent. - */ page.visit(new DirectiveVisitor(compiler)); + } + public static void validateExDirectives(Compiler compiler, Node.Nodes page) + throws JasperException { // Determine the default output content type PageInfo pageInfo = compiler.getPageInfo(); String contentType = pageInfo.getContentType(); --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]