Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions chapi-ast-java/src/main/antlr/JavaLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ SEMI : ';';
COMMA : ',';
DOT : '.';

// Java 22+ (preview, continued into later releases): unnamed variables / patterns
UNDERSCORE : '_';

// Operators

ASSIGN : '=';
Expand Down
21 changes: 14 additions & 7 deletions chapi-ast-java/src/main/antlr/JavaParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,13 @@ variableDeclarator
;

variableDeclaratorId
: identifier ('[' ']')*
: (identifier | UNDERSCORE) ('[' ']')*
;

// Java 22+ (preview, continued into later releases): unnamed variables / patterns
variableName
: identifier
| UNDERSCORE
;

variableInitializer
Expand Down Expand Up @@ -310,7 +316,7 @@ lambdaLVTIList
;

lambdaLVTIParameter
: variableModifier* VAR identifier
: variableModifier* VAR (identifier | UNDERSCORE)
;

qualifiedName
Expand Down Expand Up @@ -474,7 +480,7 @@ blockStatement
;

localVariableDeclaration
: variableModifier* (VAR identifier '=' expression | typeType variableDeclarators)
: variableModifier* (VAR (identifier | UNDERSCORE) '=' expression | typeType variableDeclarators)
;

identifier
Expand Down Expand Up @@ -539,7 +545,7 @@ statement
;

catchClause
: CATCH '(' variableModifier* catchType identifier ')' block
: CATCH '(' variableModifier* catchType (identifier | UNDERSCORE) ')' block
;

catchType
Expand All @@ -559,7 +565,7 @@ resources
;

resource
: variableModifier* (classOrInterfaceType variableDeclaratorId | VAR identifier) '=' expression
: variableModifier* (classOrInterfaceType variableDeclaratorId | VAR (identifier | UNDERSCORE)) '=' expression
| qualifiedName
;

Expand All @@ -574,7 +580,7 @@ switchLabel
: CASE (
constantExpression = expression
| enumConstantName = IDENTIFIER
| typeType varName = identifier
| typeType varName = variableName
)
| DEFAULT
;
Expand Down Expand Up @@ -697,8 +703,9 @@ lambdaExpression

lambdaParameters
: identifier
| UNDERSCORE
| '(' formalParameterList? ')'
| '(' identifier (',' identifier)* ')'
| '(' (identifier | UNDERSCORE) (',' (identifier | UNDERSCORE))* ')'
| '(' lambdaLVTIList? ')'
;

Expand Down
14 changes: 12 additions & 2 deletions chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaAnalyser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import org.antlr.v4.runtime.CommonTokenStream
import org.antlr.v4.runtime.tree.ParseTreeWalker

