UNPKG

ui5plugin-parser

Version:
1,100 lines (1,099 loc) 63.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CustomJSClass = void 0; /* eslint-disable @typescript-eslint/no-var-requires */ const commentParser = require("comment-parser"); const path = require("path"); const ParserPool_1 = require("../../../../parser/pool/ParserPool"); const AbstractCustomClass_1 = require("../AbstractCustomClass"); const LineColumn = require("line-column"); const acornLoose = require("acorn-loose"); class CustomJSClass extends AbstractCustomClass_1.AbstractCustomClass { constructor(className, syntaxAnalyser, parser, documentText) { super(className, parser); this.classText = ""; this.parentClassNameDotNotation = ""; this.methods = []; this.fields = []; this.comments = []; this.acornMethodsAndFields = []; this.syntaxAnalyser = syntaxAnalyser; this.fsPath = this.parser.fileReader.getClassFSPathFromClassName(this.className) ?? ""; this._readFileContainingThisClassCode(documentText); //todo: rename. not always reading anyore. this.UIDefine = this._getUIDefine(); this.acornClassBody = this._getThisClassBodyAcorn(); this._fillParentClassName(); this._fillUI5Metadata(); this._fillMethodsAndFields(); this._enrichMemberInfoWithJSDocs(); this._enrichClassInfoWithJSDocs(); this._enrichMethodParamsWithHungarianNotation(); this._fillIsAbstract(); this._enrichVariablesWithJSDocTypesAndVisibility(); } _enrichClassInfoWithJSDocs() { if (this.acornReturnedClassExtendBody) { const comment = this.comments.find(comment => { const positionDifference = this.acornReturnedClassExtendBody.start - comment.end; return positionDifference < 100 && positionDifference > 0; }); const ui5ModelTag = comment?.jsdoc?.tags?.find((tag) => tag.tag === "ui5model"); if (ui5ModelTag) { this.defaultModelClassName = ui5ModelTag.type; } if (comment?.jsdoc?.description) { this.description = comment?.jsdoc?.description; } const abstractTag = comment?.jsdoc?.tags?.find((tag) => tag.tag === "abstract"); if (abstractTag) { this.abstract = true; } } } getMembers() { return super.getMembers(); } _fillIsAbstract() { this.abstract = this.abstract || !!this.methods.find(method => method.abstract) || !!this.fields.find(field => field.abstract); } _enrichMemberInfoWithJSDocs() { if (this.acornClassBody) { //instance methods let methods = this.acornClassBody.properties?.filter((node) => node.value.type === "FunctionExpression" || node.value.type === "ArrowFunctionExpression") || []; const fields = this.acornClassBody.properties?.filter((node) => node.value.type !== "FunctionExpression" && node.value.type !== "ArrowFunctionExpression") || []; //static methods //TODO: Move this const UIDefineBody = this.getUIDefineAcornBody(); if (UIDefineBody && this.classBodyAcornVariableName) { const thisClassVariableAssignments = UIDefineBody.filter((node) => { return (node.type === "ExpressionStatement" && (node.expression?.left?.object?.name === this.classBodyAcornVariableName || node.expression?.left?.object?.object?.name === this.classBodyAcornVariableName)); }); const staticMethods = thisClassVariableAssignments .filter(node => { const assignmentBody = node.expression.right; return (assignmentBody.type === "ArrowFunctionExpression" || assignmentBody.type === "FunctionExpression"); }) .map(node => ({ key: { name: node.expression.left.property.name, start: node.expression.left.property.start, end: node.expression.left.property.end, type: "Identifier" }, value: node.expression.right, start: node.expression.left.object.start, end: node.expression.right.end, type: "Property" })); methods = methods.concat(staticMethods); } this.acornMethodsAndFields = this.acornMethodsAndFields.concat(methods); methods?.forEach((method) => { const methodName = method.key.name || method.key.value; const params = method.value.params; const comment = this.comments.find(comment => { const positionDifference = method.start - comment.end; return positionDifference < 15 && positionDifference > 0; }); if (comment) { const paramTags = comment.jsdoc?.tags?.filter((tag) => tag.tag === "param"); const returnTag = comment.jsdoc?.tags?.find((tag) => tag.tag === "return" || tag.tag === "returns"); const asyncTag = comment.jsdoc?.tags?.find((tag) => tag.tag === "async"); const isPrivate = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "private"); const isPublic = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "public"); const isProtected = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "protected"); const isIgnored = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "ui5ignore"); const isAbstract = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "abstract"); const isStatic = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "static"); const isDeprecated = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "deprecated"); const UIMethod = this.methods.find(method => method.name === methodName); if (paramTags && UIMethod) { paramTags.forEach((tag) => { this._fillParamJSTypesFromTag(tag, params, UIMethod); }); } if (UIMethod) { if (isPrivate || isPublic || isProtected) { UIMethod.visibility = isPrivate ? "private" : isProtected ? "protected" : isPublic ? "public" : UIMethod.visibility; } if (asyncTag) { UIMethod.returnType = "Promise"; } if (returnTag) { UIMethod.returnType = returnTag.type; } if (comment.jsdoc) { UIMethod.description = comment.jsdoc.description; } if (isIgnored) { UIMethod.ui5ignored = true; } if (isAbstract) { UIMethod.abstract = true; } if (isStatic) { UIMethod.static = true; } if (isDeprecated) { UIMethod.deprecated = true; } if (paramTags) { UIMethod.params.forEach((param, i) => { const jsDocParam = paramTags[i]; if (jsDocParam) { param.isOptional = jsDocParam.optional; } }); } } } }); fields.forEach((field) => { const fieldName = field.key.name || field.key.value; const comment = this.comments.find(comment => { const positionDifference = field.start - comment.end; return positionDifference < 15 && positionDifference > 0; }); if (comment) { const isPrivate = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "private"); const isPublic = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "public"); const isProtected = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "protected"); const fieldType = comment.jsdoc?.tags?.find((tag) => tag.tag === "type"); const ui5ignored = comment.jsdoc?.tags?.find((tag) => tag.tag === "ui5ignore"); const isAbstract = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "abstract"); const isStatic = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "static"); const isDeprecated = !!comment.jsdoc?.tags?.find((tag) => tag.tag === "deprecated"); const UIField = this.fields.find(field => field.name === fieldName); if (UIField) { if (isPrivate || isPublic || isProtected) { UIField.visibility = isPrivate ? "private" : isProtected ? "protected" : isPublic ? "public" : UIField.visibility; } if (comment.jsdoc) { UIField.description = comment.jsdoc.description; } if (fieldType) { UIField.type = fieldType.type; } if (ui5ignored) { UIField.ui5ignored = true; } if (isAbstract) { UIField.abstract = true; } if (isStatic) { UIField.static = true; } if (isDeprecated) { UIField.deprecated = true; } } } }); } } _fillParamJSTypesFromTag(tag, params, method) { const tagNameParts = tag.name.split("."); if (tagNameParts.length > 1) { const paramName = tagNameParts.shift(); const param = params.find((param) => param.name === paramName); if (param) { if (!param.customData) { param.customData = {}; } this._fillFieldsRecursively(param.customData, tagNameParts, tag); } } else { const param = params.find((param) => param.name === tag.name); if (param) { param.jsType = tag.type; const UIParam = method.params.find(param => param.name === tag.name); if (UIParam && param.jsType) { UIParam.type = param.jsType; } } } } _fillFieldsRecursively(object, keys, tag) { const key = keys.shift(); if (key) { object[key] = typeof object[key] !== "object" ? {} : object[key]; if (keys.length > 0) { this._fillFieldsRecursively(object[key], keys, tag); } else { object[key] = tag.type; } } } _enrichMethodParamsWithHungarianNotation() { this.methods.forEach(method => { method.params.forEach(param => { if (param.type === "any" || !param.type) { param.type = CustomJSClass.getTypeFromHungarianNotation(param.name) || "any"; } }); }); } _readFileContainingThisClassCode(documentText) { if (!documentText) { documentText = this.parser.fileReader.getDocumentTextFromCustomClassName(this.className); } this.classText = documentText || ""; if (documentText) { try { this.fileContent = acornLoose.parse(documentText, { ecmaVersion: 11, locations: true, onComment: (isBlock, text, start, end, startLoc, endLoc) => { if (isBlock && text?.startsWith("*")) { this.comments.push({ text: text, start: start, end: end, jsdoc: commentParser.parse(`/*${text}*/`)[0], loc: { start: startLoc, end: endLoc } }); } } }); } catch (error) { console.error(error); this.fileContent = null; } } else { this.classExists = false; } } _getUIDefine() { let UIDefine = []; if (this.fileContent) { const args = this._getUIDefineItself()?.expression?.arguments; if (args && args.length >= 2) { const UIDefinePaths = args[0].elements?.map((part) => part.value) || []; const UIDefineClassNames = args[1].params?.map((part) => part.name) || []; UIDefine = UIDefinePaths.filter(path => !!path).map((classPath, index) => { return { path: classPath, className: UIDefineClassNames[index], classNameDotNotation: this._generateClassNameDotNotationFor(classPath), start: args[0].elements[index].start, end: args[0].elements[index].end, node: args[0].elements[index] }; }); } } return UIDefine; } _generateClassNameDotNotationFor(classPath) { let className = classPath.replace(/\//g, "."); if (classPath?.startsWith(".")) { const manifest = ParserPool_1.default.getManifestForClass(this.className); if (manifest && this.fsPath) { const normalizedManifestPath = path.normalize(manifest.fsPath); const importClassPath = path.resolve(path.dirname(this.fsPath), classPath); const relativeToManifest = path.relative(normalizedManifestPath, importClassPath); const pathRelativeToManifestDotNotation = relativeToManifest.split(path.sep).join("."); className = `${manifest.componentName}.${pathRelativeToManifestDotNotation}`; } } if (className.endsWith(".controller")) { className = className.substring(0, className.length - ".controller".length); } return className; } _getThisClassBodyAcorn() { const body = this.fileContent; let classBody; const returnKeyword = this._getReturnKeywordFromBody(); if (returnKeyword && body) { classBody = this._getClassBodyFromPartAcorn(returnKeyword.argument); } return classBody; } _getReturnKeywordFromBody() { let returnKeyword; const UIDefineBody = this.getUIDefineAcornBody(); if (UIDefineBody) { returnKeyword = UIDefineBody.find((body) => body.type === "ReturnStatement"); } return returnKeyword; } _getClassBodyFromPartAcorn(part) { const bodyParts = this.getUIDefineAcornBody(); if (!part || !bodyParts) { return null; } let classBody; if (part.type === "CallExpression") { classBody = this._getClassBodyFromClassExtendAcorn(part); this.acornReturnedClassExtendBody = part; if (classBody) { if (part.callee.object.name) { this._parentVariableName = part.callee.object.name; } else if (part.callee.object.object?.name === "sap" && part.callee.object.property?.name === "ui") { this.parentClassNameDotNotation = this._getParentNameFromManifest() || ""; } } } else if (part.type === "ObjectExpression") { classBody = part; } else if (part.type === "Identifier") { const variable = bodyParts .filter((body) => body.type === "VariableDeclaration") .find((variable) => variable.declarations.find((declaration) => declaration.id.name === part.name)); if (variable) { const neededDeclaration = variable.declarations.find((declaration) => declaration.id.name === part.name); classBody = this._getClassBodyFromPartAcorn(neededDeclaration.init); this.acornReturnedClassExtendBody = neededDeclaration.init; this.classBodyAcornVariableName = part.name; } } return classBody; } _getParentNameFromManifest() { let parentName; const manifest = ParserPool_1.default.getManifestForClass(this.className); if (manifest?.content && manifest?.content["sap.ui5"]?.extends?.extensions && manifest?.content["sap.ui5"]?.extends?.extensions["sap.ui.controllerExtensions"]) { const mControllerExtensions = manifest.content["sap.ui5"].extends.extensions["sap.ui.controllerExtensions"]; parentName = Object.keys(mControllerExtensions).find(sControllerName => { return mControllerExtensions[sControllerName].controllerName === this.className; }); } return parentName; } _getClassBodyFromClassExtendAcorn(part) { let classBody; if (this._isThisPartAClassBodyAcorn(part)) { classBody = part.arguments[1]; } return classBody; } _isThisPartAClassBodyAcorn(part) { const propertyName = part?.callee?.property?.name; return propertyName === "extend" || propertyName === "declareStaticClass" || propertyName === "controller"; } isAssignmentStatementForThisVariable(node) { return (node.type === "AssignmentExpression" && node.operator === "=" && node.left?.type === "MemberExpression" && node.left?.property?.name && node.left?.object?.type === "ThisExpression"); } _fillMethods() { return []; } _fillFields() { return []; } _fillParentClassName() { if (this._parentVariableName) { const parentClassUIDefine = this.UIDefine.find(UIDefine => UIDefine.className === this._parentVariableName); if (parentClassUIDefine) { this.parentClassNameDotNotation = parentClassUIDefine.classNameDotNotation; } } } _fillMethodsAndFields() { if (this.acornClassBody?.properties) { this.acornClassBody.properties.forEach((property) => { const name = property.key?.name || property.key?.value; if (property.value?.type === "FunctionExpression" || property.value?.type === "ArrowFunctionExpression") { const method = { name: name, params: this._generateParamTextForMethod(property.value.params), returnType: property.returnType || property.value.async ? "Promise" : "void", position: property.start, description: "", visibility: name?.startsWith("_") ? "private" : "public", acornParams: property.value.params, node: property.value, isEventHandler: false, owner: this.className, loc: property.key.loc, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }; this.methods.push(method); } else if (property.value?.type === "Identifier" || property.value?.type === "Literal") { this.fields.push({ name: name, type: property.jsType, node: property, description: property.jsType || "", visibility: name?.startsWith("_") ? "private" : "public", owner: this.className, loc: property.key.loc, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); this.acornMethodsAndFields.push(property); } else if (property.value?.type === "ObjectExpression") { this.fields.push({ name: name, type: "map", description: "map", node: property, customData: this._generateCustomDataForObject(property.value), visibility: name?.startsWith("_") ? "private" : "public", owner: this.className, loc: property.key.loc, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); this.acornMethodsAndFields.push(property); } else if (property.value?.type === "MemberExpression") { this.fields.push({ name: name, type: undefined, description: "", node: property, visibility: name?.startsWith("_") ? "private" : "public", owner: this.className, loc: property.key.loc, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); this.acornMethodsAndFields.push(property); } else if (property.value?.type === "ArrayExpression") { this.fields.push({ name: name, type: "any[]", description: "", node: property, visibility: name?.startsWith("_") ? "private" : "public", owner: this.className, loc: property.key.loc, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); this.acornMethodsAndFields.push(property); } else if (property.value?.type === "NewExpression") { this.fields.push({ name: name, type: undefined, description: "", node: property, visibility: name?.startsWith("_") ? "private" : "public", owner: this.className, loc: property.key.loc, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); this.acornMethodsAndFields.push(property); } }); this.acornClassBody.properties?.forEach((property) => { if (property.value?.type === "FunctionExpression" || property.value?.type === "ArrowFunctionExpression") { const assignmentExpressions = this.syntaxAnalyser .expandAllContent(property.value.body) .filter((node) => node.type === "AssignmentExpression"); assignmentExpressions?.forEach((node) => { if (this.isAssignmentStatementForThisVariable(node)) { const field = this.fields.find(field => field.name === node.left.property.name); if (field) { field.type = field.type || node.left.property.name.jsType; field.node = node.left; } else { this.fields.push({ name: node.left.property.name, type: node.left.property.name.jsType, description: node.left.property.name.jsType || "", visibility: node.left.property.name?.startsWith("_") ? "private" : "public", node: node.left, owner: this.className, loc: node.left.property.loc, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); } } }); } }); this._fillMethodsAndFieldsFromPrototype(); //remove duplicates this.fields = this.fields.reduce((accumulator, field) => { const existingField = accumulator.find(accumulatedField => accumulatedField.name === field.name); if (existingField && field.type && !existingField.type) { accumulator[accumulator.indexOf(existingField)] = field; } else if (!existingField) { accumulator.push(field); } return accumulator; }, []); this.fields.push({ name: "prototype", description: "Prototype of the class", type: this.className, visibility: "public", owner: this.className, static: false, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); } this._fillMethodsFromMetadata(); const constructorMethod = this.methods.find(method => method.name === "constructor"); if (constructorMethod) { constructorMethod.returnType = this.className; } } _generateParamTextForMethod(acornParams) { const params = acornParams.map((param) => { let name = ""; if (param.type === "Identifier") { name = param.name || "Unknown"; } else if (param.type === "AssignmentPattern") { name = param.left?.name || "Unknown"; } else { name = "Unknown"; } return { name: name, description: "", type: param.jsType || "any", isOptional: false }; }); return params; } _generateCustomDataForObject(node, looseObject = {}) { node.properties?.forEach((property) => { looseObject[property.key.name] = {}; if (property.value.type === "ObjectExpression") { this._generateCustomDataForObject(property.value, looseObject[property.key.name]); } }); return looseObject; } getUIDefineAcornBody() { const UIDefineBody = this._getUIDefineItself()?.expression?.arguments[1]?.body?.body || this._getUIDefineItself()?.expression?.arguments[2]?.body?.body; return UIDefineBody; } _getUIDefineItself() { const uiDefineItself = this.fileContent?.body?.find((body) => { return ((body.expression?.arguments?.[1]?.body?.body || body.expression?.arguments?.[2]?.body?.body)); }); return uiDefineItself; } _fillMethodsAndFieldsFromPrototype() { const UIDefineBody = this.getUIDefineAcornBody(); if (UIDefineBody && this.classBodyAcornVariableName) { const thisClassVariableAssignments = UIDefineBody.filter((node) => { return (node.type === "ExpressionStatement" && (node.expression?.left?.object?.name === this.classBodyAcornVariableName || node.expression?.left?.object?.object?.name === this.classBodyAcornVariableName)); }); thisClassVariableAssignments?.forEach(node => { const assignmentBody = node.expression?.right; const isMethod = assignmentBody?.type === "ArrowFunctionExpression" || assignmentBody?.type === "FunctionExpression"; const isField = !isMethod; const name = node?.expression?.left?.property?.name; const isStatic = node.expression?.left?.object?.property?.name !== "prototype"; if (isMethod) { const method = { name: name, params: assignmentBody.params.map((param) => ({ name: param.name, description: `${param.name} parameter`, type: param.jsType || "" })), returnType: assignmentBody.returnType || assignmentBody.async ? "Promise" : "void", position: node.expression.left.property.start, description: "", visibility: name?.startsWith("_") ? "private" : "public", acornParams: assignmentBody.params, node: assignmentBody, isEventHandler: false, owner: this.className, loc: node.expression.left.property.loc, static: isStatic, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }; this.methods.push(method); } else if (isField) { this.fields.push({ name: name, visibility: name?.startsWith("_") ? "private" : "public", type: typeof assignmentBody.value, description: assignmentBody.jsType || "", node: node.expression.left, owner: this.className, loc: node.expression.left.property.loc, static: isStatic, abstract: false, deprecated: false, ui5ignored: false, mentionedInTheXMLDocument: false }); } }); } } static generateDescriptionForMethod(method) { return `(${method.params.map(param => param.name).join(", ")}) : ${method.returnType ? method.returnType : "void"}`; } fillTypesFromHungarionNotation() { this.fields.forEach(field => { if (!field.type) { field.type = CustomJSClass.getTypeFromHungarianNotation(field.name); } }); } static getTypeFromHungarianNotation(variable = "") { let type; if (variable.length >= 2) { const map = { $: "Element", o: "object", a: "any[]", i: "int", f: "float", m: "map", s: "string", b: "boolean", p: "Promise", d: "Date", r: "RegExp", v: "any", fn: "function" }; variable = variable.replace("_", "").replace("this.", ""); const firstChar = variable[0]; const secondChar = variable[1]; if (firstChar && secondChar && map[firstChar] && secondChar === secondChar.toUpperCase()) { type = map[firstChar]; } } return type; } _fillMethodsFromMetadata() { const additionalMethods = []; this._fillPropertyMethods(additionalMethods); this._fillAggregationMethods(additionalMethods); this._fillEventMethods(additionalMethods); this._fillAssociationMethods(additionalMethods); this.methods = this.methods.concat(additionalMethods); } _fillPropertyMethods(aMethods) { this.properties?.forEach(property => { const propertyWithFirstBigLetter = `${property.name[0].toUpperCase()}${property.name.substring(1, property.name.length)}`; const getterName = `get${propertyWithFirstBigLetter}`; const setterName = `set${propertyWithFirstBigLetter}`; aMethods.push({ name: getterName, description: `Getter for property ${property.name}`, params: [], returnType: property.type || "void", visibility: property.visibility, isEventHandler: false, owner: this.className, static: false, abstract: false, deprecated: false, mentionedInTheXMLDocument: false, ui5ignored: false }); aMethods.push({ name: setterName, description: `Setter for property ${property.name}`, params: [ { name: `v${propertyWithFirstBigLetter}`, type: "any", description: "Property for setting its value", isOptional: false } ], returnType: this.className, visibility: property.visibility, isEventHandler: false, owner: this.className, static: false, abstract: false, deprecated: false, mentionedInTheXMLDocument: false, ui5ignored: false }); }); } _fillAggregationMethods(additionalMethods) { this.aggregations?.forEach(aggregation => { if (!aggregation.singularName) { return; } const aggregationWithFirstBigLetter = `${aggregation.singularName[0].toUpperCase()}${aggregation.singularName.substring(1, aggregation.singularName.length)}`; let aMethods = []; if (aggregation.multiple) { aMethods = [ { name: `get${aggregationWithFirstBigLetter}s`, returnType: `${aggregation.type}[]`, params: [] }, { name: `add${aggregationWithFirstBigLetter}`, returnType: this.className, params: [ { name: `v${aggregationWithFirstBigLetter}`, type: aggregation.type, description: aggregation.type, isOptional: false } ] }, { name: `insert${aggregationWithFirstBigLetter}`, returnType: this.className, params: [ { name: `v${aggregationWithFirstBigLetter}`, type: aggregation.type, description: aggregation.type, isOptional: false }, { name: `v${aggregationWithFirstBigLetter}`, type: "number", description: "index the item should be inserted at", isOptional: false } ] }, { name: `indexOf${aggregationWithFirstBigLetter}`, returnType: "int", params: [ { name: `v${aggregationWithFirstBigLetter}`, type: aggregation.type, description: aggregation.type, isOptional: false } ] }, { name: `remove${aggregationWithFirstBigLetter}`, returnType: `${aggregation.type}`, params: [ { name: `v${aggregationWithFirstBigLetter}`, type: aggregation.type, description: aggregation.type, isOptional: false } ] }, { name: `removeAll${aggregationWithFirstBigLetter}s`, returnType: `${aggregation.type}[]`, params: [] }, { name: `destroy${aggregationWithFirstBigLetter}s`, returnType: this.className, params: [] }, { name: `bind${aggregationWithFirstBigLetter}s`, returnType: this.className, params: [ { name: "oBindingInfo", type: "object", description: "The binding information", isOptional: false } ] }, { name: `unbind${aggregationWithFirstBigLetter}s`, returnType: this.className, params: [] } ]; } else { aMethods = [ { name: `get${aggregationWithFirstBigLetter}`, returnType: `${aggregation.type}`, params: [] }, { name: `set${aggregationWithFirstBigLetter}`, returnType: this.className, params: [ { name: `v${aggregationWithFirstBigLetter}`, type: aggregation.type, description: aggregation.type, isOptional: false } ] }, { name: `bind${aggregationWithFirstBigLetter}`, returnType: this.className, params: [ { name: "oBindingInfo", type: "object", description: "The binding information", isOptional: false } ] }, { name: `unbind${aggregationWithFirstBigLetter}`, returnType: this.className, params: [] } ]; } aMethods.forEach(method => { additionalMethods.push({ name: method.name, description: `Generic method from "${aggregation.name}" aggregation`, params: method.params, returnType: method.returnType, visibility: aggregation.visibility, isEventHandler: false, owner: this.className, static: false, abstract: false, deprecated: false, mentionedInTheXMLDocument: false, ui5ignored: false }); }); }); } _fillEventMethods(aMethods) { this.events?.forEach(event => { const eventWithFirstBigLetter = `${event.name[0].toUpperCase()}${event.name.substring(1, event.name.length)}`; const aEventMethods = [ { name: `fire${eventWithFirstBigLetter}`, params: [ { name: "mEventParams", type: "map", isOptional: true, description: "Event params" } ] }, { name: `attach${eventWithFirstBigLetter}`, params: [ { name: "fnHandler", type: "function", isOptional: false, description: "Event Handler" }, { name: "oContext", type: "object", isOptional: true, description: "context of the event handler" } ] }, { name: `detach${eventWithFirstBigLetter}`, params: [ { name: "fnHandler", type: "function", isOptional: false, description: "Event Handler" }, { name: "oContext", type: "object", isOptional: true, description: "context of the event handler" } ] } ]; aEventMethods?.forEach(eventMethod => { aMethods.push({ name: eventMethod.name, description: `Generic method for event ${event.name}`, params: eventMethod.params, returnType: this.className, visibility: event.visibility, isEventHandler: false, owner: this.className, static: false, abstract: false, deprecated: false, mentionedInTheXMLDocument: false, ui5ignored: false }); }); }); } _fillAssociationMethods(additionalMethods) { this.associations?.forEach(association => { const associationWithFirstBigLetter = `${association.singularName[0].toUpperCase()}${association.singularName.substring(1, association.singularName.length)}`; let aMethods = []; if (association.multiple) { aMethods = [ { name: `get${associationWithFirstBigLetter}`, params: [] }, { name: `add${associationWithFirstBigLetter}`, params: [ { name: `v${associationWithFirstBigLetter}`, type: association.type || "any", isOptional: false, description: `Add ${associationWithFirstBigLetter}` } ] }, { name: `remove${associationWithFirstBigLetter}`, params: [ { name: `v${associationWithFirstBigLetter}`, type: association.type || "any", isOptional: false, description: `Remove ${associationWithFirstBigLetter}` } ] }, { name: `removeAll${associationWithFirstBigLetter}s`, params: [] } ]; } else { aMethods = [ { name: `get${associationWithFirstBigLetter}`, params: [] }, { name: `set${associationWithFirstBigLetter}`, params: [ { name: `v${associationWithFirstBigLetter}`, type: association.type || "any", isOptional: false, description: `Set ${associationWithFirstBigLetter}` } ] } ]; } aMethods?.forEach(method => { additionalMethods.push({ name: method.name, description: `Generic method from ${association.name} association`, params: method.params, returnType: association.type || this.className, visibility: association.visibility, isEventHandler: false, owner: this.className, static: false, abstract: false, deprecated: false, mentionedInTheXMLDocument: false, ui5ignored: false }); }); }); } _fillUI5Metadata() { if (this.acornClassBody?.properties) { const metadataExists = !!this.acornClassBody.properties?.find((property) => property.key?.name === "metadata" || property.key?.value === "metadata"); const customMetadataExists = !!this.acornClassBody.properties?.find((property) => property.key?.name === "customMetadata" || property.key?.value === "customMetadata"); if (metadataExists) { const metadataObject = this.acornClassBody.properties?.find((property) => property.key?.name === "metadata" || property.key?.value === "metadata"); this.aggregations = this._fillAggregations(metadataObject); this.events = this._fillEvents(metadataObject); this.properties = this._fillProperties(metadataObject); this.associations = this._fillAssociations(metadataObject); this.interfaces = this._fillInterfaces(metadataObject); } if (customMetadataExists) { const customMetadataObject = this.acornClassBody.properties?.find((property) => property.key?.name === "customMetadata" || property.key?.value === "customMetadata"); this.associations.push(...this._fillAssociations(customMetadataObject));