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)