SmartIT_Extensions/BMC/smart-it-full-helix/scripts/app/common/expression-engine/expression-evaluator-servic...

317 lines
14 KiB
JavaScript

"use strict";
(function () {
'use strict';
angular.module('myitsmApp')
.factory('expressionEvaluatorService', ['expressionSyntaxTreeBuilder', 'objectValueMapperService', 'keywordEvaluatorService', 'functionEvaluatorService', function (expressionSyntaxTreeBuilder, objectValueMapperService, keywordEvaluatorService, functionEvaluatorService) {
var parsedExpressionCache = {};
function andHandler(a, b) {
return Boolean(a) && Boolean(b);
}
function orHandler(a, b) {
return Boolean(a) || Boolean(b);
}
function likeHandler(a, b) {
if (_.isString(a) && _.isString(b)) {
if (a.length > b.length) {
return b && _.includes(a, b);
}
else {
return a && _.includes(b, a);
}
}
else if (_.isArray(a)) {
return _.includes(a, b);
}
else if (_.isArray(b)) {
return _.includes(b, a);
}
else {
return false;
}
}
function checkForNullValues(input) {
if ((_.isString(input) && input.toUpperCase() === 'NULL') || input === null) {
input = '';
}
return input;
}
var operators = {
'==': function (a, b) {
return Boolean(checkForNullValues(a) === checkForNullValues(b));
},
'+': function (a, b) {
if (_.isUndefined(a) || (a === null && _.isString(b))) {
return b;
}
else if (_.isUndefined(b) || (b === null && _.isString(a))) {
return a;
}
else if (a === null && b === null) {
return null;
}
return a + b;
},
'-': function (a, b) {
if (_.isUndefined(a)) {
a = 0;
}
if (_.isUndefined(b)) {
b = 0;
}
return a - b;
},
'*': function (a, b) {
if (_.isUndefined(a)) {
a = 0;
}
if (_.isUndefined(b)) {
b = 0;
}
return a * b;
},
'/': function (a, b) {
if (_.isUndefined(a) || _.isUndefined(b)) {
return 0;
}
return a / b;
},
'<': function (a, b) {
return a < b;
},
'>': function (a, b) {
return a > b;
},
'>=': function (a, b) {
return a >= b;
},
'<=': function (a, b) {
return a <= b;
},
'!=': function (a, b) {
return checkForNullValues(a) !== checkForNullValues(b);
},
'%': function (a, b) {
if (_.isUndefined(a) || _.isUndefined(b)) {
return 0;
}
return a % b;
},
'&&': andHandler,
'||': orHandler,
'LIKE': likeHandler,
'like': likeHandler
};
var unaryOperators = {
'-': function (value) {
if (_.isUndefined(value)) {
return 0;
}
else {
return -Number(value);
}
},
'!': function (value) {
return !Boolean(value);
}
};
function parseExpression(expression) {
if (!parsedExpressionCache[expression]) {
var parsedExpression = expressionSyntaxTreeBuilder(expression);
if (validateChildNodeTypes(parsedExpression)) {
parsedExpressionCache[expression] = parsedExpression;
}
else {
throw new Error('Invalid syntax found during parsing');
}
}
return parsedExpressionCache[expression];
}
function validateChildNodeTypes(node) {
var isValid = false;
switch (node.type) {
case 'LogicalExpression':
case 'BinaryExpression':
isValid = validateChildNodeTypes(node.left) && validateChildNodeTypes(node.right);
break;
case 'CallExpression':
isValid = true;
break;
case 'UnaryExpression':
isValid = validateChildNodeTypes(node.argument);
break;
case 'Identifier':
case 'Literal':
isValid = true;
break;
case 'ConditionalExpression':
isValid = true;
break;
}
return isValid;
}
function _evaluateNode(node) {
var left, right, nodeName, fieldname;
switch (node.type) {
case 'CallExpression':
var args = node.arguments, callee = node.callee.name, argsEvaluated = [];
if (_.indexOf(['ISREQUIRED', 'ISREADONLY', 'ISHIDDEN', 'ONCHANGE'], callee) > -1) {
_.each(args, function (arg) {
if (arg.type === 'Literal') {
if (_.startsWith(arg.raw, "'$")) {
nodeName = arg.raw.replace(/['$]/g, '');
argsEvaluated.push(nodeName);
}
}
else {
if (_.startsWith(arg.name, '$')) {
nodeName = arg.name.substr(1, arg.name.length - 1);
argsEvaluated.push(nodeName);
}
else {
throw new Error('Function ' + callee + ' has invalid arguments.');
}
}
});
}
else {
_.each(args, function (arg) {
argsEvaluated.push(_evaluateNode(arg));
fieldname = arg.type === 'Identifier' ? arg.name : arg.value;
if (callee.indexOf('SELECTION') !== -1 && fieldname) {
var field = objectValueMapperService.getFieldByName(fieldname.substr(1));
if (field && field.options) {
argsEvaluated.push(field.options);
}
}
});
}
return functionEvaluatorService.execute(callee, argsEvaluated);
case 'ConditionalExpression':
var test = _evaluateNode(node.test);
if (test) {
return _evaluateNode(node.consequent);
}
else {
return _evaluateNode(node.alternate);
}
break;
case 'LogicalExpression':
left = _evaluateNode(node.left);
right = _evaluateNode(node.right);
if (operators[node.operator]) {
var result = operators[node.operator](left, right);
if (_.isNaN(result)) {
throw new Error('Operator: ' + node.operator + ' has invalid arguments: ' + left + ', ' + right);
}
else {
return result;
}
}
else {
throw new Error('Unknown Logical operator: ' + node.operator);
}
break;
case 'BinaryExpression':
left = _evaluateNode(node.left);
right = _evaluateNode(node.right);
if (operators[node.operator]) {
var binaryResult = operators[node.operator](left, right);
if (_.isNaN(binaryResult)) {
throw new Error('Operator: ' + node.operator + ' has invalid arguments: ' + left + ', ' + right);
}
else {
return binaryResult;
}
}
else {
throw new Error('Unknown binary operator: ' + node.operator);
}
break;
case 'Literal':
if (_.startsWith(node.raw, "'$")) {
if (_.endsWith(node.raw, "$'")) {
//'$KEYWORD$'
nodeName = node.raw.replace(/['$]/g, '');
return keywordEvaluatorService.evaluate(nodeName);
}
else {
//'$FIELD_VARIABLE'
nodeName = node.raw.replace(/['$]/g, '');
return objectValueMapperService.getExactValueByFieldName(nodeName);
}
}
else if (node.value === 0 || node.value === false) {
return node.value;
}
else {
return node.value || '';
}
break;
case 'Identifier':
if (_.startsWith(node.name, '$')) {
if (_.endsWith(node.name, '$')) {
//$KEYWORD$
nodeName = node.name.substr(1, node.name.length - 2);
return keywordEvaluatorService.evaluate(nodeName);
}
else {
//$FIELD_VARIABLE
return objectValueMapperService.getExactValueByFieldName(node.name.substr(1));
}
}
else {
return node.name;
}
break;
case 'UnaryExpression':
if (unaryOperators[node.operator]) {
var evaluatedResult = _evaluateNode(node.argument), unaryResult = unaryOperators[node.operator](evaluatedResult);
if (_.isNaN(unaryResult)) {
throw new Error('Operator: ' + node.operator + ' has invalid argument: ' + evaluatedResult);
}
else {
return unaryResult;
}
}
else {
throw new Error('Unknown unary operator: ' + node.operator);
}
break;
default:
throw new Error('Invalid syntax');
}
}
function evaluate(expression) {
var evaluatedExpression = expression;
if (_.isString(expression)) {
expression = _.trim(expression);
if (expression) {
//todo to have helper service to prepare so that all operators are normalized
var preparedExpression = expression, parsedExpression;
try {
parsedExpression = parseExpression(preparedExpression);
}
catch (e) {
throw new Error('Cannot parse expression "' + expression + '": ' + e.message + '.');
}
try {
evaluatedExpression = _evaluateNode(parsedExpression);
}
catch (e) {
throw new Error('Cannot evaluate expression "' + expression + '": ' + e.message + '.');
}
}
else {
evaluatedExpression = null;
}
}
return evaluatedExpression;
}
return {
evaluate: evaluate,
parseExpression: parseExpression,
validateChildNodeTypes: validateChildNodeTypes
};
}]);
})();