Skip to main content
Glama
JavaClassParser.ts5.09 kB
import { ParsedEntity, ParsedRelationship } from '../../../types.js'; import { EntityFactory } from '../../base/EntityFactory.js'; import { RelationshipBuilder } from '../../base/RelationshipBuilder.js'; import { JavaContentExtractor } from '../../extractors/java/JavaContentExtractor.js'; import { JavaDocExtractor } from '../../extractors/java/JavaDocExtractor.js'; import { JavaFrameworkDetector } from '../../framework-detection/java/JavaFrameworkDetector.js'; import { JavaAnnotationExtractor } from '../../extractors/java/JavaAnnotationExtractor.js'; export class JavaClassParser { private contentExtractor = new JavaContentExtractor(); private docExtractor = new JavaDocExtractor(); private frameworkDetector = new JavaFrameworkDetector(); private annotationExtractor = new JavaAnnotationExtractor(); parseClasses( content: string, filePath: string, packageName: string, entities: ParsedEntity[], relationships: ParsedRelationship[], addEntity: (entity: Omit<ParsedEntity, 'project_id'>) => void, addRelationship: (rel: Omit<ParsedRelationship, 'project_id'>) => void ): void { const extractionResult = this.contentExtractor.extractContent(content, filePath); for (const parsedClass of extractionResult.classes) { const classId = `${packageName}.${parsedClass.name}`; const qualifiedName = `${packageName}.${parsedClass.name}`; // Extract documentation const documentation = parsedClass.startLine ? this.docExtractor.extractDocumentation(content, this.getPositionFromLine(content, parsedClass.startLine)) : undefined; // Extract annotations using modular extractor const annotationResult = this.annotationExtractor.extractAnnotations(content, this.getPositionFromLine(content, parsedClass.startLine || 1)); const annotations = annotationResult.annotations; // Create class entity const classEntity = EntityFactory.createClass( classId, parsedClass.name, qualifiedName, filePath, parsedClass.startLine, parsedClass.endLine, parsedClass.modifiers, documentation, annotations ); addEntity(classEntity); // Create package relationship const packageId = packageName; addRelationship(RelationshipBuilder.createBelongsTo(classId, packageId, filePath)); // Handle inheritance if (parsedClass.extends && parsedClass.extends.length > 0) { for (const parentClass of parsedClass.extends) { const parentId = this.resolveType(parentClass.trim(), packageName, extractionResult.imports); addRelationship(RelationshipBuilder.createExtends(classId, parentId, filePath)); } } // Handle interfaces if (parsedClass.implements && parsedClass.implements.length > 0) { for (const interfaceName of parsedClass.implements) { const interfaceId = this.resolveType(interfaceName.trim(), packageName, extractionResult.imports); addRelationship(RelationshipBuilder.createImplements(classId, interfaceId, filePath)); } } } } private extractAnnotations(content: string, startLine: number): any[] { const annotations: any[] = []; const lines = content.split('\n'); // Look backwards from the class declaration for annotations for (let i = startLine - 2; i >= 0; i--) { const line = lines[i].trim(); if (!line || line.startsWith('//') || line.startsWith('/*')) continue; const annotationMatch = line.match(/@([A-Za-z_][A-Za-z0-9_]*)/); if (annotationMatch) { const annotationName = annotationMatch[1]; const framework = this.frameworkDetector.detectFramework(annotationName) || 'Unknown'; const category = this.frameworkDetector.categorizeAnnotation(annotationName) || 'unknown'; annotations.unshift({ name: annotationName, framework, category }); } else if (line && !line.startsWith('@')) { break; // Stop at non-annotation content } } return annotations; } private resolveType(typeName: string, packageName: string, imports: any[]): string { // Remove generic type parameters const baseType = typeName.split('<')[0].trim(); // Check if it's a fully qualified name if (baseType.includes('.')) { return baseType; } // Check imports for the type for (const imp of imports) { if (imp.items?.includes(baseType) || imp.module.endsWith(`.${baseType}`)) { return imp.module.includes('.') ? imp.module : `${packageName}.${baseType}`; } } // Default to same package return `${packageName}.${baseType}`; } private getPositionFromLine(content: string, lineNumber: number): number { const lines = content.split('\n'); let position = 0; for (let i = 0; i < Math.min(lineNumber - 1, lines.length); i++) { position += lines[i].length + 1; // +1 for newline } return position; } }

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