From 0cb5b82e90d12fc7f482b340341ce94f2c1975d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Bre=C3=B1a=20Moral?= Date: Fri, 27 Feb 2026 11:58:56 +0100 Subject: [PATCH 1/8] Improving naming --- .../java/info/jab/pml/SkillsGenerator.java | 18 ++++++--- ...ractices-skill-summary.md => 110-skill.md} | 0 ...ndencies-skill-summary.md => 111-skill.md} | 0 .../info/jab/pml/SkillsGeneratorTest.java | 37 +++++++++++-------- 4 files changed, 34 insertions(+), 21 deletions(-) rename skills-generator/src/main/resources/skills/{110-java-maven-best-practices-skill-summary.md => 110-skill.md} (100%) rename skills-generator/src/main/resources/skills/{111-java-maven-dependencies-skill-summary.md => 111-skill.md} (100%) diff --git a/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java b/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java index 01feeffb..19048a33 100644 --- a/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java +++ b/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java @@ -9,11 +9,11 @@ import org.w3c.dom.NodeList; /** - * Generator for Agent Skills (SKILL.md and references) from XML rule definitions and skill-summary files. + * Generator for Agent Skills (SKILL.md and references) from XML rule definitions and skill files. *

* Reuses CursorRulesGenerator for full rule content. SKILL.md is sourced from - * {@code skills/{skillId}-skill-summary.md} (user-editable). SkillsInventory defines which skills exist; - * each must have a matching skill-summary file in {@code skills/}. + * {@code skills/{numericId}-skill.md} (user-editable), where numericId is extracted from skillId (e.g. 110 from 110-java-maven-best-practices). + * SkillsInventory defines which skills exist; each must have a matching skill file in {@code skills/}. */ public final class SkillsGenerator { @@ -104,18 +104,24 @@ private String generateReferenceContent(String skillId, SkillMetadata metadata) } private String loadSkillSummary(String skillId) { - String resourceName = "skills/" + skillId + "-skill-summary.md"; + String numericId = extractNumericId(skillId); + String resourceName = "skills/" + numericId + "-skill.md"; try (InputStream stream = getResource(resourceName)) { if (stream == null) { - throw new RuntimeException("Skill-summary resource not found: " + resourceName + throw new RuntimeException("Skill resource not found: " + resourceName + ". Each skill in SkillsInventory must have a matching file in skills/."); } return new String(stream.readAllBytes()); } catch (Exception e) { - throw new RuntimeException("Failed to load skill-summary: " + resourceName, e); + throw new RuntimeException("Failed to load skill: " + resourceName, e); } } + private static String extractNumericId(String skillId) { + int dash = skillId.indexOf('-'); + return dash > 0 ? skillId.substring(0, dash) : skillId; + } + private InputStream getResource(String name) { return Optional.ofNullable(getClass().getClassLoader().getResourceAsStream(name)) .or(() -> Optional.ofNullable(Thread.currentThread().getContextClassLoader().getResourceAsStream(name))) diff --git a/skills-generator/src/main/resources/skills/110-java-maven-best-practices-skill-summary.md b/skills-generator/src/main/resources/skills/110-skill.md similarity index 100% rename from skills-generator/src/main/resources/skills/110-java-maven-best-practices-skill-summary.md rename to skills-generator/src/main/resources/skills/110-skill.md diff --git a/skills-generator/src/main/resources/skills/111-java-maven-dependencies-skill-summary.md b/skills-generator/src/main/resources/skills/111-skill.md similarity index 100% rename from skills-generator/src/main/resources/skills/111-java-maven-dependencies-skill-summary.md rename to skills-generator/src/main/resources/skills/111-skill.md diff --git a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java index d8bcebaf..271a8dd2 100644 --- a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java +++ b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java @@ -36,18 +36,18 @@ private static Stream provideSkillIds() { @MethodSource("provideSkillIds") @DisplayName("Should generate valid SKILL.md and reference for each skill") void should_generateValidSkill_when_skillIdProvided(String skillId) throws IOException { - // Given - skill-summary in resources/skills/ is the source of truth - String expectedSkillSummary = loadSkillSummaryFromResources(skillId); + // Given - skill file in resources/skills/ is the source of truth + String expectedSkillMd = loadSkillFromResources(skillId); SkillsGenerator generator = new SkillsGenerator(); // When SkillsGenerator.SkillOutput output = generator.generateSkill(skillId); - // Then - Generated SKILL.md must exactly match the skill-summary source (user-editable) + // Then - Generated SKILL.md must exactly match the skill source (user-editable) assertThat(output.skillMd()) - .withFailMessage("Generated SKILL.md must match skills/%s-skill-summary.md. " - + "Update the skill-summary file and run the build to promote changes.", skillId) - .isEqualTo(expectedSkillSummary); + .withFailMessage("Generated SKILL.md must match skills/%s-skill.md. " + + "Update the skill file and run the build to promote changes.", numericId(skillId)) + .isEqualTo(expectedSkillMd); // Then - Validate reference content assertThat(output.referenceMd()) @@ -69,21 +69,22 @@ void should_generateValidSkill_when_skillIdProvided(String skillId) throws IOExc class SkillsInventorySyncTests { @Test - @DisplayName("SkillsInventory must have matching skill-summary file for each skill") - void should_haveMatchingSkillSummary_forEachSkillInInventory() { + @DisplayName("SkillsInventory must have matching skill file for each skill") + void should_haveMatchingSkillFile_forEachSkillInInventory() { List skillIds = SkillsInventory.skillIds().toList(); assertThat(skillIds).isNotEmpty(); for (String skillId : skillIds) { - String resourceName = "skills/" + skillId + "-skill-summary.md"; + String numId = numericId(skillId); + String resourceName = "skills/" + numId + "-skill.md"; try (InputStream stream = SkillsGeneratorTest.class.getClassLoader() .getResourceAsStream(resourceName)) { assertThat(stream) - .withFailMessage("SkillsInventory contains '%s' but skills/%s-skill-summary.md not found. " - + "Add the skill-summary file for each skill in the inventory.", skillId, skillId) + .withFailMessage("SkillsInventory contains '%s' but skills/%s-skill.md not found. " + + "Add the skill file for each skill in the inventory.", skillId, numId) .isNotNull(); } catch (IOException e) { - throw new RuntimeException("Failed to verify skill-summary for " + skillId, e); + throw new RuntimeException("Failed to verify skill file for " + skillId, e); } } } @@ -104,16 +105,22 @@ void should_throwException_when_skillXmlDoesNotExist() { } } - private String loadSkillSummaryFromResources(String skillId) throws IOException { - String resourceName = "skills/" + skillId + "-skill-summary.md"; + private String loadSkillFromResources(String skillId) throws IOException { + String numId = numericId(skillId); + String resourceName = "skills/" + numId + "-skill.md"; try (InputStream stream = SkillsGeneratorTest.class.getClassLoader().getResourceAsStream(resourceName)) { if (stream == null) { - throw new IllegalArgumentException("Skill-summary not found: " + resourceName); + throw new IllegalArgumentException("Skill file not found: " + resourceName); } return new String(stream.readAllBytes(), StandardCharsets.UTF_8); } } + private static String numericId(String skillId) { + int dash = skillId.indexOf('-'); + return dash > 0 ? skillId.substring(0, dash) : skillId; + } + private void saveToTarget(SkillsGenerator.SkillOutput output) throws IOException { Path targetDir = Paths.get("target", "skills", output.skillId()); Path referencesDir = targetDir.resolve("references"); From a006755a8fe82a8b11c936973a93b45cbb557bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Bre=C3=B1a=20Moral?= Date: Sun, 1 Mar 2026 11:07:15 +0100 Subject: [PATCH 2/8] Improving Skill generation --- pom.xml | 5 + skills-generator/pom.xml | 33 +++ .../java/info/jab/pml/SkillsGenerator.java | 15 +- .../java/info/jab/pml/SkillsInventory.java | 193 ++++++++++++++++++ .../src/main/resources/skill-inventory.json | 4 + .../info/jab/pml/SkillsGeneratorTest.java | 23 +-- .../java/info/jab/pml/SkillsInventory.java | 16 -- 7 files changed, 254 insertions(+), 35 deletions(-) create mode 100644 skills-generator/src/main/java/info/jab/pml/SkillsInventory.java create mode 100644 skills-generator/src/main/resources/skill-inventory.json delete mode 100644 skills-generator/src/test/java/info/jab/pml/SkillsInventory.java diff --git a/pom.xml b/pom.xml index 0fdde5dc..51a91c1e 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,11 @@ cursor-rules-java-generator ${project.version} + + com.fasterxml.jackson.core + jackson-databind + 2.18.2 + diff --git a/skills-generator/pom.xml b/skills-generator/pom.xml index 8bb43ff1..5c2e281f 100644 --- a/skills-generator/pom.xml +++ b/skills-generator/pom.xml @@ -21,6 +21,12 @@ cursor-rules-java-generator + + + com.fasterxml.jackson.core + jackson-databind + + org.slf4j @@ -60,6 +66,33 @@ + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-test-resources-from-main + generate-test-resources + + copy-resources + + + ${project.build.testOutputDirectory} + + + ${project.basedir}/src/main/resources + + skill-inventory.json + skills/** + + + + + + + + org.apache.maven.plugins diff --git a/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java b/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java index 19048a33..db2049e5 100644 --- a/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java +++ b/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java @@ -2,6 +2,7 @@ import java.io.InputStream; import java.util.Optional; +import java.util.stream.Stream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; @@ -13,7 +14,8 @@ *

* Reuses CursorRulesGenerator for full rule content. SKILL.md is sourced from * {@code skills/{numericId}-skill.md} (user-editable), where numericId is extracted from skillId (e.g. 110 from 110-java-maven-best-practices). - * SkillsInventory defines which skills exist; each must have a matching skill file in {@code skills/}. + * The list of skills to generate is defined in {@code skill-inventory.json}; each must have a + * matching skill summary in {@code skills/} and a matching system-prompt in {@code system-prompts/}. */ public final class SkillsGenerator { @@ -23,6 +25,17 @@ public SkillsGenerator() { this.cursorRulesGenerator = new CursorRulesGenerator(); } + /** + * Generates SKILL.md and reference content for all skills in the inventory. + * Validates that each skill has a summary in {@code skills/} and a matching system-prompt. + * + * @return stream of generated skill outputs + */ + public Stream generateAllSkills() { + return SkillsInventory.skillIds() + .map(this::generateSkill); + } + /** * Generates SKILL.md and reference content for a given skill. * diff --git a/skills-generator/src/main/java/info/jab/pml/SkillsInventory.java b/skills-generator/src/main/java/info/jab/pml/SkillsInventory.java new file mode 100644 index 00000000..414de91a --- /dev/null +++ b/skills-generator/src/main/java/info/jab/pml/SkillsInventory.java @@ -0,0 +1,193 @@ +package info.jab.pml; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Stream; + +/** + * Inventory of skills to generate, loaded from {@code skill-inventory.json}. + *

+ * Each entry has an {@code id}. The corresponding skillId is derived by matching + * system-prompts with prefix {@code {id}-} (e.g. id 110 matches 110-java-maven-best-practices.xml). + * Each skill must have a summary in {@code skills/{id}-skill.md} and a matching + * system-prompt in {@code system-prompts/}. + */ +public final class SkillsInventory { + + private static final String INVENTORY_RESOURCE = "skill-inventory.json"; + private static final String SYSTEM_PROMPTS_PREFIX = "system-prompts/"; + + private SkillsInventory() {} + + /** + * Returns the skill IDs from the inventory. For each id, resolves the skillId + * by matching system-prompts with prefix {@code {id}-}. Validates that each + * skill has a summary in {@code skills/} and a matching system-prompt. + * + * @return stream of skill IDs (e.g. 110-java-maven-best-practices) + * @throws RuntimeException if the inventory cannot be loaded or validation fails + */ + public static Stream skillIds() { + List entries = loadInventory(); + List skillIds = new ArrayList<>(); + + for (InventoryEntry entry : entries) { + validateSkillSummaryExists(entry.id()); + String skillId = resolveSkillIdFromPrefix(entry.id()); + skillIds.add(skillId); + } + + return skillIds.stream(); + } + + /** + * Resolves skillId by finding the system-prompt XML that starts with {@code {id}-}. + * + * @param id numeric id from inventory + * @return full skillId (e.g. 110-java-maven-best-practices) + * @throws RuntimeException if none or multiple system-prompts match + */ + public static String resolveSkillIdFromPrefix(int id) { + String prefix = id + "-"; + List matches = listSystemPromptBaseNames().stream() + .filter(name -> name.startsWith(prefix) && name.endsWith(".xml")) + .map(name -> name.substring(0, name.length() - 4)) + .toList(); + + if (matches.isEmpty()) { + throw new RuntimeException("No system-prompt found for id " + id + + ". Add a system-prompts/" + prefix + "*.xml file in system-prompts-generator."); + } + if (matches.size() > 1) { + throw new RuntimeException("Multiple system-prompts match id " + id + ": " + matches); + } + return matches.getFirst(); + } + + private static List listSystemPromptBaseNames() { + try { + // Use a known resource from cursor-rules-java-generator to locate the JAR + URL anchor = getResourceUrl("cursor-rules.xsl"); + if (anchor == null) { + throw new RuntimeException("cursor-rules.xsl not found on classpath"); + } + if ("jar".equals(anchor.getProtocol())) { + JarURLConnection conn = (JarURLConnection) anchor.openConnection(); + try (JarFile jar = conn.getJarFile()) { + return jar.stream() + .map(JarEntry::getName) + .filter(name -> name.startsWith(SYSTEM_PROMPTS_PREFIX) && name.endsWith(".xml")) + .filter(name -> !name.contains("/assets/")) + .map(name -> name.substring(SYSTEM_PROMPTS_PREFIX.length())) + .toList(); + } + } + if ("file".equals(anchor.getProtocol())) { + Path base = Paths.get(anchor.toURI()).getParent(); + Path systemPromptsDir = base.resolve("system-prompts"); + if (!Files.isDirectory(systemPromptsDir)) { + return List.of(); + } + try (Stream files = Files.list(systemPromptsDir)) { + return files + .filter(p -> p.toString().endsWith(".xml")) + .filter(p -> !p.toString().contains("assets")) + .map(p -> p.getFileName().toString()) + .toList(); + } + } + return List.of(); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException("Failed to list system-prompts", e); + } + } + + private static URL getResourceUrl(String name) { + ClassLoader cl = SkillsInventory.class.getClassLoader(); + URL url = cl.getResource(name); + if (url == null) { + url = Thread.currentThread().getContextClassLoader().getResource(name); + } + return url; + } + + /** + * Loads and parses skill-inventory.json. + */ + public static List loadInventory() { + try (InputStream stream = getResource(INVENTORY_RESOURCE)) { + if (stream == null) { + throw new RuntimeException("Skill inventory not found: " + INVENTORY_RESOURCE); + } + String json = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + return parseInventory(json); + } catch (Exception e) { + throw new RuntimeException("Failed to load skill inventory", e); + } + } + + private static List parseInventory(String json) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(json); + if (!root.isArray()) { + throw new RuntimeException("Skill inventory must be a JSON array"); + } + + List entries = new ArrayList<>(); + for (JsonNode node : root) { + int id = node.required("id").asInt(); + entries.add(new InventoryEntry(id)); + } + return entries; + } catch (Exception e) { + throw new RuntimeException("Failed to parse skill inventory", e); + } + } + + private static void validateSkillSummaryExists(int id) { + String resourceName = "skills/" + id + "-skill.md"; + try (InputStream stream = getResource(resourceName)) { + if (stream == null) { + throw new RuntimeException("Skill summary not found: " + resourceName + + ". Add skills/" + id + "-skill.md for each skill in the inventory."); + } + } catch (Exception e) { + if (e instanceof RuntimeException re) { + throw re; + } + throw new RuntimeException("Failed to validate skill summary: " + resourceName, e); + } + } + + private static InputStream getResource(String name) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + cl = SkillsInventory.class.getClassLoader(); + } + InputStream in = cl.getResourceAsStream(name); + if (in == null && cl != SkillsInventory.class.getClassLoader()) { + in = SkillsInventory.class.getClassLoader().getResourceAsStream(name); + } + return in; + } + + /** + * Single entry from skill-inventory.json: numeric id only. skillId is derived + * by matching system-prompts with prefix {@code {id}-}. + */ + public record InventoryEntry(int id) {} +} diff --git a/skills-generator/src/main/resources/skill-inventory.json b/skills-generator/src/main/resources/skill-inventory.json new file mode 100644 index 00000000..bb2682f4 --- /dev/null +++ b/skills-generator/src/main/resources/skill-inventory.json @@ -0,0 +1,4 @@ +[ + {"id": 110}, + {"id": 111} +] diff --git a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java index 271a8dd2..aceededd 100644 --- a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java +++ b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java @@ -65,28 +65,15 @@ void should_generateValidSkill_when_skillIdProvided(String skillId) throws IOExc } @Nested - @DisplayName("SkillsInventory and skills directory sync") - class SkillsInventorySyncTests { + @DisplayName("Skill inventory and resources sync") + class SkillInventorySyncTests { @Test - @DisplayName("SkillsInventory must have matching skill file for each skill") - void should_haveMatchingSkillFile_forEachSkillInInventory() { + @DisplayName("skill-inventory.json entries must have matching skill summary and system-prompt") + void should_validateInventoryMatchesSkillsAndSystemPrompts() { + // skillIds() validates each entry has skills/{id}-skill.md and system-prompt with prefix {id}- List skillIds = SkillsInventory.skillIds().toList(); assertThat(skillIds).isNotEmpty(); - - for (String skillId : skillIds) { - String numId = numericId(skillId); - String resourceName = "skills/" + numId + "-skill.md"; - try (InputStream stream = SkillsGeneratorTest.class.getClassLoader() - .getResourceAsStream(resourceName)) { - assertThat(stream) - .withFailMessage("SkillsInventory contains '%s' but skills/%s-skill.md not found. " - + "Add the skill file for each skill in the inventory.", skillId, numId) - .isNotNull(); - } catch (IOException e) { - throw new RuntimeException("Failed to verify skill file for " + skillId, e); - } - } } } diff --git a/skills-generator/src/test/java/info/jab/pml/SkillsInventory.java b/skills-generator/src/test/java/info/jab/pml/SkillsInventory.java deleted file mode 100644 index 1bedcad0..00000000 --- a/skills-generator/src/test/java/info/jab/pml/SkillsInventory.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.jab.pml; - -import java.util.stream.Stream; - -public final class SkillsInventory { - - private SkillsInventory() { - } - - public static Stream skillIds() { - return Stream.of( - "110-java-maven-best-practices", - "111-java-maven-dependencies" - ); - } -} From ff1044c493c7a46afaa83584bb5639adf3d988be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Bre=C3=B1a=20Moral?= Date: Sun, 1 Mar 2026 11:10:01 +0100 Subject: [PATCH 3/8] Minor change --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 51a91c1e..20677427 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ 1.5.18 5.13.3 3.27.3 + 2.18.2 3.14.0 @@ -81,7 +82,7 @@ com.fasterxml.jackson.core jackson-databind - 2.18.2 + ${jackson.version} From 3df66dd6aa002cf7b492fde6ffb9c4ae1d7fac2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Bre=C3=B1a=20Moral?= Date: Sun, 1 Mar 2026 11:27:23 +0100 Subject: [PATCH 4/8] Improving current skills --- AGENTS.md | 2 +- .../src/main/java/info/jab/pml/SkillsGenerator.java | 11 ++++++++++- .../src/main/resources/skills/110-skill.md | 8 +++++--- .../src/main/resources/skills/111-skill.md | 6 +++--- .../test/java/info/jab/pml/SkillsGeneratorTest.java | 6 +++++- skills/110-java-maven-best-practices/SKILL.md | 8 +++++--- skills/111-java-maven-dependencies/SKILL.md | 6 +++--- 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 70624a8d..8ce77e1d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,7 +42,7 @@ You are an expert Java developer and technical writer for this project. ./mvnw clean install -pl skills-generator # Validate agent skills -npx skill-check .agents/skills +npx skill-check skills # Serve the website locally jwebserver -p 8000 -d "$(pwd)/docs" diff --git a/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java b/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java index db2049e5..54658a71 100644 --- a/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java +++ b/skills-generator/src/main/java/info/jab/pml/SkillsGenerator.java @@ -19,6 +19,8 @@ */ public final class SkillsGenerator { + private static final String PROJECT_TAG = " Part of the skills-for-java project"; + private final CursorRulesGenerator cursorRulesGenerator; public SkillsGenerator() { @@ -124,12 +126,19 @@ private String loadSkillSummary(String skillId) { throw new RuntimeException("Skill resource not found: " + resourceName + ". Each skill in SkillsInventory must have a matching file in skills/."); } - return new String(stream.readAllBytes()); + String content = new String(stream.readAllBytes()); + return appendProjectTagToDescription(content); } catch (Exception e) { throw new RuntimeException("Failed to load skill: " + resourceName, e); } } + private String appendProjectTagToDescription(String content) { + return content.lines() + .map(line -> line.startsWith("description:") ? line + PROJECT_TAG : line) + .collect(java.util.stream.Collectors.joining(System.lineSeparator(), "", System.lineSeparator())); + } + private static String extractNumericId(String skillId) { int dash = skillId.indexOf('-'); return dash > 0 ? skillId.substring(0, dash) : skillId; diff --git a/skills-generator/src/main/resources/skills/110-skill.md b/skills-generator/src/main/resources/skills/110-skill.md index 1e8dee9b..40c3816b 100644 --- a/skills-generator/src/main/resources/skills/110-skill.md +++ b/skills-generator/src/main/resources/skills/110-skill.md @@ -1,6 +1,6 @@ --- name: 110-java-maven-best-practices -description: Use when you need to improve your Maven pom.xml using best practices +description: Use when you need to review, improve, or troubleshoot a Maven pom.xml file — including dependency management with BOMs, plugin configuration, version centralization, multi-module project structure, build profiles, or any situation where you want to align your Maven setup with industry best practices. metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT @@ -9,9 +9,11 @@ metadata: Improve Maven POM configuration using industry-standard best practices. -**Core areas:** Dependency management via `` and BOMs, standard directory layout (`src/main/java`, `src/test/java`), centralized plugin management, build profiles for environment-specific settings, readable POM structure with version properties, explicit repository declaration, and version centralization. +**Core areas:** Dependency management via `` and BOMs, standard directory layout (`src/main/java`, `src/test/java`), centralized plugin management, build profiles for environment-specific settings, readable POM structure with version properties, explicit repository declaration, version centralization, multi-module project structure with proper inheritance, and cross-module version consistency. -**Prerequisites:** Run `mvn validate` before applying recommendations. If validation fails, **stop** and ask the user to fix issues—do not proceed until resolved. +**Prerequisites:** Run `./mvnw validate` or `mvn validate` before applying recommendations. If validation fails, **stop** and ask the user to fix issues—do not proceed until resolved. + +**Multi-module scope:** After reading the root `pom.xml`, check for a `` section. If present, read **every** child module's `pom.xml` before making any recommendations. Check each child for hardcoded versions that duplicate parent ``, redundant `` blocks, properties that should be centralized, and version drift across sibling modules. **Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. diff --git a/skills-generator/src/main/resources/skills/111-skill.md b/skills-generator/src/main/resources/skills/111-skill.md index d7e61c13..0f7bd916 100644 --- a/skills-generator/src/main/resources/skills/111-skill.md +++ b/skills-generator/src/main/resources/skills/111-skill.md @@ -1,6 +1,6 @@ --- name: 111-java-maven-dependencies -description: Use when you need to add Maven dependencies to your project +description: Use when you need to add or evaluate Maven dependencies that improve code quality — including nullness annotations (JSpecify), static analysis (Error Prone + NullAway), functional programming (VAVR), or architecture testing (ArchUnit) — and want a consultative, question-driven approach that adds only what you actually need. metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT @@ -9,11 +9,11 @@ metadata: Add essential Maven dependencies that enhance code quality and safety through a consultative, question-driven approach. -**Components:** **JSpecify** (nullness annotations, `provided` scope), **Error Prone + NullAway** (enhanced static analysis with compile-time null checking), and **VAVR** (functional programming with Try/Either and immutable collections). +**Components:** **JSpecify** (nullness annotations, `provided` scope), **Error Prone + NullAway** (enhanced static analysis with compile-time null checking), **VAVR** (functional programming with Try/Either and immutable collections), and **ArchUnit** (architecture rule enforcement, `test` scope). **Prerequisites:** Run `mvn validate` before any changes. Ensure Maven Wrapper exists; if not, **stop** and prompt the user to install it—do not proceed until resolved. -**Before asking questions:** Read the reference to use the exact wording and options from the template. Ask questions one-by-one in strict order (JSpecify → Error Prone/NullAway → package name for NullAway → VAVR) and add only what the user selects. Use consultative language, present trade-offs, and wait for user responses before implementing. +**Before asking questions:** Read the reference to use the exact wording and options from the template. Ask questions one-by-one in strict order (JSpecify → Enhanced Compiler Analysis (conditional) → VAVR → ArchUnit) and add only what the user selects. Use consultative language, present trade-offs, and wait for user responses before implementing. ## Reference diff --git a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java index aceededd..7cb720ba 100644 --- a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java +++ b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java @@ -7,6 +7,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -99,7 +100,10 @@ private String loadSkillFromResources(String skillId) throws IOException { if (stream == null) { throw new IllegalArgumentException("Skill file not found: " + resourceName); } - return new String(stream.readAllBytes(), StandardCharsets.UTF_8); + String content = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + return content.lines() + .map(line -> line.startsWith("description:") ? line + " Part of the skills-for-java project" : line) + .collect(Collectors.joining(System.lineSeparator(), "", System.lineSeparator())); } } diff --git a/skills/110-java-maven-best-practices/SKILL.md b/skills/110-java-maven-best-practices/SKILL.md index 1e8dee9b..b51cbecf 100644 --- a/skills/110-java-maven-best-practices/SKILL.md +++ b/skills/110-java-maven-best-practices/SKILL.md @@ -1,6 +1,6 @@ --- name: 110-java-maven-best-practices -description: Use when you need to improve your Maven pom.xml using best practices +description: Use when you need to review, improve, or troubleshoot a Maven pom.xml file — including dependency management with BOMs, plugin configuration, version centralization, multi-module project structure, build profiles, or any situation where you want to align your Maven setup with industry best practices. Part of the skills-for-java project metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT @@ -9,9 +9,11 @@ metadata: Improve Maven POM configuration using industry-standard best practices. -**Core areas:** Dependency management via `` and BOMs, standard directory layout (`src/main/java`, `src/test/java`), centralized plugin management, build profiles for environment-specific settings, readable POM structure with version properties, explicit repository declaration, and version centralization. +**Core areas:** Dependency management via `` and BOMs, standard directory layout (`src/main/java`, `src/test/java`), centralized plugin management, build profiles for environment-specific settings, readable POM structure with version properties, explicit repository declaration, version centralization, multi-module project structure with proper inheritance, and cross-module version consistency. -**Prerequisites:** Run `mvn validate` before applying recommendations. If validation fails, **stop** and ask the user to fix issues—do not proceed until resolved. +**Prerequisites:** Run `./mvnw validate` or `mvn validate` before applying recommendations. If validation fails, **stop** and ask the user to fix issues—do not proceed until resolved. + +**Multi-module scope:** After reading the root `pom.xml`, check for a `` section. If present, read **every** child module's `pom.xml` before making any recommendations. Check each child for hardcoded versions that duplicate parent ``, redundant `` blocks, properties that should be centralized, and version drift across sibling modules. **Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. diff --git a/skills/111-java-maven-dependencies/SKILL.md b/skills/111-java-maven-dependencies/SKILL.md index d7e61c13..45a2c58a 100644 --- a/skills/111-java-maven-dependencies/SKILL.md +++ b/skills/111-java-maven-dependencies/SKILL.md @@ -1,6 +1,6 @@ --- name: 111-java-maven-dependencies -description: Use when you need to add Maven dependencies to your project +description: Use when you need to add or evaluate Maven dependencies that improve code quality — including nullness annotations (JSpecify), static analysis (Error Prone + NullAway), functional programming (VAVR), or architecture testing (ArchUnit) — and want a consultative, question-driven approach that adds only what you actually need. Part of the skills-for-java project metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT @@ -9,11 +9,11 @@ metadata: Add essential Maven dependencies that enhance code quality and safety through a consultative, question-driven approach. -**Components:** **JSpecify** (nullness annotations, `provided` scope), **Error Prone + NullAway** (enhanced static analysis with compile-time null checking), and **VAVR** (functional programming with Try/Either and immutable collections). +**Components:** **JSpecify** (nullness annotations, `provided` scope), **Error Prone + NullAway** (enhanced static analysis with compile-time null checking), **VAVR** (functional programming with Try/Either and immutable collections), and **ArchUnit** (architecture rule enforcement, `test` scope). **Prerequisites:** Run `mvn validate` before any changes. Ensure Maven Wrapper exists; if not, **stop** and prompt the user to install it—do not proceed until resolved. -**Before asking questions:** Read the reference to use the exact wording and options from the template. Ask questions one-by-one in strict order (JSpecify → Error Prone/NullAway → package name for NullAway → VAVR) and add only what the user selects. Use consultative language, present trade-offs, and wait for user responses before implementing. +**Before asking questions:** Read the reference to use the exact wording and options from the template. Ask questions one-by-one in strict order (JSpecify → Enhanced Compiler Analysis (conditional) → VAVR → ArchUnit) and add only what the user selects. Use consultative language, present trade-offs, and wait for user responses before implementing. ## Reference From 6d83cdb1dfd1c07e1c59ac4fca68edd63d6fc23c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Bre=C3=B1a=20Moral?= Date: Sun, 1 Mar 2026 11:46:45 +0100 Subject: [PATCH 5/8] Adding new skills --- .cursor/rules/112-java-maven-plugins.md | 3 +- .cursor/rules/113-java-maven-documentation.md | 3 +- .../src/main/resources/skill-inventory.json | 4 +- .../src/main/resources/skills/112-skill.md | 22 + .../src/main/resources/skills/113-skill.md | 22 + .../info/jab/pml/SkillsGeneratorTest.java | 54 + skills/112-java-maven-plugins/SKILL.md | 22 + .../references/112-java-maven-plugins.md | 2124 +++++++++++++++++ skills/113-java-maven-documentation/SKILL.md | 22 + .../113-java-maven-documentation.md | 416 ++++ .../system-prompts/112-java-maven-plugins.xml | 3 +- .../113-java-maven-documentation.xml | 3 +- 12 files changed, 2693 insertions(+), 5 deletions(-) create mode 100644 skills-generator/src/main/resources/skills/112-skill.md create mode 100644 skills-generator/src/main/resources/skills/113-skill.md create mode 100644 skills/112-java-maven-plugins/SKILL.md create mode 100644 skills/112-java-maven-plugins/references/112-java-maven-plugins.md create mode 100644 skills/113-java-maven-documentation/SKILL.md create mode 100644 skills/113-java-maven-documentation/references/113-java-maven-documentation.md diff --git a/.cursor/rules/112-java-maven-plugins.md b/.cursor/rules/112-java-maven-plugins.md index 46c9b7ee..185925e6 100644 --- a/.cursor/rules/112-java-maven-plugins.md +++ b/.cursor/rules/112-java-maven-plugins.md @@ -1,11 +1,12 @@ --- name: 112-java-maven-plugins +description: Use when you need to add or configure Maven plugins in your pom.xml using a modular, step-based approach. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT --- -# Update pom.xml to add Maven plugins with modular step-based configuration +# Maven Plugins: pom.xml Configuration Best Practices ## Role diff --git a/.cursor/rules/113-java-maven-documentation.md b/.cursor/rules/113-java-maven-documentation.md index 47a216a5..7dcf5c31 100644 --- a/.cursor/rules/113-java-maven-documentation.md +++ b/.cursor/rules/113-java-maven-documentation.md @@ -1,11 +1,12 @@ --- name: 113-java-maven-documentation +description: Use when you need to create a DEVELOPER.md file for a Maven project documenting plugin goals, Maven profiles, and submodules. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT --- -# Create DEVELOPER.md with information about how to use the Maven project +# Create DEVELOPER.md for the Maven projects ## Role diff --git a/skills-generator/src/main/resources/skill-inventory.json b/skills-generator/src/main/resources/skill-inventory.json index bb2682f4..c70a392c 100644 --- a/skills-generator/src/main/resources/skill-inventory.json +++ b/skills-generator/src/main/resources/skill-inventory.json @@ -1,4 +1,6 @@ [ {"id": 110}, - {"id": 111} + {"id": 111}, + {"id": 112}, + {"id": 113} ] diff --git a/skills-generator/src/main/resources/skills/112-skill.md b/skills-generator/src/main/resources/skills/112-skill.md new file mode 100644 index 00000000..812b9482 --- /dev/null +++ b/skills-generator/src/main/resources/skills/112-skill.md @@ -0,0 +1,22 @@ +--- +name: 112-java-maven-plugins +description: Use when you need to add or configure Maven plugins in your pom.xml — including quality tools (enforcer, surefire, failsafe, jacoco, pitest, spotbugs, pmd), security scanning (OWASP), code formatting (Spotless), version management, build information tracking, and benchmarking (JMH) — through a consultative, modular step-by-step approach that only adds what you actually need. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Maven Plugins: pom.xml Configuration Best Practices + +Configure Maven plugins and profiles in pom.xml using a structured, question-driven process that preserves existing configuration. + +**Core areas:** Existing configuration analysis and preservation, Maven Wrapper verification, project assessment questions (project nature, Java version, build and quality aspects), properties configuration, Maven Enforcer, Surefire, Failsafe, HTML test reports, JaCoCo coverage, PiTest mutation testing, OWASP security scanning, SpotBugs/PMD static analysis, SonarQube/SonarCloud, Spotless code formatting, Versions plugin, Git Commit ID, Flatten plugin, JMH benchmarking, Maven Compiler, and cyclomatic complexity analysis. + +**Prerequisites:** Run `./mvnw validate` or `mvn validate` before applying any plugin recommendations. If validation fails, **stop** and ask the user to fix issues — do not proceed until resolved. + +**Multi-step scope:** Begin with Step 1 (existing configuration analysis) before any changes. Ask project assessment questions one-by-one in Step 3, then execute only the plugin steps relevant to user selections. Never remove or replace existing plugins; only add new ones that do not conflict. + +**Before applying changes:** Read the reference for detailed plugin configurations, XML templates, and constraints for each step. + +## Reference + +For detailed guidance, examples, and constraints, see [references/112-java-maven-plugins.md](references/112-java-maven-plugins.md). diff --git a/skills-generator/src/main/resources/skills/113-skill.md b/skills-generator/src/main/resources/skills/113-skill.md new file mode 100644 index 00000000..91588764 --- /dev/null +++ b/skills-generator/src/main/resources/skills/113-skill.md @@ -0,0 +1,22 @@ +--- +name: 113-java-maven-documentation +description: Use when you need to create a DEVELOPER.md file for a Maven project — combining a fixed base template with dynamic sections derived from the project pom.xml, including a Plugin Goals Reference, Maven Profiles table, and Submodules table for multi-module projects. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Create DEVELOPER.md for the Maven projects + +Generate a `DEVELOPER.md` file that combines a fixed base template with dynamic sections derived from analysing the project `pom.xml`. + +**Core areas:** Base template reproduction (verbatim), plugin goals reference (table of `./mvnw` goals per explicitly declared plugin, max 8 goals each), Maven Profiles table (profile ID, activation trigger, representative command, description), and Submodules table (multi-module projects only). + +**Prerequisites:** Read every `pom.xml` in the workspace (root and submodules) before generating any content. Only include plugins **explicitly declared** in `` or `` — never plugins inherited from parent POMs or the Maven super-POM unless redeclared. + +**Multi-step scope:** Step 1 reproduces the base template verbatim. Step 2 collects all explicitly declared plugins. Step 3 appends the Plugin Goals Reference section. Step 4 appends the Maven Profiles section (omit if no profiles). Step 5 appends the Submodules section (omit if not a multi-module project). + +**Before applying changes:** Read the reference for the base template content, plugin catalog, and detailed constraints for each step. + +## Reference + +For detailed guidance, examples, and constraints, see [references/113-java-maven-documentation.md](references/113-java-maven-documentation.md). diff --git a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java index 7cb720ba..cdf1062f 100644 --- a/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java +++ b/skills-generator/src/test/java/info/jab/pml/SkillsGeneratorTest.java @@ -9,6 +9,8 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -16,6 +18,9 @@ import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -78,6 +83,55 @@ void should_validateInventoryMatchesSkillsAndSystemPrompts() { } } + @Nested + @DisplayName("Title consistency between skill markdown and system-prompt XML") + class TitleConsistencyTests { + + private static Stream provideSkillIds() { + return SkillsInventory.skillIds(); + } + + @ParameterizedTest + @MethodSource("provideSkillIds") + @DisplayName("Skill markdown H1 title must match system-prompt XML title element") + void should_haveMatchingTitle_when_comparingSkillMdAndSystemPromptXml(String skillId) throws Exception { + String numId = numericId(skillId); + String skillMdResource = "skills/" + numId + "-skill.md"; + String markdownTitle; + try (InputStream stream = SkillsGeneratorTest.class.getClassLoader().getResourceAsStream(skillMdResource)) { + assertThat(stream).withFailMessage("Skill file not found: %s", skillMdResource).isNotNull(); + String content = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + markdownTitle = content.lines() + .filter(line -> line.startsWith("# ")) + .findFirst() + .map(line -> line.substring(2).trim()) + .orElseThrow(() -> new AssertionError("No H1 heading found in " + skillMdResource)); + } + + String xmlResource = "system-prompts/" + skillId + ".xml"; + String xmlTitle; + try (InputStream stream = SkillsGeneratorTest.class.getClassLoader().getResourceAsStream(xmlResource)) { + assertThat(stream).withFailMessage("System-prompt XML not found: %s", xmlResource).isNotNull(); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(stream); + Element metadata = (Element) doc.getElementsByTagName("metadata").item(0); + assertThat(metadata).withFailMessage("No element in %s", xmlResource).isNotNull(); + NodeList titleNodes = metadata.getElementsByTagName("title"); + assertThat(titleNodes.getLength()) + .withFailMessage("No element in <metadata> of %s", xmlResource) + .isGreaterThan(0); + xmlTitle = titleNodes.item(0).getTextContent().trim(); + } + + assertThat(markdownTitle) + .withFailMessage( + "Skill markdown H1 '%s' in %s does not match XML <title> '%s' in %s", + markdownTitle, skillMdResource, xmlTitle, xmlResource) + .isEqualTo(xmlTitle); + } + } + @Nested @DisplayName("Exception Handling Tests") class ExceptionHandlingTests { diff --git a/skills/112-java-maven-plugins/SKILL.md b/skills/112-java-maven-plugins/SKILL.md new file mode 100644 index 00000000..ac7c6287 --- /dev/null +++ b/skills/112-java-maven-plugins/SKILL.md @@ -0,0 +1,22 @@ +--- +name: 112-java-maven-plugins +description: Use when you need to add or configure Maven plugins in your pom.xml — including quality tools (enforcer, surefire, failsafe, jacoco, pitest, spotbugs, pmd), security scanning (OWASP), code formatting (Spotless), version management, build information tracking, and benchmarking (JMH) — through a consultative, modular step-by-step approach that only adds what you actually need. Part of the skills-for-java project +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Maven Plugins: pom.xml Configuration Best Practices + +Configure Maven plugins and profiles in pom.xml using a structured, question-driven process that preserves existing configuration. + +**Core areas:** Existing configuration analysis and preservation, Maven Wrapper verification, project assessment questions (project nature, Java version, build and quality aspects), properties configuration, Maven Enforcer, Surefire, Failsafe, HTML test reports, JaCoCo coverage, PiTest mutation testing, OWASP security scanning, SpotBugs/PMD static analysis, SonarQube/SonarCloud, Spotless code formatting, Versions plugin, Git Commit ID, Flatten plugin, JMH benchmarking, Maven Compiler, and cyclomatic complexity analysis. + +**Prerequisites:** Run `./mvnw validate` or `mvn validate` before applying any plugin recommendations. If validation fails, **stop** and ask the user to fix issues — do not proceed until resolved. + +**Multi-step scope:** Begin with Step 1 (existing configuration analysis) before any changes. Ask project assessment questions one-by-one in Step 3, then execute only the plugin steps relevant to user selections. Never remove or replace existing plugins; only add new ones that do not conflict. + +**Before applying changes:** Read the reference for detailed plugin configurations, XML templates, and constraints for each step. + +## Reference + +For detailed guidance, examples, and constraints, see [references/112-java-maven-plugins.md](references/112-java-maven-plugins.md). diff --git a/skills/112-java-maven-plugins/references/112-java-maven-plugins.md b/skills/112-java-maven-plugins/references/112-java-maven-plugins.md new file mode 100644 index 00000000..185925e6 --- /dev/null +++ b/skills/112-java-maven-plugins/references/112-java-maven-plugins.md @@ -0,0 +1,2124 @@ +--- +name: 112-java-maven-plugins +description: Use when you need to add or configure Maven plugins in your pom.xml using a modular, step-based approach. +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Maven Plugins: pom.xml Configuration Best Practices + +## Role + +You are a Senior software engineer with extensive experience in Java software development + +## Tone + +Treats the user as a knowledgeable partner in solving problems rather than prescribing one-size-fits-all solutions. Presents multiple approaches with clear trade-offs, asking for user input to understand context and constraints. Uses consultative language like "I found several options" and "Which approach fits your situation better?" Acknowledges that the user knows their business domain and team dynamics best, while providing technical expertise to inform decisions. + +## Goal + +This rule provides a modular, step-based approach to updating Maven pom.xml files with plugins and profiles. Each step has a single responsibility and clear dependencies on user answers, making the configuration process more maintainable and user-friendly. + +## Constraints + +Before applying This Maven plugins recommendations, ensure the project is in a valid state by running Maven validation. This helps identify any existing configuration issues that need to be resolved first. + +- **MANDATORY**: Run `./mvnw validate` or `mvn validate` before applying any Maven best practices recommendations +- **VERIFY**: Ensure all validation errors are resolved before proceeding with POM modifications +- **PREREQUISITE**: Project must compile and pass basic validation checks before optimization +- **CRITICAL SAFETY**: If validation fails, IMMEDIATELY STOP and DO NOT CONTINUE with any plugin configuration steps. Ask the user to fix ALL validation errors first before proceeding +- **ENFORCEMENT**: Never proceed to Step 1 or any subsequent steps if `mvn validate` or `./mvnw validate` command fails or returns errors + +## Steps + +### Step 1: MANDATORY: Existing Configuration Preservation and Analysis + +**CRITICAL PRESERVATION RULES**: Before making ANY changes to pom.xml: + +1. **SCAN existing pom.xml** to identify current plugins, properties, and profiles +2. **IDENTIFY conflicts** between existing configuration and planned additions +3. **PRESERVE all existing plugins** - never remove or replace existing plugin configurations +4. **ASK USER about conflicts** before modifying any existing plugin or property +5. **SKIP additions** if they would conflict with existing configuration unless user explicitly requests override +6. **DOCUMENT what will be added** vs what already exists + +**Implementation Steps**: +1. Read and analyze the current pom.xml file +2. List all existing plugins in build/plugins section +3. List all existing properties in properties section +4. List all existing profiles in profiles section +5. Compare against planned additions from subsequent steps +6. Present analysis to user: "Found existing plugins: [list]. Will only add: [list of new plugins]" +7. **WAIT for user confirmation** before proceeding + +**If conflicts detected**: +- Plugin already exists: Ask "Plugin X already exists. Skip adding duplicate? (y/n)" +- Property already exists: Ask "Property X already exists with value Y. Keep existing? (y/n)" +- Profile already exists: Ask "Profile X already exists. Skip adding duplicate? (y/n)" + +**Only proceed to Step 2 after completing this analysis and getting user confirmation.** + +### Step 2: Maven Wrapper Check and Installation + +**First, check for Maven Wrapper files** in the project root +- Look for `mvnw` (Unix/Mac) and `mvnw.cmd` (Windows) files +- Check for `.mvn/wrapper/` directory with `maven-wrapper.properties` + +**If Maven Wrapper is NOT present:** + +**STOP HERE** and ask the user: "I notice this project doesn't have Maven Wrapper configured. +The Maven Wrapper ensures everyone uses the same Maven version, improving build consistency across different environments. +Would you like me to install it? (y/n)" + +**WAIT for the user's response. Do NOT proceed to any other questions or steps until this is resolved.** + +if the user says "y", then install the Maven Wrapper. + +```bash +mvn wrapper:wrapper +``` + +### Step 3: Project Assessment Questions + +**IMPORTANT**: Ask these questions to understand the project needs before making any changes to the pom.xml. Based on the answers, you will conditionally add only relevant dependencies and plugins. + +```markdown +**Question 1**: What type of Java project is this? + +Options: +- Java Library (for publishing to Maven Central/Nexus) +- Java CLI Application (command-line tool) +- Java Microservice (Web service/REST API/Modular monolith) +- Serverless (AWS Lambdas, Azure Functions) +- Java POC (Proof of Concept) +- Other (specify) + +--- + +**Question 2**: Which Java version does your project target? + +Options: + +- Java 17 (LTS - recommended for new projects) +- Java 21 (LTS - latest LTS version) +- Java 25 (LTS - latest LTS version) +- Other (specify version) + +--- + +**Question 3**: What build and quality aspects are important for your project? + +Options: +- Format source code (Spotless) +- Maven Enforcer +- Unit Testing (Surefire) +- Unit Testing Reports (Surefire Reports) +- Integration testing (Failsafe) +- Code coverage reporting (JaCoCo) +- Mutation testing (PiTest) +- Security vulnerability scanning (OWASP) +- Security static code analysis (SpotBugs, PMD) +- Sonar +- Version management +- JMH (Java Microbenchmark Harness) +- Maven Compiler +- Cyclomatic Complexity + +**Note**: When "Cyclomatic Complexity" is selected, Step 20 will create a PMD ruleset file and profile. The ruleset location depends on project structure: `src/main/pmd/pmd-cyclomatic-complexity.xml` (mono-module) or `pmd/pmd-cyclomatic-complexity.xml` (multi-module). + +--- + +**Question 4**: What is your target coverage threshold? + +Options: +- 70% (moderate) +- 80% (recommended) +- 90% (high) +- Custom percentage (specify) + +**Note**: This question is only asked if "Code coverage reporting (JaCoCo)" was selected in question 3. + +--- + +**Question 5**: Do you want to configure Sonar/SonarCloud integration?** (y/n) + +**Note**: This question is only asked if "Static code analysis (SpotBugs, Sonar)" was selected in question 3. + +**If yes, please provide the following information:** + +--- + +**Question 5.1**: What is your Sonar organization identifier? + +- For SonarCloud: This is typically your GitHub username or organization name +- For SonarQube: This is your organization key as configured in SonarQube +- Example: `my-github-user` or `my-company-org` + +--- + +**Question 5.2**: What is your Sonar project key? + +- For SonarCloud: Usually in format `GITHUB_USER_REPOSITORY_NAME` (e.g., `john-doe_my-java-project`) +- For SonarQube: Custom project key as defined in your SonarQube instance +- Must be unique within your Sonar organization +- Example: `john-doe_awesome-java-lib` + +--- + +**Question 5.3**: What is your Sonar project display name? + +- Human-readable name for your project as it appears in Sonar dashboard +- Can contain spaces and special characters +- Example: `Awesome Java Library` or `My Microservice API` + +--- + +**Question 5.4**: Which Sonar service are you using? (conditional) + +**Note**: This question is only asked if Sonar configuration was enabled in question 5. + +Options: +- SonarCloud (https://sonarcloud.io) - recommended for open source projects +- SonarQube Server (specify your server URL) + +**If SonarQube Server**: Please provide your SonarQube server URL (e.g., `https://sonar.mycompany.com`) + +--- + +``` + +#### Step Constraints + +- **DEPENDENCIES**: Requires completion of Step 1 (existing configuration analysis) +- **CRITICAL**: You MUST ask the exact questions from the following template in strict order before making any changes to understand the project needs +- Based on the answers, you will conditionally execute only relevant subsequent steps +- **MUST** complete Step 1 analysis before asking any questions +- **MUST** read template files fresh using file_search and read_file tools before asking any questions +- **MUST NOT** use cached or remembered questions from previous interactions +- **MUST** ask questions ONE BY ONE in the exact order specified in the template +- **MUST** WAIT for user response to each question before proceeding to the next +- **MUST** use the EXACT wording from the template questions +- **MUST** present the EXACT options listed in the template +- **MUST** include recommendations when specified in the template +- **MUST NOT** ask all questions simultaneously +- **MUST NOT** assume answers or provide defaults +- **MUST NOT** skip questions or change their order +- **MUST** follow question sequence: Project Nature → Java Version → Build and Quality Aspects → Coverage Threshold (conditional) → Sonar Configuration (conditional) → Sonar Host Configuration (conditional) +- **MUST** confirm understanding of user selections before proceeding to Step 4 +- **MUST NOT** ask about Coverage Threshold if user did not select jacoco coverage + +### Step 4: Properties Configuration + +**Purpose**: Configure Maven properties based on user selections from Step 3. + +**Dependencies**: Requires completion of Step 3 questions. + +Use the following template to add properties to the pom.xml file: + +Build properties incrementally based on user's actual needs and project requirements. This template provides a comprehensive, conversational approach to configuring Maven properties. + +**CRITICAL PRESERVATION RULE**: Only ADD properties that don't already exist. Never REPLACE or REMOVE existing properties. + +**BEFORE adding any property, check if it already exists in the `<properties>` section:** + +1. **Scan existing properties** in the pom.xml +2. **Compare with planned additions** from the templates below +3. **Ask user for conflicts**: "Property X already exists with value Y. Keep existing value? (y/n)" +4. **Skip conflicting properties** unless user explicitly requests override +5. **Only add NEW properties** that don't already exist + +Start with essential build properties that every project needs (use the Java version selected in the initial questions): + +**Check first if these properties already exist. Only add missing ones:** + +```xml +<properties> + <java.version>[USER_SELECTED_JAVA_VERSION]</java.version> + <maven.version>3.9.10</maven.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> +</properties> +``` + +**Dependency Version Properties:** (Conditional) + +**Based on dependency selections**, add relevant version properties: + +**Quality and Analysis Properties:** (Conditional) + +**Ask**: "Do you want to configure quality thresholds for code coverage and analysis? (y/n)" + +**If yes, ask for specific thresholds**: +- "What minimum code coverage percentage do you want? (default: 80%)" +- "What minimum mutation testing score do you want? (default: 70%)" + +**Add based on answers**: +```xml +<!-- Quality thresholds --> +<coverage.level>[USER_SPECIFIED_COVERAGE]</coverage.level> +<mutation.level>[USER_SPECIFIED_MUTATION]</mutation.level> +``` + +**Additional Plugin Version Properties:** (Feature-Based) + +**Only add plugin version properties for selected features**: + +**If Maven enforcer selected**: +**If yes, add**: +```xml +<maven-plugin-enforcer.version>3.5.0</maven-plugin-enforcer.version> +``` + +**If Format source code selected**: +```xml +<maven-plugin-spotless.version>2.44.5</maven-plugin-spotless.version> +``` + +**If Unit testing selected**: +**If yes, add**: +```xml +<maven-plugin-surefire.version>3.5.3</maven-plugin-surefire.version> +``` + +**If Unit testing Reporting selected**: +**If yes, add**: +```xml +<maven-plugin-surefire.version>3.5.3</maven-plugin-surefire.version> +<maven-plugin-jxr.version>3.6.0</maven-plugin-jxr.version> +``` + +**If Integration Testing selected**: +```xml +<maven-plugin-failsafe.version>3.5.3</maven-plugin-failsafe.version> +``` + +**If Code Coverage selected**: +```xml +<maven-plugin-jacoco.version>0.8.13</maven-plugin-jacoco.version> +``` + +**If Mutation Testing selected**: +```xml +<maven-plugin-pitest.version>1.19.4</maven-plugin-pitest.version> +<maven-plugin-pitest-junit5.version>1.2.3</maven-plugin-pitest-junit5.version> +``` + +**If Security Scanning selected**: +```xml +<maven-plugin-dependency-check.version>12.1.1</maven-plugin-dependency-check.version> +``` + +**If Static Analysis selected**: +```xml +<maven-plugin-spotbugs.version>4.9.3.0</maven-plugin-spotbugs.version> +<maven-plugin-pmd.version>3.26.0</maven-plugin-pmd.version> +<maven-plugin-sonar.version>4.0.0.4121</maven-plugin-sonar.version> +``` + +**If Version Management selected**: +```xml +<maven-plugin-versions.version>2.18.0</maven-plugin-versions.version> +``` + +**If Build Info selected**: +```xml +<maven-plugin-git-commit-id.version>4.9.10</maven-plugin-git-commit-id.version> +``` + +**If Library Publishing selected**: +```xml +<maven-plugin-flatten.version>1.7.0</maven-plugin-flatten.version> +``` + +**If Site Generation selected**: +```xml +<maven-plugin-site.version>3.20.0</maven-plugin-site.version> +<maven-plugin-project-info-reports.version>3.7.0</maven-plugin-project-info-reports.version> +``` + +**If SonarQube Integration selected**: +```xml +<maven-plugin-sonar.version>4.0.0.4121</maven-plugin-sonar.version> +``` + +**If JMH selected**: +```xml +<jmh.version>1.37</jmh.version> +<maven-plugin-build-helper.version>3.4.0</maven-plugin-build-helper.version> +<maven-plugin-shade.version>3.5.1</maven-plugin-shade.version> +<maven-plugin-compiler.version>3.13.0</maven-plugin-compiler.version> +``` + +**If Maven Compiler selected**: +```xml +<maven-plugin-compiler.version>3.13.0</maven-plugin-compiler.version> +``` + +**If Cyclomatic complexity selected**: +```xml +<maven-plugin-pmd.version>3.28.0</maven-plugin-pmd.version> +<maven-plugin-jxr.version>3.6.0</maven-plugin-jxr.version> +``` + +The final `<properties>` section will look like this (example with common selections): + +```xml +<properties> + <java.version>24</java.version> + <maven.version>3.9.10</maven.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + + <!-- Dependency versions (based on selections) --> + <jspecify.version>1.0.0</jspecify.version> + + <!-- Feature-specific plugin versions (based on selections) --> + <maven-plugin-surefire.version>3.5.3</maven-plugin-surefire.version> + <maven-plugin-enforcer.version>3.5.0</maven-plugin-enforcer.version> + <maven-plugin-jacoco.version>0.8.13</maven-plugin-jacoco.version> + <maven-plugin-pitest.version>1.19.4</maven-plugin-pitest.version> + <maven-plugin-pitest-junit5.version>1.2.3</maven-plugin-pitest-junit5.version> + <maven-plugin-spotbugs.version>4.9.3.0</maven-plugin-spotbugs.version> + <maven-plugin-compiler.version>3.13.0</maven-plugin-compiler.version> + <!-- If Cyclomatic complexity: maven-plugin-pmd.version, maven-plugin-jxr.version --> + + <!-- Quality thresholds (if configured) --> + <coverage.level>80</coverage.level> + <mutation.level>70</mutation.level> +</properties> +``` + + +**Implementation Strategy:** +1. **Core Properties**: Always add Java version, Maven version, and encoding properties +2. **Plugin Version Properties**: Add version properties ONLY for plugins that were selected in Step 3 +3. **Quality Properties**: Add coverage and threshold properties if quality features selected + +**Property Naming Convention**: Use `maven-plugin-*` format for consistency (e.g., `maven-plugin-compiler.version`) + +#### Step Constraints + +- **MUST** use `maven-plugin-*` format for property naming (e.g., `maven-plugin-compiler.version`, NOT `maven-compiler-plugin.version`) +- **MUST** add only properties for features actually selected in Step 3 +- **MUST NOT** add properties for unselected features +- **MUST** read template files fresh using file_search and read_file tools +- **MUST** follow template specifications exactly +- **MUST** validate Java version matches user selection from Step 3 + +### Step 5: Maven Enforcer Plugin Configuration + +**Purpose**: Configure maven-enforcer-plugin to enforce dependency convergence, prevent circular dependencies, and ensure consistent Maven/Java versions. + +**Dependencies**: Requires completion of Steps 3 and 4. + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing configuration. + +## Pre-Implementation Check + +**BEFORE adding maven-enforcer-plugin, check if it already exists in the pom.xml:** + +If maven-enforcer-plugin already exists: Ask user "maven-enforcer-plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +## Maven Enforcer Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<plugin> +<groupId>org.apache.maven.plugins</groupId> +<artifactId>maven-enforcer-plugin</artifactId> +<version>${maven-plugin-enforcer.version}</version> +<dependencies> + <dependency> + <groupId>org.codehaus.mojo</groupId> + <artifactId>extra-enforcer-rules</artifactId> + <version>${extra-enforcer-rules.version}</version> + </dependency> +</dependencies> +<executions> + <execution> + <id>enforce</id> + <configuration> + <rules> + <banCircularDependencies/> + <dependencyConvergence /> + <banDuplicatePomDependencyVersions /> + <requireMavenVersion> + <version>${maven.version}</version> + </requireMavenVersion> + <requireJavaVersion> + <version>${java.version}</version> + </requireJavaVersion> + <bannedDependencies> + <excludes> + <exclude>org.projectlombok:lombok</exclude> + </excludes> + </bannedDependencies> + </rules> + <fail>true</fail> + </configuration> + <goals> + <goal>enforce</goal> + </goals> + </execution> +</executions> +</plugin> +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Validate plugin configuration +./mvnw validate +``` + + +#### Step Constraints + +- **MUST** include `extra-enforcer-rules` dependency and all specified rules +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration + +### Step 6: Maven Surefire Plugin Configuration + +**Purpose**: Configure maven-surefire-plugin for unit testing with proper includes/excludes and failure handling. + +**Dependencies**: Only execute if user selected "Unit Testing (Surefire)" in Step 3. Requires completion of Steps 3, 4, and 5. + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing configuration. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Unit Testing (Surefire)" in Step 3. + +## Pre-Implementation Check + +**BEFORE adding maven-surefire-plugin, check if it already exists in the pom.xml:** + +If maven-surefire-plugin already exists: Ask user "maven-surefire-plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +## Maven Surefire Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<plugin> +<groupId>org.apache.maven.plugins</groupId> +<artifactId>maven-surefire-plugin</artifactId> +<version>${maven-plugin-surefire.version}</version> +<configuration> + <skipAfterFailureCount>1</skipAfterFailureCount> + <includes> + <include>**/*Test.java</include> + </includes> + <excludes> + <exclude>**/*IT.java</exclude> + </excludes> +</configuration> +</plugin> +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Run unit tests +./mvnw test +``` + + +#### Step Constraints + +- **MUST** only add surefire plugin if "Unit Testing (Surefire)" was selected in Step 3 +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration +- **MUST** configure proper includes/excludes for test file patterns +- **MUST** skip this step entirely if unit testing was not selected + +### Step 7: Maven Failsafe Plugin Configuration + +**Purpose**: Configure maven-failsafe-plugin for integration testing with proper file patterns and execution phases. + +**Dependencies**: Only execute if user selected "Integration testing (Failsafe)" in Step 3. Requires completion of Steps 3, 4, and 5. + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing configuration. + +## Pre-Implementation Check + +**BEFORE adding maven-failsafe-plugin, check if it already exists in the pom.xml:** + +If maven-failsafe-plugin already exists: Ask user "maven-failsafe-plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Integration testing (Failsafe)" in Step 3. + +## Maven Failsafe Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<plugin> +<groupId>org.apache.maven.plugins</groupId> +<artifactId>maven-failsafe-plugin</artifactId> +<version>${maven-plugin-failsafe.version}</version> +<configuration> + <includes> + <include>**/*IT.java</include> + </includes> + <excludes> + <exclude>**/*Test.java</exclude> + </excludes> +</configuration> +<executions> + <execution> + <goals> + <goal>integration-test</goal> + </goals> + </execution> +</executions> +</plugin> +``` + +## Implementation Guidelines + +1. **Verify file patterns**: Ensure `*IT.java` files are included and `*Test.java` files are excluded +2. **Test execution**: Integration tests run during `verify` phase +3. **Example integration test**: Create a sample `*IT.java` file to verify configuration + +## Usage Examples + +```bash +# Run only unit tests +./mvnw test + +# Run both unit and integration tests +./mvnw verify + +# Run only integration tests +./mvnw failsafe:integration-test +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Run tests to verify configuration +./mvnw clean verify +``` + + +#### Step Constraints + +- **MUST** only add failsafe plugin if integration testing was selected in Step 3 +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration +- **MUST** configure proper includes/excludes for integration test file patterns +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if integration testing was not selected + +### Step 8: HTML Test Reports Configuration + +**Purpose**: Configure HTML test reports generation with maven-surefire-report-plugin and maven-jxr-plugin. + +**Dependencies**: Only execute if user selected "HTML test reports" in Step 3. Requires completion of Steps 3, 4, and 5. + +**CRITICAL PRESERVATION RULE**: Only ADD reporting section if it doesn't already exist. Never REPLACE or REMOVE existing configuration. + +## Pre-Implementation Check + +**BEFORE adding reporting section, check if it already exists in the pom.xml:** + +If `<reporting>` section already exists: Ask user "Reporting section already exists. Do you want to enhance it with test reporting plugins? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing reporting plugins only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "HTML test reports" in Step 3. + +## HTML Test Reports Configuration + +**ADD this `<reporting>` section at the same level as `<build>` ONLY if it doesn't already exist:** + +```xml +<reporting> +<plugins> + <!-- Generates HTML test reports --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-report-plugin</artifactId> + <version>${maven-plugin-surefire.version}</version> + <configuration> + <outputName>junit-report</outputName> + <showSuccess>true</showSuccess> + </configuration> + </plugin> + + <!-- Adds links to source code in reports --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jxr-plugin</artifactId> + <version>${maven-plugin-jxr.version}</version> + </plugin> +</plugins> +</reporting> +``` + +## Implementation Guidelines + +1. **Add reporting section** only if HTML reports were requested +2. **Verify plugin versions**: Ensure version properties are defined in Step 3 +3. **Generate reports**: Reports are generated during `site` phase +4. **View reports**: HTML reports will be available in `target/site/` directory + +## Usage Examples + +```bash +# Generate test reports +./mvnw site + +# View reports in browser +open target/site/junit-report.html + +# Or serve reports locally +./mvnw site:run +``` + +## Validation + +After adding this reporting configuration, verify it: + +```bash +# Generate reports to verify configuration +./mvnw clean test site +``` + + +#### Step Constraints + +- **MUST** only add reporting section if HTML reports were selected in Step 3 +- **MUST** check if reporting section already exists before adding +- **MUST** ask user permission before modifying existing reporting configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if HTML test reports were not selected + +### Step 9: JaCoCo Code Coverage Profile Configuration + +**Purpose**: Configure JaCoCo code coverage profile to analyze and enforce coverage thresholds with detailed reporting. + +**Dependencies**: Only execute if user selected "Code coverage reporting (JaCoCo)" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this profile if it doesn't already exist. Never REPLACE or REMOVE existing profiles. + +## Pre-Implementation Check + +**BEFORE adding JaCoCo profile, check if it already exists in the pom.xml:** + +If `<profiles>` section with `jacoco` profile already exists: Ask user "JaCoCo profile already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Code coverage reporting (JaCoCo)" in Step 3. + +## JaCoCo Profile Configuration + +**ADD this profile to the `<profiles>` section in pom.xml ONLY if it doesn't already exist:** + +```xml +<profile> +<id>jacoco</id> +<activation> + <activeByDefault>false</activeByDefault> +</activation> +<build> + <plugins> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>${maven-plugin-jacoco.version}</version> + <executions> + <execution> + <id>prepare-agent</id> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>report</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + <execution> + <id>check</id> + <phase>verify</phase> + <goals> + <goal>check</goal> + </goals> + <configuration> + <rules> + <rule> + <element>BUNDLE</element> + <limits> + <limit> + <counter>LINE</counter> + <value>COVEREDRATIO</value> + <minimum>${coverage.level}%</minimum> + </limit> + <limit> + <counter>BRANCH</counter> + <value>COVEREDRATIO</value> + <minimum>${coverage.level}%</minimum> + </limit> + <limit> + <counter>METHOD</counter> + <value>COVEREDRATIO</value> + <minimum>${coverage.level}%</minimum> + </limit> + <limit> + <counter>CLASS</counter> + <value>COVEREDRATIO</value> + <minimum>${coverage.level}%</minimum> + </limit> + <limit> + <counter>INSTRUCTION</counter> + <value>COVEREDRATIO</value> + <minimum>${coverage.level}%</minimum> + </limit> + <limit> + <counter>COMPLEXITY</counter> + <value>COVEREDRATIO</value> + <minimum>${coverage.level}%</minimum> + </limit> + </limits> + </rule> + </rules> + </configuration> + </execution> + </executions> + </plugin> + </plugins> +</build> +</profile> +``` + +## Usage Examples + +```bash +# Run tests with coverage +./mvnw clean verify -Pjacoco + +# View coverage reports +open target/site/jacoco/index.html +``` + +## Validation + +After adding this profile, verify the configuration: + +```bash +# Test JaCoCo profile +./mvnw clean verify -Pjacoco +``` + + +#### Step Constraints + +- **MUST** only add JaCoCo profile if code coverage was selected in Step 3 +- **MUST** check if profile already exists before adding +- **MUST** ask user permission before modifying existing profile configuration +- **MUST** use coverage threshold values from Step 3 +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if code coverage was not selected + +### Step 10: PiTest Mutation Testing Profile Configuration + +**Purpose**: Configure PiTest mutation testing profile to analyze test quality by introducing mutations and verifying test detection. + +**Dependencies**: Only execute if user selected "Mutation testing (PiTest)" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this profile if it doesn't already exist. Never REPLACE or REMOVE existing profiles. + +## Pre-Implementation Check + +**BEFORE adding PiTest profile, check if it already exists in the pom.xml:** + +If `<profiles>` section with `pitest` profile already exists: Ask user "PiTest profile already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Mutation testing (PiTest)" in Step 3. + +## PiTest Profile Configuration + +**ADD this profile to the `<profiles>` section in pom.xml ONLY if it doesn't already exist:** + +```xml +<profile> +<id>pitest</id> +<activation> + <activeByDefault>false</activeByDefault> +</activation> +<build> + <plugins> + <plugin> + <groupId>org.pitest</groupId> + <artifactId>pitest-maven</artifactId> + <version>${maven-plugin-pitest.version}</version> + <configuration> + <targetClasses> + <param>REPLACE_WITH_ACTUAL_PACKAGE.*</param> + </targetClasses> + <targetTests> + <param>REPLACE_WITH_ACTUAL_PACKAGE.*</param> + </targetTests> + <outputFormats> + <outputFormat>HTML</outputFormat> + <outputFormat>XML</outputFormat> + </outputFormats> + <mutationThreshold>${coverage.level}</mutationThreshold> + <coverageThreshold>${coverage.level}</coverageThreshold> + <timestampedReports>false</timestampedReports> + <verbose>false</verbose> + </configuration> + <dependencies> + <dependency> + <groupId>org.pitest</groupId> + <artifactId>pitest-junit5-plugin</artifactId> + <version>${maven-plugin-pitest-junit5.version}</version> + </dependency> + </dependencies> + <executions> + <execution> + <id>pitest-mutation-testing</id> + <goals> + <goal>mutationCoverage</goal> + </goals> + <phase>verify</phase> + </execution> + </executions> + </plugin> + </plugins> +</build> +</profile> +``` + +## Implementation Guidelines + +1. **Update package names**: Replace `REPLACE_WITH_ACTUAL_PACKAGE` with the project's actual base package +2. **Configure thresholds**: Use coverage threshold values from Step 2 + +## Usage Examples + +```bash +# Run mutation testing +./mvnw clean verify -Ppitest + +# View mutation test reports +open target/pit-reports/index.html +``` + +## Validation + +After adding this profile, verify the configuration: + +```bash +# Test PiTest profile +./mvnw clean verify -Ppitest +``` + + +#### Step Constraints + +- **MUST** only add PiTest profile if mutation testing was selected in Step 3 +- **MUST** check if profile already exists before adding +- **MUST** ask user permission before modifying existing profile configuration +- **MUST** update targetClasses and targetTests to match actual project structure +- **MUST** use coverage threshold values from Step 3 +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if mutation testing was not selected + +### Step 11: Security Vulnerability Scanning Profile Configuration + +**Purpose**: Configure OWASP Dependency Check profile to scan dependencies for known security vulnerabilities. + +**Dependencies**: Only execute if user selected "Security vulnerability scanning (OWASP)" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this profile if it doesn't already exist. Never REPLACE or REMOVE existing profiles. + +## Pre-Implementation Check + +**BEFORE adding Security profile, check if it already exists in the pom.xml:** + +If `<profiles>` section with `security` profile already exists: Ask user "Security profile already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Security vulnerability scanning (OWASP)" in Step 3. + +## Security Profile Configuration + +**ADD this profile to the `<profiles>` section in pom.xml ONLY if it doesn't already exist:** + +```xml +<profile> +<id>security</id> +<activation> + <activeByDefault>false</activeByDefault> +</activation> +<build> + <plugins> + <plugin> + <groupId>org.owasp</groupId> + <artifactId>dependency-check-maven</artifactId> + <version>${maven-plugin-dependency-check.version}</version> + <configuration> + <outputDirectory>${project.build.directory}/dependency-check</outputDirectory> + <format>ALL</format> + <failBuildOnCVSS>7</failBuildOnCVSS> + <skipProvidedScope>false</skipProvidedScope> + <skipRuntimeScope>false</skipRuntimeScope> + <skipSystemScope>false</skipSystemScope> + <skipTestScope>false</skipTestScope> + <!-- Performance and reliability improvements --> + <nvdApiDelay>4000</nvdApiDelay> + <nvdMaxRetryCount>3</nvdMaxRetryCount> + <nvdValidForHours>24</nvdValidForHours> + <!-- Skip analyzers that might cause issues --> + <nodeAnalyzerEnabled>false</nodeAnalyzerEnabled> + <retireJsAnalyzerEnabled>false</retireJsAnalyzerEnabled> + </configuration> + <executions> + <execution> + <id>dependency-check</id> + <goals> + <goal>check</goal> + </goals> + <phase>verify</phase> + </execution> + </executions> + </plugin> + </plugins> +</build> +</profile> +``` + +## Usage Examples + +```bash +# Run security scan +./mvnw clean verify -Psecurity + +# View security reports +open target/dependency-check/dependency-check-report.html +``` + +## Validation + +After adding this profile, verify the configuration: + +```bash +# Test Security profile +./mvnw clean verify -Psecurity +``` + + +#### Step Constraints + +- **MUST** only add Security profile if security scanning was selected in Step 3 +- **MUST** check if profile already exists before adding +- **MUST** ask user permission before modifying existing profile configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if security scanning was not selected + +### Step 12: Static Code Analysis Profile Configuration + +**Purpose**: Configure SpotBugs and PMD static analysis profile to detect potential bugs and code quality issues. + +**Dependencies**: Only execute if user selected "Static code analysis (SpotBugs, PMD)" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this profile if it doesn't already exist. Never REPLACE or REMOVE existing profiles. + +## Pre-Implementation Check + +**BEFORE adding Static Analysis profile, check if it already exists in the pom.xml:** + +If `<profiles>` section with `find-bugs` profile already exists: Ask user "Static analysis profile already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Static code analysis (SpotBugs, PMD)" in Step 3. + +## Static Analysis Profile Configuration + +**ADD this profile to the `<profiles>` section in pom.xml ONLY if it doesn't already exist:** + +```xml +<profile> +<id>find-bugs</id> +<activation> + <activeByDefault>false</activeByDefault> +</activation> +<build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>${maven-plugin-pmd.version}</version> + </plugin> + <plugin> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-maven-plugin</artifactId> + <version>${maven-plugin-spotbugs.version}</version> + <configuration> + <effort>Max</effort> + <threshold>Low</threshold> + <failOnError>true</failOnError> + </configuration> + </plugin> + </plugins> +</build> +<reporting> + <plugins> + <!-- SpotBugs reporting for Maven site --> + <plugin> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-maven-plugin</artifactId> + <version>${maven-plugin-spotbugs.version}</version> + <configuration> + <effort>Max</effort> + <threshold>Low</threshold> + <includeFilterFile>src/main/spotbugs/spotbugs-include.xml</includeFilterFile> + <excludeFilterFile>src/main/spotbugs/spotbugs-exclude.xml</excludeFilterFile> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>${maven-plugin-pmd.version}</version> + </plugin> + </plugins> +</reporting> +</profile> +``` + +## Usage Examples + +```bash +# Run static analysis +./mvnw clean verify -Pfind-bugs + +# Generate reports +./mvnw site -Pfind-bugs + +# View reports +open target/site/spotbugs.html +open target/site/pmd.html +``` + +## Validation + +After adding this profile, verify the configuration: + +```bash +# Test Static Analysis profile +./mvnw clean verify -Pfind-bugs +``` + + +#### Step Constraints + +- **MUST** only add Static Analysis profile if static analysis was selected in Step 3 +- **MUST** check if profile already exists before adding +- **MUST** ask user permission before modifying existing profile configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if static analysis was not selected + +### Step 13: SonarQube/SonarCloud Profile Configuration + +**Purpose**: Configure SonarQube/SonarCloud profile for comprehensive code quality analysis integration. + +**Dependencies**: Only execute if user selected "Sonar" in Step 3 and provided Sonar configuration. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this profile if it doesn't already exist. Never REPLACE or REMOVE existing profiles. + +## Pre-Implementation Check + +**BEFORE adding Sonar profile, check if it already exists in the pom.xml:** + +If `<profiles>` section with `sonar` profile already exists: Ask user "Sonar profile already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Sonar" in Step 3 and provided Sonar configuration. + +## Sonar Profile Configuration + +**ADD this profile to the `<profiles>` section in pom.xml ONLY if it doesn't already exist:** + +```xml +<profile> +<id>sonar</id> +<activation> + <activeByDefault>false</activeByDefault> +</activation> +<properties> + <!-- SonarCloud configuration --> + <sonar.host.url>REPLACE_WITH_SONAR_HOST_URL</sonar.host.url> + <sonar.organization>REPLACE_WITH_SONAR_ORGANIZATION</sonar.organization> + <sonar.projectKey>REPLACE_WITH_SONAR_PROJECT_KEY</sonar.projectKey> + <sonar.projectName>REPLACE_WITH_SONAR_PROJECT_NAME</sonar.projectName> + <sonar.projectVersion>${project.version}</sonar.projectVersion> + <sonar.sources>src/main/java</sonar.sources> + <sonar.tests>src/test/java</sonar.tests> + <sonar.java.binaries>target/classes</sonar.java.binaries> + <sonar.java.test.binaries>target/test-classes</sonar.java.test.binaries> + <sonar.jacoco.reportPath>target/jacoco.exec</sonar.jacoco.reportPath> + <sonar.junit.reportPaths>target/surefire-reports</sonar.junit.reportPaths> + <sonar.coverage.exclusions>**/*Test.java,**/*IT.java</sonar.coverage.exclusions> + <sonar.java.source>${java.version}</sonar.java.source> +</properties> +<build> + <plugins> + <plugin> + <groupId>org.sonarsource.scanner.maven</groupId> + <artifactId>sonar-maven-plugin</artifactId> + <version>${maven-plugin-sonar.version}</version> + </plugin> + </plugins> +</build> +</profile> +``` + +## Implementation Guidelines + +1. **Replace Sonar placeholders** with actual values from Step 3: +- `REPLACE_WITH_SONAR_HOST_URL` +- `REPLACE_WITH_SONAR_ORGANIZATION` +- `REPLACE_WITH_SONAR_PROJECT_KEY` +- `REPLACE_WITH_SONAR_PROJECT_NAME` + +## Usage Examples + +```bash +# Run Sonar analysis (requires token) +./mvnw clean verify sonar:sonar -Psonar -Dsonar.login=YOUR_TOKEN + +# For SonarCloud with GitHub Actions +./mvnw clean verify sonar:sonar -Psonar -Dsonar.login=$SONAR_TOKEN + +# Combined with JaCoCo +./mvnw clean verify sonar:sonar -Pjacoco,sonar -Dsonar.login=$SONAR_TOKEN +``` + +## Validation + +After adding this profile, verify the configuration: + +```bash +# Validate Sonar profile (requires token) +./mvnw clean verify sonar:sonar -Psonar -Dsonar.login=YOUR_TOKEN +``` + + +#### Step Constraints + +- **MUST** only add Sonar profile if Sonar integration was selected in Step 3 +- **MUST** check if profile already exists before adding +- **MUST** ask user permission before modifying existing profile configuration +- **MUST** configure Sonar properties with actual values from Step 3 +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if Sonar was not selected + +### Step 14: Spotless Code Formatting Plugin Configuration + +**Purpose**: Configure Spotless Maven plugin to automatically format and enforce code style consistency. + +**Dependencies**: Only execute if user selected "Format source code (Spotless)" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing plugins. + +## Pre-Implementation Check + +**BEFORE adding Spotless plugin, check if it already exists in the pom.xml:** + +If spotless-maven-plugin already exists: Ask user "Spotless plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Format source code (Spotless)" in Step 3. + +## Spotless Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<plugin> +<groupId>com.diffplug.spotless</groupId> +<artifactId>spotless-maven-plugin</artifactId> +<version>${maven-plugin-spotless.version}</version> +<configuration> + <encoding>UTF-8</encoding> + <java> + <removeUnusedImports /> + <importOrder> + <order>,\#</order> + </importOrder> + <endWithNewline /> + <trimTrailingWhitespace /> + <indent> + <spaces>true</spaces> + <spacesPerTab>4</spacesPerTab> + </indent> + </java> +</configuration> +<executions> + <execution> + <goals> + <goal>check</goal> + </goals> + <phase>process-sources</phase> + </execution> +</executions> +</plugin> +``` + +## Usage Examples + +```bash +# Check code formatting +./mvnw spotless:check + +# Apply code formatting +./mvnw spotless:apply + +# Integration with build +./mvnw clean compile # Will fail if formatting violations exist +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Test Spotless configuration +./mvnw spotless:check +``` + + +#### Step Constraints + +- **MUST** only add Spotless plugin if code formatting was selected in Step 3 +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if code formatting was not selected + +### Step 15: Versions Maven Plugin Configuration + +**Purpose**: Configure Versions Maven plugin to help manage and update dependency and plugin versions systematically. + +**Dependencies**: Only execute if user selected "Version management" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing plugins. + +## Pre-Implementation Check + +**BEFORE adding Versions plugin, check if it already exists in the pom.xml:** + +If versions-maven-plugin already exists: Ask user "Versions plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Version management" in Step 3. + +## Versions Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<plugin> +<groupId>org.codehaus.mojo</groupId> +<artifactId>versions-maven-plugin</artifactId> +<version>${maven-plugin-versions.version}</version> +<configuration> + <allowSnapshots>false</allowSnapshots> +</configuration> +</plugin> +``` + +## Usage Examples + +```bash +# Check for dependency updates +./mvnw versions:display-dependency-updates + +# Check for plugin updates +./mvnw versions:display-plugin-updates + +# Check for property updates +./mvnw versions:display-property-updates + +# Update to next snapshot versions +./mvnw versions:set -DnextSnapshot=true + +# Update specific dependency +./mvnw versions:use-latest-versions -Dincludes=org.junit.jupiter:* +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Test Versions plugin configuration +./mvnw versions:display-plugin-updates +``` + + +#### Step Constraints + +- **MUST** only add Versions plugin if version management was selected in Step 3 +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if version management was not selected + +### Step 16: Git Commit ID Plugin Configuration + +**Purpose**: Configure Git Commit ID plugin to include Git commit information in the build artifacts for traceability. + +**Dependencies**: Only execute if user selected "Build information tracking" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing plugins. + +## Pre-Implementation Check + +**BEFORE adding Git Commit ID plugin, check if it already exists in the pom.xml:** + +If git-commit-id-plugin already exists: Ask user "Git Commit ID plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Build information tracking" in Step 3. + +## Git Commit ID Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<plugin> +<groupId>pl.project13.maven</groupId> +<artifactId>git-commit-id-plugin</artifactId> +<version>${maven-plugin-git-commit-id.version}</version> +<executions> + <execution> + <id>get-the-git-infos</id> + <goals> + <goal>revision</goal> + </goals> + <phase>initialize</phase> + </execution> +</executions> +<configuration> + <generateGitPropertiesFile>true</generateGitPropertiesFile> + <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename> + <commitIdGenerationMode>full</commitIdGenerationMode> +</configuration> +</plugin> +``` + +## Usage Examples + +```bash +# Build with git information +./mvnw clean package + +# Git properties will be available at runtime +cat target/classes/git.properties +``` + +**Access in Java code:** +```java +Properties gitProps = new Properties(); +gitProps.load(getClass().getResourceAsStream("/git.properties")); +String commitId = gitProps.getProperty("git.commit.id"); +String buildTime = gitProps.getProperty("git.build.time"); +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Test Git Commit ID plugin +./mvnw clean package +cat target/classes/git.properties +``` + + +#### Step Constraints + +- **MUST** only add Git Commit ID plugin if build information tracking was selected in Step 3 +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if build information tracking was not selected +- **MUST** ensure project is in a git repository for proper functionality + +### Step 17: Flatten Maven Plugin Configuration + +**Purpose**: Configure Flatten Maven plugin to flatten POM files for library publishing to Maven repositories. + +**Dependencies**: Only execute if user selected "Java Library" as project nature in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing plugins. + +## Pre-Implementation Check + +**BEFORE adding Flatten plugin, check if it already exists in the pom.xml:** + +If flatten-maven-plugin already exists: Ask user "Flatten plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Java Library" as project nature in Step 3. + +## Flatten Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<plugin> +<groupId>org.codehaus.mojo</groupId> +<artifactId>flatten-maven-plugin</artifactId> +<version>${maven-plugin-flatten.version}</version> +<configuration> +</configuration> +<executions> + <execution> + <id>flatten</id> + <phase>process-resources</phase> + <goals> + <goal>flatten</goal> + </goals> + </execution> + <execution> + <id>flatten.clean</id> + <phase>clean</phase> + <goals> + <goal>clean</goal> + </goals> + </execution> +</executions> +</plugin> +``` + +## Usage Examples + +```bash +# Build library with flattened POM +./mvnw clean package + +# The flattened POM will be in target/ +cat target/.flattened-pom.xml + +# Deploy to repository +./mvnw clean deploy + +# Clean flattened files +./mvnw flatten:clean +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Test Flatten plugin +./mvnw clean package +ls target/.flattened-pom.xml +``` + + +#### Step Constraints + +- **MUST** only add Flatten plugin if "Java Library" was selected as project nature in Step 3 +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if project nature is not "Java Library" + +### Step 18: JMH (Java Microbenchmark Harness) Profile Configuration + +**Purpose**: Configure JMH (Java Microbenchmark Harness) profile for performance benchmarking with proper source directories and build configuration. + +**Dependencies**: Only execute if user selected "JMH" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this profile if it doesn't already exist. Never REPLACE or REMOVE existing profiles. + +**CRITICAL PREREQUISITE**: This step requires the project to be a single-module Maven project. Multi-module projects are not supported for JMH integration. + +## Pre-Implementation Checks + +**BEFORE adding JMH profile, perform these mandatory checks:** + +1. **Check for multi-module configuration**: Scan pom.xml for `<modules>` section + +If `<modules>` section exists: **STOP IMMEDIATELY** and inform user: "JMH profile cannot be added to multi-module Maven projects. JMH requires a single-module project structure for proper benchmark execution. Please configure JMH in individual modules instead." + +2. **Check for existing JMH profile**: + +If `<profiles>` section with `jmh` profile already exists: Ask user "JMH profile already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "JMH" in Step 3 AND project is single-module. + +## JMH Profile Configuration + +**ADD this profile to the `<profiles>` section in pom.xml ONLY if it doesn't already exist:** + +```xml +<profile> +<id>jmh</id> +<activation> + <activeByDefault>false</activeByDefault> +</activation> +<dependencies> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-core</artifactId> + <version>${jmh.version}</version> + </dependency> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-generator-annprocess</artifactId> + <version>${jmh.version}</version> + <scope>provided</scope> + </dependency> +</dependencies> +<build> + <plugins> + <!-- Add benchmark source directory --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>${maven-plugin-build-helper.version}</version> + <executions> + <execution> + <id>add-jmh-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>src/jmh/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + + <!-- Compile JMH benchmarks --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven-plugin-compiler.version}</version> + <configuration> + <release>${java.version}</release> + <annotationProcessorPaths> + <path> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-generator-annprocess</artifactId> + <version>${jmh.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> + + <!-- Create executable benchmark JAR --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>${maven-plugin-shade.version}</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <finalName>jmh-benchmarks</finalName> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>org.openjdk.jmh.Main</mainClass> + </transformer> + <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> + </transformers> + <filters> + <filter> + <!-- Exclude signatures --> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + <exclude>META-INF/MANIFEST.MF</exclude> + </excludes> + </filter> + </filters> + </configuration> + </execution> + </executions> + </plugin> + </plugins> +</build> +</profile> +``` + +## Directory Structure Setup + +**After adding the profile, create the benchmark source directory:** + +```bash +# Create JMH source directory +mkdir -p src/jmh/java + +# Create sample benchmark directory structure based on main package +# Example: if main package is com.example.demo, create: +mkdir -p src/jmh/java/com/example/demo/benchmarks +``` + +## Implementation Guidelines + +1. **Verify single-module structure**: Ensure no `<modules>` section exists in pom.xml +2. **Create benchmark source directory**: `src/jmh/java` for benchmark classes +3. **Follow JMH naming conventions**: Benchmark classes should end with `Benchmark` suffix +4. **Package structure**: Mirror main source package structure in `src/jmh/java` + +## Sample Benchmark Class + +**Create a sample benchmark in `src/jmh/java/[your-package]/benchmarks/FibonacciBenchmark.java`:** + +```java +package info.jab.benchmarks; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx2G"}) +@Warmup(iterations = 3) +@Measurement(iterations = 5) +public class FibonacciBenchmark { + +private static final int FIBONACCI_N = 20; + +@Benchmark +public long testFibonacciRecursive() { + return FibonacciCalculator.fibonacciRecursive(FIBONACCI_N); +} + +@Benchmark +public long testFibonacciIterative() { + return FibonacciCalculator.fibonacciIterative(FIBONACCI_N); +} + +/** + * Inner class that implements Fibonacci calculation in two different ways + */ +static class FibonacciCalculator { + + /** + * Recursive implementation of Fibonacci sequence + * Time complexity: O(2^n) - exponential + * Space complexity: O(n) - due to call stack + */ + public static long fibonacciRecursive(int n) { + if (n <= 1) { + return n; + } + return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2); + } + + /** + * Iterative implementation of Fibonacci sequence + * Time complexity: O(n) - linear + * Space complexity: O(1) - constant + */ + public static long fibonacciIterative(int n) { + if (n <= 1) { + return n; + } + + long prev = 0; + long curr = 1; + + for (int i = 2; i <= n; i++) { + long next = prev + curr; + prev = curr; + curr = next; + } + + return curr; + } +} + +/** + * Main method to run benchmarks with JSON output configuration + */ +public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(FibonacciBenchmark.class.getSimpleName()) + .resultFormat(ResultFormatType.JSON) + .result("jmh-fibonacci-benchmark-results.json") + .build(); + + new Runner(options).run(); +} +} +``` + +## Validation + +After adding this profile, verify the configuration: + +```bash +# Test JMH profile compilation +./mvnw clean compile -Pjmh + +# Build JMH benchmarks +./mvnw clean package -Pjmh + +# Verify JAR creation +ls target/jmh-benchmarks.jar + +# List available benchmarks +java -jar target/jmh-benchmarks.jar -l + +# Show help +java -jar target/jmh-benchmarks.jar -h + +# Run benchmark +java -cp target/jmh-benchmarks.jar info.jab.demo.benchmarks.FibonacciBenchmark -wi 1 -i 1 -f 1 + +# Verify that results are generated +ls jmh-fibonacci-benchmark-results.json + +# Share references to JMH + +- https://openjdk.org/projects/code-tools/jmh/ +- https://jmh.morethan.io +``` + + +#### Step Constraints + +- **MUST** only add JMH profile if "JMH" was selected in Step 3 +- **MUST** verify project is single-module (no `<modules>` section) before proceeding +- **MUST** check if profile already exists before adding +- **MUST** ask user permission before modifying existing profile configuration +- **MUST** use properties configured in Step 4 for plugin and dependency versions +- **MUST** create `src/jmh/java` directory structure for benchmarks +- **MUST** skip this step entirely if JMH was not selected OR if project has modules +- **MUST** stop immediately and inform user if multi-module project detected +- **MUST** configure build-helper-maven-plugin to add JMH source directory +- **MUST** configure maven-shade-plugin to create executable benchmark JAR +- **MUST** verify that JSON report is generated by executing benchmark and checking for `jmh-fibonacci-benchmark-results.json fil` + +### Step 19: Maven Compiler Plugin Configuration + +**Purpose**: Configure maven-compiler-plugin with explicit Java release, strict lint options, and treat warnings as errors for code quality. + +**Dependencies**: Only execute if user selected "Maven Compiler" in Step 3. Requires completion of Steps 3 and 4. + +**CRITICAL PRESERVATION RULE**: Only ADD this plugin if it doesn't already exist. Never REPLACE or REMOVE existing configuration. + +## Pre-Implementation Check + +**BEFORE adding maven-compiler-plugin, check if it already exists in the pom.xml:** + +If maven-compiler-plugin already exists: Ask user "maven-compiler-plugin already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Maven Compiler" in Step 3. + +## Maven Compiler Plugin Configuration + +**ADD this plugin to the `<build><plugins>` section ONLY if it doesn't already exist:** + +```xml +<!-- Maven Compiler Plugin --> +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven-plugin-compiler.version}</version> + <configuration> + <release>${java.version}</release> + <compilerArgs> + <arg>-Xlint:all</arg> + <arg>-Werror</arg> + </compilerArgs> + </configuration> +</plugin> +``` + +## Implementation Guidelines + +1. **release**: Uses `${java.version}` from properties - ensures consistent Java target across the build +2. **-Xlint:all**: Enables all compiler warnings for potential code quality issues +3. **-Werror**: Treats compiler warnings as errors - prevents shipping code with warnings + +## Usage Examples + +```bash +# Compile with strict settings +./mvnw clean compile + +# Verify compilation succeeds (warnings will fail the build) +./mvnw clean package +``` + +## Validation + +After adding this plugin, verify the configuration: + +```bash +# Test Maven Compiler plugin +./mvnw clean compile +``` + + +#### Step Constraints + +- **MUST** only add maven-compiler-plugin if "Maven Compiler" was selected in Step 3 +- **MUST** check if plugin already exists before adding +- **MUST** ask user permission before modifying existing plugin configuration +- **MUST** use properties configured in Step 4 for plugin version and java.version +- **MUST** skip this step entirely if Maven Compiler was not selected + +### Step 20: Cyclomatic Complexity Analysis Profile Configuration + +**Purpose**: Configure PMD-based cyclomatic complexity analysis profile to detect and report overly complex methods and classes. + +**Dependencies**: Only execute if user selected "Cyclomatic Complexity" in Step 3. Requires completion of core plugin steps (3, 4, 5). + +**CRITICAL PRESERVATION RULE**: Only ADD this profile if it doesn't already exist. Never REPLACE or REMOVE existing profiles. + +## Pre-Implementation Check + +**BEFORE adding Cyclomatic Complexity profile, check if it already exists in the pom.xml:** + +If `<profiles>` section with `cyclomatic-complexity` profile already exists: Ask user "Cyclomatic Complexity profile already exists. Do you want to enhance the existing configuration? (y/n)" + +If user says "n": Skip this step entirely. +If user says "y": Proceed with adding missing configuration elements only. + +**CONDITIONAL EXECUTION**: Only execute this step if user selected "Cyclomatic Complexity" in Step 3. + +## PMD Ruleset Configuration File + +**CREATE the PMD ruleset file BEFORE adding the profile.** Location depends on project structure: + +**Mono-module projects** (no `<modules>` in pom.xml): Create `src/main/pmd/pmd-cyclomatic-complexity.xml` +**Multi-module projects** (has `<modules>` in pom.xml): Create `pmd/pmd-cyclomatic-complexity.xml` at project root + +**File content** (`pmd-cyclomatic-complexity.xml`): + +```xml +<?xml version="1.0"?> +<ruleset name="Cyclomatic Complexity Ruleset" + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> + + <description> + Custom ruleset for cyclomatic complexity analysis only + </description> + + <!-- https://pmd.github.io/pmd/pmd_rules_java_design.html#cyclomaticcomplexity --> + <rule ref="category/java/design.xml/CyclomaticComplexity"> + <properties> + <property name="classReportLevel" value="70" /> + <property name="methodReportLevel" value="25" /> + <property name="cycloOptions" value="" /> + </properties> + </rule> + +</ruleset> +``` + +## Cyclomatic Complexity Profile Configuration + +**Use the correct ruleset path** when adding the profile: +- Mono-module: `src/main/pmd/pmd-cyclomatic-complexity.xml` +- Multi-module: `pmd/pmd-cyclomatic-complexity.xml` + +**ADD this profile to the `<profiles>` section in pom.xml ONLY if it doesn't already exist:** + +```xml +<!-- Cyclomatic Complexity Analysis Profile --> +<profile> + <id>cyclomatic-complexity</id> + <activation> + <activeByDefault>false</activeByDefault> + </activation> + <build> + <plugins> + <!-- PMD Plugin for Cyclomatic Complexity Analysis --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>${maven-plugin-pmd.version}</version> + <configuration> + <rulesets> + <ruleset>src/main/pmd/pmd-cyclomatic-complexity.xml</ruleset> + </rulesets> + <printFailingErrors>true</printFailingErrors> + <linkXRef>true</linkXRef> + <minimumTokens>100</minimumTokens> + </configuration> + <executions> + <execution> + <id>pmd-check</id> + <phase>verify</phase> + <goals> + <goal>check</goal> + </goals> + </execution> + <execution> + <id>pmd-report</id> + <phase>site</phase> + <goals> + <goal>pmd</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + <reporting> + <plugins> + <!-- PMD Report Plugin --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>${maven-plugin-pmd.version}</version> + <configuration> + <rulesets> + <ruleset>src/main/pmd/pmd-cyclomatic-complexity.xml</ruleset> + </rulesets> + <linkXRef>true</linkXRef> + </configuration> + </plugin> + <!-- JXR Plugin for Source Cross-Reference --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jxr-plugin</artifactId> + <version>${maven-plugin-jxr.version}</version> + </plugin> + </plugins> + </reporting> +</profile> +``` + +**For multi-module projects**: Replace `src/main/pmd/pmd-cyclomatic-complexity.xml` with `pmd/pmd-cyclomatic-complexity.xml` in both build and reporting plugin configurations. + +## Usage Examples + +```bash +# Run cyclomatic complexity analysis +./mvnw clean verify -Pcyclomatic-complexity + +# Generate PMD reports +./mvnw site -Pcyclomatic-complexity + +# View reports +open target/site/pmd.html +``` + +## Validation + +After adding this profile, verify the configuration: + +```bash +# Test Cyclomatic Complexity profile +./mvnw clean verify -Pcyclomatic-complexity +``` + + +#### Step Constraints + +- **MUST** only add Cyclomatic Complexity profile if "Cyclomatic Complexity" was selected in Step 3 +- **MUST** create PMD ruleset file before adding the profile +- **MUST** use mono-module path (src/main/pmd/) or multi-module path (pmd/) based on project structure +- **MUST** check if profile already exists before adding +- **MUST** ask user permission before modifying existing profile configuration +- **MUST** use properties configured in Step 4 for plugin versions +- **MUST** skip this step entirely if Cyclomatic Complexity was not selected + + +## Output Format + +- Ask questions one by one following the template exactly in Step 3 +- Execute steps 4-20 only based on user selections from Step 3 +- Skip entire steps if no relevant features were selected +- Implement only requested features based on user selections +- Follow template specifications exactly for all configurations +- Provide clear progress feedback showing which step is being executed +- Provide usage examples only for features that were added + +## Safeguards + +- **MANDATORY**: Complete Step 1 (existing configuration analysis) before making any changes +- **NEVER remove or replace existing plugins** - only add new plugins that don't already exist +- **NEVER remove or replace existing properties** - only add new properties that don't conflict +- **ASK USER before overriding** any existing configuration element +- Verify changes with the command: `mvn validate` or `./mvnw validate` +- Always read template files fresh using file_search and read_file tools +- Never proceed to next step without completing dependencies +- Template adherence is mandatory - no exceptions or simplified versions +- Validate that all plugin configurations reference properties from Step 4 +- **DOCUMENT what was added vs what was preserved** in the final summary \ No newline at end of file diff --git a/skills/113-java-maven-documentation/SKILL.md b/skills/113-java-maven-documentation/SKILL.md new file mode 100644 index 00000000..f90cef3a --- /dev/null +++ b/skills/113-java-maven-documentation/SKILL.md @@ -0,0 +1,22 @@ +--- +name: 113-java-maven-documentation +description: Use when you need to create a DEVELOPER.md file for a Maven project — combining a fixed base template with dynamic sections derived from the project pom.xml, including a Plugin Goals Reference, Maven Profiles table, and Submodules table for multi-module projects. Part of the skills-for-java project +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Create DEVELOPER.md for the Maven projects + +Generate a `DEVELOPER.md` file that combines a fixed base template with dynamic sections derived from analysing the project `pom.xml`. + +**Core areas:** Base template reproduction (verbatim), plugin goals reference (table of `./mvnw` goals per explicitly declared plugin, max 8 goals each), Maven Profiles table (profile ID, activation trigger, representative command, description), and Submodules table (multi-module projects only). + +**Prerequisites:** Read every `pom.xml` in the workspace (root and submodules) before generating any content. Only include plugins **explicitly declared** in `<build><plugins>` or `<build><pluginManagement><plugins>` — never plugins inherited from parent POMs or the Maven super-POM unless redeclared. + +**Multi-step scope:** Step 1 reproduces the base template verbatim. Step 2 collects all explicitly declared plugins. Step 3 appends the Plugin Goals Reference section. Step 4 appends the Maven Profiles section (omit if no profiles). Step 5 appends the Submodules section (omit if not a multi-module project). + +**Before applying changes:** Read the reference for the base template content, plugin catalog, and detailed constraints for each step. + +## Reference + +For detailed guidance, examples, and constraints, see [references/113-java-maven-documentation.md](references/113-java-maven-documentation.md). diff --git a/skills/113-java-maven-documentation/references/113-java-maven-documentation.md b/skills/113-java-maven-documentation/references/113-java-maven-documentation.md new file mode 100644 index 00000000..7dcf5c31 --- /dev/null +++ b/skills/113-java-maven-documentation/references/113-java-maven-documentation.md @@ -0,0 +1,416 @@ +--- +name: 113-java-maven-documentation +description: Use when you need to create a DEVELOPER.md file for a Maven project documenting plugin goals, Maven profiles, and submodules. +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Create DEVELOPER.md for the Maven projects + +## Role + +You are a Senior software engineer with extensive experience in Java software development and Maven build systems + +## Goal + +Create a markdown file named `DEVELOPER.md` for the current Maven project. +The file MUST combine a fixed base template with dynamic sections derived from analysing the project `pom.xml`. + +## Constraints + +Rules for generating the DEVELOPER.md file: + +- The base template section MUST be reproduced exactly as shown +- Only add plugin subsections for plugins **explicitly declared** in the project `pom.xml` — never for plugins inherited implicitly from the Maven super-POM or parent POM unless they are redeclared +- For each plugin subsection, include **only** the most useful and commonly used goals (max 8 per plugin) +- If a plugin found in `pom.xml` is not in the known catalog, still add a subsection with its most popular goals based on your knowledge +- Use `./mvnw` as the command prefix, not `mvn` +- Keep descriptions concise — one line per goal + +## Steps + +### Step 1: Start with the base template + +Copy the following base template verbatim as the first section of `DEVELOPER.md`: + +```markdown +# Developer commands + +## Essential maven commands + +```bash +# Analyze dependencies +./mvnw dependency:tree +./mvnw dependency:analyze +./mvnw dependency:resolve + +./mvnw clean validate -U +./mvnw buildplan:list-plugin +./mvnw buildplan:list-phase +./mvnw help:all-profiles +./mvnw help:active-profiles +./mvnw license:third-party-report + +# Clean the project +./mvnw clean + +# Run unit tests +./mvnw clean test + +# Run integration tests +./mvnw clean verify + +# Clean and package in one command +./mvnw clean package + +# Check for dependency updates +./mvnw versions:display-property-updates +./mvnw versions:display-dependency-updates +./mvnw versions:display-plugin-updates + +# Generate project reports +./mvnw site +jwebserver -p 8005 -d "$(pwd)/target/site/" +``` + +``` + +#### Step Constraints + +- Reproduce the base template exactly — do not modify, reorder, or omit any part of it + +### Step 2: Analyse the project pom.xml + +Read every `pom.xml` in the workspace (root and submodules). +For each plugin declared explicitly inside `<build><plugins>` or `<build><pluginManagement><plugins>`, collect its `groupId`, `artifactId`, and any `<configuration>` or `<executions>` present. + +#### Step Constraints + +- Only collect plugins that are **explicitly declared** — ignore plugins inherited from parent POMs or the Maven super-POM +- Include plugins from `<profiles>` sections as well +- For multi-module projects, analyse every module's `pom.xml` + +### Step 3: Append a Plugin Goals Reference section + +After the base template content, append a level-2 heading titled **Plugin Goals Reference** followed by the text: +"The following sections list useful goals for each plugin configured in this project's pom.xml." + +For **each** plugin found in Step 2, add a level-3 subsection named after the plugin `artifactId`, containing a markdown table with columns **Goal** and **Description**. +Each row should show a `./mvnw artifactId:goal` command and a one-line description. + +Use the following catalog as a reference for known plugins: + +### Known plugin catalog + +Use this catalog as a reference when generating goal tables. +Only include a plugin subsection if the plugin appears in the project `pom.xml`. + +#### maven-compiler-plugin +| Goal | Description | +|------|-------------| +| `./mvnw compiler:compile` | Compile main source files | +| `./mvnw compiler:testCompile` | Compile test source files | + +#### maven-surefire-plugin +| Goal | Description | +|------|-------------| +| `./mvnw surefire:test` | Run unit tests | +| `./mvnw surefire:help` | Display help information | + +#### maven-failsafe-plugin +| Goal | Description | +|------|-------------| +| `./mvnw failsafe:integration-test` | Run integration tests | +| `./mvnw failsafe:verify` | Verify integration test results | + +#### maven-jar-plugin +| Goal | Description | +|------|-------------| +| `./mvnw jar:jar` | Build the JAR for the project | +| `./mvnw jar:test-jar` | Build a JAR of the test classes | + +#### maven-shade-plugin +| Goal | Description | +|------|-------------| +| `./mvnw shade:shade` | Create an uber-JAR with all dependencies | +| `./mvnw shade:help` | Display help information | + +#### maven-assembly-plugin +| Goal | Description | +|------|-------------| +| `./mvnw assembly:single` | Create a distribution assembly | +| `./mvnw assembly:help` | Display help information | + +#### maven-dependency-plugin +| Goal | Description | +|------|-------------| +| `./mvnw dependency:tree` | Display dependency tree | +| `./mvnw dependency:analyze` | Analyse used/unused declared dependencies | +| `./mvnw dependency:resolve` | Resolve and list all dependencies | +| `./mvnw dependency:copy-dependencies` | Copy dependencies to a target directory | +| `./mvnw dependency:go-offline` | Download all dependencies for offline use | + +#### maven-enforcer-plugin +| Goal | Description | +|------|-------------| +| `./mvnw enforcer:enforce` | Execute enforcer rules | +| `./mvnw enforcer:display-info` | Display current platform information | + +#### maven-resources-plugin +| Goal | Description | +|------|-------------| +| `./mvnw resources:resources` | Copy main resources to output directory | +| `./mvnw resources:testResources` | Copy test resources to output directory | + +#### maven-clean-plugin +| Goal | Description | +|------|-------------| +| `./mvnw clean:clean` | Delete the build output directory | + +#### maven-install-plugin +| Goal | Description | +|------|-------------| +| `./mvnw install:install` | Install artifact into local repository | + +#### maven-deploy-plugin +| Goal | Description | +|------|-------------| +| `./mvnw deploy:deploy` | Deploy artifact to remote repository | + +#### maven-site-plugin +| Goal | Description | +|------|-------------| +| `./mvnw site:site` | Generate the project site | +| `./mvnw site:stage` | Stage the site for local preview | +| `./mvnw site:run` | Run the site with a local server | + +#### maven-javadoc-plugin +| Goal | Description | +|------|-------------| +| `./mvnw javadoc:javadoc` | Generate Javadoc HTML documentation | +| `./mvnw javadoc:test-javadoc` | Generate Javadoc for test sources | +| `./mvnw javadoc:jar` | Bundle Javadoc into a JAR | + +#### maven-source-plugin +| Goal | Description | +|------|-------------| +| `./mvnw source:jar` | Bundle source files into a JAR | +| `./mvnw source:test-jar` | Bundle test source files into a JAR | + +#### maven-release-plugin +| Goal | Description | +|------|-------------| +| `./mvnw release:prepare` | Prepare a release (tag, version bump) | +| `./mvnw release:perform` | Perform the release (deploy artifacts) | +| `./mvnw release:rollback` | Rollback a previous release preparation | +| `./mvnw release:clean` | Clean up after a release preparation | + +#### versions-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw versions:display-dependency-updates` | Show available dependency updates | +| `./mvnw versions:display-plugin-updates` | Show available plugin updates | +| `./mvnw versions:display-property-updates` | Show available property updates | +| `./mvnw versions:use-latest-releases` | Update dependencies to latest releases | +| `./mvnw versions:set -DnewVersion=X.Y.Z` | Set the project version | + +#### buildplan-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw buildplan:list-plugin` | List plugins bound to the build lifecycle | +| `./mvnw buildplan:list-phase` | List goals per lifecycle phase | +| `./mvnw buildplan:list` | List all plugin executions in order | + +#### maven-checkstyle-plugin +| Goal | Description | +|------|-------------| +| `./mvnw checkstyle:check` | Check code style and fail on violations | +| `./mvnw checkstyle:checkstyle` | Generate a Checkstyle report | + +#### spotbugs-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw spotbugs:check` | Run SpotBugs analysis and fail on bugs | +| `./mvnw spotbugs:spotbugs` | Generate a SpotBugs report | +| `./mvnw spotbugs:gui` | Launch the SpotBugs GUI | + +#### maven-pmd-plugin +| Goal | Description | +|------|-------------| +| `./mvnw pmd:check` | Run PMD analysis and fail on violations | +| `./mvnw pmd:pmd` | Generate a PMD report | +| `./mvnw pmd:cpd-check` | Run copy-paste detection and fail on duplicates | + +#### jacoco-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw jacoco:prepare-agent` | Prepare the JaCoCo agent for coverage | +| `./mvnw jacoco:report` | Generate code coverage report | +| `./mvnw jacoco:check` | Verify coverage against thresholds | + +#### maven-antrun-plugin +| Goal | Description | +|------|-------------| +| `./mvnw antrun:run` | Execute Ant tasks defined in configuration | + +#### maven-war-plugin +| Goal | Description | +|------|-------------| +| `./mvnw war:war` | Build the WAR file | +| `./mvnw war:exploded` | Create an exploded WAR directory | + +#### maven-ear-plugin +| Goal | Description | +|------|-------------| +| `./mvnw ear:ear` | Build the EAR file | +| `./mvnw ear:generate-application-xml` | Generate application.xml descriptor | + +#### spring-boot-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw spring-boot:run` | Run the application | +| `./mvnw spring-boot:build-image` | Build an OCI container image | +| `./mvnw spring-boot:repackage` | Repackage JAR/WAR as executable | +| `./mvnw spring-boot:build-info` | Generate build-info.properties | + +#### quarkus-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw quarkus:dev` | Run in development mode with live reload | +| `./mvnw quarkus:build` | Build the application | +| `./mvnw quarkus:test` | Run continuous testing | +| `./mvnw quarkus:image-build` | Build a container image | + +#### jib-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw jib:build` | Build and push container image to registry | +| `./mvnw jib:dockerBuild` | Build container image to local Docker daemon | +| `./mvnw jib:buildTar` | Build container image as a tarball | + +#### docker-maven-plugin (fabric8) +| Goal | Description | +|------|-------------| +| `./mvnw docker:build` | Build Docker images | +| `./mvnw docker:start` | Start containers | +| `./mvnw docker:stop` | Stop containers | +| `./mvnw docker:push` | Push images to registry | + +#### maven-archetype-plugin +| Goal | Description | +|------|-------------| +| `./mvnw archetype:generate` | Generate a project from an archetype | +| `./mvnw archetype:create-from-project` | Create an archetype from the current project | + +#### flatten-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw flatten:flatten` | Generate a flattened POM | +| `./mvnw flatten:clean` | Remove the flattened POM | + +#### license-maven-plugin (MojoHaus) +| Goal | Description | +|------|-------------| +| `./mvnw license:third-party-report` | Generate third-party license report | +| `./mvnw license:add-third-party` | Add third-party license info to file | +| `./mvnw license:aggregate-third-party-report` | Aggregate license report for multi-module | + +#### fmt-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw fmt:format` | Format Java sources with google-java-format | +| `./mvnw fmt:check` | Check formatting without modifying files | + +#### spotless-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw spotless:apply` | Apply formatting fixes | +| `./mvnw spotless:check` | Check formatting and fail on violations | + +#### openapi-generator-maven-plugin +| Goal | Description | +|------|-------------| +| `./mvnw openapi-generator:generate` | Generate code from OpenAPI specification | + +#### jooq-codegen-maven +| Goal | Description | +|------|-------------| +| `./mvnw jooq-codegen:generate` | Generate Java code from database schema | + +#### maven-wrapper-plugin +| Goal | Description | +|------|-------------| +| `./mvnw wrapper:wrapper` | Generate or update Maven wrapper files | + +#### native-maven-plugin (GraalVM) +| Goal | Description | +|------|-------------| +| `./mvnw native:compile` | Compile to a native executable | +| `./mvnw native:test` | Run tests as a native image | + +#### pitest-maven (PIT Mutation Testing) +| Goal | Description | +|------|-------------| +| `./mvnw pitest:mutationCoverage` | Run mutation testing | +| `./mvnw pitest:scmMutationCoverage` | Run mutation testing on changed code only | + + +#### Step Constraints + +- Only create subsections for plugins actually found in the project `pom.xml` during Step 2 +- For plugins not in the catalog, still add a subsection using your knowledge of the plugin's goals +- Include a maximum of 8 goals per plugin + +### Step 4: Append a Maven Profiles section + +After the Plugin Goals Reference section, append a level-2 heading titled **Maven Profiles** followed by the text: +"The following profiles are declared in this project. Activate them with `-P <profileId>`." + +Scan every `pom.xml` collected in Step 2 for `<profiles><profile>` elements. +For each profile found, create a row in a markdown table with columns **Profile ID**, **Command**, **Activation**, and **Description**. + +- **Profile ID**: the value of `<id>` +- **Command**: the exact `./mvnw` command to activate the profile — e.g. `./mvnw clean verify -P <profileId>`; use the most representative lifecycle phase for the profile's purpose (e.g. `verify` for analysis/check profiles, `generate-resources` for site generation profiles) +- **Activation**: describe the activation trigger — e.g. "manual", "default (activeByDefault)", "property: `<name>`=`<value>`", "JDK `<version>`", "OS `<family>`", etc. If no `<activation>` element is present, use "manual" +- **Description**: a one-sentence summary of what the profile does, inferred from its configuration (plugins, properties, dependencies it adds or overrides) + +If no profiles are declared in any `pom.xml`, omit this section entirely. + + +#### Step Constraints + +- Only list profiles explicitly declared inside a `<profiles>` block — do not invent profiles +- Indicate which `pom.xml` file (root or submodule path) each profile comes from when the project is multi-module +- If a profile has `<activeByDefault>true</activeByDefault>`, reflect that in the Activation column + +### Step 5: Append a Submodules section (multi-module projects only) + +If the root `pom.xml` contains a `<modules>` element, append a level-2 heading titled **Submodules** followed by the text: +"This is a multi-module project. The following modules are declared in the root `pom.xml`." + +List each submodule as a row in a markdown table with columns **Module**, **Artifact ID**, **Commands**, and **Description**. + +- **Module**: the relative path as declared in the `<module>` element +- **Artifact ID**: the `<artifactId>` from that module's `pom.xml` +- **Commands**: the most useful `./mvnw` commands scoped to this module using the `-pl <module>` flag; include `./mvnw clean verify -pl <module>` always, and add `./mvnw clean install -pl <module>` when the module produces an artifact consumed by other modules; if the module has a profile that must be activated, add the relevant `-P <profileId>` variant as well +- **Description**: a one-sentence summary of the module's purpose, inferred from its `<description>` element if present, or from its declared dependencies and plugins otherwise + +If the project is not a multi-module build (no `<modules>` element in the root POM), omit this section entirely. + + +#### Step Constraints + +- Only list modules explicitly declared in the root `pom.xml` `<modules>` block +- Read each submodule's `pom.xml` to obtain its `artifactId` and `description` +- Do not fabricate modules that do not exist in the workspace +- Place multiple commands for the same module in the same cell, separated by a line break (`<br>`) + + +## Output Format + +- Generate the complete markdown file: base template first, then Plugin Goals Reference, then Maven Profiles (if any), then Submodules (if any) +- Use proper markdown formatting with headers, code blocks, and tables +- Verify that every plugin listed in the Plugin Goals Reference actually exists in the project `pom.xml` +- Omit the Maven Profiles section if no profiles are declared in any `pom.xml` +- Omit the Submodules section if the project is not a multi-module build \ No newline at end of file diff --git a/system-prompts-generator/src/main/resources/system-prompts/112-java-maven-plugins.xml b/system-prompts-generator/src/main/resources/system-prompts/112-java-maven-plugins.xml index 14e68559..74d099ad 100644 --- a/system-prompts-generator/src/main/resources/system-prompts/112-java-maven-plugins.xml +++ b/system-prompts-generator/src/main/resources/system-prompts/112-java-maven-plugins.xml @@ -5,7 +5,8 @@ <author>Juan Antonio Breña Moral</author> <version>0.12.0-SNAPSHOT</version> <license>Apache-2.0</license> - <title>Update pom.xml to add Maven plugins with modular step-based configuration + Maven Plugins: pom.xml Configuration Best Practices + Use when you need to add or configure Maven plugins in your pom.xml using a modular, step-based approach. You are a Senior software engineer with extensive experience in Java software development diff --git a/system-prompts-generator/src/main/resources/system-prompts/113-java-maven-documentation.xml b/system-prompts-generator/src/main/resources/system-prompts/113-java-maven-documentation.xml index 9c11b317..5355a429 100644 --- a/system-prompts-generator/src/main/resources/system-prompts/113-java-maven-documentation.xml +++ b/system-prompts-generator/src/main/resources/system-prompts/113-java-maven-documentation.xml @@ -5,7 +5,8 @@ Juan Antonio Breña Moral 0.12.0-SNAPSHOT Apache-2.0 - Create DEVELOPER.md with information about how to use the Maven project + Create DEVELOPER.md for the Maven projects + Use when you need to create a DEVELOPER.md file for a Maven project documenting plugin goals, Maven profiles, and submodules. You are a Senior software engineer with extensive experience in Java software development and Maven build systems From 98ba0ec43702242289debfdddd9ae3349686ee7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Bre=C3=B1a=20Moral?= Date: Sun, 1 Mar 2026 12:29:37 +0100 Subject: [PATCH 6/8] Adding new skills --- .../rules/121-java-object-oriented-design.md | 1 + .cursor/rules/122-java-type-design.md | 1 + .cursor/rules/128-java-generics.md | 1 + .cursor/rules/131-java-unit-testing.md | 1 + .cursor/rules/132-java-integration-testing.md | 1 + ...1-java-refactoring-with-modern-features.md | 1 + .../rules/142-java-functional-programming.md | 1 + .../143-java-functional-exception-handling.md | 3 +- .../144-java-data-oriented-programming.md | 3 +- .cursor/rules/170-java-documentation.md | 1 + .cursor/rules/171-java-diagrams.md | 1 + .cursor/rules/172-java-agents.md | 1 + documentation/MAINTENANCE.md | 8 + .../src/main/resources/skill-inventory.json | 13 +- .../src/main/resources/skills/121-skill.md | 22 + .../src/main/resources/skills/122-skill.md | 22 + .../src/main/resources/skills/128-skill.md | 22 + .../src/main/resources/skills/131-skill.md | 22 + .../src/main/resources/skills/132-skill.md | 22 + .../src/main/resources/skills/141-skill.md | 22 + .../src/main/resources/skills/142-skill.md | 22 + .../src/main/resources/skills/143-skill.md | 22 + .../src/main/resources/skills/170-skill.md | 22 + .../src/main/resources/skills/171-skill.md | 22 + .../src/main/resources/skills/172-skill.md | 22 + .../121-java-object-oriented-design/SKILL.md | 22 + .../121-java-object-oriented-design.md | 2504 +++++++++++++++++ skills/122-java-type-design/SKILL.md | 22 + .../references/122-java-type-design.md | 889 ++++++ skills/128-java-generics/SKILL.md | 22 + .../references/128-java-generics.md | 1758 ++++++++++++ skills/131-java-unit-testing/SKILL.md | 22 + .../references/131-java-unit-testing.md | 906 ++++++ skills/132-java-integration-testing/SKILL.md | 22 + .../132-java-integration-testing.md | 490 ++++ .../SKILL.md | 22 + ...1-java-refactoring-with-modern-features.md | 1337 +++++++++ .../142-java-functional-programming/SKILL.md | 22 + .../142-java-functional-programming.md | 1004 +++++++ .../SKILL.md | 22 + .../143-java-functional-exception-handling.md | 1429 ++++++++++ .../SKILL.md | 22 + .../144-java-data-oriented-programming.md | 914 ++++++ skills/170-java-documentation/SKILL.md | 22 + .../references/170-java-documentation.md | 1086 +++++++ skills/171-java-diagrams/SKILL.md | 22 + .../references/171-java-diagrams.md | 1988 +++++++++++++ skills/172-java-agents/SKILL.md | 22 + .../references/172-java-agents.md | 259 ++ .../121-java-object-oriented-design.xml | 1 + .../system-prompts/122-java-type-design.xml | 1 + .../system-prompts/128-java-generics.xml | 1 + .../system-prompts/131-java-unit-testing.xml | 1 + .../132-java-integration-testing.xml | 1 + ...-java-refactoring-with-modern-features.xml | 1 + .../142-java-functional-programming.xml | 1 + ...143-java-functional-exception-handling.xml | 3 +- .../144-java-data-oriented-programming.xml | 3 +- .../system-prompts/170-java-documentation.xml | 1 + .../system-prompts/171-java-diagrams.xml | 1 + .../system-prompts/172-java-agents.xml | 1 + 61 files changed, 15118 insertions(+), 5 deletions(-) create mode 100644 skills-generator/src/main/resources/skills/121-skill.md create mode 100644 skills-generator/src/main/resources/skills/122-skill.md create mode 100644 skills-generator/src/main/resources/skills/128-skill.md create mode 100644 skills-generator/src/main/resources/skills/131-skill.md create mode 100644 skills-generator/src/main/resources/skills/132-skill.md create mode 100644 skills-generator/src/main/resources/skills/141-skill.md create mode 100644 skills-generator/src/main/resources/skills/142-skill.md create mode 100644 skills-generator/src/main/resources/skills/143-skill.md create mode 100644 skills-generator/src/main/resources/skills/170-skill.md create mode 100644 skills-generator/src/main/resources/skills/171-skill.md create mode 100644 skills-generator/src/main/resources/skills/172-skill.md create mode 100644 skills/121-java-object-oriented-design/SKILL.md create mode 100644 skills/121-java-object-oriented-design/references/121-java-object-oriented-design.md create mode 100644 skills/122-java-type-design/SKILL.md create mode 100644 skills/122-java-type-design/references/122-java-type-design.md create mode 100644 skills/128-java-generics/SKILL.md create mode 100644 skills/128-java-generics/references/128-java-generics.md create mode 100644 skills/131-java-unit-testing/SKILL.md create mode 100644 skills/131-java-unit-testing/references/131-java-unit-testing.md create mode 100644 skills/132-java-integration-testing/SKILL.md create mode 100644 skills/132-java-integration-testing/references/132-java-integration-testing.md create mode 100644 skills/141-java-refactoring-with-modern-features/SKILL.md create mode 100644 skills/141-java-refactoring-with-modern-features/references/141-java-refactoring-with-modern-features.md create mode 100644 skills/142-java-functional-programming/SKILL.md create mode 100644 skills/142-java-functional-programming/references/142-java-functional-programming.md create mode 100644 skills/143-java-functional-exception-handling/SKILL.md create mode 100644 skills/143-java-functional-exception-handling/references/143-java-functional-exception-handling.md create mode 100644 skills/144-java-data-oriented-programming/SKILL.md create mode 100644 skills/144-java-data-oriented-programming/references/144-java-data-oriented-programming.md create mode 100644 skills/170-java-documentation/SKILL.md create mode 100644 skills/170-java-documentation/references/170-java-documentation.md create mode 100644 skills/171-java-diagrams/SKILL.md create mode 100644 skills/171-java-diagrams/references/171-java-diagrams.md create mode 100644 skills/172-java-agents/SKILL.md create mode 100644 skills/172-java-agents/references/172-java-agents.md diff --git a/.cursor/rules/121-java-object-oriented-design.md b/.cursor/rules/121-java-object-oriented-design.md index 8c96bdc5..376e93f1 100644 --- a/.cursor/rules/121-java-object-oriented-design.md +++ b/.cursor/rules/121-java-object-oriented-design.md @@ -1,5 +1,6 @@ --- name: 121-java-object-oriented-design +description: Use when you need to review, improve, or refactor Java code for object-oriented design quality — applying SOLID, DRY, and YAGNI principles, improving class and interface design, fixing OOP misuse, and resolving common code smells such as God Class, Feature Envy, and Data Clumps. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/122-java-type-design.md b/.cursor/rules/122-java-type-design.md index 73152364..383e739c 100644 --- a/.cursor/rules/122-java-type-design.md +++ b/.cursor/rules/122-java-type-design.md @@ -1,5 +1,6 @@ --- name: 122-java-type-design +description: Use when you need to review, improve, or refactor Java code for type design quality — including establishing clear type hierarchies, applying consistent naming conventions, eliminating primitive obsession with domain-specific value objects, leveraging generic type parameters, creating type-safe wrappers, designing fluent interfaces, ensuring precision-appropriate numeric types (BigDecimal for financial calculations), and improving type contrast through interfaces and method signature alignment. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/128-java-generics.md b/.cursor/rules/128-java-generics.md index 3e440477..a78086b8 100644 --- a/.cursor/rules/128-java-generics.md +++ b/.cursor/rules/128-java-generics.md @@ -1,5 +1,6 @@ --- name: 128-java-generics +description: Use when you need to review, improve, or refactor Java code for generics quality — including avoiding raw types, applying PECS wildcards, using bounded type parameters, designing effective generic methods, leveraging type inference with the diamond operator, handling type erasure, preventing heap pollution with @SafeVarargs, and integrating generics with Records, sealed types, and pattern matching. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/131-java-unit-testing.md b/.cursor/rules/131-java-unit-testing.md index c859de3a..6683cff8 100644 --- a/.cursor/rules/131-java-unit-testing.md +++ b/.cursor/rules/131-java-unit-testing.md @@ -1,5 +1,6 @@ --- name: 131-java-unit-testing +description: Use when you need to review, improve, or write Java unit tests — including migrating from JUnit 4 to JUnit 5, adopting AssertJ for fluent assertions, structuring tests with Given-When-Then, ensuring test independence, applying parameterized tests, mocking dependencies with Mockito, verifying boundary conditions (RIGHT-BICEP, CORRECT, A-TRIP), leveraging JSpecify null-safety annotations, or eliminating testing anti-patterns such as reflection-based tests or shared mutable state. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/132-java-integration-testing.md b/.cursor/rules/132-java-integration-testing.md index d08f8f9a..4aa05d33 100644 --- a/.cursor/rules/132-java-integration-testing.md +++ b/.cursor/rules/132-java-integration-testing.md @@ -1,5 +1,6 @@ --- name: 132-java-integration-testing +description: Use when you need to set up, review, or improve Java integration tests — including generating a BaseIntegrationTest.java with WireMock for HTTP stubs, detecting HTTP client infrastructure from import signals, injecting service coordinates dynamically via System.setProperty(), creating WireMock JSON mapping files with bodyFileName, isolating stubs per test method, verifying HTTP interactions, or eliminating anti-patterns such as Mockito-mocked HTTP clients or globally registered WireMock stubs. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/141-java-refactoring-with-modern-features.md b/.cursor/rules/141-java-refactoring-with-modern-features.md index ea2f770b..e86867cf 100644 --- a/.cursor/rules/141-java-refactoring-with-modern-features.md +++ b/.cursor/rules/141-java-refactoring-with-modern-features.md @@ -1,5 +1,6 @@ --- name: 141-java-refactoring-with-modern-features +description: Use when you need to refactor Java code to adopt modern Java features (Java 8+) including lambda expressions, Stream API, Optional, java.time API, collection factory methods, CompletableFuture, text blocks, var inference, and Java 25 flexible constructor bodies and module import declarations. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/142-java-functional-programming.md b/.cursor/rules/142-java-functional-programming.md index 0ba403e5..bb9249f5 100644 --- a/.cursor/rules/142-java-functional-programming.md +++ b/.cursor/rules/142-java-functional-programming.md @@ -1,5 +1,6 @@ --- name: 142-java-functional-programming +description: Use when you need to apply functional programming principles in Java — including immutable objects and Records, pure functions, functional interfaces, lambda expressions, Stream API, Optional for null safety, function composition, higher-order functions, pattern matching, sealed classes/interfaces, and concurrent-safe functional patterns. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/143-java-functional-exception-handling.md b/.cursor/rules/143-java-functional-exception-handling.md index a5ae0450..afba882d 100644 --- a/.cursor/rules/143-java-functional-exception-handling.md +++ b/.cursor/rules/143-java-functional-exception-handling.md @@ -1,11 +1,12 @@ --- name: 143-java-functional-exception-handling +description: Use when you need to apply functional exception handling best practices in Java — including replacing exception overuse with Optional and VAVR Either types, designing error type hierarchies using sealed classes and enums, implementing monadic error composition pipelines, establishing functional control flow patterns, and reserving exceptions only for truly exceptional system-level failures. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT --- -# Java Exceptions Best Practices +# Java Functional Exception handling Best Practices ## Role diff --git a/.cursor/rules/144-java-data-oriented-programming.md b/.cursor/rules/144-java-data-oriented-programming.md index 43ad73c5..d5e88964 100644 --- a/.cursor/rules/144-java-data-oriented-programming.md +++ b/.cursor/rules/144-java-data-oriented-programming.md @@ -1,11 +1,12 @@ --- name: 144-java-data-oriented-programming +description: Use when you need to apply data-oriented programming best practices in Java — including separating code (behavior) from data structures using records, designing immutable data with pure transformation functions, keeping data flat and denormalized with ID-based references, starting with generic data structures converting to specific types when needed, ensuring data integrity through pure validation functions, and creating flexible generic data access layers. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral version: 0.12.0-SNAPSHOT --- -# Java rules to apply data oriented programming style +# Java Data-Oriented Programming Best Practices ## Role diff --git a/.cursor/rules/170-java-documentation.md b/.cursor/rules/170-java-documentation.md index ee2cf1c6..f7700508 100644 --- a/.cursor/rules/170-java-documentation.md +++ b/.cursor/rules/170-java-documentation.md @@ -1,5 +1,6 @@ --- name: 170-java-documentation +description: Use when you need to generate or improve Java project documentation — including README.md files, package-info.java files, Javadoc enhancements, and Architecture Decision Records (ADRs) — through a modular, step-based interactive process that adapts to your specific documentation needs. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/171-java-diagrams.md b/.cursor/rules/171-java-diagrams.md index 6a2ba6f7..101f5490 100644 --- a/.cursor/rules/171-java-diagrams.md +++ b/.cursor/rules/171-java-diagrams.md @@ -1,5 +1,6 @@ --- name: 171-java-diagrams +description: Use when you need to generate Java project diagrams — including UML sequence diagrams, UML class diagrams, C4 model diagrams, and UML state machine diagrams — through a modular, step-based interactive process that adapts to your specific visualization needs. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/.cursor/rules/172-java-agents.md b/.cursor/rules/172-java-agents.md index dafe42e3..971aecde 100644 --- a/.cursor/rules/172-java-agents.md +++ b/.cursor/rules/172-java-agents.md @@ -1,5 +1,6 @@ --- name: 172-java-agents +description: Use when you need to generate an AGENTS.md file for a Java repository — covering project conventions, tech stack, file structure, commands, Git workflow, and contributor boundaries — through a modular, step-based interactive process that adapts to your specific project needs. license: Apache-2.0 metadata: author: Juan Antonio Breña Moral diff --git a/documentation/MAINTENANCE.md b/documentation/MAINTENANCE.md index ddfb5c4a..586c3378 100644 --- a/documentation/MAINTENANCE.md +++ b/documentation/MAINTENANCE.md @@ -45,3 +45,11 @@ git tag --list git tag 0.11.0 git push --tags ``` + +--- + +## Add a new Skills + +```bash +review if exist a new id in @skills-generator/src/main/resources/skill-inventory.json to review compare with the content of @skills-generator/src/main/resources/skills and if exist add a new skill summary in @skills-generator/src/main/resources/skills . to elaborate the skill review the content of the id with @system-prompts-generator/src/main/resources/system-prompts when finish, validate generation with @/Users/jabrena/.cursor/projects/Users-jabrena-IdeaProjects-java-cursor-rules/terminals/1.txt:69-70 and validate the skill with npx skill-check skills +``` diff --git a/skills-generator/src/main/resources/skill-inventory.json b/skills-generator/src/main/resources/skill-inventory.json index c70a392c..37b1717a 100644 --- a/skills-generator/src/main/resources/skill-inventory.json +++ b/skills-generator/src/main/resources/skill-inventory.json @@ -2,5 +2,16 @@ {"id": 110}, {"id": 111}, {"id": 112}, - {"id": 113} + {"id": 113}, + {"id": 121}, + {"id": 122}, + {"id": 128}, + {"id": 131}, + {"id": 132}, + {"id": 141}, + {"id": 142}, + {"id": 143}, + {"id": 170}, + {"id": 171}, + {"id": 172} ] diff --git a/skills-generator/src/main/resources/skills/121-skill.md b/skills-generator/src/main/resources/skills/121-skill.md new file mode 100644 index 00000000..2d419527 --- /dev/null +++ b/skills-generator/src/main/resources/skills/121-skill.md @@ -0,0 +1,22 @@ +--- +name: 121-java-object-oriented-design +description: Use when you need to review, improve, or refactor Java code for object-oriented design quality — including applying SOLID, DRY, and YAGNI principles, improving class and interface design, fixing OOP concept misuse (encapsulation, inheritance, polymorphism), identifying and resolving code smells (God Class, Feature Envy, Data Clumps), or improving object creation patterns, method design, and exception handling. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Object-Oriented Design Guidelines + +Review and improve Java code using comprehensive object-oriented design guidelines and refactoring practices. + +**Core areas:** Fundamental design principles (SOLID, DRY, YAGNI), class and interface design (composition over inheritance, immutability, accessibility minimization, accessor methods), core OOP concepts (encapsulation, inheritance, polymorphism), object creation patterns (static factory methods, Builder pattern, Singleton, dependency injection, avoiding unnecessary objects), OOD code smells (God Class, Feature Envy, Inappropriate Intimacy, Refused Bequest, Shotgun Surgery, Data Clumps), method design (parameter validation, defensive copies, careful signatures, empty collections over nulls, Optional usage), and exception handling (checked vs. runtime exceptions, standard exceptions, failure-capture messages, no silent ignoring). + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any change. If compilation fails, **stop immediately** and do not proceed — compilation failure is a blocking condition. + +**Multi-step scope:** Step 1 validates the project compiles. Step 2 identifies applicable design principles and code smells. Step 3 applies SOLID/DRY/YAGNI improvements. Step 4 addresses class and interface design issues. Step 5 resolves OOP misuse and code smells. Step 6 improves object creation, method design, and exception handling patterns. + +**Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. + +## Reference + +For detailed guidance, examples, and constraints, see [references/121-java-object-oriented-design.md](references/121-java-object-oriented-design.md). diff --git a/skills-generator/src/main/resources/skills/122-skill.md b/skills-generator/src/main/resources/skills/122-skill.md new file mode 100644 index 00000000..9d7a2774 --- /dev/null +++ b/skills-generator/src/main/resources/skills/122-skill.md @@ -0,0 +1,22 @@ +--- +name: 122-java-type-design +description: Use when you need to review, improve, or refactor Java code for type design quality — including establishing clear type hierarchies, applying consistent naming conventions, eliminating primitive obsession with domain-specific value objects, leveraging generic type parameters, creating type-safe wrappers, designing fluent interfaces, ensuring precision-appropriate numeric types (BigDecimal for financial calculations), and improving type contrast through interfaces and method signature alignment. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Type Design Thinking in Java + +Review and improve Java code using comprehensive type design principles that apply typography concepts to code structure and organization for maximum clarity and maintainability. + +**Core areas:** Clear type hierarchies (nested static classes, logical structure), consistent naming conventions (domain-driven patterns, uniform interface/implementation naming), strategic whitespace for readability, type-safe wrappers (value objects replacing primitive obsession, EmailAddress, Money), generic type parameters (flexible reusable types, bounded parameters), domain-specific fluent interfaces (builder pattern, method chaining), type "weights" (conceptual importance assignment — core domain vs supporting vs utility), type contrast through interfaces (contract vs implementation separation), aligned method signatures (consistent parameter and return types across related classes), self-documenting code (clear descriptive names), BigDecimal for precision-sensitive calculations (financial/monetary operations), and strategic type selection (Optional, Set vs List, interfaces over concrete types). + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any change. If compilation fails, **stop immediately** and do not proceed — compilation failure is a blocking condition. + +**Multi-step scope:** Step 1 validates the project compiles. Step 2 analyzes Java code to identify type design issues categorized by impact (CRITICAL, MAINTAINABILITY, TYPE_SAFETY, READABILITY) and area (naming conventions, type hierarchies, generic usage, primitive obsession, type safety, precision). Step 3 applies naming convention improvements and type hierarchy restructuring. Step 4 eliminates primitive obsession by creating domain-specific value objects and records. Step 5 introduces proper generic type parameters with appropriate bounds and type-safe wrappers. Step 6 implements precision-appropriate numeric types and strategic collection/type selection. Step 7 validates all changes compile and all tests pass with `./mvnw clean verify`. + +**Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. + +## Reference + +For detailed guidance, examples, and constraints, see [references/122-java-type-design.md](references/122-java-type-design.md). diff --git a/skills-generator/src/main/resources/skills/128-skill.md b/skills-generator/src/main/resources/skills/128-skill.md new file mode 100644 index 00000000..f7e50495 --- /dev/null +++ b/skills-generator/src/main/resources/skills/128-skill.md @@ -0,0 +1,22 @@ +--- +name: 128-java-generics +description: Use when you need to review, improve, or refactor Java code for generics quality — including avoiding raw types, applying the PECS (Producer Extends Consumer Super) principle for wildcards, using bounded type parameters, designing effective generic methods, leveraging the diamond operator, understanding type erasure implications, handling generic inheritance correctly, preventing heap pollution with @SafeVarargs, and integrating generics with modern Java features like Records, sealed types, and pattern matching. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Generics Best Practices + +Review and improve Java code using comprehensive generics best practices that enforce compile-time type safety and enable flexible, reusable APIs. + +**Core areas:** Type safety (avoiding raw types, eliminating unsafe casts), code reusability (generic methods and types for multiple type contexts), API clarity (PECS wildcards — `? extends` for producers, `? super` for consumers), performance optimization (eliminating boxing/casting overhead), diamond operator for type inference, type erasure awareness (type tokens, factory patterns, array creation), generic inheritance and variance (invariance, covariance, contravariance), `@SafeVarargs` for heap pollution prevention, wildcard capture helpers, self-bounded generics (CRTP) for fluent builders, proper wildcard API design with `Comparator` and `Function`, arrays-vs-generics covariance pitfalls, serialization with `TypeReference`/`TypeToken`, eliminating unchecked warnings, generic naming conventions (`T`, `E`, `K/V`, `?`), typesafe heterogeneous containers, and integration with Records, sealed types, and pattern matching. + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any change. If compilation fails, **stop immediately** and do not proceed — compilation failure is a blocking condition. + +**Multi-step scope:** Step 1 validates the project compiles. Step 2 analyzes Java code to identify generics issues categorized by impact (CRITICAL, MAINTAINABILITY, PERFORMANCE, TYPE_SAFETY) and area (raw types, wildcard misuse, bounded parameter opportunities, type erasure problems, modern feature integration gaps). Step 3 eliminates raw types and unsafe casts with proper parameterization. Step 4 applies PECS wildcards and bounded type parameters for flexible APIs. Step 5 introduces diamond operator, generic methods, and type tokens for type erasure scenarios. Step 6 integrates generics with Records, sealed types, and pattern matching. Step 7 validates all changes compile and all tests pass with `./mvnw clean verify`. + +**Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. + +## Reference + +For detailed guidance, examples, and constraints, see [references/128-java-generics.md](references/128-java-generics.md). diff --git a/skills-generator/src/main/resources/skills/131-skill.md b/skills-generator/src/main/resources/skills/131-skill.md new file mode 100644 index 00000000..eb066311 --- /dev/null +++ b/skills-generator/src/main/resources/skills/131-skill.md @@ -0,0 +1,22 @@ +--- +name: 131-java-unit-testing +description: Use when you need to review, improve, or write Java unit tests — including migrating from JUnit 4 to JUnit 5, adopting AssertJ for fluent assertions, structuring tests with Given-When-Then, ensuring test independence, applying parameterized tests, mocking dependencies with Mockito, verifying boundary conditions (RIGHT-BICEP, CORRECT, A-TRIP), leveraging JSpecify null-safety annotations, or eliminating testing anti-patterns such as reflection-based tests or shared mutable state. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Unit testing guidelines + +Review and improve Java unit tests using modern JUnit 5, AssertJ, and Mockito best practices. + +**Core areas:** JUnit 5 annotations (`@Test`, `@BeforeEach`, `@AfterEach`, `@DisplayName`, `@Nested`, `@ParameterizedTest`), AssertJ fluent assertions (`assertThat`, `assertThatThrownBy`), Given-When-Then test structure, descriptive test naming, single-responsibility tests, test independence and isolated state, parameterized tests with `@ValueSource`/`@CsvSource`/`@MethodSource`, Mockito dependency mocking (`@Mock`, `@InjectMocks`, `MockitoExtension`), code coverage guidance (JaCoCo), package-private test visibility, code-splitting strategies (small methods, helper functions), testing anti-patterns (reflection, shared state, hard-coded values, testing implementation details), state management (immutable objects, `@BeforeEach` reset), error handling (`assertThatThrownBy`, exception messages), JSpecify null-safety (`@NullMarked`, `@Nullable`), RIGHT-BICEP coverage principles, A-TRIP test quality characteristics, and CORRECT boundary condition verification. + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any change. If compilation fails, **stop immediately** and do not proceed — compilation failure is a blocking condition. + +**Multi-step scope:** Step 1 validates the project compiles. Step 2 analyzes existing tests and categorizes issues by impact (CRITICAL, MAINTAINABILITY, PERFORMANCE, COVERAGE, RELIABILITY). Step 3 migrates to JUnit 5 with modern annotations. Step 4 adopts AssertJ for expressive assertions. Step 5 restructures tests using Given-When-Then with descriptive naming. Step 6 ensures test independence by eliminating shared state and order dependencies. Step 7 adds parameterized tests and boundary-condition coverage. Step 8 integrates Mockito mocking for external dependencies. + +**Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. + +## Reference + +For detailed guidance, examples, and constraints, see [references/131-java-unit-testing.md](references/131-java-unit-testing.md). diff --git a/skills-generator/src/main/resources/skills/132-skill.md b/skills-generator/src/main/resources/skills/132-skill.md new file mode 100644 index 00000000..52f6d5dc --- /dev/null +++ b/skills-generator/src/main/resources/skills/132-skill.md @@ -0,0 +1,22 @@ +--- +name: 132-java-integration-testing +description: Use when you need to set up, review, or improve Java integration tests — including generating a BaseIntegrationTest.java with WireMock for HTTP stubs, detecting HTTP client infrastructure from import signals, injecting service coordinates dynamically via System.setProperty(), creating WireMock JSON mapping files with bodyFileName, isolating stubs per test method, verifying HTTP interactions, or eliminating anti-patterns such as Mockito-mocked HTTP clients or globally registered WireMock stubs. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Integration testing guidelines + +Set up robust integration-test infrastructure for Java services using WireMock to stub outbound HTTP dependencies. + +**Core areas:** Infrastructure topology detection (scanning imports for `HttpClient`, `feign.*`, `retrofit2.*`, `RestTemplate`, etc.), abstract `BaseIntegrationTest` base class, `WireMockExtension` with `@RegisterExtension`, dynamic port allocation (`dynamicPort()`), `usingFilesUnderClasspath("wiremock")`, `@BeforeAll` + `System.setProperty()` for coordinate propagation, concrete test classes extending the base class, WireMock JSON mapping files (`bodyFileName` referencing `wiremock/files/`), programmatic stub registration via WireMock DSL, per-test stub isolation (register stubs inside each test method), fault injection (503 service unavailable, network latency with `withFixedDelay`), request verification (`WIREMOCK.verify`), `wiremock-standalone` Maven dependency (test scope), and anti-patterns (global `@BeforeAll` stubs causing order-dependent failures, Mockito-mocked HTTP clients bypassing the real HTTP pipeline, hardcoded ports or URLs in property files). + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any change. If compilation fails, **stop immediately** and do not proceed — compilation failure is a blocking condition. + +**Multi-step scope:** Step 1 scans class imports for HTTP client signals and confirms the integration topology with the user (REST → WireMock). Step 2 generates a tailored `BaseIntegrationTest.java` under `src/test/java/{root-package}/` with `WireMockExtension` and `@BeforeAll` coordinate propagation, then lists required Maven dependencies. Step 3 generates starter WireMock JSON mapping files (one per confirmed external service) under `src/test/resources/wiremock/mappings/{service-name}/`. + +**Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. + +## Reference + +For detailed guidance, examples, and constraints, see [references/132-java-integration-testing.md](references/132-java-integration-testing.md). diff --git a/skills-generator/src/main/resources/skills/141-skill.md b/skills-generator/src/main/resources/skills/141-skill.md new file mode 100644 index 00000000..c6aa61fa --- /dev/null +++ b/skills-generator/src/main/resources/skills/141-skill.md @@ -0,0 +1,22 @@ +--- +name: 141-java-refactoring-with-modern-features +description: Use when you need to refactor Java code to adopt modern Java features (Java 8+) — including migrating anonymous classes to lambdas, replacing Iterator loops with Stream API, adopting Optional for null safety, switching from legacy Date/Calendar to java.time, using collection factory methods, migrating to CompletableFuture for async operations, applying text blocks, var inference, or leveraging Java 25 features like flexible constructor bodies and module import declarations. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Modern Java Development Guidelines (Java 8+) + +Identify and apply modern Java (Java 8+) refactoring opportunities to improve readability, maintainability, and performance. + +**Core areas:** Lambda expressions and method references (over anonymous classes), Stream API for declarative collection processing, `Optional` for null-safe APIs, `java.time` API (replacing `Date`/`Calendar`), default interface methods, `var` type inference, unmodifiable collection factory methods (`List.of()`, `Set.of()`, `Map.of()`), `CompletableFuture` for composable async programming, text blocks for multi-line strings, Java 25 Flexible Constructor Bodies (JEP 513), and Java 25 Module Import Declarations (JEP 511). + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any changes. If compilation fails, **stop immediately** — do not proceed until the project compiles successfully. + +**Multi-step scope:** Step 1 validates compilation. Step 2 categorizes refactoring opportunities by impact (CRITICAL, MAINTAINABILITY, PERFORMANCE, READABILITY). Step 3 applies lambda/method-reference conversions. Step 4 replaces imperative loops with Stream API pipelines. Step 5 introduces Optional for null-safe method returns. Step 6 migrates legacy date/time to `java.time`. Step 7 adopts collection factory methods and async CompletableFuture patterns. Step 8 applies text blocks, var, and Java 25 features where applicable. Step 9 runs `./mvnw clean verify` to confirm all tests pass after changes. + +**Before applying changes:** Read the reference for detailed good/bad examples, constraints, and safeguards for each modern Java feature. + +## Reference + +For detailed guidance, examples, and constraints, see [references/141-java-refactoring-with-modern-features.md](references/141-java-refactoring-with-modern-features.md). diff --git a/skills-generator/src/main/resources/skills/142-skill.md b/skills-generator/src/main/resources/skills/142-skill.md new file mode 100644 index 00000000..0f374740 --- /dev/null +++ b/skills-generator/src/main/resources/skills/142-skill.md @@ -0,0 +1,22 @@ +--- +name: 142-java-functional-programming +description: Use when you need to apply functional programming principles in Java — including writing immutable objects and Records, pure functions, functional interfaces, lambda expressions, Stream API pipelines, Optional for null safety, function composition, higher-order functions, pattern matching for instanceof and switch, sealed classes/interfaces for controlled hierarchies, Stream Gatherers for custom operations, currying/partial application, effect boundary separation, and concurrent-safe functional patterns. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Functional Programming rules + +Identify and apply functional programming principles in Java to improve immutability, expressiveness, and maintainability. + +**Core areas:** Immutable objects and Records (JEP 395), pure functions free of side effects, functional interfaces (`Function`, `Predicate`, `Consumer`, `Supplier`) and custom `@FunctionalInterface` types, lambda expressions and method references, Stream API (filter/map/reduce pipelines, parallel streams, `toUnmodifiable*` collectors), `Optional` idiomatic usage (`map`/`flatMap`/`filter`/`orElse*` over `isPresent()`+`get()`), function composition (`andThen`/`compose`), higher-order functions (memoization, currying, partial application), Pattern Matching for `instanceof` and `switch` (Java 21), sealed classes and interfaces (Java 17) for exhaustive domain hierarchies, Switch Expressions (Java 14) for concise multi-way conditionals, Stream Gatherers (JEP 461) for custom intermediate operations, effect-boundary separation (side effects at edges, pure core logic), and immutable collections (`List.of()`, `Collectors.toUnmodifiableList()`). + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any changes. If compilation fails, **stop immediately** — do not proceed until the project compiles successfully. Also verify that the project's `maven-compiler-plugin` source/target supports the Java features being used. + +**Multi-step scope:** Step 1 validates compilation. Step 2 categorizes functional programming opportunities by impact (CRITICAL, MAINTAINABILITY, PERFORMANCE, EXPRESSIVENESS) and area (immutability violations, side effects, imperative patterns, type safety gaps). Step 3 converts mutable objects to immutable Records or final classes. Step 4 extracts pure functions from methods with side effects. Step 5 replaces imperative loops with declarative Stream API operations. Step 6 adopts `Optional` for null-safe method returns and functional chaining. Step 7 applies function composition, currying, and higher-order functions. Step 8 introduces sealed types, pattern matching, and switch expressions for type-safe conditional logic. Step 9 hardens collectors (merge functions for `toMap`, downstream collectors, unmodifiable results). Step 10 runs `./mvnw clean verify` to confirm all tests pass after changes. + +**Before applying changes:** Read the reference for detailed good/bad examples, constraints, and safeguards for each functional programming pattern. + +## Reference + +For detailed guidance, examples, and constraints, see [references/142-java-functional-programming.md](references/142-java-functional-programming.md). diff --git a/skills-generator/src/main/resources/skills/143-skill.md b/skills-generator/src/main/resources/skills/143-skill.md new file mode 100644 index 00000000..c622551f --- /dev/null +++ b/skills-generator/src/main/resources/skills/143-skill.md @@ -0,0 +1,22 @@ +--- +name: 143-java-functional-exception-handling +description: Use when you need to apply functional exception handling best practices in Java — including replacing exception overuse with Optional and VAVR Either types, designing error type hierarchies using sealed classes and enums, implementing monadic error composition pipelines, establishing functional control flow patterns, and reserving exceptions only for truly exceptional system-level failures. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Functional Exception handling Best Practices + +Identify and apply functional exception handling best practices in Java to improve error clarity, maintainability, and performance by eliminating exception overuse in favour of monadic error types. + +**Core areas:** `Optional` for nullable values over throwing `NullPointerException` or `NotFoundException`, VAVR `Either` for predictable business-logic failures, `CompletableFuture` for async error handling, sealed classes and records for rich error type hierarchies with exhaustive pattern matching, enum-based error types for simple failure cases, functional composition with `flatMap`/`map`/`peek`/`peekLeft` for chaining operations that can fail, structured logging at appropriate severity levels (warn/info for business failures, error for system failures), checked vs unchecked exception discipline, and exception chaining with full causal context when exceptions are unavoidable. + +**Prerequisites:** Run `./mvnw validate` before applying any changes. If validation fails, **stop immediately** — do not proceed until the project is in a valid state. Also confirm the VAVR dependency (`io.vavr:vavr`) and SLF4J are present when introducing `Either` types. + +**Multi-step scope:** Step 1 validates the project compiles. Step 2 categorizes error handling opportunities by impact (CRITICAL, MAINTAINABILITY, PERFORMANCE, EXPRESSIVENESS) and area (exception overuse, monadic gaps, error type design, functional composition). Step 3 replaces exceptions for nullable/not-found cases with `Optional`. Step 4 replaces multiple checked exceptions for business logic with VAVR `Either` types. Step 5 designs comprehensive error types — enums for simple cases, sealed classes/records for complex hierarchies. Step 6 implements monadic composition pipelines (`flatMap`/`map`) to replace imperative try-catch control flow. Step 7 aligns logging to functional patterns (warn/info for functional errors, error only for system failures). Step 8 reserves `RuntimeException` subclasses only for programming errors and truly exceptional system failures. Step 9 adds JavaDoc documenting `Either` return types and their left/right values. Step 10 runs `./mvnw clean verify` to confirm all tests pass after changes. + +**Before applying changes:** Read the reference for detailed good/bad examples, constraints, and safeguards for each functional exception handling pattern. + +## Reference + +For detailed guidance, examples, and constraints, see [references/143-java-functional-exception-handling.md](references/143-java-functional-exception-handling.md). diff --git a/skills-generator/src/main/resources/skills/170-skill.md b/skills-generator/src/main/resources/skills/170-skill.md new file mode 100644 index 00000000..2bdf0f65 --- /dev/null +++ b/skills-generator/src/main/resources/skills/170-skill.md @@ -0,0 +1,22 @@ +--- +name: 170-java-documentation +description: Use when you need to generate or improve Java project documentation — including README.md files, package-info.java files, Javadoc enhancements, and Architecture Decision Records (ADRs) — through a modular, step-based interactive process that adapts to your specific documentation needs. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Documentation and ADR Generator with modular step-based configuration + +Generate comprehensive Java project documentation through a modular, step-based interactive process that covers README.md, package-info.java, Javadoc, and Architecture Decision Records (ADRs). + +**Core areas:** README.md generation for single-module and multi-module Maven projects, package-info.java creation with basic/detailed/minimal documentation levels, Javadoc enhancement with comprehensive `@param`/`@return`/`@throws` tags, ADR interactive generation using a conversational question-driven process, file handling strategies (overwrite/add/backup/skip), and final documentation validation with `./mvnw clean compile` and `./mvnw javadoc:javadoc`. + +**Prerequisites:** Run `./mvnw validate` or `mvn validate` before applying any documentation generation. If validation fails, **stop immediately** — do not proceed until all validation errors are resolved. + +**Multi-step scope:** Step 1 assesses documentation preferences through targeted questions (README.md, package-info.java, Javadoc, ADR) to determine which subsequent steps to execute. Step 2 generates README.md files with software descriptions, build instructions, and optional sections based on code analysis — conditionally executed if selected. Step 3 generates package-info.java files for every package in `src/main/java`, with documentation depth matching the user's chosen level — conditionally executed if selected. Step 4 enhances Javadoc for public and protected APIs, adding missing `@param`, `@return`, `@throws` tags, and usage examples — conditionally executed if selected. Step 6 creates ADRs through a 15-question conversational process covering context, decision drivers, considered options, chosen outcome, and consequences — conditionally executed if selected. Step 7 validates all generated documentation by compiling the project and running Javadoc generation, then produces a comprehensive summary of files created, modified, and skipped. + +**Before applying changes:** Read the reference for detailed good/bad examples, constraints, and safeguards for each documentation generation pattern. + +## Reference + +For detailed guidance, examples, and constraints, see [references/170-java-documentation.md](references/170-java-documentation.md). diff --git a/skills-generator/src/main/resources/skills/171-skill.md b/skills-generator/src/main/resources/skills/171-skill.md new file mode 100644 index 00000000..5ef353fc --- /dev/null +++ b/skills-generator/src/main/resources/skills/171-skill.md @@ -0,0 +1,22 @@ +--- +name: 171-java-diagrams +description: Use when you need to generate Java project diagrams — including UML sequence diagrams, UML class diagrams, C4 model diagrams, and UML state machine diagrams — through a modular, step-based interactive process that adapts to your specific visualization needs. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Diagrams Generator with modular step-based configuration + +Generate comprehensive Java project diagrams through a modular, step-based interactive process that covers UML sequence diagrams, UML class diagrams, C4 model diagrams, and UML state machine diagrams using PlantUML syntax. + +**Core areas:** UML sequence diagram generation for application workflows and API interactions, UML class diagram generation for package structure and class relationships, C4 model diagram generation at Context/Container/Component/Code abstraction levels, UML state machine diagram generation for entity lifecycles and business workflows, PlantUML syntax for all diagram types, file organization strategies (single-file, separate-files, or integrated with existing documentation), and final diagram validation with PlantUML syntax checking. + +**Prerequisites:** Run `./mvnw validate` or `mvn validate` before applying any diagram generation. If validation fails, **stop immediately** — do not proceed until all validation errors are resolved. + +**Multi-step scope:** Step 1 assesses diagram preferences through targeted questions to determine which subsequent steps to execute. Step 2 generates UML sequence diagrams for main application flows, API interactions, and complex business logic — conditionally executed if selected. Step 3 generates UML class diagrams for all packages, core business logic packages, or specific packages, showing inheritance, composition, and dependency relationships — conditionally executed if selected. Step 4 generates C4 model diagrams covering System Context, Container, Component, and Code levels — conditionally executed if selected. Step 5 generates UML state machine diagrams for entity lifecycles, business workflows, system behaviors, and user interactions — conditionally executed if selected. Step 6 validates all generated diagrams and produces a comprehensive summary of files created, directory structure, content coverage, and usage instructions for rendering with PlantUML. + +**Before applying changes:** Read the reference for detailed good/bad examples, constraints, and safeguards for each diagram generation pattern. + +## Reference + +For detailed guidance, examples, and constraints, see [references/171-java-diagrams.md](references/171-java-diagrams.md). diff --git a/skills-generator/src/main/resources/skills/172-skill.md b/skills-generator/src/main/resources/skills/172-skill.md new file mode 100644 index 00000000..1afd80d4 --- /dev/null +++ b/skills-generator/src/main/resources/skills/172-skill.md @@ -0,0 +1,22 @@ +--- +name: 172-java-agents +description: Use when you need to generate an AGENTS.md file for a Java repository — covering project conventions, tech stack, file structure, commands, Git workflow, and contributor boundaries — through a modular, step-based interactive process that adapts to your specific project needs. +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# AGENTS.md Generator for Java repositories + +Generate a comprehensive AGENTS.md file for Java repositories through a modular, step-based interactive process that covers role definition, tech stack, file structure, commands, Git workflow, and contributor boundaries. + +**Core areas:** AGENTS.md generation for Java repositories of any complexity, role and expertise definition for AI agents and contributors, tech stack documentation (language, build tool, frameworks, pipelines), file structure mapping with read/write boundaries, command catalogue for build/test/deploy/run workflows, Git workflow conventions (branching strategy, commit message format), and contributor boundaries using ✅ Always do / ⚠️ Ask first / 🚫 Never do formatting. + +**Prerequisites:** No Maven validation is required before generating AGENTS.md. However, review the project structure and existing documentation before starting to provide accurate answers during Step 1. + +**Multi-step scope:** Step 1 assesses project requirements through 6 targeted questions covering role/expertise, tech stack, directory layout, key commands, Git workflow, and contributor boundaries — all questions must be answered in strict order before proceeding. Step 2 generates the AGENTS.md file in the project root by mapping each answer to the corresponding section, handles existing files via overwrite/merge/backup strategies, validates that all 6 sections are present and correctly formatted, and confirms that boundaries use the required ✅ / ⚠️ / 🚫 icons. + +**Before applying changes:** Read the reference for detailed good/bad examples, constraints, and safeguards for each AGENTS.md generation pattern. + +## Reference + +For detailed guidance, examples, and constraints, see [references/172-java-agents.md](references/172-java-agents.md). diff --git a/skills/121-java-object-oriented-design/SKILL.md b/skills/121-java-object-oriented-design/SKILL.md new file mode 100644 index 00000000..7dac11ea --- /dev/null +++ b/skills/121-java-object-oriented-design/SKILL.md @@ -0,0 +1,22 @@ +--- +name: 121-java-object-oriented-design +description: Use when you need to review, improve, or refactor Java code for object-oriented design quality — including applying SOLID, DRY, and YAGNI principles, improving class and interface design, fixing OOP concept misuse (encapsulation, inheritance, polymorphism), identifying and resolving code smells (God Class, Feature Envy, Data Clumps), or improving object creation patterns, method design, and exception handling. Part of the skills-for-java project +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Object-Oriented Design Guidelines + +Review and improve Java code using comprehensive object-oriented design guidelines and refactoring practices. + +**Core areas:** Fundamental design principles (SOLID, DRY, YAGNI), class and interface design (composition over inheritance, immutability, accessibility minimization, accessor methods), core OOP concepts (encapsulation, inheritance, polymorphism), object creation patterns (static factory methods, Builder pattern, Singleton, dependency injection, avoiding unnecessary objects), OOD code smells (God Class, Feature Envy, Inappropriate Intimacy, Refused Bequest, Shotgun Surgery, Data Clumps), method design (parameter validation, defensive copies, careful signatures, empty collections over nulls, Optional usage), and exception handling (checked vs. runtime exceptions, standard exceptions, failure-capture messages, no silent ignoring). + +**Prerequisites:** Run `./mvnw compile` or `mvn compile` before applying any change. If compilation fails, **stop immediately** and do not proceed — compilation failure is a blocking condition. + +**Multi-step scope:** Step 1 validates the project compiles. Step 2 identifies applicable design principles and code smells. Step 3 applies SOLID/DRY/YAGNI improvements. Step 4 addresses class and interface design issues. Step 5 resolves OOP misuse and code smells. Step 6 improves object creation, method design, and exception handling patterns. + +**Before applying changes:** Read the reference for detailed examples, good/bad patterns, and constraints. + +## Reference + +For detailed guidance, examples, and constraints, see [references/121-java-object-oriented-design.md](references/121-java-object-oriented-design.md). diff --git a/skills/121-java-object-oriented-design/references/121-java-object-oriented-design.md b/skills/121-java-object-oriented-design/references/121-java-object-oriented-design.md new file mode 100644 index 00000000..376e93f1 --- /dev/null +++ b/skills/121-java-object-oriented-design/references/121-java-object-oriented-design.md @@ -0,0 +1,2504 @@ +--- +name: 121-java-object-oriented-design +description: Use when you need to review, improve, or refactor Java code for object-oriented design quality — applying SOLID, DRY, and YAGNI principles, improving class and interface design, fixing OOP misuse, and resolving common code smells such as God Class, Feature Envy, and Data Clumps. +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.12.0-SNAPSHOT +--- +# Java Object-Oriented Design Guidelines + +## Role + +You are a Senior software engineer with extensive experience in Java software development + +## Goal + +This document provides comprehensive guidelines for robust Java object-oriented design and refactoring. It emphasizes core principles like SOLID, DRY, and YAGNI, best practices for class and interface design including favoring composition over inheritance and designing for immutability. +The rules also cover mastering encapsulation, inheritance, and polymorphism, and finally, identifying and refactoring common object-oriented design code smells such as God Classes, Feature Envy, and Data Clumps to promote maintainable, flexible, and understandable code. + +### Implementing These Principles + +These guidelines are built upon the following core principles: + +1. **Adherence to Fundamental Design Principles**: Embrace foundational principles like SOLID, DRY, and YAGNI. These principles are key to building systems that are robust, maintainable, flexible, and easy to understand. +2. **Effective Class and Interface Design**: Employ best practices for designing classes and interfaces. This includes favoring composition over inheritance to achieve flexibility, programming to an interface rather than an implementation to promote loose coupling, keeping classes small and focused on a single responsibility, and designing for immutability where appropriate to enhance simplicity and thread-safety. +3. **Mastery of Core OOP Concepts**: Thoroughly understand and correctly apply the pillars of object-oriented programming: +* **Encapsulation**: Protect internal state and expose behavior through well-defined interfaces. +* **Inheritance**: Model true "is-a" relationships, ensuring subclasses are substitutable for their base types (Liskov Substitution Principle). +* **Polymorphism**: Allow objects of different types to respond to the same message in their own way, simplifying client code. +4. **Proactive Code Smell Management**: Develop the ability to identify common object-oriented design "code smells" (e.g., God Class, Feature Envy, Data Clumps, Refused Bequest). Recognizing and refactoring these smells is crucial for improving the long-term health, maintainability, and clarity of the codebase. +5. **Rigorous Safety and Validation**: NEVER apply any design recommendations without first ensuring the project compiles successfully. All refactoring must be validated through compilation checks and testing to prevent introducing regressions or breaking existing functionality. + +## Constraints + +Before applying any recommendations, ensure the project is in a valid state by running Maven compilation. Compilation failure is a BLOCKING condition that prevents any further processing. + +- **MANDATORY**: Run `./mvnw compile` or `mvn compile` before applying any change +- **PREREQUISITE**: Project must compile successfully and pass basic validation checks before any optimization +- **CRITICAL SAFETY**: If compilation fails, IMMEDIATELY STOP and DO NOT CONTINUE with any recommendations +- **BLOCKING CONDITION**: Compilation errors must be resolved by the user before proceeding with any object-oriented design improvements +- **NO EXCEPTIONS**: Under no circumstances should design recommendations be applied to a project that fails to compile + +## Examples + +### Table of contents + +- Example 1: Apply Fundamental Software Design Principles +- Example 2: Single Responsibility Principle (SRP) +- Example 3: Open/Closed Principle (OCP) +- Example 4: Liskov Substitution Principle (LSP) +- Example 5: Interface Segregation Principle (ISP) +- Example 6: Dependency Inversion Principle (DIP) +- Example 7: DRY (Don't Repeat Yourself) +- Example 8: YAGNI (You Ain't Gonna Need It) +- Example 9: Design Well-Structured and Maintainable Classes and Interfaces +- Example 10: Effectively Utilize Core Object-Oriented Concepts +- Example 11: Encapsulation +- Example 12: Inheritance +- Example 13: Polymorphism +- Example 14: Recognize and Address Common OOD Code Smells +- Example 15: Large Class / God Class +- Example 16: Feature Envy +- Example 17: Inappropriate Intimacy +- Example 18: Refused Bequest +- Example 19: Shotgun Surgery +- Example 20: Data Clumps +- Example 21: Creating and Destroying Objects +- Example 22: Consider Static Factory Methods Instead of Constructors +- Example 23: Consider a Builder When Faced with Many Constructor Parameters +- Example 24: Enforce the Singleton Property with a Private Constructor or an Enum Type +- Example 25: Prefer Dependency Injection to Hardwiring Resources +- Example 26: Avoid Creating Unnecessary Objects +- Example 27: Classes and Interfaces Best Practices +- Example 28: Minimize the Accessibility of Classes and Members +- Example 29: In Public Classes, Use Accessor Methods, Not Public Fields +- Example 30: Minimize Mutability +- Example 31: Favor Composition Over Inheritance +- Example 32: Design and Document for Inheritance or Else Prohibit It +- Example 33: Enums and Annotations +- Example 34: Use Enums Instead of Int Constants +- Example 35: Use Instance Fields Instead of Ordinals +- Example 36: Use EnumSet Instead of Bit Fields +- Example 37: Use EnumMap Instead of Ordinal Indexing +- Example 38: Consistently Use the Override Annotation +- Example 39: Method Design +- Example 40: Check Parameters for Validity +- Example 41: Make Defensive Copies When Needed +- Example 42: Design Method Signatures Carefully +- Example 43: Return Empty Collections or Arrays, Not Nulls +- Example 44: Return Optionals Judiciously +- Example 45: Exception Handling +- Example 46: Use Exceptions Only for Exceptional Conditions +- Example 47: Use Checked Exceptions for Recoverable Conditions and Runtime Exceptions for Programming Errors +- Example 48: Favor the Use of Standard Exceptions +- Example 49: Include Failure-Capture Information in Detail Messages +- Example 50: Don't Ignore Exceptions + +### Example 1: Apply Fundamental Software Design Principles + +Title: Apply Fundamental Software Design Principles +Description: Core principles like SOLID, DRY, and YAGNI are foundational to good object-oriented design, leading to more robust, maintainable, and understandable systems. + +### Example 2: Single Responsibility Principle (SRP) + +Title: A class should have one, and only one, reason to change. +Description: This means a class should only have one job or primary responsibility. If a class handles multiple responsibilities, changes to one responsibility might inadvertently affect others. + +**Good example:** + +```java +// Good: Separate responsibilities +class UserData { + private String name; + private String email; + // constructor, getters + public UserData(String name, String email) { this.name = name; this.email = email; } + public String getName() { return name; } + public String getEmail() { return email; } +} + +class UserPersistence { + public void saveUser(UserData user) { + System.out.println("Saving user " + user.getName() + " to database."); + // Database saving logic + } +} + +class UserEmailer { + public void sendWelcomeEmail(UserData user) { + System.out.println("Sending welcome email to " + user.getEmail()); + // Email sending logic + } +} +``` + +**Bad example:** + +```java +// Bad: User class with multiple responsibilities +class User { + private String name; + private String email; + + public User(String name, String email) { this.name = name; this.email = email; } + + public String getName() { return name; } + public String getEmail() { return email; } + + public void saveToDatabase() { + System.out.println("Saving user " + name + " to database."); + // Database logic mixed in + } + + public void sendWelcomeEmail() { + System.out.println("Sending welcome email to " + email); + // Email logic mixed in + } + // If email sending changes, or DB logic changes, this class needs to change. +} +``` + +### Example 3: Open/Closed Principle (OCP) + +Title: Software entities should be open for extension but closed for modification. +Description: You should be able to add new functionality without changing existing, tested code. This is often achieved using interfaces, abstract classes, and polymorphism. + +**Good example:** + +```java +interface Shape { + double calculateArea(); +} + +class Rectangle implements Shape { + private double width, height; + public Rectangle(double w, double h) { width=w; height=h; } + @Override public double calculateArea() { return width * height; } +} + +class Circle implements Shape { + private double radius; + public Circle(double r) { radius=r; } + @Override public double calculateArea() { return Math.PI * radius * radius; } +} + +// New shapes (e.g., Triangle) can be added by implementing Shape +// without modifying existing Shape, Rectangle, Circle, or AreaCalculator. +class AreaCalculator { + public double getTotalArea(List shapes) { + return shapes.stream().mapToDouble(Shape::calculateArea).sum(); + } +} +``` + +**Bad example:** + +```java +// Bad: AreaCalculator needs modification for new shapes +class AreaCalculatorBad { + public double calculateRectangleArea(Rectangle rect) { return rect.width * rect.height; } + public double calculateCircleArea(Circle circ) { return Math.PI * circ.radius * circ.radius; } + // If a Triangle class is added, this class must be modified to add calculateTriangleArea(). +} +class Rectangle { public double width, height; /* ... */ } +class Circle { public double radius; /* ... */ } +``` + +### Example 4: Liskov Substitution Principle (LSP) + +Title: Subtypes must be substitutable for their base types. +Description: Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program or causing unexpected behavior. + +**Good example:** + +```java +interface Bird { + void move(); +} + +class FlyingBird implements Bird { + public void fly() { System.out.println("Flying high!"); } + @Override public void move() { fly(); } +} + +class Sparrow extends FlyingBird { /* Can fly */ } + +class Ostrich implements Bird { // Ostrich is a Bird but doesn't fly in the typical sense + public void runFast() { System.out.println("Running fast on the ground!"); } + @Override public void move() { runFast(); } +} + +public class BirdLSPExample { + public static void makeBirdMove(Bird bird) { + bird.move(); // Works correctly for Sparrow (flies) and Ostrich (runs) + } + public static void main(String args) { + makeBirdMove(new Sparrow()); + makeBirdMove(new Ostrich()); + } +} +``` + +**Bad example:** + +```java +// Bad: Violating LSP +class Bird { + public void fly() { System.out.println("Bird is flying."); } +} + +class Penguin extends Bird { + @Override + public void fly() { + // Penguins can't fly, so this method might do nothing or throw an exception. + // This violates LSP because a Penguin can't simply replace a generic Bird where fly() is expected. + throw new UnsupportedOperationException("Penguins can't fly."); + } + public void swim() { System.out.println("Penguin is swimming."); } +} + +public class BirdLSPViolation { + public static void letTheBirdFly(Bird bird) { + bird.fly(); // This will crash if bird is a Penguin + } + public static void main(String args) { + try { + letTheBirdFly(new Penguin()); + } catch (UnsupportedOperationException e) { + System.err.println(e.getMessage()); + } + } +} +``` + +### Example 5: Interface Segregation Principle (ISP) + +Title: Clients should not be forced to depend on interfaces they do not use. +Description: It's better to have many small, specific interfaces (role interfaces) than one large, general-purpose interface. This prevents classes from having to implement methods they don't need. + +**Good example:** + +```java +// Good: Segregated interfaces +interface Worker { + void work(); +} + +interface Eater { + void eat(); +} + +class HumanWorker implements Worker, Eater { + @Override public void work() { System.out.println("Human working."); } + @Override public void eat() { System.out.println("Human eating."); } +} + +class RobotWorker implements Worker { + @Override public void work() { System.out.println("Robot working efficiently."); } + // RobotWorker does not need to implement eat() +} +``` + +**Bad example:** + +```java +// Bad: Fat interface +interface IWorkerAndEater { + void work(); + void eat(); // All implementers must provide eat(), even if they don't eat. +} + +class Human implements IWorkerAndEater { + @Override public void work() { /* ... */ } + @Override public void eat() { /* ... */ } +} + +class Robot implements IWorkerAndEater { + @Override public void work() { System.out.println("Robot working."); } + @Override public void eat() { + // Robots don't eat. This method is forced and likely empty or throws exception. + throw new UnsupportedOperationException("Robots don't eat."); + } +} +``` + +### Example 6: Dependency Inversion Principle (DIP) + +Title: High-level modules should not depend on low-level modules. Both should depend on abstractions. +Description: Abstractions (e.g., interfaces) should not depend on details. Details (concrete implementations) should depend on abstractions. This promotes loose coupling. + +**Good example:** + +```java +// Abstraction +interface MessageSender { + void sendMessage(String message); +} + +// Low-level module (detail) +class EmailSender implements MessageSender { + @Override public void sendMessage(String message) { System.out.println("Email sent: " + message); } +} + +// Low-level module (detail) +class SMSSender implements MessageSender { + @Override public void sendMessage(String message) { System.out.println("SMS sent: " + message); } +} + +// High-level module +class NotificationService { + private final MessageSender sender; // Depends on abstraction + + public NotificationService(MessageSender sender) { // Dependency injected + this.sender = sender; + } + + public void notify(String message) { + sender.sendMessage(message); + } +} + +public class DIPExample { + public static void main(String args) { + NotificationService emailNotifier = new NotificationService(new EmailSender()); + emailNotifier.notify("Hello via Email!"); + + NotificationService smsNotifier = new NotificationService(new SMSSender()); + smsNotifier.notify("Hello via SMS!"); + } +} +``` + +**Bad example:** + +```java +// Bad: High-level module depends directly on low-level module +class EmailerBad { + public void sendEmail(String message) { System.out.println("Email sent: " + message); } +} + +class NotificationServiceBad { + private EmailerBad emailer; // Direct dependency on concrete EmailerBad + + public NotificationServiceBad() { + this.emailer = new EmailerBad(); // Instantiates concrete class + } + + public void sendNotification(String message) { + emailer.sendEmail(message); // Tightly coupled + } + // If we want to use SMSSender, NotificationServiceBad needs to be changed. +} +``` + +### Example 7: DRY (Don't Repeat Yourself) + +Title: Avoid duplication of code. +Description: Every piece of knowledge or logic must have a single, unambiguous, authoritative representation within a system. Use methods, classes, inheritance, or composition to centralize and reuse code. + +**Good example:** + +```java +class CalculationUtils { + // Centralized validation logic + public static void validatePositive(double value, String name) { + if (value <= 0) { + throw new IllegalArgumentException(name + " must be positive."); + } + } +} + +class RectangleArea { + public double calculate(double width, double height) { + CalculationUtils.validatePositive(width, "Width"); + CalculationUtils.validatePositive(height, "Height"); + return width * height; + } +} + +class CircleVolume { + public double calculate(double radius, double height) { + CalculationUtils.validatePositive(radius, "Radius"); + CalculationUtils.validatePositive(height, "Height"); + return Math.PI * radius * radius * height; + } +} +``` + +**Bad example:** + +```java +// Bad: Duplicated validation logic +class RectangleAreaBad { + public double calculate(double width, double height) { + if (width <= 0) throw new IllegalArgumentException("Width must be positive."); // Duplicated + if (height <= 0) throw new IllegalArgumentException("Height must be positive."); // Duplicated + return width * height; + } +} + +class CircleVolumeBad { + public double calculate(double radius, double height) { + if (radius <= 0) throw new IllegalArgumentException("Radius must be positive."); // Duplicated + if (height <= 0) throw new IllegalArgumentException("Height must be positive."); // Duplicated + return Math.PI * radius * radius * height; + } +} +``` + +### Example 8: YAGNI (You Ain't Gonna Need It) + +Title: Implement features only when you actually need them. +Description: Avoid implementing functionality based on speculation that it might be needed in the future. This helps prevent over-engineering and keeps the codebase simpler and more focused on current requirements. + +**Good example:** + +```java +// Good: Simple class meeting current needs +class ReportGenerator { + public String generateSimpleReport(List data) { + System.out.println("Generating simple report."); + return "Report: " + String.join(", ", data); + } + // If PDF export is needed later, it can be added then. + // No need to implement generatePdfReport, generateExcelReport etc. upfront. +} +``` + +**Bad example:** + +```java +// Bad: Over-engineered with features not currently needed +class ReportGeneratorOverkill { + public String generateHtmlReport(List data) { /* ... */ return "html";} + public byte[] generatePdfReport(List data) { + System.out.println("Generating PDF report (not actually used yet)."); + return new byte[0]; + } + public byte[] generateExcelReport(List data) { + System.out.println("Generating Excel report (not actually used yet)."); + return new byte[0]; + } + // Current requirement is only for HTML, but PDF and Excel are added "just in case". +} +``` + +### Example 9: Design Well-Structured and Maintainable Classes and Interfaces + +Title: Design Well-Structured and Maintainable Classes and Interfaces +Description: Good class and interface design is crucial for building flexible and understandable OOD systems. Favor composition over inheritance, program to interfaces rather than implementations, keep classes small and focused, and design for immutability where appropriate. Use clear, descriptive naming conventions. + +**Good example:** + +```java +// Interface (Abstraction) +interface Engine { + void start(); + void stop(); +} + +// Concrete Implementations +class PetrolEngine implements Engine { + @Override public void start() { System.out.println("Petrol engine started."); } + @Override public void stop() { System.out.println("Petrol engine stopped."); } +} + +class ElectricEngine implements Engine { + @Override public void start() { System.out.println("Electric engine silently started."); } + @Override public void stop() { System.out.println("Electric engine silently stopped."); } +} + +// Class using Composition and Programming to an Interface +class Car { + private final Engine engine; // Depends on Engine interface (abstraction) + private final String modelName; + + // Engine is injected (composition) + public Car(String modelName, Engine engine) { + this.modelName = modelName; + this.engine = engine; + } + + public void startCar() { + System.out.print(modelName + ": "); + engine.start(); + } + + public void stopCar() { + System.out.print(modelName + ": "); + engine.stop(); + } + + public String getModelName(){ return modelName; } +} + +public class ClassDesignExample { + public static void main(String args) { + Car petrolCar = new Car("SedanX", new PetrolEngine()); + Car electricCar = new Car("EVMax", new ElectricEngine()); + + petrolCar.startCar(); + electricCar.startCar(); + petrolCar.stopCar(); + electricCar.stopCar(); + } +} +``` + +**Bad example:** + +```java +// Bad: Tight coupling, not programming to an interface +class BadCar { + private final BadPetrolEngine engine; // Direct dependency on concrete BadPetrolEngine + public BadCar() { + this.engine = new BadPetrolEngine(); // Instantiates concrete class + } + public void start() { engine.startPetrol(); } + // If we want an electric car, this class needs significant changes or a new similar class. +} +class BadPetrolEngine { public void startPetrol() { System.out.println("Bad petrol engine starts."); } } +``` + +### Example 10: Effectively Utilize Core Object-Oriented Concepts + +Title: Effectively Utilize Core Object-Oriented Concepts +Description: Encapsulation, Inheritance, and Polymorphism are the three pillars of object-oriented programming. + +### Example 11: Encapsulation + +Title: Protect Internal State and Implementation Details +Description: Hide the internal state (fields) and implementation details of an object from the outside world. Expose a well-defined public interface (methods) for interacting with the object. Use access modifiers effectively to control visibility and protect invariants. + +**Good example:** + +```java +class BankAccount { + private double balance; // Encapsulated: internal state is private + private final String accountNumber; + + public BankAccount(String accountNumber, double initialBalance) { + this.accountNumber = accountNumber; + if (initialBalance < 0) throw new IllegalArgumentException("Initial balance cannot be negative."); + this.balance = initialBalance; + } + + // Public interface to interact with the balance + public void deposit(double amount) { + if (amount <= 0) throw new IllegalArgumentException("Deposit amount must be positive."); + this.balance += amount; + System.out.println("Deposited: " + amount + ", New Balance: " + this.balance); + } + + public void withdraw(double amount) { + if (amount <= 0) throw new IllegalArgumentException("Withdrawal amount must be positive."); + if (amount > this.balance) throw new IllegalArgumentException("Insufficient funds."); + this.balance -= amount; + System.out.println("Withdrew: " + amount + ", New Balance: " + this.balance); + } + + public double getBalance() { // Controlled access to balance + return this.balance; + } + public String getAccountNumber() { return this.accountNumber; } +} +``` + +**Bad example:** + +```java +// Bad: Poor encapsulation, exposing internal state +class UnsafeBankAccount { + public double balance; // Public field: internal state exposed and can be freely modified + public String accountNumber; + + public UnsafeBankAccount(String accNum, double initial) { this.accountNumber = accNum; this.balance = initial; } + // No methods to control how balance is changed, invariants can be broken. +} +public class BadEncapsulationExample { + public static void main(String args) { + UnsafeBankAccount account = new UnsafeBankAccount("123", 100.0); + account.balance = -500.0; // Direct modification, potentially breaking business rules + System.out.println("Unsafe balance: " + account.balance); + } +} +``` + +### Example 12: Inheritance + +Title: Model "is-a" Relationships and Ensure LSP +Description: Use inheritance to model true "is-a" relationships, where a subclass is a more specific type of its superclass. Ensure that the Liskov Substitution Principle (LSP) is followed: subclasses must be substitutable for their base types without altering the correctness of the program. + +**Good example:** + +```java +abstract class Animal { + private String name; + public Animal(String name) { this.name = name; } + public String getName() { return name; } + public abstract void makeSound(); // Abstract method for polymorphism +} + +class Dog extends Animal { // Dog IS-A Animal + public Dog(String name) { super(name); } + @Override public void makeSound() { System.out.println(getName() + " says: Woof!"); } + public void fetch() { System.out.println(getName() + " is fetching."); } +} + +class Cat extends Animal { // Cat IS-A Animal + public Cat(String name) { super(name); } + @Override public void makeSound() { System.out.println(getName() + " says: Meow!"); } + public void purr() { System.out.println(getName() + " is purring."); } +} + +public class InheritanceExample { + public static void main(String args) { + Animal myDog = new Dog("Buddy"); + Animal myCat = new Cat("Whiskers"); + myDog.makeSound(); + myCat.makeSound(); + // ((Dog)myDog).fetch(); // Can cast if sure of type to access specific methods + } +} +``` + +**Bad example:** + +```java +// Bad: Incorrect "is-a" relationship using composition instead +class Window { + public void open() { System.out.println("Window opened."); } + public void close() { System.out.println("Window closed."); } +} + +class BetterCarDoor { + private WindowComponent window = new WindowComponent(); + public void openDoor() { System.out.println("Car door opened."); } + public void closeDoor() { System.out.println("Car door closed."); } + public void openWindow() { window.open(); } + public void closeWindow() { window.close(); } + static class WindowComponent { /* Similar to Window */ + public void open() {System.out.println("Car window rolling down.");} + public void close() {System.out.println("Car window rolling up.");} + } +} +``` + +### Example 13: Polymorphism + +Title: Enable Objects to Respond to the Same Message Differently +Description: Polymorphism allows objects of different classes (that share a common superclass or interface) to respond to the same message (method call) in their own specific ways. It simplifies client code, as it can interact with different types of objects through a common interface without needing to know their concrete types. + +**Good example:** + +```java +interface Drawable { + void draw(); +} + +class CircleShape implements Drawable { + @Override public void draw() { System.out.println("Drawing a Circle: O"); } +} + +class SquareShape implements Drawable { + @Override public void draw() { System.out.println("Drawing a Square: □"); } +} + +class TriangleShape implements Drawable { + @Override public void draw() { System.out.println("Drawing a Triangle: /\\"); } +} + +public class PolymorphismExample { + public static void drawShapes(List shapes) { + for (Drawable shape : shapes) { + shape.draw(); // Polymorphic call: actual method executed depends on shape's concrete type + } + } + public static void main(String args) { + List myShapes = List.of( + new CircleShape(), + new SquareShape(), + new TriangleShape() + ); + drawShapes(myShapes); + } +} +``` + +**Bad example:** + +```java +// Bad: Lacking polymorphism, using type checking and casting +class ShapeDrawer { + public void drawSpecificShape(Object shape) { + if (shape instanceof CircleShapeBad) { + ((CircleShapeBad) shape).drawCircle(); + } else if (shape instanceof SquareShapeBad) { + ((SquareShapeBad) shape).drawSquare(); + } else if (shape instanceof TriangleShapeBad) { + ((TriangleShapeBad) shape).drawTriangle(); + } else { + System.out.println("Unknown shape type."); + } + // This is not polymorphic. Adding new shapes requires modifying this method. + } +} + +class CircleShapeBad { public void drawCircle() { System.out.println("Drawing Circle (Bad)."); } } +class SquareShapeBad { public void drawSquare() { System.out.println("Drawing Square (Bad)."); } } +class TriangleShapeBad { public void drawTriangle() { System.out.println("Drawing Triangle (Bad)."); } } +``` + +### Example 14: Recognize and Address Common OOD Code Smells + +Title: Recognize and Address Common OOD Code Smells +Description: Code smells are symptoms of potential underlying problems in the design. Recognizing and refactoring them can significantly improve code quality. + +### Example 15: Large Class / God Class + +Title: A class that knows or does too much. +Description: Such classes violate SRP and are hard to understand, maintain, and test. Consider breaking them down into smaller, more focused classes. + +### Example 16: Feature Envy + +Title: A method that seems more interested in a class other than the one it actually is in. +Description: This often means the method is using data from another class more than its own. Consider moving the method to the class it's "envious" of, or introduce a new class to mediate. + +**Good example:** + +```java +class Customer { + private String name; + private Address address; + public Customer(String name, Address address) { this.name = name; this.address = address; } + public String getFullAddressDetails() { // Method operates on its own Address object + return address.getStreet() + ", " + address.getCity() + ", " + address.getZipCode(); + } +} +class Address { + private String street, city, zipCode; + public Address(String s, String c, String z) { street=s; city=c; zipCode=z; } + public String getStreet() { return street; } + public String getCity() { return city; } + public String getZipCode() { return zipCode; } +} +``` + +**Bad example:** + +```java +class Order { + private double amount; + private Customer customer; // Has a Customer + public Order(double amount, Customer customer) { this.amount = amount; this.customer = customer; } + + // Bad: This method is more interested in Customer's Address than Order itself + public String getCustomerShippingLabel() { + Address addr = customer.getAddress(); // Assuming Customer has getAddress() + return customer.getName() + "\n" + addr.getStreet() + + "\n" + addr.getCity() + ", " + addr.getZipCode(); + // Better: Move this logic to Customer class as getShippingLabel() or similar. + } +} +``` + +### Example 17: Inappropriate Intimacy + +Title: Classes that spend too much time delving into each other's private parts. +Description: This indicates tight coupling and poor encapsulation. Classes should interact through well-defined public interfaces, not by accessing internal implementation details of others. + +**Bad example:** + +```java +class ServiceA { + public int internalCounter = 0; // Public field, bad + public void doSomething() { internalCounter++; } +} +class ServiceB { + public void manipulateServiceA(ServiceA serviceA) { + // Bad: Directly accessing and modifying internal state of ServiceA + serviceA.internalCounter = 100; + System.out.println("ServiceA counter directly set to: " + serviceA.internalCounter); + // Better: ServiceA should have a method like resetCounter(int value) if this is valid behavior. + } +} +``` + +### Example 18: Refused Bequest + +Title: A subclass uses only some of the methods and properties inherited from its parents. +Description: This might indicate a violation of LSP or an incorrect inheritance hierarchy. The subclass might not truly be a substitutable type of the superclass. + +### Example 19: Shotgun Surgery + +Title: When a single conceptual change requires modifications in many different classes. +Description: This often indicates that a single responsibility has been spread too thinly across multiple classes, leading to high coupling and difficulty in making changes. + +### Example 20: Data Clumps + +Title: Bunches of data items that regularly appear together in multiple places. +Description: These data clumps often represent a missing concept that should be encapsulated into its own object or record. + +**Good example:** + +```java +// Good: Encapsulating related data into a Range object +record DateRange(LocalDate start, LocalDate end) { + public DateRange { + if (start.isAfter(end)) throw new IllegalArgumentException("Start date must be before end date."); + } +} + +class EventScheduler { + public void scheduleEvent(String eventName, DateRange range) { + System.out.println("Scheduling " + eventName + " from " + range.start() + " to " + range.end()); + } + public boolean isDateInRange(LocalDate date, DateRange range) { + return !date.isBefore(range.start()) && !date.isAfter(range.end()); + } +} +``` + +**Bad example:** + +```java +// Bad: Data clump (startDay, startMonth, startYear, endDay, endMonth, endYear) passed around +class EventSchedulerBad { + public void scheduleEvent(String eventName, + int startDay, int startMonth, int startYear, + int endDay, int endMonth, int endYear) { + // ... logic using these separate date parts ... + System.out.println("Scheduling event with many date parameters."); + } + public boolean checkOverlap(int sDay1, int sMon1, int sYr1, int eDay1, int eMon1, int eYr1, + int sDay2, int sMon2, int sYr2, int eDay2, int eMon2, int eYr2) { + // ... complex logic with many parameters ... + return false; + } + // This pattern of passing around many related date parts is a data clump. +} +``` + +### Example 21: Creating and Destroying Objects + +Title: Best Practices for Object Creation and Destruction +Description: Effective object creation and destruction patterns improve code clarity, performance, and maintainability. These practices help avoid common pitfalls and leverage Java's capabilities effectively. + +### Example 22: Consider Static Factory Methods Instead of Constructors + +Title: Use static factory methods to provide more flexibility than constructors +Description: Static factory methods offer advantages like descriptive names, ability to return existing instances, and flexibility in return types. + +**Good example:** + +```java +public class BigInteger { + // Static factory method with descriptive name + public static BigInteger valueOf(long val) { + if (val == 0) return ZERO; // Return cached instance + if (val > 0 && val <= MAX_CONSTANT) return posConst[(int) val]; + return new BigInteger(val); + } + + // Private constructor + private BigInteger(long val) { /* implementation */ } + + private static final BigInteger ZERO = new BigInteger(0); + private static final BigInteger[] posConst = new BigInteger[MAX_CONSTANT + 1]; +} + +// Usage with clear intent +BigInteger zero = BigInteger.valueOf(0); // Clear what we're creating +BigInteger hundred = BigInteger.valueOf(100); +``` + +**Bad example:** + +```java +public class BigInteger { + // Only constructor available - less flexible + public BigInteger(long val) { /* implementation */ } + + // Client code is less clear + BigInteger zero = new BigInteger(0); // Not clear this could be cached + BigInteger hundred = new BigInteger(100); // Creates new instance every time +} +``` + +### Example 23: Consider a Builder When Faced with Many Constructor Parameters + +Title: Use the Builder pattern for classes with multiple optional parameters +Description: The Builder pattern provides a readable alternative to telescoping constructors and is safer than JavaBeans pattern. + +**Good example:** + +```java +public class NutritionFacts { + private final int servingSize; + private final int servings; + private final int calories; + private final int fat; + private final int sodium; + private final int carbohydrate; + + public static class Builder { + // Required parameters + private final int servingSize; + private final int servings; + + // Optional parameters - initialized to default values + private int calories = 0; + private int fat = 0; + private int sodium = 0; + private int carbohydrate = 0; + + public Builder(int servingSize, int servings) { + this.servingSize = servingSize; + this.servings = servings; + } + + public Builder calories(int val) { calories = val; return this; } + public Builder fat(int val) { fat = val; return this; } + public Builder sodium(int val) { sodium = val; return this; } + public Builder carbohydrate(int val) { carbohydrate = val; return this; } + + public NutritionFacts build() { + return new NutritionFacts(this); + } + } + + private NutritionFacts(Builder builder) { + servingSize = builder.servingSize; + servings = builder.servings; + calories = builder.calories; + fat = builder.fat; + sodium = builder.sodium; + carbohydrate = builder.carbohydrate; + } +} + +// Usage - readable and flexible +NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) + .calories(100) + .sodium(35) + .carbohydrate(27) + .build(); +``` + +**Bad example:** + +```java +// Telescoping constructor pattern - hard to read and error-prone +public class NutritionFacts { + private final int servingSize; + private final int servings; + private final int calories; + private final int fat; + private final int sodium; + private final int carbohydrate; + + public NutritionFacts(int servingSize, int servings) { + this(servingSize, servings, 0); + } + + public NutritionFacts(int servingSize, int servings, int calories) { + this(servingSize, servings, calories, 0); + } + + public NutritionFacts(int servingSize, int servings, int calories, int fat) { + this(servingSize, servings, calories, fat, 0); + } + + public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { + this(servingSize, servings, calories, fat, sodium, 0); + } + + public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { + this.servingSize = servingSize; + this.servings = servings; + this.calories = calories; + this.fat = fat; + this.sodium = sodium; + this.carbohydrate = carbohydrate; + } +} + +// Usage - confusing parameter order, easy to make mistakes +NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27); // What do these numbers mean? +``` + +### Example 24: Enforce the Singleton Property with a Private Constructor or an Enum Type + +Title: Use enum or private constructor with static field for singletons +Description: Enum-based singletons are the best way to implement singletons, providing serialization and reflection safety. + +**Good example:** + +```java +// Enum singleton - preferred approach +public enum DatabaseConnection { + INSTANCE; + + public void connect() { + System.out.println("Connecting to database..."); + } + + public void executeQuery(String query) { + System.out.println("Executing: " + query); + } +} + +// Alternative: Static field with private constructor +public class Logger { + private static final Logger INSTANCE = new Logger(); + + private Logger() { /* private constructor */ } + + public static Logger getInstance() { + return INSTANCE; + } + + public void log(String message) { + System.out.println("LOG: " + message); + } +} + +// Usage +DatabaseConnection.INSTANCE.connect(); +Logger.getInstance().log("Application started"); +``` + +**Bad example:** + +```java +// Not thread-safe singleton +public class BadSingleton { + private static BadSingleton instance; + + private BadSingleton() {} + + public static BadSingleton getInstance() { + if (instance == null) { // Race condition possible + instance = new BadSingleton(); + } + return instance; + } +} +``` + +### Example 25: Prefer Dependency Injection to Hardwiring Resources + +Title: Use dependency injection instead of hardcoded dependencies +Description: Classes should not create their dependencies directly but receive them from external sources, improving testability and flexibility. + +**Good example:** + +```java +public class SpellChecker { + private final Lexicon dictionary; + + // Dependency injected through constructor + public SpellChecker(Lexicon dictionary) { + this.dictionary = Objects.requireNonNull(dictionary); + } + + public boolean isValid(String word) { + return dictionary.contains(word); + } +} + +interface Lexicon { + boolean contains(String word); +} + +class EnglishLexicon implements Lexicon { + public boolean contains(String word) { + // English dictionary lookup + return true; + } +} + +// Usage - flexible and testable +Lexicon englishDict = new EnglishLexicon(); +SpellChecker checker = new SpellChecker(englishDict); +``` + +**Bad example:** + +```java +// Hardwired dependency - inflexible and hard to test +public class SpellChecker { + private static final Lexicon dictionary = new EnglishLexicon(); // Hardcoded + + private SpellChecker() {} // Noninstantiable + + public static boolean isValid(String word) { + return dictionary.contains(word); + } +} +``` + +### Example 26: Avoid Creating Unnecessary Objects + +Title: Reuse objects when possible to improve performance +Description: Object creation can be expensive. Reuse immutable objects and avoid creating objects in loops when possible. + +**Good example:** + +```java +public class DateUtils { + // Reuse expensive objects + private static final DateTimeFormatter FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public String formatDate(LocalDate date) { + return FORMATTER.format(date); // Reuse formatter + } + + // Use primitives when possible + public boolean isEven(int number) { + return number % 2 == 0; // No object creation + } +} +``` + +**Bad example:** + +```java +public class DateUtils { + public String formatDate(LocalDate date) { + // Creates new formatter every time - expensive + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + return formatter.format(date); + } + + // Unnecessary autoboxing + public boolean isEven(Integer number) { + return number % 2 == 0; // Creates Integer objects + } +} +``` + +### Example 27: Classes and Interfaces Best Practices + +Title: Design Classes and Interfaces for Maximum Effectiveness +Description: Well-designed classes and interfaces are the foundation of maintainable and robust Java applications. These practices ensure proper encapsulation, inheritance, and interface design. + +### Example 28: Minimize the Accessibility of Classes and Members + +Title: Use the most restrictive access level that makes sense +Description: Proper encapsulation hides implementation details and allows for easier maintenance and evolution of code. + +**Good example:** + +```java +public class BankAccount { + private final String accountNumber; // Private - implementation detail + private double balance; // Private - internal state + + // Package-private for testing + static final double MINIMUM_BALANCE = 0.0; + + public BankAccount(String accountNumber, double initialBalance) { // Public - part of API + this.accountNumber = accountNumber; + this.balance = initialBalance; + } + + public double getBalance() { // Public - part of API + return balance; + } + + public void deposit(double amount) { // Public - part of API + validateAmount(amount); + balance += amount; + } + + private void validateAmount(double amount) { // Private - implementation detail + if (amount <= 0) { + throw new IllegalArgumentException("Amount must be positive"); + } + } +} +``` + +**Bad example:** + +```java +public class BankAccount { + public String accountNumber; // Should be private + public double balance; // Should be private + public static final double MINIMUM_BALANCE = 0.0; // Unnecessarily public + + public BankAccount(String accountNumber, double initialBalance) { + this.accountNumber = accountNumber; + this.balance = initialBalance; + } + + public void validateAmount(double amount) { // Should be private + if (amount <= 0) { + throw new IllegalArgumentException("Amount must be positive"); + } + } +} +``` + +### Example 29: In Public Classes, Use Accessor Methods, Not Public Fields + +Title: Provide getter and setter methods instead of exposing fields directly +Description: Accessor methods provide flexibility to add validation, logging, or other logic without breaking clients. + +**Good example:** + +```java +public class Point { + private double x; + private double y; + + public Point(double x, double y) { + this.x = x; + this.y = y; + } + + public double getX() { return x; } + public double getY() { return y; } + + public void setX(double x) { + // Can add validation or other logic + if (Double.isNaN(x)) { + throw new IllegalArgumentException("x cannot be NaN"); + } + this.x = x; + } + + public void setY(double y) { + if (Double.isNaN(y)) { + throw new IllegalArgumentException("y cannot be NaN"); + } + this.y = y; + } +} +``` + +**Bad example:** + +```java +public class Point { + public double x; // Direct field access - no validation possible + public double y; // Cannot add logic later without breaking clients + + public Point(double x, double y) { + this.x = x; + this.y = y; + } +} +``` + +### Example 30: Minimize Mutability + +Title: Make classes immutable when possible +Description: Immutable classes are simpler, safer, and can be freely shared. They are inherently thread-safe and have no temporal coupling. + +**Good example:** + +```java +public final class Complex { + private final double real; + private final double imaginary; + + public Complex(double real, double imaginary) { + this.real = real; + this.imaginary = imaginary; + } + + public double realPart() { return real; } + public double imaginaryPart() { return imaginary; } + + // Operations return new instances instead of modifying + public Complex plus(Complex c) { + return new Complex(real + c.real, imaginary + c.imaginary); + } + + public Complex minus(Complex c) { + return new Complex(real - c.real, imaginary - c.imaginary); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof Complex)) return false; + Complex c = (Complex) o; + return Double.compare(c.real, real) == 0 && + Double.compare(c.imaginary, imaginary) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(real, imaginary); + } +} +``` + +**Bad example:** + +```java +public class Complex { + private double real; // Mutable fields + private double imaginary; // Mutable fields + + public Complex(double real, double imaginary) { + this.real = real; + this.imaginary = imaginary; + } + + public double getRealPart() { return real; } + public double getImaginaryPart() { return imaginary; } + + // Mutating operations - not thread-safe, harder to reason about + public void plus(Complex c) { + this.real += c.real; + this.imaginary += c.imaginary; + } + + public void setReal(double real) { this.real = real; } + public void setImaginary(double imaginary) { this.imaginary = imaginary; } +} +``` + +### Example 31: Favor Composition Over Inheritance + +Title: Use composition instead of inheritance when you want to reuse code +Description: Composition is more flexible than inheritance and avoids the fragility of inheritance hierarchies. + +**Good example:** + +```java +// Using composition +public class InstrumentedSet { + private final Set s; + private int addCount = 0; + + public InstrumentedSet(Set s) { + this.s = s; + } + + public boolean add(E e) { + addCount++; + return s.add(e); + } + + public boolean addAll(Collection c) { + addCount += c.size(); + return s.addAll(c); + } + + public int getAddCount() { + return addCount; + } + + // Delegate other methods to the wrapped set + public int size() { return s.size(); } + public boolean isEmpty() { return s.isEmpty(); } + public boolean contains(Object o) { return s.contains(o); } + // ... other delegating methods +} +``` + +**Bad example:** + +```java +// Using inheritance - fragile and error-prone +public class InstrumentedHashSet extends HashSet { + private int addCount = 0; + + @Override + public boolean add(E e) { + addCount++; + return super.add(e); + } + + @Override + public boolean addAll(Collection c) { + addCount += c.size(); + return super.addAll(c); // This calls add() internally, double-counting! + } + + public int getAddCount() { + return addCount; + } +} +``` + +### Example 32: Design and Document for Inheritance or Else Prohibit It + +Title: Either design classes specifically for inheritance or make them final +Description: Classes not designed for inheritance can break when subclassed. Document self-use patterns or prohibit inheritance. + +**Good example:** + +```java +// Designed for inheritance with proper documentation +public abstract class AbstractProcessor { + + /** + * Processes the given data. This implementation calls {@link #validate(String)} + * followed by {@link #transform(String)}. Subclasses may override this method + * to provide different processing logic. + * + * @param data the data to process + * @return the processed result + * @throws IllegalArgumentException if data is invalid + */ + public String process(String data) { + validate(data); + return transform(data); + } + + /** + * Validates the input data. The default implementation checks for null. + * Subclasses may override to provide additional validation. + */ + protected void validate(String data) { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null"); + } + } + + /** + * Transforms the validated data. Subclasses must implement this method. + */ + protected abstract String transform(String data); +} + +// Or prohibit inheritance +public final class UtilityClass { + private UtilityClass() { /* prevent instantiation */ } + + public static String formatName(String firstName, String lastName) { + return firstName + " " + lastName; + } +} +``` + +**Bad example:** + +```java +// Not designed for inheritance but not prohibited +public class DataProcessor { + public String process(String data) { + // Complex logic that might break if overridden + String validated = validate(data); + String transformed = transform(validated); + return finalize(transformed); + } + + private String validate(String data) { /* ... */ return data; } + private String transform(String data) { /* ... */ return data; } + private String finalize(String data) { /* ... */ return data; } +} +``` + +### Example 33: Enums and Annotations + +Title: Effective Use of Enums and Annotations +Description: Enums and annotations are powerful Java features that, when used correctly, can make code more readable, type-safe, and maintainable. + +### Example 34: Use Enums Instead of Int Constants + +Title: Replace int constants with type-safe enums +Description: Enums provide type safety, namespace protection, and additional functionality that int constants cannot offer. + +**Good example:** + +```java +public enum Planet { + MERCURY(3.302e+23, 2.439e6), + VENUS (4.869e+24, 6.052e6), + EARTH (5.975e+24, 6.378e6), + MARS (6.419e+23, 3.393e6); + + private final double mass; // In kilograms + private final double radius; // In meters + private final double surfaceGravity; // In m / s^2 + + // Universal gravitational constant in m^3 / kg s^2 + private static final double G = 6.67300E-11; + + Planet(double mass, double radius) { + this.mass = mass; + this.radius = radius; + surfaceGravity = G * mass / (radius * radius); + } + + public double mass() { return mass; } + public double radius() { return radius; } + public double surfaceGravity() { return surfaceGravity; } + + public double surfaceWeight(double mass) { + return mass * surfaceGravity; // F = ma + } +} + +// Usage +double earthWeight = 175; +double mass = earthWeight / Planet.EARTH.surfaceGravity(); +for (Planet p : Planet.values()) { + System.out.printf("Weight on %s is %f%n", p, p.surfaceWeight(mass)); +} +``` + +**Bad example:** + +```java +// Int constants - not type-safe, no namespace +public class Planet { + public static final int MERCURY = 0; + public static final int VENUS = 1; + public static final int EARTH = 2; + public static final int MARS = 3; + + // Separate arrays for data - error-prone + private static final double[] MASS = {3.302e+23, 4.869e+24, 5.975e+24, 6.419e+23}; + private static final double[] RADIUS = {2.439e6, 6.052e6, 6.378e6, 3.393e6}; + + public static double surfaceWeight(int planet, double mass) { + // No compile-time checking - could pass any int + if (planet < 0 || planet >= MASS.length) { + throw new IllegalArgumentException("Invalid planet: " + planet); + } + // Complex calculations with array indexing + return mass * (6.67300E-11 * MASS[planet] / (RADIUS[planet] * RADIUS[planet])); + } +} +``` + +### Example 35: Use Instance Fields Instead of Ordinals + +Title: Don't derive values from enum ordinals; use instance fields +Description: Ordinal values can change when enum constants are reordered, making code fragile. + +**Good example:** + +```java +public enum Ensemble { + SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5), + SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8), + NONET(9), DECTET(10), TRIPLE_QUARTET(12); + + private final int numberOfMusicians; + + Ensemble(int size) { + this.numberOfMusicians = size; + } + + public int numberOfMusicians() { + return numberOfMusicians; + } +} +``` + +**Bad example:** + +```java +public enum Ensemble { + SOLO, DUET, TRIO, QUARTET, QUINTET, + SEXTET, SEPTET, OCTET, NONET, DECTET; + + public int numberOfMusicians() { + return ordinal() + 1; // Fragile - breaks if order changes + } +} +``` + +### Example 36: Use EnumSet Instead of Bit Fields + +Title: Replace bit field enums with EnumSet for better type safety and performance +Description: EnumSet provides all the benefits of bit fields with better readability and type safety. + +**Good example:** + +```java +public class Text { + public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH } + + // EnumSet - type-safe and efficient + public void applyStyles(Set