317 lines
14 KiB
JavaScript
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 _.contains(a, b);
|
|
}
|
|
else {
|
|
return _.contains(b, a);
|
|
}
|
|
}
|
|
else if (_.isArray(a)) {
|
|
return _.contains(a, b);
|
|
}
|
|
else if (_.isArray(b)) {
|
|
return _.contains(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
|
|
};
|
|
}]);
|
|
})();
|