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>
