This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-clean-plugin.git

commit 00107cb437b722cef2ffe4d9b57619bc35504c6f
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Oct 16 20:28:38 2025 +0200

    Replace the `PathSelector` class by the implementation provided by Maven 
core.
---
 .../org/apache/maven/plugins/clean/CleanMojo.java  |  18 +-
 .../org/apache/maven/plugins/clean/Cleaner.java    | 112 ++--
 .../org/apache/maven/plugins/clean/Fileset.java    |  53 +-
 .../apache/maven/plugins/clean/PathSelector.java   | 640 ---------------------
 .../apache/maven/plugins/clean/CleanMojoTest.java  |  66 ++-
 .../apache/maven/plugins/clean/CleanerTest.java    |  21 +-
 6 files changed, 185 insertions(+), 725 deletions(-)

diff --git a/src/main/java/org/apache/maven/plugins/clean/CleanMojo.java 
b/src/main/java/org/apache/maven/plugins/clean/CleanMojo.java
index 090e804..9ac88c4 100644
--- a/src/main/java/org/apache/maven/plugins/clean/CleanMojo.java
+++ b/src/main/java/org/apache/maven/plugins/clean/CleanMojo.java
@@ -34,6 +34,7 @@ import org.apache.maven.api.plugin.Log;
 import org.apache.maven.api.plugin.MojoException;
 import org.apache.maven.api.plugin.annotations.Mojo;
 import org.apache.maven.api.plugin.annotations.Parameter;
+import org.apache.maven.api.services.PathMatcherFactory;
 import org.apache.maven.api.services.ProjectManager;
 
 /**
@@ -235,6 +236,12 @@ public class CleanMojo implements 
org.apache.maven.api.plugin.Mojo {
     @Inject
     private Project project;
 
+    /**
+     * The service to use for creating include and exclude filters.
+     */
+    @Inject
+    private PathMatcherFactory matcherFactory;
+
     /**
      * Deletes build directories and file-sets.
      * Directories are deleted in the following order:
@@ -284,7 +291,16 @@ public class CleanMojo implements 
org.apache.maven.api.plugin.Mojo {
                     + FAST_MODE_BACKGROUND + "', '" + FAST_MODE_AT_END + "' 
and '" + FAST_MODE_DEFER + "'.");
         }
         final var cleaner = new Cleaner(
-                session, logger, isVerbose(), fastDir, fastMode, 
followSymLinks, force, failOnError, retryOnError);
+                session,
+                matcherFactory,
+                logger,
+                isVerbose(),
+                fastDir,
+                fastMode,
+                followSymLinks,
+                force,
+                failOnError,
+                retryOnError);
         try {
             for (Path directoryItem : getDirectories()) {
                 cleaner.delete(directoryItem);
diff --git a/src/main/java/org/apache/maven/plugins/clean/Cleaner.java 
b/src/main/java/org/apache/maven/plugins/clean/Cleaner.java
index 2ad8276..2cfce01 100644
--- a/src/main/java/org/apache/maven/plugins/clean/Cleaner.java
+++ b/src/main/java/org/apache/maven/plugins/clean/Cleaner.java
@@ -25,14 +25,15 @@ import java.nio.file.FileVisitOption;
 import java.nio.file.FileVisitResult;
 import java.nio.file.FileVisitor;
 import java.nio.file.Files;
+import java.nio.file.NotDirectoryException;
 import java.nio.file.Path;
+import java.nio.file.PathMatcher;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.DosFileAttributeView;
 import java.nio.file.attribute.PosixFileAttributeView;
 import java.nio.file.attribute.PosixFilePermission;
 import java.util.ArrayDeque;
-import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Deque;
 import java.util.EnumSet;
@@ -50,6 +51,7 @@ import org.apache.maven.api.SessionData;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.annotations.Nullable;
 import org.apache.maven.api.plugin.Log;
+import org.apache.maven.api.services.PathMatcherFactory;
 
 /**
  * Cleans directories.
@@ -107,11 +109,23 @@ final class Cleaner implements FileVisitor<Path> {
     private final String fastMode;
 
     /**
-     * Combination of includes and excludes path matchers.
-     * A {@code null} value means to include everything.
+     * The service to use for creating include and exclude filters.
+     * Used for setting a value to {@link #fileMatcher} and {@link 
#directoryMatcher}.
      */
-    @Nullable
-    private PathSelector selector;
+    @Nonnull
+    private final PathMatcherFactory matcherFactory;
+
+    /**
+     * Combination of includes and excludes path matchers applied on files.
+     */
+    @Nonnull
+    private PathMatcher fileMatcher;
+
+    /**
+     * Combination of includes and excludes path matchers applied on 
directories.
+     */
+    @Nonnull
+    private PathMatcher directoryMatcher;
 
     /**
      * Whether the base directory is excluded from the set of directories to 
delete.
@@ -120,6 +134,14 @@ final class Cleaner implements FileVisitor<Path> {
      */
     private boolean isBaseDirectoryExcluded;
 
