← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 20497: Program indicator, using lenient evaluation of expressions, useful for null handling

 

------------------------------------------------------------
revno: 20497
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Sun 2015-10-04 22:21:21 +0200
message:
  Program indicator, using lenient evaluation of expressions, useful for null handling
modified:
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java
  dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionFunctions.java
  dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionUtils.java
  dhis-2/dhis-support/dhis-support-commons/src/test/java/org/hisp/dhis/commons/util/ExpressionUtilsTest.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java	2015-09-27 09:29:50 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java	2015-10-04 20:21:21 +0000
@@ -62,7 +62,6 @@
 import org.hisp.dhis.trackedentityattributevalue.TrackedEntityAttributeValueService;
 import org.hisp.dhis.trackedentitydatavalue.TrackedEntityDataValue;
 import org.hisp.dhis.trackedentitydatavalue.TrackedEntityDataValueService;
-import org.hisp.dhis.util.ObjectUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -81,6 +80,8 @@
         put( DaysBetweenSqlFunction.KEY, new DaysBetweenSqlFunction() ).
         put( ConditionalSqlFunction.KEY, new ConditionalSqlFunction() ).build();
 
+    private static final String NULL_REPLACEMENT = "null";
+    
     // -------------------------------------------------------------------------
     // Dependencies
     // -------------------------------------------------------------------------
