UNPKG

@sap/odata-v4

Version:

OData V4.0 server library

259 lines (237 loc) 8.78 kB
'use strict'; const Visitor = require('./Visitor'); const ExpressionKind = require('../uri/Expression').ExpressionKind; const UriHelper = require('../uri/UriHelper'); /** * The `next` function which will be called on finish visiting an expression node. * * @callback Next * @param {*} context1 The current expression context * @param {*} context2 The second context to provide to next recursive call * @param {*} stack The current stack */ /** * The ExpressionVisitor visits any provided context recursively regarding to call the provided * next function. * * @extends Visitor */ class ExpressionVisitor extends Visitor { /** * Creates an instance of ExpressionVisitor. */ constructor() { super(); } /** * Sets the type property to target derived form source. * * @param {Expression} source Any type of expression * @param {Object} _target An object to set the type property for */ visitType(source, _target) { const target = _target; target.type = source.getType(); if (target.type) { target.type = target.type.getFullQualifiedName().toString(); } } /** * Sets the properties for a MemberExpression. * * @param {MemberExpression} source The source expression * @param {Object} _target An object to set the expression properties to */ visitMemberExpression(source, _target) { const target = _target; target.nodeType = 'MemberExpression'; target.pathSegments = []; this.visitType(source, target); } /** * Sets the properties for a BinaryExpression. * * @param {BinaryExpression} source The source expression * @param {Object} _target An object to set the expression properties to */ visitBinaryExpression(source, _target) { const target = _target; target.nodeType = 'BinaryExpression'; target.operator = source.getOperator(); this.visitType(source, target); } /** * Sets the properties for a MethodExpression. * * @param {MethodExpression} source The source expression * @param {Object} _target An object to set the expression properties to */ visitMethodExpression(source, _target) { const target = _target; target.nodeType = 'MethodExpression'; target.operator = source.getMethod(); target.parameters = []; this.visitType(source, target); } /** * Sets the properties for a LiteralExpression. * * @param {LiteralExpression} source The source expression * @param {Object} _target An object to set the expression properties to */ visitLiteralExpression(source, _target) { const target = _target; target.nodeType = 'LiteralExpression'; target.value = source.getText(); this.visitType(source, target); } /** * Sets the properties for a AliasExpression. * * @param {AliasExpression} source The source expression * @param {Object} _target An object to set the expression properties to */ visitAliasExpression(source, _target) { const target = _target; target.nodeType = 'AliasExpression'; target.alias = source.getAlias(); this.visitType(source, target); } /** * Sets the properties for a UnaryExpression. * * @param {UnaryExpression} source The source expression * @param {Object} _target An object to set the expression properties to */ visitUnaryExpression(source, _target) { const target = _target; target.nodeType = 'UnaryExpression'; target.operator = source.getOperator(); this.visitType(source, target); } /** * Visits a path segment. * * @param {UriResource} source The uri segment * @param {Object} _target An object to set the expression properties to * @param {Array} stack the current stack * @param {Next} next the callback function to continue */ visitPathSegment(source, _target, stack, next) { const target = _target; const edmType = source.getEdmType(); if (edmType) { target.type = edmType.getFullQualifiedName().toString(); } target.nodeType = source.getKind(); target.name = source.getPathSegmentIdentifier(); const result = this.buildKeyPredicates(source.getKeyPredicates()); if (result) { target.keys = result; } const innerExpression = source.getExpression(); if (innerExpression) { target.expression = {}; next(innerExpression, target.expression, stack); } const expVarName = source.getExpressionVariableName(); if (expVarName) { target.variableName = expVarName; } const func = source.getFunction(); if (func) { const parameters = source.getFunctionParameters(); if (parameters) { target.parameters = parameters.map(param => { const name = param.getEdmRef().getName(); const value = param.getText(); const localParamCtx = { [name]: value }; const expression = param.getExpression(); if (expression) { localParamCtx.expression = {}; next(expression, localParamCtx.expression, stack); } return localParamCtx; }); } } } /** * Build the key predicates. * * @param {EdmKeyPropertyRef[]} keys The keys * @returns {Object} The built keys. Built as key:value pairs */ buildKeyPredicates(keys) { let resultPredicates = UriHelper.buildKeyPredicates(keys); for (let keyPredicate of resultPredicates) { keyPredicate.type = keyPredicate.type.toString(); } return resultPredicates && resultPredicates.length > 0 ? resultPredicates : null; } /** * Visits an expression. Expects that the provided property is an {@link Expression}. * Makes recursive calls to submethods regarding to the type of the provided Expression type. * * @param {Expression} property The expression to visit * @param {*} stack the current stack * @param {*} currentContext The current context to decorate the result of the expression visiting * @param {Next} next The callback to call on finish visiting of an expression node */ visitExpression(property, stack, currentContext, next) { const ctx = currentContext; if (property.getKind() === ExpressionKind.ALIAS) { this.visitAliasExpression(property, ctx); const expression = property.getExpression(); if (expression) { ctx.expression = {}; next(expression, ctx.expression, stack); } } if (property.getKind() === ExpressionKind.UNARY) { this.visitUnaryExpression(property, ctx); const operand = property.getOperand(); if (operand) { ctx.operand = {}; next(operand, ctx.operand, stack); } } if (property.getKind() === ExpressionKind.MEMBER) { this.visitMemberExpression(property, ctx); const pathSegments = property.getPathSegments(); for (const pathSegment of pathSegments) { const localCtx = {}; ctx.pathSegments.push(localCtx); this.visitPathSegment(pathSegment, localCtx, stack, next); } } if (property.getKind() === ExpressionKind.METHOD) { this.visitMethodExpression(property, ctx); const parameters = property.getParameters(); for (const param of parameters) { const localCtx = {}; ctx.parameters.push(localCtx); next(param, localCtx, stack); } } if (property.getKind() === ExpressionKind.BINARY) { this.visitBinaryExpression(property, ctx); const left = property.getLeftOperand(); if (left) { ctx.left = {}; next(left, ctx.left, stack); } const right = property.getRightOperand(); if (right) { ctx.right = {}; next(right, ctx.right, stack); } } if (property.getKind() === ExpressionKind.LITERAL) { this.visitLiteralExpression(property, ctx); } } } module.exports = ExpressionVisitor;