IDEMPIERE-4824 Boolean Logic Expression Enhancements (#716)

* IDEMPIERE-4824 Boolean Logic Expression Enhancements

* IDEMPIERE-4824 Boolean Logic Expression Enhancements

Fix handling of conditional variable (i.e @VariableName:DefaultValue@)

* IDEMPIERE-4824 Boolean Logic Expression Enhancements

Fix unit test launch configuration
This commit is contained in:
hengsin 2021-06-10 23:08:22 +08:00 committed by GitHub
parent 2110322149
commit d7153575e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 2308 additions and 203 deletions

View File

@ -45,6 +45,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -56,6 +56,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -55,6 +55,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -55,6 +55,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -55,6 +55,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -5,6 +5,11 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
@ -40,5 +45,6 @@
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
</natures>
</projectDescription>

View File

@ -57,6 +57,7 @@ Export-Package: bsh,
org.idempiere.broadcast,
org.idempiere.cache,
org.idempiere.distributed,
org.idempiere.expression.logic,
org.idempiere.fa.service.api,
org.idempiere.model,
org.idempiere.process
@ -97,6 +98,13 @@ Import-Package: com.google.zxing,
net.sf.cglib.proxy,
net.sourceforge.barbecue,
net.sourceforge.barbecue.linear.ean,
org.antlr.v4.runtime;version="4.9.2",
org.antlr.v4.runtime.atn;version="4.9.2",
org.antlr.v4.runtime.dfa;version="4.9.2",
org.antlr.v4.runtime.misc;version="4.9.2",
org.antlr.v4.runtime.tree;version="4.9.2",
org.antlr.v4.runtime.tree.pattern;version="4.9.2",
org.antlr.v4.runtime.tree.xpath;version="4.9.2",
org.apache.activemq;version="5.3.0",
org.apache.commons.collections;version="3.2.2",
org.apache.commons.collections.keyvalue;version="3.2.2",

View File

@ -0,0 +1,53 @@
grammar SimpleBoolean;
@header {
package org.idempiere.expression.logic;
}
parse
: expression EOF
;
expression
: LPAREN expression RPAREN #parenExpression
| NOT expression #notExpression
| left=expression op=comparator right=expression #comparatorExpression
| left=expression op=binary right=expression #binaryExpression
| bool #boolExpression
| VARIABLE #contextVariables
| QTEXT #quotedText
| DQTEXT #doubleQuotedText
| TEXT #text
| DECIMAL #decimalExpression
;
comparator
: GT | GE | LT | LE | EQ | NE | RE
;
binary
: AND | OR
;
bool
: TRUE | FALSE
;
AND : '&' ;
OR : '|' ;
NOT : '$!';
TRUE : 'true' ;
FALSE : 'false' ;
GT : '>' ;
GE : '>=' ;
LT : '<' ;
LE : '<=' ;
EQ : '=' ;
NE : [!^] ;
RE : '~' ;
LPAREN : '(' ;
RPAREN : ')' ;
DECIMAL : '-'? [0-9]+ ( '.' [0-9]+ )? ;
VARIABLE : '@'(.*?)'@' ;
QTEXT : ['](.*?)['] ;
DQTEXT : ["](.*?)["] ;
TEXT : [a-zA-Z_0-9,]+ ;
WS : [ \r\t\u000C\n]+ -> skip;

View File

@ -43,6 +43,7 @@ import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.idempiere.cache.ImmutableIntPOCache;
import org.idempiere.cache.ImmutablePOSupport;
import org.idempiere.expression.logic.LogicEvaluator;
/**
* Persistent Column Model
@ -545,6 +546,12 @@ public class MColumn extends X_AD_Column implements ImmutablePOSupport
setSeqNoSelection(next);
}
//validate readonly logic expression
if (newRecord || is_ValueChanged(COLUMNNAME_ReadOnlyLogic)) {
if (isActive() && !Util.isEmpty(getReadOnlyLogic(), true) && !getReadOnlyLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getReadOnlyLogic());
}
}
return true;
} // beforeSave

View File

@ -20,8 +20,10 @@ import java.sql.ResultSet;
import java.util.Properties;
import org.compiere.util.Env;
import org.compiere.util.Util;
import org.idempiere.cache.ImmutableIntPOCache;
import org.idempiere.cache.ImmutablePOSupport;
import org.idempiere.expression.logic.LogicEvaluator;
/**
@ -217,6 +219,23 @@ public class MField extends X_AD_Field implements ImmutablePOSupport
setIsToolbarButton(null);
}
//validate logic expression
if (newRecord || is_ValueChanged(COLUMNNAME_ReadOnlyLogic)) {
if (isActive() && !Util.isEmpty(getReadOnlyLogic(), true) && !getReadOnlyLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getReadOnlyLogic());
}
}
if (newRecord || is_ValueChanged(COLUMNNAME_DisplayLogic)) {
if (isActive() && !Util.isEmpty(getDisplayLogic(), true) && !getDisplayLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getDisplayLogic());
}
}
if (newRecord || is_ValueChanged(COLUMNNAME_MandatoryLogic)) {
if (isActive() && !Util.isEmpty(getMandatoryLogic(), true) && !getMandatoryLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getMandatoryLogic());
}
}
return true;
} // beforeSave

View File

@ -23,8 +23,10 @@ import java.util.logging.Level;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Util;
import org.idempiere.cache.ImmutableIntPOCache;
import org.idempiere.cache.ImmutablePOSupport;
import org.idempiere.expression.logic.LogicEvaluator;
/**
@ -333,6 +335,18 @@ public class MProcessPara extends X_AD_Process_Para implements ImmutablePOSuppor
setHelp (element.getHelp());
}
//validate logic expression
if (newRecord || is_ValueChanged(COLUMNNAME_ReadOnlyLogic)) {
if (isActive() && !Util.isEmpty(getReadOnlyLogic(), true) && !getReadOnlyLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getReadOnlyLogic());
}
}
if (newRecord || is_ValueChanged(COLUMNNAME_DisplayLogic)) {
if (isActive() && !Util.isEmpty(getDisplayLogic(), true) && !getDisplayLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getDisplayLogic());
}
}
return true;
} // beforeSave

View File

@ -24,8 +24,10 @@ import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.idempiere.cache.ImmutablePOCache;
import org.idempiere.cache.ImmutablePOSupport;
import org.idempiere.expression.logic.LogicEvaluator;
/**
@ -198,6 +200,24 @@ public class MUserDefField extends X_AD_UserDef_Field implements ImmutablePOSupp
setAD_Val_Rule_ID(0);
setIsToolbarButton(null);
}
//validate logic expression
if (newRecord || is_ValueChanged(COLUMNNAME_ReadOnlyLogic)) {
if (isActive() && !Util.isEmpty(getReadOnlyLogic(), true) && !getReadOnlyLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getReadOnlyLogic());
}
}
if (newRecord || is_ValueChanged(COLUMNNAME_DisplayLogic)) {
if (isActive() && !Util.isEmpty(getDisplayLogic(), true) && !getDisplayLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getDisplayLogic());
}
}
if (newRecord || is_ValueChanged(COLUMNNAME_MandatoryLogic)) {
if (isActive() && !Util.isEmpty(getMandatoryLogic(), true) && !getMandatoryLogic().startsWith("@SQL=")) {
LogicEvaluator.validate(getMandatoryLogic());
}
}
return true;
}

View File

@ -16,17 +16,17 @@
*****************************************************************************/
package org.compiere.util;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.idempiere.expression.logic.LogicEvaluator;
/**
* Expression Evaluator
@ -82,202 +82,16 @@ public class Evaluator
/**
* Evaluate Logic.
* <code>
* format := <expression> [<logic> <expression>]
* expression := @<context>@<exLogic><value>
* logic := <|> | <&>
* exLogic := <=> | <!> | <^> | <<> | <>>
*
* context := any global or window context
* value := strings can be with ' or "
* logic operators := AND or OR with the prevoius result from left to right
*
* Example '@AD_Table@=Test | @Language@=GERGER
* </code>
* @see LogicEvaluator#evaluateLogic(Evaluatee, String)
* @param source class implementing get_ValueAsString(variable)
* @param logic logic string
* @return logic result
*/
public static boolean evaluateLogic (Evaluatee source, String logic)
{
// Conditional
StringTokenizer st = new StringTokenizer(logic.trim(), "&|", true);
int it = st.countTokens();
if (((it/2) - ((it+1)/2)) == 0) // only uneven arguments
{
s_log.severe ("Logic does not comply with format "
+ "'<expression> [<logic> <expression>]' => " + logic);
return false;
}
String exprStrand = st.nextToken().trim();
if (exprStrand.matches("^@\\d+$") || "@P".equals(exprStrand))
{
exprStrand = exprStrand.concat(st.nextToken());
exprStrand = exprStrand.concat(st.nextToken());
}
//boolean retValue = evaluateLogicTuple(source, st.nextToken());
boolean retValue = evaluateLogicTuple(source, exprStrand);
while (st.hasMoreTokens())
{
String logOp = st.nextToken().trim();
//boolean temp = evaluateLogicTuple(source, st.nextToken());
exprStrand = st.nextToken().trim();
if (exprStrand.matches("^@\\d+$") || "@P".equals(exprStrand))
{
exprStrand = exprStrand.concat(st.nextToken());
exprStrand = exprStrand.concat(st.nextToken());
}
boolean temp = evaluateLogicTuple(source, exprStrand);
if (logOp.equals("&"))
retValue = retValue & temp;
else if (logOp.equals("|"))
retValue = retValue | temp;
else
{
s_log.log(Level.SEVERE, "Logic operand '|' or '&' expected => " + logic);
return false;
}
} // hasMoreTokens
return retValue;
return LogicEvaluator.evaluateLogic(source, logic);
} // evaluateLogic
/**
* Evaluate @context@=value or @context@!value or @context@^value.
* <pre>
* value: strips ' and " always (no escape or mid stream)
* value: can also be a context variable
* </pre>
* @param source class implementing get_ValueAsString(variable)
* @param logic logic tuple
* @return true or false
*/
private static boolean evaluateLogicTuple (Evaluatee source, String logic)
{
StringTokenizer st = new StringTokenizer (logic.trim(), "!=^><", true);
if (st.countTokens() != 3)
{
s_log.log(Level.SEVERE, "Logic tuple does not comply with format "
+ "'@context@=value' where operand could be one of '=!^><' => " + logic);
return false;
}
// First Part
String first = st.nextToken().trim(); // get '@tag@'
String firstEval = first.trim();
if (first.indexOf('@') != -1) // variable
{
first = first.replace ('@', ' ').trim (); // strip 'tag'
// IDEMPIERE-194 Handling null context variable
String defaultValue = "";
int idx = first.indexOf(":"); // or clause
if (idx >= 0)
{
defaultValue = first.substring(idx+1, first.length());
first = first.substring(0, idx);
}
firstEval = source.get_ValueAsString (first); // replace with it's value
if (Util.isEmpty(firstEval) && !Util.isEmpty(defaultValue)) {
firstEval = defaultValue;
}
}
//NPE sanity check
if (firstEval == null)
firstEval = "";
firstEval = firstEval.replace('\'', ' ').replace('"', ' ').trim(); // strip ' and "
// Comperator
String operand = st.nextToken();
// Second Part
String rightToken = st.nextToken(); // get value
String[] list = rightToken.split("[,]");
for(String second : list)
{
String secondEval = second.trim();
if (second.indexOf('@') != -1) // variable
{
second = second.replace('@', ' ').trim(); // strip tag
secondEval = source.get_ValueAsString (second); // replace with it's value
}
secondEval = secondEval.replace('\'', ' ').replace('"', ' ').trim(); // strip ' and "
// Handling of ID compare (null => 0)
if (first.trim().endsWith("_ID") && firstEval.length() == 0)
firstEval = "0";
if (second.trim().endsWith("_ID") && secondEval.length() == 0)
secondEval = "0";
// Logical Comparison
boolean result = evaluateLogicTuple (firstEval, operand, secondEval);
//
if (s_log.isLoggable(Level.FINEST)) s_log.finest(logic
+ " => \"" + firstEval + "\" " + operand + " \"" + secondEval + "\" => " + result);
if (result)
return true;
}
//
return false;
} // evaluateLogicTuple
/**
* Evaluate Logic Tuple
* @param value1 value
* @param operand operand = ~ ^ ! > <
* @param value2
* @return evaluation
*/
private static boolean evaluateLogicTuple (String value1, String operand, String value2)
{
if (value1 == null || operand == null || value2 == null)
return false;
BigDecimal value1bd = null;
BigDecimal value2bd = null;
try
{
if (!value1.startsWith("'"))
value1bd = new BigDecimal (value1);
if (!value2.startsWith("'"))
value2bd = new BigDecimal (value2);
}
catch (Exception e)
{
value1bd = null;
value2bd = null;
}
//
if (operand.equals("="))
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) == 0;
return value1.compareTo(value2) == 0;
}
else if (operand.equals("<"))
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) < 0;
return value1.compareTo(value2) < 0;
}
else if (operand.equals(">"))
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) > 0;
return value1.compareTo(value2) > 0;
}
else // interpreted as not
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) != 0;
return value1.compareTo(value2) != 0;
}
} // evaluateLogicTuple
/**
* Parse String and add variables with @ to the list.
* @param list list to be added to

View File

@ -0,0 +1,234 @@
/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.util;
import java.math.BigDecimal;
import java.util.StringTokenizer;
import java.util.logging.Level;
/**
* Legacy Logic Expression Evaluator
*
* @author Jorg Janke
* @version $Id: Evaluator.java,v 1.3 2006/07/30 00:54:36 jjanke Exp $
* @deprecated
*/
public final class LegacyLogicEvaluator {
private final static CLogger s_log = CLogger.getCLogger(LegacyLogicEvaluator.class);
private LegacyLogicEvaluator() {
}
/**
* Evaluate Logic.
* <code>
* format := <expression> [<logic> <expression>]
* expression := @<context>@<exLogic><value>
* logic := <|> | <&>
* exLogic := <=> | <!> | <^> | <<> | <>>
*
* context := any global or window context
* value := strings can be with ' or "
* logic operators := AND or OR with the prevoius result from left to right
*
* Example '@AD_Table@=Test | @Language@=GERGER
* </code>
* @param source class implementing get_ValueAsString(variable)
* @param logic logic string
* @return logic result
*/
public static boolean evaluateLogic (Evaluatee source, String logic)
{
// Conditional
StringTokenizer st = new StringTokenizer(logic.trim(), "&|", true);
int it = st.countTokens();
if (((it/2) - ((it+1)/2)) == 0) // only uneven arguments
{
s_log.severe ("Logic does not comply with format "
+ "'<expression> [<logic> <expression>]' => " + logic);
return false;
}
String exprStrand = st.nextToken().trim();
if (exprStrand.matches("^@\\d+$") || "@P".equals(exprStrand))
{
exprStrand = exprStrand.concat(st.nextToken());
exprStrand = exprStrand.concat(st.nextToken());
}
//boolean retValue = evaluateLogicTuple(source, st.nextToken());
boolean retValue = evaluateLogicTuple(source, exprStrand);
while (st.hasMoreTokens())
{
String logOp = st.nextToken().trim();
//boolean temp = evaluateLogicTuple(source, st.nextToken());
exprStrand = st.nextToken().trim();
if (exprStrand.matches("^@\\d+$") || "@P".equals(exprStrand))
{
exprStrand = exprStrand.concat(st.nextToken());
exprStrand = exprStrand.concat(st.nextToken());
}
boolean temp = evaluateLogicTuple(source, exprStrand);
if (logOp.equals("&"))
retValue = retValue & temp;
else if (logOp.equals("|"))
retValue = retValue | temp;
else
{
s_log.log(Level.SEVERE, "Logic operand '|' or '&' expected => " + logic);
return false;
}
} // hasMoreTokens
return retValue;
} // evaluateLogic
/**
* Evaluate @context@=value or @context@!value or @context@^value.
* <pre>
* value: strips ' and " always (no escape or mid stream)
* value: can also be a context variable
* </pre>
* @param source class implementing get_ValueAsString(variable)
* @param logic logic tuple
* @return true or false
*/
private static boolean evaluateLogicTuple (Evaluatee source, String logic)
{
StringTokenizer st = new StringTokenizer (logic.trim(), "!=^><", true);
if (st.countTokens() != 3)
{
s_log.log(Level.SEVERE, "Logic tuple does not comply with format "
+ "'@context@=value' where operand could be one of '=!^><' => " + logic);
return false;
}
// First Part
String first = st.nextToken().trim(); // get '@tag@'
String firstEval = first.trim();
if (first.indexOf('@') != -1) // variable
{
first = first.replace ('@', ' ').trim (); // strip 'tag'
// IDEMPIERE-194 Handling null context variable
String defaultValue = "";
int idx = first.indexOf(":"); // or clause
if (idx >= 0)
{
defaultValue = first.substring(idx+1, first.length());
first = first.substring(0, idx);
}
firstEval = source.get_ValueAsString (first); // replace with it's value
if (Util.isEmpty(firstEval) && !Util.isEmpty(defaultValue)) {
firstEval = defaultValue;
}
}
//NPE sanity check
if (firstEval == null)
firstEval = "";
firstEval = firstEval.replace('\'', ' ').replace('"', ' ').trim(); // strip ' and "
// Comperator
String operand = st.nextToken();
// Second Part
String rightToken = st.nextToken(); // get value
String[] list = rightToken.split("[,]");
for(String second : list)
{
String secondEval = second.trim();
if (second.indexOf('@') != -1) // variable
{
second = second.replace('@', ' ').trim(); // strip tag
secondEval = source.get_ValueAsString (second); // replace with it's value
}
secondEval = secondEval.replace('\'', ' ').replace('"', ' ').trim(); // strip ' and "
// Handling of ID compare (null => 0)
if (first.trim().endsWith("_ID") && firstEval.length() == 0)
firstEval = "0";
if (second.trim().endsWith("_ID") && secondEval.length() == 0)
secondEval = "0";
// Logical Comparison
boolean result = evaluateLogicTuple (firstEval, operand, secondEval);
//
if (s_log.isLoggable(Level.FINEST)) s_log.finest(logic
+ " => \"" + firstEval + "\" " + operand + " \"" + secondEval + "\" => " + result);
if (result)
return true;
}
//
return false;
} // evaluateLogicTuple
/**
* Evaluate Logic Tuple
* @param value1 value
* @param operand operand = ~ ^ ! > <
* @param value2
* @return evaluation
*/
private static boolean evaluateLogicTuple (String value1, String operand, String value2)
{
if (value1 == null || operand == null || value2 == null)
return false;
BigDecimal value1bd = null;
BigDecimal value2bd = null;
try
{
if (!value1.startsWith("'"))
value1bd = new BigDecimal (value1);
if (!value2.startsWith("'"))
value2bd = new BigDecimal (value2);
}
catch (Exception e)
{
value1bd = null;
value2bd = null;
}
//
if (operand.equals("="))
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) == 0;
return value1.compareTo(value2) == 0;
}
else if (operand.equals("<"))
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) < 0;
return value1.compareTo(value2) < 0;
}
else if (operand.equals(">"))
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) > 0;
return value1.compareTo(value2) > 0;
}
else // interpreted as not
{
if (value1bd != null && value2bd != null)
return value1bd.compareTo(value2bd) != 0;
return value1.compareTo(value2) != 0;
}
} // evaluateLogicTuple
}

