/*
* 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. 2018. 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}");
		}
	
	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 );
        }      
        
    }
}
