Author: remy.maucherat(a)jboss.com
Date: 2014-03-17 10:36:42 -0400 (Mon, 17 Mar 2014)
New Revision: 2390
Modified:
branches/7.4.x/src/main/java/org/apache/jasper/compiler/Parser.java
branches/7.4.x/webapps/docs/sysprops.xml
Log:
Add submitted org.apache.jasper.compiler.Parser.OPTIMIZE_SCRIPTLETS system property.
Modified: branches/7.4.x/src/main/java/org/apache/jasper/compiler/Parser.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/jasper/compiler/Parser.java 2014-03-13
16:21:30 UTC (rev 2389)
+++ branches/7.4.x/src/main/java/org/apache/jasper/compiler/Parser.java 2014-03-17
14:36:42 UTC (rev 2390)
@@ -23,6 +23,7 @@
import java.net.URL;
import java.util.Iterator;
import java.util.List;
+import java.util.regex.Pattern;
import javax.servlet.jsp.tagext.TagAttributeInfo;
import javax.servlet.jsp.tagext.TagFileInfo;
@@ -38,7 +39,7 @@
* This class implements a parser for a JSP page (non-xml view). JSP page
* grammar is included here for reference. The token '#' that appears in the
* production indicates the current input token location in the production.
- *
+ *
* @author Kin-man Chung
* @author Shawn Bayern
* @author Mark Roth
@@ -76,6 +77,10 @@
private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT =
"JAVAX_BODY_CONTENT_TEMPLATE_TEXT";
+ private static final boolean OPTIMIZE_SCRIPTLETS = Boolean.valueOf(
+
System.getProperty("org.apache.jasper.compiler.Parser.OPTIMIZE_SCRIPTLETS",
+ "false")).booleanValue();
+
/**
* The constructor
*/
@@ -96,7 +101,7 @@
/**
* The main entry for Parser
- *
+ *
* @param pc
* The ParseController, use for getting other objects in compiler
* and for parsing included pages
@@ -244,12 +249,12 @@
String ret = null;
try {
char quote = watch.charAt(watch.length() - 1);
-
+
// If watch is longer than 1 character this is a scripting
// expression and EL is always ignored
boolean isElIgnored =
pageInfo.isELIgnored() || watch.length() > 1;
-
+
ret = AttributeParser.getUnquoted(reader.getText(start, stop),
quote, isElIgnored,
pageInfo.isDeferredSyntaxAllowedAsLiteral());
@@ -649,10 +654,61 @@
err.jspError(start, MESSAGES.unterminatedTag("<%="));
}
- new Node.Expression(parseScriptText(reader.getText(start, stop)),
- start, parent);
+ String expression = reader.getText(start, stop);
+ // check for string concatenation inside expressions, separating from expression
allows for optimizations later on
+ if(!OPTIMIZE_SCRIPTLETS){
+ new Node.Expression(parseScriptText(expression),
+ start, parent);
+ }
+ else {
+ if (!matchesConcat(expression)) {
+ new Node.Expression(parseScriptText(expression),
+ start, parent);
+ } else {
+ //need to separate expressions being concatenated
+ expression = expression.replaceAll("\\+\\s*\"", "\\+
\"").replaceAll("\"\\s*\\+", "\" \\+");
+ String[] tokens =
expression.split("((?=\\+\\s\")|(?<=\"\\s\\+))");
+ if (tokens.length > 1) {
+ for (String token : tokens) {
+ if (matchesStringLiteral(token) &&
!matchesStringParam(token)) {
+ //maybe evaluate the expression here before storing as text
node?
+ new Node.TemplateText(cleanTextToken(token),
+ start, parent);
+ } else {
+ new Node.Expression(parseScriptText(cleanExprToken(token)),
+ start, parent);
+ }
+ }
+ } else {
+ //only have one token, therefore there is no string concatenation
occurring and string literal is being used as part of expression
+ new Node.Expression(parseScriptText(tokens[0]),
+ start, parent);
+
+ }
+ }
+ }
}
+ private boolean matchesStringLiteral(String token) {
+ return Pattern.compile("\"").matcher(token).find() ||
"".equals(token.trim());
+ }
+
+ private boolean matchesStringParam(String token) {
+ return
Pattern.compile("\"\\s*\\)|\\(\\s*\"").matcher(token).find();
+ }
+
+ private boolean matchesConcat(String token) {
+ return
Pattern.compile("\\+\\s*\"|\"\\s*\\+").matcher(token).find();
+ }
+
+ private String cleanTextToken(String token) {
+ return
cleanExprToken(token.trim().replaceAll("(?<!\\\\)\"|\t|\n|\r",
"").replaceAll("\\\\\"","\""));
+ }
+
+ private String cleanExprToken(String token) {
+ return token.trim().replaceAll("^\\+|\\+$","").trim();
+ }
+
/*
* XMLExpressionBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char*
'<'))
* CDSect?)* ETag ) | <TRANSLATION_ERROR>
@@ -961,7 +1017,7 @@
/**
* Attempts to parse 'JspAttributeAndBody' production. Returns true if it
* matched, or false if not. Assumes EmptyBody is okay as well.
- *
+ *
* JspAttributeAndBody ::= ( '>' # S? ( '<jsp:attribute'
NamedAttributes )? '<jsp:body' (
* JspBodyBody | <TRANSLATION_ERROR> ) S? ETag )
*/
Modified: branches/7.4.x/webapps/docs/sysprops.xml
===================================================================
--- branches/7.4.x/webapps/docs/sysprops.xml 2014-03-13 16:21:30 UTC (rev 2389)
+++ branches/7.4.x/webapps/docs/sysprops.xml 2014-03-17 14:36:42 UTC (rev 2390)
@@ -230,13 +230,18 @@
<code>true</code> will be used.</p>
</property>
- <property
name="org.apache.jasper.Constants.DEFAULT_TAG_BUFFER_SIZE">
- <p>Any tag buffer that expands beyond
- <code>org.apache.jasper.Constants.DEFAULT_TAG_BUFFER_SIZE</code> will
be
- destroyed and a new buffer created of the default size. If not specified,
- the default value of <code>512</code> will be used.</p>
+ <property
name="org.apache.jasper.compiler.Parser.STRICT_QUOTE_ESCAPING">
+ <p>If <code>false</code> the requirements for escpaing quotes in
JSP
+ attributes will be relaxed so that a missing required quote will not
+ cause an error. If not specified, the specification compliant default of
+ <code>true</code> will be used.</p>
</property>
+ <property
name="org.apache.jasper.compiler.Parser.OPTIMIZE_SCRIPTLETS">
+ <p>Optimize JSP scriptlets. If not specified,
+ the default value of <code>false</code> will be used.</p>
+ </property>
+
<property name="org.apache.jasper.runtime.JspFactoryImpl.USE_POOL">
<p>If <code>true</code>, a ThreadLocal
<code>PageContext</code> pool will
be used. If not specified, the default value of <code>true</code> will
be