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

gnodet pushed a commit to branch maven-3.9.x
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/maven-3.9.x by this push:
     new e7691912d8 [MNG-8707] Add methods to remove compile and test source 
roots (#2275)
e7691912d8 is described below

commit e7691912d8b1296f961eb7db862c50d296fe8e98
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Wed Apr 30 23:10:07 2025 +0200

    [MNG-8707] Add methods to remove compile and test source roots (#2275)
---
 .../org/apache/maven/project/MavenProject.java     | 290 +++++++++++++++++++--
 1 file changed, 271 insertions(+), 19 deletions(-)

diff --git 
a/maven-core/src/main/java/org/apache/maven/project/MavenProject.java 
b/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
index 97d595291e..29a18ff0b2 100644
--- a/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
+++ b/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
@@ -21,12 +21,16 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.Writer;
+import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
@@ -276,40 +280,104 @@ public DependencyManagement getDependencyManagement() {
     // Test and compile sourceroots.
     // ----------------------------------------------------------------------
 
+    /**
+     * Sanitizes a path by trimming it and converting it to an absolute path.
+     *
+     * @param path the path to sanitize
+     * @return the sanitized path, or null if the input path is null, empty, 
or consists only of whitespace
+     */
+    private String sanitizePath(String path) {
+        if (path == null) {
+            return null;
+        }
+        path = path.trim();
+        if (path.isEmpty()) {
+            return null;
+        }
+        File file = new File(path);
+        if (file.isAbsolute()) {
+            return file.getAbsolutePath();
+        } else if (".".equals(path)) {
+            return getBasedir().getAbsolutePath();
+        } else {
+            return new File(getBasedir(), path).getAbsolutePath();
+        }
+    }
+
     private void addPath(List<String> paths, String path) {
-        if (path != null) {
-            path = path.trim();
-            if (path.length() > 0) {
-                File file = new File(path);
-                if (file.isAbsolute()) {
-                    path = file.getAbsolutePath();
-                } else if (".".equals(path)) {
-                    path = getBasedir().getAbsolutePath();
-                } else {
-                    path = new File(getBasedir(), path).getAbsolutePath();
-                }
+        String sanitizedPath = sanitizePath(path);
+        if (sanitizedPath != null && !paths.contains(sanitizedPath)) {
+            paths.add(sanitizedPath);
+        }
+    }
 
-                if (!paths.contains(path)) {
-                    paths.add(path);
-                }
-            }
+    private void removePath(List<String> paths, String path) {
+        String sanitizedPath = sanitizePath(path);
+        if (sanitizedPath != null) {
+            paths.remove(sanitizedPath);
         }
     }
 
+    /**
+     * Adds the specified path to the list of compile source roots for this 
project.
+     *
+     * @param path the source root to add
+     */
     public void addCompileSourceRoot(String path) {
-        addPath(getCompileSourceRoots(), path);
+        addPath(compileSourceRoots, path);
     }
 
+    /**
+     * Removes the specified path from the list of compile source roots.
+     *
+     * @param path the source root to remove
+     * @since 3.9.10
+     */
+    public void removeCompileSourceRoot(String path) {
+        removePath(compileSourceRoots, path);
+    }
+
+    /**
+     * Adds the specified path to the list of test compile source roots for 
this project.
+     *
+     * @param path the test source root to add
+     */
     public void addTestCompileSourceRoot(String path) {
-        addPath(getTestCompileSourceRoots(), path);
+        addPath(testCompileSourceRoots, path);
+    }
+
+    /**
+     * Removes the specified path from the list of test compile source roots.
+     *
+     * @param path the test source root to remove
+     * @since 3.9.10
+     */
+    public void removeTestCompileSourceRoot(String path) {
+        removePath(testCompileSourceRoots, path);
     }
 
+    /**
+     * Gets the list of compile source roots for this project.
+     * <p>
+     * <strong>Note:</strong> The collection returned by this method should 
not be modified directly.
+     * Use {@link #addCompileSourceRoot(String)} and {@link 
#removeCompileSourceRoot(String)} methods instead.
+     *
+     * @return a list of compile source roots
+     */
     public List<String> getCompileSourceRoots() {
-        return compileSourceRoots;
+        return new LoggingList<>(compileSourceRoots, "compileSourceRoots");
     }
 
+    /**
+     * Gets the list of test compile source roots for this project.
+     * <p>
+     * <strong>Note:</strong> The collection returned by this method should 
not be modified directly.
+     * Use {@link #addTestCompileSourceRoot(String)} and {@link 
#removeTestCompileSourceRoot(String)} methods instead.
+     *
+     * @return a list of test compile source roots
+     */
     public List<String> getTestCompileSourceRoots() {
-        return testCompileSourceRoots;
+        return new LoggingList<>(testCompileSourceRoots, 
"testCompileSourceRoots");
     }
 
     public List<String> getCompileClasspathElements() throws 
DependencyResolutionRequiredException {
@@ -1120,6 +1188,190 @@ private static String getProjectReferenceId(String 
groupId, String artifactId, S
         return buffer.toString();
     }
 
+    /**
+     * A List implementation that logs warnings when modified directly instead 
of using the proper add/remove methods.
+     * This is a wrapper that delegates all operations to the underlying list, 
so modifications to this list
+     * will affect the original list.
+     *
+     * @param <E> the type of elements in the list
+     * @since 3.9.10
+     */
+    private class LoggingList<E> extends AbstractList<E> {
+        private static final String DISABLE_WARNINGS_PROPERTY = 
"maven.project.sourceRoots.warningsDisabled";
+        private final List<E> delegate;
+        private final String collectionName;
+
+        LoggingList(List<E> delegate, String collectionName) {
+            this.delegate = delegate;
+            this.collectionName = collectionName;
+        }
+
+        private void logWarning(String method) {
+            // Check if warnings are disabled
+            String property = 
getProperties().getProperty(DISABLE_WARNINGS_PROPERTY);
+            if (property == null) {
+                property = System.getProperty(DISABLE_WARNINGS_PROPERTY);
+            }
+            if (Boolean.parseBoolean(property)) {
+                return;
+            }
+
+            LOGGER.warn("Direct modification of " + collectionName + " through 
" + method
+                    + "() is deprecated and will not work in Maven 4.0.0. "
+                    + "Please use the add/remove methods instead. If you're 
using a plugin that causes this warning, "
+                    + "please upgrade to the latest version and report an 
issue if the warning persists. "
+                    + "To disable these warnings, set -D" + 
DISABLE_WARNINGS_PROPERTY + "=true on the command line, "
+                    + "in the .mvn/maven.config file, or in project POM 
properties.");
+            // Log a stack trace to help identify the caller
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Stack trace", new Exception("Stack trace"));
+            }
+        }
+
+        @Override
+        public E get(int index) {
+            return delegate.get(index);
+        }
+
+        @Override
+        public int size() {
+            return delegate.size();
+        }
+
+        @Override
+        public E set(int index, E element) {
+            logWarning("set");
+            return delegate.set(index, element);
+        }
+
+        @Override
+        public void add(int index, E element) {
+            logWarning("add");
+            delegate.add(index, element);
+        }
+
+        @Override
+        public E remove(int index) {
+            logWarning("remove");
+            return delegate.remove(index);
+        }
+
+        @Override
+        public void clear() {
+            logWarning("clear");
+            delegate.clear();
+        }
+
+        @Override
+        public boolean addAll(int index, Collection<? extends E> c) {
+            logWarning("addAll");
+            return delegate.addAll(index, c);
+        }
+
+        @Override
+        public Iterator<E> iterator() {
+            return new Iterator<E>() {
+                private final Iterator<E> it = delegate.iterator();
+
+                @Override
+                public boolean hasNext() {
+                    return it.hasNext();
+                }
+
+                @Override
+                public E next() {
+                    return it.next();
+                }
+
+                @Override
+                public void remove() {
+                    logWarning("iterator.remove");
+                    it.remove();
+                }
+            };
+        }
+
+        @Override
+        public ListIterator<E> listIterator() {
+            return listIterator(0);
+        }
+
+        @Override
+        public ListIterator<E> listIterator(int index) {
+            return new ListIterator<E>() {
+                private final ListIterator<E> it = 
delegate.listIterator(index);
+
+                @Override
+                public boolean hasNext() {
+                    return it.hasNext();
+                }
+
+                @Override
+                public E next() {
+                    return it.next();
+                }
+
+                @Override
+                public boolean hasPrevious() {
+                    return it.hasPrevious();
+                }
+
+                @Override
+                public E previous() {
+                    return it.previous();
+                }
+
+                @Override
+                public int nextIndex() {
+                    return it.nextIndex();
+                }
+
+                @Override
+                public int previousIndex() {
+                    return it.previousIndex();
+                }
+
+                @Override
+                public void remove() {
+                    logWarning("listIterator.remove");
+                    it.remove();
+                }
+
+                @Override
+                public void set(E e) {
+                    logWarning("listIterator.set");
+                    it.set(e);
+                }
+
+                @Override
+                public void add(E e) {
+                    logWarning("listIterator.add");
+                    it.add(e);
+                }
+            };
+        }
+
+        @Override
+        public List<E> subList(int fromIndex, int toIndex) {
+            return new LoggingList<>(delegate.subList(fromIndex, toIndex), 
collectionName);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return delegate.equals(o);
+        }
+
+        @Override
+        public int hashCode() {
+            return delegate.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return delegate.toString();
+        }
+    }
+
     /**
      * Sets the value of the context value of this project identified by the 
given key. If the supplied value is
      * <code>null</code>, the context value is removed from this project. 
Context values are intended to allow core

Reply via email to