← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19613: ExpressionUtils, added custom functions

 

------------------------------------------------------------
revno: 19613
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Fri 2015-07-10 13:32:13 +0200
message:
  ExpressionUtils, added custom functions
added:
  dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/ExpressionFunctions.java
modified:
  dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/OneIfZeroOrPositiveFunction.java
  dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionUtils.java
  dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java
  dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/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
=== added file 'dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/ExpressionFunctions.java'
--- dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/ExpressionFunctions.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/ExpressionFunctions.java	2015-07-10 11:32:13 +0000
@@ -0,0 +1,67 @@
+package org.hisp.dhis.commons.math;
+
+/*
+ * Copyright (c) 2004-2015, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+public class ExpressionFunctions
+{
+    public static final String NAMESPACE = "d2";
+    
+    /**
+     * Function which will return zero if the argument is a negative number.
+     * 
+     * @param value the value.
+     * @return a Double.
+     */
+    public static Double zing( String value )
+    {
+        if ( value == null )
+        {
+            throw new IllegalArgumentException( "Argument is null: " + value );
+        }
+        
+        return Math.max( 0d, Double.valueOf( value ) );
+    }
+
+    /**
+     * Function which will return one if the argument is zero or a positive 
+     * number, and zero if not.
+     * 
+     * @param value the value.
+     * @return a Double.
+     */
+    public static Double oizp( String value )
+    {
+        if ( value == null )
+        {
+            throw new IllegalArgumentException( "Argument is null: " + value );
+        }
+        
+        return ( Double.valueOf( value ) >= 0d ) ? 1d : 0d;
+    }
+}

