// This class is used for evaluate a conditional
// format := {expression} [{logic} {expression}]
// expression := @{context}@{operand}{value} or @{context}@{operand}{value}
// logic := {|}|{&}
// context := any global or window context
// value := strings or numbers
// logic operators := AND or OR with the previous result from left to right
// operand := eq{=}, gt{>}, le{<}, not{~^!}
// Examples:
// @AD_Table_ID@=14 | @Language@!GERGER
// @PriceLimit@>10 | @PriceList@>@PriceActual@
// @Name@>J
// Strings may be in single quotes (optional)
class evaluator {
/**
* Evaluate logic's
* @param {string} parentUuid Parent (Window / Process / Smart Browser)
*/
static evaluateLogic(objectToEvaluate) {
let defaultUndefined = false
if (objectToEvaluate.type === 'displayed') {
defaultUndefined = true
}
// empty logic
if (objectToEvaluate.logic === undefined ||
objectToEvaluate.logic === null ||
objectToEvaluate.logic.trim() === '') {
return defaultUndefined
}
const st = objectToEvaluate.logic.trim().replace('\n', '')
const expr = /(\||&)/
const stList = st.split(expr)
const it = stList.length
if (((it / 2) - ((it + 1) / 2)) === 0) {
console.info(`Logic does not comply with format " [ ]" --> ${objectToEvaluate.logic}`)
return defaultUndefined
}
let retValue = null
let logOp = ''
stList.forEach(function(element) {
if (element.trim() === '|' || element.trim() === '&') {
logOp = element
} else if (retValue === null) {
retValue = evaluator.evaluateLogicTuples({
...objectToEvaluate,
conditional: element
})
} else {
if (logOp.trim() === '&') {
retValue = retValue & evaluator.evaluateLogicTuples({
...objectToEvaluate,
conditional: element
})
} else if (logOp.trim() === '|') {
retValue = retValue | evaluator.evaluateLogicTuples({
...objectToEvaluate,
conditional: element
})
} else {
console.info(`Logic operant '|' or '&' expected --> ${objectToEvaluate.logic}`)
return defaultUndefined
}
}
})
return Boolean(retValue)
} // evaluateLogic
/**
* Evaluate Logic Tuples
* @param {object} objectToEvaluate Complete object
* @param {string} logic If is displayed or not (mandatory and readonly)
* @return {boolean}
*/
static evaluateLogicTuples(objectToEvaluate) {
let _defaultUndefined = false
if (objectToEvaluate.type === 'displayed') {
_defaultUndefined = true
}
const logic = objectToEvaluate.conditional
// not context info, not logic
if (logic === undefined) {
return _defaultUndefined
}
/**
* fist group: (['"@#\w\d-_\s]{0,}) only values aphabetic (\w), numerics (\d),
* space (\d) and '"@#$-_ characters, at least ocurrency to 0 position
* second group: (<>|<=|==|>=|!=|<|=|>|!|\^) coincides only with some of the
* conditions <>, <=, ==, >=, !=, <, =, >, !, ^
* third group: same as the first group
* flag: global match (g), insensitive case (i), multiline (m)
*/
let expr = /^(['"@#$-_\w\d\s]{0,})(<>|<=|==|>=|!=|<|=|>|!|\^)(['"@#$-_\w\d\s]{0,})/gim
let st = expr.test(logic)
if (!st) {
console.info(`.Logic tuple does not comply with format '@context@=value' where operand could be one of '=!^><' --> ${logic}`)
return _defaultUndefined
}
expr = /(<>|<=|==|>=|!=|<|=|>|!|\^)/gm
st = logic.split(expr)
// First Part (or column name)
let first = st[0].trim()
let firstEval = first
let isCountable = false
let isGlobal = false
expr = /@/
if (expr.test(first)) {
first = first.replace(/@/g, '').trim()
isGlobal = first.startsWith('#')
isCountable = first.startsWith('$')
const value = objectToEvaluate.context.getContext({
parentUuid: (isGlobal || isCountable) ? '' : objectToEvaluate.parentUuid,
containerUuid: (isGlobal || isCountable) ? '' : objectToEvaluate.containerUuid,
columnName: first
})
// in context exists this column name
if (value === null || value === undefined) {
// console.info(`.The column ${first} not exists in context.`)
return _defaultUndefined
}
firstEval = value // replace with it's value
}
if (firstEval === null || firstEval === undefined) {
return _defaultUndefined
}
if (typeof firstEval === 'string') {
firstEval = firstEval.replace(/['"]/g, '').trim() // strip ' and "
}
// Operator
const operand = st[1]
// Second Part
let second = st[2].trim()
let secondEval = second.trim()
if (expr.test(second)) {
second = second.replace(/@/g, ' ').trim() // strip tag
secondEval = objectToEvaluate.context.getContext({
parentUuid: (isGlobal || isCountable) ? null : objectToEvaluate.parentUuid,
containerUuid: (isGlobal || isCountable) ? null : objectToEvaluate.containerUuid,
columnName: first
}) // replace with it's value
}
if (typeof secondEval === 'string') {
secondEval = secondEval.replace(/['"]/g, '').trim() // strip ' and " for string values
}
// Handling of ID compare (null => 0)
if (first.includes('_ID') && firstEval.length === 0) {
firstEval = '0'
}
if (second.includes('_ID') && secondEval.length === 0) {
secondEval = '0'
}
// Logical Comparison
const result = this.evaluateLogicTuple(firstEval, operand, secondEval)
return result
}
/**
* Evuale logic Tuple
* @param {string|number} value1 Value in Context
* @param {string} operand Comparison
* @param {string|number} value2 Value in Logic
* @return {boolean}
*/
static evaluateLogicTuple(value1, operand, value2) {
// Convert value 1 string value to boolean value
if (value1 === 'Y') {
value1 = true
} else if (value1 === 'N') {
value1 = false
}
// Convert value 2 string value to boolean value
if (value2 === 'Y') {
value2 = true
} else if (value2 === 'N') {
value2 = false
}
let isValueLogic
// TODO: Add '^' operand comparison
switch (operand) {
case '=':
case '==':
isValueLogic = value1 === value2
break
case '<':
isValueLogic = value1 < value2
break
case '<=':
isValueLogic = value1 <= value2
break
case '>':
isValueLogic = value1 > value2
break
case '>=':
isValueLogic = value1 >= value2
break
case '!':
case '!=':
case '<>':
default:
isValueLogic = value1 !== value2
break
}
return isValueLogic
}
/**
* Parse Depends or relations
* @param {string} parseString
* @return {array}
*/
static parseDepends(parseString) {
const listFields = []
if (parseString === null || parseString === undefined) {
// return array empty
return listFields
}
let string = parseString.replace('@SQL=', '')
// while we have variables
while (string.includes('@')) {
let pos = string.indexOf('@')
// remove first @: @ExampleColumn@ = ExampleColumn@
string = string.substring(pos + 1)
pos = string.indexOf('@')
if (pos === -1) {
continue
} // error number of @@ not correct
// remove second @: ExampleColumn@ = ExampleColumn
const value = string.substring(0, pos)
// delete secodn columnName and @
string = string.substring(pos + 1)
// add column name in array
listFields.push(value)
}
return listFields
}
}
export default evaluator