<?xml version="1.0"?>
<project name="listutils">

  <dirname property="listutils.dir" file="${ant.file.listutils}"/>
  <import file="${listutils.dir}/editstring.xml"/>
  
  <macrodef name="string-list"
            description="List is a string. Default separator is path.separator.">
    <attribute name="properties" description="Location of the properties file."/>
    <attribute name="string" description="The string to edit."/>
    <attribute name="list.target"
               description="The target to run against each element of the list."/>
    <attribute name="keep-properties" default="false"
               description="Flag to keep the temporary properties file."/>
    <attribute name="separator" default="${path.separator}"
               description="Separator for elements of list."/>
    <element name="string-list-args"/>
    <sequential>
      <property name="list.keep.properties.file.." value="@{keep-properties}"/>
      <propertyfile file="@{properties}" comment="Automatically generated by string-list.">
        <entry key="list.counter.." type="int" value="0"/>
        <entry key="list" value="@{string}"/>
      </propertyfile>
      <string-list-args/>
      <antcall inheritall="false" target="list-initial__">
        <param name="properties" location="@{properties}"/>
        <param name="list.target" value="@{list.target}"/>
        <param name="separator" value="@{separator}"/>
      </antcall>
      <antcall inheritall="true" target="maybe-del-properties-file__"/>
    </sequential>
  </macrodef>
  
  <macrodef name="union-list"
            description="List is result of ${toString:[union-id]}. Separator is path.separator.">
    <attribute name="properties" description="Location of the properties file."/>
    <attribute name="union-id" description="Id of a union containing the list."/>
    <attribute name="list.target"
               description="The target to run against each element of the list."/>
    <attribute name="keep-properties" default="false"
               description="Flag to keep the temporary properties file."/>
    <element name="union-list-args"/>
    <sequential>
      <string-list properties="@{properties}" string="${toString:@{union-id}}"
                   list.target="@{list.target}" keep-properties="@{keep-properties}">
        <string-list-args>
          <union-list-args/>
        </string-list-args>
      </string-list>
    </sequential>
  </macrodef>
  
  <macrodef name="filelist-list"
            description="List is a filelist. Must be converted to a union.">
    <attribute name="properties" description="Location of the properties file."/>
    <attribute name="filelist-id" description="Id of a filelist containing the list."/>
    <attribute name="list.target"
               description="The target to run against each element of the list."/>
    <attribute name="keep-properties" default="false"
               description="Flag to keep the temporary properties file."/>
    <element name="filelist-list-args"/>
    <sequential>
    <!-- The list is constructed by taking a <union> of a <filelist>.
        Union is required, because the ${toString:<propertyname>} expansion does
        not give the contents when applied to a <filelist>. When used on a <union>,
        it returns a text list separated by ${path.separator}.
    -->
      <union id="flist-union">
        <filelist refid="@{filelist-id}"/>
      </union>
      <union-list properties="@{properties}" union-id="flist-union"
                  list.target="@{list.target}" keep-properties="@{keep-properties}">
        <union-list-args>
          <filelist-list-args/>
        </union-list-args>
      </union-list>
    </sequential>
  </macrodef>

  <target name="list-element-count__">
    <property file="${properties}"/>
    <echo>Number of elements processed: ${list.counter..}</echo>
  </target>

  <!-- Clean up the temporary properties file. -->
  <target name="maybe-del-properties-file__" depends="list-element-count__"
          unless="${list.keep.properties.file..}">
    <delete file="${properties}"/>
  </target>

  <target name="list-initial__" depends="list-set-properties__" if="list.target" unless="list.finished">
    <!-- Split the list property into first and rest -->
    <editstring string="${list}" toproperty="first" pattern="^(.*?)(?&lt;!.*${separator}.*)(${separator}(.+))?$" replace="\1"/>
    <editstring string="${list}" toproperty="rest" pattern="^(.*?)(?&lt;!.*${separator}.*)(${separator}(.+))?$" replace="\3"/>
    <!-- Set up the properties file for the next (including first) iteration. -->
    <propertyfile file="${properties}">
      <entry key="list.first" value="${first}"/>
      <entry key="list" value="${rest}"/>
    </propertyfile>
    <!-- Start recursive processing -->
    <antcall inheritall="false" target="list-recur__"/>
  </target>

  <!-- Recursive process. Depends on:
      list-set-properties__ loads the temporary properties file, containing the properties
                            which must vary from invocation to invocation.
      list-recur1__ checks for termination condition, setting termination flag property.
      
      If list not empty, list-recur__ runs the target against the per-invocation properties,
      then updates the element count in the ${properties} file.

      Finally it recursively calls list-initial__  -->
  <target name="list-recur__" depends="list-set-properties__, maybe-list-finished__"
          unless="list.finished">
    <antcall inheritall="true" target="${list.target}"/>
    <propertyfile file="${properties}">
      <entry key="list.counter.." type="int" operation="+" value="1"/>
    </propertyfile>
    <antcall inheritall="false" target="list-initial__"/>
  </target>

  <!-- Load the property file to obtain the properties for this invocation -->
  <target name="list-set-properties__">
    <property file="${properties}"/>
  </target>

  <!-- Set the property list.finished if the list is exhausted. -->
  <target name="maybe-list-finished__">
    <condition property="list.finished">
      <equals arg1="${list.first}" arg2="$${rest}"/>
    </condition>
  </target>

</project>