@@ -252,7 +253,7 @@
                     
                     if ( dataValue == null )
                     {
-                        value = String.valueOf( ObjectUtils.firstNonNull( indicator.getMissingValueReplacement(), 0 ) );
+                        value = NULL_REPLACEMENT;
                     }
                     else
                     {
@@ -282,7 +283,7 @@
                     
                     if ( attributeValue == null )
                     {
-                        value = String.valueOf( ObjectUtils.firstNonNull( indicator.getMissingValueReplacement(), 0 ) );
+                        value = NULL_REPLACEMENT;
                     }
                     else
                     {

=== modified file 'dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionFunctions.java'
--- dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionFunctions.java	2015-10-01 14:39:13 +0000
+++ dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionFunctions.java	2015-10-04 20:21:21 +0000
@@ -51,7 +51,7 @@
     {
         if ( value == null )
         {
-            throw new IllegalArgumentException( "Argument is null: " + value );
+            return null;
         }
         
         return Math.max( 0d, value.doubleValue() );
@@ -68,7 +68,7 @@
     {
         if ( value == null )
         {
-            throw new IllegalArgumentException( "Argument is null: " + value );
+            return null;
         }
         
         return ( value.doubleValue() >= 0d ) ? 1d : 0d;

=== modified file 'dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionUtils.java'
--- dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionUtils.java	2015-10-01 15:36:32 +0000
+++ dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionUtils.java	2015-10-04 20:21:21 +0000
@@ -47,6 +47,7 @@
 public class ExpressionUtils
 {
     private static final JexlEngine JEXL = new JexlEngine();
+    private static final JexlEngine JEXL_STRICT = new JexlEngine();
     
     private static final Map<String, String> EL_SQL_MAP = new HashMap<>();
     private static final String IGNORED_KEYWORDS_REGEX = 
@@ -62,7 +63,12 @@
         JEXL.setFunctions( functions );
         JEXL.setCache( 512 );
         JEXL.setSilent( false );
-        JEXL.setStrict( true );
+        JEXL.setLenient( true ); // Lenient
+        
+        JEXL_STRICT.setFunctions( functions );
+        JEXL_STRICT.setCache( 512 );
+        JEXL_STRICT.setSilent( false );
+        JEXL_STRICT.setStrict( true ); // Strict
         
         EL_SQL_MAP.put( "&&", "and" );
         EL_SQL_MAP.put( "\\|\\|", "or" );
@@ -81,9 +87,22 @@
      */
     public static Object evaluate( String expression, Map<String, Object> vars )
     {
+        return evaluate( expression, vars, false );
+    }
+
+    /**
+     * @param expression the expression.
+     * @param vars the variables, can be null.
+     * @param strict indicates whether to use strict or lenient engine mode.
+     * @return the result of the evaluation.
+     */
+    private static Object evaluate( String expression, Map<String, Object> vars, boolean strict )
+    {
         expression = expression.replaceAll( IGNORED_KEYWORDS_REGEX, StringUtils.EMPTY );
         
-        Expression exp = JEXL.createExpression( expression );
+        JexlEngine engine = strict ? JEXL_STRICT : JEXL;
+        
+        Expression exp = engine.createExpression( expression );
         
         JexlContext context = vars != null ? new MapContext( vars ) : new MapContext();
         
@@ -166,7 +185,7 @@
     {
         try
         {
-            Object result = evaluate( expression, vars );
+            Object result = evaluate( expression, vars, true );
             
             return result != null;
         }

=== modified file 'dhis-2/dhis-support/dhis-support-commons/src/test/java/org/hisp/dhis/commons/util/ExpressionUtilsTest.java'
--- dhis-2/dhis-support/dhis-support-commons/src/test/java/org/hisp/dhis/commons/util/ExpressionUtilsTest.java	2015-10-01 15:36:32 +0000
+++ dhis-2/dhis-support/dhis-support-commons/src/test/java/org/hisp/dhis/commons/util/ExpressionUtilsTest.java	2015-10-04 20:21:21 +0000
@@ -51,6 +51,7 @@
         assertEquals( 3d, ExpressionUtils.evaluateToDouble( "3", null ), DELTA );
         assertEquals( 3.45, ExpressionUtils.evaluateToDouble( "3.45", null ), DELTA );
         assertEquals( 5d, ExpressionUtils.evaluateToDouble( "2 + 3", null ), DELTA );
+        assertEquals( 2d, ExpressionUtils.evaluateToDouble( "5 + -3", null ), DELTA );
         assertEquals( 15.6, ExpressionUtils.evaluateToDouble( "12.4 + 3.2", null ), DELTA );
         assertEquals( 2.0, ExpressionUtils.evaluateToDouble( "2 > 1 ? 2.0 : 1.0", null ), DELTA );
         assertEquals( 1.0, ExpressionUtils.evaluateToDouble( "2 > 4 ? 2.0 : 1.0", null ), DELTA );
@@ -65,6 +66,15 @@
         assertEquals( 1d, ExpressionUtils.evaluateToDouble( "d2:zing(d2:oizp(3))", null ), DELTA );
         assertEquals( 2d, ExpressionUtils.evaluateToDouble( "d2:zpvc(1,3)", null ), DELTA );
         assertEquals( 3d, ExpressionUtils.evaluateToDouble( "d2:zpvc(1,-1,2,-3,0)", null ), DELTA );
+        assertEquals( 4d, ExpressionUtils.evaluateToDouble( "d2:condition('3 > 2',4,3)", null ), DELTA );
+        assertEquals( 3d, ExpressionUtils.evaluateToDouble( "2 + null + 1", null ), DELTA );
+        assertEquals( 4d, ExpressionUtils.evaluateToDouble( "null + 4", null ), DELTA );
+        assertEquals( 5d, ExpressionUtils.evaluateToDouble( "(3 + 2) - null", null ), DELTA );
+        assertEquals( 9d, ExpressionUtils.evaluateToDouble( "(3 + 2) - null + 4 + null", null ), DELTA );
+        assertEquals( 2d, ExpressionUtils.evaluateToDouble( "d2:zing(null) + 2", null ), DELTA );
+        assertEquals( 2d, ExpressionUtils.evaluateToDouble( "d2:oizp(null) + 2", null ), DELTA );
+        assertEquals( 3d, ExpressionUtils.evaluateToDouble( "d2:zpvc(1,null,2,-3,0)", null ), DELTA );
+        assertEquals( 2d, ExpressionUtils.evaluateToDouble( "d2:zpvc(null,null,2,-3,0)", null ), DELTA );
     }
     
     @Test
@@ -96,7 +106,7 @@
             "((d2:zing(4) + d2:zing(0) + d2:zing(-1)) / d2:zpvc(2,0,-1) * 0.25) + " +
             "((d2:zing(8) + d2:zing(0) + d2:zing(-1)) / d2:zpvc(2,0,-1) * 0.75)";
 
-        assertEquals( 3.5, ExpressionUtils.evaluateToDouble( expression, null ), DELTA );        
+        assertEquals( 3.5, ExpressionUtils.evaluateToDouble( expression, null ), DELTA );
     }
 
     @Test
@@ -200,9 +210,9 @@
         assertTrue( ExpressionUtils.isValid( "average(2+1)", null ) );
         
         assertFalse( ExpressionUtils.isValid( "2 a 3", null ) );
-        assertFalse( ExpressionUtils.isValid( "v2 + 3", vars ) );
-        assertFalse( ExpressionUtils.isValid( "4 + abc", vars ) );
-        assertFalse( ExpressionUtils.isValid( "'goat' == goat", null ) );
+        assertFalse( ExpressionUtils.isValid( "4 b", vars ) );
+        assertFalse( ExpressionUtils.isValid( "4 + A", vars ) );
+        assertFalse( ExpressionUtils.isValid( "4 + someunkownvar", vars ) );
         assertFalse( ExpressionUtils.isValid( "aver(2+1)", null ) );
     }