Posted in Salesforce

Salesforce Apex : Evaluate Boolean String Expression

(FALSE AND ((TRUE OR FALSE) AND (TRUE OR FALSE)))

Wondering how to evaluate this expression which is a string using Apex?

Following class can be used to evaluate:

/***************************************************************************************************
* Class Name : BooleanExpression
* Created Date : July 4, 2018
* Author : Vijaya Sankar N
* Description : Class to evaluate a boolean expression
* Example : (TRUE AND (FALSE OR TRUE)) is TRUE
****************************************************************************************************/
public class BooleanExpression {
static Map<String, String> logicTypes = new Map<String, String>();
static Map<String, Map<String, String>> expressionLogic = new Map<String, Map<String, String>>();
/**
* Evaluate a boolean expreassion
*
*/
public static Boolean eval(String expression) {
// If expression contains all TRUE or FALSE
if(expression.containsNone('FALSE')) { return TRUE; }
if(expression.containsNone('TRUE')) { return FALSE; }
fillLogic();
return Boolean.valueOf(evaluateExpression(expression.toUpperCase()));
}
/**
* Evaluate the expression
*
*/
public static String evaluateExpression(String expression) {
for(String logicType : logicTypes.keySet()) {
if(expression.contains(logicType)) {
expression = simplifyExpression(expression, logicTypes.get(logicType));
}
}
if(expression.contains('AND') || expression.contains('OR') || expression.contains('(')) {
expression = evaluateExpression(expression);
}
return expression;
}
/**
* Simplify the expression
*
*/
public static string simplifyExpression(String expression, String LogicType){
Map<String, String> Logic = new Map<String, String>(expressionLogic.get(LogicType));
for(String key : Logic.keySet()) {
expression = expression.replace(key, Logic.get(key));
}
return expression;
}
/**
* Fill AND and OR Logic
*
*/
public static void fillLogic() {
Map<String, String> ANDLogic = new Map<String, String>();
Map<String, String> ORLogic = new Map<String, String>();
Map<String, String> BRACELogic = new Map<String, String>();
logicTypes.put('AND', 'AND');
logicTypes.put('OR', 'OR');
logicTypes.put('(', 'BRACES');
// AND Logic
ANDLogic.put('TRUE AND TRUE', 'TRUE');
ANDLogic.put('TRUE AND FALSE', 'FALSE');
ANDLogic.put('FALSE AND TRUE', 'FALSE');
ANDLogic.put('FALSE AND FALSE', 'FALSE');
expressionLogic.put('AND', ANDLogic);
// OR Logic
ORLogic.put('TRUE OR TRUE', 'TRUE');
ORLogic.put('TRUE OR FALSE', 'TRUE');
ORLogic.put('FALSE OR TRUE', 'TRUE');
ORLogic.put('FALSE OR FALSE', 'FALSE');
expressionLogic.put('OR', ORLogic);
// Braces Logic
BRACELogic.put('(TRUE)', 'TRUE');
BRACELogic.put('(FALSE)', 'FALSE');
expressionLogic.put('BRACES', BRACELogic);
}
}
@isTest
public class BooleanExpression_Test {
@isTest static void eval_test() {
System.assert(BooleanExpression.eval('TRUE'));
System.assert(BooleanExpression.eval('TRUE OR FALSE'));
System.assert(BooleanExpression.eval('TRUE OR TRUE'));
System.assert(BooleanExpression.eval('TRUE OR (TRUE AND FALSE)'));
System.assert(BooleanExpression.eval('TRUE OR (TRUE AND FALSE AND TRUE OR TRUE)'));
System.assert(BooleanExpression.eval('TRUE OR (TRUE AND FALSE AND (TRUE OR FALSE))'));
System.assert(BooleanExpression.eval('TRUE OR (TRUE OR (FALSE AND (TRUE OR FALSE)))'));
System.assert(BooleanExpression.eval('(FALSE OR ((TRUE OR FALSE) AND (TRUE OR FALSE)))'));
System.assert(!BooleanExpression.eval('FALSE'));
System.assert(!BooleanExpression.eval('TRUE AND FALSE'));
System.assert(!BooleanExpression.eval('FALSE AND FALSE'));
System.assert(!BooleanExpression.eval('TRUE AND (TRUE AND FALSE)'));
System.assert(!BooleanExpression.eval('FALSE AND (TRUE AND FALSE AND TRUE OR TRUE)'));
System.assert(!BooleanExpression.eval('TRUE AND (TRUE AND FALSE AND (TRUE OR FALSE))'));
System.assert(!BooleanExpression.eval('TRUE AND (TRUE AND (FALSE AND (TRUE OR FALSE)))'));
System.assert(!BooleanExpression.eval('(FALSE AND ((TRUE OR FALSE) AND (TRUE OR FALSE)))'));
}
}