View File

@ -0,0 +1,227 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.expression.logic;
import java.math.BigDecimal;
import java.util.regex.Pattern;
import org.compiere.util.Evaluatee;
import org.compiere.util.Util;
import org.idempiere.expression.logic.SimpleBooleanParser.BinaryContext;
import org.idempiere.expression.logic.SimpleBooleanParser.BoolContext;
import org.idempiere.expression.logic.SimpleBooleanParser.ComparatorContext;
import org.idempiere.expression.logic.SimpleBooleanParser.ComparatorExpressionContext;
import org.idempiere.expression.logic.SimpleBooleanParser.ContextVariablesContext;
import org.idempiere.expression.logic.SimpleBooleanParser.DoubleQuotedTextContext;
import org.idempiere.expression.logic.SimpleBooleanParser.QuotedTextContext;
import org.idempiere.expression.logic.SimpleBooleanParser.TextContext;
/**
*
* @author hengsin
*
*/
public class EvaluationVisitor extends SimpleBooleanBaseVisitor<Object> {
private final Evaluatee evaluatee;
public EvaluationVisitor(Evaluatee evaluatee) {
this.evaluatee = evaluatee;
}
@Override
public Object visitParse(SimpleBooleanParser.ParseContext ctx) {
return super.visit(ctx.expression());
}
@Override
public Object visitDecimalExpression(SimpleBooleanParser.DecimalExpressionContext ctx) {
return new BigDecimal(ctx.DECIMAL().getText());
}
@Override
public Object visitQuotedText(QuotedTextContext ctx) {
return ctx.QTEXT().getText().replaceAll("[']", "");
}
@Override
public Object visitDoubleQuotedText(DoubleQuotedTextContext ctx) {
return ctx.DQTEXT().getText().replaceAll("[\"]", "");
}
@Override
public Object visitText(TextContext ctx) {
return ctx.TEXT().getText();
}
@Override
public Object visitNotExpression(SimpleBooleanParser.NotExpressionContext ctx) {
return !((Boolean) this.visit(ctx.expression()));
}
@Override
public Object visitParenExpression(SimpleBooleanParser.ParenExpressionContext ctx) {
return super.visit(ctx.expression());
}
@Override
public Object visitComparatorExpression(SimpleBooleanParser.ComparatorExpressionContext ctx) {
if (ctx.op.EQ() != null) {
return isEqual(ctx);
} else if (ctx.op.LE() != null) {
return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) <= 0;
} else if (ctx.op.GE() != null) {
return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) >= 0;
} else if (ctx.op.LT() != null) {
return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) < 0;
} else if (ctx.op.GT() != null) {
return asBigDecimal(ctx.left).compareTo(asBigDecimal(ctx.right)) > 0;
} else if (ctx.op.NE() != null) {
return !(isEqual(ctx));
} else if (ctx.op.RE() != null) {
return regularExpressionMatch(ctx);
}
throw new RuntimeException("not implemented: comparator operator " + ctx.op.getText());
}
//input ~ regex
private Boolean regularExpressionMatch(ComparatorExpressionContext ctx) {
String left = this.visit(ctx.left).toString();
String right = this.visit(ctx.right).toString();
return Pattern.matches(right, left);
}
private Boolean isEqual(SimpleBooleanParser.ComparatorExpressionContext ctx) {
Object left = this.visit(ctx.left);
Object right = this.visit(ctx.right);
if (left instanceof String && right instanceof String && !(ctx.right.getText().startsWith("'") && !(ctx.right.getText().startsWith("\"")))) {
String rightText = (String) right;
if (rightText.indexOf(",") > 0) {
return isIn((String)left, rightText);
}
}
if (left instanceof BigDecimal && right instanceof BigDecimal) {
return ((BigDecimal)left).compareTo((BigDecimal) right) == 0;
} else {
String leftStr = left.toString();
if (left instanceof BigDecimal) {
if (((BigDecimal)left).stripTrailingZeros().scale() <= 0) {
leftStr = Integer.toString(((BigDecimal)left).intValue());
}
}
String rightStr = right.toString();
if (right instanceof BigDecimal) {
if (((BigDecimal)right).stripTrailingZeros().scale() <= 0) {
rightStr = Integer.toString(((BigDecimal)right).intValue());
}
}
return leftStr.equals(rightStr);
}
}
private Boolean isIn(String left, String rightText) {
String[] values = rightText.split("[,]");
for(String s : values) {
s = s.trim();
if ((s.startsWith("'") && s.endsWith("'")) || (s.startsWith("\"") && s.endsWith("\"")))
s = s.substring(1, s.length()-1);
if (left.equals(s))
return Boolean.TRUE;
}
return Boolean.FALSE;
}
@Override
public Object visitBinaryExpression(SimpleBooleanParser.BinaryExpressionContext ctx) {
if (ctx.op.AND() != null) {
return asBoolean(ctx.left) && asBoolean(ctx.right);
} else if (ctx.op.OR() != null) {
return asBoolean(ctx.left) || asBoolean(ctx.right);
}
throw new RuntimeException("not implemented: binary operator " + ctx.op.getText());
}
@Override
public Object visitBoolExpression(SimpleBooleanParser.BoolExpressionContext ctx) {
return Boolean.valueOf(ctx.getText());
}
@Override
public Object visitComparator(ComparatorContext ctx) {
return super.visitComparator(ctx);
}
@Override
public Object visitBool(BoolContext ctx) {
return super.visitBool(ctx);
}
@Override
public Object visitBinary(BinaryContext ctx) {
return super.visitBinary(ctx);
}
@Override
public Object visitContextVariables(ContextVariablesContext ctx) {
String context = ctx.getText().substring(1, ctx.getText().length()-1);
// IDEMPIERE-194 Handling null context variable
String defaultValue = "";
int idx = context.indexOf(":"); // or clause
if (idx >= 0)
{
defaultValue = context.substring(idx+1, context.length());
context = context.substring(0, idx);
}
String value = evaluatee.get_ValueAsString(context);
if (Util.isEmpty(value) && !Util.isEmpty(defaultValue)) {
value = defaultValue;
}
if (value == null)
value = "";
if (Util.isEmpty(value, true) && context.endsWith("_ID"))
return "0";
else
return value;
}
private boolean asBoolean(SimpleBooleanParser.ExpressionContext ctx) {
return (boolean) visit(ctx);
}
private BigDecimal asBigDecimal(SimpleBooleanParser.ExpressionContext ctx) {
Object value = visit(ctx);
if (value instanceof String)
return new BigDecimal((String)value);
else if (value instanceof BigDecimal)
return (BigDecimal)value;
else
return new BigDecimal(value.toString());
}
}

