ui5plugin-parser
Version:
989 lines • 67.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AcornSyntaxAnalyzer = void 0;
const ParserPool_1 = require("../../../parser/pool/ParserPool");
const CustomJSClass_1 = require("../ui5class/js/CustomJSClass");
const FieldsAndMethodForPositionBeforeCurrentStrategy_1 = require("./typesearch/FieldsAndMethodForPositionBeforeCurrentStrategy");
const InnerPropertiesStrategy_1 = require("./typesearch/InnerPropertiesStrategy");
class AcornSyntaxAnalyzer {
constructor(parser) {
this.declarationStack = [];
this.parser = parser;
}
getFieldsAndMethodsOfTheCurrentVariable(document, position) {
let fieldsAndMethods;
const aStrategies = [
new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this, this.parser),
new InnerPropertiesStrategy_1.InnerPropertiesStrategy(this, this.parser)
];
aStrategies.find(strategy => {
fieldsAndMethods = strategy.getFieldsAndMethods(document, position);
return !!fieldsAndMethods;
});
return fieldsAndMethods;
}
findInnerNode(node, position) {
let innerNode;
if (node.type === "VariableDeclaration") {
const declaration = this.findAcornNode(node.declarations, position - 1);
if (declaration) {
innerNode = declaration.init;
}
}
else if (node.type === "TryStatement") {
innerNode = this.findAcornNode(node.block.body, position);
if (!innerNode && node.handler) {
innerNode = this.findAcornNode(node.handler?.body?.body, position);
}
if (!innerNode && node.finalizer) {
innerNode = this.findAcornNode(node.finalizer?.body, position);
}
}
else if (node.type === "CallExpression") {
innerNode = this.findAcornNode(node.arguments, position);
if (!innerNode) {
innerNode = node.callee;
}
}
else if (node.type === "MemberExpression") {
// innerNode = this.findAcornNode([node.object], position) || this.findAcornNode([node.property], position) || node.object;
innerNode = node.object;
}
else if (node.type === "BlockStatement") {
innerNode = this.findAcornNode(node.body, position);
}
else if (node.type === "ThrowStatement") {
innerNode = node.argument;
}
else if (node.type === "AwaitExpression") {
innerNode = node.argument;
}
else if (node.type === "ExpressionStatement") {
innerNode = node.expression;
}
else if (node.type === "ThisExpression") {
// innerNode = node.object;
}
else if (node.type === "ArrayExpression") {
innerNode = this.findAcornNode(node.elements, position);
}
else if (node.type === "ReturnStatement") {
innerNode = node.argument;
}
else if (node.type === "SpreadElement") {
innerNode = node.argument;
}
else if (node.type === "IfStatement") {
innerNode = this._getIfStatementPart(node, position);
}
else if (node.type === "SwitchStatement") {
innerNode = this._getSwitchStatementPart(node, position);
}
else if (node.type === "AssignmentExpression") {
innerNode = this.findAcornNode([node.right], position);
if (!innerNode) {
innerNode = this.findAcornNode([node.left], position);
}
}
else if (node.type === "BinaryExpression") {
innerNode = node.right && this.findAcornNode([node.right], position);
if (!innerNode && node.left) {
innerNode = this.findAcornNode([node.left], position);
}
}
else if (node.type === "ConditionalExpression") {
innerNode = this._getIfStatementPart(node, position);
}
else if (node.type === "LogicalExpression") {
innerNode = node.right && this.findAcornNode([node.right], position);
if (!innerNode) {
innerNode = node.left && this.findAcornNode([node.left], position);
}
}
else if (node.type === "NewExpression") {
if (node.callee.end > position) {
innerNode = node.callee;
}
else {
innerNode = this.findAcornNode(node.arguments, position);
}
}
else if (node.type === "ObjectExpression") {
innerNode = this.findAcornNode(node.properties, position);
}
else if (node.type === "FunctionDeclaration" ||
node.type === "FunctionExpression" ||
node.type === "ArrowFunctionExpression") {
if (node.body) {
innerNode = this.findAcornNode([node.body], position);
}
if (!innerNode) {
innerNode = this.findAcornNode(node.params, position);
}
}
else if (node.type === "Property") {
if (node.value) {
innerNode = this.findAcornNode([node.value], position);
}
}
else if (node.type === "UnaryExpression") {
if (node.argument) {
innerNode = this.findAcornNode([node.argument], position);
}
}
else if (node.type === "TemplateLiteral") {
if (node.expressions) {
innerNode = this.findAcornNode(node.expressions, position);
}
}
else if (node.type === "WhileStatement" ||
node.type === "DoWhileStatement" ||
node.type === "ForStatement" ||
node.type === "ForInStatement") {
if (node.body) {
innerNode = this.findAcornNode([node.body], position);
}
if (!innerNode && node.test) {
innerNode = this.findAcornNode([node.test], position);
}
}
else if (node.type === "ForOfStatement") {
if (node.body) {
innerNode = this.findAcornNode([node.body], position);
}
if (!innerNode && node.left) {
innerNode = this.findAcornNode([node.left], position);
}
if (!innerNode && node.right) {
innerNode = this.findAcornNode([node.right], position);
}
}
else if (node.type === "AssignmentPattern") {
if (node.left) {
innerNode = this.findAcornNode([node.left], position);
}
if (!innerNode && node.right) {
innerNode = this.findAcornNode([node.right], position);
}
}
else if (node.type === "ArrayPattern") {
if (node.elements) {
innerNode = this.findAcornNode(node.elements, position);
}
}
else if (node.type === "ChainExpression") {
if (node.expression) {
innerNode = this.findAcornNode([node.expression], position);
}
}
return innerNode;
}
_getSwitchStatementPart(node, position) {
let correctPart;
const correctSwitchStatementPart = this.findAcornNode(node.cases, position);
if (correctSwitchStatementPart) {
correctPart = this.findAcornNode(correctSwitchStatementPart.consequent, position);
}
return correctPart;
}
_getIfStatementPart(node, position) {
let correctPart;
if (node.test?.start < position && node.test?.end >= position) {
correctPart = node.test;
}
else if (node.consequent?.start < position && node.consequent?.end >= position) {
correctPart = this.findAcornNode(node.consequent.body || [node.consequent], position);
}
else if (node.alternate) {
correctPart = this._getIfStatementPart(node.alternate, position);
}
else if (node.start < position && node.end >= position && node.type === "BlockStatement") {
correctPart = this.findAcornNode(node.body, position);
}
else if (node.start < position && node.end >= position && node.type !== "IfStatement") {
correctPart = node;
}
return correctPart;
}
findClassNameForStack(stack, currentClassName, primaryClassName = currentClassName, clearStack = false) {
let className = "";
let stackWasModified = false;
if (clearStack) {
this.declarationStack = [];
}
if (stack.length === 0 || !currentClassName) {
return "";
}
const isGetViewException = this._checkForGetViewByIdException(stack, currentClassName);
//this.getView().byId("") exception
if (isGetViewException) {
currentClassName = this._getClassNameFromViewById(stack, primaryClassName);
if (stack.length > 0) {
className = this.findClassNameForStack(stack, currentClassName, primaryClassName, false);
}
else {
className = currentClassName;
}
}
else {
let temporaryCurrentClassName = currentClassName;
//the rest of the cases
const currentNode = stack.shift();
if (currentNode.type === "ThisExpression") {
if (stack.length > 0) {
className = this.findClassNameForStack(stack, currentClassName, primaryClassName, false);
}
else {
className = currentClassName;
}
}
else if (currentNode._acornSyntaxAnalyserType && currentNode._acornSyntaxAnalyserType !== "map") {
className = currentNode._acornSyntaxAnalyserType;
if (stack[0]?.type === "CallExpression") {
stack.shift();
}
}
else if (currentNode.type === "MemberExpression") {
const memberName = currentNode.property?.name;
const isCallOrApply = stack[0]?.type === "MemberExpression" &&
(stack[0]?.property?.name === "call" || stack[0]?.property?.name === "apply");
const isMethod = stack[0]?.type === "CallExpression" || isCallOrApply;
const isArray = currentClassName.endsWith("[]");
if (!isMethod && isArray) {
// className = currentClassName.replace("[]", "");
}
else if (isMethod) {
if (isCallOrApply) {
stack.shift();
}
const callExpression = stack.shift();
if (currentClassName === "sap.ui.core.UIComponent" && memberName === "getRouterFor") {
className = this._getClassNameOfTheRouterFromManifest(primaryClassName);
}
else if (memberName === "getOwnerComponent") {
className = this._getClassNameOfTheComponent(primaryClassName);
}
else if (memberName === "getModel" && callExpression.arguments) {
const modelName = callExpression.arguments[0]?.value || "";
className = this.getClassNameOfTheModelFromManifest(modelName, primaryClassName) || className;
}
if (!className) {
const method = this.findMethodHierarchically(currentClassName, memberName);
if (method) {
if (currentClassName === "sap.ui.base.Event") {
stack.unshift(callExpression);
className = this._handleBaseEventException(currentNode, stack, primaryClassName);
}
if (!className) {
if (!method.returnType ||
method.returnType === "void" ||
method.returnType === "Promise") {
this.findMethodReturnType(method, currentClassName);
}
className = method.returnType;
}
if (className === "map" && method.node) {
currentNode._acornSyntaxAnalyserType = "map";
const UIClass = this.parser.classFactory.getUIClass(currentClassName);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const body = method.node.body?.body;
if (body) {
const returnStatement = body.find((node) => node.type === "ReturnStatement");
if (returnStatement?.argument) {
className = this.getClassNameFromSingleAcornNode(returnStatement.argument, UIClass, stack);
}
}
}
}
}
else {
stack = [];
}
}
}
else {
const field = this._findFieldHierarchically(currentClassName, memberName);
if (field) {
if (!field.type) {
this.findFieldType(field, currentClassName);
className = field.type || "";
}
else {
className = field.type;
}
if (className === "map" && field.node?.value) {
currentNode._acornSyntaxAnalyserType = "map";
const UIClass = this.parser.classFactory.getUIClass(primaryClassName);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
className = this.getClassNameFromSingleAcornNode(field.node.value, UIClass, stack);
}
}
}
if (!className && currentClassName === "map") {
const UIClass = this.parser.classFactory.getUIClass(primaryClassName);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
className = this.getClassNameFromSingleAcornNode(currentNode, UIClass, stack);
}
}
if (!className) {
//if clear stack, there is no type for such cases as mData.Calories.map(mCaloryData => new CaloryData(mCaloryData));
// stack = [];
}
}
}
else if (currentNode.type === "Identifier") {
if (currentNode.name === "sap") {
className = this._generateSAPStandardClassNameFromStack(stack);
}
else {
const UIClass = this.parser.classFactory.getUIClass(currentClassName);
const variableDeclaration = this._getAcornVariableDeclarationFromUIClass(currentClassName, currentNode.name, currentNode.end);
if (variableDeclaration) {
const neededDeclaration = variableDeclaration.declarations.find((declaration) => declaration.id?.name === currentNode.name);
const stackBeforeDeclaration = stack.length;
className = this._getClassNameFromAcornVariableDeclaration(neededDeclaration, UIClass, stack);
stackWasModified = stackBeforeDeclaration !== stack.length;
}
else {
const neededAssignment = this._getAcornAssignmentsFromUIClass(currentClassName, currentNode.name, currentNode.end);
if (neededAssignment) {
className = this.getClassNameFromSingleAcornNode(neededAssignment.right, UIClass, stack);
}
}
if (!className) {
//get class name from sap.ui.define
className = this._getClassNameFromUIDefineDotNotation(currentNode.name, UIClass);
}
if (!className) {
//get class name from method parameters
className = this._getClassNameFromMethodParams(currentNode, UIClass);
}
//if variable is map
if (className?.indexOf("__mapparam__") > -1) {
const fields = stack
.filter(stackPart => stackPart.type === "MemberExpression")
.map(memberExpression => memberExpression.property?.name)
.join(".");
className = `${className}__mapparam__${fields}`;
stack = [];
}
//if variable is the variable of current class
if (!className && currentNode.name === UIClass.classBodyAcornVariableName) {
className = UIClass.className;
}
//if variable is part of .map, .forEach etc
if (!className && currentClassName) {
className = this._getClassNameIfNodeIsParamOfArrayMethod(currentNode, currentClassName);
}
//get hungarian notation type
if ((!className || className === "any" || className === "void") && !stackWasModified) {
className = CustomJSClass_1.CustomJSClass.getTypeFromHungarianNotation(currentNode.name) || "";
}
}
}
else if (currentNode.type === "NewExpression") {
const UIClass = this.parser.classFactory.getUIClass(currentClassName);
if (currentNode.callee?.type === "Identifier") {
className = this._getClassNameFromUIDefineDotNotation(currentNode.callee?.name, UIClass);
}
else if (currentNode.callee?.type === "MemberExpression") {
const newStack = this.expandAllContent(currentNode).reverse();
newStack.pop(); //removes NewExpression
className = this.findClassNameForStack(newStack, currentClassName, primaryClassName, false);
}
}
else if (currentNode.type === "AwaitExpression") {
// const nodesInPromise = this.getContent(currentNode.argument);
// stack = stack.filter((node: any) => !nodesInPromise.includes(node));
// const promiseClassName = this.findClassNameForStack(nodesInPromise, currentClassName, primaryClassName, false);
className = this.getResultOfPromise(currentClassName);
}
if (!currentNode._acornSyntaxAnalyserType &&
!stackWasModified &&
!className?.includes("__map__") &&
className) {
currentNode._acornSyntaxAnalyserType = className;
}
temporaryCurrentClassName = this._handleArrayMethods(stack, primaryClassName, className);
if (temporaryCurrentClassName) {
className = temporaryCurrentClassName;
}
}
if (className?.includes("module:")) {
className = className.replace(/module:/g, "");
className = className.replace(/\//g, ".");
}
if (className && stack.length > 0) {
className = this.findClassNameForStack(stack, className, primaryClassName, false);
}
if (className === "array") {
className = "any[]";
}
return className;
}
getResultOfPromise(className) {
if (/Promise<.*?>/.test(className)) {
className = this._removeOnePromiseLevel(className);
}
else if (className === "Promise") {
className = "any";
}
return className;
}
_removeOnePromiseLevel(className) {
let openedLTCount = 0;
let closedLTCount = 0;
let startIndex = 0;
let endIndex = 0;
let i = 0;
while (i < className.length &&
!(openedLTCount > 0 && closedLTCount > 0 && openedLTCount - closedLTCount === 0)) {
if (className[i] === "<") {
openedLTCount++;
if (openedLTCount === 1) {
startIndex = i;
}
}
if (className[i] === ">") {
closedLTCount++;
endIndex = i;
}
i++;
}
return className.substring(startIndex + 1, endIndex) + className.substring(endIndex + 1, className.length);
}
getClassNameOfTheModelFromManifest(modelName, className, clearStack = false) {
const stackCopy = [...this.declarationStack];
if (clearStack) {
this.declarationStack = [];
}
let modelClassName = "";
const manifest = ParserPool_1.default.getManifestForClass(className);
if (manifest && manifest.content["sap.ui5"]?.models) {
const modelEntry = manifest.content["sap.ui5"].models[modelName];
if (modelEntry?.type) {
modelClassName = modelEntry.type;
}
}
if (!modelClassName) {
const methods = this.parser.classFactory.getClassMethods(className);
const method = methods.find(method => {
let methodFound = false;
if (method.node) {
const content = this.expandAllContent(method.node);
const memberExpression = content.find(content => this._checkOfThisIsCorrectSetModel(content, modelName, method.owner || className));
methodFound = !!memberExpression;
}
return methodFound;
});
if (method?.node) {
const content = this.expandAllContent(method.node);
const memberExpression = content.find(content => this._checkOfThisIsCorrectSetModel(content, modelName, method.owner || className));
if (memberExpression && memberExpression.arguments[0]) {
this.declarationStack = stackCopy;
const model = memberExpression.arguments[0];
const strategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this, this.parser);
if (!this.declarationStack.includes(model)) {
this.declarationStack.push(model);
const stack = strategy.getStackOfNodesForPosition(method.owner || className, model.end, true);
modelClassName = this.findClassNameForStack(stack, method.owner || className) || "";
}
else {
this.declarationStack = [];
}
}
}
}
return modelClassName;
}
_checkOfThisIsCorrectSetModel(content, modelName, className) {
let bIsSetModelMethod = content.type === "CallExpression" &&
content.callee?.property?.name === "setModel" &&
(content.arguments[1]?.value || "") === modelName;
if (bIsSetModelMethod) {
const position = content.callee.property.start;
const strategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this, this.parser);
const classNameAtCurrentPosition = strategy.getClassNameOfTheVariableAtPosition(className, position);
bIsSetModelMethod =
classNameAtCurrentPosition === className || classNameAtCurrentPosition === "sap.ui.core.mvc.View";
}
return bIsSetModelMethod;
}
_getClassNameOfTheRouterFromManifest(className) {
let routerClassName = "";
const manifest = ParserPool_1.default.getManifestForClass(className);
if (manifest && manifest.content["sap.ui5"]?.routing?.config?.routerClass) {
routerClassName = manifest.content["sap.ui5"].routing.config.routerClass;
}
if (!routerClassName) {
const manifests = ParserPool_1.default.getAllManifests();
const manifest = manifests.find(manifest => {
return manifest.content["sap.ui5"]?.routing?.config?.routerClass;
});
if (manifest) {
routerClassName = manifest.content["sap.ui5"].routing.config.routerClass;
}
}
return routerClassName;
}
_getClassNameOfTheComponent(className) {
let componentClassName = "";
const manifest = ParserPool_1.default.getManifestForClass(className);
if (manifest && manifest.content["sap.app"]?.id) {
componentClassName = `${manifest.content["sap.app"]?.id}.Component`;
}
return componentClassName;
}
_handleBaseEventException(node, stack, primaryClassName) {
let className = "";
const callExpression = stack.shift();
const UIClass = this.parser.classFactory.getUIClass(primaryClassName);
if (UIClass instanceof CustomJSClass_1.CustomJSClass && node.property?.name) {
const methodName = node.property.name;
const eventData = this.getEventHandlerData(node, primaryClassName);
if (eventData) {
if (methodName === "getSource") {
className = eventData.className;
}
else if (methodName === "getParameter") {
if (callExpression && callExpression.arguments && callExpression.arguments[0]) {
const parameterName = callExpression.arguments[0].value;
const parameters = this.getParametersOfTheEvent(eventData.eventName, eventData.className);
const parameter = parameters?.find(param => param.name === parameterName);
if (parameter) {
className = parameter.type;
}
}
}
}
}
return className;
}
getParametersOfTheEvent(eventName, className) {
const events = this.parser.classFactory.getClassEvents(className);
const event = events.find(event => event.name === eventName);
return event?.params;
}
getEventHandlerData(node, className) {
let eventHandlerData;
const UIClass = this.parser.classFactory.getUIClass(className);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const currentClassEventHandlerName = this._getEventHandlerName(node, className);
const viewOfTheController = this.parser.fileReader.getViewForController(className);
if (viewOfTheController && currentClassEventHandlerName) {
eventHandlerData = this._getEventHandlerDataFromXMLText(viewOfTheController, currentClassEventHandlerName);
if (!eventHandlerData) {
viewOfTheController.fragments.find(fragment => {
eventHandlerData = this._getEventHandlerDataFromXMLText(fragment, currentClassEventHandlerName);
return !!eventHandlerData;
});
}
}
if (currentClassEventHandlerName && !eventHandlerData) {
// const fragmentsOfTheController = UI5Plugin.getInstance().fileReader.getFragmentsForClass(className);
const UIClass = this.parser.classFactory.getUIClass(className);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const fragmentsOfTheController = this.parser.classFactory.getViewsAndFragmentsOfControlHierarchically(UIClass, [], true, true, true).fragments;
fragmentsOfTheController.find(fragmentOfTheController => {
eventHandlerData = this._getEventHandlerDataFromXMLText(fragmentOfTheController, currentClassEventHandlerName);
return !!eventHandlerData;
});
if (!eventHandlerData) {
eventHandlerData = this.getEventHandlerDataFromJSClass(className, currentClassEventHandlerName);
}
}
}
}
return eventHandlerData;
}
getEventHandlerDataFromJSClass(className, eventHandlerName) {
let eventHandlerData;
const UIClass = this.parser.classFactory.getUIClass(className);
const strategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this, this.parser);
const eventHandler = UIClass.methods.find(method => method.name === eventHandlerName);
if (eventHandler) {
let eventHandlerNode = null;
UIClass.methods.find(method => {
if (method.node) {
const callExpressions = this.expandAllContent(method.node).filter((node) => node.type === "CallExpression");
callExpressions.find((callExpression) => {
if (callExpression.arguments &&
callExpression.arguments.length > 0 &&
callExpression.callee?.property?.name) {
const attachMethodName = callExpression.callee.property.name;
const eventMethodNameCapital = attachMethodName.replace("attach", "");
let eventName = `${eventMethodNameCapital[0].toLowerCase()}${eventMethodNameCapital.substring(1, eventMethodNameCapital.length)}`;
let firstArgument, secondArgument;
if (eventName === "event") {
eventName = callExpression.arguments[0].value;
firstArgument = callExpression.arguments[1];
secondArgument = callExpression.arguments[2];
}
else {
firstArgument = callExpression.arguments[0];
secondArgument = callExpression.arguments[1];
}
if (firstArgument?.type === "MemberExpression" &&
firstArgument?.object?.type === "ThisExpression") {
eventHandlerNode = firstArgument;
}
else if (secondArgument?.type === "MemberExpression" &&
secondArgument?.object?.type === "ThisExpression") {
eventHandlerNode = secondArgument;
}
if (eventHandlerNode && eventHandlerNode.property?.name === eventHandler.name) {
const className = strategy.acornGetClassName(UIClass.className, callExpression.callee.property.start, false);
if (className) {
const events = this.parser.classFactory.getClassEvents(className);
if (events.find(event => event.name === eventName)) {
eventHandlerData = {
className: className,
eventName: eventName,
node: eventHandlerNode
};
}
}
else {
eventHandlerNode = null;
}
}
else {
eventHandlerNode = null;
}
}
return !!eventHandlerNode;
});
}
return !!eventHandlerNode;
});
}
return eventHandlerData;
}
_getEventHandlerDataFromXMLText(viewOrFragment, currentClassEventHandlerName) {
let eventHandlerData;
const tagsAndAttributes = this.parser.xmlParser.getXMLFunctionCallTagsAndAttributes(viewOrFragment, currentClassEventHandlerName);
if (tagsAndAttributes.length > 0) {
const { tag, attributes } = tagsAndAttributes[0];
const attribute = attributes[0];
const { attributeName } = this.parser.xmlParser.getAttributeNameAndValue(attribute);
const eventName = attributeName;
if (eventName) {
const tagPrefix = this.parser.xmlParser.getTagPrefix(tag.text);
const classNameOfTheTag = this.parser.xmlParser.getClassNameFromTag(tag.text);
if (classNameOfTheTag) {
const libraryPath = this.parser.xmlParser.getLibraryPathFromTagPrefix(viewOrFragment, tagPrefix, tag.positionBegin);
const classOfTheTag = [libraryPath, classNameOfTheTag].join(".");
eventHandlerData = {
className: classOfTheTag,
eventName: eventName
};
}
}
}
return eventHandlerData;
}
_getEventHandlerName(node, className) {
let eventHandlerName = "";
const UIClass = this.parser.classFactory.getUIClass(className);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const eventHandlerMethod = UIClass.methods.find(method => {
let correctMethod = false;
if (method.node) {
correctMethod = method.node.start < node.start && method.node.end > node.start;
}
return correctMethod;
});
if (eventHandlerMethod) {
eventHandlerName = eventHandlerMethod.name;
}
}
return eventHandlerName;
}
_handleArrayMethods(stack, currentClassName, variableClassName) {
let className = "";
//if it is map, filter or find
const arrayMethods = ["map", "filter", "find"];
const propertyName = stack[0]?.property?.name;
if (stack.length >= 2 &&
stack[0].type === "MemberExpression" &&
stack[1].type === "CallExpression" &&
arrayMethods.includes(propertyName)) {
if (propertyName === "map") {
const returnClass = stack[1].arguments[0];
let returnStatement;
if (returnClass?.body?.body) {
returnStatement = returnClass?.body?.body?.find((node) => node.type === "ReturnStatement")?.argument;
}
else {
returnStatement = returnClass?.body;
}
if (returnStatement) {
const strategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this, this.parser);
const newStack = strategy.getStackOfNodesForPosition(currentClassName, returnStatement.end, true);
className =
this.findClassNameForStack(newStack, currentClassName) ||
(typeof returnStatement.value === "undefined" ? "any" : typeof returnStatement.value);
}
if (propertyName === "map") {
className = `${className}[]`;
}
}
else if (propertyName === "filter") {
className = variableClassName;
}
else if (propertyName === "find") {
if (variableClassName === "array") {
variableClassName = "any[]";
}
className = variableClassName.replace("[]", "");
}
stack.splice(0, 2);
className = this._handleArrayMethods(stack, currentClassName, className);
}
else {
className = variableClassName;
}
return className;
}
_getClassNameIfNodeIsParamOfArrayMethod(identifierNode, currentClassName) {
let className = "";
if (!this.declarationStack.includes(identifierNode) ||
(this.declarationStack.length === 1 && this.declarationStack[0] === identifierNode)) {
const UIClass = this.parser.classFactory.getUIClass(currentClassName);
this.declarationStack.push(identifierNode);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const acornMethod = this.findAcornNode(UIClass.acornMethodsAndFields, identifierNode.end);
if (acornMethod) {
const content = this.expandAllContent(acornMethod.value);
const node = this._getCallExpressionNodeWhichIsArrayMethod(content, identifierNode.end);
if (node) {
const isFirstParamOfArrayMethod = node.arguments[0]?.params && node.arguments[0]?.params[0]?.name === identifierNode.name;
if (isFirstParamOfArrayMethod) {
const strategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this, this.parser);
className =
strategy.acornGetClassName(currentClassName, node.callee.object.end + 1, false) || "";
if (className.endsWith("[]")) {
className = className.replace("[]", "");
}
else if (className.toLowerCase() === "array") {
className = "any";
}
}
}
}
}
}
return className;
}
_getCallExpressionNodeWhichIsArrayMethod(nodes, position) {
const content = nodes
.filter(content => content.type === "CallExpression" && this._isArrayMethod(content.callee?.property?.name))
.reverse();
return this.findAcornNode(content, position);
}
_isArrayMethod(methodName) {
const arrayMethods = ["forEach", "map", "filter", "find"];
return arrayMethods.indexOf(methodName) > -1;
}
_generateSAPStandardClassNameFromStack(stack) {
const classNameParts = [];
let usedNodeCount = 0;
let node = stack[usedNodeCount];
while (node && node.type === "MemberExpression") {
if (node.object.type === "Identifier") {
classNameParts.push(node.object.name);
node.object._acornSyntaxAnalyserType = classNameParts.join(".");
}
classNameParts.push(node.property.name);
node._acornSyntaxAnalyserType = classNameParts.join(".");
usedNodeCount++;
node = stack[usedNodeCount];
const UIClass = this.parser.classFactory.getUIClass(classNameParts.join("."));
if (UIClass.classExists) {
const node = this.parser.nodeDAO.findNode(UIClass.className);
if (node?.getMetadata()?.getRawMetadata()?.kind === "class") {
break;
}
}
}
if (stack[usedNodeCount]?.type === "CallExpression") {
//this means that last MemberExpression was related to the method name, not to the class name
classNameParts.pop();
usedNodeCount--;
node = stack[usedNodeCount];
if (node && node._acornSyntaxAnalyserType) {
node._acornSyntaxAnalyserType = null;
}
}
stack.splice(0, usedNodeCount);
return classNameParts.join(".");
}
_checkForGetViewByIdException(stack, className) {
let isGetViewByIdException = false;
if ((className === "sap.ui.core.mvc.View" ||
className === "sap.ui.core.mvc.View|undefined" ||
this.parser.classFactory.isClassAChildOfClassB(className, "sap.ui.core.Control") ||
this.parser.classFactory.isClassAChildOfClassB(className, "sap.ui.core.mvc.Controller")) &&
stack.length > 1 &&
stack[0].property?.name === "byId" &&
stack[1].arguments?.length === 1) {
isGetViewByIdException = true;
}
return isGetViewByIdException;
}
_getClassNameFromViewById(stack, currentControllerName) {
let className = "";
if (stack.length > 1) {
const callExpression = stack[1];
stack.splice(0, 2);
const controlId = callExpression.arguments[0]?.value;
if (controlId) {
className = this.parser.fileReader.getClassNameFromView(currentControllerName, controlId) || "";
}
}
return className;
}
findMethodReturnType(method, className, includeParentMethods = true, clearStack = false) {
if (clearStack) {
this.declarationStack = [];
}
const UIClass = this.parser.classFactory.getUIClass(className);
if (method.returnType === "void") {
const innerMethod = UIClass.methods.find(innermethod => method.name === innermethod.name);
if (innerMethod && innerMethod.returnType !== "void") {
method.returnType = innerMethod.returnType;
}
else if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const methodNode = UIClass.acornMethodsAndFields?.find((property) => property.key.name === method.name);
if (methodNode) {
const methodBody = methodNode?.value?.body?.body;
const returnStatement = methodBody?.find((bodyPart) => bodyPart.type === "ReturnStatement");
if (returnStatement) {
method.returnType =
this.getClassNameFromSingleAcornNode(returnStatement.argument, UIClass) || "void";
}
}
}
if (includeParentMethods &&
(!method.returnType || method.returnType === "void") &&
UIClass.parentClassNameDotNotation) {
this.findMethodReturnType(method, UIClass.parentClassNameDotNotation);
}
}
else if (method.returnType === "Promise") {
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const UIMethod = UIClass.methods.find(innerMethod => innerMethod.name === method.name);
if (UIMethod) {
const methodBody = UIMethod.node?.body?.body;
const returnStatement = methodBody?.find((bodyPart) => bodyPart.type === "ReturnStatement");
if (returnStatement) {
const returnType = this.getClassNameFromSingleAcornNode(returnStatement.argument, UIClass) || "void";
if (returnType) {
if (UIMethod.node?.async) {
method.returnType = `Promise<${returnType}>`;
}
else {
method.returnType = returnType;
}
}
}
}
}
}
if (method.returnType?.includes("__map__")) {
if (method.returnType.endsWith("[]")) {
method.returnType = "map[]";
}
else {
method.returnType = "map";
}
}
}
findFieldType(field, className, includeParentMethods = true, clearStack = false) {
if (![undefined, "any"].includes(field.type)) {
return;
}
const UIClass = this.parser.classFactory.getUIClass(className);
if (clearStack) {
this.declarationStack = [];
}
const innerField = UIClass.fields.find(innerfield => innerfield.name === field.name);
const customField = innerField;
if (customField?.node?.value?.type === "Literal" && UIClass instanceof CustomJSClass_1.CustomJSClass) {
customField.type = this.getClassNameFromSingleAcornNode(customField.node.value, UIClass);
}
if (innerField && innerField.type) {
field.type = innerField.type;
}
else if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
UIClass.acornMethodsAndFields.find((property) => {
let typeFound = false;
if (property.value.type === "FunctionExpression" || property.value.type === "ArrowFunctionExpression") {
const assignmentExpressions = this.expandAllContent(property.value.body).filter((node) => node.type === "AssignmentExpression");
assignmentExpressions.forEach((node) => {
if (UIClass.isAssignmentStatementForThisVariable(node) &&
node?.left?.property?.name === field.name) {
field.type = this.getClassNameFromSingleAcornNode(node.right, UIClass);
}
});
}
else if (property.value.type === "Identifier" && property.key.name === field.name) {
field.type = this._getClassNameFromUIDefineDotNotation(property.value.name, UIClass);
}
else if (property.value.type === "MemberExpression" && property.key.name === field.name) {
const strategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this, this.parser);
const stack = strategy.getStackOfNodesForPosition(className, property.value.end, true);
if (stack.length > 0) {
const lastMember = stack.pop();
const type = this.findClassNameForStack(stack, className);
if (type) {
const fieldsAndMethods = strategy.destructureFieldsAndMethodsAccordingToMapParams(type);
const fieldFromAnotherClass = fieldsAndMethods?.fields.find(field => field.name === lastMember.property.name);
const methodFromAnotherClass = fieldsAndMethods?.methods.find(method => method.name === lastMember.property.name);
if (fieldFromAnotherClass) {
field.type = fieldFromAnotherClass.type;
}
else if (methodFromAnotherClass) {
const UIClass = this.parser.classFactory.getUIClass(className);
UIClass.fields.splice(UIClass.fields.indexOf(field), 1);
UIClass.methods.push({
name: field.name,
description: field.description,
params: methodFromAnotherClass.params,
returnType: methodFromAnotherClass.returnType,
visibility: field.visibility,
owner: UIClass.className,
static: field.static,
abstract: field.abstract,
deprecated: false
});
}
}
}
}
else if (property.value.type === "NewExpression" && property.key.name === field.name) {
field.type = this.getClassNameFromSingleAcornNode(property.value, UIClass);
}
if (field.type) {
typeFound = true;
}
return typeFound;
});
}
if (includeParentMethods && !field.type && UIClass.parentClassNameDotNotation) {
this.findFieldType(field, UIClass.parentClassNameDotNotation);
}
if (!field.type && UIClass instanceof CustomJSClass_1.CustomJSClass) {
field.type = CustomJSClass_1.CustomJSClass.getTypeFromHungarianNotation(field.name);
}
if (field.type?.includes("__map__")) {
field.type = "map";
}
}
_getAcornVariableDeclarationFromUIClass(className, variableName, position) {
let variableDeclaration;
const UIClass = this.parser.classFactory.getUIClass(className);
const functionExpression = UIClass.acornMethodsAndFields?.find((method) => method.start < position && method.end >= position);
const functionParts = functionExpression?.value?.body?.body;
if (functionParts) {
con