+    /**
+     * Whether to follow symbolic links while deleting files from the 
directories.
+     * This value is specified by the <abbr>MOJO</abbr> plugin configuration,
+     * but can be overridden by {@link Fileset}.
+     *
+     * @see CleanMojo#followSymLinks
+     * @see Fileset#followSymlinks
+     */
     private boolean followSymlinks;
 
     /**
@@ -168,29 +190,35 @@ final class Cleaner implements FileVisitor<Path> {
 
     /**
      * Creates a new cleaner.
+     * By default, the cleaner has no include or exclude filters,
+     * does not exclude the base directory and does not follow symbolic links.
+     * These properties can be modified by {@link #delete(Fileset)}.
      *
-     * @param session  the Maven session to be used
-     * @param logger   the logger to use
-     * @param verbose  whether to perform verbose logging
-     * @param fastDir  the explicit configured directory or to be deleted in 
fast mode
-     * @param fastMode the fast deletion mode
-     * @param followSymlinks whether to follow symlinks
-     * @param force          whether to force the deletion of read-only files
-     * @param failOnError    whether to abort with an exception in case a 
selected file/directory could not be deleted
-     * @param retryOnError   whether to undertake additional delete attempts 
in case the first attempt failed
+     * @param session         the Maven session to be used
+     * @param matcherFactory  the service to use for creating include and 
exclude filters.
+     * @param logger          the logger to use
+     * @param verbose         whether to perform verbose logging
+     * @param fastDir         the explicit configured directory or to be 
deleted in fast mode
+     * @param fastMode        the fast deletion mode
+     * @param followSymlinks  whether to follow symlinks
+     * @param force           whether to force the deletion of read-only files
+     * @param failOnError     whether to abort with an exception in case a 
selected file/directory could not be deleted
+     * @param retryOnError    whether to undertake additional delete attempts 
in case the first attempt failed
      */
     @SuppressWarnings("checkstyle:ParameterNumber")
     Cleaner(
-            @Nonnull Session session,
+            @Nullable Session session,
+            @Nonnull PathMatcherFactory matcherFactory,
             @Nonnull Log logger,
             boolean verbose,
-            @Nonnull Path fastDir,
+            @Nullable Path fastDir,
             @Nonnull String fastMode,
             boolean followSymlinks,
             boolean force,
             boolean failOnError,
             boolean retryOnError) {
         this.session = session;
+        this.matcherFactory = matcherFactory;
         this.logger = logger;
         this.verbose = verbose;
         this.fastDir = fastDir;
@@ -201,32 +229,38 @@ final class Cleaner implements FileVisitor<Path> {
         this.retryOnError = retryOnError;
         listDeletedFiles = verbose ? logger.isInfoEnabled() : 
logger.isDebugEnabled();
         nonEmptyDirectoryLevels = new BitSet();
+        fileMatcher = matcherFactory.includesAll();
+        directoryMatcher = fileMatcher;
     }
 
     /**
      * Deletes the specified fileset.
+     * This method modifies the include and exclude filters,
+     * whether to exclude the base directory and whether to follow symbolic 
links.
      *
-     * @param fileset the fileset to delete, must not be {@code null}
-     * @throws IOException if a file/directory could not be deleted and {@code 
failOnError} is {@code true}
+     * @param fileset the fileset to delete
+     * @throws IOException if a file/directory could not be deleted and {@link 
#failOnError} is {@code true}
      */
     public void delete(@Nonnull Fileset fileset) throws IOException {
-        selector = new PathSelector(
-                fileset.getDirectory(),
-                Arrays.asList(fileset.getIncludes()),
-                Arrays.asList(fileset.getExcludes()),
-                fileset.isUseDefaultExcludes());
-        if (selector.isEmpty()) {
-            selector = null;
-        }
+        fileMatcher = matcherFactory.createPathMatcher(
+                fileset.getDirectory(), fileset.getIncludes(), 
fileset.getExcludes(), fileset.useDefaultExcludes());
+        directoryMatcher = matcherFactory.deriveDirectoryMatcher(fileMatcher);
         isBaseDirectoryExcluded = fileset.isBaseDirectoryExcluded();
-        followSymlinks = fileset.isFollowSymlinks();
+        followSymlinks = fileset.followSymlinks();
         delete(fileset.getDirectory());
     }
 
     /**
-     * Deletes the specified directory and its contents.
+     * Deletes the specified directory and its contents using the current 
configuration.
      * Non-existing directories will be silently ignored.
      *
+     * <h4>Configuration</h4>
+     * The behavior of this method depends on the {@code Cleaner} 
configuration.
+     * Some configuration can be modified by calls to {@link #delete(Fileset)}.
+     * Therefore, for deleting files with the default configuration (no include
+     * or exclude filters, not following symbolic links), this method should be
+     * invoked first.
+     *
      * @param basedir the directory to delete, must not be {@code null}
      * @throws IOException if a file/directory could not be deleted and {@code 
failOnError} is {@code true}
      */
@@ -238,17 +272,17 @@ final class Cleaner implements FileVisitor<Path> {
                 }
                 return;
             }
-            throw new IOException("Invalid base directory " + basedir);
+            throw new NotDirectoryException("Invalid base directory " + 
basedir);
         }
         if (logger.isInfoEnabled()) {
-            logger.info("Deleting " + basedir + (selector != null ? " (" + 
selector + ")" : ""));
+            logger.info("Deleting " + basedir + (isClearAll() ? "" : " (" + 
fileMatcher + ')'));
         }
         var options = EnumSet.noneOf(FileVisitOption.class);
         if (followSymlinks) {
             options.add(FileVisitOption.FOLLOW_LINKS);
             basedir = getCanonicalPath(basedir, null);
         }