View File

@ -0,0 +1,100 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.expression.logic;
import java.util.logging.Level;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.compiere.util.CLogger;
import org.compiere.util.Evaluatee;
/**
*
* @author hengsin
*
*/
public final class LogicEvaluator {
private final static CLogger s_log = CLogger.getCLogger(LogicEvaluator.class);
private LogicEvaluator() {
}
/**
* Evaluate Logic.
* {@code
* format: <negate>(<expression> [<logic> <expression>]).
* <negate>: $!.
* <expression>: @<context>@<comparison><value>.
* <logic>: | or & (Example '@AD_Table@=Test | @Language@=GERGER).
* <comparison>: = | ! | ^ | < | > | <= | >= | ~ (Equal, Not Equal, Not Equal, Less Than,
* Greater Than, Less Than or Equal, Greater Than or Equal, Regular Expression Match).
* <Regular Expression Match>: <input string> ~ '<regular expression>'.
* <List>: value1,value2,value3 (Example '@CalculationType@=A,R,S').
* <context>: any global or window context.
* <value>: strings can be with ' or ".
* <logic operators>: AND or OR with the previous result from left to right.
* <()>: override the default left to right evaluation order (Example '@GrandTotal@=0 |(@GrandTotal@>0 & @PaymentRule@=X)").
*
* }
* @param source class implementing get_ValueAsString(variable)
* @param logic logic string
* @return logic result
*/
public static boolean evaluateLogic (Evaluatee source, String logic) {
SimpleBooleanLexer lexer = new SimpleBooleanLexer(CharStreams.fromString(logic));
SimpleBooleanParser parser = new SimpleBooleanParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.addErrorListener(ThrowingErrorListener.INSTANCE);
try {
Object result = new EvaluationVisitor(source).visit(parser.parse());
if (result != null && result instanceof Boolean) {
return (boolean) result;
} else {
s_log.severe ("Logic does not comply with format "
+ "'<expression> [<logic> <expression>]' => " + logic);
return false;
}
} catch (ParseCancellationException e) {
s_log.log(Level.SEVERE, "Logic="+logic+" Error="+e.getMessage(), e);
return false;
}
}
/**
* Throw exception if logic is not valid
* @param logic
* @throws ParseCancellationException
*/
public static void validate(String logic) {
SimpleBooleanLexer lexer = new SimpleBooleanLexer(CharStreams.fromString(logic));
SimpleBooleanParser parser = new SimpleBooleanParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.addErrorListener(ThrowingErrorListener.INSTANCE);
parser.parse();
}
}

View File

