Author: cbrisson
Date: Mon Feb 20 10:58:58 2017
New Revision: 1783739

URL: http://svn.apache.org/viewvc?rev=1783739&view=rev
Log:
[engine] implements new strategy for reference boolean evaluation

1) return false for a null object

2) return its value for a Boolean object, or the result of the getAsBoolean() 
method if it exists.

3) If directive.if.emptycheck = false (true by default), stop here and return 
true.

4) check for emptiness:
  - return whether an array is empty.
  - return whether isEmpty() is false (covers String and all Collection 
classes).
  - return whether length() is zero (covers CharSequence classes other than 
String).
  - returns whether size() is zero.
  - return whether a Number *strictly* equals zero.

5) check for emptiness after explicit conversion methods:
  - return whether the result of getAsString() is empty (and false for a null 
result) if it exists.
  - return whether the result of getAsNumber() *strictly* equals zero (and 
false for a null result) if it exists. 




Modified:
    
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java
    
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
    
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/DuckType.java
    
velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
    
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java

Modified: 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java?rev=1783739&r1=1783738&r2=1783739&view=diff
==============================================================================
--- 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java
 (original)
+++ 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java
 Mon Feb 20 10:58:58 2017
@@ -76,6 +76,12 @@ public interface RuntimeConstants
     String SKIP_INVALID_ITERATOR = "directive.foreach.skip.invalid";
 
     /**
+     * An empty object (string, collection) or zero number is false.
+     * @since 2.0
+     */
+    String CHECK_EMPTY_OBJECTS = "directive.if.emptycheck";
+    
+    /**
      * Starting tag for error messages triggered by passing a parameter not 
allowed in the #include directive. Only string literals,
      * and references are allowed.
      */
@@ -98,7 +104,7 @@ public interface RuntimeConstants
      * @since 1.7
      */
     String PROVIDE_SCOPE_CONTROL = "provide.scope.control";
