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

paulk pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit a95378bb503f59b5802934ce90cd4227c51cdeeb
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Mar 25 16:38:06 2020 +1000

    GROOVY-9478: Groovy Ant task could support Ant resources (closes #1204)
---
 .../main/java/org/codehaus/groovy/ant/Groovy.java  | 160 ++++++++++++++++-----
 .../groovy-ant/src/spec/doc/groovy-ant-task.adoc   |  50 ++++++-
 .../org/codehaus/groovy/ant/GroovyTest.xml         |  32 +++++
 .../groovy/org/codehaus/groovy/ant/GroovyTest.java |  40 ++++++
 4 files changed, 239 insertions(+), 43 deletions(-)

diff --git 
a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovy.java 
b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovy.java
index 7ec8aef..12c3c96 100644
--- a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovy.java
+++ b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovy.java
@@ -28,11 +28,16 @@ import groovy.util.CharsetToolkit;
 import org.apache.groovy.io.StringBuilderWriter;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
 import org.apache.tools.ant.taskdefs.Java;
 import org.apache.tools.ant.types.Commandline;
 import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterChain;
 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.FileResource;
 import org.apache.tools.ant.util.FileUtils;
 import org.codehaus.groovy.control.CompilationFailedException;
 import org.codehaus.groovy.control.CompilerConfiguration;
@@ -42,11 +47,15 @@ import org.codehaus.groovy.runtime.InvokerHelper;
 import org.codehaus.groovy.runtime.ResourceGroovyMethods;
 import org.codehaus.groovy.tools.ErrorReporter;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
 import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.io.Reader;
@@ -55,6 +64,7 @@ import java.lang.reflect.Field;
 import java.nio.charset.Charset;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.List;
 import java.util.Vector;
 
 /**
@@ -69,6 +79,11 @@ public class Groovy extends Java {
     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
 
     /**
+     * encoding; set to null or empty means 'default'
+     */
+    private String encoding = null;
+
+    /**
      * output encoding; set to null or empty means 'default'
      */
     private String outputEncoding = null;
@@ -81,9 +96,9 @@ public class Groovy extends Java {
     private final Vector<FileSet> filesets = new Vector<FileSet>();
 
     /**
-     * input file
+     * The input resource
      */
-    private File srcFile = null;
+    private Resource src = null;
 
     /**
      * input command
@@ -91,7 +106,7 @@ public class Groovy extends Java {
     private String command = "";
 
     /**
-     * Results Output file.
+     * Results Output file
      */
     private File output = null;
 
@@ -109,6 +124,8 @@ public class Groovy extends Java {
     private String scriptBaseClass;
     private String configscript;
 
+    private final List<FilterChain> filterChains = new Vector<>();
+
     /**
      * Compiler configuration.
      * <p>
@@ -141,6 +158,18 @@ public class Groovy extends Java {
     }
 
     /**
+     * Declare the encoding to use when inputting from a resource;
+     * If not supplied or the empty encoding is supplied, a guess will be made 
for file resources,
+     * otherwise the platform's default encoding will be used.
+     *
+     * @param encoding the character encoding to use.
+     * @since 3.0.3
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
      * Should a new GroovyShell be used when forking. Special variables won't 
be available
      * but you don't need Ant in the classpath.
      *
@@ -171,12 +200,12 @@ public class Groovy extends Java {
 
     /**
      * Set the name of the file to be run. The folder of the file is 
automatically added to the classpath.
-     * Required unless statements are enclosed in the build file
+     * Required unless statements are enclosed in the build file or a nested 
resource is supplied.
      *
      * @param srcFile the file containing the groovy script to execute
      */
     public void setSrc(final File srcFile) {
-        this.srcFile = srcFile;
+        addConfigured(new FileResource(srcFile));
     }
 
     /**
@@ -186,14 +215,14 @@ public class Groovy extends Java {
      * @param txt the inline groovy commands to execute
      */
     public void addText(String txt) {
-        log("addText('" + txt + "')", Project.MSG_VERBOSE);
+        log.verbose("addText('" + txt + "')");
         this.command += txt;
     }
 
     /**
-     * Adds a set of files (nested fileset attribute).
+     * Adds a fileset (nested fileset attribute) which should represent a 
single source file.
      *
-     * @param set the fileset representing source files
+     * @param set the fileset representing a source file
      */
     public void addFileset(FileSet set) {
         filesets.addElement(set);
@@ -311,14 +340,28 @@ public class Groovy extends Java {
 
         command = command.trim();
 
-        if (srcFile == null && command.length() == 0 && filesets.isEmpty()) {
-            throw new BuildException("Source file does not exist!", 
getLocation());
+        // process filesets
+        for (FileSet next : filesets) {
+            for (Resource res : next) {
+                if (src == null) {
+                    src = res;
+                } else {
+                    throw new BuildException("A single source resource must be 
provided!", getLocation());
+                }
+            }
+        }
+
+        if (src == null && command.length() == 0) {
+            throw new BuildException("Source does not exist!", getLocation());
         }
 
-        if (srcFile != null && !srcFile.exists()) {
-            throw new BuildException("Source file does not exist!", 
getLocation());
+        if (src != null && !src.isExists()) {
+            throw new BuildException("Source resource does not exist!", 
getLocation());
         }
 
+        if (outputEncoding == null || outputEncoding.isEmpty()) {
+            outputEncoding = Charset.defaultCharset().name();
+        }
         try {
             PrintStream out = System.out;
             try {
@@ -326,21 +369,55 @@ public class Groovy extends Java {
                     log.verbose("Opening PrintStream to output file " + 
output);
                     BufferedOutputStream bos = new BufferedOutputStream(
                             new FileOutputStream(output.getAbsolutePath(), 
append));
-                    out = new PrintStream(bos, false,
-                            (outputEncoding == null || 
outputEncoding.isEmpty()) ? Charset.defaultCharset().name() : outputEncoding);
+                    out = new PrintStream(bos, false, outputEncoding);
                 }
 
                 // if there are no groovy statements between the enclosing 
Groovy tags
-                // then read groovy statements in from a text file using the 
src attribute
+                // then read groovy statements in from a resource using the 
src attribute
                 if (command == null || command.trim().length() == 0) {
-                    if (srcFile == null || !srcFile.exists()) {
-                        throw new BuildException("Source file does not 
exist!", getLocation());
+                    Reader reader;
+                    if (src instanceof FileResource) {
+                        File file = ((FileResource) src).getFile();
+                        createClasspath().add(new Path(getProject(), 
file.getParentFile().getCanonicalPath()));
+                        if (encoding != null && !encoding.isEmpty()) {
+                            reader = new LineNumberReader(new 
InputStreamReader(new FileInputStream(file), encoding));
+                        } else {
+                            reader = new CharsetToolkit(file).getReader();
+                        }
+                    } else {
+                        if (encoding != null && !encoding.isEmpty()) {
+                            reader = new InputStreamReader(new 
BufferedInputStream(src.getInputStream()), encoding);
+                        } else {
+                            reader = new InputStreamReader(new 
BufferedInputStream(src.getInputStream()), Charset.defaultCharset());
+                        }
+                    }
+                    try {
+                        final long len = src.getSize();
+                        log.debug("resource size = " + (len != 
Resource.UNKNOWN_SIZE ? String.valueOf(len) : "unknown"));
+                        if (len == 0) {
+                            log.info("Ignoring empty resource");
+                            command = null;
+                        } else {
+                            try (ChainReaderHelper.ChainReader chainReader = 
new ChainReaderHelper(getProject(), reader, filterChains).with(crh -> {
+                                if (len != Resource.UNKNOWN_SIZE && len <= 
Integer.MAX_VALUE) {
+                                    crh.setBufferSize((int) len);
+                                }
+                            }).getAssembledReader()) {
+                                command = chainReader.readFully();
+                            }
+                        }
+                    } catch (final IOException ioe) {
+                        throw new BuildException("Unable to load resource: ", 
ioe, getLocation());
+                    }
+                } else {
+                    if (src != null) {
+                        log.info("Ignoring supplied resource as direct script 
text found");
                     }
-                    createClasspath().add(new Path(getProject(), 
srcFile.getParentFile().getCanonicalPath()));
-                    command = getText(new CharsetToolkit(srcFile).getReader());
                 }
 
-                execGroovy(command, out);
+                if (command != null) {
+                    execGroovy(command, out);
+                }
 
             } finally {
                 if (out != null && out != System.out) {
@@ -351,20 +428,7 @@ public class Groovy extends Java {
             throw new BuildException(e, getLocation());
         }
 
-        log.verbose("statements executed successfully");
-    }
-
-    private static String getText(BufferedReader reader) throws IOException {
-        StringBuilder answer = new StringBuilder();
-        // reading the content of the file within a char buffer allow to keep 
the correct line endings
-        char[] charBuffer = new char[4096];
-        int nbCharRead = 0;
-        while ((nbCharRead = reader.read(charBuffer)) != -1) {
-            // appends buffer
-            answer.append(charBuffer, 0, nbCharRead);
-        }
-        reader.close();
-        return answer.toString();
+        log.verbose("Statements executed successfully");
     }
 
     public Commandline.Argument createArg() {
@@ -372,6 +436,25 @@ public class Groovy extends Java {
     }
 
     /**
+     * Add the FilterChain element.
+     * @param filter the filter to add
+     */
+    public final void addFilterChain(FilterChain filter) {
+        filterChains.add(filter);
+    }
+
+    /**
+     * Set the source resource.
+     * @param a the resource to load as a single element Resource collection.
+     */
+    public void addConfigured(ResourceCollection a) {
+        if (a.size() != 1) {
+            throw new BuildException("Only single argument resource 
collections are supported");
+        }
+        src = a.iterator().next();
+    }
+
+    /**
      * Read in lines and execute them.
      *
      * @param reader the reader from which to get the groovy source to exec
@@ -630,8 +713,9 @@ public class Groovy extends Java {
      * @return the name to use when compiling the script
      */
     private String computeScriptName() {
-        if (srcFile != null) {
-            return srcFile.getAbsolutePath();
+        if (src instanceof FileResource) {
+            FileResource fr = (FileResource) src;
+            return fr.getFile().getAbsolutePath();
         } else {
             String name = PREFIX;
             if (getLocation().getFileName().length() > 0)
@@ -663,8 +747,8 @@ public class Groovy extends Java {
      */
     protected void printResults(PrintStream out) {
         log.debug("printResults()");
-        StringBuilder line = new StringBuilder();
-        out.println(line);
+//        StringBuilder line = new StringBuilder();
+//        out.println(line);
         out.println();
     }
 
diff --git a/subprojects/groovy-ant/src/spec/doc/groovy-ant-task.adoc 
b/subprojects/groovy-ant/src/spec/doc/groovy-ant-task.adoc
index 9dfcd16..0867dfb 100644
--- a/subprojects/groovy-ant/src/spec/doc/groovy-ant-task.adoc
+++ b/subprojects/groovy-ant/src/spec/doc/groovy-ant-task.adoc
@@ -22,7 +22,7 @@
 = The <groovy> Ant Task
 
 Executes a series of Groovy statements from http://ant.apache.org/[Apache Ant].
-Statements can either be read in from a text file using the `src` attribute or 
from between the enclosing Groovy tags.
+Statements can either be read in from a resource or as direct text between the 
enclosing Groovy tags.
 
 == Required taskdef
 
@@ -38,22 +38,62 @@ the `groovy` task being invoked.
          classpathref="my.classpath"/>
 ----
 
-Now use the task like this:
+You can simply place statements between the `groovy` tags like this:
 
 [source,xml]
 ----
-<groovy src="..." otherAttributes="...">
+<groovy>
+...
+</groovy>
+----
+
+Or you can supply the Groovy source script as a resource. You can specify the 
pathname using the `src` attribute like this:
+
+[source,xml]
+----
+<groovy src="/some/path/MyGroovyScript.groovy" otherAttributes="...">
 ----
 
-Or this:
+Or as a nested `fileset` like this (though the fileset definition is expected 
to select just one file):
 
 [source,xml]
 ----
 <groovy>
-...
+    <fileset file="MyGroovyScript.groovy"/>
+</groovy>
+----
+
+Or as a nested single element 
https://ant.apache.org/manual/Types/resources.html#collection[resource 
collection] which could look like any of these:
+
+[source,xml]
+----
+<groovy>
+    <file file="MyGroovyScript.groovy"/>
+</groovy>
+
+<groovy>
+    <url url="https://some.domain/some/path/to/MyGroovyScript.groovy"/>
+</groovy>
+
+<groovy>
+    <javaconstant name="some.packagename.SomeClass.MY_CODE_FRAGMENT"/>
 </groovy>
 ----
 
+You may also supply a 
https://ant.apache.org/manual/Types/filterchain.html[filter chain] like this:
+
+[source,xml]
+----
+<groovy>
+    <fileset file="MyGroovyScript.groovy"/>
+    <!-- take 5 lines after skipping 18 lines, just as an example -->
+    <filterchain>
+        <headfilter lines="5" skip="18"/>
+    </filterchain>
+</groovy>
+----
+
+
 You might need to use the _contextClassLoader_ attribute (see below) if any of 
your modules load services via the classpath, e.g. `groovy-json`.
 
 == <groovy> attributes
diff --git 
a/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovyTest.xml
 
b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovyTest.xml
index 1efbb19..75b098d 100644
--- 
a/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovyTest.xml
+++ 
b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovyTest.xml
@@ -39,6 +39,34 @@
         <groovy src="GroovyTest1.groovy"/>
     </target>
 
+    <target name="groovyCodeInExternalFileset" depends="defineTask">
+        <groovy>
+            <fileset file="GroovyTest1.groovy"/>
+            <!-- skip over copyright header, just as an example -->
+            <filterchain>
+                <headfilter skip="18"/>
+            </filterchain>
+        </groovy>
+    </target>
+
+    <target name="groovyCodeInExternalFileResource" depends="defineTask">
+        <groovy>
+            <file file="GroovyTest1.groovy"/>
+        </groovy>
+    </target>
+
+    <target name="groovyCodeInExternalJavaConstantResource" 
depends="defineTask">
+        <groovy>
+            <javaconstant 
name="org.codehaus.groovy.ant.GroovyTest.CODE_FRAGMENT"/>
+        </groovy>
+    </target>
+
+    <target name="groovyCodeInExternalURLResource" depends="defineTask">
+        <groovy>
+            <url 
url="https://raw.githubusercontent.com/apache/groovy/master/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovyTest1.groovy"/>
+        </groovy>
+    </target>
+
     <target name="groovyCodeInExternalFileWithOtherClass" depends="defineTask">
         <groovy src="GroovyTest2.groovy"/>
     </target>
@@ -100,6 +128,10 @@
         <groovy src="GroovyTest_errorMessage.groovy"/>
     </target>
 
+    <target name="groovyErrorMsg_NonExistingFile" depends="defineTask">
+        <groovy src="Does_not_exist.groovy"/>
+    </target>
+
     <target name="defineTask">
         <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
     </target>
diff --git 
a/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovyTest.java
 
b/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovyTest.java
index 8f9811f..e232373 100644
--- 
a/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovyTest.java
+++ 
b/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovyTest.java
@@ -40,6 +40,7 @@ import java.util.regex.Pattern;
  */
 public class GroovyTest extends GroovyTestCase {
     public static String FLAG = null;
+    public static final String CODE_FRAGMENT = 
"org.codehaus.groovy.ant.GroovyTest.FLAG = \"from Java constant resource\"";
     private final File antFile = new 
File("src/test-resources/org/codehaus/groovy/ant/GroovyTest.xml");
     private Project project;
 
@@ -51,6 +52,10 @@ public class GroovyTest extends GroovyTestCase {
         TestSuite suite = new TestSuite();
         suite.addTest(new GroovyTest("testGroovyCodeWithinTag"));
         suite.addTest(new GroovyTest("testGroovyCodeExternalFile"));
+        suite.addTest(new GroovyTest("testGroovyCodeExternalFileset"));
+        suite.addTest(new GroovyTest("testGroovyCodeExternalFileResource"));
+        suite.addTest(new GroovyTest("testGroovyCodeExternalURLResource"));
+        suite.addTest(new 
GroovyTest("testGroovyCodeExternalJavaConstantResource"));
         suite.addTest(new 
GroovyTest("testGroovyCodeInExternalFileWithOtherClass"));
         suite.addTest(new GroovyTest("testPropertiesWithoutFork"));
         suite.addTest(new GroovyTest("testClasspath_missing"));
@@ -59,6 +64,7 @@ public class GroovyTest extends GroovyTestCase {
         suite.addTest(new GroovyTest("testClasspath_nestedclasspath"));
         suite.addTest(new GroovyTest("testGroovyArgUsage"));
         suite.addTest(new GroovyTest("testFileNameInStackTrace"));
+        suite.addTest(new GroovyTest("testNonExistingFile"));
         return suite;
     }
 
@@ -86,6 +92,30 @@ public class GroovyTest extends GroovyTestCase {
         assertEquals("from groovy file called from ant", FLAG);
     }
 
+    public void testGroovyCodeExternalFileset() {
+        assertNull(FLAG);
+        project.executeTarget("groovyCodeInExternalFileset");
+        assertEquals("from groovy file called from ant", FLAG);
+    }
+
+    public void testGroovyCodeExternalFileResource() {
+        assertNull(FLAG);
+        project.executeTarget("groovyCodeInExternalFileResource");
+        assertEquals("from groovy file called from ant", FLAG);
+    }
+
+    public void testGroovyCodeExternalURLResource() {
+        assertNull(FLAG);
+        project.executeTarget("groovyCodeInExternalURLResource");
+        assertEquals("from groovy file called from ant", FLAG);
+    }
+
+    public void testGroovyCodeExternalJavaConstantResource() {
+        assertNull(FLAG);
+        project.executeTarget("groovyCodeInExternalJavaConstantResource");
+        assertEquals("from Java constant resource", FLAG);
+    }
+
     public void testPropertiesWithoutFork() {
         assertNull(FLAG);
         project.executeTarget("groovyAntPropertyWithoutFork");
@@ -131,6 +161,16 @@ public class GroovyTest extends GroovyTestCase {
         assertEquals("from groovytest3.GroovyTest3Class.doSomethingWithArgs() 
1 2 3", FLAG);
     }
 
+    public void testNonExistingFile() {
+        try {
+            project.executeTarget("groovyErrorMsg_NonExistingFile");
+            fail();
+        }
+        catch (final BuildException e) {
+            assertTrue(e.getMessage().contains("Source resource does not 
exist!"));
+        }
+    }
+
     /**
      * Test that helpful "file name" appears in the stack trace and not just 
"Script1" 
      */

Reply via email to