open class JavaAnalyser : TwoStepAnalyser() {
companion object {
// Reuse ParseTreeWalker instance to reduce object creation
private val walker = ParseTreeWalker()
}

override fun analysis(code: String, filePath: String, parseMode: ParseMode): CodeContainer {
return when (parseMode) {
ParseMode.Basic -> identBasicInfo(code, filePath)
Expand All @@ -27,24 +32,29 @@ open class JavaAnalyser : TwoStepAnalyser() {
val context = this.parse(str).compilationUnit()
val listener = JavaFullIdentListener(fileName, classes)

ParseTreeWalker().walk(listener, context)
walker.walk(listener, context)
return listener.getNodeInfo()
}

open fun identBasicInfo(str: String, fileName: String): CodeContainer {
val context = this.parse(str).compilationUnit()
val listener = JavaBasicIdentListener(fileName)

ParseTreeWalker().walk(listener, context)
walker.walk(listener, context)

return listener.getNodeInfo()
}

open fun parse(str: String): JavaParser {
val fromString = CharStreams.fromString(str)
val lexer = JavaLexer(fromString)
// Remove default error listeners to reduce I/O overhead
lexer.removeErrorListeners()

val tokenStream = CommonTokenStream(lexer)
val parser = JavaParser(tokenStream)
// Remove default error listeners
parser.removeErrorListeners()
return parser
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ open class JavaBasicIdentListener(fileName: String) : JavaAstListener() {
Language = "java",
Kind = ContainerKind.SOURCE_FILE
)
private var classNodes: List<CodeDataStruct> = listOf()
private var imports: List<CodeImport> = listOf()
private var classNodes: MutableList<CodeDataStruct> = mutableListOf()
private var imports: MutableList<CodeImport> = mutableListOf()
// Index for fast import lookup by class name
// Using MutableList to handle import collisions
private var importsByClassName: MutableMap<String, MutableList<CodeImport>> = mutableMapOf()

private var currentNode = CodeDataStruct()
private var currentFunction = CodeFunction(IsConstructor = false)
Expand Down Expand Up @@ -58,8 +61,14 @@ open class JavaBasicIdentListener(fileName: String) : JavaAstListener() {

codeImport.PathSegments = fullSource.split(".")

imports += codeImport
imports.add(codeImport)
codeContainer.Imports += codeImport

// Build import index for non-wildcard imports only
if (!isWildcard) {
val className = fullSource.substringAfterLast('.')
importsByClassName.getOrPut(className) { mutableListOf() }.add(codeImport)
}
}

override fun enterPackageDeclaration(ctx: JavaParser.PackageDeclarationContext?) {
Expand All @@ -83,27 +92,39 @@ open class JavaBasicIdentListener(fileName: String) : JavaAstListener() {
}

override fun exitClassBodyDeclaration(ctx: JavaParser.ClassBodyDeclarationContext?) {
// Class members are handled in their own enter/exit callbacks.
// Finalizing the class here would duplicate nodes (one per member) and miss empty classes.
}

override fun exitClassDeclaration(ctx: JavaParser.ClassDeclarationContext?) {
hasEnterClass = false
if (currentNode.NodeName != "") {
classNodes += currentNode
classNodes.add(currentNode)
}
currentNode = CodeDataStruct()
}

open fun buildImplements(ctx: JavaParser.ClassDeclarationContext): List<String> {
return ctx.typeList()
.map { it.text }
.filter { imports.containsType(it) }
.filter { containsType(it) }
}

private fun <T> List<T>.containsType(it: String?): Boolean {
return imports.filter { imp -> imp.Source.endsWith(".$it") }.toTypedArray().isNotEmpty()
private fun containsType(typeName: String?): Boolean {
if (typeName == null) return false
// Fast lookup using index
val importsByName = importsByClassName[typeName]
if (importsByName != null && importsByName.isNotEmpty()) {
return true
}
// Fallback to linear search for wildcard imports
return imports.any { imp -> imp.Source.endsWith(".$typeName") }
}

open fun buildImplements(ctx: JavaParser.EnumDeclarationContext): List<String> {
val typeText = ctx.typeList().text
return when {
imports.containsType(typeText) -> listOf(typeText)
containsType(typeText) -> listOf(typeText)
else -> listOf()
}
}
Expand All @@ -120,7 +141,7 @@ open class JavaBasicIdentListener(fileName: String) : JavaAstListener() {
override fun exitInterfaceDeclaration(ctx: JavaParser.InterfaceDeclarationContext?) {
hasEnterClass = false
if (currentNode.NodeName != "") {
classNodes += currentNode
classNodes.add(currentNode)
}
}

Expand Down Expand Up @@ -236,14 +257,13 @@ open class JavaBasicIdentListener(fileName: String) : JavaAstListener() {
override fun exitEnumBodyDeclarations(ctx: JavaParser.EnumBodyDeclarationsContext?) {
hasEnterClass = false
if (currentNode.NodeName != "") {
classNodes += currentNode
classNodes.add(currentNode)
}
currentNode = CodeDataStruct()
}

fun getNodeInfo(): CodeContainer {
codeContainer.DataStructures = classNodes
codeContainer.DataStructures = classNodes.toList()
return codeContainer
}
}

}
Loading
Loading