UNPKG

@aaswe/codebase-ai

Version:

AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs

673 lines 28.4 kB
"use strict"; /** * Java AST Analyzer * Implements AST analysis for Java files using java-parser */ Object.defineProperty(exports, "__esModule", { value: true }); exports.JavaAnalyzer = void 0; const java_parser_1 = require("java-parser"); const BaseAnalyzer_1 = require("./BaseAnalyzer"); class JavaAnalyzer extends BaseAnalyzer_1.BaseAnalyzer { constructor() { super('java'); } async parseAST(content, _context) { try { // Use proper Java parser for production-quality AST analysis const cst = (0, java_parser_1.parse)(content); return this.convertCSTToAST(cst); } catch (error) { throw new Error(`Failed to parse Java AST: ${error}`); } } async extractNodes(ast, context) { const nodes = []; // Extract package declaration if (ast.packageDeclaration) { nodes.push({ id: this.generateId(), type: 'PackageDeclaration', name: ast.packageDeclaration.name, startLine: ast.packageDeclaration.line, endLine: ast.packageDeclaration.line, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { packageName: ast.packageDeclaration.name } }); } // Extract import declarations for (const imp of ast.imports || []) { nodes.push({ id: this.generateId(), type: 'ImportDeclaration', name: imp.name, startLine: imp.line, endLine: imp.line, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { importName: imp.name, isStatic: imp.isStatic } }); } // Extract class declarations for (const cls of ast.classes || []) { const classNode = { id: this.generateId(), type: 'ClassDeclaration', name: cls.name, startLine: cls.startLine, endLine: cls.endLine, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { modifiers: cls.modifiers, superClass: cls.superClass, interfaces: cls.interfaces } }; nodes.push(classNode); // Extract methods for (const method of cls.methods || []) { nodes.push({ id: this.generateId(), type: 'MethodDeclaration', name: method.name, startLine: method.startLine, endLine: method.endLine, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: classNode.id, children: [], properties: { modifiers: method.modifiers, returnType: method.returnType, parameters: method.parameters } }); } // Extract fields for (const field of cls.fields || []) { nodes.push({ id: this.generateId(), type: 'FieldDeclaration', name: field.name, startLine: field.line, endLine: field.line, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: classNode.id, children: [], properties: { modifiers: field.modifiers, type: field.type } }); } } return nodes; } async extractFunctions(ast, context) { const functions = []; for (const cls of ast.classes || []) { for (const method of cls.methods || []) { const func = { id: this.generateId(), name: method.name, parameters: this.extractJavaParameters(method.parameters || []), returnType: method.returnType || undefined, complexity: this.calculateJavaMethodComplexity(method), startLine: method.startLine, endLine: method.endLine, filePath: context.filePath, isAsync: false, // Java doesn't have async/await like JS/TS isExported: this.isJavaPublic(method.modifiers), visibility: this.getJavaVisibility(method.modifiers), dependencies: this.extractJavaMethodDependencies(method), calls: this.extractJavaMethodCalls(method) }; functions.push(func); } } return functions; } async extractClasses(ast, context) { const classes = []; for (const cls of ast.classes || []) { const classObj = { id: this.generateId(), name: cls.name, methods: [], properties: this.extractJavaClassProperties(cls), extends: cls.superClass || undefined, implements: cls.interfaces || [], startLine: cls.startLine, endLine: cls.endLine, filePath: context.filePath, isExported: this.isJavaPublic(cls.modifiers), visibility: this.getJavaVisibility(cls.modifiers), isAbstract: this.isJavaAbstract(cls.modifiers) }; classes.push(classObj); } return classes; } async extractImports(ast, context) { const imports = []; for (const imp of ast.imports || []) { const importDecl = { id: this.generateId(), source: imp.name, imports: [{ name: this.getJavaImportName(imp.name), alias: undefined, isDefault: false, isNamespace: imp.name.endsWith('.*') }], filePath: context.filePath, startLine: imp.line, endLine: imp.line }; imports.push(importDecl); } return imports; } async extractExports(ast, context) { const exports = []; // In Java, public classes are effectively "exported" for (const cls of ast.classes || []) { if (this.isJavaPublic(cls.modifiers)) { exports.push({ id: this.generateId(), name: cls.name, type: 'class', isDefault: false, filePath: context.filePath, startLine: cls.startLine, endLine: cls.endLine }); } // Public methods are also exported for (const method of cls.methods || []) { if (this.isJavaPublic(method.modifiers)) { exports.push({ id: this.generateId(), name: `${cls.name}.${method.name}`, type: 'function', isDefault: false, filePath: context.filePath, startLine: method.startLine, endLine: method.endLine }); } } } return exports; } async extractDependencies(ast, _context) { const dependencies = new Set(); // Add imports as dependencies for (const imp of ast.imports || []) { const packageName = imp.name.split('.')[0]; if (packageName !== 'java' && packageName !== 'javax') { dependencies.add(packageName); } } return Array.from(dependencies); } async calculateComplexity(ast, _context) { let cyclomaticComplexity = 1; let cognitiveComplexity = 0; let linesOfCode = 0; for (const cls of ast.classes || []) { linesOfCode += cls.endLine - cls.startLine + 1; for (const method of cls.methods || []) { cyclomaticComplexity += this.calculateJavaMethodComplexity(method); cognitiveComplexity += this.calculateJavaCognitiveComplexity(method); } } const maintainabilityIndex = Math.max(0, 171 - 5.2 * Math.log(linesOfCode) - 0.23 * cyclomaticComplexity); const technicalDebt = Math.max(0, (cyclomaticComplexity - 10) * 0.5 + (cognitiveComplexity - 15) * 0.3); return { cyclomaticComplexity, cognitiveComplexity, linesOfCode, maintainabilityIndex, technicalDebt }; } getParserConfig() { return { language: 'java', sourceType: 'module', allowImportExportEverywhere: false, allowReturnOutsideFunction: false, strictMode: true, plugins: [] }; } // Helper methods for CST to AST conversion convertCSTToAST(cst) { const result = { packageDeclaration: null, imports: [], classes: [] }; if (cst.children?.compilationUnit) { const compilationUnit = cst.children.compilationUnit[0]; // Extract package declaration if (compilationUnit.children?.packageDeclaration) { const packageDecl = compilationUnit.children.packageDeclaration[0]; result.packageDeclaration = this.extractPackageDeclaration(packageDecl); } // Extract imports if (compilationUnit.children?.importDeclaration) { result.imports = compilationUnit.children.importDeclaration.map((imp) => this.extractImportDeclaration(imp)); } // Extract type declarations (classes, interfaces, enums) if (compilationUnit.children?.typeDeclaration) { for (const typeDecl of compilationUnit.children.typeDeclaration) { if (typeDecl.children?.classDeclaration) { result.classes.push(this.extractClassDeclaration(typeDecl.children.classDeclaration[0])); } if (typeDecl.children?.interfaceDeclaration) { result.classes.push(this.extractInterfaceDeclaration(typeDecl.children.interfaceDeclaration[0])); } if (typeDecl.children?.enumDeclaration) { result.classes.push(this.extractEnumDeclaration(typeDecl.children.enumDeclaration[0])); } } } } return result; } extractPackageDeclaration(packageDecl) { const packageName = this.extractQualifiedName(packageDecl.children?.qualifiedName?.[0]); return { name: packageName, line: packageDecl.location?.startLine || 1 }; } extractImportDeclaration(importDecl) { const qualifiedName = this.extractQualifiedName(importDecl.children?.qualifiedName?.[0]); const isStatic = !!importDecl.children?.Static; const isWildcard = !!importDecl.children?.Star; return { name: qualifiedName + (isWildcard ? '.*' : ''), isStatic, line: importDecl.location?.startLine || 1 }; } extractClassDeclaration(classDecl) { const className = this.extractIdentifier(classDecl.children?.normalClassDeclaration?.[0]?.children?.typeIdentifier?.[0]); const modifiers = this.extractModifiers(classDecl.children?.normalClassDeclaration?.[0]?.children?.classModifier); const classBody = classDecl.children?.normalClassDeclaration?.[0]?.children?.classBody?.[0]; const methods = this.extractMethods(classBody); const fields = this.extractFields(classBody); // Extract superclass and interfaces const superClass = this.extractSuperClass(classDecl.children?.normalClassDeclaration?.[0]); const interfaces = this.extractInterfaces(classDecl.children?.normalClassDeclaration?.[0]); return { name: className, modifiers, superClass, interfaces, startLine: classDecl.location?.startLine || 1, endLine: classDecl.location?.endLine || 1, methods, fields }; } extractInterfaceDeclaration(interfaceDecl) { const interfaceName = this.extractIdentifier(interfaceDecl.children?.normalInterfaceDeclaration?.[0]?.children?.typeIdentifier?.[0]); const modifiers = this.extractModifiers(interfaceDecl.children?.normalInterfaceDeclaration?.[0]?.children?.interfaceModifier); const interfaceBody = interfaceDecl.children?.normalInterfaceDeclaration?.[0]?.children?.interfaceBody?.[0]; const methods = this.extractInterfaceMethods(interfaceBody); return { name: interfaceName, modifiers: [...modifiers, 'interface'], superClass: null, interfaces: [], startLine: interfaceDecl.location?.startLine || 1, endLine: interfaceDecl.location?.endLine || 1, methods, fields: [] }; } extractEnumDeclaration(enumDecl) { const enumName = this.extractIdentifier(enumDecl.children?.typeIdentifier?.[0]); const modifiers = this.extractModifiers(enumDecl.children?.classModifier); return { name: enumName, modifiers: [...modifiers, 'enum'], superClass: null, interfaces: [], startLine: enumDecl.location?.startLine || 1, endLine: enumDecl.location?.endLine || 1, methods: [], fields: [] }; } extractMethods(classBody) { const methods = []; if (classBody?.children?.classBodyDeclaration) { for (const bodyDecl of classBody.children.classBodyDeclaration) { if (bodyDecl.children?.classMemberDeclaration?.[0]?.children?.methodDeclaration) { const methodDecl = bodyDecl.children.classMemberDeclaration[0].children.methodDeclaration[0]; methods.push(this.extractMethodDeclaration(methodDecl, bodyDecl)); } if (bodyDecl.children?.classMemberDeclaration?.[0]?.children?.constructorDeclaration) { const constructorDecl = bodyDecl.children.classMemberDeclaration[0].children.constructorDeclaration[0]; methods.push(this.extractConstructorDeclaration(constructorDecl, bodyDecl)); } } } return methods; } extractInterfaceMethods(interfaceBody) { const methods = []; if (interfaceBody?.children?.interfaceMemberDeclaration) { for (const memberDecl of interfaceBody.children.interfaceMemberDeclaration) { if (memberDecl.children?.interfaceMethodDeclaration) { const methodDecl = memberDecl.children.interfaceMethodDeclaration[0]; methods.push(this.extractInterfaceMethodDeclaration(methodDecl, memberDecl)); } } } return methods; } extractFields(classBody) { const fields = []; if (classBody?.children?.classBodyDeclaration) { for (const bodyDecl of classBody.children.classBodyDeclaration) { if (bodyDecl.children?.classMemberDeclaration?.[0]?.children?.fieldDeclaration) { const fieldDecl = bodyDecl.children.classMemberDeclaration[0].children.fieldDeclaration[0]; const extractedFields = this.extractFieldDeclaration(fieldDecl, bodyDecl); fields.push(...extractedFields); } } } return fields; } extractMethodDeclaration(methodDecl, bodyDecl) { const methodName = this.extractIdentifier(methodDecl.children?.methodHeader?.[0]?.children?.methodDeclarator?.[0]?.children?.identifier?.[0]); const returnType = this.extractType(methodDecl.children?.methodHeader?.[0]?.children?.result?.[0]); const modifiers = this.extractModifiers(bodyDecl.children?.modifier); const parameters = this.extractMethodParameters(methodDecl.children?.methodHeader?.[0]?.children?.methodDeclarator?.[0]); const body = this.extractMethodBodyFromCST(methodDecl.children?.methodBody?.[0]); return { name: methodName, returnType, parameters, modifiers, startLine: methodDecl.location?.startLine || 1, endLine: methodDecl.location?.endLine || 1, body }; } extractConstructorDeclaration(constructorDecl, bodyDecl) { const constructorName = this.extractIdentifier(constructorDecl.children?.constructorDeclarator?.[0]?.children?.simpleTypeName?.[0]?.children?.typeIdentifier?.[0]); const modifiers = this.extractModifiers(bodyDecl.children?.modifier); const parameters = this.extractMethodParameters(constructorDecl.children?.constructorDeclarator?.[0]); const body = this.extractMethodBodyFromCST(constructorDecl.children?.constructorBody?.[0]); return { name: constructorName, returnType: 'void', parameters, modifiers, startLine: constructorDecl.location?.startLine || 1, endLine: constructorDecl.location?.endLine || 1, body }; } extractInterfaceMethodDeclaration(methodDecl, memberDecl) { const methodName = this.extractIdentifier(methodDecl.children?.methodHeader?.[0]?.children?.methodDeclarator?.[0]?.children?.identifier?.[0]); const returnType = this.extractType(methodDecl.children?.methodHeader?.[0]?.children?.result?.[0]); const modifiers = this.extractModifiers(memberDecl.children?.interfaceMethodModifier); const parameters = this.extractMethodParameters(methodDecl.children?.methodHeader?.[0]?.children?.methodDeclarator?.[0]); return { name: methodName, returnType, parameters, modifiers: [...modifiers, 'abstract'], startLine: methodDecl.location?.startLine || 1, endLine: methodDecl.location?.endLine || 1, body: '' }; } extractFieldDeclaration(fieldDecl, bodyDecl) { const type = this.extractType(fieldDecl.children?.unannType?.[0]); const modifiers = this.extractModifiers(bodyDecl.children?.modifier); const fields = []; if (fieldDecl.children?.variableDeclaratorList?.[0]?.children?.variableDeclarator) { for (const varDecl of fieldDecl.children.variableDeclaratorList[0].children.variableDeclarator) { const fieldName = this.extractIdentifier(varDecl.children?.variableDeclaratorId?.[0]?.children?.identifier?.[0]); fields.push({ name: fieldName, type, modifiers, line: fieldDecl.location?.startLine || 1 }); } } return fields; } extractMethodParameters(methodDeclarator) { const parameters = []; if (methodDeclarator?.children?.formalParameterList?.[0]?.children?.formalParameter) { for (const param of methodDeclarator.children.formalParameterList[0].children.formalParameter) { const paramType = this.extractType(param.children?.unannType?.[0]); const paramName = this.extractIdentifier(param.children?.variableDeclaratorId?.[0]?.children?.identifier?.[0]); parameters.push({ type: paramType, name: paramName }); } } return parameters; } extractMethodBodyFromCST(methodBody) { // For complexity analysis, we'll extract a simplified representation if (methodBody?.children?.block?.[0]) { return this.extractBlockStatements(methodBody.children.block[0]); } return ''; } extractBlockStatements(block) { // Extract block statements for complexity analysis let statements = ''; if (block?.children?.blockStatements?.[0]?.children?.blockStatement) { for (const stmt of block.children.blockStatements[0].children.blockStatement) { statements += this.extractStatementType(stmt) + ' '; } } return statements; } extractStatementType(statement) { if (statement.children?.statement?.[0]) { const stmt = statement.children.statement[0]; if (stmt.children?.ifThenStatement || stmt.children?.ifThenElseStatement) return 'if'; if (stmt.children?.whileStatement) return 'while'; if (stmt.children?.forStatement) return 'for'; if (stmt.children?.switchStatement) return 'switch'; if (stmt.children?.tryStatement) return 'try'; if (stmt.children?.throwStatement) return 'throw'; if (stmt.children?.returnStatement) return 'return'; } return 'statement'; } extractModifiers(modifierList) { const modifiers = []; if (!modifierList) return modifiers; for (const modifier of modifierList) { if (modifier.children?.Public) modifiers.push('public'); if (modifier.children?.Private) modifiers.push('private'); if (modifier.children?.Protected) modifiers.push('protected'); if (modifier.children?.Static) modifiers.push('static'); if (modifier.children?.Final) modifiers.push('final'); if (modifier.children?.Abstract) modifiers.push('abstract'); if (modifier.children?.Synchronized) modifiers.push('synchronized'); } return modifiers; } extractQualifiedName(qualifiedName) { if (!qualifiedName) return ''; if (qualifiedName.children?.identifier) { return qualifiedName.children.identifier.map((id) => this.extractIdentifier(id)).join('.'); } return ''; } extractIdentifier(identifier) { return identifier?.image || ''; } extractType(typeNode) { if (!typeNode) return 'void'; if (typeNode.children?.primitiveType?.[0]) { const primitiveType = typeNode.children.primitiveType[0]; if (primitiveType.children?.Int) return 'int'; if (primitiveType.children?.Boolean) return 'boolean'; if (primitiveType.children?.Char) return 'char'; if (primitiveType.children?.Byte) return 'byte'; if (primitiveType.children?.Short) return 'short'; if (primitiveType.children?.Long) return 'long'; if (primitiveType.children?.Float) return 'float'; if (primitiveType.children?.Double) return 'double'; } if (typeNode.children?.referenceType?.[0]?.children?.classOrInterfaceType?.[0]) { return this.extractQualifiedName(typeNode.children.referenceType[0].children.classOrInterfaceType[0].children?.classType?.[0]?.children?.identifier?.[0]); } return 'Object'; } extractSuperClass(classDecl) { if (classDecl?.children?.superclass?.[0]?.children?.classType?.[0]) { return this.extractQualifiedName(classDecl.children.superclass[0].children.classType[0].children?.identifier?.[0]); } return null; } extractInterfaces(classDecl) { const interfaces = []; if (classDecl?.children?.superinterfaces?.[0]?.children?.interfaceTypeList?.[0]?.children?.interfaceType) { for (const interfaceType of classDecl.children.superinterfaces[0].children.interfaceTypeList[0].children.interfaceType) { const interfaceName = this.extractQualifiedName(interfaceType.children?.classType?.[0]?.children?.identifier?.[0]); if (interfaceName) interfaces.push(interfaceName); } } return interfaces; } extractJavaParameters(javaParams) { return javaParams.map(param => ({ name: param.name, type: param.type || undefined, optional: false, defaultValue: undefined })); } calculateJavaMethodComplexity(method) { if (!method.body) return 1; let complexity = 1; const body = method.body; // Count decision points complexity += (body.match(/\bif\b/g) || []).length; complexity += (body.match(/\belse\b/g) || []).length; complexity += (body.match(/\bwhile\b/g) || []).length; complexity += (body.match(/\bfor\b/g) || []).length; complexity += (body.match(/\bswitch\b/g) || []).length; complexity += (body.match(/\bcase\b/g) || []).length; complexity += (body.match(/\bcatch\b/g) || []).length; complexity += (body.match(/\b\?\b/g) || []).length; // Ternary operator return complexity; } calculateJavaCognitiveComplexity(method) { // Calculate cognitive complexity using established 0.8 multiplier for Java return Math.floor(this.calculateJavaMethodComplexity(method) * 0.8); } isJavaPublic(modifiers) { return modifiers.includes('public'); } getJavaVisibility(modifiers) { if (modifiers.includes('private')) return 'private'; if (modifiers.includes('protected')) return 'protected'; return 'public'; } isJavaAbstract(modifiers) { return modifiers.includes('abstract'); } getJavaImportName(importPath) { const parts = importPath.split('.'); return parts[parts.length - 1].replace('*', ''); } extractJavaClassProperties(cls) { return (cls.fields || []).map((field) => ({ name: field.name, type: field.type || undefined, visibility: this.getJavaVisibility(field.modifiers), isStatic: field.modifiers.includes('static'), isReadonly: field.modifiers.includes('final'), defaultValue: undefined })); } extractJavaMethodDependencies(method) { if (!method.body) return []; const dependencies = new Set(); const body = method.body; // Extract class names (simplified) const classMatches = body.match(/\b[A-Z]\w+\b/g) || []; classMatches.forEach((match) => dependencies.add(match)); return Array.from(dependencies); } extractJavaMethodCalls(method) { if (!method.body) return []; const calls = new Set(); const body = method.body; // Extract method calls (simplified) const callMatches = body.match(/\b\w+\s*\(/g) || []; callMatches.forEach((match) => { const methodName = match.replace(/\s*\($/, ''); calls.add(methodName); }); return Array.from(calls); } } exports.JavaAnalyzer = JavaAnalyzer; //# sourceMappingURL=JavaAnalyzer.js.map