NetBeansReporter.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.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import org.sandbox.jdt.triggerpattern.llm.CommitEvaluation;
import org.sandbox.jdt.triggerpattern.llm.CommitEvaluation.TrafficLight;
/**
* Formats evaluations in NetBeans/compiler-warning format.
*
* <p>One line per evaluation:
* {@code REPO/COMMIT:1: SEVERITY: [TRAFFIC_LIGHT/CATEGORY] SUMMARY — scores}</p>
*
* <p>This format is compatible with IDE error parsers and can be piped
* through grep, sort, or other Unix tools.</p>
*/
public class NetBeansReporter {
private static final String EVALUATIONS_TXT = "evaluations.txt"; //$NON-NLS-1$
/**
* Formats a list of evaluations in NetBeans format.
*
* @param evaluations the evaluations to format
* @return formatted string with one line per evaluation
*/
public String format(List<CommitEvaluation> evaluations) {
StringBuilder sb = new StringBuilder();
for (CommitEvaluation eval : evaluations) {
sb.append(formatLine(eval)).append("\n"); //$NON-NLS-1$
}
return sb.toString();
}
/**
* Writes the formatted evaluations to a file.
*
* @param evaluations the evaluations to write
* @param outputDir the output directory
* @throws IOException if file writing fails
*/
public void write(List<CommitEvaluation> evaluations, Path outputDir) throws IOException {
Files.createDirectories(outputDir);
String content = format(evaluations);
Files.writeString(outputDir.resolve(EVALUATIONS_TXT), content, StandardCharsets.UTF_8);
}
/**
* Prints the formatted evaluations to the given print stream.
*
* @param evaluations the evaluations to print
* @param out the output stream (typically System.out)
*/
public void printToStream(List<CommitEvaluation> evaluations, PrintStream out) {
for (CommitEvaluation eval : evaluations) {
out.println(formatLine(eval));
}
}
/**
* Formats a single evaluation as a NetBeans-style line.
*
* @param eval the evaluation
* @return formatted line
*/
String formatLine(CommitEvaluation eval) {
String repo = eval.repoUrl() != null ? repoShortName(eval.repoUrl()) : "unknown"; //$NON-NLS-1$
String hash = eval.commitHash() != null
? eval.commitHash().substring(0, Math.min(7, eval.commitHash().length()))
: "???????"; //$NON-NLS-1$
String severity = severityFromTrafficLight(eval.trafficLight());
String category = eval.category() != null ? eval.category() : "uncategorized"; //$NON-NLS-1$
String summary = eval.summary() != null ? eval.summary() : ""; //$NON-NLS-1$
String scores = "R=" + eval.reusability() + "/I=" + eval.codeImprovement() //$NON-NLS-1$ //$NON-NLS-2$
+ "/E=" + eval.implementationEffort(); //$NON-NLS-1$
String trafficLightLabel = trafficLightLabel(eval.trafficLight());
return repo + "/" + hash + ":1: " + severity + ": [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ trafficLightLabel + "/" + category + "] " //$NON-NLS-1$ //$NON-NLS-2$
+ summary + " \u2014 " + scores; //$NON-NLS-1$
}
private static String trafficLightLabel(TrafficLight light) {
if (light == null) {
return "UNKNOWN"; //$NON-NLS-1$
}
return light.toString();
}
public static String severityFromTrafficLight(TrafficLight light) {
if (light == null) {
return "info"; //$NON-NLS-1$
}
return switch (light) {
case GREEN -> "warning"; //$NON-NLS-1$
case YELLOW -> "info"; //$NON-NLS-1$
case RED -> "info"; //$NON-NLS-1$
case NOT_APPLICABLE -> "info"; //$NON-NLS-1$
};
}
public static String repoShortName(String url) {
String name = url;
if (name.endsWith(".git")) { //$NON-NLS-1$
name = name.substring(0, name.length() - 4);
}
int lastSlash = name.lastIndexOf('/');
if (lastSlash >= 0) {
name = name.substring(lastSlash + 1);
}
return name;
}
}