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

maartenc pushed a commit to branch mappedresources-npe-fix
in repository https://gitbox.apache.org/repos/asf/ant.git

commit c6fd720491c8d71a486ca0aaa90922249ff54d82
Author: Maarten Coene <[email protected]>
AuthorDate: Sat Aug 22 23:19:11 2026 +0200

    Fixed NPE in <mappedresources> when the mapper doesn't apply to a resource
---
 WHATSNEW                                           |   4 +
 .../types/resources/MappedResourceCollection.java  |  14 ++-
 .../types/resources/mappedresources-test.xml       | 102 +++++++++++++++++++++
 3 files changed, 115 insertions(+), 5 deletions(-)

diff --git a/WHATSNEW b/WHATSNEW
index 23fb76db9..ffcb2c049 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -54,6 +54,10 @@ Fixed bugs:
    fixed.
    Bugzilla Report 70158
 
+ * <mappedresources> with "enableMultipleMappings" set to "true" threw
+   a NullPointerException if the mapper didn't apply to one of the
+   resources. Unmapped resources are now omitted from the collection.
+
 Changes from Ant 1.10.16 TO Ant 1.10.17
 =======================================
 
diff --git 
a/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.java 
b/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.java
index 544c60b76..6c5b9e953 100644
--- 
a/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.java
+++ 
b/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.java
@@ -20,7 +20,6 @@ package org.apache.tools.ant.types.resources;
 import java.io.File;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.Objects;
 import java.util.Stack;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -237,10 +236,15 @@ public class MappedResourceCollection
         Stream<MappedResource> stream;
         if (enableMultipleMappings) {
             stream = nested.stream()
-                .flatMap(r -> Stream.of(m.mapFileName(r.getName()))
-                    .filter(Objects::nonNull)
-                    .map(MergingMapper::new)
-                    .map(mm -> new MappedResource(r, mm)));
+                .flatMap(r -> {
+                    String[] names = m.mapFileName(r.getName());
+                    if (names == null) {
+                        return Stream.empty();
+                    }
+                    return Stream.of(names)
+                                 .map(MergingMapper::new)
+                                 .map(mm -> new MappedResource(r, mm));
+                });
         } else {
             stream = nested.stream().map(r -> new MappedResource(r, m));
         }
diff --git a/src/tests/antunit/types/resources/mappedresources-test.xml 
b/src/tests/antunit/types/resources/mappedresources-test.xml
new file mode 100644
index 000000000..4752554f3
--- /dev/null
+++ b/src/tests/antunit/types/resources/mappedresources-test.xml
@@ -0,0 +1,102 @@
+<?xml version="1.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
+
+      https://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.
+-->
+<project default="antunit" xmlns:au="antlib:org.apache.ant.antunit">
+
+  <import file="../../antunit-base.xml"/>
+
+  <target name="setUp">
+    <echo file="${input}/a.txt">a</echo>
+    <echo file="${input}/b.dat">b</echo>
+    <echo file="${input}/c.txt">c</echo>
+  </target>
+
+  <target name="testMultipleMappingsSkipsUnmappedResources">
+    <mappedresources id="mapped" enableMultipleMappings="true">
+      <filelist dir="${input}" files="a.txt,b.dat,c.txt"/>
+      <globmapper from="*.txt" to="*.bak"/>
+    </mappedresources>
+    <resourcecount property="count">
+      <mappedresources refid="mapped"/>
+    </resourcecount>
+    <au:assertPropertyEquals name="count" value="2"/>
+    <pathconvert property="names" pathsep=",">
+      <mappedresources refid="mapped"/>
+    </pathconvert>
+    <au:assertPropertyEquals name="names" value="a.bak,c.bak"/>
+  </target>
+
+  <target name="testMultipleMappingsIsEmptyWhenNothingMaps">
+    <mappedresources id="mapped" enableMultipleMappings="true">
+      <filelist dir="${input}" files="a.txt,b.dat,c.txt"/>
+      <globmapper from="*.nomatch" to="*.bak"/>
+    </mappedresources>
+    <resourcecount property="count">
+      <mappedresources refid="mapped"/>
+    </resourcecount>
+    <au:assertPropertyEquals name="count" value="0"/>
+    <au:assertEquals expected="" actual="${toString:mapped}"/>
+  </target>
+
+  <target name="testMultipleMappingsKeepsEveryMappedName">
+    <mappedresources id="mapped" enableMultipleMappings="true">
+      <filelist dir="${input}" files="a.txt,b.dat"/>
+      <compositemapper>
+        <globmapper from="a.*" to="one.*"/>
+        <globmapper from="a.*" to="two.*"/>
+      </compositemapper>
+    </mappedresources>
+    <resourcecount property="count">
+      <mappedresources refid="mapped"/>
+    </resourcecount>
+    <au:assertPropertyEquals name="count" value="2"/>
+    <pathconvert property="names" pathsep=",">
+      <mappedresources refid="mapped"/>
+    </pathconvert>
+    <au:assertPropertyEquals name="names" value="one.txt,two.txt"/>
+  </target>
+
+  <target name="testMultipleMappingsWithCachingIsStableAcrossIterations">
+    <mappedresources id="mapped" enableMultipleMappings="true" cache="true">
+      <filelist dir="${input}" files="a.txt,b.dat,c.txt"/>
+      <globmapper from="*.txt" to="*.bak"/>
+    </mappedresources>
+    <pathconvert property="first" pathsep=",">
+      <mappedresources refid="mapped"/>
+    </pathconvert>
+    <pathconvert property="second" pathsep=",">
+      <mappedresources refid="mapped"/>
+    </pathconvert>
+    <au:assertPropertyEquals name="first" value="a.bak,c.bak"/>
+    <au:assertPropertyEquals name="second" value="a.bak,c.bak"/>
+  </target>
+
+  <target name="testCopyWithNonMatchingMapper">
+    <mkdir dir="${output}"/>
+    <copy todir="${output}">
+      <mappedresources enableMultipleMappings="true">
+        <filelist dir="${input}" files="a.txt,b.dat,c.txt"/>
+        <globmapper from="*.dat" to="*.copied"/>
+      </mappedresources>
+    </copy>
+    <au:assertFileExists file="${output}/b.copied"/>
+    <au:assertFileDoesntExist file="${output}/a.txt"/>
+    <au:assertFileDoesntExist file="${output}/a.copied"/>
+    <au:assertFilesMatch expected="${input}/b.dat"
+                         actual="${output}/b.copied"/>
+  </target>
+</project>

Reply via email to