=== 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 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 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 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 ) ); }