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 ""; - } - } - }