From d7153575e63eae781701971f5557c850482062d5 Mon Sep 17 00:00:00 2001 From: hengsin Date: Thu, 10 Jun 2021 23:08:22 +0800 Subject: [PATCH] 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 --- .../model.generator.launch | 1 + .../packinfolder.app.launch | 1 + .../sign.database.build.launch | 1 + .../synchronize-terminology.app.launch | 1 + .../translation.app.launch | 1 + org.adempiere.base/.project | 6 + org.adempiere.base/META-INF/MANIFEST.MF | 8 + org.adempiere.base/antlr/SimpleBoolean.g4 | 53 ++ .../src/org/compiere/model/MColumn.java | 7 + .../src/org/compiere/model/MField.java | 19 + .../src/org/compiere/model/MProcessPara.java | 14 + .../src/org/compiere/model/MUserDefField.java | 20 + .../src/org/compiere/util/Evaluator.java | 194 +------ .../compiere/util/LegacyLogicEvaluator.java | 234 ++++++++ .../expression/logic/EvaluationVisitor.java | 227 ++++++++ .../expression/logic/LogicEvaluator.java | 100 ++++ .../expression/logic/SimpleBoolean.interp | 56 ++ .../expression/logic/SimpleBoolean.tokens | 33 ++ .../logic/SimpleBooleanBaseVisitor.java | 115 ++++ .../logic/SimpleBooleanLexer.interp | 77 +++ .../expression/logic/SimpleBooleanLexer.java | 111 ++++ .../logic/SimpleBooleanLexer.tokens | 33 ++ .../expression/logic/SimpleBooleanParser.java | 530 ++++++++++++++++++ .../logic/SimpleBooleanVisitor.java | 110 ++++ .../logic/ThrowingErrorListener.java | 46 ++ org.adempiere.install/install.app.launch | 1 + .../install.console.app.launch | 1 + .../install.silent.app.launch | 1 + org.adempiere.server-feature/feature.xml | 21 + .../server.product.functionaltest.launch | 1 + .../server.product.launch | 1 + .../setup/configuration/config.ini | 3 +- .../maven.locations.xml | 6 + .../org.idempiere.p2.targetplatform.target | 27 +- org.idempiere.test/idempiere.unit.test.launch | 1 + .../test/base/LogicExpressionTest.java | 450 +++++++++++++++ 36 files changed, 2308 insertions(+), 203 deletions(-) create mode 100644 org.adempiere.base/antlr/SimpleBoolean.g4 create mode 100644 org.adempiere.base/src/org/compiere/util/LegacyLogicEvaluator.java create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/LogicEvaluator.java create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.interp create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.tokens create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanBaseVisitor.java create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.interp create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.java create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.tokens create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanParser.java create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanVisitor.java create mode 100644 org.adempiere.base/src/org/idempiere/expression/logic/ThrowingErrorListener.java create mode 100644 org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java diff --git a/org.adempiere.base-feature/model.generator.launch b/org.adempiere.base-feature/model.generator.launch index f688b557da..91dc9191b3 100644 --- a/org.adempiere.base-feature/model.generator.launch +++ b/org.adempiere.base-feature/model.generator.launch @@ -45,6 +45,7 @@ + diff --git a/org.adempiere.base-feature/packinfolder.app.launch b/org.adempiere.base-feature/packinfolder.app.launch index d959b78a3b..94666c04ca 100644 --- a/org.adempiere.base-feature/packinfolder.app.launch +++ b/org.adempiere.base-feature/packinfolder.app.launch @@ -56,6 +56,7 @@ + diff --git a/org.adempiere.base-feature/sign.database.build.launch b/org.adempiere.base-feature/sign.database.build.launch index 053d83f020..140ac22c9e 100644 --- a/org.adempiere.base-feature/sign.database.build.launch +++ b/org.adempiere.base-feature/sign.database.build.launch @@ -55,6 +55,7 @@ + diff --git a/org.adempiere.base-feature/synchronize-terminology.app.launch b/org.adempiere.base-feature/synchronize-terminology.app.launch index 9c571edbec..082e569c8e 100644 --- a/org.adempiere.base-feature/synchronize-terminology.app.launch +++ b/org.adempiere.base-feature/synchronize-terminology.app.launch @@ -55,6 +55,7 @@ + diff --git a/org.adempiere.base-feature/translation.app.launch b/org.adempiere.base-feature/translation.app.launch index 34ee71c5c4..a631833985 100644 --- a/org.adempiere.base-feature/translation.app.launch +++ b/org.adempiere.base-feature/translation.app.launch @@ -55,6 +55,7 @@ + diff --git a/org.adempiere.base/.project b/org.adempiere.base/.project index dea9827e1e..8b3e9cd83a 100644 --- a/org.adempiere.base/.project +++ b/org.adempiere.base/.project @@ -5,6 +5,11 @@ + + org.eclipse.xtext.ui.shared.xtextBuilder + + + org.eclipse.jdt.core.javabuilder @@ -40,5 +45,6 @@ org.eclipse.m2e.core.maven2Nature org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature + org.eclipse.xtext.ui.shared.xtextNature diff --git a/org.adempiere.base/META-INF/MANIFEST.MF b/org.adempiere.base/META-INF/MANIFEST.MF index a71f0f66ed..4a3e3561b7 100644 --- a/org.adempiere.base/META-INF/MANIFEST.MF +++ b/org.adempiere.base/META-INF/MANIFEST.MF @@ -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", diff --git a/org.adempiere.base/antlr/SimpleBoolean.g4 b/org.adempiere.base/antlr/SimpleBoolean.g4 new file mode 100644 index 0000000000..2c3d65ddae --- /dev/null +++ b/org.adempiere.base/antlr/SimpleBoolean.g4 @@ -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; diff --git a/org.adempiere.base/src/org/compiere/model/MColumn.java b/org.adempiere.base/src/org/compiere/model/MColumn.java index 32e7afa03b..7e78b46a8c 100644 --- a/org.adempiere.base/src/org/compiere/model/MColumn.java +++ b/org.adempiere.base/src/org/compiere/model/MColumn.java @@ -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 diff --git a/org.adempiere.base/src/org/compiere/model/MField.java b/org.adempiere.base/src/org/compiere/model/MField.java index 8ea2058da8..31cb6ec445 100644 --- a/org.adempiere.base/src/org/compiere/model/MField.java +++ b/org.adempiere.base/src/org/compiere/model/MField.java @@ -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 diff --git a/org.adempiere.base/src/org/compiere/model/MProcessPara.java b/org.adempiere.base/src/org/compiere/model/MProcessPara.java index 4d2537e3a2..b23c4a501a 100644 --- a/org.adempiere.base/src/org/compiere/model/MProcessPara.java +++ b/org.adempiere.base/src/org/compiere/model/MProcessPara.java @@ -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 diff --git a/org.adempiere.base/src/org/compiere/model/MUserDefField.java b/org.adempiere.base/src/org/compiere/model/MUserDefField.java index 55453c52ba..f64a866c68 100644 --- a/org.adempiere.base/src/org/compiere/model/MUserDefField.java +++ b/org.adempiere.base/src/org/compiere/model/MUserDefField.java @@ -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; } diff --git a/org.adempiere.base/src/org/compiere/util/Evaluator.java b/org.adempiere.base/src/org/compiere/util/Evaluator.java index 3dfcea07fb..31ee06371c 100644 --- a/org.adempiere.base/src/org/compiere/util/Evaluator.java +++ b/org.adempiere.base/src/org/compiere/util/Evaluator.java @@ -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. - * - * format := [ ] - * expression := @@ - * 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 - * + * @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 " - + "' [ ]' => " + 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. - *
-	 *	value: strips ' and " always (no escape or mid stream)
-	 *  value: can also be a context variable
-	 *  
- * @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 diff --git a/org.adempiere.base/src/org/compiere/util/LegacyLogicEvaluator.java b/org.adempiere.base/src/org/compiere/util/LegacyLogicEvaluator.java new file mode 100644 index 0000000000..fadcb419ad --- /dev/null +++ b/org.adempiere.base/src/org/compiere/util/LegacyLogicEvaluator.java @@ -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. + * + * format := [ ] + * expression := @@ + * 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 + * + * @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 " + + "' [ ]' => " + 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. + *
+	 *	value: strips ' and " always (no escape or mid stream)
+	 *  value: can also be a context variable
+	 *  
+ * @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 +} diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java b/org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java new file mode 100644 index 0000000000..13cc177c78 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/EvaluationVisitor.java @@ -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 { + + 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()); + } +} \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/LogicEvaluator.java b/org.adempiere.base/src/org/idempiere/expression/logic/LogicEvaluator.java new file mode 100644 index 0000000000..0c8708d15f --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/LogicEvaluator.java @@ -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: ( [ ]). + * : $!. + * : @@. + * : | or & (Example '@AD_Table@=Test | @Language@=GERGER). + * : = | ! | ^ | < | > | <= | >= | ~ (Equal, Not Equal, Not Equal, Less Than, + * Greater Than, Less Than or Equal, Greater Than or Equal, Regular Expression Match). + * : ~ ''. + * : value1,value2,value3 (Example '@CalculationType@=A,R,S'). + * : any global or window context. + * : strings can be with ' or ". + * : 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 " + + "' [ ]' => " + 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(); + } +} diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.interp b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.interp new file mode 100644 index 0000000000..fe4359c9cc --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.interp @@ -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] \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.tokens b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.tokens new file mode 100644 index 0000000000..afffaf07f1 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBoolean.tokens @@ -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 diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanBaseVisitor.java b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanBaseVisitor.java new file mode 100644 index 0000000000..9826a6e0ca --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanBaseVisitor.java @@ -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 The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public class SimpleBooleanBaseVisitor extends AbstractParseTreeVisitor implements SimpleBooleanVisitor { + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitBoolExpression(@NotNull SimpleBooleanParser.BoolExpressionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitBool(@NotNull SimpleBooleanParser.BoolContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNotExpression(@NotNull SimpleBooleanParser.NotExpressionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParse(@NotNull SimpleBooleanParser.ParseContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParenExpression(@NotNull SimpleBooleanParser.ParenExpressionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitDoubleQuotedText(@NotNull SimpleBooleanParser.DoubleQuotedTextContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitBinaryExpression(@NotNull SimpleBooleanParser.BinaryExpressionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitComparator(@NotNull SimpleBooleanParser.ComparatorContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitDecimalExpression(@NotNull SimpleBooleanParser.DecimalExpressionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitBinary(@NotNull SimpleBooleanParser.BinaryContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitContextVariables(@NotNull SimpleBooleanParser.ContextVariablesContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitText(@NotNull SimpleBooleanParser.TextContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitQuotedText(@NotNull SimpleBooleanParser.QuotedTextContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitComparatorExpression(@NotNull SimpleBooleanParser.ComparatorExpressionContext ctx) { return visitChildren(ctx); } +} \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.interp b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.interp new file mode 100644 index 0000000000..f234a9fef6 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.interp @@ -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] \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.java b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.java new file mode 100644 index 0000000000..0b890e4c03 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.java @@ -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); + } + } +} \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.tokens b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.tokens new file mode 100644 index 0000000000..afffaf07f1 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanLexer.tokens @@ -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 diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanParser.java b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanParser.java new file mode 100644 index 0000000000..e8b4573559 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanParser.java @@ -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 = { + "", "'&'", "'|'", "'$!'", "'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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 expression() { + return getRuleContexts(ExpressionContext.class); + } + public BinaryExpressionContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 expression() { + return getRuleContexts(ExpressionContext.class); + } + public ComparatorExpressionContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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 accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SimpleBooleanVisitor ) return ((SimpleBooleanVisitor)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); + } + } +} \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanVisitor.java b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanVisitor.java new file mode 100644 index 0000000000..66ec9de122 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/SimpleBooleanVisitor.java @@ -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 The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface SimpleBooleanVisitor extends ParseTreeVisitor { + /** + * 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); +} \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/expression/logic/ThrowingErrorListener.java b/org.adempiere.base/src/org/idempiere/expression/logic/ThrowingErrorListener.java new file mode 100644 index 0000000000..fa687a17c0 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/expression/logic/ThrowingErrorListener.java @@ -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); + } +} diff --git a/org.adempiere.install/install.app.launch b/org.adempiere.install/install.app.launch index 1dbc74c1c8..d54a26ae63 100644 --- a/org.adempiere.install/install.app.launch +++ b/org.adempiere.install/install.app.launch @@ -48,6 +48,7 @@ + diff --git a/org.adempiere.install/install.console.app.launch b/org.adempiere.install/install.console.app.launch index 187975d213..4c6f61d8dd 100644 --- a/org.adempiere.install/install.console.app.launch +++ b/org.adempiere.install/install.console.app.launch @@ -48,6 +48,7 @@ + diff --git a/org.adempiere.install/install.silent.app.launch b/org.adempiere.install/install.silent.app.launch index 29eb37d1a5..4b48c83d88 100644 --- a/org.adempiere.install/install.silent.app.launch +++ b/org.adempiere.install/install.silent.app.launch @@ -48,6 +48,7 @@ + diff --git a/org.adempiere.server-feature/feature.xml b/org.adempiere.server-feature/feature.xml index abd8dc62ed..094a933e70 100644 --- a/org.adempiere.server-feature/feature.xml +++ b/org.adempiere.server-feature/feature.xml @@ -560,4 +560,25 @@ fragment="true" unpack="false"/> + + + + + + diff --git a/org.adempiere.server-feature/server.product.functionaltest.launch b/org.adempiere.server-feature/server.product.functionaltest.launch index b5f16f0f3b..c292c4aa5f 100644 --- a/org.adempiere.server-feature/server.product.functionaltest.launch +++ b/org.adempiere.server-feature/server.product.functionaltest.launch @@ -145,6 +145,7 @@ + diff --git a/org.adempiere.server-feature/server.product.launch b/org.adempiere.server-feature/server.product.launch index 5accbcf01e..1e5552d179 100644 --- a/org.adempiere.server-feature/server.product.launch +++ b/org.adempiere.server-feature/server.product.launch @@ -145,6 +145,7 @@ + diff --git a/org.adempiere.server-feature/setup/configuration/config.ini b/org.adempiere.server-feature/setup/configuration/config.ini index 0c5aeb0b37..62e2aabb29 100644 --- a/org.adempiere.server-feature/setup/configuration/config.ini +++ b/org.adempiere.server-feature/setup/configuration/config.ini @@ -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 diff --git a/org.idempiere.p2.targetplatform/maven.locations.xml b/org.idempiere.p2.targetplatform/maven.locations.xml index 045a1c155d..1f9facf9f9 100644 --- a/org.idempiere.p2.targetplatform/maven.locations.xml +++ b/org.idempiere.p2.targetplatform/maven.locations.xml @@ -176,5 +176,11 @@ 3.4.1 jar + + org.antlr + antlr4-runtime + 4.9.2 + diff --git a/org.idempiere.p2.targetplatform/org.idempiere.p2.targetplatform.target b/org.idempiere.p2.targetplatform/org.idempiere.p2.targetplatform.target index cde3a1b7b0..f74d2ed20b 100644 --- a/org.idempiere.p2.targetplatform/org.idempiere.p2.targetplatform.target +++ b/org.idempiere.p2.targetplatform/org.idempiere.p2.targetplatform.target @@ -465,19 +465,22 @@ 1.1.0.Final jar - - dev.samstevens.totp - totp - 1.7.1 - jar + + dev.samstevens.totp + totp + 1.7.1 + jar - - com.google.zxing - javase - 3.4.1 - jar + + com.google.zxing + javase + 3.4.1 + jar + + + org.antlr + antlr4-runtime + 4.9.2 diff --git a/org.idempiere.test/idempiere.unit.test.launch b/org.idempiere.test/idempiere.unit.test.launch index f68b2b4c19..5ba01e10f8 100644 --- a/org.idempiere.test/idempiere.unit.test.launch +++ b/org.idempiere.test/idempiere.unit.test.launch @@ -108,6 +108,7 @@ + diff --git a/org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java b/org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java new file mode 100644 index 0000000000..568270d2a7 --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/base/LogicExpressionTest.java @@ -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); + } + + } +}