From 32ddfa988b18d7b6b712e08210cb65693c03e4b1 Mon Sep 17 00:00:00 2001 From: Leo Galambos Date: Mon, 9 Mar 2026 23:30:45 +0100 Subject: [PATCH] chore: fix PMD warnings and improve code quality --- build.gradle | 11 +++++ .../egothor/methodatlas/MethodAtlasApp.java | 40 ++++++++++++------- .../methodatlas/ai/AiSuggestionEngine.java | 1 + .../methodatlas/ai/AnthropicClient.java | 2 +- .../egothor/methodatlas/ai/OllamaClient.java | 2 +- .../ai/OpenAiCompatibleClient.java | 2 +- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index aaeb29d..d63baab 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,17 @@ configurations { mockitoAgent } +pmd { + consoleOutput = true + toolVersion = '7.20.0' + sourceSets = [sourceSets.main] + ruleSetFiles = files(rootProject.file(".ruleset")) +} + +tasks.withType(Pmd) { + maxHeapSize = "16g" +} + java { toolchain { languageVersion = JavaLanguageVersion.of(21) diff --git a/src/main/java/org/egothor/methodatlas/MethodAtlasApp.java b/src/main/java/org/egothor/methodatlas/MethodAtlasApp.java index 8e88f33..0fe858f 100644 --- a/src/main/java/org/egothor/methodatlas/MethodAtlasApp.java +++ b/src/main/java/org/egothor/methodatlas/MethodAtlasApp.java @@ -7,6 +7,7 @@ import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -25,6 +26,7 @@ import com.github.javaparser.ParserConfiguration; import com.github.javaparser.ParserConfiguration.LanguageLevel; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.PackageDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.AnnotationExpr; @@ -145,7 +147,7 @@ import com.github.javaparser.ast.expr.MemberValuePair; * @see org.egothor.methodatlas.ai.AiSuggestionEngine * @see #main(String[]) */ -public class MethodAtlasApp { +public class MethodAtlasApp { // NOPMD private static final Logger LOG = Logger.getLogger(MethodAtlasApp.class.getName()); @@ -296,8 +298,8 @@ public class MethodAtlasApp { private static void processFile(Path path, OutputMode mode, AiOptions aiOptions, AiSuggestionEngine aiEngine) { try { CompilationUnit compilationUnit = StaticJavaParser.parse(path); - String packageName = compilationUnit.getPackageDeclaration() - .map(packageDeclaration -> packageDeclaration.getNameAsString()).orElse(""); + String packageName = compilationUnit.getPackageDeclaration().map(PackageDeclaration::getNameAsString) + .orElse(""); compilationUnit.findAll(ClassOrInterfaceDeclaration.class).forEach(clazz -> { String className = clazz.getNameAsString(); @@ -317,8 +319,9 @@ public class MethodAtlasApp { }); }); } catch (Exception e) { - LOG.log(Level.WARNING, "Failed to parse: {0}", path); - e.printStackTrace(); + if (LOG.isLoggable(Level.WARNING)) { + LOG.log(Level.WARNING, "Failed to parse: {0} due to {1}", new Object[] { path, e.getMessage() }); + } } } @@ -346,8 +349,10 @@ public class MethodAtlasApp { String classSource = clazz.toString(); if (classSource.length() > aiOptions.maxClassChars()) { - LOG.log(Level.INFO, "Skipping AI for {0}: class source too large ({1} chars)", - new Object[] { fqcn, classSource.length() }); + if (LOG.isLoggable(Level.INFO)) { + LOG.log(Level.INFO, "Skipping AI for {0}: class source too large ({1} chars)", + new Object[] { fqcn, classSource.length() }); + } return SuggestionLookup.from(null); } @@ -355,7 +360,10 @@ public class MethodAtlasApp { AiClassSuggestion aiClassSuggestion = aiEngine.suggestForClass(fqcn, classSource); return SuggestionLookup.from(aiClassSuggestion); } catch (AiSuggestionException e) { - LOG.log(Level.WARNING, "AI suggestion failed for class " + fqcn + ": " + e.getMessage()); + if (LOG.isLoggable(Level.WARNING)) { + LOG.log(Level.WARNING, "AI suggestion failed for class {0}: {1}", + new Object[] { fqcn, e.getMessage() }); + } return SuggestionLookup.from(null); } } @@ -523,7 +531,8 @@ public class MethodAtlasApp { * @throws IllegalArgumentException if an option value is missing, malformed, or * unsupported */ - private static CliConfig parseArgs(String[] args) { + @SuppressWarnings("PMD.AvoidReassigningLoopVariables") + private static CliConfig parseArgs(String... args) { OutputMode outputMode = OutputMode.CSV; List paths = new ArrayList<>(); AiOptions.Builder aiBuilder = AiOptions.builder(); @@ -534,14 +543,15 @@ public class MethodAtlasApp { switch (arg) { case "-plain" -> outputMode = OutputMode.PLAIN; case "-ai" -> aiBuilder.enabled(true); - case "-ai-provider" -> aiBuilder.provider(AiProvider.valueOf(nextArg(args, ++i, arg).toUpperCase())); + case "-ai-provider" -> + aiBuilder.provider(AiProvider.valueOf(nextArg(args, ++i, arg).toUpperCase(Locale.ROOT))); case "-ai-model" -> aiBuilder.modelName(nextArg(args, ++i, arg)); case "-ai-base-url" -> aiBuilder.baseUrl(nextArg(args, ++i, arg)); case "-ai-api-key" -> aiBuilder.apiKey(nextArg(args, ++i, arg)); case "-ai-api-key-env" -> aiBuilder.apiKeyEnv(nextArg(args, ++i, arg)); case "-ai-taxonomy" -> aiBuilder.taxonomyFile(Paths.get(nextArg(args, ++i, arg))); - case "-ai-taxonomy-mode" -> - aiBuilder.taxonomyMode(AiOptions.TaxonomyMode.valueOf(nextArg(args, ++i, arg).toUpperCase())); + case "-ai-taxonomy-mode" -> aiBuilder + .taxonomyMode(AiOptions.TaxonomyMode.valueOf(nextArg(args, ++i, arg).toUpperCase(Locale.ROOT))); case "-ai-max-class-chars" -> aiBuilder.maxClassChars(Integer.parseInt(nextArg(args, ++i, arg))); case "-ai-timeout-sec" -> aiBuilder.timeout(Duration.ofSeconds(Long.parseLong(nextArg(args, ++i, arg)))); @@ -635,9 +645,9 @@ public class MethodAtlasApp { for (AnnotationExpr annotation : method.getAnnotations()) { String name = annotation.getNameAsString(); - if ("Tag".equals(name)) { + if ("Tag".equals(name)) { // NOPMD extractTagValue(annotation).ifPresent(tagValues::add); - } else if ("Tags".equals(name)) { + } else if ("Tags".equals(name)) { // NOPMD extractTagsContainerValues(annotation, tagValues); } } @@ -665,7 +675,7 @@ public class MethodAtlasApp { if (annotation.isNormalAnnotationExpr()) { for (MemberValuePair pair : annotation.asNormalAnnotationExpr().getPairs()) { - if ("value".equals(pair.getNameAsString())) { + if ("value".equals(pair.getNameAsString())) { // NOPMD extractTagsFromContainerValue(pair.getValue(), tagValues); } } diff --git a/src/main/java/org/egothor/methodatlas/ai/AiSuggestionEngine.java b/src/main/java/org/egothor/methodatlas/ai/AiSuggestionEngine.java index 8e82f35..5001167 100644 --- a/src/main/java/org/egothor/methodatlas/ai/AiSuggestionEngine.java +++ b/src/main/java/org/egothor/methodatlas/ai/AiSuggestionEngine.java @@ -34,6 +34,7 @@ package org.egothor.methodatlas.ai; * @see AiProviderClient * @see org.egothor.methodatlas.MethodAtlasApp */ +@SuppressWarnings("PMD.ImplicitFunctionalInterface") public interface AiSuggestionEngine { /** * Requests AI-generated security classification for a single parsed test class. diff --git a/src/main/java/org/egothor/methodatlas/ai/AnthropicClient.java b/src/main/java/org/egothor/methodatlas/ai/AnthropicClient.java index 45b6eb0..1c96371 100644 --- a/src/main/java/org/egothor/methodatlas/ai/AnthropicClient.java +++ b/src/main/java/org/egothor/methodatlas/ai/AnthropicClient.java @@ -158,7 +158,7 @@ public final class AnthropicClient implements AiProviderClient { AiClassSuggestion suggestion = httpSupport.objectMapper().readValue(json, AiClassSuggestion.class); return normalize(suggestion); - } catch (Exception e) { + } catch (Exception e) { // NOPMD throw new AiSuggestionException("Anthropic suggestion failed for " + fqcn, e); } } diff --git a/src/main/java/org/egothor/methodatlas/ai/OllamaClient.java b/src/main/java/org/egothor/methodatlas/ai/OllamaClient.java index db14343..0421d65 100644 --- a/src/main/java/org/egothor/methodatlas/ai/OllamaClient.java +++ b/src/main/java/org/egothor/methodatlas/ai/OllamaClient.java @@ -162,7 +162,7 @@ public final class OllamaClient implements AiProviderClient { AiClassSuggestion suggestion = httpSupport.objectMapper().readValue(json, AiClassSuggestion.class); return normalize(suggestion); - } catch (Exception e) { + } catch (Exception e) { // NOPMD throw new AiSuggestionException("Ollama suggestion failed for " + fqcn, e); } } diff --git a/src/main/java/org/egothor/methodatlas/ai/OpenAiCompatibleClient.java b/src/main/java/org/egothor/methodatlas/ai/OpenAiCompatibleClient.java index 080b554..629bc7f 100644 --- a/src/main/java/org/egothor/methodatlas/ai/OpenAiCompatibleClient.java +++ b/src/main/java/org/egothor/methodatlas/ai/OpenAiCompatibleClient.java @@ -170,7 +170,7 @@ public final class OpenAiCompatibleClient implements AiProviderClient { AiClassSuggestion suggestion = httpSupport.objectMapper().readValue(json, AiClassSuggestion.class); return normalize(suggestion); - } catch (Exception e) { + } catch (Exception e) { // NOPMD throw new AiSuggestionException("OpenAI-compatible suggestion failed for " + fqcn, e); } }