How to use?

This static method can be called as:


BooleanExpression.eval('(FALSE AND ((TRUE OR FALSE) AND (TRUE OR FALSE)))');

How it evolved?

While replicating the logic of Assignment Rule in Salesforce lightning, needed a way to implement Filter Logic like, 1 AND ( 2 OR 3 ) . Added few more to the above to achieve.

What is added to replicate Filter Logic in Assignment Rule?

Added a method:

/**
 * Substitue numbers in expression with values based on the index	
 * 
 */
public static String substituteValues(String expression, Boolean[] values) {
    String[] arguments = expression.split(' ');
    for(String arg : arguments) {
        try {
            Integer index = Integer.valueOf(arg.replaceAll('[^0-9]', ''));
            expression = expression.replaceAll(String.valueOf(index), String.valueOf(values[index - 1]));    
        } catch(Exception e) {}
    }
    return expression;
}

Call the method in eval(), and added another argument for accepting values as list. Updated eval() method would be:

/**
 * Evaluate a boolean expreassion 
 * 
 */
public static Boolean eval(String expression, Boolean[] values) {
    expression = substituteValues(expression.toUpperCase(), values);
    
    // If expression contains all true or false
    if(expression.containsNone('false')) { return true; }        
    if(expression.containsNone('true')) { return false; }
    
    return Boolean.valueOf(evaluateExpression(expression));
}

To invoke,

List values = new List {
	true, false, true
};

BooleanExpession.eval('1 AND (2 OR 4)', values);

7 thoughts on “Salesforce Apex : Evaluate Boolean String Expression

  1. Hi Vijaya,

    Thanks for the post. It is helpful.
    I found a couple of bugs in the code. So wanted to bring it to your notice.

    1) ‘TRUE’.containsNone(‘FALSE’); // evaluates to false because of the character ‘E’. So, I believe the first two statements of the eval method need to be updated as
    if(expression.containsNone(‘F’) && expression.containsNone(‘f’)) { return TRUE; }
    if(expression.containsNone(‘T’) && expression.containsNone(‘t’)) { return FALSE; }

    2) Also, the logic is failing if additional spaces are added in the Boolean expression. When you try evaluating TRUE OR ( TRUE AND FALSE ) in this way with a space between ‘(‘ and ‘T’ and space between ‘E’ and ‘)’ logic is throwing an exception.

    Thanks,
    Prudhvi

    Like

  2. Thanks for the following post

    Salesforce Apex : Evaluate Boolean String Expression

    But the code has a major bug.

    The following statements are wrong in terms of logic:
    if(expression.containsNone(‘FALSE’)) { return TRUE; }
    if(expression.containsNone(‘TRUE’)) { return FALSE; }
    Because .containsNone() will deal with characters individually and not the string as a whole

    Instead please replace them with:
    Integer falseCount = expression.countMatches(‘FALSE’);
    Integer trueCount = expression.countMatches(‘TRUE’);
    if(falseCount == 0 && trueCount == 0) { return true; }
    if(falseCount == 0 && trueCount > 0 ) { return TRUE; }
    if(trueCount == 0 && falseCount > 0) { return FALSE; }

    Like

  3. Thanks for this. This along with bugs resolved for the char E worked like charm(thsnkd prudhvikonda ), Since I am cleansing the Logic string in page before sending in to the controller , the other bugs didn’t bother me ! cheers.

    Like

Leave a comment