You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2026/09/11 09:15:44 UTC

(velocity-engine) 02/03: Deprecate 'and', 'or', 'not', 'lt', 'gt', ... in favor of '&&', '||', '!', '<', '>', ...

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/velocity-engine.git

commit e724d9fa02c4d3609e75f8db0d9c0d72f01c0c7a
Author: Claude Brisson <cl...@renegat.net>
AuthorDate: Fri Sep 11 08:19:38 2026 +0200

    Deprecate 'and', 'or', 'not', 'lt', 'gt', ... in favor of '&&', '||', '!', '<', '>', ...
---
 src/changes/changes.xml                            |  3 +-
 .../runtime/parser/node/ASTBinaryOperator.java     | 18 +++++
 .../velocity/runtime/parser/node/ASTNotNode.java   | 18 +++++
 .../velocity/runtime/parser/node/SimpleNode.java   | 46 +++++++++++
 .../velocity/test/DeprecationWarningTestCase.java  | 93 ++++++++++++++++++++++
 5 files changed, 177 insertions(+), 1 deletion(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c09ff62b..255a1269 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -44,7 +44,8 @@
             <code>BigDecimal</code> in VTL math operations.
         </action>
         <action type="add" dev="cbrisson" issue="VELOCITY-995">
-            Deprecate <code>${foo|bar}</code> in favor of <code>${foo?:bar}</code>, along with
+            Deprecate <code>${foo|bar}</code> in favor of <code>${foo?:bar}</code>, the textual
+            operators <code>and or not gt ge lt le eq ne</code> in favor of their symbols, and
             the <code>parser.allow_hyphen_in_identifiers</code> option; warnings gated by
             <code>runtime.deprecation.warn</code>, on by default.
         </action>
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTBinaryOperator.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTBinaryOperator.java
index 8b776eaf..4c225667 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTBinaryOperator.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTBinaryOperator.java
@@ -3,9 +3,15 @@
 import org.apache.velocity.context.InternalContextAdapter;
 import org.apache.velocity.exception.TemplateInitException;
 import org.apache.velocity.runtime.parser.Parser;
+import org.apache.velocity.runtime.parser.Token;
 
 public abstract class ASTBinaryOperator extends SimpleNode
 {
+    /**
+     * The operator token, kept until init
+     */
+    private Token operator;
+
     public ASTBinaryOperator(int id)
     {
         super(id);
@@ -16,6 +22,16 @@ public ASTBinaryOperator(Parser p, int id)
         super(p, id);
     }
 
+    /**
+     * @see org.apache.velocity.runtime.parser.node.Node#jjtOpen()
+     */
+    @Override
+    public void jjtOpen()
+    {
+        super.jjtOpen();
+        operator = parser.getToken(0); // the operator has just been consumed
+    }
+
     /**
      * @throws TemplateInitException
      * @see org.apache.velocity.runtime.parser.node.Node#init(org.apache.velocity.context.InternalContextAdapter, java.lang.Object)
@@ -24,6 +40,8 @@ public ASTBinaryOperator(Parser p, int id)
     public Object init(InternalContextAdapter context, Object data) throws TemplateInitException
     {
         Object obj = super.init(context, data);
+        warnTextualOperator(operator);
+        operator = null;
         cleanupParserAndTokens(); // drop reference to Parser and all JavaCC Tokens
         return obj;
     }
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTNotNode.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTNotNode.java
index 1ad97978..3dad7edd 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTNotNode.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTNotNode.java
@@ -23,12 +23,18 @@
 import org.apache.velocity.exception.MethodInvocationException;
 import org.apache.velocity.exception.TemplateInitException;
 import org.apache.velocity.runtime.parser.Parser;
+import org.apache.velocity.runtime.parser.Token;
 
 /**
  *
  */
 public class ASTNotNode extends SimpleNode
 {
+    /**
+     * The operator token, kept until init
+     */
+    private Token operator;
+
     /**
      * @param id
      */
@@ -47,6 +53,16 @@ public ASTNotNode(Parser p, int id)
     }
 
 
+    /**
+     * @see org.apache.velocity.runtime.parser.node.Node#jjtOpen()
+     */
+    @Override
+    public void jjtOpen()
+    {
+        super.jjtOpen();
+        operator = parser.getToken(0); // the operator has just been consumed
+    }
+
     /**
      * @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.node.StandardParserVisitor, java.lang.Object)
      */
@@ -84,6 +100,8 @@ public Object value(InternalContextAdapter context)
     public Object init(InternalContextAdapter context, Object data) throws TemplateInitException
     {
     	Object obj = super.init(context, data);
+    	warnTextualOperator(operator);
+    	operator = null;
     	cleanupParserAndTokens(); // drop reference to Parser and all JavaCC Tokens
     	return obj;
     }
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SimpleNode.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SimpleNode.java
index 9b5218a1..1bc1c68a 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SimpleNode.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SimpleNode.java
@@ -406,6 +406,52 @@ public Object init(InternalContextAdapter context, Object data) throws TemplateI
         return data;
     }
 
+    /**
+     * The symbol spelling of an operator written with its deprecated word spelling.
+     *
+     * @param operator image of an operator token
+     * @return the symbol to use instead, or null if this is not a word spelling
+     */
+    private static String symbolFor(String operator)
+    {
+        switch (operator)
+        {
+            case "and": return "&&";
+            case "or":  return "||";
+            case "not": return "!";
+            case "eq":  return "==";
+            case "ne":  return "!=";
+            case "lt":  return "<";
+            case "le":  return "<=";
+            case "gt":  return ">";
+            case "ge":  return ">=";
+            default:    return null;
+        }
+    }
+
+    /**
+     * VTL syntax deprecation warning (VELOCITY-995), gated by runtime.deprecation.warn:
+     * the word spellings of the operators are deprecated in favour of the symbols.
+     * <p>
+     * Called by the operator nodes at init time, with the operator token they captured
+     * while parsing. Silent when the symbol spelling was used.
+     *
+     * @param operator the operator token, may be null
+     */
+    protected void warnTextualOperator(Token operator)
+    {
+        if (operator == null)
+        {
+            return;
+        }
+        String symbol = symbolFor(operator.image);
+        if (symbol != null && rsvc.getBoolean(RuntimeConstants.RUNTIME_DEPRECATION_WARN, true))
+        {
+            log.warn("the textual operator '{}' is deprecated; write '{}' instead - {} [line {}, column {}]",
+                     operator.image, symbol, getTemplateName(), operator.beginLine, operator.beginColumn);
+        }
+    }
+
     /**
      * @see org.apache.velocity.runtime.parser.node.Node#evaluate(org.apache.velocity.context.InternalContextAdapter)
      */
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/DeprecationWarningTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/DeprecationWarningTestCase.java
index 3d51afc2..0efe0339 100644
--- a/velocity-engine-core/src/test/java/org/apache/velocity/test/DeprecationWarningTestCase.java
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/DeprecationWarningTestCase.java
@@ -163,6 +163,99 @@ public void testHyphenOptionSilentWhenDeprecationOff()
         assertFalse(warningsAtInit(hyphenEngine(false, true)).contains("allow_hyphen_in_identifiers"));
     }
 
