@aaswe/codebase-ai
Version:
AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs
673 lines • 28.4 kB
JavaScript
"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