1313
1414package com .ibm .northstar ;
1515
16-
1716import com .github .javaparser .Problem ;
17+ import com .google .common .reflect .TypeToken ;
1818import com .google .gson .*;
1919import com .ibm .northstar .entities .JavaCompilationUnit ;
2020import com .ibm .northstar .utils .BuildProject ;
2727import picocli .CommandLine .Option ;
2828
2929import java .io .File ;
30+ import java .io .FileReader ;
3031import java .io .FileWriter ;
3132import java .io .IOException ;
33+ import java .lang .reflect .Type ;
3234import java .nio .file .Files ;
3335import java .nio .file .Path ;
3436import java .nio .file .Paths ;
@@ -44,6 +46,9 @@ public class CodeAnalyzer implements Runnable {
4446 @ Option (names = {"-i" , "--input" }, description = "Path to the project root directory." )
4547 private static String input ;
4648
49+ @ Option (names = {"-t" , "--target-files" }, description = "Paths to files to be analyzed from the input application." )
50+ private static List <String > targetFiles ;
51+
4752 @ Option (names = {"-s" , "--source-analysis" }, description = "Analyze a single string of java source code instead the project." )
4853 private static String sourceAnalysis ;
4954
@@ -62,6 +67,8 @@ public class CodeAnalyzer implements Runnable {
6267 @ Option (names = {"-v" , "--verbose" }, description = "Print logs to console." )
6368 private static boolean verbose = false ;
6469
70+ private static final String outputFileName = "analysis.json" ;
71+
6572
6673 public static Gson gson = new GsonBuilder ()
6774 .setFieldNamingPolicy (FieldNamingPolicy .LOWER_CASE_WITH_UNDERSCORES )
@@ -109,11 +116,46 @@ private static void analyze() throws IOException, ClassHierarchyException, CallG
109116 } else {
110117 Log .warn ("Failed to download library dependencies of project" );
111118 }
112- // construct symbol table for project, write parse problems to file in output directory if specified
113- Pair <Map <String , JavaCompilationUnit >, Map <String , List <Problem >>> symbolTableExtractionResult =
114- SymbolTable .extractAll (Paths .get (input ));
119+ boolean analysisFileExists = output != null && Files .exists (Paths .get (output + File .separator + outputFileName ));
120+
121+ // if target files are specified, compute symbol table information for the given files
122+ if (targetFiles != null ) {
123+ Log .info (targetFiles .size () + "target files specified for analysis: " + targetFiles );
124+
125+ // if target files specified for analysis level 2, downgrade to analysis level 1
126+ if (analysisLevel > 1 ) {
127+ Log .warn ("Incremental analysis is supported at analysis level 1 only; " +
128+ "performing analysis level 1 for target files" );
129+ analysisLevel = 1 ;
130+ }
131+
132+ // extract symbol table for the specified files
133+ symbolTable = SymbolTable .extract (Paths .get (input ), targetFiles .stream ().map (Paths ::get ).toList ()).getLeft ();
134+
135+ // if analysis file exists, update it with new symbol table information for the specified fiels
136+ if (analysisFileExists ) {
137+ // read symbol table information from existing analysis file
138+ Map <String , JavaCompilationUnit > existingSymbolTable = readSymbolTableFromFile (
139+ new File (output , outputFileName ));
140+ if (existingSymbolTable != null ) {
141+ // for each file, tag its symbol table information as "updated" and update existing symbol table
142+ for (String targetFile : targetFiles ) {
143+ String targetPathAbs = Paths .get (targetFile ).toAbsolutePath ().toString ();
144+ JavaCompilationUnit javaCompilationUnit = symbolTable .get (targetPathAbs );
145+ javaCompilationUnit .setModified (true );
146+ existingSymbolTable .put (targetPathAbs , javaCompilationUnit );
147+ }
148+ }
149+ symbolTable = existingSymbolTable ;
150+ }
151+ }
152+ else {
153+ // construct symbol table for project, write parse problems to file in output directory if specified
154+ Pair <Map <String , JavaCompilationUnit >, Map <String , List <Problem >>> symbolTableExtractionResult =
155+ SymbolTable .extractAll (Paths .get (input ));
115156
116- symbolTable = symbolTableExtractionResult .getLeft ();
157+ symbolTable = symbolTableExtractionResult .getLeft ();
158+ }
117159
118160 if (analysisLevel > 1 ) {
119161 // Save SDG, and Call graph as JSON
@@ -166,4 +208,15 @@ private static void emit(String consolidatedJSONString) throws IOException {
166208 }
167209 }
168210 }
211+
212+ private static Map <String , JavaCompilationUnit > readSymbolTableFromFile (File analysisJsonFile ) {
213+ Type symbolTableType = new TypeToken <Map <String , JavaCompilationUnit >>() {}.getType ();
214+ try (FileReader reader = new FileReader (analysisJsonFile )) {
215+ JsonObject jsonObject = JsonParser .parseReader (reader ).getAsJsonObject ();
216+ return gson .fromJson (jsonObject .get ("symbol_table" ), symbolTableType );
217+ } catch (IOException e ) {
218+ Log .error ("Error reading analysis file: " + e .getMessage ());
219+ }
220+ return null ;
221+ }
169222}
0 commit comments