/*
* Licensed Materials - Property of IBM* and/or HCL**
* UrbanCode Deploy
* UrbanCode Build
* UrbanCode Release
* AnthillPro
* (c) Copyright IBM Corporation 2011, 2017. All Rights Reserved.
* (c) Copyright HCL Technologies Ltd. 2020. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*
* * Trademark of International Business Machines
* ** Trademark of HCL Technologies Limited
*/
package com.ibm.urbancode.zos.common

import groovy.xml.StreamingMarkupBuilder
import groovy.xml.XmlUtil

import java.beans.PropertyChangeEvent
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Date

import java.util.regex.Matcher
import java.util.regex.Pattern

import org.codehaus.jettison.json.*;
import groovy.json.JsonSlurper
import com.ibm.urbancode.zos.jes.*;

class PackageManifestXMLHelper {
    def fXmlParser;

    def Logger logger = Logger.getDefault();

    public PackageManifestXMLHelper() {
        fXmlParser = new XmlParser();
    }

    def getAllDeployDatasets(manifestFilePath) {
        def manifestFile = new File(manifestFilePath);
        def manifest = fXmlParser.parse(manifestFile);


        def allAddDatasetsContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });

        def allDeleteDatasetsContainers = manifest.deleted.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });

        def allCreatedDatasetsContainers = manifest.created.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });

        def allUpdatedDatasetsContainers = manifest.updated.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });

        def allDataSets = allAddDatasetsContainers + allDeleteDatasetsContainers + allCreatedDatasetsContainers + allUpdatedDatasetsContainers;
        if (!allDataSets || allDataSets.size() < 1) {
            throw new IllegalArgumentException("There is no datasets to deploy for this version");
        }

        def names = allDataSets."@name";
        names = names*.trim();

        return new HashSet<String>(names);
    }

    // Added to getAllDeployHFSFiles
    def getAllDeployHFSFiles(manifestFilePath) {
        def manifestFile = new File(manifestFilePath);
        def manifest = fXmlParser.parse(manifestFile);


        def allAddHFSFilesContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.FILE_CONTAINER_TYPE
        });

        def allDeleteHFSFilesContainers = manifest.deleted.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.FILE_CONTAINER_TYPE
        });

        def allCreatedHFSFilesContainers = manifest.created.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.FILE_CONTAINER_TYPE
        });

        def allUpdatedHFSFilesContainers = manifest.updated.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.FILE_CONTAINER_TYPE
        });

        def allHFSFiles = allAddHFSFilesContainers + allDeleteHFSFilesContainers + allCreatedHFSFilesContainers + allUpdatedHFSFilesContainers;




        if (!allHFSFiles || allHFSFiles.size() < 1) {
            throw new IllegalArgumentException("There is no HFS Files to deploy for this version");
        }

        def names = allHFSFiles."@name";
        names = names*.trim();

        for (int i = 0; i < names.size(); i++) {

            if (names[i].getAt(0) != '/') {
                names[i] = '/' + names[i];
            }

        }
        return new HashSet<String>(names);
    }


    def reFormatXML(manifestFilePath, manifestFilePath4deploy) {
        def manifestFile = new File(manifestFilePath);
        if (!manifestFile.exists()) {
            throw new IllegalArgumentException("${manifestFilePath} not found. Please make sure a Copy or FTP plug-in step is successfully executed before the Deploy plug-in step.");
        }
        def manifest = new XmlParser().parse(manifestFile)
        def seResult = new StringWriter().with { sw ->
            new XmlNodePrinter(new PrintWriter(sw)).print(manifest);
            sw.toString();
        }
        def outputXml = "<?xml version=\"1.0\"?>\n" + seResult.toString();

        new File(manifestFilePath4deploy).withWriter('IBM-1047') { out ->
            out << outputXml
        }
        (new AntBuilder()).copy(file: "${manifestFilePath4deploy}", tofile: "${manifestFilePath}");
    }

    // To delete containers from Mainfest file for Partial Deployment
    def deleteContainersNotSelectedInFilter(deployManifestPath, containersDSList, isFirstTimePartialDeploy) {
        def manifestFile = new File(deployManifestPath);
        if (!manifestFile.exists()) {
            throw new IllegalArgumentException("${deployManifestPath} not found. Please make sure download artifacts step is successfully executed before the Deploy plug-in step.");
        }
        def manifest = new XmlParser().parse(manifestFile)
        // Delete containers which are not required for deployment
        Iterator<String> iterator = containersDSList.iterator();
        while (iterator.hasNext()) {
            String dsName = iterator.next();
            manifest.container.findAll { it.@name.toUpperCase() == dsName.toUpperCase() }.each { manifest.remove(it) }
            manifest.deleted.container.findAll { it.@name.toUpperCase() == dsName.toUpperCase() }.each { it.parent().remove(it) }
        }
        if (hasHFSFiles(deployManifestPath) && !isFirstTimePartialDeploy) {
            // Delete HFS directories as well from manifest as it is already been deployed first time
            manifest.container.findAll { it.@type.toLowerCase() == DeploymentConstants.DIRECTORY_CONTAINER_TYPE }.each { manifest.remove(it) }
            manifest.deleted.container.findAll { it.@type.toLowerCase() == DeploymentConstants.DIRECTORY_CONTAINER_TYPE }.each { it.parent().remove(it) }
        }

        def seResult = new StringWriter().with { sw ->
            new XmlNodePrinter(new PrintWriter(sw)).print(manifest);
            sw.toString();
        }
        def outputXml = "<?xml version=\"1.0\"?>\n" + seResult.toString();

        new File(deployManifestPath).withWriter('IBM-1047') { out ->
            out << outputXml
        }
    }

    def removeNodes4Deployment(manifestFilePath, manifestFilePath4deploy) {
        def manifestFile = new File(manifestFilePath);
        if (!manifestFile.exists()) {
            throw new IllegalArgumentException("${manifestFilePath} not found. Please make sure a Copy or FTP plug-in step is successfully executed before the Deploy plug-in step.");
        }
        def manifest = new XmlParser().parse(manifestFile)

        def allGenericContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.GENERIC_CONTAINER_TYPE;
        });
        def allContainerInputsNodes = manifest.container.inputs;
        def allResourceInputsNodes = manifest.container.resource.inputs;

        if (allGenericContainers.size() > 0 || allContainerInputsNodes.size() > 0 || allResourceInputsNodes.size() > 0) {
            if (allGenericContainers.size() > 0) {
                for (genericNode in allGenericContainers) {
                    def parent = genericNode.parent();
                    parent.remove(genericNode);
                }
            }
            if (allContainerInputsNodes.size() > 0) {
                for (containerInputs in allContainerInputsNodes) {
                    def parent = containerInputs.parent();
                    parent.remove(containerInputs);
                }
            }
            if (allResourceInputsNodes.size() > 0) {
                for (resourceInputs in allResourceInputsNodes) {
                    def parent = resourceInputs.parent();
                    parent.remove(resourceInputs);
                }
            }

            def seResult = new StringWriter().with { sw ->
                new XmlNodePrinter(new PrintWriter(sw)).print(manifest);
                sw.toString();
            }
            def outputXml = "<?xml version=\"1.0\"?>\n" + seResult.toString();

            new File(manifestFilePath4deploy).withWriter('IBM-1047') { out ->
                out << outputXml
            }
        } else {
            (new AntBuilder()).copy(file: "${manifestFilePath}", tofile: "${manifestFilePath4deploy}");
        }
        String fileContents = new File(manifestFilePath4deploy).text

        Pattern containerPattern = Pattern.compile("<container (.*)name=(.*) (.*)>");
        Matcher containerMatch = containerPattern.matcher(fileContents);

        StringBuffer sb = new StringBuffer();
        while (containerMatch.find()) {
            containerMatch.appendReplacement(sb, "<container name=\$2 \$1 \$3>");
        }
        containerMatch.appendTail(sb);
        fileContents = sb.toString();

        Pattern resourcePattern = Pattern.compile("<resource (.*)name=(.*) (.*)>");
        Matcher resoureMatch = resourcePattern.matcher(fileContents);
        StringBuffer resourceSb = new StringBuffer();
        while (resoureMatch.find()) {
            resoureMatch.appendReplacement(resourceSb, "<resource name=\$2 \$1 \$3>");
        }
        resoureMatch.appendTail(resourceSb);
        fileContents = resourceSb.toString();

        new File(manifestFilePath4deploy).withWriter('IBM-1047') { out ->
            out << fileContents
        }
    }

    def isGenericOnlyVersion(manifestFilePath) {
        def ret = false;

        def manifestFile = new File(manifestFilePath);
        def manifest = new XmlSlurper(false, false).parse(manifestFile)

        def allAddDatasetsContainers = manifest.container.findAll({ node ->
            node.@type != DeploymentConstants.GENERIC_CONTAINER_TYPE;
        });
        if (allAddDatasetsContainers.size() > 0) {
            return false;
        }

        def allDeleteDatasetsContainers = manifest.deleted.container.findAll({ node ->
            node.@type != DeploymentConstants.GENERIC_CONTAINER_TYPE;
        });
        if (allDeleteDatasetsContainers.size() > 0) {
            return false;
        }

        def allGenericContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.GENERIC_CONTAINER_TYPE;
        });
        if (allGenericContainers.size() > 0) {
            ret = true;
        }

        return ret;
    }

    //check for any PDS or sequential type containers.
    def hasDataSets(manifestFilePath) {
        def manifestFile = new File(manifestFilePath);
        def manifest = new XmlSlurper(false, false).parse(manifestFile)

        def allAddDatasetsContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });

        if (allAddDatasetsContainers.size() > 0) {
            return true;
        }

        def allDeleteDatasetsContainers = manifest.deleted.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });
        if (allDeleteDatasetsContainers.size() > 0) {
            return true;
        }

        def allCreatedDatasetsContainers = manifest.created.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });
        if (allCreatedDatasetsContainers.size() > 0) {
            return true;
        }
        def allUpdatedDatasetsContainers = manifest.updated.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE ||
                    node.@type == DeploymentConstants.SEQUANTIAL_CONTAINER_TYPE
        });
        if (allUpdatedDatasetsContainers.size() > 0) {
            return true;
        }


        return false;
    }

    //check for any HFS file containers
    def hasHFSFiles(manifestFilePath) {
        def manifestFile = new File(manifestFilePath);
        def manifest = new XmlSlurper(false, false).parse(manifestFile)

        def allAddDirectoryContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE
        });

        if (allAddDirectoryContainers.size() > 0) {
            return true;
        }

        def allDeleteDirectoryContainers = manifest.deleted.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE
        });
        if (allDeleteDirectoryContainers.size() > 0) {
            return true;
        }

        def allCreatedDirectoryContainers = manifest.created.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE
        });
        if (allCreatedDirectoryContainers.size() > 0) {
            return true;
        }

        def allUpdatedDirectoryContainers = manifest.updated.container.findAll({ node ->
            node.@type == DeploymentConstants.DIRECTORY_CONTAINER_TYPE
        });
        if (allUpdatedDirectoryContainers.size() > 0) {
            return true;
        }

        return false;
    }

    /**
     * Compare each artifact with the same artifact from the inventory record. 
     * If the inventory record contains an identical artifact, remove the artifact
     * from the manifest so it it won't be deployed. 
     *
     * Two artifacts are identical if all following attributes are identical
     *  - data set name
     *  - member name
     *  - lastModifiedTime
     *  - customer properties starts with SYS.id.
     *
     * @param inventoryArtifacts inventory querty results. 
     * @param manifestFilePath manifest of the to be deployed version.
     * @return no return value. The manifestFile has already been modified to only contain 
     *                 the delta that needs to be deployed. i.e. identical artifacts are removed. 
     */
    def calculateIncrementalManifest(JSONArray inventoryArtifacts, manifestFilePath) {

        logger.writeDebug("Inventory artifacts : ");
        logger.writeDebug(inventoryArtifacts.toString(2));
        logger.writeDebug("Version manifest file ${manifestFilePath} : ");
        logger.writeDebug(new File(manifestFilePath).text);

        def deltaApplied = false;
        def jsonSlurper = new JsonSlurper()
        def artifacts = jsonSlurper.parseText(inventoryArtifacts.toString())
        def manifestFile = new File(manifestFilePath);
        if (!manifestFile.exists()) {
            throw new IllegalArgumentException("${manifestFilePath} not found.");
        }
        def manifest = new XmlParser().parse(manifestFile)

        // only added data sets requires delta calculation. deletes will always do deletes
        def allAddDatasetsContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE
        });

        //process resources and remove identical entries
        for (container in allAddDatasetsContainers) {
            for (resource in container.resource) {
                logger.writeDebug("        Checking ${container.@name}(${resource.@name}) with inventory records");
                //locate the inventory record of the same resource
                def artifact = artifacts.find {
                    //println  "${it.containerName}(${it.name})  :  ${it.file.zosContainerType}";
                    it.file.zosContainerType == "PDSMember" &&
                            it.containerName == container.@name &&
                            it.name == resource.@name
                }
                if (artifact == null) {
                    logger.writeLog("        ${container.@name}(${resource.@name}) is not found in inventory.");
                    continue
                }

                logger.writeDebug("        Artifact found in inventory version ${artifact.version.name} ");
                logger.writeDebug("        Comparing attributes");

                //get id attributes for the version artifact
                def versionIdMap = [:];
                if (resource.@lastModifiedTimestamp) {
                    def longLastModified = convertDateStringToLong(resource.@lastModifiedTimestamp)
                    versionIdMap.put("lastModifiedTimestamp", longLastModified)
                }
                if (resource.property) {
                    for (property in resource.property) {
                        if (property.@name.startsWith("SYS.id.")) {
                            versionIdMap.put(property.@name, property.@value);
                        }
                    }
                }

                //get id attributes for the inventory artifact
                def inventoryIdMap = [:];
                if (artifact.file.lastModified) {
                    inventoryIdMap.put("lastModifiedTimestamp", artifact.file.lastModified);
                }
                if (artifact.file.userAttributes && artifact.file.userAttributes.zOSCustomerPropertiesKey) {
                    for (property in artifact.file.userAttributes.zOSCustomerPropertiesKey) {
                        if (property.key.startsWith("SYS.id.")) {
                            inventoryIdMap.put(property.key, property.value);
                        }
                    }
                }

                //compare
                def identical = true;
                logger.writeDebug("        version   :" + versionIdMap);
                logger.writeDebug("        inventory:" + inventoryIdMap);

                if (versionIdMap.size() == 0) {
                    logger.writeDebug("        The version artifact does not have any id fields. Continue to deploy. ");
                    identical = false;
                } else if (versionIdMap.size() != inventoryIdMap.size()) {
                    logger.writeDebug("        Number of id fields does not match. Continue to deploy ");
                    identical = false;
                } else {
                    for (property in versionIdMap) {
                        def name = property.getKey();
                        def value = property.getValue();
                        def invValue = inventoryIdMap.get(name)
                        if (invValue != value) {
                            logger.writeDebug("        ${name} does not match. version value : ${value} <> inventory value : ${invValue} . Continue to deploy ");
                            identical = false;
                            break;
                        }
                    }
                }

                if (identical) {
                    logger.writeLog("        ${container.@name}(${resource.@name}) is identical with inventory version ${artifact.version.name}. Skip deploy for this artifact.");
                    container.remove(resource);
                    deltaApplied = true;
                } else {
                    logger.writeLog("        ${container.@name}(${resource.@name}) is different with inventory version ");
                }
            }
            if (container.resource.size() == 0) {
                manifest.remove(container)
            }
        }

        if (deltaApplied) {
            def deltaManifest = new StringWriter().with { sw ->
                new XmlNodePrinter(new PrintWriter(sw)).print(manifest);
                sw.toString();
            }
            def outputXml = "<?xml version=\"1.0\"?>\n" + deltaManifest.toString();

            new File(manifestFilePath).withWriter('IBM-1047') { out ->
                //new File(manifestFilePath + ".delta").withWriter('IBM-1047'){ out ->
                out << outputXml
            }
            logger.writeDebug("    ${manifestFilePath} updated to contain the delta only");
            logger.writeDebug(outputXml);
        }
    }
    /**
     * Compare each artifact with the same artifact from the target environment.
     * If the inventory record contains an identical artifact, remove the artifact
     * from the manifest so it it won't be deployed.
     *
     * Two artifacts are identical if all following attributes are identical
     *  - data set name
     *  - member name
     *  - Generated Hashvalue of target environment matches with the Hashvalue
     *     of to be deployed artifact
     *  - customer properties starts with SYS.id.
     *
     * @param inventoryArtifacts inventory querty results.
     * @param manifestFilePath manifest of the to be deployed version.
     * @return no return value. The manifestFile has already been modified to only contain
     *                 the delta that needs to be deployed. i.e. identical artifacts are removed.
     */

    def calculateTargetIncrementalManifest(manifestFilePath, dsToDeploySrc2TargetMap, zosScriptPath) {
        logger.writeDebug("Version manifest file ${manifestFilePath} : ");
        logger.writeDebug(new File(manifestFilePath).text);

        def deltaApplied = false;
        def manifestFile = new File(manifestFilePath);
        if (!manifestFile.exists()) {
            throw new IllegalArgumentException("${manifestFilePath} not found.");
        }
        def manifest = new XmlParser().parse(manifestFile)

        // only added data sets requires delta calculation. deletes will always do deletes
        def allAddDatasetsContainers = manifest.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE
        });

        //process resources and remove identical entries
        for (container in allAddDatasetsContainers) {
            for (resource in container.resource) {

                logger.writeDebug("        Comparing attributes");

                //get id attributes for the version artifact
                def versionIdMap = [:];

                if (resource.property) {
                    for (property in resource.property) {
                        if (property.@name.startsWith("SYS.id.HashValue")) {
                            versionIdMap.put(property.@name, property.@value);
                        }
                    }
                }

                //get id attributes for the inventory artifact
                def targetEnvIdMap = [:];
                def targetPDS;
                if (dsToDeploySrc2TargetMap.get(container.@name)) {
                    targetPDS = dsToDeploySrc2TargetMap.get(container.@name);
                } else {
                    targetPDS = container.@name;
                }
                logger.writeDebug("      source is    :" + container.@name);
                logger.writeDebug("      target is    :" + targetPDS);

                //def ispfpath = "/var/ucd/sitagent/bin/startispf.sh ";
                def commands = zosScriptPath + " HASHCAL " + targetPDS + " " + resource.@name + " " + "DEPLOY";
                logger.writeDebug("command to execute is " + commands)
                Process process = null;
                int packageResult = 0;
                process = Runtime.getRuntime().exec(commands);
                BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))
                def line = null
                def outputXMLStringBuf = new StringBuffer()
                while ((line = br.readLine()) != null) {
                    if (line.trim().startsWith("Connection Protocol")) {
                        //skip lines which cause XML parsing problem
                        continue
                    }
                    outputXMLStringBuf.append(line).append('\n')
                }
                process.waitFor();
                packageResult = process.exitValue();
                if (process.exitValue() == 0) {
                    String outputXMLString;
                    outputXMLString = outputXMLStringBuf.toString();
                    int hashValConstStrLen = 'HASH VALUE IS '.length()
                    int hashValPosition = outputXMLString.indexOf('HASH VALUE IS ')
                    targetEnvIdMap.put("SYS.id.HashValue", outputXMLString.substring(hashValPosition + hashValConstStrLen + 1, hashValPosition + hashValConstStrLen + 41));
                }

                //compare
                def identical = true;
                logger.writeDebug("      version    :" + versionIdMap);
                logger.writeDebug("      Target Env :" + targetEnvIdMap);

                if (versionIdMap.size() == 0) {
                    logger.writeLog("        The version artifact does not have any id fields. Continue to deploy. ");
                    identical = false;
                } else if (versionIdMap.size() != targetEnvIdMap.size()) {
                    logger.writeLog("        Number of id fields does not match. Continue to deploy ");
                    identical = false;
                } else {
                    for (property in versionIdMap) {
                        def name = property.getKey();
                        def value = property.getValue();
                        def invValue = targetEnvIdMap.get(name)
                        if (invValue != null && (DeploymentConstants.ARTIFACT_NOT_FOUND == invValue.toString())) {
                            logger.writeLog("      ${container.@name}(${resource.@name}) does not exist in target inventory  . Continue to deploy ");
                            identical = false;
                            break;
                        }
                        if (invValue != value) {
                            logger.writeLog("        ${name} does  not match. version value : ${value} <> inventory value : ${invValue} . Continue to deploy ${container.@name}(${resource.@name}) ");
                            identical = false;
                            break;
                        }
                    }
                }

                if (identical) {
                    logger.writeLog("        ${container.@name}(${resource.@name}) is identical with target inventory version. Skip deploy for this artifact.");
                    container.remove(resource);
                    deltaApplied = true;
                }
            }
            if (container.resource.size() == 0) {
                manifest.remove(container)
            }
        }

        if (deltaApplied) {
            def deltaManifest = new StringWriter().with { sw ->
                new XmlNodePrinter(new PrintWriter(sw)).print(manifest);
                sw.toString();
            }
            def outputXml = "<?xml version=\"1.0\"?>\n" + deltaManifest.toString();

            new File(manifestFilePath).withWriter('IBM-1047') { out ->
                //new File(manifestFilePath + ".delta").withWriter('IBM-1047'){ out ->
                out << outputXml
            }
            logger.writeDebug("    ${manifestFilePath} updated to contain the delta only");
            logger.writeDebug(outputXml);
        }
    }
    /**
     * Convert date string to long
     *
     * @param dateString
     * @return
     */
    def convertDateStringToLong(String dateString) {
        Long timesetamp = null;

        try {
            timesetamp = Long.parseLong(dateString);
        } catch (Exception e) {
            try {
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                Date date = formatter.parse(dateString.trim());
                timesetamp = date.getTime();
            } catch (Exception e2) {
            }
        }

        if (timesetamp) {
            return timesetamp
        } else {
            return dateString
        }
    }

    /**
     * When deploying a merged version. The parent manifest has already gone through delta calculation
     * and only contains the delta. Each of the child manifest should be compared to the parent and 
     * any resources which doesn't in parent should also be removed from child so it does not get deployed. 
     *
     * @param parentManifest manifest of the merged version, already gone through delta calculation
     * @param childManifest manifest of the included versions
     * @return no return value. The childManifest has  been modified
     */
    def calculateIncrementalManifestForChild(parentManifest, childManifest) {

        def deltaApplied = false;

        def parentManifestFile = new File(parentManifest);
        if (!parentManifestFile.exists()) {
            throw new IllegalArgumentException("${parentManifest} not found.");
        }
        def parent = new XmlParser().parse(parentManifestFile)
        // only added data sets requires delta calculation. deletes will always do deletes
        def parentContainers = parent.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE
        });

        def childManifestFile = new File(childManifest);
        if (!childManifestFile.exists()) {
            throw new IllegalArgumentException("${childManifest} not found.");
        }
        def child = new XmlParser().parse(childManifestFile)
        // only added data sets requires delta calculation. deletes will always do deletes
        def childContainers = child.container.findAll({ node ->
            node.@type == DeploymentConstants.PDS_CONTAINER_TYPE
        });

        for (container in childContainers) {
            for (resource in container.resource) {
                def foundInParent = false;
                parentContainers.each { c ->
                    if (c.@name == container.@name) {
                        c.resource.each { r ->
                            if (r.@name == resource.@name) {
                                foundInParent = true;
                            }
                        }
                    }
                }
                if (!foundInParent) {
                    logger.writeDebug("        ${container.@name}(${resource.@name}) is skipped ");
                    container.remove(resource);
                    deltaApplied = true;
                }
            }
            if (container.resource.size() == 0) {
                child.remove(container)
            }
        }

        if (deltaApplied) {
            def deltaManifest = new StringWriter().with { sw ->
                new XmlNodePrinter(new PrintWriter(sw)).print(child);
                sw.toString();
            }
            def outputXml = "<?xml version=\"1.0\"?>\n" + deltaManifest.toString();

            new File(childManifest).withWriter('IBM-1047') { out ->
                out << outputXml
            }
            logger.writeDebug("${childManifest} updated to contain the delta only");
            logger.writeDebug(outputXml);
        }

    }

}
