@sap/odata-v4
Version:
OData V4.0 server library
259 lines (237 loc) • 8.78 kB
JavaScript
'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;