+    /* ---- the word spellings of the operators: deprecated in favour of the symbols ---- */
+
+    /** so that the comparisons have something to compare, keeping the log to warnings */
+    private void operands()
+    {
+        context.put("a", 2);
+        context.put("b", 3);
+    }
+
+    private int warningCount(String vtl)
+    {
+        String out = warningsFor(vtl);
+        return out.split("textual operator", -1).length - 1;
+    }
+
+    public void testTextualOperatorsWarn()
+    {
+        operands();
+        assertWarns("#if($a and $b)x#end");
+        assertWarns("#if($a or $b)x#end");
+        assertWarns("#if(not $a)x#end");
+        assertWarns("#if($a eq $b)x#end");
+        assertWarns("#if($a ne $b)x#end");
+        assertWarns("#if($a lt $b)x#end");
+        assertWarns("#if($a le $b)x#end");
+        assertWarns("#if($a gt $b)x#end");
+        assertWarns("#if($a ge $b)x#end");
+        assertWarns("#set($x = $a lt $b)");
+    }
+
+    public void testTextualOperatorWarningNamesTheSymbol()
+    {
+        operands();
+        String out = warningsFor("#if($a and $b)x#end");
+        assertTrue("the warning must name the '&&' spelling, log was:\n" + out,
+                   out.contains("the textual operator 'and' is deprecated; write '&&' instead"));
+
+        out = warningsFor("#if($a ge $b)x#end");
+        assertTrue("the warning must name the '>=' spelling, log was:\n" + out,
+                   out.contains("the textual operator 'ge' is deprecated; write '>=' instead"));
+    }
+
+    public void testSymbolOperatorsNeverWarn()
+    {
+        operands();
+        assertNoWarn("#if($a && $b)x#end");
+        assertNoWarn("#if($a || $b)x#end");
+        assertNoWarn("#if(!$a)x#end");
+        assertNoWarn("#if($a == $b)x#end");
+        assertNoWarn("#if($a != $b)x#end");
+        assertNoWarn("#if($a < $b)x#end");
+        assertNoWarn("#if($a <= $b)x#end");
+        assertNoWarn("#if($a > $b)x#end");
+        assertNoWarn("#if($a >= $b)x#end");
+        assertNoWarn("#set($x = $a + $b * 2 - 1)");
+        assertNoWarn("#set($x = \"and or not gt\")");
+        assertNoWarn("#set($x = $a)#if($x)x#end");
+    }
+
+    public void testOneWarningPerTextualOperatorOccurrence()
+    {
+        operands();
+        context.put("c", 4);
+        assertEquals("one warning per operator expected",
+                     3, warningCount("#if($a and $b and not $c)x#end"));
+        assertEquals("only the word spellings count",
+                     1, warningCount("#if($a && $b or $a == $b)x#end"));
+        assertEquals("one warning per operator expected, nesting included",
+                     2, warningCount("#if(not ($a gt $b))x#end"));
+    }
+
+    public void testTextualOperatorWarnsFromATemplateFile()
+    {
+        operands();
+        addTemplate("wordops.vm", "#if($a and $b)x#end");
+        log.startCapture();
+        assertTmplEquals("x", "wordops.vm");
+        log.stopCapture();
+        String out = log.getLog();
+        assertTrue("a template file must warn just like evaluate(), log was:\n" + out,
+                   out.contains("the textual operator 'and' is deprecated"));
+        assertTrue("the warning must point at the operator itself, log was:\n" + out,
+                   out.contains("wordops.vm [line 1, column 8]"));
+    }
+
+    public void testTextualOperatorSilentWhenDeprecationOff()
+    {
+        operands();
+        String out = warningsWithoutSetting("#if($a and not $b)x#end", "false");
+        assertFalse("no warning expected with runtime.deprecation.warn = false, log was:\n" + out,
+                    out.contains("deprecated"));
+    }
+
     /* ---- on by default, silenced explicitly ---- */
 
     private String warningsWithoutSetting(String vtl, String deprecationWarn)