11package io .github .svaningelgem ;
22
3+ import lombok .var ;
4+ import org .apache .maven .execution .MavenSession ;
5+ import org .apache .maven .model .Plugin ;
6+ import org .apache .maven .model .PluginExecution ;
37import org .apache .maven .plugin .AbstractMojo ;
8+ import org .apache .maven .plugin .MojoExecution ;
49import org .apache .maven .plugin .MojoExecutionException ;
510import org .apache .maven .plugins .annotations .LifecyclePhase ;
611import org .apache .maven .plugins .annotations .Mojo ;
712import org .apache .maven .plugins .annotations .Parameter ;
13+ import org .apache .maven .project .MavenProject ;
814import org .codehaus .plexus .util .xml .Xpp3Dom ;
915import org .jacoco .core .analysis .Analyzer ;
1016import org .jacoco .core .analysis .CoverageBuilder ;
1824import org .jacoco .report .MultiSourceFileLocator ;
1925import org .jacoco .report .xml .XMLFormatter ;
2026import org .jetbrains .annotations .NotNull ;
27+ import org .jetbrains .annotations .Nullable ;
2128
2229import java .io .File ;
2330import java .io .FileInputStream ;
3643import java .util .LinkedList ;
3744import java .util .List ;
3845import java .util .Locale ;
46+ import java .util .Objects ;
3947import java .util .Properties ;
4048import java .util .Queue ;
4149import java .util .Set ;
50+ import java .util .concurrent .atomic .AtomicBoolean ;
4251import java .util .function .Consumer ;
4352import java .util .regex .Matcher ;
4453import java .util .regex .Pattern ;
@@ -141,13 +150,19 @@ public class JacocoConsoleReporterMojo extends AbstractMojo {
141150 * The Maven project.
142151 */
143152 @ Parameter (defaultValue = "${project}" , readonly = true , required = true )
144- org . apache . maven . project . MavenProject project ;
153+ MavenProject project ;
145154
146155 /**
147156 * The Maven session.
148157 */
149158 @ Parameter (defaultValue = "${session}" , readonly = true , required = true )
150- org .apache .maven .execution .MavenSession mavenSession ;
159+ MavenSession mavenSession ;
160+
161+ /**
162+ * The execution step
163+ */
164+ @ Parameter (defaultValue = "${mojoExecution}" , readonly = true )
165+ MojoExecution mojoExecution ;
151166
152167 /**
153168 * JaCoCo plugin info for dependency discovery
@@ -310,12 +325,16 @@ void addSonarFileExclusions(@NotNull String exclusions) {
310325 * <exclude>com/baeldung/** /*DTO.*</exclude>
311326 * <exclude>** /config/*</exclude>
312327 */
313- Pattern convertExclusionToPattern (@ NotNull String jacocoPattern ) {
314- jacocoPattern = jacocoPattern .replace ("\\ " , "/" );
328+ @ Nullable Pattern convertExclusionToPattern (@ NotNull String jacocoPattern ) {
329+ jacocoPattern = jacocoPattern .replace ("\\ " , "/" ). trim () ;
315330
316331 // Handle the .class extension
317332 if (jacocoPattern .toLowerCase (Locale .ENGLISH ).endsWith (".class" )) {
318- jacocoPattern = jacocoPattern .substring (0 , jacocoPattern .length () - 6 );
333+ jacocoPattern = jacocoPattern .substring (0 , jacocoPattern .length () - 6 ).trim ();
334+ }
335+
336+ if (jacocoPattern .isEmpty ()) {
337+ return null ;
319338 }
320339
321340 // Use temporary placeholders to avoid interference between replacements
@@ -335,8 +354,12 @@ Pattern convertExclusionToPattern(@NotNull String jacocoPattern) {
335354 }
336355
337356 void addExclusion (@ NotNull String jacocoPattern ) {
338- Pattern pattern = convertExclusionToPattern (jacocoPattern );
339- collectedExcludePatterns .add (pattern );
357+ for (String pattern : jacocoPattern .split ("," )) {
358+ var converted = convertExclusionToPattern (pattern );
359+ if (converted != null ) {
360+ collectedExcludePatterns .add (converted );
361+ }
362+ }
340363 }
341364
342365 /**
@@ -400,6 +423,57 @@ boolean shouldReport() {
400423 return !deferReporting || project .getId ().equals (mavenSession .getProjects ().get (mavenSession .getProjects ().size () - 1 ).getId ());
401424 }
402425
426+ Queue <Xpp3Dom > digIntoConfig (Xpp3Dom config , String [] parts ) {
427+ if (config == null || parts == null || parts .length == 0 ) {
428+ return null ;
429+ }
430+
431+ // Queue of nodes to process at the current level
432+ Queue <Xpp3Dom > currentLevelNodes = new LinkedList <>();
433+ Queue <Xpp3Dom > nextLevelNodes = null ;
434+ currentLevelNodes .add (config );
435+
436+ // Process each part of the path
437+ for (String part : parts ) {
438+ nextLevelNodes = new LinkedList <>();
439+
440+ // Process all nodes at the current level
441+ for (Xpp3Dom currentNode : currentLevelNodes ) {
442+ // Get all children with matching name
443+ Xpp3Dom [] children = currentNode .getChildren (part );
444+ Collections .addAll (nextLevelNodes , children );
445+ }
446+
447+ // If this is the last part in the path, apply consumer to all matching nodes
448+ if (nextLevelNodes .isEmpty ()) {
449+ // If no matching nodes found at this level, stop processing
450+ return null ;
451+ }
452+
453+ // Continue with the next level
454+ currentLevelNodes = nextLevelNodes ;
455+ }
456+
457+ return nextLevelNodes ;
458+ }
459+
460+ Queue <Xpp3Dom > getConfiguration (Plugin plugin , String [] parts ) {
461+ Queue <Xpp3Dom > config = null ;
462+
463+ if (mojoExecution != null ) {
464+ PluginExecution exec = plugin .getExecutionsAsMap ().get (mojoExecution .getGoal ());
465+ if (exec != null ) {
466+ config = digIntoConfig ((Xpp3Dom ) exec .getConfiguration (), parts );
467+ }
468+ }
469+
470+ if (config != null ) {
471+ return config ;
472+ }
473+
474+ return digIntoConfig ((Xpp3Dom ) plugin .getConfiguration (), parts );
475+ }
476+
403477 void doSomethingForEachPluginConfiguration (String groupId , String artifactId , @ NotNull String configValue , Consumer <String > configurationConsumer , String defaultValue ) {
404478 final String [] parts = Arrays .stream (configValue .split ("\\ ." )).filter (s -> !s .isEmpty ()).toArray (String []::new );
405479
@@ -408,47 +482,20 @@ void doSomethingForEachPluginConfiguration(String groupId, String artifactId, @N
408482 boolean artifactEquals = artifactId .equals (plugin .getArtifactId ());
409483 return groupEquals && artifactEquals ;
410484 }).forEach (plugin -> {
411- boolean [] foundValue = new boolean [ 1 ] ;
485+ AtomicBoolean foundValue = new AtomicBoolean ( false ) ;
412486
413- Xpp3Dom config = ( Xpp3Dom ) plugin . getConfiguration ();
487+ Queue < Xpp3Dom > config = getConfiguration (plugin , parts );
414488 if (config != null ) {
489+ config .forEach (node -> {
490+ String value = node .getValue ().trim ();
491+ if (value .isEmpty ()) return ;
415492
416- // Queue of nodes to process at the current level
417- Queue <Xpp3Dom > currentLevelNodes = new LinkedList <>();
418- currentLevelNodes .add (config );
419-
420- // Process each part of the path
421- for (int i = 0 ; i < parts .length ; i ++) {
422- String part = parts [i ];
423- Queue <Xpp3Dom > nextLevelNodes = new LinkedList <>();
424-
425- // Process all nodes at the current level
426- for (Xpp3Dom currentNode : currentLevelNodes ) {
427- // Get all children with matching name
428- Xpp3Dom [] children = currentNode .getChildren (part );
429- Collections .addAll (nextLevelNodes , children );
430- }
431-
432- // If this is the last part in the path, apply consumer to all matching nodes
433- if (i == parts .length - 1 ) {
434- nextLevelNodes .forEach (node -> {
435- String value = node .getValue ().trim ();
436- if (value .isEmpty ()) return ;
437-
438- configurationConsumer .accept (value );
439- foundValue [0 ] = true ;
440- });
441- } else if (nextLevelNodes .isEmpty ()) {
442- // If no matching nodes found at this level, stop processing
443- break ;
444- } else {
445- // Continue with the next level
446- currentLevelNodes = nextLevelNodes ;
447- }
448- }
493+ configurationConsumer .accept (value );
494+ foundValue .set (true );
495+ });
449496 }
450497
451- if (!foundValue [ 0 ] && defaultValue != null ) {
498+ if (!foundValue . get () && defaultValue != null ) {
452499 configurationConsumer .accept (defaultValue );
453500 }
454501 });
0 commit comments