@ -0,0 +1,56 @@
token literal names:
null
'&'
'|'
'$!'
'true'
'false'
'>'
'>='
'<'
'<='
'='
null
'~'
'('
')'
null
null
null
null
null
null
token symbolic names:
null
AND
OR
NOT
TRUE
FALSE
GT
GE
LT
LE
EQ
NE
RE
LPAREN
RPAREN
DECIMAL
VARIABLE
QTEXT
DQTEXT
TEXT
WS
rule names:
parse
expression
comparator
binary
bool
atn:
[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 22, 50, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 29, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 39, 10, 3, 12, 3, 14, 3, 42, 11, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 2, 3, 4, 7, 2, 4, 6, 8, 10, 2, 5, 3, 2, 8, 14, 3, 2, 3, 4, 3, 2, 6, 7, 2, 53, 2, 12, 3, 2, 2, 2, 4, 28, 3, 2, 2, 2, 6, 43, 3, 2, 2, 2, 8, 45, 3, 2, 2, 2, 10, 47, 3, 2, 2, 2, 12, 13, 5, 4, 3, 2, 13, 14, 7, 2, 2, 3, 14, 3, 3, 2, 2, 2, 15, 16, 8, 3, 1, 2, 16, 17, 7, 15, 2, 2, 17, 18, 5, 4, 3, 2, 18, 19, 7, 16, 2, 2, 19, 29, 3, 2, 2, 2, 20, 21, 7, 5, 2, 2, 21, 29, 5, 4, 3, 11, 22, 29, 5, 10, 6, 2, 23, 29, 7, 18, 2, 2, 24, 29, 7, 19, 2, 2, 25, 29, 7, 20, 2, 2, 26, 29, 7, 21, 2, 2, 27, 29, 7, 17, 2, 2, 28, 15, 3, 2, 2, 2, 28, 20, 3, 2, 2, 2, 28, 22, 3, 2, 2, 2, 28, 23, 3, 2, 2, 2, 28, 24, 3, 2, 2, 2, 28, 25, 3, 2, 2, 2, 28, 26, 3, 2, 2, 2, 28, 27, 3, 2, 2, 2, 29, 40, 3, 2, 2, 2, 30, 31, 12, 10, 2, 2, 31, 32, 5, 6, 4, 2, 32, 33, 5, 4, 3, 11, 33, 39, 3, 2, 2, 2, 34, 35, 12, 9, 2, 2, 35, 36, 5, 8, 5, 2, 36, 37, 5, 4, 3, 10, 37, 39, 3, 2, 2, 2, 38, 30, 3, 2, 2, 2, 38, 34, 3, 2, 2, 2, 39, 42, 3, 2, 2, 2, 40, 38, 3, 2, 2, 2, 40, 41, 3, 2, 2, 2, 41, 5, 3, 2, 2, 2, 42, 40, 3, 2, 2, 2, 43, 44, 9, 2, 2, 2, 44, 7, 3, 2, 2, 2, 45, 46, 9, 3, 2, 2, 46, 9, 3, 2, 2, 2, 47, 48, 9, 4, 2, 2, 48, 11, 3, 2, 2, 2, 5, 28, 38, 40]

View File

@ -0,0 +1,33 @@
QTEXT=17
OR=2
LT=8
LPAREN=13
TRUE=4
DECIMAL=15
TEXT=19
RPAREN=14
EQ=10
GT=6
NOT=3
RE=12
VARIABLE=16
AND=1
NE=11
LE=9
FALSE=5
DQTEXT=18
WS=20
GE=7
'false'=5
')'=14
'('=13
'&'=1
'<='=9
'>'=6
'~'=12
'$!'=3
'='=10
'|'=2
'<'=8
'>='=7
'true'=4

View File

