UNPKG

ui5plugin-parser

Version:
989 lines 67.4 kB
"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