=== modified file 'dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/OneIfZeroOrPositiveFunction.java'
--- dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/OneIfZeroOrPositiveFunction.java	2015-05-28 18:23:26 +0000
+++ dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/math/OneIfZeroOrPositiveFunction.java	2015-07-10 11:32:13 +0000
@@ -47,6 +47,6 @@
     @Override
     public Double eval( double arg )
     {
-        return ( arg >= 0 ) ? 1d : 0d;
+        return ( arg >= 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-06-15 13:44:20 +0000
+++ dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/ExpressionUtils.java	2015-07-10 11:32:13 +0000
@@ -33,8 +33,11 @@
 import org.apache.commons.jexl2.JexlEngine;
 import org.apache.commons.jexl2.JexlException;
 import org.apache.commons.jexl2.MapContext;
+import org.hisp.dhis.commons.math.ExpressionFunctions;
 
+import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * @author Lars Helge Overland
@@ -43,8 +46,14 @@
 {
     private static final JexlEngine JEXL = new JexlEngine();
 
+    private static final Pattern NUMERIC_PATTERN = Pattern.compile( "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E(-)?\\d+)?$" );
+    
     static 
     {
+        Map<String, Object> functions = new HashMap<>();
+        functions.put( ExpressionFunctions.NAMESPACE, ExpressionFunctions.class );
+        
+        JEXL.setFunctions( functions );
         JEXL.setCache( 512 );
         JEXL.setSilent( false );
     }
@@ -67,6 +76,32 @@
     }
 
     /**
+     * Evaluates the given expression. The given variables will be substituted 
+     * in the expression. Converts the result of the evaluation to a Double.
+     * Throws an IllegalStateException if the result could not be converted to
+     * a Double
+     * 
+     * @param expression the expression.
+     * @param vars the variables, can be null.
+     * @return the result of the evaluation.
+     */
+    public static Double evaluateToDouble( String expression, Map<String, Object> vars )
+    {
+        Expression exp = JEXL.createExpression( expression );
+        
+        JexlContext context = vars != null ? new MapContext( vars ) : new MapContext();
+                
+        Object result = exp.evaluate( context );
+        
+        if ( result == null || !isNumeric( String.valueOf( result ) ) )
+        {
+            throw new IllegalStateException( "Result must be not null and numeric: " + result );
+        }
+        
+        return Double.valueOf( String.valueOf( result ) );
+    }
+
+    /**
      * Evaluates the given expression to true or false. The given variables will 
      * be substituted in the expression.
      * 
@@ -102,4 +137,15 @@
             return false;
         }
     }
+    
+    /**
+     * Indicates whether the given value is numeric.
+     * 
+     * @param value the value.
+     * @return true or false.
+     */
+    public static boolean isNumeric( String value )
+    {
+        return NUMERIC_PATTERN.matcher( value ).matches();
+    }
 }

=== modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java'
--- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java	2015-05-28 18:21:56 +0000
+++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java	2015-07-10 11:32:13 +0000
@@ -28,17 +28,15 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import static org.hisp.dhis.i18n.locale.LocaleManager.DHIS_STANDARD_LOCALE;
-
 import java.math.BigDecimal;
 import java.math.MathContext;
 import java.util.List;
+import java.util.Locale;
 import java.util.Random;
 import java.util.regex.Pattern;
 
 import org.apache.commons.validator.routines.DoubleValidator;
 import org.apache.commons.validator.routines.IntegerValidator;
-import org.hisp.dhis.datavalue.DataValue;
 import org.hisp.dhis.expression.Operator;
 import org.hisp.dhis.commons.math.OneIfZeroOrPositiveFunction;
 import org.hisp.dhis.commons.math.ZeroIfNegativeFunction;
@@ -51,6 +49,8 @@
 {
     public static final Double ZERO = new Double( 0 );
     
+    private static final Locale LOCALE = new Locale( "en" );
+    
     private static DoubleValidator DOUBLE_VALIDATOR = new DoubleValidator();
     private static IntegerValidator INT_VALIDATOR = new IntegerValidator();
     
@@ -334,7 +334,7 @@
      */
     public static boolean isNumeric( String value )
     {
-        return value != null && DOUBLE_VALIDATOR.isValid( value, DHIS_STANDARD_LOCALE ) && NUMERIC_PATTERN.matcher( value ).matches();
+        return value != null && DOUBLE_VALIDATOR.isValid( value, LOCALE ) && NUMERIC_PATTERN.matcher( value ).matches();
     }
 
     /**
@@ -346,7 +346,7 @@
      */
     public static boolean isNumericLenient( String value )
     {
-        return value != null && DOUBLE_VALIDATOR.isValid( value, DHIS_STANDARD_LOCALE ) && NUMERIC_LENIENT_PATTERN.matcher( value ).matches();
+        return value != null && DOUBLE_VALIDATOR.isValid( value, LOCALE ) && NUMERIC_LENIENT_PATTERN.matcher( value ).matches();
     }
     
     /**
@@ -458,7 +458,7 @@
      */
     public static boolean isBool( String value )
     {
-        return value != null && ( value.equals( DataValue.TRUE ) || value.equals( DataValue.FALSE ) );
+        return value != null && ( value.equals( "true" ) || value.equals( "false" ) );
     }
     
     /**

=== modified file 'dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/ExpressionUtilsTest.java'
--- dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/ExpressionUtilsTest.java	2015-06-15 13:44:20 +0000
+++ dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/ExpressionUtilsTest.java	2015-07-10 11:32:13 +0000
@@ -30,6 +30,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -42,6 +43,37 @@
  */
 public class ExpressionUtilsTest
 {
+    private static final double DELTA = 0.01;
+    
+    @Test
+    public void testEvaluateToDouble()
+    {
+        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( 15.6, ExpressionUtils.evaluateToDouble( "12.4 + 3.2", null ), DELTA );        
+        assertEquals( 3d, ExpressionUtils.evaluateToDouble( "d2:zing('3')", null ), DELTA );        
+        assertEquals( 2d, ExpressionUtils.evaluateToDouble( "d2:zing('-3') + 2.0", null ), DELTA );
+        assertEquals( 4d, ExpressionUtils.evaluateToDouble( "d2:zing('-1') + 4 + d2:zing('-2')", null ), DELTA );
+        assertEquals( 0d, ExpressionUtils.evaluateToDouble( "d2:oizp('-4')", null ), DELTA );
+        assertEquals( 1d, ExpressionUtils.evaluateToDouble( "d2:oizp('0')", null ), DELTA );
+        assertEquals( 2d, ExpressionUtils.evaluateToDouble( "d2:oizp('-4') + d2:oizp('0') + d2:oizp('3.0')", null ), DELTA );
+    }
+
+    @Test
+    public void testEvaluateToDoubleWithVars()
+    {
+        Map<String, Object> vars = new HashMap<String, Object>();
+        
+        vars.put( "v1", "4" );
+        vars.put( "v2", "-5" );
+        
+        assertEquals( 7d, ExpressionUtils.evaluateToDouble( "v1 + 3", vars ), DELTA );
+        assertEquals( 4d, ExpressionUtils.evaluateToDouble( "d2:zing(v1)", vars ), DELTA );
+        assertEquals( 0d, ExpressionUtils.evaluateToDouble( "d2:zing(v2)", vars ), DELTA );
+        assertEquals( 4d, ExpressionUtils.evaluateToDouble( "d2:zing(v1) + d2:zing(v2)", vars ), DELTA );
+    }
+    
     @Test
     public void testIsTrue()
     {