Author: byron
Date: Wed Jan 21 08:36:34 2009
New Revision: 736335

URL: http://svn.apache.org/viewvc?rev=736335&view=rev
Log:
VELOCITY-623 Strict escaping behavior implementation

Added:
    
velocity/engine/trunk/src/test/org/apache/velocity/test/StrictEscapeTestCase.java
Modified:
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/ParserTokenManager.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
    velocity/engine/trunk/src/parser/Parser.jjt

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java?rev=736335&r1=736334&r2=736335&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java
 Wed Jan 21 08:36:34 2009
@@ -59,6 +59,11 @@
     String RUNTIME_REFERENCES_STRICT = "runtime.references.strict";
     
     /**
+     * Indicates we are going to use modifed escape behavior in strict mode
+     */
+    String RUNTIME_REFERENCES_STRICT_ESCAPE = 
"runtime.references.strict.escape";
+       
+    /**
      * @deprecated  This appears to have always been meaningless.
      */
     String RUNTIME_LOG_ERROR_STACKTRACE = "runtime.log.error.stacktrace";

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java?rev=736335&r1=736334&r2=736335&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java 
(original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java 
Wed Jan 21 08:36:34 2009
@@ -11,6 +11,7 @@
 import org.apache.velocity.runtime.directive.MacroParseException;
 import org.apache.velocity.util.StringUtils;
 import org.apache.commons.lang.text.StrBuilder;
+import org.apache.velocity.runtime.RuntimeConstants;
 
 /**
  * This class is responsible for parsing a Velocity
@@ -28,7 +29,7 @@
 */
 public class Parser/*...@bgen(jjtree)*/implements ParserTreeConstants, 
ParserConstants {/*...@bgen(jjtree)*/
   protected JJTParserState jjtree = new JJTParserState();/**
-     *  This Map contains a list of all of the dynamic directives.
+     * This Map contains a list of all of the dynamic directives.
      */
     private Map directives = new HashMap();
 
@@ -38,10 +39,16 @@
     private Map macroNames = new HashMap();
 
     /**
-     *  Name of current template we are parsing.  Passed to us in parse()
+     * Name of current template we are parsing.  Passed to us in parse()
      */
     public String currentTemplateName = "";
 
+    /**
+     * Set to true if the property 
+     * RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE is set to true
+     */
+    public boolean strictEscape = false;
+
     VelocityCharStream velcharstream = null;
 
     private RuntimeServices rsvc = null;
@@ -68,6 +75,10 @@
         velcharstream = new VelocityCharStream(
                 new ByteArrayInputStream("\n".getBytes()), 1, 1 );
 
+
+        strictEscape =
+            rs.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, 
false);
+
         /*
          *  and save the RuntimeServices
          */
@@ -187,14 +198,17 @@
         }
 
         /*
-         *  is this a PD or a control directive?
+         *  If this is a predefined derective or if we detect
+         *  a macro definition (this is aproximate at best) then
+         *  we absorb the forward slash.  If in strict reference
+         *  mode then we always absord the forward slash regardless
+         *  if the derective is defined or not.
          */
 
-        if ( isDirective(dirTag) )
-        {
-           bRecognizedDirective = true;
-        }
-        else if (macroNames.containsKey(dirTag) || rsvc.isVelocimacro(dirTag, 
currentTemplateName))
+        if (strictEscape
+             || isDirective(dirTag)
+             || macroNames.containsKey(dirTag)
+             || rsvc.isVelocimacro(dirTag, currentTemplateName))
         {
             bRecognizedDirective = true;
         }
@@ -478,11 +492,13 @@
          * if that failed, lets lookahead to see if we matched a PD or a VM
          */
         String nTag = t.next.image.substring(1);
-
-        if ( isDirective(nTag) )
-            control = true;
-        else if ( rsvc.isVelocimacro(nTag, currentTemplateName))
+        if (strictEscape
+            || isDirective(nTag)
+            || macroNames.containsKey(nTag)
+            || rsvc.isVelocimacro(nTag, currentTemplateName))
+        {
             control = true;
+        }
 
         jjtn000.val = "";
 
@@ -2769,108 +2785,6 @@
     finally { jj_save(11, xla); }
   }
 
