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

mbenson pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ant.git

commit 90ed3ff6cca8634e38d7c3c82858ce48c9c4be2b
Author: Matt Benson <[email protected]>
AuthorDate: Wed Feb 9 13:00:49 2022 -0600

    pathconvert to outputstream so that logging requires no in-memory String 
accumulation
---
 .../org/apache/tools/ant/taskdefs/PathConvert.java | 126 ++++++++++-----------
 1 file changed, 60 insertions(+), 66 deletions(-)

diff --git a/src/main/org/apache/tools/ant/taskdefs/PathConvert.java 
b/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
index fa68c3f..be46bbd 100644
--- a/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
+++ b/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
@@ -18,10 +18,16 @@
 package org.apache.tools.ant.taskdefs;
 
 import java.io.File;
-import java.util.ArrayList;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 import java.util.List;
-import java.util.StringTokenizer;
+import java.util.Objects;
 import java.util.Vector;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.Project;
@@ -31,12 +37,12 @@ import org.apache.tools.ant.types.EnumeratedAttribute;
 import org.apache.tools.ant.types.Mapper;
 import org.apache.tools.ant.types.Path;
 import org.apache.tools.ant.types.Reference;
-import org.apache.tools.ant.types.Resource;
 import org.apache.tools.ant.types.ResourceCollection;
 import org.apache.tools.ant.types.resources.Resources;
 import org.apache.tools.ant.types.resources.Union;
 import org.apache.tools.ant.util.FileNameMapper;
 import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.PropertyOutputStream;
 
 /**
  * Converts path and classpath information to a specific target OS
@@ -195,7 +201,7 @@ public class PathConvert extends Task {
     private synchronized Resources getPath() {
         if (path == null) {
             path = new Resources(getProject());
-            path.setCache(true);
+            path.setCache(false);
         }
         return path;
     }
@@ -344,56 +350,18 @@ public class PathConvert extends Task {
             }
             validateSetup(); // validate our setup
 
-            // Currently, we deal with only two path formats: Unix and Windows
-            // And Unix is everything that is not Windows
-            // (with the exception for NetWare and OS/2 below)
-
-            // for NetWare and OS/2, piggy-back on Windows, since here and
-            // in the apply code, the same assumptions can be made as with
-            // windows - that \\ is an OK separator, and do comparisons
-            // case-insensitive.
-            String fromDirSep = onWindows ? "\\" : "/";
-
-            StringBuilder rslt = new StringBuilder();
-
-            ResourceCollection resources = isPreserveDuplicates() ? path : new 
Union(path);
-            List<String> ret = new ArrayList<>();
-            FileNameMapper mapperImpl = mapper == null ? new IdentityMapper() 
: mapper.getImplementation();
-            for (Resource r : resources) {
-                String[] mapped = mapperImpl.mapFileName(String.valueOf(r));
-                for (int m = 0; mapped != null && m < mapped.length; ++m) {
-                    ret.add(mapped[m]);
-                }
-            }
             boolean first = true;
-            for (String string : ret) {
-                String elem = mapElement(string); // Apply the path prefix map
-
-                // Now convert the path and file separator characters from the
-                // current os to the target os.
-
-                if (!first) {
-                    rslt.append(pathSep);
-                }
-                first = false;
-
-                StringTokenizer stDirectory = new StringTokenizer(elem, 
fromDirSep, true);
-
-                while (stDirectory.hasMoreTokens()) {
-                    String token = stDirectory.nextToken();
-                    rslt.append(fromDirSep.equals(token) ? dirSep : token);
-                }
-            }
-            // Place the result into the specified property,
-            // unless setonempty == false
-            if (setonempty || rslt.length() > 0) {
-                String value = rslt.toString();
-                if (property == null) {
-                    log(value);
-                } else {
-                    log("Set property " + property + " = " + value, 
Project.MSG_VERBOSE);
-                    getProject().setNewProperty(property, value);
+            try (Writer w = new OutputStreamWriter(createOutputStream())) {
+                for (String s : (Iterable<String>) 
streamResources()::iterator) {
+                    if (first) {
+                        first = false;
+                    } else {
+                        w.write(pathSep);
+                    }
+                    w.write(s);
                 }
+            } catch (IOException e) {
+                throw new BuildException(e);
             }
         } finally {
             path = savedPath;
@@ -402,6 +370,44 @@ public class PathConvert extends Task {
         }
     }
 
+    private OutputStream createOutputStream() {
+        if (property == null) {
+            return new LogOutputStream(this);
+        }
+        return new PropertyOutputStream(getProject(), property) {
+            @Override
+            public void close() {
+                if (setonempty || size() > 0) {
+                    super.close();
+                    log("Set property " + property + " = " + 
getProject().getProperty(property), Project.MSG_VERBOSE);
+                }
+            }
+        };
+    }
+
+    private Stream<String> streamResources() {
+        ResourceCollection resources = isPreserveDuplicates() ? path : 
Union.getInstance(path);
+        FileNameMapper mapperImpl = mapper == null ? new IdentityMapper() : 
mapper.getImplementation();
+
+        final boolean parallel = false;
+        Stream<String> result = StreamSupport.stream(resources.spliterator(), 
parallel).map(String::valueOf)
+            
.map(mapperImpl::mapFileName).filter(Objects::nonNull).flatMap(Stream::of).map(this::mapElement);
+
+        // Currently, we deal with only two path formats: Unix and Windows
+        // And Unix is everything that is not Windows
+        // (with the exception for NetWare and OS/2 below)
+
+        // for NetWare and OS/2, piggy-back on Windows, since here and
+        // in the apply code, the same assumptions can be made as with
+        // windows - that \\ is an OK separator, and do comparisons
+        // case-insensitive.
+        final String fromDirSep = onWindows ? "\\" : "/";
+        if (fromDirSep.equals(dirSep)) {
+            return result;
+        }
+        return result.map(s -> s.replace(fromDirSep, dirSep));
+    }
+
     /**
      * Apply the configured map to a path element. The map is used to convert
      * between Windows drive letters and Unix paths. If no map is configured,
@@ -411,20 +417,8 @@ public class PathConvert extends Task {
      * @return String Updated element.
      */
     private String mapElement(String elem) {
-        // Iterate over the map entries and apply each one.
-        // Stop when one of the entries actually changes the element.
-
-        for (MapEntry entry : prefixMap) {
-            String newElem = entry.apply(elem);
-
-            // Note I'm using "!=" to see if we got a new object back from
-            // the apply method.
-
-            if (newElem != elem) {
-                return newElem;
-            }
-        }
-        return elem;
+        final Predicate<Object> changed = o -> o != elem;
+        return prefixMap.stream().map(e -> 
e.apply(elem)).filter(changed).findFirst().orElse(elem);
     }
 
     /**

Reply via email to