-        if (selector == null && !followSymlinks && fastDir != null && session 
!= null) {
+        if (isClearAll() && !followSymlinks && fastDir != null && session != 
null) {
             // If anything wrong happens, we'll just use the usual deletion 
mechanism
             if (fastDelete(basedir)) {
                 return;
@@ -257,6 +291,14 @@ final class Cleaner implements FileVisitor<Path> {
         Files.walkFileTree(basedir, options, Integer.MAX_VALUE, this);
     }
 
+    /**
+     * {@return whether {@link #fileMatcher} matches all files}.
+     * This is a required condition for allowing the use of {@link 
#fastDelete(Path)}.
+     */
+    private boolean isClearAll() {
+        return fileMatcher == matcherFactory.includesAll();
+    }
+
     private boolean fastDelete(Path baseDir) {
         // Handle the case where we use 
${maven.multiModuleProjectDirectory}/target/.clean for example
         if (fastDir.toAbsolutePath().startsWith(baseDir.toAbsolutePath())) {
@@ -320,7 +362,7 @@ final class Cleaner implements FileVisitor<Path> {
             visitFile(dir, attrs);
             return FileVisitResult.SKIP_SUBTREE;
         }
-        if (selector == null || selector.couldHoldSelected(dir)) {
+        if (directoryMatcher.matches(dir)) {
             nonEmptyDirectoryLevels.clear(++currentDepth);
             return FileVisitResult.CONTINUE;
         } else {
@@ -336,7 +378,7 @@ final class Cleaner implements FileVisitor<Path> {
      */
     @Override
     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
throws IOException {
-        if ((selector == null || selector.matches(file)) && tryDelete(file)) {
+        if (fileMatcher.matches(file) && tryDelete(file)) {
             if (listDeletedFiles) {
                 logDelete(file, attrs);
             }
@@ -377,8 +419,8 @@ final class Cleaner implements FileVisitor<Path> {
             canDelete = false;
         } else {
             canDelete &= (currentDepth != 0 || !isBaseDirectoryExcluded);
-            if (canDelete && selector != null) {
-                canDelete = selector.matches(dir);
+            if (canDelete) {
+                canDelete = fileMatcher.matches(dir);
             }
         }
         if (canDelete && tryDelete(dir)) {
diff --git a/src/main/java/org/apache/maven/plugins/clean/Fileset.java 
b/src/main/java/org/apache/maven/plugins/clean/Fileset.java
index de497f8..b55dcc4 100644
--- a/src/main/java/org/apache/maven/plugins/clean/Fileset.java
+++ b/src/main/java/org/apache/maven/plugins/clean/Fileset.java
@@ -19,10 +19,12 @@
 package org.apache.maven.plugins.clean;
 
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Customizes the string representation of
- * {@code org.apache.maven.shared.model.fileset.FileSet} to return the
+ * {@code org.apache.maven.api.model.FileSet} to return the
  * included and excluded files from the file-set's directory. Specifically,
  * <code>"file-set: <I>[directory]</I> (included: <I>[included files]</I>,
  * excluded: <I>[excluded files]</I>)"</code>
@@ -54,17 +56,37 @@ public class Fileset {
     }
 
     /**
-     * {@return the patterns of the file to include, or an empty array if 
unspecified}.
+     * {@return the patterns of the file to include, or an empty list if 
unspecified}.
      */
-    public String[] getIncludes() {
-        return (includes != null) ? includes : new String[0];
+    public List<String> getIncludes() {
+        return listWithoutNull(includes);
     }
 
     /**
-     * {@return the patterns of the file to exclude, or an empty array if 
unspecified}.
+     * {@return the patterns of the file to exclude, or an empty list if 
unspecified}.
      */
-    public String[] getExcludes() {
-        return (excludes != null) ? excludes : new String[0];
+    public List<String> getExcludes() {
+        return listWithoutNull(excludes);
+    }
+
+    /**
+     * {@return the content of the given array without null elements}.
+     * The existence of null elements has been observed in practice,
+     * not sure where they come from.
+     *
+     * @param patterns the {@link #includes} or {@link #excludes} array, or 
{@code null} if none
+     */
+    private static List<String> listWithoutNull(String[] patterns) {
+        if (patterns == null) {
+            return List.of();
+        }
+        var list = new ArrayList<String>(patterns.length);
+        for (String pattern : patterns) {
+            if (pattern != null) {
+                list.add(pattern);
+            }
+        }
+        return list;
     }
 
     /**
@@ -86,14 +108,14 @@ public class Fileset {
     /**
      * {@return whether to follow symbolic links}.
      */
-    public boolean isFollowSymlinks() {
+    public boolean followSymlinks() {
         return followSymlinks;
     }
 
     /**
      * {@return whether to use a default set of excludes}.
      */
-    public boolean isUseDefaultExcludes() {
+    public boolean useDefaultExcludes() {
         return useDefaultExcludes;
     }
 
@@ -105,15 +127,12 @@ public class Fileset {
      * @param label label identifying the array of elements to add
      * @param patterns the elements to append, or {@code null} if none
      */
-    static void append(StringBuilder buffer, String label, String[] patterns) {
+    private static void append(StringBuilder buffer, String label, 
List<String> patterns) {
         buffer.append(label).append(": [");
-        if (patterns != null) {
-            for (int i = 0; i < patterns.length; i++) {
-                if (i != 0) {
-                    buffer.append(", ");
-                }
-                buffer.append(patterns[i]);
-            }
+        String separator = "";
+        for (String pattern : patterns) {
+            buffer.append(separator).append(pattern);
+            separator = ", ";
         }
         buffer.append(']');
     }
diff --git a/src/main/java/org/apache/maven/plugins/clean/PathSelector.java 
b/src/main/java/org/apache/maven/plugins/clean/PathSelector.java
deleted file mode 100644
index 2d81082..0000000
--- a/src/main/java/org/apache/maven/plugins/clean/PathSelector.java
+++ /dev/null
@@ -1,640 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.plugins.clean;
-
-import java.io.File;
-import java.nio.file.FileSystem;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Determines whether a path is selected according to include/exclude patterns.
- * The pathnames used for method parameters will be relative to some base 
directory
- * and use {@code '/'} as separator, regardless of the hosting operating 
system.
- *
- * <h2>Syntax</h2>
- * If a pattern contains the {@code ':'} character and the prefix before is 
longer than 1 character,
- * then that pattern is given verbatim to {@link 
FileSystem#getPathMatcher(String)}, which interprets
- * the part before {@code ':'} as the syntax (usually {@code "glob"} or {@code 
"regex"}).
- * If a pattern does not contain the {@code ':'} character, or if the prefix 
is one character long
- * (interpreted as a Windows drive), then the syntax defaults to a 
reproduction of the Maven 3 behavior.
- * This is implemented as the {@code "glob"} syntax with the following 
modifications:
- *
- * <ul>
- *   <li>The platform-specific separator ({@code '\\'} on Windows) is replaced 
by {@code '/'}.
- *       Note that it means that the backslash cannot be used for escaping 
characters.</li>
- *   <li>Trailing {@code "/"} is completed as {@code "/**"}.</li>
- *   <li>The {@code "**"} wildcard means "0 or more directories" instead of "1 
or more directories".
- *       This is implemented by adding variants of the pattern without the 
{@code "**"} wildcard.</li>
- *   <li>Bracket characters [ ] and { } are escaped.</li>
- *   <li>On Unix only, the escape character {@code '\\'} is itself 
escaped.</li>
- * </ul>
- *
- * If above changes are not desired, put an explicit {@code "glob:"} prefix 
before the pattern.
- * Note that putting such a prefix is recommended anyway for better 
performances.
- *
- * @author Benjamin Bentmann
- * @author Martin Desruisseaux
- *
- * @see java.nio.file.FileSystem#getPathMatcher(String)
- */
-final class PathSelector implements PathMatcher {
-    /**
-     * Patterns which should be excluded by default, like <abbr>SCM</abbr> 
files.
-     *
-     * <p><b>Source:</b> this list is copied from {@code plexus-utils-4.0.2} 
(released in
-     * September 23, 2024), class {@code 
org.codehaus.plexus.util.AbstractScanner}.</p>
-     */
-    private static final List<String> DEFAULT_EXCLUDES = List.of(
-            // Miscellaneous typical temporary files
-            "**/*~",
-            "**/#*#",
-            "**/.#*",
-            "**/%*%",
-            "**/._*",
-
-            // CVS
-            "**/CVS",
-            "**/CVS/**",
-            "**/.cvsignore",
-
-            // RCS
-            "**/RCS",
-            "**/RCS/**",
-
-            // SCCS
-            "**/SCCS",
-            "**/SCCS/**",
-
-            // Visual SourceSafe
-            "**/vssver.scc",
-
-            // MKS
-            "**/project.pj",
-
-            // Subversion
-            "**/.svn",
-            "**/.svn/**",
-
-            // Arch
-            "**/.arch-ids",
-            "**/.arch-ids/**",
-
-            // Bazaar
-            "**/.bzr",
-            "**/.bzr/**",
-
-            // SurroundSCM
-            "**/.MySCMServerInfo",
-
-            // Mac
-            "**/.DS_Store",
-
-            // Serena Dimensions Version 10
-            "**/.metadata",
-            "**/.metadata/**",
-
-            // Mercurial
-            "**/.hg",
-            "**/.hg/**",
-
-            // git
-            "**/.git",
-            "**/.git/**",
-            "**/.gitignore",
-
-            // BitKeeper
-            "**/BitKeeper",
-            "**/BitKeeper/**",
-            "**/ChangeSet",
-            "**/ChangeSet/**",
-
-            // darcs
-            "**/_darcs",
-            "**/_darcs/**",
-            "**/.darcsrepo",
-            "**/.darcsrepo/**",
-            "**/-darcs-backup*",
-            "**/.darcs-temp-mail");
-
-    /**
-     * Maximum number of characters of the prefix before {@code ':'} for 
handling as a Maven syntax.
-     */
-    private static final int MAVEN_SYNTAX_THRESHOLD = 1;
-
-    /**
-     * The default syntax to use if none was specified. Note that when this 
default syntax is applied,
-     * the user-provided pattern get some changes as documented in class 
Javadoc.
-     */
-    private static final String DEFAULT_SYNTAX = "glob:";
-
-    /**
-     * Characters having a special meaning in the glob syntax.
-     *
-     * @see FileSystem#getPathMatcher(String)
-     */
-    private static final String SPECIAL_CHARACTERS = "*?[]{}\\";
-
-    /**
-     * A path matcher which accepts all files.
-     *
-     * @see #simplify()
-     */
-    private static final PathMatcher INCLUDES_ALL = (path) -> true;
-
-    /**
-     * String representations of the normalized include filters.
-     * Each pattern shall be prefixed by its syntax, which is {@value 
#DEFAULT_SYNTAX} by default.
-     * An empty array means to include all files.
-     *
-     * @see #toString()
-     */
-    private final String[] includePatterns;
-
-    /**
-     * String representations of the normalized exclude filters.
-     * Each pattern shall be prefixed by its syntax. If no syntax is specified,
-     * the default is a Maven 3 syntax similar, but not identical, to {@value 
#DEFAULT_SYNTAX}.
-     * This array may be longer or shorter than the user-supplied excludes, 
depending on whether
-     * default excludes have been added and whether some unnecessary excludes 
have been omitted.
-     *
-     * @see #toString()
-     */
-    private final String[] excludePatterns;
-
-    /**
-     * The matcher for includes. The length of this array is equal to {@link 
#includePatterns} array length.
-     * An empty array means to include all files.
-     */
-    private final PathMatcher[] includes;
-
-    /**
-     * The matcher for excludes. The length of this array is equal to {@link 
#excludePatterns} array length.
-     */
-    private final PathMatcher[] excludes;
-
-    /**
-     * The matcher for all directories to include. This array includes the 
parents of all those directories,
-     * because they need to be accepted before we can walk to the 
sub-directories.
-     * This is an optimization for skipping whole directories when possible.
-     * An empty array means to include all directories.
-     */
-    private final PathMatcher[] dirIncludes;
-
-    /**
-     * The matcher for directories to exclude. This array does <em>not</em> 
include the parent directories,
-     * because they may contain other sub-trees that need to be included.
-     * This is an optimization for skipping whole directories when possible.
-     */
-    private final PathMatcher[] dirExcludes;
-
-    /**
-     * The base directory. All files will be relativized to that directory 
before to be matched.
-     */
-    private final Path baseDirectory;
-
-    /**
-     * Whether paths must be relativized before to be given to a matcher. If 
{@code true}, then every paths
-     * will be made relative to {@link #baseDirectory} for allowing patterns 
like {@code "foo/bar/*.java"}
-     * to work. As a slight optimization, we can skip this step if all 
patterns start with {@code "**"}.
-     */
-    private final boolean needRelativize;
-
-    /**
-     * Creates a new selector from the given includes and excludes.
-     *
-     * @param directory the base directory of the files to filter
-     * @param includes the patterns of the files to include, or null or empty 
for including all files
-     * @param excludes the patterns of the files to exclude, or null or empty 
for no exclusion
-     * @param useDefaultExcludes whether to augment the excludes with a 
default set of <abbr>SCM</abbr> patterns
-     */
-    PathSelector(Path directory, Collection<String> includes, 
Collection<String> excludes, boolean useDefaultExcludes) {
-        includePatterns = normalizePatterns(includes, false);
-        excludePatterns = normalizePatterns(effectiveExcludes(excludes, 
includePatterns, useDefaultExcludes), true);
-        baseDirectory = directory;
-        FileSystem fs = directory.getFileSystem();
-        this.includes = matchers(fs, includePatterns);
-        this.excludes = matchers(fs, excludePatterns);
-        dirIncludes = matchers(fs, directoryPatterns(includePatterns, false));
-        dirExcludes = matchers(fs, directoryPatterns(excludePatterns, true));
-        needRelativize = needRelativize(includePatterns) || 
needRelativize(excludePatterns);
-    }
-
-    /**
-     * Returns the given array of excludes, optionally expanded with a default 
set of excludes,
-     * then with unnecessary excludes omitted. An unnecessary exclude is an 
exclude which will never
-     * match a file because there is no include which would accept a file that 
could match the exclude.
-     * For example, if the only include is {@code "*.java"}, then the 
<code>"**&sol;project.pj"</code>,
-     * <code>"**&sol;.DS_Store"</code> and other excludes will never match a 
file and can be omitted.
-     * Because the list of {@linkplain #DEFAULT_EXCLUDES default excludes} 
contains many elements,
-     * removing unnecessary excludes can reduce a lot the number of matches 
tested on each source file.
-     *
-     * <h4>Implementation note</h4>
-     * The removal of unnecessary excludes is done on a best effort basis. The 
current implementation
-     * compares only the prefixes and suffixes of each pattern, keeping the 
pattern in case of doubt.
-     * This is not bad, but it does not remove all unnecessary patterns. It 
would be possible to do
-     * better in the future if benchmarking suggests that it would be worth 
the effort.
-     *
-     * @param excludes the user-specified excludes, potentially not yet 
converted to glob syntax
-     * @param includes the include patterns converted to glob syntax
-     * @param useDefaultExcludes whether to expand user exclude with the set 
of default excludes
-     * @return the potentially expanded or reduced set of excludes to use
-     */
-    private static Collection<String> effectiveExcludes(
-            Collection<String> excludes, final String[] includes, final 
boolean useDefaultExcludes) {
-        if (excludes == null || excludes.isEmpty()) {
-            if (useDefaultExcludes) {
-                excludes = new ArrayList<>(DEFAULT_EXCLUDES);
-            } else {
-                return List.of();
-            }
-        } else {
-            excludes = new ArrayList<>(excludes);
-            excludes.removeIf(Objects::isNull);
-            if (useDefaultExcludes) {
-                excludes.addAll(DEFAULT_EXCLUDES);
-            }
-        }
-        if (includes.length == 0) {
-            return excludes;
-        }
-        /*
-         * Get the prefixes and suffixes of all includes, stopping at the 
first special character.
-         * Redundant prefixes and suffixes are omitted.
-         */
-        var prefixes = new String[includes.length];
-        var suffixes = new String[includes.length];
-        for (int i = 0; i < includes.length; i++) {
-            String include = includes[i];
-            if (!include.startsWith(DEFAULT_SYNTAX)) {
-                return excludes; // Do not filter if at least one pattern is 
too complicated.
-            }
-            include = include.substring(DEFAULT_SYNTAX.length());
-            prefixes[i] = prefixOrSuffix(include, false);
-            suffixes[i] = prefixOrSuffix(include, true);
-        }
-        prefixes = sortByLength(prefixes, false);
-        suffixes = sortByLength(suffixes, true);
-        /*
-         * Keep only the exclude which start with one of the prefixes and end 
with one of the suffixes.
-         * Note that a prefix or suffix may be the empty string, which match 
everything.
-         */
-        final Iterator<String> it = excludes.iterator();
-        nextExclude:
-        while (it.hasNext()) {
-            final String exclude = it.next();
-            final int s = exclude.indexOf(':');
-            if (s <= MAVEN_SYNTAX_THRESHOLD || 
exclude.startsWith(DEFAULT_SYNTAX)) {
-                if (cannotMatch(exclude, prefixes, false) || 
cannotMatch(exclude, suffixes, true)) {
-                    it.remove();
-                }
-            }
-        }
-        return excludes;
-    }
-
-    /**
-     * Returns the maximal amount of ordinary characters at the beginning or 
end of the given pattern.
-     * The prefix or suffix stops at the first {@linkplain #SPECIAL_CHARACTERS 
special character}.
-     *
-     * @param include the pattern for which to get a prefix or suffix without 
special character
-     * @param suffix {@code false} if a prefix is desired, or {@code true} if 
a suffix is desired
-     */
-    private static String prefixOrSuffix(final String include, boolean suffix) 
{
-        int s = suffix ? -1 : include.length();
-        for (int i = SPECIAL_CHARACTERS.length(); --i >= 0; ) {
-            char c = SPECIAL_CHARACTERS.charAt(i);
-            if (suffix) {
-                s = Math.max(s, include.lastIndexOf(c));
-            } else {
-                int p = include.indexOf(c);
-                if (p >= 0 && p < s) {
-                    s = p;
-                }
-            }
-        }
-        return suffix ? include.substring(s + 1) : include.substring(0, s);
-    }
-
-    /**
-     * Returns {@code true} if the given exclude cannot match any include 
patterns.
-     * In case of doubt, returns {@code false}.
-     *
-     * @param exclude the exclude pattern to test
-     * @param fragments the prefixes or suffixes (fragments without special 
characters) of the includes
-     * @param suffix {@code false} if the specified fragments are prefixes, 
{@code true} if they are suffixes
-     * @return {@code true} if it is certain that the exclude pattern cannot 
match, or {@code false} in case of doubt
-     */
-    private static boolean cannotMatch(String exclude, final String[] 
fragments, final boolean suffix) {
-        exclude = prefixOrSuffix(exclude, suffix);
-        for (String fragment : fragments) {
-            int fg = fragment.length();
-            int ex = exclude.length();
-            int length = Math.min(fg, ex);
-            if (suffix) {
-                fg -= length;
-                ex -= length;
-            } else {
-                fg = 0;
-                ex = 0;
-            }
-            if (exclude.regionMatches(ex, fragment, fg, length)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Sorts the given patterns by their length. The main intent is to have 
the empty string first,
-     * while will cause the loops testing for prefixes and suffixes to stop 
almost immediately.
-     * Short prefixes or suffixes are also more likely to be matched.
-     *
-     * @param fragments the fragments to sort in-place
-     * @param suffix {@code false} if the specified fragments are prefixes, 
{@code true} if they are suffixes
-     * @return the given array, or a smaller array if some fragments were 
discarded because redundant
-     */
-    private static String[] sortByLength(final String[] fragments, final 
boolean suffix) {
-        Arrays.sort(fragments, (s1, s2) -> s1.length() - s2.length());
-        int count = 0;
-        /*
-         * Simplify the array of prefixes or suffixes by removing all 
redundant elements.
-         * An element is redundant if there is a shorter prefix or suffix with 
the same characters.
-         */
-        nextBase:
-        for (String fragment : fragments) {
-            for (int i = count; --i >= 0; ) {
-                String base = fragments[i];
-                if (suffix ? fragment.endsWith(base) : 
fragment.startsWith(base)) {
-                    continue nextBase; // Skip this fragment
-                }
-            }
-            fragments[count++] = fragment;
-        }
-        return (fragments.length == count) ? fragments : 
Arrays.copyOf(fragments, count);
-    }
-
-    /**
-     * Returns the given array of patterns with path separator normalized to 
{@code '/'}.
-     * Null or empty patterns are ignored, and duplications are removed.
-     *
-     * @param patterns the patterns to normalize
-     * @param excludes whether the patterns are exclude patterns
-     * @return normalized patterns without null, empty or duplicated patterns
-     */
-    private static String[] normalizePatterns(final Collection<String> 
patterns, final boolean excludes) {
-        if (patterns == null || patterns.isEmpty()) {
-            return new String[0];
-        }
-        // TODO: use `LinkedHashSet.newLinkedHashSet(int)` instead with JDK19.
-        final var normalized = new LinkedHashSet<String>(patterns.size());
-        for (String pattern : patterns) {
-            if (pattern != null && !pattern.isEmpty()) {
-                if (pattern.indexOf(':') <= MAVEN_SYNTAX_THRESHOLD) {
-                    pattern = pattern.replace(File.separatorChar, '/');
-                    if (pattern.endsWith("/")) {
-                        pattern += "**";
-                    }
-                    // Following are okay only when "**" means "0 or more 
directories".
-                    while (pattern.endsWith("/**/**")) {
-                        pattern = pattern.substring(0, pattern.length() - 3);
-                    }
-                    while (pattern.startsWith("**/**/")) {
-                        pattern = pattern.substring(3);
-                    }
-                    pattern = pattern.replace("/**/**/", "/**/");
-                    pattern = pattern.replace("\\", "\\\\")
-                            .replace("[", "\\[")
-                            .replace("]", "\\]")
-                            .replace("{", "\\{")
-                            .replace("}", "\\}");
-                    normalized.add(DEFAULT_SYNTAX + pattern);
-                    /*
-                     * If the pattern starts or ends with "**", Java GLOB 
expects a directory level at
-                     * that location while Maven seems to consider that "**" 
can mean "no directory".
-                     * Add another pattern for reproducing this effect.
-                     */
-                    addPatternsWithOneDirRemoved(normalized, pattern, 0);
-                } else {
-                    normalized.add(pattern);
-                }
-            }
-        }
-        return simplify(normalized, excludes);
-    }
-
-    /**
-     * Adds all variants of the given pattern with {@code **} removed.
-     * This is used for simulating the Maven behavior where {@code "**} may 
match zero directory.
-     * Tests suggest that we need an explicit GLOB pattern with no {@code 
"**"} for matching an absence of directory.
-     *
-     * @param patterns where to add the derived patterns
-     * @param pattern  the pattern for which to add derived forms, without the 
"glob:" syntax prefix
-     * @param end      should be 0 (reserved for recursive invocations of this 
method)
-     */
-    private static void addPatternsWithOneDirRemoved(final Set<String> 
patterns, final String pattern, int end) {
-        final int length = pattern.length();
-        int start;
-        while ((start = pattern.indexOf("**", end)) >= 0) {
-            end = start + 2; // 2 is the length of "**".
-            if (end < length) {
-                if (pattern.charAt(end) != '/') {
-                    continue;
-                }
-                if (start == 0) {
-                    end++; // Ommit the leading slash if there is nothing 
before it.
-                }
-            }
-            if (start > 0 && pattern.charAt(--start) != '/') {
-                continue;
-            }
-            String reduced = pattern.substring(0, start) + 
pattern.substring(end);
-            patterns.add(DEFAULT_SYNTAX + reduced);
-            addPatternsWithOneDirRemoved(patterns, reduced, start);
-        }
-    }
-
-    /**
-     * Applies some heuristic rules for simplifying the set of patterns,
-     * then returns the patterns as an array.
-     *
-     * @param patterns the patterns to simplify and return as an array
-     * @param excludes whether the patterns are exclude patterns
-     * @return the set content as an array, after simplification
-     */
-    private static String[] simplify(Set<String> patterns, boolean excludes) {
-        /*
-         * If the "**" pattern is present, it makes all other patterns useless.
-         * In the case of include patterns, an empty set means to include 
everything.
-         */
-        if (patterns.remove("**")) {
-            patterns.clear();
-            if (excludes) {
-                patterns.add("**");
-            }
-        }
-        return patterns.toArray(String[]::new);
-    }
-
-    /**
-     * Eventually adds the parent directory of the given patterns, without 
duplicated values.
-     * The patterns given to this method should have been normalized.
-     *
-     * @param patterns the normalized include or exclude patterns
-     * @param excludes whether the patterns are exclude patterns
-     * @return pattens of directories to include or exclude
-     */
-    private static String[] directoryPatterns(final String[] patterns, final 
boolean excludes) {
-        // TODO: use `LinkedHashSet.newLinkedHashSet(int)` instead with JDK19.
-        final var directories = new LinkedHashSet<String>(patterns.length);
-        for (String pattern : patterns) {
-            if (pattern.startsWith(DEFAULT_SYNTAX)) {
-                if (excludes) {
-                    if (pattern.endsWith("/**")) {
-                        directories.add(pattern.substring(0, pattern.length() 
- 3));
-                    }
-                } else {
-                    int s = pattern.indexOf(':');
-                    if (pattern.regionMatches(++s, "**/", 0, 3)) {
-                        s = pattern.indexOf('/', s + 3);
-                        if (s < 0) {
-                            return new String[0]; // Pattern is "**", so we 
need to accept everything.
-                        }
-                        directories.add(pattern.substring(0, s));
-                    }
-                }
-            }
-        }
-        return simplify(directories, excludes);
-    }
-
-    /**
-     * Returns {@code true} if at least one pattern requires path to be 
relativized before to be matched.
-     *
-     * @param patterns include or exclude patterns
-     * @return whether at least one pattern require relativization
-     */
-    private static boolean needRelativize(String[] patterns) {
-        for (String pattern : patterns) {
-            if (!pattern.startsWith(DEFAULT_SYNTAX + "**/")) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Creates the path matchers for the given patterns.
-     * The syntax (usually {@value #DEFAULT_SYNTAX}) must be specified for 
each pattern.
-     */
-    private static PathMatcher[] matchers(final FileSystem fs, final String[] 
patterns) {
-        final var matchers = new PathMatcher[patterns.length];
-        for (int i = 0; i < patterns.length; i++) {
-            matchers[i] = fs.getPathMatcher(patterns[i]);
-        }
-        return matchers;
-    }
-
-    /**
-     * {@return whether there is no include or exclude filters}.
-     * In such case, this {@code PathSelector} instance should be ignored.
-     */
-    public boolean isEmpty() {
-        return (includes.length | excludes.length) == 0;
-    }
-
-    /**
-     * {@return a potentially simpler matcher equivalent to this matcher}.
-     */
-    @SuppressWarnings("checkstyle:MissingSwitchDefault")
-    public PathMatcher simplify() {
-        if (!needRelativize && excludes.length == 0) {
-            switch (includes.length) {
-                case 0:
-                    return INCLUDES_ALL;
-                case 1:
-                    return includes[0];
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Determines whether a path is selected.
-     * This is true if the given file matches an include pattern and no 
exclude pattern.
-     *
-     * @param path the pathname to test, must not be {@code null}
-     * @return {@code true} if the given path is selected, {@code false} 
otherwise
-     */
-    @Override
-    public boolean matches(Path path) {
-        if (needRelativize) {
-            path = baseDirectory.relativize(path);
-        }
-        return (includes.length == 0 || isMatched(path, includes))
-                && (excludes.length == 0 || !isMatched(path, excludes));
-    }
-
-    /**
-     * {@return whether the given file matches according to one of the given 
matchers}.
-     */
-    private static boolean isMatched(Path path, PathMatcher[] matchers) {
-        for (PathMatcher matcher : matchers) {
-            if (matcher.matches(path)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Determines whether a directory could contain selected paths.
-     *
-     * @param directory the directory pathname to test, must not be {@code 
null}
-     * @return {@code true} if the given directory might contain selected 
paths, {@code false} if the
-     *         directory will definitively not contain selected paths
-     */
-    public boolean couldHoldSelected(Path directory) {
-        if (baseDirectory.equals(directory)) {
-            return true;
-        }
-        directory = baseDirectory.relativize(directory);
-        return (dirIncludes.length == 0 || isMatched(directory, dirIncludes))
-                && (dirExcludes.length == 0 || !isMatched(directory, 
dirExcludes));
-    }
-
-    /**
-     * {@return a string representation for logging purposes}.
-     */
-    @Override
-    public String toString() {
-        var buffer = new StringBuilder();
-        Fileset.append(buffer, "includes", includePatterns);
-        Fileset.append(buffer.append(", "), "excludes", excludePatterns);
-        return buffer.toString();
-    }
-}
diff --git a/src/test/java/org/apache/maven/plugins/clean/CleanMojoTest.java 
b/src/test/java/org/apache/maven/plugins/clean/CleanMojoTest.java
index 3f0f7cd..c7935de 100644
--- a/src/test/java/org/apache/maven/plugins/clean/CleanMojoTest.java
+++ b/src/test/java/org/apache/maven/plugins/clean/CleanMojoTest.java
@@ -34,6 +34,8 @@ import org.apache.maven.api.plugin.MojoException;
 import org.apache.maven.api.plugin.testing.Basedir;
 import org.apache.maven.api.plugin.testing.InjectMojo;
 import org.apache.maven.api.plugin.testing.MojoTest;
+import org.apache.maven.api.services.PathMatcherFactory;
+import org.apache.maven.impl.DefaultPathMatcherFactory;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 import org.junit.jupiter.api.condition.EnabledOnOs;
@@ -55,6 +57,15 @@ class CleanMojoTest {
 
     private final Log log = mock(Log.class);
 
+    /**
+     * The factory to use for creating patch matcher.
+     * The actual implementation is used rather than a mock because filtering 
is an important part
+     * of this plugin and is tedious to test. Therefore, it is hard to 
guarantee that the tests of
+     * {@code PathMatcherFactory} in Maven core are sufficient, and we want 
this plugin to test it
+     * more.
+     */
+    private final PathMatcherFactory matcherFactory = new 
DefaultPathMatcherFactory();
+
     /**
      * Tests the simple removal of directories
      *
@@ -66,9 +77,9 @@ class CleanMojoTest {
     void basicClean(CleanMojo mojo) throws Exception {
         mojo.execute();
 
-        assertFalse(checkExists(getBasedir() + "/buildDirectory"), "Directory 
exists");
-        assertFalse(checkExists(getBasedir() + "/buildOutputDirectory"), 
"Directory exists");
-        assertFalse(checkExists(getBasedir() + "/buildTestDirectory"), 
"Directory exists");
+        assertFalse(checkExists("buildDirectory"), "Directory exists");
+        assertFalse(checkExists("buildOutputDirectory"), "Directory exists");
+        assertFalse(checkExists("buildTestDirectory"), "Directory exists");
     }
 
     /**
@@ -82,9 +93,9 @@ class CleanMojoTest {
     void cleanNestedStructure(CleanMojo mojo) throws Exception {
         mojo.execute();
 
-        assertFalse(checkExists(getBasedir() + "/target"));
-        assertFalse(checkExists(getBasedir() + "/target/classes"));
-        assertFalse(checkExists(getBasedir() + "/target/test-classes"));
+        assertFalse(checkExists("target"));
+        assertFalse(checkExists("target/classes"));
+        assertFalse(checkExists("target/test-classes"));
     }
 
     /**
@@ -99,10 +110,10 @@ class CleanMojoTest {
     void cleanEmptyDirectories(CleanMojo mojo) throws Exception {
         mojo.execute();
 
-        assertTrue(checkExists(getBasedir() + "/testDirectoryStructure"));
-        assertTrue(checkExists(getBasedir() + 
"/testDirectoryStructure/file.txt"));
-        assertTrue(checkExists(getBasedir() + 
"/testDirectoryStructure/outputDirectory"));
-        assertTrue(checkExists(getBasedir() + 
"/testDirectoryStructure/outputDirectory/file.txt"));
+        assertTrue(checkExists("testDirectoryStructure"));
+        assertTrue(checkExists("testDirectoryStructure/file.txt"));
+        assertTrue(checkExists("testDirectoryStructure/outputDirectory"));
+        
assertTrue(checkExists("testDirectoryStructure/outputDirectory/file.txt"));
     }
 
     /**
@@ -117,18 +128,18 @@ class CleanMojoTest {
         mojo.execute();
 
         // fileset 1
-        assertTrue(checkExists(getBasedir() + "/target"));
-        assertTrue(checkExists(getBasedir() + "/target/classes"));
-        assertFalse(checkExists(getBasedir() + "/target/test-classes"));
-        assertTrue(checkExists(getBasedir() + "/target/subdir"));
-        assertFalse(checkExists(getBasedir() + "/target/classes/file.txt"));
-        assertTrue(checkEmpty(getBasedir() + "/target/classes"));
-        assertFalse(checkEmpty(getBasedir() + "/target/subdir"));
-        assertTrue(checkExists(getBasedir() + "/target/subdir/file.txt"));
+        assertTrue(checkExists("target"));
+        assertTrue(checkExists("target/classes"));
+        assertFalse(checkExists("target/test-classes"));
+        assertTrue(checkExists("target/subdir"));
+        assertFalse(checkExists("target/classes/file.txt"));
+        assertTrue(checkEmpty("target/classes"));
+        assertFalse(checkEmpty("target/subdir"));
+        assertTrue(checkExists("target/subdir/file.txt"));
 
         // fileset 2
-        assertTrue(checkExists(getBasedir() + "/" + "buildOutputDirectory"));
-        assertFalse(checkExists(getBasedir() + "/" + 
"buildOutputDirectory/file.txt"));
+        assertTrue(checkExists("buildOutputDirectory"));
+        assertFalse(checkExists("buildOutputDirectory/file.txt"));
     }
 
     /**
@@ -154,7 +165,7 @@ class CleanMojoTest {
     void missingDirectory(CleanMojo mojo) throws Exception {
         mojo.execute();
 
-        assertFalse(checkExists(getBasedir() + "/does-not-exist"));
+        assertFalse(checkExists("does-not-exist"));
     }
 
     /**
@@ -244,7 +255,7 @@ class CleanMojoTest {
     }
 
     private void testSymlink(LinkCreator linkCreator) throws Exception {
-        Cleaner cleaner = new Cleaner(null, log, false, null, null, false, 
false, true, false);
+        Cleaner cleaner = new Cleaner(null, matcherFactory, log, false, null, 
null, false, false, true, false);
         Path testDir = 
Paths.get("target/test-classes/unit/test-dir").toAbsolutePath();
         Path dirWithLnk = testDir.resolve("dir");
         Path orgDir = testDir.resolve("org-dir");
@@ -270,7 +281,7 @@ class CleanMojoTest {
         Files.write(file, Collections.singleton("Hello world"));
         linkCreator.createLink(jctDir, orgDir);
         // delete
-        cleaner = new Cleaner(null, log, false, null, null, true, false, true, 
false);
+        cleaner = new Cleaner(null, matcherFactory, log, false, null, null, 
true, false, true, false);
         cleaner.delete(dirWithLnk);
         // verify
         assertFalse(Files.exists(file));
@@ -283,16 +294,17 @@ class CleanMojoTest {
      * @param dir a dir or a file
      * @return true if a file/dir exists, false otherwise
      */
-    private boolean checkExists(String dir) {
-        return new File(new File(dir).getAbsolutePath()).exists();
+    private static boolean checkExists(String dir) {
+        return Files.exists(Path.of(getBasedir(), dir));
     }
 
     /**
      * @param dir a directory
      * @return true if a dir is empty, false otherwise
      */
-    private boolean checkEmpty(String dir) {
-        File[] files = new File(dir).listFiles();
+    private static boolean checkEmpty(String dir) {
+        Path path = Path.of(getBasedir(), dir);
+        File[] files = path.toFile().listFiles();
         return files == null || files.length == 0;
     }
 }
diff --git a/src/test/java/org/apache/maven/plugins/clean/CleanerTest.java 
b/src/test/java/org/apache/maven/plugins/clean/CleanerTest.java
index 8de70d1..8c84cee 100644
--- a/src/test/java/org/apache/maven/plugins/clean/CleanerTest.java
+++ b/src/test/java/org/apache/maven/plugins/clean/CleanerTest.java
@@ -25,6 +25,8 @@ import java.nio.file.attribute.PosixFilePermissions;
 import java.util.Set;
 
 import org.apache.maven.api.plugin.Log;
+import org.apache.maven.api.services.PathMatcherFactory;
+import org.apache.maven.impl.DefaultPathMatcherFactory;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 import org.junit.jupiter.api.condition.OS;
@@ -54,12 +56,21 @@ class CleanerTest {
 
     private final Log log = mock(Log.class);
 
+    /**
+     * The factory to use for creating patch matcher.
+     * The actual implementation is used rather than a mock because filtering 
is an important part
+     * of this plugin and is tedious to test. Therefore, it is hard to 
guarantee that the tests of
+     * {@code PathMatcherFactory} in Maven core are sufficient, and we want 
this plugin to test it
+     * more.
+     */
+    private final PathMatcherFactory matcherFactory = new 
DefaultPathMatcherFactory();
+
     @Test
     @DisabledOnOs(OS.WINDOWS)
     void deleteSucceedsDeeply(@TempDir Path tempDir) throws Exception {
         final Path basedir = 
createDirectory(tempDir.resolve("target")).toRealPath();
         final Path file = createFile(basedir.resolve("file"));
-        final var cleaner = new Cleaner(null, log, false, null, null, false, 
false, true, false);
+        final var cleaner = new Cleaner(null, matcherFactory, log, false, 
null, null, false, false, true, false);
         cleaner.delete(basedir);
         assertFalse(exists(basedir));
         assertFalse(exists(file));
@@ -74,7 +85,7 @@ class CleanerTest {
         // Remove the executable flag to prevent directory listing, which will 
result in a DirectoryNotEmptyException.
         final Set<PosixFilePermission> permissions = 
PosixFilePermissions.fromString("rw-rw-r--");
         setPosixFilePermissions(basedir, permissions);
-        final var cleaner = new Cleaner(null, log, false, null, null, false, 
false, true, false);
+        final var cleaner = new Cleaner(null, matcherFactory, log, false, 
null, null, false, false, true, false);
         final var exception = assertThrows(AccessDeniedException.class, () -> 
cleaner.delete(basedir));
         verify(log, times(1)).warn(any(CharSequence.class), 
any(Throwable.class));
         assertTrue(exception.getMessage().contains(basedir.toString()));
@@ -88,7 +99,7 @@ class CleanerTest {
         // Remove the executable flag to prevent directory listing, which will 
result in a DirectoryNotEmptyException.
         final Set<PosixFilePermission> permissions = 
PosixFilePermissions.fromString("rw-rw-r--");
         setPosixFilePermissions(basedir, permissions);
-        final var cleaner = new Cleaner(null, log, false, null, null, false, 
false, true, true);
+        final var cleaner = new Cleaner(null, matcherFactory, log, false, 
null, null, false, false, true, true);
         final var exception = assertThrows(AccessDeniedException.class, () -> 
cleaner.delete(basedir));
         assertTrue(exception.getMessage().contains(basedir.toString()));
     }
@@ -102,7 +113,7 @@ class CleanerTest {
         // Remove the writable flag to prevent deletion of the file, which 
will result in an AccessDeniedException.
         final Set<PosixFilePermission> permissions = 
PosixFilePermissions.fromString("r-xr-xr-x");
         setPosixFilePermissions(basedir, permissions);
-        final var cleaner = new Cleaner(null, log, false, null, null, false, 
false, false, false);
+        final var cleaner = new Cleaner(null, matcherFactory, log, false, 
null, null, false, false, false, false);
         assertDoesNotThrow(() -> cleaner.delete(basedir));
         verify(log, times(1)).warn(any(CharSequence.class), 
any(Throwable.class));
         InOrder inOrder = inOrder(log);
@@ -120,7 +131,7 @@ class CleanerTest {
         // Remove the writable flag to prevent deletion of the file, which 
will result in an AccessDeniedException.
         final Set<PosixFilePermission> permissions = 
PosixFilePermissions.fromString("r-xr-xr-x");
         setPosixFilePermissions(basedir, permissions);
-        final var cleaner = new Cleaner(null, log, false, null, null, false, 
false, false, false);
+        final var cleaner = new Cleaner(null, matcherFactory, log, false, 
null, null, false, false, false, false);
         assertDoesNotThrow(() -> cleaner.delete(basedir));
         verify(log, never()).warn(any(CharSequence.class), 
any(Throwable.class));
     }

Reply via email to