diff --git a/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java b/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java index d00714cf3..a6d6bf6b2 100644 --- a/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java +++ b/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java @@ -678,16 +678,23 @@ public AbstractJavadocMojo( * -subpackages. Multiple packages can be separated by commas (,), colons (:) * or semicolons (;). *

- * Wildcards work as followed: + * Wildcards work as follows: *

*

* Example: *
      * <excludePackageNames>*.internal:org.acme.exclude1.*:org.acme.exclude2</excludePackageNames>
      * 
+ * This will exclude: + * * @see Javadoc option exclude. */ @Parameter(property = "excludePackageNames") diff --git a/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java b/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java index 040e9992e..fdb3c96a0 100644 --- a/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java +++ b/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java @@ -389,13 +389,19 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { for (String excludePackagename : excludePackagenames) { // Usage of wildcard was bad specified and bad implemented, i.e. using String.contains() // without respecting surrounding context - // Following implementation should match requirements as defined in the examples: + // Following implementation should match requirements as aligned with javadoc -exclude behavior: // - A wildcard at the beginning should match one or more directories - // - Any other wildcard must match exactly one directory - Pattern p = Pattern.compile(excludePackagename - .replace(".", regexFileSeparator) - .replaceFirst("^\\*", ".+") - .replace("*", "[^" + regexFileSeparator + "]+")); + // - A wildcard at the end should match one or more directories (to include all subpackages) + // - A wildcard in the middle should match exactly one directory + String pattern = excludePackagename.replace(".", regexFileSeparator); + // Handle leading wildcard: match one or more directory levels + pattern = pattern.replaceFirst("^\\*", ".+"); + // Handle trailing wildcard: match one or more directory levels (for subpackages) + pattern = pattern.replaceFirst("\\*$", ".+"); + // Handle wildcards in the middle: match exactly one directory level + pattern = pattern.replace("*", "[^" + regexFileSeparator + "]+"); + + Pattern p = Pattern.compile(pattern); for (String aFileList : fileList) { if (p.matcher(aFileList).matches()) { diff --git a/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java b/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java index 3b4f50e33..9e1255c51 100644 --- a/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java +++ b/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java @@ -681,4 +681,80 @@ public void testToList() { List values = JavadocUtil.toList(value); assertThat(values).containsExactly("*.internal", "org.acme.exclude1.*", "org.acme.exclude2"); } + + /** + * Test for getExcludedPackages with wildcard patterns + */ + public void testGetExcludedPackages() throws Exception { + // Create test directory structure + File testDir = new File(getBasedir(), "target/test/unit/exclude-packages-test"); + if (testDir.exists()) { + FileUtils.deleteDirectory(testDir); + } + + // Create package structure: + // org.example (with Main.java) + // org.example.sub1 (with Class1.java) + // org.example.sub2 (with Class2.java) + // org.example.sub2.subsub (with Class3.java) + // org.other (with Other.java) + // com.internal (with Internal.java) + + File orgExample = new File(testDir, "org/example"); + File orgExampleSub1 = new File(testDir, "org/example/sub1"); + File orgExampleSub2 = new File(testDir, "org/example/sub2"); + File orgExampleSub2Subsub = new File(testDir, "org/example/sub2/subsub"); + File orgOther = new File(testDir, "org/other"); + File comInternal = new File(testDir, "com/internal"); + + assertTrue(orgExample.mkdirs()); + assertTrue(orgExampleSub1.mkdirs()); + assertTrue(orgExampleSub2.mkdirs()); + assertTrue(orgExampleSub2Subsub.mkdirs()); + assertTrue(orgOther.mkdirs()); + assertTrue(comInternal.mkdirs()); + + // Create Java files in each directory + new File(orgExample, "Main.java").createNewFile(); + new File(orgExampleSub1, "Class1.java").createNewFile(); + new File(orgExampleSub2, "Class2.java").createNewFile(); + new File(orgExampleSub2Subsub, "Class3.java").createNewFile(); + new File(orgOther, "Other.java").createNewFile(); + new File(comInternal, "Internal.java").createNewFile(); + + Path testPath = testDir.toPath(); + + // Test 1: org.example.* should match all subpackages of org.example + // Expected: org.example.sub1, org.example.sub2, org.example.sub2.subsub + Collection excludes1 = Collections.singletonList("org.example.*"); + Collection result1 = JavadocUtil.getExcludedPackages(testPath, excludes1); + assertThat(result1) + .as("org.example.* should match all subpackages of org.example") + .containsExactlyInAnyOrder("org.example.sub1", "org.example.sub2", "org.example.sub2.subsub"); + + // Test 2: org.example should match only org.example itself + Collection excludes2 = Collections.singletonList("org.example"); + Collection result2 = JavadocUtil.getExcludedPackages(testPath, excludes2); + assertThat(result2) + .as("org.example should match only org.example package") + .containsExactly("org.example"); + + // Test 3: *.internal should match any package ending with .internal + Collection excludes3 = Collections.singletonList("*.internal"); + Collection result3 = JavadocUtil.getExcludedPackages(testPath, excludes3); + assertThat(result3).as("*.internal should match com.internal").containsExactly("com.internal"); + + // Test 4: org.*.sub1 should match org.example.sub1 (wildcard in the middle matches exactly one level) + Collection excludes4 = Collections.singletonList("org.*.sub1"); + Collection result4 = JavadocUtil.getExcludedPackages(testPath, excludes4); + assertThat(result4).as("org.*.sub1 should match org.example.sub1").containsExactly("org.example.sub1"); + + // Test 5: Multiple patterns + Collection excludes5 = Arrays.asList("org.example.*", "org.other"); + Collection result5 = JavadocUtil.getExcludedPackages(testPath, excludes5); + assertThat(result5) + .as("Multiple patterns should work together") + .containsExactlyInAnyOrder( + "org.example.sub1", "org.example.sub2", "org.example.sub2.subsub", "org.other"); + } }