Skip to content

Commit

Permalink
Enhance DiIndexProcessor
Browse files Browse the repository at this point in the history
Sort and *update* the file (in case of a partial build)
  • Loading branch information
gnodet committed Sep 10, 2024
1 parent 0c7e733 commit bb99857
Showing 1 changed file with 90 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,67 +29,122 @@
import javax.tools.FileObject;
import javax.tools.StandardLocation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

import org.apache.maven.api.di.Named;

// Auto-register the annotation processor
@SupportedAnnotationTypes("org.apache.maven.api.di.Named")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class DiIndexProcessor extends AbstractProcessor {

private final Set<String> processedClasses = new HashSet<>();

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// Collect the fully qualified names of classes annotated with @Named
StringBuilder builder = new StringBuilder();

processingEnv
.getMessager()
.printMessage(
Diagnostic.Kind.NOTE,
"Processing " + roundEnv.getRootElements().size() + " classes");
logMessage(
Diagnostic.Kind.NOTE, "Processing " + roundEnv.getRootElements().size() + " classes");

for (Element element : roundEnv.getElementsAnnotatedWith(Named.class)) {
if (element instanceof TypeElement typeElement) {
// Get the fully qualified class name
String className = typeElement.getQualifiedName().toString();

// Handle inner classes by checking if the enclosing element is a class or interface
Element enclosingElement = typeElement.getEnclosingElement();
if (enclosingElement instanceof TypeElement) {
// It's an inner class, replace the last dot with a '$'
String enclosingClassName =
((TypeElement) enclosingElement).getQualifiedName().toString();
className = enclosingClassName + "$" + typeElement.getSimpleName();
}
String className = getFullClassName(typeElement);
processedClasses.add(className);
}
}

builder.append(className).append("\n");
if (roundEnv.processingOver()) {
try {
updateFileIfChanged();
} catch (Exception e) {
logError("Error updating file", e);
}
}

if (!builder.isEmpty()) { // Check if the StringBuilder is non-empty
return true;
}

private String getFullClassName(TypeElement typeElement) {
String className = typeElement.getQualifiedName().toString();
Element enclosingElement = typeElement.getEnclosingElement();
if (enclosingElement instanceof TypeElement) {
String enclosingClassName =
((TypeElement) enclosingElement).getQualifiedName().toString();
className = enclosingClassName + "$" + typeElement.getSimpleName();
}
return className;
}

private void updateFileIfChanged() throws IOException {
String path = "META-INF/maven/org.apache.maven.api.di.Inject";
Set<String> existingClasses = new TreeSet<>(); // Using TreeSet for natural ordering
String existingContent = "";

// Try to read existing content
try {
FileObject inputFile = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", path);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputFile.openInputStream()))) {
String line;
StringBuilder contentBuilder = new StringBuilder();
while ((line = reader.readLine()) != null) {
if (!line.trim().startsWith("#")) {
existingClasses.add(line.trim());
}
contentBuilder.append(line).append("\n");
}
existingContent = contentBuilder.toString();
}
} catch (IOException e) {
logMessage(Diagnostic.Kind.NOTE, "Unable to read existing file. Proceeding with empty content.");
}

Set<String> allClasses = new TreeSet<>(existingClasses); // Using TreeSet for natural ordering
allClasses.addAll(processedClasses);

StringBuilder newContentBuilder = new StringBuilder();
for (String className : allClasses) {
newContentBuilder.append(className).append("\n");
}
String newContent = newContentBuilder.toString();

if (!newContent.equals(existingContent)) {
logMessage(Diagnostic.Kind.NOTE, "Content has changed. Updating file.");
try {
writeFile(builder.toString());
FileObject outputFile =
processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", path);
try (Writer writer = outputFile.openWriter()) {
writer.write(newContent);
}
} catch (IOException e) {
processingEnv
.getMessager()
.printMessage(Diagnostic.Kind.ERROR, "Error writing file: " + e.getMessage());
logError("Failed to write to file", e);
throw e; // Re-throw to ensure the compilation fails
}
} else {
logMessage(Diagnostic.Kind.NOTE, "Content unchanged. Skipping file update.");
}
}

return true; // Indicate that annotations are claimed by this processor
private void logMessage(Diagnostic.Kind kind, String message) {
processingEnv.getMessager().printMessage(kind, message);
}

private void writeFile(String content) throws IOException {
// Create the file META-INF/maven/org.apache.maven.api.di.Inject
FileObject fileObject = processingEnv
.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/maven/org.apache.maven.api.di.Inject");
private void logError(String message, Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String stackTrace = sw.toString();

try (Writer writer = fileObject.openWriter()) {
writer.write(content);
}
String fullMessage = message + "\n" + "Exception: "
+ e.getClass().getName() + "\n" + "Message: "
+ e.getMessage() + "\n" + "Stack trace:\n"
+ stackTrace;

processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, fullMessage);
}
}

0 comments on commit bb99857

Please sign in to comment.