@ -0,0 +1,115 @@
// Generated from SimpleBoolean.g4 by ANTLR 4.4
package org.idempiere.expression.logic;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
/**
* This class provides an empty implementation of {@link SimpleBooleanVisitor},
* which can be extended to create a visitor which only needs to handle a subset
* of the available methods.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public class SimpleBooleanBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SimpleBooleanVisitor<T> {
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitBoolExpression(@NotNull SimpleBooleanParser.BoolExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitBool(@NotNull SimpleBooleanParser.BoolContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitNotExpression(@NotNull SimpleBooleanParser.NotExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitParse(@NotNull SimpleBooleanParser.ParseContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitParenExpression(@NotNull SimpleBooleanParser.ParenExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitDoubleQuotedText(@NotNull SimpleBooleanParser.DoubleQuotedTextContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitBinaryExpression(@NotNull SimpleBooleanParser.BinaryExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitComparator(@NotNull SimpleBooleanParser.ComparatorContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitDecimalExpression(@NotNull SimpleBooleanParser.DecimalExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitBinary(@NotNull SimpleBooleanParser.BinaryContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitContextVariables(@NotNull SimpleBooleanParser.ContextVariablesContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitText(@NotNull SimpleBooleanParser.TextContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitQuotedText(@NotNull SimpleBooleanParser.QuotedTextContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitComparatorExpression(@NotNull SimpleBooleanParser.ComparatorExpressionContext ctx) { return visitChildren(ctx); }
}

View File

@ -0,0 +1,77 @@
token literal names:
null
'&'
'|'
'$!'
'true'
'false'
'>'
'>='
'<'
'<='
'='
null
'~'
'('
')'
null
null
null
null
null
null
token symbolic names:
null
AND
OR
NOT
TRUE
FALSE
GT
GE
LT
LE
EQ
NE
RE
LPAREN
RPAREN
DECIMAL
VARIABLE
QTEXT
DQTEXT
TEXT
WS
rule names:
AND
OR
NOT
TRUE
FALSE
GT
GE
LT
LE
EQ
NE
RE
LPAREN
RPAREN
DECIMAL
VARIABLE
QTEXT
DQTEXT
TEXT
WS
channel names:
DEFAULT_TOKEN_CHANNEL
HIDDEN
mode names:
DEFAULT_MODE
atn:
[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 22, 136, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 5, 16, 83, 10, 16, 3, 16, 6, 16, 86, 10, 16, 13, 16, 14, 16, 87, 3, 16, 3, 16, 6, 16, 92, 10, 16, 13, 16, 14, 16, 93, 5, 16, 96, 10, 16, 3, 17, 3, 17, 7, 17, 100, 10, 17, 12, 17, 14, 17, 103, 11, 17, 3, 17, 3, 17, 3, 18, 3, 18, 7, 18, 109, 10, 18, 12, 18, 14, 18, 112, 11, 18, 3, 18, 3, 18, 3, 19, 3, 19, 7, 19, 118, 10, 19, 12, 19, 14, 19, 121, 11, 19, 3, 19, 3, 19, 3, 20, 6, 20, 126, 10, 20, 13, 20, 14, 20, 127, 3, 21, 6, 21, 131, 10, 21, 13, 21, 14, 21, 132, 3, 21, 3, 21, 4, 110, 119, 2, 22, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 3, 2, 9, 4, 2, 35, 35, 96, 96, 3, 2, 50, 59, 8, 2, 37, 38, 50, 59, 67, 92, 97, 97, 99, 124, 128, 128, 3, 2, 41, 41, 3, 2, 36, 36, 7, 2, 46, 46, 50, 59, 67, 92, 97, 97, 99, 124, 5, 2, 11, 12, 14, 15, 34, 34, 2, 144, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 3, 43, 3, 2, 2, 2, 5, 45, 3, 2, 2, 2, 7, 47, 3, 2, 2, 2, 9, 50, 3, 2, 2, 2, 11, 55, 3, 2, 2, 2, 13, 61, 3, 2, 2, 2, 15, 63, 3, 2, 2, 2, 17, 66, 3, 2, 2, 2, 19, 68, 3, 2, 2, 2, 21, 71, 3, 2, 2, 2, 23, 73, 3, 2, 2, 2, 25, 75, 3, 2, 2, 2, 27, 77, 3, 2, 2, 2, 29, 79, 3, 2, 2, 2, 31, 82, 3, 2, 2, 2, 33, 97, 3, 2, 2, 2, 35, 106, 3, 2, 2, 2, 37, 115, 3, 2, 2, 2, 39, 125, 3, 2, 2, 2, 41, 130, 3, 2, 2, 2, 43, 44, 7, 40, 2, 2, 44, 4, 3, 2, 2, 2, 45, 46, 7, 126, 2, 2, 46, 6, 3, 2, 2, 2, 47, 48, 7, 38, 2, 2, 48, 49, 7, 35, 2, 2, 49, 8, 3, 2, 2, 2, 50, 51, 7, 118, 2, 2, 51, 52, 7, 116, 2, 2, 52, 53, 7, 119, 2, 2, 53, 54, 7, 103, 2, 2, 54, 10, 3, 2, 2, 2, 55, 56, 7, 104, 2, 2, 56, 57, 7, 99, 2, 2, 57, 58, 7, 110, 2, 2, 58, 59, 7, 117, 2, 2, 59, 60, 7, 103, 2, 2, 60, 12, 3, 2, 2, 2, 61, 62, 7, 64, 2, 2, 62, 14, 3, 2, 2, 2, 63, 64, 7, 64, 2, 2, 64, 65, 7, 63, 2, 2, 65, 16, 3, 2, 2, 2, 66, 67, 7, 62, 2, 2, 67, 18, 3, 2, 2, 2, 68, 69, 7, 62, 2, 2, 69, 70, 7, 63, 2, 2, 70, 20, 3, 2, 2, 2, 71, 72, 7, 63, 2, 2, 72, 22, 3, 2, 2, 2, 73, 74, 9, 2, 2, 2, 74, 24, 3, 2, 2, 2, 75, 76, 7, 128, 2, 2, 76, 26, 3, 2, 2, 2, 77, 78, 7, 42, 2, 2, 78, 28, 3, 2, 2, 2, 79, 80, 7, 43, 2, 2, 80, 30, 3, 2, 2, 2, 81, 83, 7, 47, 2, 2, 82, 81, 3, 2, 2, 2, 82, 83, 3, 2, 2, 2, 83, 85, 3, 2, 2, 2, 84, 86, 9, 3, 2, 2, 85, 84, 3, 2, 2, 2, 86, 87, 3, 2, 2, 2, 87, 85, 3, 2, 2, 2, 87, 88, 3, 2, 2, 2, 88, 95, 3, 2, 2, 2, 89, 91, 7, 48, 2, 2, 90, 92, 9, 3, 2, 2, 91, 90, 3, 2, 2, 2, 92, 93, 3, 2, 2, 2, 93, 91, 3, 2, 2, 2, 93, 94, 3, 2, 2, 2, 94, 96, 3, 2, 2, 2, 95, 89, 3, 2, 2, 2, 95, 96, 3, 2, 2, 2, 96, 32, 3, 2, 2, 2, 97, 101, 7, 66, 2, 2, 98, 100, 9, 4, 2, 2, 99, 98, 3, 2, 2, 2, 100, 103, 3, 2, 2, 2, 101, 99, 3, 2, 2, 2, 101, 102, 3, 2, 2, 2, 102, 104, 3, 2, 2, 2, 103, 101, 3, 2, 2, 2, 104, 105, 7, 66, 2, 2, 105, 34, 3, 2, 2, 2, 106, 110, 9, 5, 2, 2, 107, 109, 11, 2, 2, 2, 108, 107, 3, 2, 2, 2, 109, 112, 3, 2, 2, 2, 110, 111, 3, 2, 2, 2, 110, 108, 3, 2, 2, 2, 111, 113, 3, 2, 2, 2, 112, 110, 3, 2, 2, 2, 113, 114, 9, 5, 2, 2, 114, 36, 3, 2, 2, 2, 115, 119, 9, 6, 2, 2, 116, 118, 11, 2, 2, 2, 117, 116, 3, 2, 2, 2, 118, 121, 3, 2, 2, 2, 119, 120, 3, 2, 2, 2, 119, 117, 3, 2, 2, 2, 120, 122, 3, 2, 2, 2, 121, 119, 3, 2, 2, 2, 122, 123, 9, 6, 2, 2, 123, 38, 3, 2, 2, 2, 124, 126, 9, 7, 2, 2, 125, 124, 3, 2, 2, 2, 126, 127, 3, 2, 2, 2, 127, 125, 3, 2, 2, 2, 127, 128, 3, 2, 2, 2, 128, 40, 3, 2, 2, 2, 129, 131, 9, 8, 2, 2, 130, 129, 3, 2, 2, 2, 131, 132, 3, 2, 2, 2, 132, 130, 3, 2, 2, 2, 132, 133, 3, 2, 2, 2, 133, 134, 3, 2, 2, 2, 134, 135, 8, 21, 2, 2, 135, 42, 3, 2, 2, 2, 12, 2, 82, 87, 93, 95, 101, 110, 119, 127, 132, 3, 8, 2, 2]

View File

@ -0,0 +1,111 @@
// Generated from SimpleBoolean.g4 by ANTLR 4.4
package org.idempiere.expression.logic;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class SimpleBooleanLexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.4", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
AND=1, OR=2, NOT=3, TRUE=4, FALSE=5, GT=6, GE=7, LT=8, LE=9, EQ=10, NE=11,
RE=12, LPAREN=13, RPAREN=14, DECIMAL=15, VARIABLE=16, QTEXT=17, DQTEXT=18,
TEXT=19, WS=20;
public static String[] modeNames = {
"DEFAULT_MODE"
};
public static final String[] tokenNames = {
"'\\u0000'", "'\\u0001'", "'\\u0002'", "'\\u0003'", "'\\u0004'", "'\\u0005'",
"'\\u0006'", "'\\u0007'", "'\b'", "'\t'", "'\n'", "'\\u000B'", "'\f'",
"'\r'", "'\\u000E'", "'\\u000F'", "'\\u0010'", "'\\u0011'", "'\\u0012'",
"'\\u0013'", "'\\u0014'"
};
public static final String[] ruleNames = {
"AND", "OR", "NOT", "TRUE", "FALSE", "GT", "GE", "LT", "LE", "EQ", "NE",
"RE", "LPAREN", "RPAREN", "DECIMAL", "VARIABLE", "QTEXT", "DQTEXT", "TEXT",
"WS"
};
public SimpleBooleanLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
@Override
public String getGrammarFileName() { return "SimpleBoolean.g4"; }
@Override
public String[] getTokenNames() { return tokenNames; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\26\u0088\b\1\4\2"+
"\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4"+
"\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22"+
"\t\22\4\23\t\23\4\24\t\24\4\25\t\25\3\2\3\2\3\3\3\3\3\4\3\4\3\4\3\5\3"+
"\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\b\3\b\3\b\3\t\3\t\3\n"+
"\3\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\16\3\16\3\17\3\17\3\20\5\20S\n\20"+
"\3\20\6\20V\n\20\r\20\16\20W\3\20\3\20\6\20\\\n\20\r\20\16\20]\5\20`\n"+
"\20\3\21\3\21\7\21d\n\21\f\21\16\21g\13\21\3\21\3\21\3\22\3\22\7\22m\n"+
"\22\f\22\16\22p\13\22\3\22\3\22\3\23\3\23\7\23v\n\23\f\23\16\23y\13\23"+
"\3\23\3\23\3\24\6\24~\n\24\r\24\16\24\177\3\25\6\25\u0083\n\25\r\25\16"+
"\25\u0084\3\25\3\25\5enw\2\26\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13"+
"\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26\3\2\b\4\2##``"+
"\3\2\62;\3\2))\3\2$$\7\2..\62;C\\aac|\5\2\13\f\16\17\"\"\u0090\2\3\3\2"+
"\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17"+
"\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2"+
"\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3"+
"\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\3+\3\2\2\2\5-\3\2\2\2\7/\3\2\2\2\t\62\3"+
"\2\2\2\13\67\3\2\2\2\r=\3\2\2\2\17?\3\2\2\2\21B\3\2\2\2\23D\3\2\2\2\25"+
"G\3\2\2\2\27I\3\2\2\2\31K\3\2\2\2\33M\3\2\2\2\35O\3\2\2\2\37R\3\2\2\2"+
"!a\3\2\2\2#j\3\2\2\2%s\3\2\2\2\'}\3\2\2\2)\u0082\3\2\2\2+,\7(\2\2,\4\3"+
"\2\2\2-.\7~\2\2.\6\3\2\2\2/\60\7&\2\2\60\61\7#\2\2\61\b\3\2\2\2\62\63"+
"\7v\2\2\63\64\7t\2\2\64\65\7w\2\2\65\66\7g\2\2\66\n\3\2\2\2\678\7h\2\2"+
"89\7c\2\29:\7n\2\2:;\7u\2\2;<\7g\2\2<\f\3\2\2\2=>\7@\2\2>\16\3\2\2\2?"+
"@\7@\2\2@A\7?\2\2A\20\3\2\2\2BC\7>\2\2C\22\3\2\2\2DE\7>\2\2EF\7?\2\2F"+
"\24\3\2\2\2GH\7?\2\2H\26\3\2\2\2IJ\t\2\2\2J\30\3\2\2\2KL\7\u0080\2\2L"+
"\32\3\2\2\2MN\7*\2\2N\34\3\2\2\2OP\7+\2\2P\36\3\2\2\2QS\7/\2\2RQ\3\2\2"+
"\2RS\3\2\2\2SU\3\2\2\2TV\t\3\2\2UT\3\2\2\2VW\3\2\2\2WU\3\2\2\2WX\3\2\2"+
"\2X_\3\2\2\2Y[\7\60\2\2Z\\\t\3\2\2[Z\3\2\2\2\\]\3\2\2\2][\3\2\2\2]^\3"+
"\2\2\2^`\3\2\2\2_Y\3\2\2\2_`\3\2\2\2` \3\2\2\2ae\7B\2\2bd\13\2\2\2cb\3"+
"\2\2\2dg\3\2\2\2ef\3\2\2\2ec\3\2\2\2fh\3\2\2\2ge\3\2\2\2hi\7B\2\2i\"\3"+
"\2\2\2jn\t\4\2\2km\13\2\2\2lk\3\2\2\2mp\3\2\2\2no\3\2\2\2nl\3\2\2\2oq"+
"\3\2\2\2pn\3\2\2\2qr\t\4\2\2r$\3\2\2\2sw\t\5\2\2tv\13\2\2\2ut\3\2\2\2"+
"vy\3\2\2\2wx\3\2\2\2wu\3\2\2\2xz\3\2\2\2yw\3\2\2\2z{\t\5\2\2{&\3\2\2\2"+
"|~\t\6\2\2}|\3\2\2\2~\177\3\2\2\2\177}\3\2\2\2\177\u0080\3\2\2\2\u0080"+
"(\3\2\2\2\u0081\u0083\t\7\2\2\u0082\u0081\3\2\2\2\u0083\u0084\3\2\2\2"+
"\u0084\u0082\3\2\2\2\u0084\u0085\3\2\2\2\u0085\u0086\3\2\2\2\u0086\u0087"+
"\b\25\2\2\u0087*\3\2\2\2\f\2RW]_enw\177\u0084\3\b\2\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}

View File

@ -0,0 +1,33 @@
QTEXT=17
OR=2
LT=8
LPAREN=13
TRUE=4
DECIMAL=15
TEXT=19
RPAREN=14
EQ=10
GT=6
NOT=3
RE=12
VARIABLE=16
AND=1
NE=11
LE=9
FALSE=5
DQTEXT=18
WS=20
GE=7
'false'=5
')'=14
'('=13
'&'=1
'<='=9
'>'=6
'~'=12
'$!'=3
'='=10
'|'=2
'<'=8
'>='=7
'true'=4

View File

@ -0,0 +1,530 @@
// Generated from SimpleBoolean.g4 by ANTLR 4.4
package org.idempiere.expression.logic;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.*;
import org.antlr.v4.runtime.tree.*;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class SimpleBooleanParser extends Parser {
static { RuntimeMetaData.checkVersion("4.4", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
AND=1, OR=2, NOT=3, TRUE=4, FALSE=5, GT=6, GE=7, LT=8, LE=9, EQ=10, NE=11,
RE=12, LPAREN=13, RPAREN=14, DECIMAL=15, VARIABLE=16, QTEXT=17, DQTEXT=18,
TEXT=19, WS=20;
public static final String[] tokenNames = {
"<INVALID>", "'&'", "'|'", "'$!'", "'true'", "'false'", "'>'", "'>='",
"'<'", "'<='", "'='", "NE", "'~'", "'('", "')'", "DECIMAL", "VARIABLE",
"QTEXT", "DQTEXT", "TEXT", "WS"
};
public static final int
RULE_parse = 0, RULE_expression = 1, RULE_comparator = 2, RULE_binary = 3,
RULE_bool = 4;
public static final String[] ruleNames = {
"parse", "expression", "comparator", "binary", "bool"
};
@Override
public String getGrammarFileName() { return "SimpleBoolean.g4"; }
@Override
public String[] getTokenNames() { return tokenNames; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public ATN getATN() { return _ATN; }
public SimpleBooleanParser(TokenStream input) {
super(input);
_interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
public static class ParseContext extends ParserRuleContext {
public TerminalNode EOF() { return getToken(SimpleBooleanParser.EOF, 0); }
public ExpressionContext expression() {
return getRuleContext(ExpressionContext.class,0);
}
public ParseContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@Override public int getRuleIndex() { return RULE_parse; }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitParse(this);
else return visitor.visitChildren(this);
}
}
public final ParseContext parse() throws RecognitionException {
ParseContext _localctx = new ParseContext(_ctx, getState());
enterRule(_localctx, 0, RULE_parse);
try {
enterOuterAlt(_localctx, 1);
{
setState(10); expression(0);
setState(11); match(EOF);
}
}
catch (RecognitionException re) {
_localctx.exception = re;
_errHandler.reportError(this, re);
_errHandler.recover(this, re);
}
finally {
exitRule();
}
return _localctx;
}
public static class ExpressionContext extends ParserRuleContext {
public ExpressionContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@Override public int getRuleIndex() { return RULE_expression; }
public ExpressionContext() { }
public void copyFrom(ExpressionContext ctx) {
super.copyFrom(ctx);
}
}
public static class BinaryExpressionContext extends ExpressionContext {
public ExpressionContext left;
public BinaryContext op;
public ExpressionContext right;
public BinaryContext binary() {
return getRuleContext(BinaryContext.class,0);
}
public ExpressionContext expression(int i) {
return getRuleContext(ExpressionContext.class,i);
}
public List<ExpressionContext> expression() {
return getRuleContexts(ExpressionContext.class);
}
public BinaryExpressionContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitBinaryExpression(this);
else return visitor.visitChildren(this);
}
}
public static class DecimalExpressionContext extends ExpressionContext {
public TerminalNode DECIMAL() { return getToken(SimpleBooleanParser.DECIMAL, 0); }
public DecimalExpressionContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitDecimalExpression(this);
else return visitor.visitChildren(this);
}
}
public static class BoolExpressionContext extends ExpressionContext {
public BoolContext bool() {
return getRuleContext(BoolContext.class,0);
}
public BoolExpressionContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitBoolExpression(this);
else return visitor.visitChildren(this);
}
}
public static class ContextVariablesContext extends ExpressionContext {
public TerminalNode VARIABLE() { return getToken(SimpleBooleanParser.VARIABLE, 0); }
public ContextVariablesContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitContextVariables(this);
else return visitor.visitChildren(this);
}
}
public static class NotExpressionContext extends ExpressionContext {
public TerminalNode NOT() { return getToken(SimpleBooleanParser.NOT, 0); }
public ExpressionContext expression() {
return getRuleContext(ExpressionContext.class,0);
}
public NotExpressionContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitNotExpression(this);
else return visitor.visitChildren(this);
}
}
public static class ParenExpressionContext extends ExpressionContext {
public TerminalNode LPAREN() { return getToken(SimpleBooleanParser.LPAREN, 0); }
public TerminalNode RPAREN() { return getToken(SimpleBooleanParser.RPAREN, 0); }
public ExpressionContext expression() {
return getRuleContext(ExpressionContext.class,0);
}
public ParenExpressionContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitParenExpression(this);
else return visitor.visitChildren(this);
}
}
public static class TextContext extends ExpressionContext {
public TerminalNode TEXT() { return getToken(SimpleBooleanParser.TEXT, 0); }
public TextContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitText(this);
else return visitor.visitChildren(this);
}
}
public static class QuotedTextContext extends ExpressionContext {
public TerminalNode QTEXT() { return getToken(SimpleBooleanParser.QTEXT, 0); }
public QuotedTextContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitQuotedText(this);
else return visitor.visitChildren(this);
}
}
public static class DoubleQuotedTextContext extends ExpressionContext {
public TerminalNode DQTEXT() { return getToken(SimpleBooleanParser.DQTEXT, 0); }
public DoubleQuotedTextContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitDoubleQuotedText(this);
else return visitor.visitChildren(this);
}
}
public static class ComparatorExpressionContext extends ExpressionContext {
public ExpressionContext left;
public ComparatorContext op;
public ExpressionContext right;
public ComparatorContext comparator() {
return getRuleContext(ComparatorContext.class,0);
}
public ExpressionContext expression(int i) {
return getRuleContext(ExpressionContext.class,i);
}
public List<ExpressionContext> expression() {
return getRuleContexts(ExpressionContext.class);
}
public ComparatorExpressionContext(ExpressionContext ctx) { copyFrom(ctx); }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitComparatorExpression(this);
else return visitor.visitChildren(this);
}
}
public final ExpressionContext expression() throws RecognitionException {
return expression(0);
}
private ExpressionContext expression(int _p) throws RecognitionException {
ParserRuleContext _parentctx = _ctx;
int _parentState = getState();
ExpressionContext _localctx = new ExpressionContext(_ctx, _parentState);
ExpressionContext _prevctx = _localctx;
int _startState = 2;
enterRecursionRule(_localctx, 2, RULE_expression, _p);
try {
int _alt;
enterOuterAlt(_localctx, 1);
{
setState(26);
switch (_input.LA(1)) {
case NOT:
{
_localctx = new NotExpressionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(14); match(NOT);
setState(15); expression(9);
}
break;
case LPAREN:
{
_localctx = new ParenExpressionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(16); match(LPAREN);
setState(17); expression(0);
setState(18); match(RPAREN);
}
break;
case TRUE:
case FALSE:
{
_localctx = new BoolExpressionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(20); bool();
}
break;
case VARIABLE:
{
_localctx = new ContextVariablesContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(21); match(VARIABLE);
}
break;
case QTEXT:
{
_localctx = new QuotedTextContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(22); match(QTEXT);
}
break;
case DQTEXT:
{
_localctx = new DoubleQuotedTextContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(23); match(DQTEXT);
}
break;
case TEXT:
{
_localctx = new TextContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(24); match(TEXT);
}
break;
case DECIMAL:
{
_localctx = new DecimalExpressionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
setState(25); match(DECIMAL);
}
break;
default:
throw new NoViableAltException(this);
}
_ctx.stop = _input.LT(-1);
setState(38);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,2,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
if ( _parseListeners!=null ) triggerExitRuleEvent();
_prevctx = _localctx;
{
setState(36);
switch ( getInterpreter().adaptivePredict(_input,1,_ctx) ) {
case 1:
{
_localctx = new ComparatorExpressionContext(new ExpressionContext(_parentctx, _parentState));
((ComparatorExpressionContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_expression);
setState(28);
if (!(precpred(_ctx, 8))) throw new FailedPredicateException(this, "precpred(_ctx, 8)");
setState(29); ((ComparatorExpressionContext)_localctx).op = comparator();
setState(30); ((ComparatorExpressionContext)_localctx).right = expression(9);
}
break;
case 2:
{
_localctx = new BinaryExpressionContext(new ExpressionContext(_parentctx, _parentState));
((BinaryExpressionContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_expression);
setState(32);
if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)");
setState(33); ((BinaryExpressionContext)_localctx).op = binary();
setState(34); ((BinaryExpressionContext)_localctx).right = expression(8);
}
break;
}
}
}
setState(40);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,2,_ctx);
}
}
}
catch (RecognitionException re) {
_localctx.exception = re;
_errHandler.reportError(this, re);
_errHandler.recover(this, re);
}
finally {
unrollRecursionContexts(_parentctx);
}
return _localctx;
}
public static class ComparatorContext extends ParserRuleContext {
public TerminalNode RE() { return getToken(SimpleBooleanParser.RE, 0); }
public TerminalNode GE() { return getToken(SimpleBooleanParser.GE, 0); }
public TerminalNode LT() { return getToken(SimpleBooleanParser.LT, 0); }
public TerminalNode GT() { return getToken(SimpleBooleanParser.GT, 0); }
public TerminalNode LE() { return getToken(SimpleBooleanParser.LE, 0); }
public TerminalNode EQ() { return getToken(SimpleBooleanParser.EQ, 0); }
public TerminalNode NE() { return getToken(SimpleBooleanParser.NE, 0); }
public ComparatorContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@Override public int getRuleIndex() { return RULE_comparator; }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitComparator(this);
else return visitor.visitChildren(this);
}
}
public final ComparatorContext comparator() throws RecognitionException {
ComparatorContext _localctx = new ComparatorContext(_ctx, getState());
enterRule(_localctx, 4, RULE_comparator);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
setState(41);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << GT) | (1L << GE) | (1L << LT) | (1L << LE) | (1L << EQ) | (1L << NE) | (1L << RE))) != 0)) ) {
_errHandler.recoverInline(this);
}
consume();
}
}
catch (RecognitionException re) {
_localctx.exception = re;
_errHandler.reportError(this, re);
_errHandler.recover(this, re);
}
finally {
exitRule();
}
return _localctx;
}
public static class BinaryContext extends ParserRuleContext {
public TerminalNode AND() { return getToken(SimpleBooleanParser.AND, 0); }
public TerminalNode OR() { return getToken(SimpleBooleanParser.OR, 0); }
public BinaryContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@Override public int getRuleIndex() { return RULE_binary; }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitBinary(this);
else return visitor.visitChildren(this);
}
}
public final BinaryContext binary() throws RecognitionException {
BinaryContext _localctx = new BinaryContext(_ctx, getState());
enterRule(_localctx, 6, RULE_binary);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
setState(43);
_la = _input.LA(1);
if ( !(_la==AND || _la==OR) ) {
_errHandler.recoverInline(this);
}
consume();
}
}
catch (RecognitionException re) {
_localctx.exception = re;
_errHandler.reportError(this, re);
_errHandler.recover(this, re);
}
finally {
exitRule();
}
return _localctx;
}
public static class BoolContext extends ParserRuleContext {
public TerminalNode FALSE() { return getToken(SimpleBooleanParser.FALSE, 0); }
public TerminalNode TRUE() { return getToken(SimpleBooleanParser.TRUE, 0); }
public BoolContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@Override public int getRuleIndex() { return RULE_bool; }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor<? extends T>)visitor).visitBool(this);
else return visitor.visitChildren(this);
}
}
public final BoolContext bool() throws RecognitionException {
BoolContext _localctx = new BoolContext(_ctx, getState());
enterRule(_localctx, 8, RULE_bool);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
setState(45);
_la = _input.LA(1);
if ( !(_la==TRUE || _la==FALSE) ) {
_errHandler.recoverInline(this);
}
consume();
}
}
catch (RecognitionException re) {
_localctx.exception = re;
_errHandler.reportError(this, re);
_errHandler.recover(this, re);
}
finally {
exitRule();
}
return _localctx;
}
public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
switch (ruleIndex) {
case 1: return expression_sempred((ExpressionContext)_localctx, predIndex);
}
return true;
}
private boolean expression_sempred(ExpressionContext _localctx, int predIndex) {
switch (predIndex) {
case 0: return precpred(_ctx, 8);
case 1: return precpred(_ctx, 7);
}
return true;
}
public static final String _serializedATN =
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\26\62\4\2\t\2\4\3"+
"\t\3\4\4\t\4\4\5\t\5\4\6\t\6\3\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3"+
"\3\3\3\3\3\3\3\3\3\3\3\5\3\35\n\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\7\3"+
"\'\n\3\f\3\16\3*\13\3\3\4\3\4\3\5\3\5\3\6\3\6\3\6\2\3\4\7\2\4\6\b\n\2"+
"\5\3\2\b\16\3\2\3\4\3\2\6\7\65\2\f\3\2\2\2\4\34\3\2\2\2\6+\3\2\2\2\b-"+
"\3\2\2\2\n/\3\2\2\2\f\r\5\4\3\2\r\16\7\2\2\3\16\3\3\2\2\2\17\20\b\3\1"+
"\2\20\21\7\5\2\2\21\35\5\4\3\13\22\23\7\17\2\2\23\24\5\4\3\2\24\25\7\20"+
"\2\2\25\35\3\2\2\2\26\35\5\n\6\2\27\35\7\22\2\2\30\35\7\23\2\2\31\35\7"+
"\24\2\2\32\35\7\25\2\2\33\35\7\21\2\2\34\17\3\2\2\2\34\22\3\2\2\2\34\26"+
"\3\2\2\2\34\27\3\2\2\2\34\30\3\2\2\2\34\31\3\2\2\2\34\32\3\2\2\2\34\33"+
"\3\2\2\2\35(\3\2\2\2\36\37\f\n\2\2\37 \5\6\4\2 !\5\4\3\13!\'\3\2\2\2\""+
"#\f\t\2\2#$\5\b\5\2$%\5\4\3\n%\'\3\2\2\2&\36\3\2\2\2&\"\3\2\2\2\'*\3\2"+
"\2\2(&\3\2\2\2()\3\2\2\2)\5\3\2\2\2*(\3\2\2\2+,\t\2\2\2,\7\3\2\2\2-.\t"+
"\3\2\2.\t\3\2\2\2/\60\t\4\2\2\60\13\3\2\2\2\5\34&(";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}

View File

@ -0,0 +1,110 @@
// Generated from SimpleBoolean.g4 by ANTLR 4.4
package org.idempiere.expression.logic;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
/**
* This interface defines a complete generic visitor for a parse tree produced
* by {@link SimpleBooleanParser}.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public interface SimpleBooleanVisitor<T> extends ParseTreeVisitor<T> {
/**
* Visit a parse tree produced by the {@code boolExpression}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBoolExpression(@NotNull SimpleBooleanParser.BoolExpressionContext ctx);
/**
* Visit a parse tree produced by {@link SimpleBooleanParser#bool}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBool(@NotNull SimpleBooleanParser.BoolContext ctx);
/**
* Visit a parse tree produced by the {@code notExpression}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitNotExpression(@NotNull SimpleBooleanParser.NotExpressionContext ctx);
/**
* Visit a parse tree produced by {@link SimpleBooleanParser#parse}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitParse(@NotNull SimpleBooleanParser.ParseContext ctx);
/**
* Visit a parse tree produced by the {@code parenExpression}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitParenExpression(@NotNull SimpleBooleanParser.ParenExpressionContext ctx);
/**
* Visit a parse tree produced by the {@code doubleQuotedText}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitDoubleQuotedText(@NotNull SimpleBooleanParser.DoubleQuotedTextContext ctx);
/**
* Visit a parse tree produced by the {@code binaryExpression}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBinaryExpression(@NotNull SimpleBooleanParser.BinaryExpressionContext ctx);
/**
* Visit a parse tree produced by {@link SimpleBooleanParser#comparator}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitComparator(@NotNull SimpleBooleanParser.ComparatorContext ctx);
/**
* Visit a parse tree produced by the {@code decimalExpression}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitDecimalExpression(@NotNull SimpleBooleanParser.DecimalExpressionContext ctx);
/**
* Visit a parse tree produced by {@link SimpleBooleanParser#binary}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBinary(@NotNull SimpleBooleanParser.BinaryContext ctx);
/**
* Visit a parse tree produced by the {@code contextVariables}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitContextVariables(@NotNull SimpleBooleanParser.ContextVariablesContext ctx);
/**
* Visit a parse tree produced by the {@code text}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitText(@NotNull SimpleBooleanParser.TextContext ctx);
/**
* Visit a parse tree produced by the {@code quotedText}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitQuotedText(@NotNull SimpleBooleanParser.QuotedTextContext ctx);
/**
* Visit a parse tree produced by the {@code comparatorExpression}
* labeled alternative in {@link SimpleBooleanParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitComparatorExpression(@NotNull SimpleBooleanParser.ComparatorExpressionContext ctx);
}

View File

@ -0,0 +1,46 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.expression.logic;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.misc.ParseCancellationException;
/**
*
* @author hengsin
*
*/
public class ThrowingErrorListener extends BaseErrorListener {
public static final ThrowingErrorListener INSTANCE = new ThrowingErrorListener();
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
String msg, RecognitionException e) throws ParseCancellationException {
throw new ParseCancellationException("line " + line + ":" + charPositionInLine + " " + msg);
}
}

