Skip to content

Commit 21221d5

Browse files
committed
feat: enrich import schema with path, is_static, and is_wildcard fields
1 parent 3676cd8 commit 21221d5

7 files changed

Lines changed: 210 additions & 4 deletions

File tree

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=2.3.6
1+
version=2.3.7

src/main/java/com/ibm/cldk/CodeAnalyzer.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,38 @@ private static void emit(String consolidatedJSONString) throws IOException {
251251
}
252252
}
253253

254+
private static boolean hasLegacyImportSchema(JsonObject symbolTableJson) {
255+
if (symbolTableJson == null) {
256+
return false;
257+
}
258+
for (Map.Entry<String, JsonElement> entry : symbolTableJson.entrySet()) {
259+
JsonElement compilationUnitElement = entry.getValue();
260+
if (!compilationUnitElement.isJsonObject()) {
261+
continue;
262+
}
263+
JsonObject compilationUnitJson = compilationUnitElement.getAsJsonObject();
264+
if (!compilationUnitJson.has("imports") || !compilationUnitJson.get("imports").isJsonArray()) {
265+
continue;
266+
}
267+
for (JsonElement importElement : compilationUnitJson.getAsJsonArray("imports")) {
268+
if (importElement.isJsonPrimitive() && importElement.getAsJsonPrimitive().isString()) {
269+
return true;
270+
}
271+
}
272+
}
273+
return false;
274+
}
275+
254276
private static Map<String, JavaCompilationUnit> readSymbolTableFromFile(File analysisJsonFile) {
255277
Type symbolTableType = new TypeToken<Map<String, JavaCompilationUnit>>() {
256278
}.getType();
257279
try (FileReader reader = new FileReader(analysisJsonFile)) {
258280
JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
259-
return gson.fromJson(jsonObject.get("symbol_table"), symbolTableType);
281+
JsonObject symbolTableJson = jsonObject.getAsJsonObject("symbol_table");
282+
if (hasLegacyImportSchema(symbolTableJson)) {
283+
throw new IllegalStateException("Existing analysis.json uses legacy import schema (imports as strings). Regenerate analysis with codeanalyzer 2.3.7 or newer.");
284+
}
285+
return gson.fromJson(symbolTableJson, symbolTableType);
260286
} catch (IOException e) {
261287
Log.error("Error reading analysis file: " + e.getMessage());
262288
}

src/main/java/com/ibm/cldk/SymbolTable.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,13 @@ private static JavaCompilationUnit processCompilationUnit(CompilationUnit parseR
133133
// Add javadoc comment
134134
// Add imports
135135
cUnit.setImports(
136-
parseResult.getImports().stream().map(NodeWithName::getNameAsString).collect(Collectors.toList()));
136+
parseResult.getImports().stream().map(importDecl -> {
137+
Import importNode = new Import();
138+
importNode.setPath(importDecl.getNameAsString());
139+
importNode.setStatic(importDecl.isStatic());
140+
importNode.setWildcard(importDecl.isAsterisk());
141+
return importNode;
142+
}).collect(Collectors.toList()));
137143

138144
// create array node for type declarations
139145
cUnit.setTypeDeclarations(parseResult.findAll(TypeDeclaration.class).stream()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.ibm.cldk.entities;
2+
3+
import lombok.Data;
4+
5+
/** Represents an import declaration in a Java compilation unit. */
6+
@Data
7+
public class Import {
8+
private String path;
9+
private boolean isStatic = false;
10+
private boolean isWildcard = false;
11+
}

src/main/java/com/ibm/cldk/entities/JavaCompilationUnit.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class JavaCompilationUnit {
1010
private String filePath;
1111
private String packageName;
1212
private List<Comment> comments = new ArrayList<>();
13-
private List<String> imports;
13+
private List<Import> imports;
1414
private Map<String, Type> typeDeclarations;
1515
private boolean isModified;
1616
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.ibm.cldk;
2+
3+
import com.ibm.cldk.entities.Import;
4+
import com.ibm.cldk.entities.JavaCompilationUnit;
5+
import java.io.File;
6+
import java.lang.reflect.InvocationTargetException;
7+
import java.lang.reflect.Method;
8+
import java.nio.charset.StandardCharsets;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
11+
import java.util.List;
12+
import java.util.Map;
13+
import org.junit.jupiter.api.Assertions;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.io.TempDir;
16+
17+
public class CodeAnalyzerTest {
18+
19+
@TempDir
20+
Path tempDir;
21+
22+
@SuppressWarnings("unchecked")
23+
private Map<String, JavaCompilationUnit> invokeReadSymbolTableFromFile(Path analysisFilePath) throws Exception {
24+
Method readSymbolTableMethod = CodeAnalyzer.class.getDeclaredMethod("readSymbolTableFromFile", File.class);
25+
readSymbolTableMethod.setAccessible(true);
26+
try {
27+
return (Map<String, JavaCompilationUnit>) readSymbolTableMethod.invoke(null, analysisFilePath.toFile());
28+
} catch (InvocationTargetException invocationTargetException) {
29+
Throwable targetException = invocationTargetException.getTargetException();
30+
if (targetException instanceof Exception) {
31+
throw (Exception) targetException;
32+
}
33+
throw new RuntimeException(targetException);
34+
}
35+
}
36+
37+
private Path writeAnalysisFile(String jsonContent) throws Exception {
38+
Path analysisFilePath = tempDir.resolve("analysis.json");
39+
Files.writeString(analysisFilePath, jsonContent, StandardCharsets.UTF_8);
40+
return analysisFilePath;
41+
}
42+
43+
@Test
44+
public void testReadSymbolTableFromFileRejectsLegacyImportSchema() throws Exception {
45+
String jsonContent = "{\n"
46+
+ " \"symbol_table\": {\n"
47+
+ " \"/tmp/T.java\": {\n"
48+
+ " \"file_path\": \"/tmp/T.java\",\n"
49+
+ " \"package_name\": \"\",\n"
50+
+ " \"comments\": [],\n"
51+
+ " \"imports\": [\"java.util.List\"],\n"
52+
+ " \"type_declarations\": {},\n"
53+
+ " \"is_modified\": false\n"
54+
+ " }\n"
55+
+ " }\n"
56+
+ "}\n";
57+
Path analysisFilePath = writeAnalysisFile(jsonContent);
58+
59+
IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class,
60+
() -> invokeReadSymbolTableFromFile(analysisFilePath));
61+
Assertions.assertTrue(exception.getMessage().contains("legacy import schema"));
62+
}
63+
64+
@Test
65+
public void testReadSymbolTableFromFileParsesExplicitImportSchema() throws Exception {
66+
String jsonContent = "{\n"
67+
+ " \"symbol_table\": {\n"
68+
+ " \"/tmp/T.java\": {\n"
69+
+ " \"file_path\": \"/tmp/T.java\",\n"
70+
+ " \"package_name\": \"\",\n"
71+
+ " \"comments\": [],\n"
72+
+ " \"imports\": [\n"
73+
+ " {\n"
74+
+ " \"path\": \"java.util.List\",\n"
75+
+ " \"is_static\": false,\n"
76+
+ " \"is_wildcard\": false\n"
77+
+ " },\n"
78+
+ " {\n"
79+
+ " \"path\": \"java.util.Collections\",\n"
80+
+ " \"is_static\": true,\n"
81+
+ " \"is_wildcard\": true\n"
82+
+ " }\n"
83+
+ " ],\n"
84+
+ " \"type_declarations\": {},\n"
85+
+ " \"is_modified\": false\n"
86+
+ " }\n"
87+
+ " }\n"
88+
+ "}\n";
89+
Path analysisFilePath = writeAnalysisFile(jsonContent);
90+
91+
Map<String, JavaCompilationUnit> symbolTable = invokeReadSymbolTableFromFile(analysisFilePath);
92+
Assertions.assertNotNull(symbolTable);
93+
Assertions.assertEquals(1, symbolTable.size());
94+
95+
JavaCompilationUnit compilationUnit = symbolTable.get("/tmp/T.java");
96+
Assertions.assertNotNull(compilationUnit);
97+
List<Import> imports = compilationUnit.getImports();
98+
Assertions.assertNotNull(imports);
99+
Assertions.assertEquals(2, imports.size());
100+
101+
Import defaultImport = imports.stream()
102+
.filter(imp -> "java.util.List".equals(imp.getPath()))
103+
.findFirst()
104+
.orElse(null);
105+
Assertions.assertNotNull(defaultImport);
106+
Assertions.assertFalse(defaultImport.isStatic());
107+
Assertions.assertFalse(defaultImport.isWildcard());
108+
109+
Import staticWildcardImport = imports.stream()
110+
.filter(imp -> "java.util.Collections".equals(imp.getPath()))
111+
.findFirst()
112+
.orElse(null);
113+
Assertions.assertNotNull(staticWildcardImport);
114+
Assertions.assertTrue(staticWildcardImport.isStatic());
115+
Assertions.assertTrue(staticWildcardImport.isWildcard());
116+
}
117+
}

src/test/java/com/ibm/cldk/SymbolTableTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package com.ibm.cldk;
22

3+
import com.google.gson.JsonArray;
4+
import com.google.gson.JsonElement;
5+
import com.google.gson.JsonObject;
6+
import com.google.gson.JsonParser;
37
import com.ibm.cldk.entities.CallSite;
48
import com.ibm.cldk.entities.Callable;
9+
import com.ibm.cldk.entities.Import;
510
import com.ibm.cldk.entities.JavaCompilationUnit;
611
import com.ibm.cldk.entities.Type;
712
import java.io.BufferedReader;
@@ -85,4 +90,45 @@ public void testCallSiteArgumentExpression() throws IOException {
8590
}
8691
}
8792

93+
@Test
94+
public void testExtractSingleImportMetadata() throws IOException {
95+
String javaCode = String.join("\n",
96+
"import java.util.List;",
97+
"import java.util.Map.*;",
98+
"import static java.util.Collections.emptyList;",
99+
"import static java.util.Collections.*;",
100+
"class T {}");
101+
Map<String, JavaCompilationUnit> symbolTable = SymbolTable.extractSingle(javaCode).getLeft();
102+
Assertions.assertEquals(1, symbolTable.size());
103+
List<Import> imports = symbolTable.values().iterator().next().getImports();
104+
Assertions.assertNotNull(imports);
105+
Assertions.assertEquals(4, imports.size());
106+
107+
assertImport(imports, "java.util.List", false, false);
108+
assertImport(imports, "java.util.Map", false, true);
109+
assertImport(imports, "java.util.Collections.emptyList", true, false);
110+
assertImport(imports, "java.util.Collections", true, true);
111+
112+
JsonArray serializedImports = JsonParser.parseString(CodeAnalyzer.gson.toJson(imports)).getAsJsonArray();
113+
Assertions.assertEquals(4, serializedImports.size());
114+
for (JsonElement serializedImport : serializedImports) {
115+
Assertions.assertTrue(serializedImport.isJsonObject());
116+
JsonObject serializedImportObject = serializedImport.getAsJsonObject();
117+
Assertions.assertTrue(serializedImportObject.has("path"));
118+
Assertions.assertTrue(serializedImportObject.has("is_static"));
119+
Assertions.assertTrue(serializedImportObject.has("is_wildcard"));
120+
}
121+
}
122+
123+
private static void assertImport(List<Import> imports, String path, boolean isStatic, boolean isWildcard) {
124+
Import matchingImport = imports.stream()
125+
.filter(imp -> path.equals(imp.getPath())
126+
&& imp.isStatic() == isStatic
127+
&& imp.isWildcard() == isWildcard)
128+
.findFirst()
129+
.orElse(null);
130+
Assertions.assertNotNull(matchingImport,
131+
String.format("Expected import '%s' with isStatic=%s and isWildcard=%s", path, isStatic, isWildcard));
132+
}
133+
88134
}

0 commit comments

Comments
 (0)