<commit_before><project xmlns:sf="antlib:com.salesforce">

  <!-- Following typedefs and macro definitions are from https://gist.github.com/afawcett/5843110 -->
  <!--   Download from Salesforce Tools page under Setup -->
  <typedef 
      uri="antlib:com.salesforce" 
      resource="com/salesforce/antlib.xml" 
      classpath="${cumulus_ci.basedir}/lib/ant-salesforce.jar"/>
      
  <!--   Download from http://sourceforge.net/projects/ant-contrib/files/ant-contrib/1.0b3/ -->
  <taskdef 
      resource="net/sf/antcontrib/antlib.xml" 
      classpath="${cumulus_ci.basedir}/lib/ant-contrib-1.0b2.jar"
      />  

  <!-- load whitelists -->
  <loadproperties srcFile="${cumulus_ci.basedir}/whitelists/object_subtype.properties"/>
  <loadfile property="metadata_whitelist" srcfile="${cumulus_ci.basedir}/whitelists/metadata.txt" />

  <!-- Ant doesn't like < in replace tokens or values so set a property with < via cdata for reuse later -->
  <property name="lt"><![CDATA[<]]></property>
  <property name="downgradePackage" value="false" />

  <scriptdef name="downgradeCheck" language="javascript">
    <attribute name="installedversion" />
    <attribute name="buildversion" />
    <!-- Taken from here: http://stackoverflow.com/questions/6701948/efficient-way-to-compare-version-strings-in-java -->
    <![CDATA[var installedVersion;
var buildVersion;
var result;

function versionCompare(installedVersion, buildVersion) {
    var installedVersionString = String(installedVersion);
    var buildVersionString = String(buildVersion);
    var installedVals = installedVersionString.split('.');
    var buildVals = buildVersionString.split('.');
    var diff;
    // var signum;

    var i = 0;
    while (i < installedVals.length && i < buildVals.length && installedVals[i] == buildVals[i]) {
        i++;
    }

    if (i < installedVals.length && i < buildVals.length) {
        diff = installedVals[i] < buildVals[i] ? installedVals[i] - buildVals[i] : (installedVals[i] > buildVals[i] ? installedVals[i] - buildVals[i] : 0)
        // signum = diff ? (diff < 0 ? -1 : 1) : 0;
    } else {
        diff = installedVals.length - buildVals.length
        // signum = diff ? (diff < 0 ? -1 : 1) : 0;
    }
    return diff ? (diff < 0 ? -1 : 1) : 0;
}

result           = false;
installedVersion = attributes.get('installedversion')+'';
buildVersion     = attributes.get('buildversion')+'';

if (installedVersion == 'Not Installed') {
    installedVersion = '0.0';
}

if (buildVersion != 'Not Installed') {
    result = versionCompare(installedVersion, buildVersion) <= 0 ? false : true;
}
project.setProperty("downgradePackage", result);
]]>
  </scriptdef>


  <macrodef name="installPackage" description="Installs the given managed package">
      <attribute name="namespace" description="Namespace of managed package to install."/>
      <attribute name="version" description="Version of managed package to install."/>
      <attribute name="packagePassword" description="Password used to install the pacakge. Optional." default=""/>
      <attribute name="username" description="Salesforce user name."/>
      <attribute name="password" description="<PASSWORD>."/>
      <sequential>
          <!-- Generate optional <password> element? -->
          <if><equals arg1="@{packagePassword}" arg2=""/>
              <then><property name="passwordElement" value=""/></then>
              <else><property name="passwordElement" value="&lt;password&gt;@{packagePassword}&lt;/password&gt;"/></else>
          </if>
          <!-- Generate working folder and metadata files representing the package to install -->
          <delete dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>         
          <mkdir dir="${basedir}/installdeploy/installedPackages"/>
          <echo file="${basedir}/installdeploy/package.xml"><![CDATA[<Package xmlns="http://soap.sforce.com/2006/04/metadata"><types><members>@{namespace}</members><name>InstalledPackage</name></types><version>${cumulusci.package.apiVersion}</version></Package>]]></echo>
          <echo file="${basedir}/installdeploy/installedPackages/@{namespace}.installedPackage"><![CDATA[<InstalledPackage xmlns="http://soap.sforce.com/2006/04/metadata"><versionNumber>@{version}</versionNumber>${passwordElement}</InstalledPackage>]]></echo>
          <sf:deploy deployRoot="${basedir}/installdeploy" username="@{username}" password="<PASSWORD>}" maxPoll="${cumulusci.maxPoll.managed}" />
          <delete dir="${basedir}/installdeploy"/>
      </sequential>
  </macrodef>


  <macrodef name="uninstallPackage" description="Uninstalls the given managed package">
      <attribute name="namespace" description="Namespace of managed package to install."/>
      <attribute name="username" description="Salesforce user name."/>
      <attribute name="password" description="Salesforce password."/>
      <sequential>
          <!-- Generate working folder and metadata files representing the package to uninstall -->       
          <delete dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>         
          <mkdir dir="${basedir}/installdeploy/installedPackages"/>
          <echo file="${basedir}/installdeploy/package.xml"><![CDATA[<Package xmlns="http://soap.sforce.com/2006/04/metadata"><version>${cumulusci.package.apiVersion}</version></Package>]]></echo>
          <echo file="${basedir}/installdeploy/destructiveChanges.xml"><![CDATA[<Package xmlns="http://soap.sforce.com/2006/04/metadata"><types><members>@{namespace}</members><name>InstalledPackage</name></types><version>${cumulusci.package.apiVersion}</version></Package>]]></echo>
          <echo file="${basedir}/installdeploy/installedPackages/@{namespace}.installedPackage"><![CDATA[<InstalledPackage xmlns="http://soap.sforce.com/2006/04/metadata"><versionNumber>@{version}</versionNumber></InstalledPackage>]]></echo>
          <sf:deploy deployRoot="${basedir}/installdeploy" username="@{username}" password="<PASSWORD>}" maxPoll="${cumulusci.maxPoll.managed}" />
      </sequential>
  </macrodef>


  <macrodef name="getPackageVersion" description="Sets the InstalledPackage.NAMESPACE.versionNumber property to the version number of the package">
    <attribute name="namespace" description="Package's name to fetch version number for" />
    <attribute name="refresh" default="true" description="Should installedPackages be refreshed from the target org and deleted at the end of execution?" />
    <sequential>
      <!-- If we should refresh the installed packages, refresh them -->
      <if><istrue value="@{refresh}" />
        <then>
          <sf:bulkRetrieve
            username="${sf.username}"
            password="${<PASSWORD>}"
            metadataType="InstalledPackage"
            retrieveTarget="${basedir}/installedPackages"/>
        </then>
      </if>

      <if>
        <available file="${basedir}/installedPackages/installedPackages/@{namespace}.installedPackage" />
        <then>
          <!-- replace the string versionNumber with @{namespace}.versionNumber so the property is namespaced when loaded -->
          <replace file="${basedir}/installedPackages/installedPackages/@{namespace}.installedPackage" token="versionNumber" value="@{namespace}.versionNumber" />

          <!-- load the installedPackage file as properties -->
          <xmlproperty file="${basedir}/installedPackages/installedPackages/@{namespace}.installedPackage" />
        </then>
        <else>
          <!-- The package does not exist in the target org -->
          <property name="InstalledPackage.@{namespace}.versionNumber" value="Not Installed" />
        </else>
      </if>

      <!-- If refresh is true, delete the directory created to fetch the installedPackage metadata since it was created in the context of this macro execution-->
      <if>
        <istrue value="@{refresh}"/>
        <then>
          <delete dir="${basedir}/installedPackages"/>
        </then>
      </if>

    </sequential>
  </macrodef>

  <macrodef name="updatePackageVersion" description="Installs a specific version of a package.  If the package needs to be downgraded, first uninstalls the package.">
    <attribute name="namespace" description="Package's name to be installed" />
    <attribute name="version" description="The version to install (ex. 1.3).  Passing Not Installed will ensure the package is uninstalled" />
    <sequential>
      <downgradeCheck installedVersion="${InstalledPackage.@{namespace}.versionNumber}" buildVersion="@{version}" />
      <if>
        <!-- NOTE: This assumes getPackageVersions was already run -->
        <not><equals arg1="${InstalledPackage.@{namespace}.versionNumber}" arg2="@{version}" /></not>
        <then>
          <!-- decide if uninstall is needed -->
          <if>
            <and>
              <!-- package is installed in org -->
              <not><equals arg1="${InstalledPackage.@{namespace}.versionNumber}" arg2="Not Installed" /></not>
            
              <!-- package needs downgrade or should be Not Installed -->
              <or>
                <bool><equals arg1="${downgradePackage}" arg2="true" /></bool>
                <equals arg1="@{version}" arg2="Not Installed" />
              </or>
            </and>
            <then>
              <echo>Uninstalling @{namespace} ${InstalledPackage.@{namespace}.versionNumber} to downgrade to @{version}</echo>
              <uninstallPackage namespace="@{namespace}" username="${sf.username}" password="${<PASSWORD>}"/>
            </then>
            <else>
              <!-- just an upgrade, no need to uninstall first -->
              <echo>Upgrading @{namespace} from ${InstalledPackage.@{namespace}.versionNumber} to @{version}</echo>
            </else>
          </if>
          <if>
            <not><equals arg1="@{version}" arg2="Not Installed" /></not>
            <then>
              <installPackage namespace="@{namespace}" version="@{version}" username="${sf.username}" password="${<PASSWORD>}"/>
            </then>
          </if>
        </then>
      </if>
    </sequential>
  </macrodef>

  <macrodef name="retrieveUnpackaged" description="Retrieves all unpackaged metadata from the target org into the specified dir">
    <attribute name="dir" description="The local directory to store the metadata files" />
    <sequential>

      <!-- Create a clean output directory structure -->
      <delete dir="@{dir}" />
      <mkdir dir="@{dir}"/>
      <mkdir dir="@{dir}/unpackaged"/>
      <mkdir dir="@{dir}/standard"/>

      <!-- Retrieve standard objects (uses /build/standard_objects.xml as the manifest) -->
      <sf:retrieve 
        username="${sf.username}" 
        password="${sf.password}" 
        serverurl="${sf.serverurl}" 
        retrieveTarget="@{dir}/standard" 
        unpackaged="${cumulus_ci.basedir}/standard_objects.xml"/>

      <!-- Retrieve unpackaged metadata (uses /build/all_metadata_types.xml as the manifest) -->
      <sf:retrieve 
        username="${sf.username}" 
        password="${<PASSWORD>}" 
        serverurl="${sf.serverurl}" 
        retrieveTarget="@{dir}/unpackaged" 
        unpackaged="${cumulus_ci.basedir}/all_meta_types.xml"/>

    </sequential>
  </macrodef>

  <macrodef name="deleteWhitelistedMetadata" description="Deletes any metadata files from a given directory which are listed in /build/whitelists/metadata.txt">
    <attribute name="dir" description="The directory to delete metadata from" />
    <sequential>
      <!-- Delete whitelisted files which are added to the org by Salesforce.  This includes both components which are always in an org and components which are automatically created by enabling Sites -->
      <for param="filename" list="${metadata_whitelist}" delimiter="${line.separator}" >
        <sequential>
          <echo>Deleting whitelisted metadata file @{filename}</echo>
          <delete>
            <fileset dir="@{dir}" includes="**/*/@{filename}" />
          </delete>
        </sequential>
      </for>
    </sequential>
  </macrodef>

  <macrodef name="retrievePackaged" description="Retrieves all metadata from a given package into the specified dir">
    <attribute name="package" description="The package name" />
    <attribute name="dir" description="The local directory to store the metadata files" />
  	<attribute name="mkdir" description="Whether to delete and recreate the target dir" default="true" />
    <sequential>
      <if>
      	<istrue value="@{mkdir}"/>
      	<then>
      	    <delete dir="@{dir}" />
      	    <mkdir dir="@{dir}"/>
      	</then>
      </if>
      <sf:retrieve 
        username="${sf.username}" 
        password="${<PASSWORD>}" 
        serverurl="${sf.serverurl}" 
        retrieveTarget="@{dir}" 
        packageNames="@{package}"/>
    </sequential>
  </macrodef>

  <macrodef name="buildPackageXmlFromDir" description="Runs through a directory of metadata and builds a package.xml format file from the contents">
    <attribute name="dir" description="The directory containing metadata (i.e. package src)" />
    <attribute name="xmlfile" description="The output file path" />
    <attribute name="version" description="The value for the version element of the package.xml file to set the api version (i.e. ${cumulusci.package.apiVersion})" />
    <attribute name="package" description="If provided, a fullName element will be added to the file so it is deployed into a Package in the target org" default="NOTSET" />
    <attribute name="exclude" description="If provided, a list of filenames to skip." default="" />
    <attribute name="installClass" description="If provided, a installClass element will be added to the file to specify a class to run when installing the package." default="NOTSET" />
    <attribute name="uninstallClass" description="If provided, a uninstallClass element will be added to the file to specify a class to run when uninstalling the package." default="NOTSET" />
    <sequential>
      <!-- Write the header for the destructiveChanges.xml -->
      <echo file="@{xmlfile}">&lt;?xml version="1.0" encoding="UTF-8"?&gt;${line.separator}</echo>
      <echo file="@{xmlfile}" append="true">&lt;Package xmlns="http://soap.sforce.com/2006/04/metadata"&gt;${line.separator}</echo>  

      <!-- Loop through the subfolders to add lines to destructiveChanges.xml -->
      <for param="typedir">
        <path>
          <dirset dir="@{dir}" includes="**/*" />
        </path>
        <sequential>
          <basename property="metatype" file="@{typedir}" />

          <!-- Map dir name to a metadata type name.  This also serves as a whitelist for which metadata types to add.  If a mapping is not listed, the type is not added to the package.xml -->
          <!-- FIXME: Is there a cleaner way to do this so the mapping could live in a separate file? -->
          <var name="typename" unset="true" /> 
          <switch value="${metatype}">
            <case value="applications">
              <property name="typename" value="CustomApplication" />
            </case>
            <case value="classes">
              <property name="typename" value="ApexClass" />
            </case>
            <case value="components">
              <property name="typename" value="ApexComponent" />
            </case>
            <case value="connectedApps">
              <property name="typename" value="ConnectedApp" />
            </case>
            <case value="dashboards">
              <property name="typename" value="Dashboard" />
            </case>
            <case value="documents">
              <property name="typename" value="Document" />
            </case>
            <case value="homePageComponents">
              <property name="typename" value="HomePageComponent" />
            </case>
            <case value="labels">
              <property name="typename" value="CustomLabel" />
            </case>
            <case value="layouts">
              <property name="typename" value="Layout" />
            </case>
            <case value="objects">
              <property name="typename" value="CustomObject" />
            </case>
            <case value="objectTranslations">
              <property name="typename" value="CustomObjectTranslation" />
            </case>
            <case value="pages">
              <property name="typename" value="ApexPage" />
            </case>
            <case value="profiles">
              <property name="typename" value="Profile" />
            </case>
            <case value="permissionsets">
              <property name="typename" value="PermissionSet" />
            </case>
            <case value="quickActions">
              <property name="typename" value="QuickAction" />
            </case>
            <case value="remoteSiteSettings">
              <property name="typename" value="RemoteSiteSetting" />
            </case>
            <case value="reportTypes">
              <property name="typename" value="ReportType" />
            </case>
            <case value="reports">
              <property name="typename" value="Report" />
            </case>
            <case value="staticresources">
              <property name="typename" value="StaticResource" />
            </case>
            <case value="tabs">
              <property name="typename" value="CustomTab" />
            </case>
            <case value="translations">
              <property name="typename" value="Translations" />
            </case>
            <case value="triggers">
              <property name="typename" value="ApexTrigger" />
            </case>
            <case value="workflows">
              <property name="typename" value="Workflow" />
            </case>
            <case value="email">
              <property name="typename" value="EmailTemplate" />
            </case>
            <case value="letterhead">
              <property name="typename" value="Letterhead" />
            </case>
            <case value="weblinks">
              <property name="typename" value="CustomPageWebLink" />
            </case>
            <default/>
          </switch>

          <if>
            <isset property="typename" />
            <then>

              <var name="parents_included" unset="true" />

              <!-- Write a new <types/> section for the meta type -->
              <echo file="@{xmlfile}" append="true">  &lt;types&gt;${line.separator}</echo>

              <!-- Loop through files in the type's dir and add <members /> element for each file -->
              <for param="file">
                <path>
                  <!-- exclude xml files and namespaced metadata files as well as any manual excludes -->
                  <fileset dir="@{typedir}" includes="**/*.*" excludes="*.xml *-meta.xml *__*__c.object @{exclude}" />
                </path>
                <sequential>
                  <var name="component_file" unset="true" />
                  <var name="component_file_dirname" unset="true" />
                  <var name="component_file_parent" unset="true" />

                  <!-- get basename and basename without suffix for file. propertyregex is used since we don't know the file suffix from only the dir name (i.e. classes vs .class) -->
                  <basename property="component_file" file="@{file}" />
                  <propertyregex property="component" input="${component_file}" regexp="(.*)\.(.*)" select="\1" />

                  <!-- get the component file's parent directory.  if the parent is not the metatype, include it (i.e. Reports and Dashboards) -->
                  <dirname property="component_file_dirname" file="@{file}" />
                  <basename property="component_file_parent" file="${component_file_dirname}" />

                  <!-- If the file is not in the exclude list, add it to the package.xml -->
                  <if>
                    <and>
                      <not><contains string="@{exclude}" substring="${component_file}" /></not>
                      <not><contains string="${component_file}" substring="-meta.xml" /></not>
                    </and>
                    <then>
                      <!-- Write the <members/> element -->
                      <if>
                        <equals arg1="${metatype}" arg2="${component_file_parent}" />
                        <then>
                          <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component}&lt;/members&gt;${line.separator}</echo>
                        </then>
                        <else>
                          <!-- This type contains metadata grouped into subdirectories under typedir.  Include the dir once and prefix all files with it -->
                          <if>
                            <not><contains string="${parents_included}" substring=",${component_file_parent}," /></not>
                            <then>
                              <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component_file_parent}&lt;/members&gt;${line.separator}</echo>
                              <var name="parents_included" value="${parents_included},${component_file_parent}," />
                            </then>
                          </if>
                          <!-- Some metadata types, such as Document, require the suffix -->
                          <if>
                            <or>
                              <equals arg1="${typename}" arg2="Document" />
                            </or>
                            <then>
                              <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component_file_parent}/${component_file}&lt;/members&gt;${line.separator}</echo>
                            </then>
                            <else>
                              <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component_file_parent}/${component}&lt;/members&gt;${line.separator}</echo>
                            </else>
                          </if>
                        </else>
                      </if> 
                    </then>
                  </if>

                  <!-- Unset basename properties for next execution since we're using basename and propertyregex which will not overwrite a property -->
                  <var name="component" unset="true" />
                  <var name="component_file" unset="true" />
                </sequential>
              </for>

              <!-- Write the <name/> element and close the <types/> element -->
              <echo file="@{xmlfile}" append="true">    &lt;name&gt;${typename}&lt;/name&gt;${line.separator}</echo>
              <echo file="@{xmlfile}" append="true">  &lt;/types&gt;${line.separator}</echo>

            </then>
            <else>
              <echo>Skipping unmapped metadata type ${metatype}</echo>
            </else>
          </if>

          <!-- Unset the metatype so basename can set it on next iteration through loop -->
          <var name="metatype" unset="true" />
        </sequential>
      </for>

      <!-- Write the install and uninstall scripts -->
      <if>
        <not><equals arg1="@{installClass}" arg2="NOTSET" /></not>
        <then>
            <echo file="@{xmlfile}" append="true">  &lt;postInstallClass&gt;@{installClass}&lt;/postInstallClass&gt;${line.separator}</echo>
        </then>
      </if>
        
      <if>
        <not><equals arg1="@{uninstallClass}" arg2="NOTSET" /></not>
        <then>
            <echo file="@{xmlfile}" append="true">  &lt;uninstallClass&gt;@{uninstallClass}&lt;/uninstallClass&gt;${line.separator}</echo>
        </then>
      </if>
      <!-- If package is provided, add the fullName element -->
      <if>
        <not><equals arg1="@{package}" arg2="NOTSET" /></not>
        <then>
          <echo file="@{xmlfile}" append="true">  &lt;fullName&gt;@{package}&lt;/fullName&gt;${line.separator}</echo>
        </then>
      </if>

      <!-- Write the <version/> tag and close <Package/> -->
      <echo file="@{xmlfile}" append="true">  &lt;version&gt;@{version}&lt;/version&gt;${line.separator}</echo>
      <echo file="@{xmlfile}" append="true">&lt;/Package&gt;</echo>
    </sequential>
  </macrodef>

  <macrodef name="getWhitelistForObjectAndType" description="Given an object and a type, returns the whitelist for the type if set">
    <attribute name="object" description="The Salesforce api name of the object" />
    <attribute name="type" description="The metadata type name (i.e. CustomField)" />
    <attribute name="property" description="The name of the property to populate with the whitelist" />
    <sequential>
      <!-- this is split out into a separate macro so we can use a dynamic property name to lookup the whitelist -->
      <if>
        <isset property="@{property}" />
        <then>
          <var name="@{property}" unset="true" />
        </then>
      </if>
      <property name="@{property}" value="${whitelist.@{object}.@{type}}" />
    </sequential>
  </macrodef>

  <macrodef name="addMetadataSubType" description="Uses an xsl template to parse out metadata subtypes from a parent file and adds to a package.xml file">
    <attribute name="dir" description="The package directory containing the files to search in." />
    <attribute name="suffix" description="The suffix of the metadata file to search in.  For example, object for object files" default="object" />
    <attribute name="xmlfile" description="The path of the target package.xml or destructiveChanges.xml file" />
    <attribute name="typename" description="The type name as would appear in the manifest type's name element" />
    <attribute name="xslt" description="The path of the xslt transform to extract type components from the standard object file" default="${cumulus_ci.basedir}/xsl/extract_subtype.xsl" />
    <attribute name="element" description="The xml element to look for.  This is passed to the xslt to allow easier template reuse" />
    <attribute name="nameelement" description="The xml element whose value should provide the name (used in the package.xml members tag)." default="fullName" />
    <attribute name="parentelement" description="The containing element name in target files.  For example, CustomObject if looking in object files" default="CustomObject" />
    <sequential>
      <!-- Make sure the directory actually exist, otherwise do nothing -->
      <if>
        <available file="@{dir}" type="dir" />
        <then>
          <!-- First, build a FILE.SUFFIX.typename file containing the <members/> elements for all components of the type -->
          <for param="file">
            <path>
              <fileset dir="@{dir}" includes="*.@{suffix}" />
            </path>
            <sequential>
              <!-- Parse the file basename and parent name from the parent's filename -->
              <basename property="parent_file" file="@{file}" />
              <basename property="parent" file="@{file}" suffix=".@{suffix}" />

              <!-- Construct an xslt file from the template by replacing ___element with @{element}.  
                   This is to get around xslt's limitations in selecting an element whose name is passed as a parameter.
                   This approach avoids the need to create an xsl file for each metadata subtype
                -->
              <copy file="@{xslt}" tofile="@{xslt}.rendered" />
              <replaceregexp file="@{xslt}.rendered" match="___parentelement___" replace="@{parentelement}" flags="g" />
              <replaceregexp file="@{xslt}.rendered" match="___element___" replace="@{element}" flags="g" />
              <replaceregexp file="@{xslt}.rendered" match="___nameelement___" replace="@{nameelement}" flags="g" />

              <!-- Run xslt to build a file for each parent containing <members/> elements for components -->
              <getWhitelistForObjectAndType object="${parent}" type="@{typename}" property="obj_type_whitelist" />
              <xslt in="@{file}" out="@{dir}/${parent_file}.@{typename}" style="@{xslt}.rendered">
                <param name="whitelist" expression="${obj_type_whitelist}" />
              </xslt>
              <delete file="@{xslt}.rendered" />

              <!-- Prepend member names with object. -->
              <if>
                <equals arg1="@{suffix}" arg2="object" />
                <then>
                  <replaceregexp file="@{dir}/${parent_file}.@{typename}" match="members>(.*)./members>" replace="members>${parent}.\1&lt;/members>${line.separator}" flags="g" />
                </then>
              </if>

              <!-- Prepend member names with workflow. -->
              <if>
                <equals arg1="@{suffix}" arg2="workflow" />
                <then>
                  <replaceregexp file="@{dir}/${parent_file}.@{typename}" match="members>(.*)./members>" replace="members>${parent}.\1&lt;/members>${line.separator}" flags="g" />
                </then>
              </if>

              <!-- Unset properites for basename call in next iteration of loop -->
              <var name="parent" unset="true" />
              <var name="parent_file" unset="true" />
            </sequential>
          </for>

          <!-- Concatenate all <members/> from earlier extraction output files -->
          <concat destfile="@{dir}/@{typename}.extract">
            <fileset dir="@{dir}" includes="**/*.@{suffix}.@{typename}" />
            <filterchain>
              <prefixlines prefix="    " />
              <linecontains>
                <contains value="members>" />
              </linecontains>
            </filterchain>
          </concat>

          <!-- If there are any members to extract, add the <types/> section to package.xml -->
          <if>
            <available file="@{dir}/@{typename}.extract" />
            <then>
              <loadfile property="type.extract" srcfile="@{dir}/@{typename}.extract" />
              <if>
                <isset property="type.extract" /> 
                <then>
                  <var name="type.extract" value="${line.separator}  ${lt}types>${line.separator}${type.extract}    ${lt}name>@{typename}${lt}/name>${line.separator}  ${lt}/types>" />
                  <replaceregexp file="@{xmlfile}" match="Package(.*)>" replace="Package\1>${type.extract}" />
                  <xslt in="@{xmlfile}" out="@{xmlfile}.tidy" style="${cumulus_ci.basedir}/xsl/tidy_package_xml.xsl" />
                  <move file="@{xmlfile}.tidy" tofile="@{xmlfile}" />
                </then>
              </if>
              <delete>
                  <fileset dir="@{dir}" includes="**/*.@{typename}" />
              </delete>
              <delete file="@{dir}/@{typename}.extract" />
        
              <var name="type.extract" unset="true" />
            </then>  
          </if>
        </then>
      </if>
    </sequential>  
  </macrodef>

  <macrodef name="buildPackageXml" description="Construct a complete package.xml file based on the metadata in the src directory">
    <attribute name="version" description="The value for the version element in the package.xml" />
    <attribute name="package" description="The value for the fullName element in the package.xml" default="NOTSET" />
    <attribute name="installClass" description="If provided, a installClass element will be added to the file to specify a class to run when installing the package." default="NOTSET" />
    <attribute name="uninstallClass" description="If provided, a uninstallClass element will be added to the file to specify a class to run when uninstalling the package." default="NOTSET" />
    <attribute name="srcDir" description="If provided, the relative path to the source directory from the project root.  Defaults to src" default="src" />
    
  
    <sequential>
      <!-- Exclude CustomLabels.labels so we can use addMetadataSubType on it instead -->
      <buildPackageXmlFromDir dir="${basedir}/@{srcDir}" xmlfile="${basedir}/@{srcDir}/package.xml.new" package="@{package}" version="@{version}" exclude="CustomLabels.labels" installClass="@{installClass}" uninstallClass="@{uninstallClass}" />

      <!-- Add metadata sub-types of object which need to be explicitly listed in package.xml -->
      <!--<addMetadataSubType dir="${basedir}/@{srcDir}" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="ActionOverride" element="actionOverrides" nameelement="actionName" />-->
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="BusinessProcess" element="businessProcesses" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="CompactLayout" element="compactLayouts" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="CustomField" element="fields" xslt="${cumulus_ci.basedir}/xsl/extract_obj_fields.xsl" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/labels" suffix="labels" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="CustomLabel" element="labels" parentelement="CustomLabels" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="FieldSet" element="fieldSets" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="ListView" element="listViews" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="NamedFilter" element="namedFilters" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="RecordType" element="recordTypes" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="ValidationRule" element="validationRules" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="WebLink" element="webLinks" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/workflows" suffix="workflow" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="WorkflowFieldUpdate" element="fieldUpdates" parentelement="Workflow" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/workflows" suffix="workflow" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="WorkflowRule" element="rules" parentelement="Workflow" />

      <move file="${basedir}/@{srcDir}/package.xml.new" tofile="${basedir}/@{srcDir}/package.xml" />
    </sequential>
  </macrodef>

  <macrodef name="buildDestroyStaleMetadata" description="Constructs a destructiveChanges.xml which deletes any metadata from one directory which does not exist in another">
    <attribute name="srcdir" description="Source directory, usually containing metadata from target org fetched via retrievePackaged" />
    <attribute name="commdir" description="The directory whose metadata should be compared to srcdir.  Any metadata not in this directory's package.xml but in srcdir's package.xml will be added to destructiveChanges.xml" />
    <attribute name="dir" description="Target directory to create the destructiveChanges.xml package" />
    <sequential>
      <!-- Run xslt against package.xml files to generate a text file containing TYPE::MEMBER lines for each member for easier comparison later -->
      <xslt in="@{srcdir}/package.xml" out="@{srcdir}/package.xml.members" style="${cumulus_ci.basedir}/xsl/diff_package_xml.xsl" />
      <xslt in="@{commdir}/package.xml" out="@{commdir}/package.xml.members" style="${cumulus_ci.basedir}/xsl/diff_package_xml.xsl" />
      <loadfile property="src.package.xml.members" srcfile="@{srcdir}/package.xml.members" /><commit_msg>Update buildPackageXml to support workflow alerts<commit_after><project xmlns:sf="antlib:com.salesforce">

  <!-- Following typedefs and macro definitions are from https://gist.github.com/afawcett/5843110 -->
  <!--   Download from Salesforce Tools page under Setup -->
  <typedef 
      uri="antlib:com.salesforce" 
      resource="com/salesforce/antlib.xml" 
      classpath="${cumulus_ci.basedir}/lib/ant-salesforce.jar"/>
      
  <!--   Download from http://sourceforge.net/projects/ant-contrib/files/ant-contrib/1.0b3/ -->
  <taskdef 
      resource="net/sf/antcontrib/antlib.xml" 
      classpath="${cumulus_ci.basedir}/lib/ant-contrib-1.0b2.jar"
      />  

  <!-- load whitelists -->
  <loadproperties srcFile="${cumulus_ci.basedir}/whitelists/object_subtype.properties"/>
  <loadfile property="metadata_whitelist" srcfile="${cumulus_ci.basedir}/whitelists/metadata.txt" />

  <!-- Ant doesn't like < in replace tokens or values so set a property with < via cdata for reuse later -->
  <property name="lt"><![CDATA[<]]></property>
  <property name="downgradePackage" value="false" />

  <scriptdef name="downgradeCheck" language="javascript">
    <attribute name="installedversion" />
    <attribute name="buildversion" />
    <!-- Taken from here: http://stackoverflow.com/questions/6701948/efficient-way-to-compare-version-strings-in-java -->
    <![CDATA[var installedVersion;
var buildVersion;
var result;

function versionCompare(installedVersion, buildVersion) {
    var installedVersionString = String(installedVersion);
    var buildVersionString = String(buildVersion);
    var installedVals = installedVersionString.split('.');
    var buildVals = buildVersionString.split('.');
    var diff;
    // var signum;

    var i = 0;
    while (i < installedVals.length && i < buildVals.length && installedVals[i] == buildVals[i]) {
        i++;
    }

    if (i < installedVals.length && i < buildVals.length) {
        diff = installedVals[i] < buildVals[i] ? installedVals[i] - buildVals[i] : (installedVals[i] > buildVals[i] ? installedVals[i] - buildVals[i] : 0)
        // signum = diff ? (diff < 0 ? -1 : 1) : 0;
    } else {
        diff = installedVals.length - buildVals.length
        // signum = diff ? (diff < 0 ? -1 : 1) : 0;
    }
    return diff ? (diff < 0 ? -1 : 1) : 0;
}

result           = false;
installedVersion = attributes.get('installedversion')+'';
buildVersion     = attributes.get('buildversion')+'';

if (installedVersion == 'Not Installed') {
    installedVersion = '0.0';
}

if (buildVersion != 'Not Installed') {
    result = versionCompare(installedVersion, buildVersion) <= 0 ? false : true;
}
project.setProperty("downgradePackage", result);
]]>
  </scriptdef>


  <macrodef name="installPackage" description="Installs the given managed package">
      <attribute name="namespace" description="Namespace of managed package to install."/>
      <attribute name="version" description="Version of managed package to install."/>
      <attribute name="packagePassword" description="Password used to install the pacakge. Optional." default=""/>
      <attribute name="username" description="Salesforce user name."/>
      <attribute name="password" description="<PASSWORD>."/>
      <sequential>
          <!-- Generate optional <password> element? -->
          <if><equals arg1="@{packagePassword}" arg2=""/>
              <then><property name="passwordElement" value=""/></then>
              <else><property name="passwordElement" value="&lt;password&gt;@{packagePassword}&lt;/password&gt;"/></else>
          </if>
          <!-- Generate working folder and metadata files representing the package to install -->
          <delete dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>         
          <mkdir dir="${basedir}/installdeploy/installedPackages"/>
          <echo file="${basedir}/installdeploy/package.xml"><![CDATA[<Package xmlns="http://soap.sforce.com/2006/04/metadata"><types><members>@{namespace}</members><name>InstalledPackage</name></types><version>${cumulusci.package.apiVersion}</version></Package>]]></echo>
          <echo file="${basedir}/installdeploy/installedPackages/@{namespace}.installedPackage"><![CDATA[<InstalledPackage xmlns="http://soap.sforce.com/2006/04/metadata"><versionNumber>@{version}</versionNumber>${passwordElement}</InstalledPackage>]]></echo>
          <sf:deploy deployRoot="${basedir}/installdeploy" username="@{username}" password="<PASSWORD>}" maxPoll="${cumulusci.maxPoll.managed}" />
          <delete dir="${basedir}/installdeploy"/>
      </sequential>
  </macrodef>


  <macrodef name="uninstallPackage" description="Uninstalls the given managed package">
      <attribute name="namespace" description="Namespace of managed package to install."/>
      <attribute name="username" description="Salesforce user name."/>
      <attribute name="password" description="<PASSWORD>."/>
      <sequential>
          <!-- Generate working folder and metadata files representing the package to uninstall -->       
          <delete dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>
          <mkdir dir="${basedir}/installdeploy"/>         
          <mkdir dir="${basedir}/installdeploy/installedPackages"/>
          <echo file="${basedir}/installdeploy/package.xml"><![CDATA[<Package xmlns="http://soap.sforce.com/2006/04/metadata"><version>${cumulusci.package.apiVersion}</version></Package>]]></echo>
          <echo file="${basedir}/installdeploy/destructiveChanges.xml"><![CDATA[<Package xmlns="http://soap.sforce.com/2006/04/metadata"><types><members>@{namespace}</members><name>InstalledPackage</name></types><version>${cumulusci.package.apiVersion}</version></Package>]]></echo>
          <echo file="${basedir}/installdeploy/installedPackages/@{namespace}.installedPackage"><![CDATA[<InstalledPackage xmlns="http://soap.sforce.com/2006/04/metadata"><versionNumber>@{version}</versionNumber></InstalledPackage>]]></echo>
          <sf:deploy deployRoot="${basedir}/installdeploy" username="@{username}" password="<PASSWORD>}" maxPoll="${cumulusci.maxPoll.managed}" />
      </sequential>
  </macrodef>


  <macrodef name="getPackageVersion" description="Sets the InstalledPackage.NAMESPACE.versionNumber property to the version number of the package">
    <attribute name="namespace" description="Package's name to fetch version number for" />
    <attribute name="refresh" default="true" description="Should installedPackages be refreshed from the target org and deleted at the end of execution?" />
    <sequential>
      <!-- If we should refresh the installed packages, refresh them -->
      <if><istrue value="@{refresh}" />
        <then>
          <sf:bulkRetrieve
            username="${sf.username}"
            password="${<PASSWORD>}"
            metadataType="InstalledPackage"
            retrieveTarget="${basedir}/installedPackages"/>
        </then>
      </if>

      <if>
        <available file="${basedir}/installedPackages/installedPackages/@{namespace}.installedPackage" />
        <then>
          <!-- replace the string versionNumber with @{namespace}.versionNumber so the property is namespaced when loaded -->
          <replace file="${basedir}/installedPackages/installedPackages/@{namespace}.installedPackage" token="versionNumber" value="@{namespace}.versionNumber" />

          <!-- load the installedPackage file as properties -->
          <xmlproperty file="${basedir}/installedPackages/installedPackages/@{namespace}.installedPackage" />
        </then>
        <else>
          <!-- The package does not exist in the target org -->
          <property name="InstalledPackage.@{namespace}.versionNumber" value="Not Installed" />
        </else>
      </if>

      <!-- If refresh is true, delete the directory created to fetch the installedPackage metadata since it was created in the context of this macro execution-->
      <if>
        <istrue value="@{refresh}"/>
        <then>
          <delete dir="${basedir}/installedPackages"/>
        </then>
      </if>

    </sequential>
  </macrodef>

  <macrodef name="updatePackageVersion" description="Installs a specific version of a package.  If the package needs to be downgraded, first uninstalls the package.">
    <attribute name="namespace" description="Package's name to be installed" />
    <attribute name="version" description="The version to install (ex. 1.3).  Passing Not Installed will ensure the package is uninstalled" />
    <sequential>
      <downgradeCheck installedVersion="${InstalledPackage.@{namespace}.versionNumber}" buildVersion="@{version}" />
      <if>
        <!-- NOTE: This assumes getPackageVersions was already run -->
        <not><equals arg1="${InstalledPackage.@{namespace}.versionNumber}" arg2="@{version}" /></not>
        <then>
          <!-- decide if uninstall is needed -->
          <if>
            <and>
              <!-- package is installed in org -->
              <not><equals arg1="${InstalledPackage.@{namespace}.versionNumber}" arg2="Not Installed" /></not>
            
              <!-- package needs downgrade or should be Not Installed -->
              <or>
                <bool><equals arg1="${downgradePackage}" arg2="true" /></bool>
                <equals arg1="@{version}" arg2="Not Installed" />
              </or>
            </and>
            <then>
              <echo>Uninstalling @{namespace} ${InstalledPackage.@{namespace}.versionNumber} to downgrade to @{version}</echo>
              <uninstallPackage namespace="@{namespace}" username="${sf.username}" password="${<PASSWORD>}"/>
            </then>
            <else>
              <!-- just an upgrade, no need to uninstall first -->
              <echo>Upgrading @{namespace} from ${InstalledPackage.@{namespace}.versionNumber} to @{version}</echo>
            </else>
          </if>
          <if>
            <not><equals arg1="@{version}" arg2="Not Installed" /></not>
            <then>
              <installPackage namespace="@{namespace}" version="@{version}" username="${sf.username}" password="${<PASSWORD>}"/>
            </then>
          </if>
        </then>
      </if>
    </sequential>
  </macrodef>

  <macrodef name="retrieveUnpackaged" description="Retrieves all unpackaged metadata from the target org into the specified dir">
    <attribute name="dir" description="The local directory to store the metadata files" />
    <sequential>

      <!-- Create a clean output directory structure -->
      <delete dir="@{dir}" />
      <mkdir dir="@{dir}"/>
      <mkdir dir="@{dir}/unpackaged"/>
      <mkdir dir="@{dir}/standard"/>

      <!-- Retrieve standard objects (uses /build/standard_objects.xml as the manifest) -->
      <sf:retrieve 
        username="${sf.username}" 
        password="${<PASSWORD>}" 
        serverurl="${sf.serverurl}" 
        retrieveTarget="@{dir}/standard" 
        unpackaged="${cumulus_ci.basedir}/standard_objects.xml"/>

      <!-- Retrieve unpackaged metadata (uses /build/all_metadata_types.xml as the manifest) -->
      <sf:retrieve 
        username="${sf.username}" 
        password="${<PASSWORD>}" 
        serverurl="${sf.serverurl}" 
        retrieveTarget="@{dir}/unpackaged" 
        unpackaged="${cumulus_ci.basedir}/all_meta_types.xml"/>

    </sequential>
  </macrodef>

  <macrodef name="deleteWhitelistedMetadata" description="Deletes any metadata files from a given directory which are listed in /build/whitelists/metadata.txt">
    <attribute name="dir" description="The directory to delete metadata from" />
    <sequential>
      <!-- Delete whitelisted files which are added to the org by Salesforce.  This includes both components which are always in an org and components which are automatically created by enabling Sites -->
      <for param="filename" list="${metadata_whitelist}" delimiter="${line.separator}" >
        <sequential>
          <echo>Deleting whitelisted metadata file @{filename}</echo>
          <delete>
            <fileset dir="@{dir}" includes="**/*/@{filename}" />
          </delete>
        </sequential>
      </for>
    </sequential>
  </macrodef>

  <macrodef name="retrievePackaged" description="Retrieves all metadata from a given package into the specified dir">
    <attribute name="package" description="The package name" />
    <attribute name="dir" description="The local directory to store the metadata files" />
  	<attribute name="mkdir" description="Whether to delete and recreate the target dir" default="true" />
    <sequential>
      <if>
      	<istrue value="@{mkdir}"/>
      	<then>
      	    <delete dir="@{dir}" />
      	    <mkdir dir="@{dir}"/>
      	</then>
      </if>
      <sf:retrieve 
        username="${sf.username}" 
        password="${<PASSWORD>}" 
        serverurl="${sf.serverurl}" 
        retrieveTarget="@{dir}" 
        packageNames="@{package}"/>
    </sequential>
  </macrodef>

  <macrodef name="buildPackageXmlFromDir" description="Runs through a directory of metadata and builds a package.xml format file from the contents">
    <attribute name="dir" description="The directory containing metadata (i.e. package src)" />
    <attribute name="xmlfile" description="The output file path" />
    <attribute name="version" description="The value for the version element of the package.xml file to set the api version (i.e. ${cumulusci.package.apiVersion})" />
    <attribute name="package" description="If provided, a fullName element will be added to the file so it is deployed into a Package in the target org" default="NOTSET" />
    <attribute name="exclude" description="If provided, a list of filenames to skip." default="" />
    <attribute name="installClass" description="If provided, a installClass element will be added to the file to specify a class to run when installing the package." default="NOTSET" />
    <attribute name="uninstallClass" description="If provided, a uninstallClass element will be added to the file to specify a class to run when uninstalling the package." default="NOTSET" />
    <sequential>
      <!-- Write the header for the destructiveChanges.xml -->
      <echo file="@{xmlfile}">&lt;?xml version="1.0" encoding="UTF-8"?&gt;${line.separator}</echo>
      <echo file="@{xmlfile}" append="true">&lt;Package xmlns="http://soap.sforce.com/2006/04/metadata"&gt;${line.separator}</echo>  

      <!-- Loop through the subfolders to add lines to destructiveChanges.xml -->
      <for param="typedir">
        <path>
          <dirset dir="@{dir}" includes="**/*" />
        </path>
        <sequential>
          <basename property="metatype" file="@{typedir}" />

          <!-- Map dir name to a metadata type name.  This also serves as a whitelist for which metadata types to add.  If a mapping is not listed, the type is not added to the package.xml -->
          <!-- FIXME: Is there a cleaner way to do this so the mapping could live in a separate file? -->
          <var name="typename" unset="true" /> 
          <switch value="${metatype}">
            <case value="applications">
              <property name="typename" value="CustomApplication" />
            </case>
            <case value="classes">
              <property name="typename" value="ApexClass" />
            </case>
            <case value="components">
              <property name="typename" value="ApexComponent" />
            </case>
            <case value="connectedApps">
              <property name="typename" value="ConnectedApp" />
            </case>
            <case value="dashboards">
              <property name="typename" value="Dashboard" />
            </case>
            <case value="documents">
              <property name="typename" value="Document" />
            </case>
            <case value="homePageComponents">
              <property name="typename" value="HomePageComponent" />
            </case>
            <case value="labels">
              <property name="typename" value="CustomLabel" />
            </case>
            <case value="layouts">
              <property name="typename" value="Layout" />
            </case>
            <case value="objects">
              <property name="typename" value="CustomObject" />
            </case>
            <case value="objectTranslations">
              <property name="typename" value="CustomObjectTranslation" />
            </case>
            <case value="pages">
              <property name="typename" value="ApexPage" />
            </case>
            <case value="profiles">
              <property name="typename" value="Profile" />
            </case>
            <case value="permissionsets">
              <property name="typename" value="PermissionSet" />
            </case>
            <case value="quickActions">
              <property name="typename" value="QuickAction" />
            </case>
            <case value="remoteSiteSettings">
              <property name="typename" value="RemoteSiteSetting" />
            </case>
            <case value="reportTypes">
              <property name="typename" value="ReportType" />
            </case>
            <case value="reports">
              <property name="typename" value="Report" />
            </case>
            <case value="staticresources">
              <property name="typename" value="StaticResource" />
            </case>
            <case value="tabs">
              <property name="typename" value="CustomTab" />
            </case>
            <case value="translations">
              <property name="typename" value="Translations" />
            </case>
            <case value="triggers">
              <property name="typename" value="ApexTrigger" />
            </case>
            <case value="workflows">
              <property name="typename" value="Workflow" />
            </case>
            <case value="email">
              <property name="typename" value="EmailTemplate" />
            </case>
            <case value="letterhead">
              <property name="typename" value="Letterhead" />
            </case>
            <case value="weblinks">
              <property name="typename" value="CustomPageWebLink" />
            </case>
            <default/>
          </switch>

          <if>
            <isset property="typename" />
            <then>

              <var name="parents_included" unset="true" />

              <!-- Write a new <types/> section for the meta type -->
              <echo file="@{xmlfile}" append="true">  &lt;types&gt;${line.separator}</echo>

              <!-- Loop through files in the type's dir and add <members /> element for each file -->
              <for param="file">
                <path>
                  <!-- exclude xml files and namespaced metadata files as well as any manual excludes -->
                  <fileset dir="@{typedir}" includes="**/*.*" excludes="*.xml *-meta.xml *__*__c.object @{exclude}" />
                </path>
                <sequential>
                  <var name="component_file" unset="true" />
                  <var name="component_file_dirname" unset="true" />
                  <var name="component_file_parent" unset="true" />

                  <!-- get basename and basename without suffix for file. propertyregex is used since we don't know the file suffix from only the dir name (i.e. classes vs .class) -->
                  <basename property="component_file" file="@{file}" />
                  <propertyregex property="component" input="${component_file}" regexp="(.*)\.(.*)" select="\1" />

                  <!-- get the component file's parent directory.  if the parent is not the metatype, include it (i.e. Reports and Dashboards) -->
                  <dirname property="component_file_dirname" file="@{file}" />
                  <basename property="component_file_parent" file="${component_file_dirname}" />

                  <!-- If the file is not in the exclude list, add it to the package.xml -->
                  <if>
                    <and>
                      <not><contains string="@{exclude}" substring="${component_file}" /></not>
                      <not><contains string="${component_file}" substring="-meta.xml" /></not>
                    </and>
                    <then>
                      <!-- Write the <members/> element -->
                      <if>
                        <equals arg1="${metatype}" arg2="${component_file_parent}" />
                        <then>
                          <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component}&lt;/members&gt;${line.separator}</echo>
                        </then>
                        <else>
                          <!-- This type contains metadata grouped into subdirectories under typedir.  Include the dir once and prefix all files with it -->
                          <if>
                            <not><contains string="${parents_included}" substring=",${component_file_parent}," /></not>
                            <then>
                              <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component_file_parent}&lt;/members&gt;${line.separator}</echo>
                              <var name="parents_included" value="${parents_included},${component_file_parent}," />
                            </then>
                          </if>
                          <!-- Some metadata types, such as Document, require the suffix -->
                          <if>
                            <or>
                              <equals arg1="${typename}" arg2="Document" />
                            </or>
                            <then>
                              <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component_file_parent}/${component_file}&lt;/members&gt;${line.separator}</echo>
                            </then>
                            <else>
                              <echo file="@{xmlfile}" append="true">    &lt;members&gt;${component_file_parent}/${component}&lt;/members&gt;${line.separator}</echo>
                            </else>
                          </if>
                        </else>
                      </if> 
                    </then>
                  </if>

                  <!-- Unset basename properties for next execution since we're using basename and propertyregex which will not overwrite a property -->
                  <var name="component" unset="true" />
                  <var name="component_file" unset="true" />
                </sequential>
              </for>

              <!-- Write the <name/> element and close the <types/> element -->
              <echo file="@{xmlfile}" append="true">    &lt;name&gt;${typename}&lt;/name&gt;${line.separator}</echo>
              <echo file="@{xmlfile}" append="true">  &lt;/types&gt;${line.separator}</echo>

            </then>
            <else>
              <echo>Skipping unmapped metadata type ${metatype}</echo>
            </else>
          </if>

          <!-- Unset the metatype so basename can set it on next iteration through loop -->
          <var name="metatype" unset="true" />
        </sequential>
      </for>

      <!-- Write the install and uninstall scripts -->
      <if>
        <not><equals arg1="@{installClass}" arg2="NOTSET" /></not>
        <then>
            <echo file="@{xmlfile}" append="true">  &lt;postInstallClass&gt;@{installClass}&lt;/postInstallClass&gt;${line.separator}</echo>
        </then>
      </if>
        
      <if>
        <not><equals arg1="@{uninstallClass}" arg2="NOTSET" /></not>
        <then>
            <echo file="@{xmlfile}" append="true">  &lt;uninstallClass&gt;@{uninstallClass}&lt;/uninstallClass&gt;${line.separator}</echo>
        </then>
      </if>
      <!-- If package is provided, add the fullName element -->
      <if>
        <not><equals arg1="@{package}" arg2="NOTSET" /></not>
        <then>
          <echo file="@{xmlfile}" append="true">  &lt;fullName&gt;@{package}&lt;/fullName&gt;${line.separator}</echo>
        </then>
      </if>

      <!-- Write the <version/> tag and close <Package/> -->
      <echo file="@{xmlfile}" append="true">  &lt;version&gt;@{version}&lt;/version&gt;${line.separator}</echo>
      <echo file="@{xmlfile}" append="true">&lt;/Package&gt;</echo>
    </sequential>
  </macrodef>

  <macrodef name="getWhitelistForObjectAndType" description="Given an object and a type, returns the whitelist for the type if set">
    <attribute name="object" description="The Salesforce api name of the object" />
    <attribute name="type" description="The metadata type name (i.e. CustomField)" />
    <attribute name="property" description="The name of the property to populate with the whitelist" />
    <sequential>
      <!-- this is split out into a separate macro so we can use a dynamic property name to lookup the whitelist -->
      <if>
        <isset property="@{property}" />
        <then>
          <var name="@{property}" unset="true" />
        </then>
      </if>
      <property name="@{property}" value="${whitelist.@{object}.@{type}}" />
    </sequential>
  </macrodef>

  <macrodef name="addMetadataSubType" description="Uses an xsl template to parse out metadata subtypes from a parent file and adds to a package.xml file">
    <attribute name="dir" description="The package directory containing the files to search in." />
    <attribute name="suffix" description="The suffix of the metadata file to search in.  For example, object for object files" default="object" />
    <attribute name="xmlfile" description="The path of the target package.xml or destructiveChanges.xml file" />
    <attribute name="typename" description="The type name as would appear in the manifest type's name element" />
    <attribute name="xslt" description="The path of the xslt transform to extract type components from the standard object file" default="${cumulus_ci.basedir}/xsl/extract_subtype.xsl" />
    <attribute name="element" description="The xml element to look for.  This is passed to the xslt to allow easier template reuse" />
    <attribute name="nameelement" description="The xml element whose value should provide the name (used in the package.xml members tag)." default="fullName" />
    <attribute name="parentelement" description="The containing element name in target files.  For example, CustomObject if looking in object files" default="CustomObject" />
    <sequential>
      <!-- Make sure the directory actually exist, otherwise do nothing -->
      <if>
        <available file="@{dir}" type="dir" />
        <then>
          <!-- First, build a FILE.SUFFIX.typename file containing the <members/> elements for all components of the type -->
          <for param="file">
            <path>
              <fileset dir="@{dir}" includes="*.@{suffix}" />
            </path>
            <sequential>
              <!-- Parse the file basename and parent name from the parent's filename -->
              <basename property="parent_file" file="@{file}" />
              <basename property="parent" file="@{file}" suffix=".@{suffix}" />

              <!-- Construct an xslt file from the template by replacing ___element with @{element}.  
                   This is to get around xslt's limitations in selecting an element whose name is passed as a parameter.
                   This approach avoids the need to create an xsl file for each metadata subtype
                -->
              <copy file="@{xslt}" tofile="@{xslt}.rendered" />
              <replaceregexp file="@{xslt}.rendered" match="___parentelement___" replace="@{parentelement}" flags="g" />
              <replaceregexp file="@{xslt}.rendered" match="___element___" replace="@{element}" flags="g" />
              <replaceregexp file="@{xslt}.rendered" match="___nameelement___" replace="@{nameelement}" flags="g" />

              <!-- Run xslt to build a file for each parent containing <members/> elements for components -->
              <getWhitelistForObjectAndType object="${parent}" type="@{typename}" property="obj_type_whitelist" />
              <xslt in="@{file}" out="@{dir}/${parent_file}.@{typename}" style="@{xslt}.rendered">
                <param name="whitelist" expression="${obj_type_whitelist}" />
              </xslt>
              <delete file="@{xslt}.rendered" />

              <!-- Prepend member names with object. -->
              <if>
                <equals arg1="@{suffix}" arg2="object" />
                <then>
                  <replaceregexp file="@{dir}/${parent_file}.@{typename}" match="members>(.*)./members>" replace="members>${parent}.\1&lt;/members>${line.separator}" flags="g" />
                </then>
              </if>

              <!-- Prepend member names with workflow. -->
              <if>
                <equals arg1="@{suffix}" arg2="workflow" />
                <then>
                  <replaceregexp file="@{dir}/${parent_file}.@{typename}" match="members>(.*)./members>" replace="members>${parent}.\1&lt;/members>${line.separator}" flags="g" />
                </then>
              </if>

              <!-- Unset properites for basename call in next iteration of loop -->
              <var name="parent" unset="true" />
              <var name="parent_file" unset="true" />
            </sequential>
          </for>

          <!-- Concatenate all <members/> from earlier extraction output files -->
          <concat destfile="@{dir}/@{typename}.extract">
            <fileset dir="@{dir}" includes="**/*.@{suffix}.@{typename}" />
            <filterchain>
              <prefixlines prefix="    " />
              <linecontains>
                <contains value="members>" />
              </linecontains>
            </filterchain>
          </concat>

          <!-- If there are any members to extract, add the <types/> section to package.xml -->
          <if>
            <available file="@{dir}/@{typename}.extract" />
            <then>
              <loadfile property="type.extract" srcfile="@{dir}/@{typename}.extract" />
              <if>
                <isset property="type.extract" /> 
                <then>
                  <var name="type.extract" value="${line.separator}  ${lt}types>${line.separator}${type.extract}    ${lt}name>@{typename}${lt}/name>${line.separator}  ${lt}/types>" />
                  <replaceregexp file="@{xmlfile}" match="Package(.*)>" replace="Package\1>${type.extract}" />
                  <xslt in="@{xmlfile}" out="@{xmlfile}.tidy" style="${cumulus_ci.basedir}/xsl/tidy_package_xml.xsl" />
                  <move file="@{xmlfile}.tidy" tofile="@{xmlfile}" />
                </then>
              </if>
              <delete>
                  <fileset dir="@{dir}" includes="**/*.@{typename}" />
              </delete>
              <delete file="@{dir}/@{typename}.extract" />
        
              <var name="type.extract" unset="true" />
            </then>  
          </if>
        </then>
      </if>
    </sequential>  
  </macrodef>

  <macrodef name="buildPackageXml" description="Construct a complete package.xml file based on the metadata in the src directory">
    <attribute name="version" description="The value for the version element in the package.xml" />
    <attribute name="package" description="The value for the fullName element in the package.xml" default="NOTSET" />
    <attribute name="installClass" description="If provided, a installClass element will be added to the file to specify a class to run when installing the package." default="NOTSET" />
    <attribute name="uninstallClass" description="If provided, a uninstallClass element will be added to the file to specify a class to run when uninstalling the package." default="NOTSET" />
    <attribute name="srcDir" description="If provided, the relative path to the source directory from the project root.  Defaults to src" default="src" />
    
  
    <sequential>
      <!-- Exclude CustomLabels.labels so we can use addMetadataSubType on it instead -->
      <buildPackageXmlFromDir dir="${basedir}/@{srcDir}" xmlfile="${basedir}/@{srcDir}/package.xml.new" package="@{package}" version="@{version}" exclude="CustomLabels.labels" installClass="@{installClass}" uninstallClass="@{uninstallClass}" />

      <!-- Add metadata sub-types of object which need to be explicitly listed in package.xml -->
      <!--<addMetadataSubType dir="${basedir}/@{srcDir}" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="ActionOverride" element="actionOverrides" nameelement="actionName" />-->
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="BusinessProcess" element="businessProcesses" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="CompactLayout" element="compactLayouts" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="CustomField" element="fields" xslt="${cumulus_ci.basedir}/xsl/extract_obj_fields.xsl" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/labels" suffix="labels" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="CustomLabel" element="labels" parentelement="CustomLabels" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="FieldSet" element="fieldSets" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="ListView" element="listViews" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="NamedFilter" element="namedFilters" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="RecordType" element="recordTypes" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="ValidationRule" element="validationRules" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/objects" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="WebLink" element="webLinks" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/workflows" suffix="workflow" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="WorkflowFieldUpdate" element="fieldUpdates" parentelement="Workflow" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/workflows" suffix="workflow" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="WorkflowRule" element="rules" parentelement="Workflow" />
      <addMetadataSubType dir="${basedir}/@{srcDir}/workflows" suffix="workflow" xmlfile="${basedir}/@{srcDir}/package.xml.new" typename="WorkflowAlert" element="alerts" parentelement="Workflow" />

      <move file="${basedir}/@{srcDir}/package.xml.new" tofile="${basedir}/@{srcDir}/package.xml" />
    </sequential>
  </macrodef>

  <macrodef name="buildDestroyStaleMetadata" description="Constructs a destructiveChanges.xml which deletes any metadata from one directory which does not exist in another">
    <attribute name="srcdir" description="Source directory, usually containing metadata from target org fetched via retrievePackaged" />
    <attribute name="commdir" description="The directory whose metadata should be compared to srcdir.  Any metadata not in this directory's package.xml but in srcdir's package.xml will be added to destructiveChanges.xml" />
    <attribute name="dir" description="Target directory to create the destructiveChanges.xml package" />
    <sequential>
      <!-- Run xslt against package.xml files to generate a text file containing TYPE::MEMBER lines for each member for easier comparison later -->
      <xslt in="@{srcdir}/package.xml" out="@{srcdir}/package.xml.members" style="${cumulus_ci.basedir}/xsl/diff_package_xml.xsl" />
      <xslt in="@{commdir}/package.xml" out="@{commdir}/package.xml.members" style="${cumulus_ci.basedir}/xsl/diff_package_xml.xsl" />
      <loadfile property="src.package.xml.members" srcfile="@{srcdir}/package.xml.members" />
{'fraction_non_alphanumeric': 0.15259929501235117, 'fraction_numerical': 0.0029975852785256323, 'mean_word_length': 2.6602326408289736, 'pattern_counts': {'":': 0, '<': 1276, '<?xml version=': 2, '>': 1284, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 212}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '765191', 'n_tokens_mistral': 19487, 'n_tokens_neox': 18650, 'n_words': 4773}