dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #38502
[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()
{