-
+    
     /*
      * ----------------------------------------------------------------------
      *  R E S O U R C E   M A N A G E R   C O N F I G U R A T I O N

Modified: 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java?rev=1783739&r1=1783738&r2=1783739&view=diff
==============================================================================
--- 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
 (original)
+++ 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
 Mon Feb 20 10:58:58 2017
@@ -579,7 +579,7 @@ public class ASTReference extends Simple
         }
         try
         {
-            return DuckType.asBoolean(value);
+            return DuckType.asBoolean(value, 
rsvc.getBoolean(RuntimeConstants.CHECK_EMPTY_OBJECTS, true));
         }
         catch(Exception e)
         {

Modified: 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/DuckType.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/DuckType.java?rev=1783739&r1=1783738&r2=1783739&view=diff
==============================================================================
--- 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/DuckType.java
 (original)
+++ 
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/DuckType.java
 Mon Feb 20 10:58:58 2017
@@ -23,9 +23,12 @@ import java.lang.reflect.Array;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.HashMap;
 import java.util.Map;
 
+import static org.apache.velocity.runtime.parser.node.MathUtils.isZero;
+
 /**
  * Support for getAs<Type>() convention for rendering (String), evaluating 
(Boolean)
  * or doing math with (Number) references.
@@ -40,7 +43,9 @@ public class DuckType
         STRING("getAsString"),
         NUMBER("getAsNumber"),
         BOOLEAN("getAsBoolean"),
-        EMPTY("isEmpty");
+        EMPTY("isEmpty"),
+        LENGTH("length"),
+        SIZE("size");
 
         final String name;
         final Map<Class,Object> cache = new HashMap();
@@ -93,11 +98,6 @@ public class DuckType
             get(value, Types.NUMBER) == null;
     }
 
-    public static boolean asBoolean(Object value)
-    {
-        return asBoolean(value, true);
-    }
-
     public static boolean asBoolean(Object value, boolean coerceType)
     {
         if (value == null)
@@ -129,23 +129,37 @@ public class DuckType
             return true;
         }
 
-        // empty string
-        if (value instanceof CharSequence)
+        // empty array
+        if (value.getClass().isArray())
         {
-            return ((CharSequence)value).length() == 0;
+            return Array.getLength(value) == 0;// [] is false
         }
 
-        // isEmpty() object
+        // isEmpty() for object / string
         Object isEmpty = get(value, Types.EMPTY);
         if (isEmpty != NO_METHOD)
         {
             return (Boolean)isEmpty;
         }
 
-        // empty array
-        if (value.getClass().isArray())
+        // isEmpty() for object / other char sequences
+        Object length = get(value, Types.LENGTH);
+        if (length != NO_METHOD && length instanceof Number)
         {
-            return Array.getLength(value) == 0;// [] is false
+            return isZero((Number)length);
+        }
+
+        // size() object / collection
+        Object size = get(value, Types.SIZE);
+        if (size != NO_METHOD && size instanceof Number)
+        {
+            return isZero((Number)size);
+        }
+
+        // zero numbers are false
+        if (value instanceof Number)
+        {
+            return isZero((Number)value);
         }
 
         // null getAsString()
@@ -166,10 +180,13 @@ public class DuckType
         {
             return true;
         }
+        // zero numbers are false
+        else if (asNumber != NO_METHOD && asNumber instanceof Number)
+        {
+            return isZero((Number)asNumber);
+        }
 
-        // empty toString()
-        String string = value.toString();
-        return string == null || string.length() == 0;
+        return false;
     }
 
     public static Number asNumber(Object value)

Modified: 
velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties?rev=1783739&r1=1783738&r2=1783739&view=diff
==============================================================================
--- 
velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
 (original)
+++ 
velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
 Mon Feb 20 10:58:58 2017
@@ -44,6 +44,15 @@ runtime.string.interning = true
 directive.foreach.maxloops = -1
 
 # ----------------------------------------------------------------------------
+# I F  P R O P E R T I E S
+# ----------------------------------------------------------------------------
+# This property controls whether empty strings and collections,
+# as long as zero numbers, do evaluate to false.
+# ----------------------------------------------------------------------------
+
+directive.if.emptycheck = true
+
+# ----------------------------------------------------------------------------
 # I N C L U D E  P R O P E R T I E S
 # ----------------------------------------------------------------------------
 # These are the properties that governed the way #include'd content

Modified: 
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java?rev=1783739&r1=1783738&r2=1783739&view=diff
==============================================================================
--- 
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java
 (original)
+++ 
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java
 Mon Feb 20 10:58:58 2017
@@ -19,7 +19,13 @@ package org.apache.velocity.test;
  * under the License.
  */
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.Collections;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 /**
  * Used to check that empty values are properly handled in #if statements
@@ -37,6 +43,12 @@ public class IfEmptyTestCase extends Bas
         assertEvalEquals("", "#if( $obj )fail#end");
     }
 
+    protected void assertNotEmpty(Object obj)
+    {
+        context.put("obj", obj);
+        assertEvalEquals("", "#if( !$obj )fail#end");
+    }
+
     public void testNull()
     {
         assertEmpty(null);
@@ -49,13 +61,43 @@ public class IfEmptyTestCase extends Bas
         assertEmpty(Collections.emptyMap());
         assertEmpty(Collections.emptyList());
         assertEmpty(new Object[]{});
+        List list = new ArrayList();
+        list.add(1);
+        assertNotEmpty(list);
+        Map map = new TreeMap();
+        map.put("foo", 1);
+        assertNotEmpty(map);
     }
 
     public void testString()
     {
         assertEmpty("");
         assertEmpty(new EmptyAsString());
-        assertEmpty(new EmptyToString());
+        assertNotEmpty("hello");
+    }
+
+    public void testNumber()
+    {
+        assertEmpty(0);
+        assertEmpty(0L);
+        assertEmpty(0.0f);
+        assertEmpty(0.0);
+        assertEmpty(BigInteger.ZERO);
+        assertEmpty(BigDecimal.ZERO);
+        assertNotEmpty(1);
+        assertNotEmpty(1L);
+        assertNotEmpty(1.0f);
+        assertNotEmpty(1.0);
+        assertNotEmpty(BigInteger.ONE);
+        assertNotEmpty(BigDecimal.ONE);
+    }
+
+    public void testStringBuilder()
+    {
+        StringBuilder builder = new StringBuilder();
+        assertEmpty(builder);
+        builder.append("yo");
+        assertNotEmpty(builder);
     }
 
     public static class NullAsString
@@ -82,12 +124,4 @@ public class IfEmptyTestCase extends Bas
         }
     }
 
-    public static class EmptyToString
-    {
-        public String toString()
-        {
-            return "";
-        }
-    }
-
 }


Reply via email to