View File

@ -48,6 +48,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -48,6 +48,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -48,6 +48,7 @@
<setEntry value="jakarta.xml.bind-api@default:default"/>
<setEntry value="javax.jms@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>
<setEntry value="org.apache.activemq.kahadb@default:default"/>
<setEntry value="org.apache.ant@default:default"/>

View File

@ -560,4 +560,25 @@
fragment="true"
unpack="false"/>
<plugin
id="org.antlr.antlr4-runtime"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="wrapped.com.google.zxing.javase"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="wrapped.dev.samstevens.totp.totp"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>

View File

@ -145,6 +145,7 @@
<setEntry value="json*20190722.0.0@default:default"/>
<setEntry value="net.sf.jasperreports.engine@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.antlr.runtime@default:default"/>
<setEntry value="org.aopalliance@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>

View File

@ -145,6 +145,7 @@
<setEntry value="json*20190722.0.0@default:default"/>
<setEntry value="net.sf.jasperreports.engine@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.antlr.runtime@default:default"/>
<setEntry value="org.aopalliance@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>

View File

@ -81,7 +81,8 @@ osgi.bundles=org.eclipse.equinox.ds@1:start,\
org.eclipse.osgi@start,\
org.dom4j,\
wrapped.com.google.zxing.javase,\
wrapped.dev.samstevens.totp.totp
wrapped.dev.samstevens.totp.totp,\
org.antlr.antlr4-runtime
osgi.framework.extensions=
osgi.bundles.defaultStartLevel=4
osgi.compatibility.bootdelegation=true

