Skip to main content
Glama
JavaMethodCallExtractor.ts8.75 kB
import { BaseMethodCallExtractor, ParsedMethodCall, MethodCallExtractionResult } from '../base/MethodCallExtractor.js'; export class JavaMethodCallExtractor extends BaseMethodCallExtractor { private static readonly INSTANCE_CALL_PATTERN = /(\w+)\.(\w+)\s*\(/g; private static readonly STATIC_CALL_PATTERN = /([A-Z]\w*)\.(\w+)\s*\(/g; private static readonly CONSTRUCTOR_PATTERN = /new\s+([A-Z]\w*)\s*\(/g; private static readonly SUPER_CALL_PATTERN = /super\.(\w+)\s*\(/g; private static readonly FUNCTION_CALL_PATTERN = /(?:^|[^\w.])(\w+)\s*\(/g; extractMethodCalls( methodBody: string, methodName: string, className?: string, packageName?: string, imports?: Map<string, string> ): MethodCallExtractionResult { const calls: ParsedMethodCall[] = []; const errors: string[] = []; try { const lines = methodBody.split('\n'); lines.forEach((line, lineIndex) => { const cleanLine = this.removeCommentsAndStrings(line); // Extract instance method calls calls.push(...this.extractInstanceCalls(cleanLine, lineIndex + 1, packageName, imports)); // Extract static method calls calls.push(...this.extractStaticCalls(cleanLine, lineIndex + 1, packageName, imports)); // Extract constructor calls calls.push(...this.extractConstructorCalls(cleanLine, lineIndex + 1, packageName, imports)); // Extract super method calls calls.push(...this.extractSuperCalls(cleanLine, lineIndex + 1, className)); // Extract direct method calls (same class) calls.push(...this.extractDirectCalls(cleanLine, lineIndex + 1, className, methodName)); }); } catch (error) { errors.push(`Error extracting method calls: ${error instanceof Error ? error.message : String(error)}`); } return { calls, errors }; } private extractInstanceCalls( line: string, lineNumber: number, packageName?: string, imports?: Map<string, string> ): ParsedMethodCall[] { const calls: ParsedMethodCall[] = []; const pattern = new RegExp(JavaMethodCallExtractor.INSTANCE_CALL_PATTERN.source, 'g'); let match; while ((match = pattern.exec(line)) !== null) { const object = match[1]; const method = match[2]; if (this.isValidMethodCall(object, method)) { calls.push({ targetMethod: this.resolveMethodCall(object, method, packageName, imports), callType: 'instance', lineNumber, callerObject: object }); } } return calls; } private extractStaticCalls( line: string, lineNumber: number, packageName?: string, imports?: Map<string, string> ): ParsedMethodCall[] { const calls: ParsedMethodCall[] = []; const pattern = new RegExp(JavaMethodCallExtractor.STATIC_CALL_PATTERN.source, 'g'); let match; while ((match = pattern.exec(line)) !== null) { const className = match[1]; const method = match[2]; if (this.isValidMethodCall(className, method)) { calls.push({ targetMethod: this.resolveStaticMethodCall(className, method, packageName, imports), callType: 'static', lineNumber, callerObject: className }); } } return calls; } private extractConstructorCalls( line: string, lineNumber: number, packageName?: string, imports?: Map<string, string> ): ParsedMethodCall[] { const calls: ParsedMethodCall[] = []; const pattern = new RegExp(JavaMethodCallExtractor.CONSTRUCTOR_PATTERN.source, 'g'); let match; while ((match = pattern.exec(line)) !== null) { const className = match[1]; if (this.isValidConstructor(className)) { calls.push({ targetMethod: this.resolveConstructorCall(className, packageName, imports), callType: 'constructor', lineNumber, callerObject: className }); } } return calls; } private extractSuperCalls( line: string, lineNumber: number, className?: string ): ParsedMethodCall[] { const calls: ParsedMethodCall[] = []; const pattern = new RegExp(JavaMethodCallExtractor.SUPER_CALL_PATTERN.source, 'g'); let match; while ((match = pattern.exec(line)) !== null) { const method = match[1]; calls.push({ targetMethod: `super.${method}`, callType: 'super', lineNumber, callerObject: 'super' }); } return calls; } private extractDirectCalls( line: string, lineNumber: number, className?: string, currentMethodName?: string ): ParsedMethodCall[] { const calls: ParsedMethodCall[] = []; const pattern = new RegExp(JavaMethodCallExtractor.FUNCTION_CALL_PATTERN.source, 'g'); let match; while ((match = pattern.exec(line)) !== null) { const functionName = match[1]; // Skip the current method itself and common keywords if (functionName !== currentMethodName && this.isValidDirectCall(functionName)) { const targetMethod = className ? `${className}.${functionName}` : functionName; calls.push({ targetMethod, callType: 'function', lineNumber }); } } return calls; } protected removeCommentsAndStrings(line: string): string { let result = line; // Remove string literals result = result.replace(/"([^"\\]|\\.)*"/g, '""'); result = result.replace(/'([^'\\]|\\.)*'/g, "''"); // Remove line comments const commentIndex = result.indexOf('//'); if (commentIndex !== -1) { result = result.substring(0, commentIndex); } return result; } protected isValidMethodCall(objectOrClass: string, method: string): boolean { // Skip common false positives const skipObjects = ['System', 'Logger', 'log', 'logger']; const skipMethods = ['out', 'err', 'println', 'print', 'debug', 'info', 'warn', 'error']; return !skipObjects.includes(objectOrClass) || !skipMethods.includes(method); } private isValidConstructor(className: string): boolean { // Skip common built-in types const builtinTypes = ['String', 'Integer', 'Boolean', 'Double', 'Float', 'Long', 'Object']; return !builtinTypes.includes(className); } private isValidDirectCall(functionName: string): boolean { // Skip common keywords and built-in functions const skipFunctions = ['if', 'for', 'while', 'switch', 'try', 'catch', 'finally', 'return', 'throw', 'assert']; return !skipFunctions.includes(functionName); } protected resolveMethodCall( object: string, method: string, packageName?: string, imports?: Map<string, string> ): string { // Enhanced resolution for Java if (object === 'this') { return packageName ? `${packageName}.${method}` : method; } // Check if object type is in imports const objectType = this.resolveObjectType(object, imports); return objectType ? `${objectType}.${method}` : `${object}.${method}`; } protected resolveStaticMethodCall( className: string, method: string, packageName?: string, imports?: Map<string, string> ): string { // Check imports first if (imports?.has(className)) { return `${imports.get(className)}.${method}`; } // Check for standard Java library classes const javaLangTypes = ['String', 'Object', 'Math', 'System', 'Thread', 'Class']; if (javaLangTypes.includes(className)) { return `java.lang.${className}.${method}`; } // Assume same package if not found in imports return packageName ? `${packageName}.${className}.${method}` : `${className}.${method}`; } protected resolveConstructorCall( className: string, packageName?: string, imports?: Map<string, string> ): string { // Check imports first if (imports?.has(className)) { return `${imports.get(className)}.<init>`; } // Check for standard Java library classes const javaLangTypes = ['String', 'Object', 'Exception', 'RuntimeException', 'IllegalArgumentException']; if (javaLangTypes.includes(className)) { return `java.lang.${className}.<init>`; } // Assume same package if not found in imports return packageName ? `${packageName}.${className}.<init>` : `${className}.<init>`; } private resolveObjectType(object: string, imports?: Map<string, string>): string | null { // This is a simplified approach - in a full implementation, // we would track variable declarations and their types return null; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JonnoC/CodeRAG'

If you have feedback or need assistance with the MCP directory API, please join our Discord server