JsonReporter.java
/*******************************************************************************
* Copyright (c) 2025 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.mining.core.report;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.time.Instant;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.sandbox.jdt.triggerpattern.llm.CommitEvaluation;
/**
* Generates JSON report files (evaluations.json and statistics.json)
* using Gson serialization.
*/
public class JsonReporter {
private static final String EVALUATIONS_FILE = "evaluations.json"; //$NON-NLS-1$
private final Gson gson;
public JsonReporter() {
this.gson = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Instant.class, new TypeAdapter<Instant>() {
@Override
public void write(JsonWriter out, Instant value) throws IOException {
out.value(value == null ? null : value.toString());
}
@Override
public Instant read(JsonReader in) throws IOException {
return Instant.parse(in.nextString());
}
})
.create();
}
/**
* Writes evaluations to a JSON file, merging with any existing evaluations
* to accumulate results across runs. Deduplicates by commitHash (newer
* evaluations replace older ones).
*
* @param evaluations the list of new evaluations from the current run
* @param outputDir the output directory
* @throws IOException if file writing fails
*/
public void writeEvaluations(List<CommitEvaluation> evaluations, Path outputDir) throws IOException {
Files.createDirectories(outputDir);
Path file = outputDir.resolve(EVALUATIONS_FILE);
// Load existing evaluations and merge
List<CommitEvaluation> existing = loadExistingEvaluations(file);
List<CommitEvaluation> merged = mergeEvaluations(existing, evaluations);
String json = gson.toJson(merged);
Files.writeString(file, json, StandardCharsets.UTF_8);
}
/**
* Loads existing evaluations from a JSON file.
*
* @param file the evaluations file
* @return list of existing evaluations, empty if file doesn't exist or is invalid
*/
public List<CommitEvaluation> loadExistingEvaluations(Path file) {
if (!Files.exists(file)) {
return List.of();
}
try {
String content = Files.readString(file, StandardCharsets.UTF_8);
List<CommitEvaluation> result = gson.fromJson(content,
new TypeToken<List<CommitEvaluation>>() {}.getType());
return result != null ? result : List.of();
} catch (Exception e) {
System.err.println("Warning: could not load existing evaluations: " + e.getMessage()); //$NON-NLS-1$
return List.of();
}
}
/**
* Merges existing and new evaluations, deduplicating by commitHash.
* New evaluations replace existing ones with the same commitHash.
*
* @param existing the existing evaluations
* @param newEvals the new evaluations
* @return merged list
*/
static List<CommitEvaluation> mergeEvaluations(List<CommitEvaluation> existing,
List<CommitEvaluation> newEvals) {
Map<String, CommitEvaluation> byHash = new LinkedHashMap<>();
for (CommitEvaluation e : existing) {
byHash.put(e.commitHash(), e);
}
for (CommitEvaluation e : newEvals) {
byHash.put(e.commitHash(), e);
}
return new ArrayList<>(byHash.values());
}
/**
* Writes statistics to a JSON file.
*
* @param stats the statistics collector
* @param outputDir the output directory
* @throws IOException if file writing fails
*/
public void writeStatistics(StatisticsCollector stats, Path outputDir) throws IOException {
Files.createDirectories(outputDir);
String json = gson.toJson(stats);
Files.writeString(outputDir.resolve("statistics.json"), json, StandardCharsets.UTF_8);
}
/**
* Serializes evaluations to a JSON string.
*
* @param evaluations the list of evaluations
* @return the JSON string
*/
public String toJson(List<CommitEvaluation> evaluations) {
return gson.toJson(evaluations);
}
}