-  final private boolean jj_3_8() {
-    if (jj_3R_33()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_74() {
-    if (jj_3R_73()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_60() {
-    if (jj_scan_token(IDENTIFIER)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_97() {
-    if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_29()) return true;
-    return false;
-  }
-
-  final private boolean jj_3_12() {
-    if (jj_scan_token(LBRACKET)) return true;
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_scan_token(31)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_36()) {
-    jj_scanpos = xsp;
-    if (jj_3R_37()) return true;
-    }
-    xsp = jj_scanpos;
-    if (jj_scan_token(31)) jj_scanpos = xsp;
-    if (jj_scan_token(DOUBLEDOT)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_71() {
-    if (jj_scan_token(FALSE)) return true;
-    return false;
-  }
-
-  final private boolean jj_3_4() {
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_scan_token(31)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_27()) jj_scanpos = xsp;
-    if (jj_3R_28()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_63() {
-    if (jj_3R_73()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_88() {
-    if (jj_scan_token(LPAREN)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_30() {
-    if (jj_3R_24()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_87() {
-    if (jj_3R_71()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_70() {
-    if (jj_scan_token(TRUE)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_86() {
-    if (jj_3R_70()) return true;
-    return false;
-  }
-
-  final private boolean jj_3_9() {
-    if (jj_scan_token(DOT)) return true;
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_3_10()) {
-    jj_scanpos = xsp;
-    if (jj_3R_34()) return true;
-    }
-    while (true) {
-      xsp = jj_scanpos;
-      if (jj_3R_90()) { jj_scanpos = xsp; break; }
-    }
-    return false;
-  }
-
-  final private boolean jj_3R_85() {
-    if (jj_3R_69()) return true;
-    return false;
-  }
-
   final private boolean jj_3R_84() {
     if (jj_3R_68()) return true;
     return false;
@@ -3192,11 +3106,6 @@
     return false;
   }
 
-  final private boolean jj_3_2() {
-    if (jj_scan_token(DOUBLE_ESCAPE)) return true;
-    return false;
-  }
-
   final private boolean jj_3R_76() {
     if (jj_3R_40()) return true;
     return false;
@@ -3208,6 +3117,11 @@
     return false;
   }
 
+  final private boolean jj_3_2() {
+    if (jj_scan_token(DOUBLE_ESCAPE)) return true;
+    return false;
+  }
+
   final private boolean jj_3R_91() {
     Token xsp;
     xsp = jj_scanpos;
@@ -3489,6 +3403,108 @@
     return false;
   }
 
+  final private boolean jj_3_8() {
+    if (jj_3R_33()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_74() {
+    if (jj_3R_73()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_60() {
+    if (jj_scan_token(IDENTIFIER)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_97() {
+    if (jj_scan_token(COMMA)) return true;
+    if (jj_3R_29()) return true;
+    return false;
+  }
+
+  final private boolean jj_3_12() {
+    if (jj_scan_token(LBRACKET)) return true;
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_scan_token(31)) jj_scanpos = xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_36()) {
+    jj_scanpos = xsp;
+    if (jj_3R_37()) return true;
+    }
+    xsp = jj_scanpos;
+    if (jj_scan_token(31)) jj_scanpos = xsp;
+    if (jj_scan_token(DOUBLEDOT)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_71() {
+    if (jj_scan_token(FALSE)) return true;
+    return false;
+  }
+
+  final private boolean jj_3_4() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_scan_token(31)) jj_scanpos = xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_27()) jj_scanpos = xsp;
+    if (jj_3R_28()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_63() {
+    if (jj_3R_73()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_88() {
+    if (jj_scan_token(LPAREN)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_30() {
+    if (jj_3R_24()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_87() {
+    if (jj_3R_71()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_70() {
+    if (jj_scan_token(TRUE)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_86() {
+    if (jj_3R_70()) return true;
+    return false;
+  }
+
+  final private boolean jj_3_9() {
+    if (jj_scan_token(DOT)) return true;
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3_10()) {
+    jj_scanpos = xsp;
+    if (jj_3R_34()) return true;
+    }
+    while (true) {
+      xsp = jj_scanpos;
+      if (jj_3R_90()) { jj_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  final private boolean jj_3R_85() {
+    if (jj_3R_69()) return true;
+    return false;
+  }
+
   public ParserTokenManager token_source;
   public Token token, jj_nt;
   private int jj_ntk;

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/ParserTokenManager.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/ParserTokenManager.java?rev=736335&r1=736334&r2=736335&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/ParserTokenManager.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/ParserTokenManager.java
 Wed Jan 21 08:36:34 2009
@@ -10,6 +10,7 @@
 import org.apache.velocity.runtime.directive.MacroParseException;
 import org.apache.velocity.util.StringUtils;
 import org.apache.commons.lang.text.StrBuilder;
+import org.apache.velocity.runtime.RuntimeConstants;
 
 public class ParserTokenManager implements ParserConstants
 {

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java?rev=736335&r1=736334&r2=736335&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
 Wed Jan 21 08:36:34 2009
@@ -83,6 +83,12 @@
      */
     private ASTIndex astIndex = null;
     
+    /**
+     * Indicates if we are using modified escape behavior in strict mode.
+     * mainly we allow \$abc -> to render as $abc
+     */
+    public boolean strictEscape = false;
+    
     private int numChildren = 0;
 
     protected Info uberInfo;
@@ -118,12 +124,11 @@
     public Object init(InternalContextAdapter context, Object data)
     throws TemplateInitException
     {
-        /*
-         *  init our children
-         */
-
         super.init(context, data);
-
+        
+        strictEscape = 
rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, false);
+        strictRef = 
rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false);
+            
         /*
          *  the only thing we can do in init() is getRoot()
          *  as that is template based, not context based,
@@ -153,7 +158,6 @@
         /*
          * make an uberinfo - saves new's later on
          */
-
         uberInfo = new Info(getTemplateName(), getLine(),getColumn());
 
         /*
@@ -161,9 +165,7 @@
          */
         logOnNull =
             
rsvc.getBoolean(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID, true);
-
-        strictRef = 
rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false);
- 
+         
         /**
          * In the case we are referencing a variable with #if($foo) or
          * #if( ! $foo) then we allow variables to be undefined and we 
@@ -340,7 +342,21 @@
             return true;
         }
 
-        Object value = execute(null, context);
+        Object value = null;
+        if (escaped && strictEscape)
+        {
+          /**
+           * If we are in strict mode and the variable is escaped, then don't 
bother to
+           * retreive the value since we won't use it. And if the var is not 
defined 
+           * it will throw an exception.  Set value to TRUE to fall through 
below with
+           * simply printing $foo, and not \$foo
+           */
+          value = Boolean.TRUE;
+        }
+        else
+        {
+          value = execute(null, context);
+        }
 
         String localNullString = null;
 
@@ -395,7 +411,12 @@
              * other...
              */
             localNullString = getNullString(context);
-            writer.write(escPrefix);
+            if (!strictEscape)
+            {
+                // If in strict escape mode then we only print escape once.
+                // Yea, I know.. brittle stuff
+                writer.write(escPrefix);
+            }            
             writer.write(escPrefix);
             writer.write(morePrefix);
             writer.write(localNullString);
@@ -710,6 +731,15 @@
 
         if (slashbang != -1)
         {
+            if (strictEscape)
+            {
+                // If we are in strict escape mode, then we consider this type 
of 
+                // pattern a non-reference, and we print it out as schmoo...
+                nullString = literal();
+                escaped = true;
+                return literal();
+            }
+          
             /*
              *  lets do all the work here.  I would argue that if this occurrs,
              *  it's not a reference at all, so preceeding \ characters in 
front

Modified: velocity/engine/trunk/src/parser/Parser.jjt
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/parser/Parser.jjt?rev=736335&r1=736334&r2=736335&view=diff
==============================================================================
--- velocity/engine/trunk/src/parser/Parser.jjt (original)
+++ velocity/engine/trunk/src/parser/Parser.jjt Wed Jan 21 08:36:34 2009
@@ -87,6 +87,7 @@
 import org.apache.velocity.runtime.directive.MacroParseException;
 import org.apache.velocity.util.StringUtils;
 import org.apache.commons.lang.text.StrBuilder;
+import org.apache.velocity.runtime.RuntimeConstants;
 
 /**
  * This class is responsible for parsing a Velocity
@@ -105,7 +106,7 @@
 public class Parser
 {
     /**
-     *  This Map contains a list of all of the dynamic directives.
+     * This Map contains a list of all of the dynamic directives.
      */
     private Map directives = new HashMap();
     
@@ -115,10 +116,16 @@
     private Map macroNames = new HashMap();
 
     /**
-     *  Name of current template we are parsing.  Passed to us in parse()
+     * Name of current template we are parsing.  Passed to us in parse()
      */
     public String currentTemplateName = "";
-
+    
+    /**
+     * Set to true if the property 
+     * RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE is set to true
+     */
+    public boolean strictEscape = false; 
+     
     VelocityCharStream velcharstream = null;
 
     private RuntimeServices rsvc = null;
@@ -144,6 +151,10 @@
          */
         velcharstream = new VelocityCharStream(
                 new ByteArrayInputStream("\n".getBytes()), 1, 1 );
+                
+                
+        strictEscape = 
+            rs.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, 
false);                
 
         /*
          *  and save the RuntimeServices
@@ -264,14 +275,17 @@
         }
 
         /*
-         *  is this a PD or a control directive?
+         *  If this is a predefined derective or if we detect
+         *  a macro definition (this is aproximate at best) then
+         *  we absorb the forward slash.  If in strict reference
+         *  mode then we always absord the forward slash regardless
+         *  if the derective is defined or not.
          */
 
-        if ( isDirective(dirTag) )
-        {
-           bRecognizedDirective = true;
-        }
-        else if (macroNames.containsKey(dirTag) || rsvc.isVelocimacro(dirTag, 
currentTemplateName))
+        if (strictEscape 
+             || isDirective(dirTag) 
+             || macroNames.containsKey(dirTag) 
+             || rsvc.isVelocimacro(dirTag, currentTemplateName))
         {
             bRecognizedDirective = true;
         }
@@ -1296,11 +1310,13 @@
          * if that failed, lets lookahead to see if we matched a PD or a VM
          */
         String nTag = t.next.image.substring(1);
-
-        if ( isDirective(nTag) )
-            control = true;
-        else if ( rsvc.isVelocimacro(nTag, currentTemplateName))
+        if (strictEscape
+            || isDirective(nTag)
+            || macroNames.containsKey(nTag)
+            || rsvc.isVelocimacro(nTag, currentTemplateName))
+        {
             control = true;
+        }
 
         jjtThis.val = "";
 

Added: 
velocity/engine/trunk/src/test/org/apache/velocity/test/StrictEscapeTestCase.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/StrictEscapeTestCase.java?rev=736335&view=auto
==============================================================================
--- 
velocity/engine/trunk/src/test/org/apache/velocity/test/StrictEscapeTestCase.java
 (added)
+++ 
velocity/engine/trunk/src/test/org/apache/velocity/test/StrictEscapeTestCase.java
 Wed Jan 21 08:36:34 2009
@@ -0,0 +1,93 @@
+package org.apache.velocity.test;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+
+/**
+ * Test Strict escape mode
+ * property: RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE set to true
+ */
+public class StrictEscapeTestCase extends BaseEvalTestCase
+{
+  public StrictEscapeTestCase(String name)
+  {
+      super(name);
+  }
+
+  public void setUp() throws Exception
+  {
+      super.setUp();
+      engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, 
true);
+      context.put("pow", "bang");
+  }
+  
+  /**
+   * Test that escaping works in strict mode
+   */
+  public void testVarEscape()
+  {
+      assertEvalEquals("\\$bogus", "\\\\$bogus");
+      assertEvalEquals("\\\\$bogus", "\\\\\\\\$bogus");
+      assertEvalEquals("\\$bogus", "#set($foo = \"\\\\$bogus\")$foo");
+    
+      assertEvalEquals("$bogus", "\\$bogus");
+      assertEvalEquals("$bogus.xyz", "\\$bogus.xyz");
+      assertEvalEquals("${bogus}", "\\${bogus}");
+      assertEvalEquals("${bogus.xyz}", "\\${bogus.xyz}");
+      assertEvalEquals("\\$bogus", "\\\\\\$bogus");
+      assertEvalEquals("\\xyz","#set($foo = \"xyz\")\\\\$foo");
+      assertEvalEquals("\\$foo","#set($foo = \"xyz\")\\\\\\$foo");
+      assertEvalEquals("$foo\\","#set($foo = \"xyz\")\\$foo\\");
+      assertEvalEquals("$pow", "#set($foo = \"\\$pow\")$foo");
+      assertEvalEquals("\\bang", "#set($foo = \"\\\\$pow\")$foo");
+      assertEvalEquals("\\$pow", "#set($foo = \"\\\\\\$pow\")$foo");
+      
+      assertEvalEquals("\\$%", "\\$%");      
+  } 
+  
+  
+  public void testMacroEscape()
+  {
+      assertEvalEquals("#bogus()", "\\#bogus()");
+      assertEvalEquals("\\#bogus", "\\\\#bogus");
+
+      // define the foo macro
+      assertEvalEquals("", "#macro(foo)bar#end");
+      
+      assertEvalEquals("#foo()", "\\#foo()");
+      assertEvalEquals("\\bar", "\\\\#foo()");
+      assertEvalEquals("\\#foo()", "\\\\\\#foo()");
+            
+      assertEvalEquals("bar", "#set($abc = \"#foo()\")$abc");
+      assertEvalEquals("#foo()", "#set($abc = \"\\#foo()\")$abc");
+      assertEvalEquals("\\bar", "#set($abc = \"\\\\#foo()\")$abc");
+      
+      assertEvalEquals("#...@foo()", "\...@foo()");
+      assertEvalEquals("\\bar", "\\...@foo()#end");
+      assertEvalEquals("#...@foo()#end", "\...@foo()\\#end");
+      
+      assertEvalEquals("#end #foreach #define() #elseif", "\\#end \\#foreach 
\\#define() \\#elseif");
+      assertEvalEquals("#{end} #{foreach} #{define}() #{elseif}", "\\#{end} 
\\#{foreach} \\#{define}() \\#{elseif}");
+      assertEvalEquals("#macro(foo) #end", "\\#macro(foo) \\#end");
+      
+  }
+}


Reply via email to