Created
January 8, 2025 20:44
-
-
Save gnodet/c297ccae07dd2ee67dfa98cf1ef128f9 to your computer and use it in GitHub Desktop.
JBang script to startup migrating a plugin to the Maven 4 API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///usr/bin/env jbang "$0" "$@" ; exit $? | |
//DEPS com.github.javaparser:javaparser-core:3.25.7 | |
//DEPS org.apache.commons:commons-lang3:3.14.0 | |
//DEPS info.picocli:picocli:4.7.5 | |
//DEPS org.dom4j:dom4j:2.1.4 | |
//JAVA 17 | |
import com.github.javaparser.JavaParser; | |
import com.github.javaparser.ast.CompilationUnit; | |
import com.github.javaparser.ast.ImportDeclaration; | |
import com.github.javaparser.ast.Modifier; | |
import com.github.javaparser.ast.Node; | |
import com.github.javaparser.ast.NodeList; | |
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; | |
import com.github.javaparser.ast.body.FieldDeclaration; | |
import com.github.javaparser.ast.body.MethodDeclaration; | |
import com.github.javaparser.ast.body.VariableDeclarator; | |
import com.github.javaparser.ast.expr.*; | |
import com.github.javaparser.ast.stmt.BlockStmt; | |
import com.github.javaparser.ast.stmt.ReturnStmt; | |
import com.github.javaparser.ast.type.ClassOrInterfaceType; | |
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; | |
import org.dom4j.Document; | |
import org.dom4j.Element; | |
import org.dom4j.io.SAXReader; | |
import org.dom4j.io.XMLWriter; | |
import picocli.CommandLine; | |
import picocli.CommandLine.Command; | |
import picocli.CommandLine.Option; | |
import picocli.CommandLine.Parameters; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.nio.file.Files; | |
import java.nio.file.Path; | |
import java.util.*; | |
import java.util.concurrent.Callable; | |
import java.util.stream.Stream; | |
@Command(name = "maven4-migrate", mixinStandardHelpOptions = true, version = "maven4-migrate 1.0", | |
description = "Migrates Maven plugins and POM files to Maven 4 API") | |
public class Maven4Migration implements Callable<Integer> { | |
private static final Map<String, String> IMPORT_REPLACEMENTS = new HashMap<>(); | |
private static final Map<String, String> PHASE_REPLACEMENTS = new HashMap<>(); | |
private static final Map<String, String> EXCEPTION_REPLACEMENTS = new HashMap<>(); | |
private static final Map<String, String> TYPE_REPLACEMENTS = new HashMap<>(); | |
static { | |
IMPORT_REPLACEMENTS.put("org.apache.maven.plugins.annotations.Mojo", | |
"org.apache.maven.api.plugin.annotations.Mojo"); | |
IMPORT_REPLACEMENTS.put("org.apache.maven.plugins.annotations.Parameter", | |
"org.apache.maven.api.plugin.annotations.Parameter"); | |
IMPORT_REPLACEMENTS.put("org.apache.maven.plugins.annotations.Component", | |
"org.apache.maven.api.di.Inject"); | |
IMPORT_REPLACEMENTS.put("org.apache.maven.project.MavenProject", | |
"org.apache.maven.api.Project"); | |
IMPORT_REPLACEMENTS.put("org.apache.maven.execution.MavenSession", | |
"org.apache.maven.api.Session"); | |
IMPORT_REPLACEMENTS.put("org.apache.maven.plugin.MojoExecutionException", | |
"org.apache.maven.api.plugin.MojoException"); | |
IMPORT_REPLACEMENTS.put("org.apache.maven.plugin.MojoFailureException", | |
"org.apache.maven.api.plugin.MojoException"); | |
// Initialize type replacements | |
TYPE_REPLACEMENTS.put("MavenProject", "Project"); | |
TYPE_REPLACEMENTS.put("MavenSession", "Session"); | |
// Initialize phase replacements | |
PHASE_REPLACEMENTS.put("GENERATE_SOURCES", "BEFORE + SOURCES"); | |
PHASE_REPLACEMENTS.put("PROCESS_SOURCES", "AFTER + SOURCES"); | |
PHASE_REPLACEMENTS.put("GENERATE_RESOURCES", "BEFORE + RESOURCES"); | |
PHASE_REPLACEMENTS.put("PROCESS_RESOURCES", "AFTER + RESOURCES"); | |
PHASE_REPLACEMENTS.put("PROCESS_CLASSES", "AFTER + COMPILE"); | |
PHASE_REPLACEMENTS.put("GENERATE_TEST_SOURCES", "BEFORE + TEST_SOURCES"); | |
PHASE_REPLACEMENTS.put("PROCESS_TEST_SOURCES", "AFTER + TEST_SOURCES"); | |
PHASE_REPLACEMENTS.put("GENERATE_TEST_RESOURCES", "BEFORE + TEST_RESOURCES"); | |
PHASE_REPLACEMENTS.put("PROCESS_TEST_RESOURCES", "AFTER + TEST_RESOURCES"); | |
PHASE_REPLACEMENTS.put("PROCESS_TEST_CLASSES", "AFTER + TEST_COMPILE"); | |
PHASE_REPLACEMENTS.put("PREPARE_PACKAGE", "BEFORE + PACKAGE"); | |
PHASE_REPLACEMENTS.put("PRE_INTEGRATION_TEST", "BEFORE + INTEGRATION_TEST"); | |
PHASE_REPLACEMENTS.put("POST_INTEGRATION_TEST", "AFTER + INTEGRATION_TEST"); | |
// Initialize exception replacements | |
EXCEPTION_REPLACEMENTS.put("MojoExecutionException", "MojoException"); | |
EXCEPTION_REPLACEMENTS.put("MojoFailureException", "MojoException"); | |
} | |
@Parameters(index = "0", description = "The source directory containing files to migrate") | |
private Path sourceDir; | |
@Option(names = {"-r", "--recursive"}, description = "Search directories recursively") | |
private boolean recursive = false; | |
@Option(names = {"-p", "--pom"}, description = "Process pom.xml files") | |
private boolean processPom = false; | |
@Override | |
public Integer call() throws Exception { | |
// First process pom.xml in root directory if requested | |
if (processPom) { | |
Path pomFile = sourceDir.resolve("pom.xml"); | |
if (Files.exists(pomFile)) { | |
processPomFile(pomFile); | |
} | |
} | |
// Then process Java files | |
try (Stream<Path> paths = recursive ? | |
Files.walk(sourceDir) : | |
Files.list(sourceDir)) { | |
paths.filter(Files::isRegularFile) | |
.filter(p -> p.toString().endsWith(".java")) | |
.forEach(this::processJavaFile); | |
} | |
return 0; | |
} | |
private void processPomFile(Path file) { | |
try { | |
System.out.println("Processing POM: " + file); | |
SAXReader reader = new SAXReader(); | |
Document document = reader.read(file.toFile()); | |
Element root = document.getRootElement(); | |
boolean modified = false; | |
modified |= updateJavaVersion(root); | |
modified |= updateMavenVersion(root); | |
modified |= addMavenDependencies(root); | |
if (modified) { | |
// Configure pretty print OutputFormat | |
org.dom4j.io.OutputFormat format = org.dom4j.io.OutputFormat.createPrettyPrint(); | |
format.setIndentSize(2); | |
format.setNewlines(true); | |
XMLWriter writer = new XMLWriter(new FileOutputStream(file.toFile()), format); | |
writer.write(document); | |
writer.close(); | |
System.out.println("Modified POM: " + file); | |
} | |
} catch (Exception e) { | |
System.err.println("Error processing POM file: " + file); | |
e.printStackTrace(); | |
} | |
} | |
private boolean updateJavaVersion(Element root) { | |
Element properties = root.element("properties"); | |
if (properties != null) { | |
Element javaVersion = properties.element("javaVersion"); | |
if (javaVersion != null) { | |
String currentVersion = javaVersion.getTextTrim(); | |
try { | |
if (Double.parseDouble(currentVersion) < 17) { | |
javaVersion.setText("17"); | |
return true; | |
} | |
} catch (NumberFormatException e) { | |
// If version is not a number, replace it anyway | |
javaVersion.setText("17"); | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
private boolean updateMavenVersion(Element root) { | |
Element properties = root.element("properties"); | |
if (properties != null) { | |
Element mavenVersion = properties.element("mavenVersion"); | |
if (mavenVersion != null) { | |
String currentVersion = mavenVersion.getTextTrim(); | |
if (!currentVersion.equals("4.0.0-rc-2")) { | |
mavenVersion.setText("4.0.0-rc-2"); | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
private boolean addMavenDependencies(Element root) { | |
Element dependencies = root.element("dependencies"); | |
if (dependencies == null) { | |
dependencies = root.addElement("dependencies"); | |
} | |
// Check if dependencies are already present | |
boolean hasApiCore = dependencies.elements().stream() | |
.anyMatch(dep -> isMatchingDependency((Element) dep, "org.apache.maven", "maven-api-core")); | |
if (!hasApiCore) { | |
// Create new dependencies and add them at the beginning | |
String[][] mavenDeps = { | |
{"maven-api-core", "Core API"}, | |
{"maven-api-model", "Model API"}, | |
{"maven-api-di", "Dependency Injection API"}, | |
{"maven-api-plugin", "Plugin API"} | |
}; | |
// Keep existing elements | |
java.util.List<Element> existingElements = new java.util.ArrayList<>(dependencies.elements()); | |
// Clear the dependencies element | |
dependencies.clearContent(); | |
// Add comment | |
dependencies.addComment(" maven api "); | |
// Add new Maven dependencies | |
for (String[] dep : mavenDeps) { | |
Element dependency = dependencies.addElement("dependency"); | |
dependency.addElement("groupId").setText("org.apache.maven"); | |
dependency.addElement("artifactId").setText(dep[0]); | |
dependency.addElement("version").setText("${mavenVersion}"); | |
} | |
// Add back existing dependencies | |
for (Element element : existingElements) { | |
dependencies.add(element.createCopy()); | |
} | |
return true; | |
} | |
return false; | |
} | |
private boolean isMatchingDependency(Element dep, String groupId, String artifactId) { | |
Element groupIdElement = dep.element("groupId"); | |
Element artifactIdElement = dep.element("artifactId"); | |
return groupIdElement != null && artifactIdElement != null && | |
groupIdElement.getTextTrim().equals(groupId) && | |
artifactIdElement.getTextTrim().equals(artifactId); | |
} | |
private void processJavaFile(Path file) { | |
try { | |
System.out.println("Processing: " + file); | |
String content = Files.readString(file); | |
JavaParser parser = new JavaParser(); | |
CompilationUnit cu = parser.parse(content).getResult().orElseThrow(); | |
boolean modified = false; | |
// First update the annotations and other content while original imports are intact | |
modified |= updateMojoAnnotations(cu); | |
modified |= updateDependencyInjection(cu); | |
modified |= updateMojoImplementation(cu); | |
modified |= updateThrowsClauses(cu); | |
modified |= updateExceptionConstructions(cu); // Add new method call | |
modified |= updateTypeReferences(cu); // Add new method call | |
modified |= updateModelImports(cu); // Add new method call | |
modified |= updateLogging(cu); // Add new method call | |
// Then update the imports last | |
modified |= updateImports(cu); | |
if (modified) { | |
Files.writeString(file, cu.toString()); | |
System.out.println("Modified: " + file); | |
} | |
} catch (IOException e) { | |
System.err.println("Error processing file: " + file); | |
e.printStackTrace(); | |
} | |
} | |
private boolean updateModelImports(CompilationUnit cu) { | |
boolean modified = false; | |
NodeList<ImportDeclaration> imports = cu.getImports(); | |
// Handle both static and non-static imports from the model package | |
for (ImportDeclaration importDecl : imports) { | |
String name = importDecl.getNameAsString(); | |
if (name.startsWith("org.apache.maven.model.")) { | |
// Replace the package part while keeping the rest of the import intact | |
String newName = "org.apache.maven.api.model." + | |
name.substring("org.apache.maven.model.".length()); | |
importDecl.setName(newName); | |
modified = true; | |
} | |
} | |
return modified; | |
} | |
private boolean updateLogging(CompilationUnit cu) { | |
boolean modified = false; | |
// Find classes that implement Mojo | |
for (ClassOrInterfaceDeclaration classDecl : cu.findAll(ClassOrInterfaceDeclaration.class)) { | |
if (implementsMojo(classDecl) && !hasParentImplementingMojo(classDecl)) { | |
// Check if the class or its subclasses use getLog() | |
if (containsGetLogCall(classDecl) || subclassesContainGetLogCall(classDecl)) { | |
// Add log field if not present | |
if (!hasLogField(classDecl)) { | |
// Add import for Log | |
cu.addImport("org.apache.maven.api.plugin.Log"); | |
cu.addImport("org.apache.maven.api.di.Inject"); | |
// Create log field | |
NodeList<Modifier> modifiers = new NodeList<>(Modifier.privateModifier()); | |
FieldDeclaration logField = new FieldDeclaration( | |
modifiers, | |
new VariableDeclarator( | |
new ClassOrInterfaceType(null, "Log"), | |
"log" | |
) | |
); | |
logField.addAnnotation("Inject"); | |
classDecl.getMembers().add(logField); | |
// Add getter if not present | |
if (!hasGetLogMethod(classDecl)) { | |
// Create getLog method | |
NodeList<Modifier> methodModifiers = new NodeList<>(Modifier.protectedModifier()); | |
MethodDeclaration getLogMethod = new MethodDeclaration( | |
methodModifiers, | |
new ClassOrInterfaceType(null, "Log"), | |
"getLog" | |
); | |
BlockStmt body = new BlockStmt(); | |
body.addStatement(new ReturnStmt(new NameExpr("log"))); | |
getLogMethod.setBody(body); | |
classDecl.getMembers().add(getLogMethod); | |
} | |
modified = true; | |
} | |
} | |
} | |
} | |
return modified; | |
} | |
private boolean hasParentImplementingMojo(ClassOrInterfaceDeclaration classDecl) { | |
try { | |
return classDecl.getExtendedTypes().stream() | |
.map(type -> { | |
try { | |
return type.resolve(); | |
} catch (Exception e) { | |
return null; | |
} | |
}) | |
.filter(Objects::nonNull) | |
.map(resolvedType -> { | |
try { | |
return resolvedType.asReferenceType().getTypeDeclaration(); | |
} catch (Exception e) { | |
return Optional.<ResolvedReferenceTypeDeclaration>empty(); | |
} | |
}) | |
.filter(Optional::isPresent) | |
.map(Optional::get) | |
.filter(decl -> decl instanceof ClassOrInterfaceDeclaration) | |
.map(decl -> (ClassOrInterfaceDeclaration) decl) | |
.anyMatch(this::implementsMojo); | |
} catch (Exception e) { | |
// If resolution fails, assume no parent implements Mojo | |
return false; | |
} | |
} | |
private boolean implementsMojo(ClassOrInterfaceDeclaration classDecl) { | |
return classDecl.getImplementedTypes().stream() | |
.anyMatch(type -> type.getNameAsString().endsWith("Mojo")); | |
} | |
private boolean containsGetLogCall(ClassOrInterfaceDeclaration classDecl) { | |
return classDecl.findAll(MethodCallExpr.class).stream() | |
.anyMatch(call -> call.getNameAsString().equals("getLog")); | |
} | |
private boolean subclassesContainGetLogCall(ClassOrInterfaceDeclaration classDecl) { | |
// Find all subclasses in the same compilation unit | |
return classDecl.findAll(ClassOrInterfaceDeclaration.class).stream() | |
.filter(cls -> cls.getExtendedTypes().stream() | |
.anyMatch(type -> type.getNameAsString().equals(classDecl.getNameAsString()))) | |
.anyMatch(this::containsGetLogCall); | |
} | |
private boolean hasLogField(ClassOrInterfaceDeclaration classDecl) { | |
return classDecl.getFields().stream() | |
.anyMatch(field -> field.getVariable(0).getNameAsString().equals("log") && | |
field.getElementType().asString().equals("Log")); | |
} | |
private boolean hasGetLogMethod(ClassOrInterfaceDeclaration classDecl) { | |
return classDecl.getMethods().stream() | |
.anyMatch(method -> method.getNameAsString().equals("getLog")); | |
} | |
private boolean updateTypeReferences(CompilationUnit cu) { | |
boolean modified = false; | |
// Update variable declarations | |
for (com.github.javaparser.ast.body.VariableDeclarator var : cu.findAll(com.github.javaparser.ast.body.VariableDeclarator.class)) { | |
if (var.getType() instanceof ClassOrInterfaceType) { | |
ClassOrInterfaceType type = (ClassOrInterfaceType) var.getType(); | |
String typeName = type.getNameAsString(); | |
if (TYPE_REPLACEMENTS.containsKey(typeName)) { | |
type.setName(TYPE_REPLACEMENTS.get(typeName)); | |
modified = true; | |
} | |
} | |
} | |
// Update method parameters | |
for (com.github.javaparser.ast.body.Parameter param : cu.findAll(com.github.javaparser.ast.body.Parameter.class)) { | |
if (param.getType() instanceof ClassOrInterfaceType) { | |
ClassOrInterfaceType type = (ClassOrInterfaceType) param.getType(); | |
String typeName = type.getNameAsString(); | |
if (TYPE_REPLACEMENTS.containsKey(typeName)) { | |
type.setName(TYPE_REPLACEMENTS.get(typeName)); | |
modified = true; | |
} | |
} | |
} | |
// Update method return types | |
for (com.github.javaparser.ast.body.MethodDeclaration method : cu.findAll(com.github.javaparser.ast.body.MethodDeclaration.class)) { | |
if (method.getType() instanceof ClassOrInterfaceType) { | |
ClassOrInterfaceType type = (ClassOrInterfaceType) method.getType(); | |
String typeName = type.getNameAsString(); | |
if (TYPE_REPLACEMENTS.containsKey(typeName)) { | |
type.setName(TYPE_REPLACEMENTS.get(typeName)); | |
modified = true; | |
} | |
} | |
} | |
// Update field declarations | |
for (com.github.javaparser.ast.body.FieldDeclaration field : cu.findAll(com.github.javaparser.ast.body.FieldDeclaration.class)) { | |
if (field.getElementType() instanceof ClassOrInterfaceType) { | |
ClassOrInterfaceType type = (ClassOrInterfaceType) field.getElementType(); | |
String typeName = type.getNameAsString(); | |
if (TYPE_REPLACEMENTS.containsKey(typeName)) { | |
type.setName(TYPE_REPLACEMENTS.get(typeName)); | |
modified = true; | |
} | |
} | |
} | |
// Update type references in generic type arguments | |
for (ClassOrInterfaceType type : cu.findAll(ClassOrInterfaceType.class)) { | |
if (type.getTypeArguments().isPresent()) { | |
for (com.github.javaparser.ast.type.Type typeArg : type.getTypeArguments().get()) { | |
if (typeArg instanceof ClassOrInterfaceType) { | |
ClassOrInterfaceType argType = (ClassOrInterfaceType) typeArg; | |
String typeName = argType.getNameAsString(); | |
if (TYPE_REPLACEMENTS.containsKey(typeName)) { | |
argType.setName(TYPE_REPLACEMENTS.get(typeName)); | |
modified = true; | |
} | |
} | |
} | |
} | |
} | |
return modified; | |
} | |
private boolean updateImports(CompilationUnit cu) { | |
boolean modified = false; | |
NodeList<ImportDeclaration> imports = cu.getImports(); | |
for (ImportDeclaration importDecl : imports) { | |
String name = importDecl.getNameAsString(); | |
if (IMPORT_REPLACEMENTS.containsKey(name)) { | |
importDecl.setName(IMPORT_REPLACEMENTS.get(name)); | |
modified = true; | |
} | |
} | |
return modified; | |
} | |
private boolean updateMojoImplementation(CompilationUnit cu) { | |
boolean modified = false; | |
for (ClassOrInterfaceDeclaration classDecl : cu.findAll(ClassOrInterfaceDeclaration.class)) { | |
// First check if we need to make changes | |
boolean hasAbstractMojo = classDecl.getExtendedTypes().stream() | |
.anyMatch(type -> type.getNameAsString().equals("AbstractMojo")); | |
if (hasAbstractMojo) { | |
// Clear all extended types | |
classDecl.getExtendedTypes().clear(); | |
// Add implements org.apache.maven.api.plugin.Mojo | |
classDecl.addImplementedType("org.apache.maven.api.plugin.Mojo"); | |
modified = true; | |
} | |
} | |
return modified; | |
} | |
private boolean updateThrowsClauses(CompilationUnit cu) { | |
boolean modified = false; | |
cu.findAll(ClassOrInterfaceDeclaration.class).forEach(classDecl -> { | |
classDecl.getMethods().forEach(method -> { | |
NodeList<com.github.javaparser.ast.type.ReferenceType> throwsList = method.getThrownExceptions(); | |
boolean hasExecutionException = false; | |
boolean hasFailureException = false; | |
// First check if we have both exception types | |
for (com.github.javaparser.ast.type.ReferenceType type : throwsList) { | |
String typeName = type.toString(); | |
if (typeName.contains("MojoExecutionException")) { | |
hasExecutionException = true; | |
} else if (typeName.contains("MojoFailureException")) { | |
hasFailureException = true; | |
} | |
} | |
// Remove both exception types if present and add MojoException once | |
if (hasExecutionException || hasFailureException) { | |
throwsList.removeIf(type -> | |
type.toString().contains("MojoExecutionException") || | |
type.toString().contains("MojoFailureException") | |
); | |
method.addThrownException(new ClassOrInterfaceType(null, "org.apache.maven.api.plugin.MojoException")); | |
} | |
}); | |
}); | |
return modified; | |
} | |
private boolean updateMojoAnnotations(CompilationUnit cu) { | |
boolean modified = false; | |
for (ClassOrInterfaceDeclaration classDecl : cu.findAll(ClassOrInterfaceDeclaration.class)) { | |
for (NormalAnnotationExpr anno : classDecl.findAll(NormalAnnotationExpr.class)) { | |
if (anno.getNameAsString().equals("Mojo")) { | |
Optional<ImportDeclaration> mojoImport = cu.getImports().stream() | |
.filter(imp -> imp.getNameAsString().endsWith(".Mojo")) | |
.findFirst(); | |
if (mojoImport.isPresent() && | |
mojoImport.get().getNameAsString().equals("org.apache.maven.plugins.annotations.Mojo")) { | |
System.out.println("DEBUG: Processing Maven Mojo annotation"); | |
// Check thread safety | |
boolean isThreadSafe = anno.getPairs().stream() | |
.filter(pair -> pair.getNameAsString().equals("threadSafe")) | |
.map(pair -> pair.getValue().toString()) | |
.map(val -> val.equals("true")) | |
.findFirst() | |
.orElse(false); | |
if (!isThreadSafe) { | |
// Add warning to class JavaDoc | |
addThreadSafetyWarning(classDecl); | |
modified = true; | |
} | |
// Remove threadSafe attribute | |
anno.getPairs().removeIf(pair -> pair.getNameAsString().equals("threadSafe")); | |
// Update defaultPhase if present | |
for (MemberValuePair pair : anno.getPairs()) { | |
if (pair.getNameAsString().equals("defaultPhase")) { | |
String phase = pair.getValue().toString(); | |
// Clean up the phase string (remove quotes and LifecyclePhase. prefix if present) | |
phase = phase.replaceAll("\"", "") | |
.replaceAll("LifecyclePhase\\.", ""); | |
if (PHASE_REPLACEMENTS.containsKey(phase)) { | |
String newPhase = PHASE_REPLACEMENTS.get(phase); | |
String phaseExpr; | |
if (newPhase.contains("+")) { | |
String[] parts = newPhase.split("\\s*\\+\\s*"); | |
phaseExpr = String.format("org.apache.maven.api.Lifecycle.%s + org.apache.maven.api.Lifecycle.Phase.%s", | |
parts[0].trim(), parts[1].trim()); | |
} else { | |
phaseExpr = "org.apache.maven.api.Lifecycle.Phase." + newPhase; | |
} | |
// Create a new name expression instead of string literal | |
pair.setValue(new NameExpr(phaseExpr)); | |
modified = true; | |
} | |
break; | |
} | |
} | |
} | |
} | |
} | |
} | |
return modified; | |
} | |
private boolean updateExceptionConstructions(CompilationUnit cu) { | |
boolean modified = false; | |
// Find all object creation expressions | |
for (ObjectCreationExpr objCreation : cu.findAll(ObjectCreationExpr.class)) { | |
String exceptionName = objCreation.getType().getNameAsString(); | |
if (EXCEPTION_REPLACEMENTS.containsKey(exceptionName)) { | |
// Get the current arguments | |
NodeList<Expression> arguments = objCreation.getArguments(); | |
// Create new MojoException with the same arguments | |
ObjectCreationExpr newException = new ObjectCreationExpr(); | |
newException.setType("org.apache.maven.api.plugin.MojoException"); | |
// Transfer all arguments to the new exception | |
newException.setArguments(arguments); | |
// Replace the old exception creation with the new one | |
objCreation.replace(newException); | |
modified = true; | |
// Make sure the import is updated | |
updateImportForException(cu); | |
} | |
} | |
return modified; | |
} | |
private void updateImportForException(CompilationUnit cu) { | |
// Remove old exception imports | |
cu.getImports().removeIf(importDecl -> | |
importDecl.getNameAsString().endsWith("MojoExecutionException") || | |
importDecl.getNameAsString().endsWith("MojoFailureException") | |
); | |
// Add new MojoException import if not already present | |
String newImport = "org.apache.maven.api.plugin.MojoException"; | |
boolean hasNewImport = cu.getImports().stream() | |
.anyMatch(importDecl -> importDecl.getNameAsString().equals(newImport)); | |
if (!hasNewImport) { | |
cu.addImport(newImport); | |
} | |
} | |
private void addThreadSafetyWarning(ClassOrInterfaceDeclaration classDecl) { | |
String warning = "TODO: Maven 4 plugins need to be thread safe. Please verify and fix thread safety issues."; | |
// Get or create JavaDoc | |
if (classDecl.getJavadoc().isPresent()) { | |
String existingDoc = classDecl.getJavadoc().get().getDescription().toText(); | |
if (!existingDoc.contains(warning)) { | |
classDecl.setJavadocComment(existingDoc + "\n\n" + warning); | |
} | |
} else { | |
classDecl.setJavadocComment(warning); | |
} | |
} | |
private Optional<String> resolveAnnotationPackage(Node node) { | |
// Find the import declaration for this annotation | |
return node.findCompilationUnit() | |
.flatMap(cu -> cu.getImports().stream() | |
.filter(imp -> imp.getNameAsString().endsWith("." + node.toString())) | |
.findFirst() | |
.map(imp -> imp.getNameAsString().substring(0, | |
imp.getNameAsString().lastIndexOf("."))) | |
); | |
} | |
private boolean isFromPackage(Node node, String expectedPackage) { | |
return resolveAnnotationPackage(node) | |
.map(pkg -> pkg.equals(expectedPackage)) | |
.orElse(false); | |
} | |
private boolean updateDependencyInjection(CompilationUnit cu) { | |
boolean modified = false; | |
// Replace Component annotations with Inject | |
for (MarkerAnnotationExpr anno : cu.findAll(MarkerAnnotationExpr.class)) { | |
if (anno.getNameAsString().equals("Component") && | |
isFromPackage(anno, "org.apache.maven.plugins.annotations")) { | |
anno.setName("Inject"); | |
modified = true; | |
} | |
} | |
return modified; | |
} | |
public static void main(String... args) { | |
int exitCode = new CommandLine(new Maven4Migration()).execute(args); | |
System.exit(exitCode); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment