Skip to main content
Glama
java-parser.ts7.96 kB
import * as path from 'path'; import { BaseLanguageParser } from './base/BaseLanguageParser.js'; import { EntityFactory } from './base/EntityFactory.js'; import { RelationshipBuilder } from './base/RelationshipBuilder.js'; import { JavaContentExtractor } from './extractors/java/JavaContentExtractor.js'; import { JavaClassParser } from './entity-parsers/java/JavaClassParser.js'; import { JavaMethodParser } from './entity-parsers/java/JavaMethodParser.js'; import { JavaFieldParser } from './entity-parsers/java/JavaFieldParser.js'; import { JavaAnnotationExtractor } from './extractors/java/JavaAnnotationExtractor.js'; import { JavaMethodCallExtractor } from './extractors/java/JavaMethodCallExtractor.js'; import { ParsedEntity, ParsedRelationship, ParseError } from '../types.js'; export class JavaParser extends BaseLanguageParser { private contentExtractor = new JavaContentExtractor(); private classParser = new JavaClassParser(); private methodParser = new JavaMethodParser(); private fieldParser = new JavaFieldParser(); private annotationExtractor = new JavaAnnotationExtractor(); private methodCallExtractor = new JavaMethodCallExtractor(); private createdPackages: Map<string, Set<string>> = new Map(); canParse(filePath: string): boolean { const ext = path.extname(filePath).toLowerCase(); return ext === '.java'; } async parseFile(filePath: string, content: string, projectId: string): Promise<{ entities: ParsedEntity[]; relationships: ParsedRelationship[]; errors: ParseError[]; }> { this.setCurrentProject(projectId); // Initialize created packages set for this project if not exists if (!this.createdPackages.has(projectId)) { this.createdPackages.set(projectId, new Set()); } const entities: ParsedEntity[] = []; const relationships: ParsedRelationship[] = []; const errors: ParseError[] = []; try { // Check if file is effectively empty (only whitespace/comments) const trimmedContent = content.trim(); if (trimmedContent === '') { // Empty file - return empty arrays return { entities, relationships, errors }; } // Extract basic content structure const extractionResult = this.contentExtractor.extractContent(content, filePath); const packageName = extractionResult.packageName || this.getPackageFromPath(filePath); const packageId = packageName; // Create package entity if not already created const createdPackagesForProject = this.createdPackages.get(projectId)!; if (!createdPackagesForProject.has(packageId)) { const packageEntity = EntityFactory.createPackage( packageId, packageName, packageName, filePath, `Package: ${packageName}` ); this.addEntity(entities, packageEntity); createdPackagesForProject.add(packageId); } // Create module entity const moduleId = `${packageName}.${path.basename(filePath, '.java')}`; const moduleEntity = EntityFactory.createModule( moduleId, path.basename(filePath, '.java'), moduleId, filePath, `Java file: ${path.basename(filePath)}` ); this.addEntity(entities, moduleEntity); this.addRelationship(relationships, RelationshipBuilder.createBelongsTo(moduleId, packageId, filePath)); // Parse different entity types using specialized parsers this.classParser.parseClasses( content, filePath, packageName, entities, relationships, (entity) => this.addEntity(entities, entity), (rel) => this.addRelationship(relationships, rel) ); this.methodParser.parseMethods( content, filePath, packageName, entities, relationships, (entity) => this.addEntity(entities, entity), (rel) => this.addRelationship(relationships, rel) ); this.fieldParser.parseFields( content, filePath, packageName, entities, relationships, (entity) => this.addEntity(entities, entity), (rel) => this.addRelationship(relationships, rel) ); // Parse interfaces and enums using content extractor this.parseInterfaces(content, filePath, packageName, entities, relationships); this.parseEnums(content, filePath, packageName, entities, relationships); } catch (error) { this.addError(errors, { message: `Failed to parse Java file: ${error instanceof Error ? error.message : 'Unknown error'}`, line: 1, file_path: filePath }); } return { entities, relationships, errors }; } private parseInterfaces( content: string, filePath: string, packageName: string, entities: ParsedEntity[], relationships: ParsedRelationship[] ): void { const extractionResult = this.contentExtractor.extractContent(content, filePath); for (const parsedInterface of extractionResult.interfaces) { const interfaceId = `${packageName}.${parsedInterface.name}`; const qualifiedName = `${packageName}.${parsedInterface.name}`; const interfaceEntity = EntityFactory.createInterface( interfaceId, parsedInterface.name, qualifiedName, filePath, parsedInterface.startLine, parsedInterface.endLine, parsedInterface.modifiers ); this.addEntity(entities, interfaceEntity); this.addRelationship(relationships, RelationshipBuilder.createBelongsTo(interfaceId, packageName, filePath)); // Handle interface inheritance if (parsedInterface.extends && parsedInterface.extends.length > 0) { for (const parentInterface of parsedInterface.extends) { const parentId = this.resolveType(parentInterface.trim(), packageName, extractionResult.imports); this.addRelationship(relationships, RelationshipBuilder.createExtends(interfaceId, parentId, filePath)); } } } } private parseEnums( content: string, filePath: string, packageName: string, entities: ParsedEntity[], relationships: ParsedRelationship[] ): void { const extractionResult = this.contentExtractor.extractContent(content, filePath); for (const parsedEnum of extractionResult.enums) { const enumId = `${packageName}.${parsedEnum.name}`; const qualifiedName = `${packageName}.${parsedEnum.name}`; const enumEntity = EntityFactory.createEnum( enumId, parsedEnum.name, qualifiedName, filePath, parsedEnum.startLine, parsedEnum.endLine, parsedEnum.modifiers ); this.addEntity(entities, enumEntity); this.addRelationship(relationships, RelationshipBuilder.createBelongsTo(enumId, packageName, filePath)); } } 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 getPackageFromPath(filePath: string): string { const normalized = path.normalize(filePath); const parts = normalized.split(path.sep); // Find src/main/java or just src directory const srcIndex = parts.findIndex(part => part === 'src'); if (srcIndex !== -1) { const startIndex = parts.includes('main') ? srcIndex + 3 : srcIndex + 1; const packageParts = parts.slice(startIndex, -1); // Exclude filename return packageParts.join('.'); } return 'default'; } }

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