View File

@ -176,5 +176,11 @@
<version>3.4.1</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate"
type="Maven">
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.9.2</version>
</location>
</locations>

View File

@ -465,19 +465,22 @@
<version>1.1.0.Final</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate"
type="Maven">
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp</artifactId>
<version>1.7.1</version>
<type>jar</type>
<location includeSource="true" missingManifest="generate" type="Maven">
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp</artifactId>
<version>1.7.1</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate"
type="Maven">
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
<type>jar</type>
<location includeSource="true" missingManifest="generate" type="Maven">
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
<type>jar</type>
</location>
<location includeSource="true" missingManifest="generate" type="Maven">
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.9.2</version>
</location>
</locations>
</target>

View File

@ -108,6 +108,7 @@
<setEntry value="json*20190722.0.0@default:default"/>
<setEntry value="net.sf.jasperreports.engine@default:default"/>
<setEntry value="net.sf.supercsv.super-csv@default:default"/>
<setEntry value="org.antlr.antlr4-runtime@default:default"/>
<setEntry value="org.antlr.runtime@default:default"/>
<setEntry value="org.aopalliance@default:default"/>
<setEntry value="org.apache.activemq.activemq-core@default:default"/>

View File

@ -0,0 +1,450 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.test.base;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.compiere.util.Env;
import org.compiere.util.Evaluatee;
import org.compiere.util.LegacyLogicEvaluator;
import org.idempiere.expression.logic.LogicEvaluator;
import org.idempiere.test.AbstractTestCase;
import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation")
/**
*
* @author hengsin
*
*/
public class LogicExpressionTest extends AbstractTestCase {
private final static ContextEvaluatee evaluatee = new ContextEvaluatee();
public LogicExpressionTest() {
}
@Test
public void testEqual() {
String expr = "@$Element_AY@='Y'";
Env.setContext(Env.getCtx(), "$Element_AY", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "Y");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "N");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "N");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
expr = "@$Element_AY@=Y";
Env.setContext(Env.getCtx(), "$Element_AY", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "Y");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "N");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_AY", "N");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testAnd() {
String expr = "@$Element_BP@=Y & @AnyBPartner@=N";
Env.setContext(Env.getCtx(), "$Element_BP", (String)null);
Env.setContext(Env.getCtx(), "AnyBPartner", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_BP", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "AnyBPartner", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "AnyBPartner", "N");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_BP", "N");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_BP", (String)null);
Env.setContext(Env.getCtx(), "AnyBPartner", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_BP", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "AnyBPartner", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "AnyBPartner", "N");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "$Element_BP", "N");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testIn() {
String expr = "@LineType@=C&@CalculationType@=A,R,S";
Env.setContext(Env.getCtx(), "LineType", (String)null);
Env.setContext(Env.getCtx(), "CalculationType", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "LineType", "C");
Env.setContext(Env.getCtx(), "CalculationType", "B");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "CalculationType", "A");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "CalculationType", "R");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "CalculationType", "S");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "LineType", "D");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "LineType", (String)null);
Env.setContext(Env.getCtx(), "CalculationType", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "LineType", "C");
Env.setContext(Env.getCtx(), "CalculationType", "B");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "CalculationType", "A");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "CalculationType", "R");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "CalculationType", "S");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "LineType", "D");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testNotEqual() {
String expr = "@C_Bpartner_ID@!0";
Env.setContext(Env.getCtx(), "C_Bpartner_ID", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Bpartner_ID", "0");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Bpartner_ID", "100");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Bpartner_ID", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Bpartner_ID", "0");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Bpartner_ID", "100");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
expr = "@IsOverUnderPayment@=Y & @C_Invoice_ID@^0";
Env.setContext(Env.getCtx(), "IsOverUnderPayment", (String)null);
Env.setContext(Env.getCtx(), "C_Invoice_ID", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsOverUnderPayment", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Invoice_ID", "0");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Invoice_ID", "100");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsOverUnderPayment", (String)null);
Env.setContext(Env.getCtx(), "C_Invoice_ID", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsOverUnderPayment", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Invoice_ID", "0");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "C_Invoice_ID", "100");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testOrAnd() {
String expr = "@IsSold@='Y' | @IsPurchased@='Y' & @IsSummary@='N'";
Env.setContext(Env.getCtx(), "IsSold", (String)null);
Env.setContext(Env.getCtx(), "IsPurchased", (String)null);
Env.setContext(Env.getCtx(), "IsSummary", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSold", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "N");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsPurchased", "Y");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSold", "N");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSold", (String)null);
Env.setContext(Env.getCtx(), "IsPurchased", (String)null);
Env.setContext(Env.getCtx(), "IsSummary", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSold", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "N");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsPurchased", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSold", "N");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testAndOr() {
String expr = "@IsSummary@='N' & @ProductType@=I | @ProductType@=S";
Env.setContext(Env.getCtx(), "IsSummary", (String)null);
Env.setContext(Env.getCtx(), "ProductType", (String)null);
Env.setContext(Env.getCtx(), "ProductType", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "N");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "ProductType", "I");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "ProductType", "S");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "Y");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", (String)null);
Env.setContext(Env.getCtx(), "ProductType", (String)null);
Env.setContext(Env.getCtx(), "ProductType", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "N");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "ProductType", "I");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "ProductType", "S");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testAndOrGroup() {
String expr = "@IsSummary@='N' & (@ProductType@=I | @ProductType@=S)";
Env.setContext(Env.getCtx(), "IsSummary", (String)null);
Env.setContext(Env.getCtx(), "ProductType", (String)null);
Env.setContext(Env.getCtx(), "ProductType", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "N");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "ProductType", "I");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "ProductType", "S");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testOrAndGroup() {
String expr = "@IsSold@='Y' | (@IsPurchased@='Y' & @IsSummary@='N')";
Env.setContext(Env.getCtx(), "IsSold", (String)null);
Env.setContext(Env.getCtx(), "IsPurchased", (String)null);
Env.setContext(Env.getCtx(), "IsSummary", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSold", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "N");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsPurchased", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSold", "N");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSummary", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testGT() {
String expr = "@IsLot@=Y& @M_LotCtl_ID@ > 0";
Env.setContext(Env.getCtx(), "IsLot", (String)null);
Env.setContext(Env.getCtx(), "M_LotCtl_ID", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsLot", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "0");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "101");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsLot", "N");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsLot", (String)null);
Env.setContext(Env.getCtx(), "M_LotCtl_ID", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsLot", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "0");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "101");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsLot", "N");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testGE() {
String expr = "@IsLot@=Y& @M_LotCtl_ID@ >= 100";
Env.setContext(Env.getCtx(), "IsLot", (String)null);
Env.setContext(Env.getCtx(), "M_LotCtl_ID", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsLot", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "0");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "100");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "99");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testLT() {
String expr = "@A_Asset_ID@<1&@A_CreateAsset@='Y'";
Env.setContext(Env.getCtx(), "A_Asset_ID", (String)null);
Env.setContext(Env.getCtx(), "A_CreateAsset", (String)null);
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_Asset_ID", "1");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_CreateAsset", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_Asset_ID", "0");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_Asset_ID", (String)null);
Env.setContext(Env.getCtx(), "A_CreateAsset", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_Asset_ID", "1");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_CreateAsset", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_Asset_ID", "0");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testLE() {
String expr = "@A_Asset_ID@<=1&@A_CreateAsset@='Y'";
Env.setContext(Env.getCtx(), "A_Asset_ID", (String)null);
Env.setContext(Env.getCtx(), "A_CreateAsset", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_Asset_ID", "2");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_CreateAsset", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "A_Asset_ID", "1");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testNegate() {
String expr = "$!(@IsLot@=Y & @M_LotCtl_ID@ > 0)";
Env.setContext(Env.getCtx(), "IsLot", (String)null);
Env.setContext(Env.getCtx(), "M_LotCtl_ID", (String)null);
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsLot", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "1");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "M_LotCtl_ID", "0");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testRE() {
String expr = "@Identifier@ ~ '^([a-zA-Z_$][a-zA-Z\\d_$]*)$'";
Env.setContext(Env.getCtx(), "Identifier", (String)null);
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "Identifier", "validIdentifier");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "Identifier", "$validIdentifier");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "Identifier", "~inValidIdentifier");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "Identifier", "_validIdentifier");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "Identifier", "0inValidIdentifier");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "Identifier", "validIdentifier0");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
@Test
public void testValidation() {
Exception ex = null;
String expr = "@IsSummary@='N' & (@ProductType@=I | @ProductType@=S)";
try {
LogicEvaluator.validate(expr);
} catch (Exception e) {
ex = e;
}
assertNull(ex);
expr = expr.substring(0, expr.length()-1);
try {
LogicEvaluator.validate(expr);
} catch (Exception e) {
ex = e;
}
assertNotNull(ex);
System.out.println(ex.getMessage());
}
@Test
public void testConditionalVariable() {
String expr = "@IsSOTrx:N@=N | @+IgnoreIsSOTrxInBPInfo:N@=Y";
Env.setContext(Env.getCtx(), "IsSOTrx", (String)null);
Env.setContext(Env.getCtx(), "+IgnoreIsSOTrxInBPInfo", (String)null);
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSOTrx", "Y");
assertFalse(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "+IgnoreIsSOTrxInBPInfo", "Y");
assertTrue(LegacyLogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSOTrx", (String)null);
Env.setContext(Env.getCtx(), "+IgnoreIsSOTrxInBPInfo", (String)null);
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "IsSOTrx", "Y");
assertFalse(LogicEvaluator.evaluateLogic(evaluatee, expr));
Env.setContext(Env.getCtx(), "+IgnoreIsSOTrxInBPInfo", "Y");
assertTrue(LogicEvaluator.evaluateLogic(evaluatee, expr));
}
private static class ContextEvaluatee implements Evaluatee {
@Override
public String get_ValueAsString(String variableName) {
return Env.getContext(Env.getCtx(), variableName);
}
}
}