/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    IBM - Initial API and implementation
 *    G&H Softwareentwicklung GmbH - internationalization implementation (bug 150933)
 *    Prosyst - create proper OSGi bundles (bug 174157)
 *    Felix Riegger (SAP AG) - consolidation of publishers for PDE formats (bug 331974)
 *    Rapicorp, Inc - Specify download stats URL and type of artifacts to monitor in category.xml (bug 436872)  
 *******************************************************************************/
package org.eclipse.pde.internal.build;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarFile;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.pde.internal.build.ant.AntScript;
import org.eclipse.pde.internal.build.ant.FileSet;
import org.eclipse.pde.internal.build.ant.TarFileSet;
import org.eclipse.pde.internal.build.ant.ZipFileSet;
import org.eclipse.pde.internal.build.builder.BuildDirector;
import org.eclipse.pde.internal.build.builder.ModelBuildScriptGenerator;
import org.eclipse.pde.internal.build.site.BuildTimeFeature;
import org.osgi.framework.Bundle;
import org.osgi.framework.Version;

/**
 * Generate an assemble script for a given feature and a given config. It
 * generates all the instruction to zip the listed plugins and features.
 */
public class AssembleConfigScriptGenerator extends AbstractScriptGenerator {
	protected String directory; // representing the directory where to generate the file
	protected String featureId;
	protected Config configInfo;
	protected BuildTimeFeature[] features; // the features that will be assembled
	protected BuildTimeFeature[] allFeatures; //the set of all the features that have been considered
	protected BundleDescription[] plugins;
	protected String filename;
	protected Collection<BuildTimeFeature> rootFileProviders;
	protected String rootFolder = null;
	protected ArrayList<String> addedByPermissions = new ArrayList<>(); //contains the list of files and folders that have been added to an archive by permission management

	private static final String PROPERTY_SOURCE = "source"; //$NON-NLS-1$
	private static final String PROPERTY_ELEMENT_NAME = "elementName"; //$NON-NLS-1$

	private static final byte BUNDLE_TYPE = 0;
	private static final byte FEATURE_TYPE = 1;

	protected String PROPERTY_ECLIPSE_PLUGINS = "eclipse.plugins"; //$NON-NLS-1$
	protected String PROPERTY_ECLIPSE_FEATURES = "eclipse.features"; //$NON-NLS-1$
	protected boolean signJars;
	private boolean generateJnlp;

	private String archiveFormat;
	private boolean groupConfigs = false;
	private String product;
	private ProductFile productFile = null;
	protected ShapeAdvisor shapeAdvisor = null;
	private Boolean p2Bundles = null;

	public AssembleConfigScriptGenerator() {
		super();
	}

	public void initialize(String directoryName, String feature, Config configurationInformation, Collection<BundleDescription> elementList, Collection<BuildTimeFeature> featureList, Collection<BuildTimeFeature> allFeaturesList, Collection<BuildTimeFeature> rootProviders) throws CoreException {
		this.directory = directoryName;
		this.featureId = feature;
		this.configInfo = configurationInformation;
		this.rootFileProviders = rootProviders != null ? rootProviders : new ArrayList<>(0);
		this.rootFolder = Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + configInfo.toStringReplacingAny(".", ANY_STRING) + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER); //$NON-NLS-1$
		this.features = new BuildTimeFeature[featureList.size()];
		featureList.toArray(this.features);

		this.allFeatures = new BuildTimeFeature[allFeaturesList.size()];
		allFeaturesList.toArray(this.allFeatures);

		this.plugins = new BundleDescription[elementList.size()];
		this.plugins = elementList.toArray(this.plugins);

		openScript(directoryName, getTargetName() + ".xml"); //$NON-NLS-1$
		shapeAdvisor = new ShapeAdvisor();
		shapeAdvisor.setForceUpdateJars(forceUpdateJarFormat);
	}

	protected String computeIconsList() {
		return computeIconsList(configInfo.getOs());
	}

	protected String computeIconsList(String os) {
		String result = Utils.getPropertyFormat(PROPERTY_LAUNCHER_ICONS);
		if (getProductFile() == null)
			return result;
		String[] icons = os != null ? productFile.getIcons(os) : productFile.getIcons();
		for (String icon2 : icons) {

			String icon = Utils.makeRelative(IPath.fromOSString(icon2), IPath.fromOSString(productFile.getLocation().getParent())).toOSString();

			String location = findFile(icon, true);
			if (location == null) {
				File iconFile = new File(productFile.getLocation().getParentFile(), icon);
				if (iconFile.exists())
					location = Utils.makeRelative(IPath.fromOSString(iconFile.getAbsolutePath()), IPath.fromOSString(workingDirectory)).toOSString();
			}
			if (location != null)
				result += ", " + Utils.getPropertyFormat(PROPERTY_BASEDIR) + '/' + location; //$NON-NLS-1$
			else {
				result += ", " + Utils.getPropertyFormat(PROPERTY_BUILD_DIRECTORY) + '/' + DEFAULT_PLUGIN_LOCATION + '/' + icon; //$NON-NLS-1$
				result += ", " + Utils.getPropertyFormat(PROPERTY_BUILD_DIRECTORY) + '/' + DEFAULT_FEATURE_LOCATION + '/' + icon; //$NON-NLS-1$
			}
		}
		return result;
	}

	@Override
	public void generate() {
		try {
			// Note that we must pass the OS information in to load product
			// so that any icon files can be calculated for the 
			// generateBrandingCalls.
			productFile = loadProduct(product, configInfo.getOs());
		} catch (CoreException e) {
			//ignore
		}
		generatePrologue();

		generateMainBegin();
		generateInitializationSteps();
		if (BuildDirector.p2Gathering) {
			generateP2Assembling();
		} else {
			generateGatherCalls();
			generateProcessingCalls();
			generateBrandingCalls();
			generateP2Steps();
		}
		generateArchivingCalls();
		generateMainEnd();

		generateEpilogue();
	}

	protected void generateGatherCalls() {
		script.printAntCallTask(TARGET_GATHER_BIN_PARTS, true, null);

		if (embeddedSource)
			script.printAntCallTask(TARGET_GATHER_SOURCES, true, null);

		printCustomAssemblyAntCall(PROPERTY_POST + TARGET_GATHER_BIN_PARTS, null);
		script.println();
	}

	protected void generateP2Assembling() {
		if (productFile != null) {
			script.printAntCallTask(TARGET_RUN_DIRECTOR, true, null);
			script.printAntCallTask(TARGET_MIRROR_PRODUCT, true, null);
		} else {
			script.printAntCallTask(TARGET_MIRROR_ARCHIVE, true, null);
		}
	}

	protected void generateMirrorProductTask() {
		Map<String, String> mirrorArgs = new HashMap<>();
		mirrorArgs.put(PROPERTY_P2_MIRROR_METADATA_DEST, Utils.getPropertyFormat(PROPERTY_P2_METADATA_REPO));
		mirrorArgs.put(PROPERTY_P2_MIRROR_ARTIFACT_DEST, Utils.getPropertyFormat(PROPERTY_P2_ARTIFACT_REPO));

		script.printTargetDeclaration(TARGET_MIRROR_PRODUCT, null, PROPERTY_P2_METADATA_REPO, PROPERTY_SKIP_MIRRORING, null);
		script.printAntCallTask(TARGET_MIRROR_ARCHIVE, true, mirrorArgs);
		script.printTargetEnd();
		script.println();
	}

	protected void generateMirrorTask(boolean assembling) {
		script.printTargetDeclaration(TARGET_MIRROR_ARCHIVE, null, null, PROPERTY_SKIP_MIRRORING, null);
		script.printProperty(PROPERTY_P2_APPEND, "true"); //$NON-NLS-1$
		script.printProperty(PROPERTY_P2_MIRROR_METADATA_DEST, "file:" + Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE)); //$NON-NLS-1$
		script.printProperty(PROPERTY_P2_MIRROR_ARTIFACT_DEST, "file:" + Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE)); //$NON-NLS-1$
		if (features.length + plugins.length + rootFileProviders.size() > 0 || productFile != null) {
			script.printTab();
			script.print("<p2.mirror "); //$NON-NLS-1$
			script.printAttribute("source", Utils.getPropertyFormat(PROPERTY_P2_BUILD_REPO), true); //$NON-NLS-1$
			script.printAttribute("mirrorProperties", TRUE, true); //$NON-NLS-1$
			script.println(">"); //$NON-NLS-1$
			script.printTab();
			script.print("\t<destination "); //$NON-NLS-1$
			script.printAttribute("location", Utils.getPropertyFormat(PROPERTY_P2_MIRROR_METADATA_DEST), true); //$NON-NLS-1$ 
			script.printAttribute("name", Utils.getPropertyFormat(PROPERTY_P2_METADATA_REPO_NAME), true); //$NON-NLS-1$
			script.printAttribute("compressed", Utils.getPropertyFormat(PROPERTY_P2_COMPRESS), true); //$NON-NLS-1$
			script.printAttribute("append", assembling ? Utils.getPropertyFormat(PROPERTY_P2_APPEND) : TRUE, true); //$NON-NLS-1$
			script.printAttribute("kind", "metadata", true); //$NON-NLS-1$ //$NON-NLS-2$
			script.println("/>"); //$NON-NLS-1$
			script.printTab();
			script.print("\t<destination "); //$NON-NLS-1$
			script.printAttribute("location", Utils.getPropertyFormat(PROPERTY_P2_MIRROR_ARTIFACT_DEST), true); //$NON-NLS-1$ 
			script.printAttribute("name", Utils.getPropertyFormat(PROPERTY_P2_ARTIFACT_REPO_NAME), true); //$NON-NLS-1$
			script.printAttribute("compressed", Utils.getPropertyFormat(PROPERTY_P2_COMPRESS), true); //$NON-NLS-1$
			script.printAttribute("append", assembling ? Utils.getPropertyFormat(PROPERTY_P2_APPEND) : TRUE, true); //$NON-NLS-1$
			script.printAttribute("kind", "artifact", true); //$NON-NLS-1$ //$NON-NLS-2$
			script.println("/>"); //$NON-NLS-1$
			script.printTab();

			for (BundleDescription plugin : plugins) {
				script.printTab();
				script.print("\t<iu "); //$NON-NLS-1$
				script.printAttribute(ID, plugin.getSymbolicName(), true);
				script.printAttribute(VERSION, plugin.getVersion().toString(), true);
				script.println("/>"); //$NON-NLS-1$
			}

			for (BuildTimeFeature feature : features) {
				script.printTab();
				script.print("\t<iu"); //$NON-NLS-1$
				script.printAttribute(ID, getFeatureGroupId(feature), true);
				script.printAttribute(VERSION, feature.getVersion(), true);
				script.println("/>"); //$NON-NLS-1$
			}

			for (BuildTimeFeature rootProvider : rootFileProviders) {
				if (!(havePDEUIState() && rootProvider.getId().equals("org.eclipse.pde.container.feature"))) { //$NON-NLS-1$
					script.printTab();
					script.print("\t<iu"); //$NON-NLS-1$
					script.printAttribute(ID, getFeatureGroupId(rootProvider), true);
					script.printAttribute(VERSION, rootProvider.getVersion(), true);
					script.println("/>"); //$NON-NLS-1$
				}
			}
			if (productFile != null) {
				script.printTab();
				script.print("\t<iu"); //$NON-NLS-1$
				script.printAttribute(ID, productFile.getId(), true);
				script.printAttribute(VERSION, getReplacedProductVersion(), true);
				script.println("/>"); //$NON-NLS-1$
			}
			//categories
			script.printTab();
			script.println("<iu query=\"property[@name='org.eclipse.equinox.p2.type.category']\" required=\"false\" />"); //$NON-NLS-1$
			script.println("</p2.mirror>"); //$NON-NLS-1$
		}
		script.printTargetEnd();
		script.println();
	}

	protected String getFeatureGroupId(BuildTimeFeature feature) {
		if (!feature.isBinary()) {
			Properties properties = getFeatureBuildProperties(feature);
			if (properties.containsKey(PROPERTY_P2_GROUP_ID))
				return properties.getProperty(PROPERTY_P2_GROUP_ID);
		}
		return feature.getId() + ".feature.group"; //$NON-NLS-1$
	}

	protected String getReplacedProductVersion() {
		String productVersion = productFile.getVersion();
		if (productVersion.endsWith(PROPERTY_QUALIFIER)) {
			Version version = new Version(productVersion);
			StringBuffer buffer = new StringBuffer();
			buffer.append(version.getMajor());
			buffer.append('.');
			buffer.append(version.getMinor());
			buffer.append('.');
			buffer.append(version.getMicro());
			buffer.append('.');
			buffer.append(Utils.getPropertyFormat(PROPERTY_P2_PRODUCT_QUALIFIER));

			productVersion = buffer.toString();
		}
		return productVersion;
	}

	protected void generateDirectorTarget(boolean assembling) {
		if (assembling) {
			script.printTargetDeclaration(TARGET_RUN_DIRECTOR_CONDITION, null, null, null, null);
			script.printTab();
			script.print("<condition"); //$NON-NLS-1$
			script.printAttribute("property", TARGET_RUN_DIRECTOR_CONDITION, true); //$NON-NLS-1$
			script.printAttribute("value", TRUE, true); //$NON-NLS-1$
			script.println(">"); //$NON-NLS-1$
			script.println("\t<or>"); //$NON-NLS-1$
			script.println("\t\t<isset property=\"" + PROPERTY_RUN_PACKAGER + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
			script.println("\t\t<isset property=\"" + PROPERTY_SKIP_DIRECTOR + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
			script.println("\t</or>"); //$NON-NLS-1$
			script.printEndTag("condition"); //$NON-NLS-1$
			script.printTargetEnd();
		}

		script.printTargetDeclaration(TARGET_RUN_DIRECTOR, assembling ? TARGET_RUN_DIRECTOR_CONDITION : null, null, assembling ? TARGET_RUN_DIRECTOR_CONDITION : PROPERTY_SKIP_DIRECTOR, null);
		Map<String, String> parameters = new HashMap<>();
		parameters.put(PROPERTY_OS, Utils.getPropertyFormat(PROPERTY_OS));
		parameters.put(PROPERTY_WS, Utils.getPropertyFormat(PROPERTY_WS));
		parameters.put(PROPERTY_ARCH, Utils.getPropertyFormat(PROPERTY_ARCH));
		parameters.put(PROPERTY_P2_REPO, Utils.getPropertyFormat(PROPERTY_P2_BUILD_REPO));
		parameters.put(PROPERTY_P2_DIRECTOR_IU, productFile != null ? productFile.getId() : Utils.getPropertyFormat(PROPERTY_P2_ROOT_NAME));
		parameters.put(PROPERTY_P2_DIRECTOR_VERSION, productFile != null ? getReplacedProductVersion() : Utils.getPropertyFormat(PROPERTY_P2_ROOT_VERSION));
		parameters.put(PROPERTY_P2_DIRECTOR_INSTALLPATH, Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE));
		script.printAntTask(Utils.getPropertyFormat(PROPERTY_GENERIC_TARGETS), null, TARGET_RUN_DIRECTOR, null, TRUE, parameters);
		script.println();
		script.printTargetEnd();
		script.println();
	}

	private void generateProcessingCalls() {
		script.printAntCallTask(TARGET_JAR_PROCESSING, true, null);
		script.println();
	}

	private void generateArchivingCalls() {
		script.printAntCallTask(TARGET_ASSEMBLE_ARCHIVE, true, null);
	}

	protected void generateMainBegin() {
		script.printTargetDeclaration(TARGET_MAIN, null, null, null, null);
	}

	protected void generateMainEnd() {
		script.printAntCallTask(TARGET_CLEANUP_ASSEMBLY, true, null);
		script.printTargetEnd();
		script.println();
	}

	protected void generateCleanupAssembly(boolean assembling) {
		String condition = (assembling && BuildDirector.p2Gathering) ? PROPERTY_RUN_PACKAGER : null;
		script.printTargetDeclaration(TARGET_CLEANUP_ASSEMBLY, null, null, condition, null);
		if (!FORMAT_FOLDER.equalsIgnoreCase(archiveFormat))
			script.printDeleteTask(Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP), null, null);
		script.printTargetEnd();
		script.println();
	}

	protected void generateBrandingCalls() {
		script.printBrandTask(rootFolder, computeIconsList(), Utils.getPropertyFormat(PROPERTY_LAUNCHER_NAME), Utils.getPropertyFormat(PROPERTY_OS));
	}

	private void generateP2Steps() {
		if (!haveP2Bundles())
			return;
		if (rootFileProviders.size() == 0 && features.length == 0 && plugins.length == 0)
			return;
		script.printAntCallTask(TARGET_P2_METADATA, true, null);
		script.println();
	}

	protected void generateArchivingTarget(boolean assembling) {
		boolean condition = assembling && BuildDirector.p2Gathering;
		if (condition) {
			script.printTargetDeclaration(TARGET_ASSEMBLE_ARCHIVE_CONDITION, null, null, null, null);
			script.printTab();
			script.print("<condition"); //$NON-NLS-1$
			script.printAttribute("property", TARGET_ASSEMBLE_ARCHIVE_CONDITION, true); //$NON-NLS-1$
			script.printAttribute("value", TRUE, true); //$NON-NLS-1$
			script.println(">"); //$NON-NLS-1$
			script.println("\t<or>"); //$NON-NLS-1$
			script.println("\t\t<isset property=\"" + PROPERTY_RUN_PACKAGER + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
			if (productFile != null)
				script.println("\t\t<isset property=\"" + PROPERTY_SKIP_DIRECTOR + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
			else
				script.println("\t\t<isset property=\"" + PROPERTY_SKIP_MIRRORING + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
			script.println("\t</or>"); //$NON-NLS-1$
			script.printEndTag("condition"); //$NON-NLS-1$
			script.printTargetEnd();
		}

		// Only run the archive target if there is something there to archive.
		// if not p2Gathering, no conditions,
		// else, if assembling condition is (runPackager || skipDirector), if packaging condition is skipDirector
		String assemblyCondition = BuildDirector.p2Gathering ? TARGET_ASSEMBLE_ARCHIVE_CONDITION : null;
		String packageCondition = BuildDirector.p2Gathering ? (productFile != null ? PROPERTY_SKIP_DIRECTOR : PROPERTY_SKIP_MIRRORING) : null;
		script.printTargetDeclaration(TARGET_ASSEMBLE_ARCHIVE, condition ? TARGET_ASSEMBLE_ARCHIVE_CONDITION : null, null, assembling ? assemblyCondition : packageCondition, null);
		Map<String, String> properties = new HashMap<>();
		properties.put(PROPERTY_ROOT_FOLDER, rootFolder);
		printCustomAssemblyAntCall(PROPERTY_PRE + "archive", properties); //$NON-NLS-1$

		if (FORMAT_FOLDER.equalsIgnoreCase(archiveFormat)) {
			generateMoveRootFiles();
		} else if (FORMAT_ZIP.equalsIgnoreCase(archiveFormat)) {
			generateZipTarget();
		} else if (FORMAT_ANTZIP.equalsIgnoreCase(archiveFormat)) {
			generateAntZipTarget();
		} else if (FORMAT_ANTTAR.equalsIgnoreCase(archiveFormat)) {
			generateAntTarTarget();
		} else if (FORMAT_TAR.equalsIgnoreCase(archiveFormat)) {
			generateTarGZTasks(true);
		}
		script.printTargetEnd();
		script.println();
	}

	private void generateMoveRootFiles() {
		if (rootFileProviders.size() == 0 || BuildDirector.p2Gathering)
			return;

		for (BuildTimeFeature rootFileProvider : rootFileProviders) {
			Properties featureProperties = getFeatureBuildProperties(rootFileProvider);
			Utils.generatePermissions(featureProperties, configInfo, PROPERTY_ECLIPSE_BASE, script);
		}

		if (Platform.getOS().equals("win32")) { //$NON-NLS-1$
			FileSet[] rootFiles = new FileSet[1];
			rootFiles[0] = new FileSet(rootFolder, null, "**/**", null, null, null, null); //$NON-NLS-1$
			script.printMoveTask(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), rootFiles, false);
			script.printDeleteTask(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + configInfo.toStringReplacingAny(".", ANY_STRING), null, null); //$NON-NLS-1$
		} else {
			List<String> params = new ArrayList<>(3);
			params.add("-R"); //$NON-NLS-1$
			params.add("."); //$NON-NLS-1$
			params.add('\'' + Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '\'');
			String rootFileFolder = Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + configInfo.toStringReplacingAny(".", ANY_STRING); //$NON-NLS-1$
			script.printExecTask("cp", rootFileFolder + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER), params, null); //$NON-NLS-1$
			script.printDeleteTask(rootFileFolder, null, null);
		}
	}

	protected Properties getFeatureBuildProperties(BuildTimeFeature feature) {
		if (feature.isBinary())
			return null;
		try {
			return AbstractScriptGenerator.readProperties(IPath.fromOSString(feature.getRootLocation()).toOSString(), PROPERTIES_FILE, IStatus.OK);
		} catch (CoreException e) {
			return null;
		}

	}

	protected void generateGatherSourceTarget() {
		script.printTargetDeclaration(TARGET_GATHER_SOURCES, null, null, null, null);
		Map<String, String> properties = new HashMap<>(1);
		properties.put(PROPERTY_DESTINATION_TEMP_FOLDER, Utils.getPropertyFormat(PROPERTY_ECLIPSE_PLUGINS));

		for (BundleDescription plugin : plugins) {
			String placeToGather = getLocation(plugin);

			script.printAntTask(DEFAULT_BUILD_SCRIPT_FILENAME, Utils.makeRelative(IPath.fromOSString(placeToGather), IPath.fromOSString(workingDirectory)).toOSString(), TARGET_GATHER_SOURCES, null, null, properties);

			Properties bundleProperties = (Properties) plugin.getUserObject();
			//Source code for plugins with . on the classpath must be put in a folder in the final jar.
			if (bundleProperties.get(WITH_DOT) == Boolean.TRUE) {
				String targetLocation = Utils.getPropertyFormat(PROPERTY_ECLIPSE_PLUGINS) + '/' + ModelBuildScriptGenerator.getNormalizedName(plugin);
				String targetLocationSrc = targetLocation + "/src"; //$NON-NLS-1$

				//Find the source zip where it has been gathered and extract it in a folder  
				script.println("<unzip dest=\"" + AntScript.getEscaped(targetLocationSrc) + "\">"); //$NON-NLS-1$//$NON-NLS-2$
				script.println("\t<fileset dir=\"" + AntScript.getEscaped(targetLocation) + "\" includes=\"**/*src.zip\" casesensitive=\"false\"/>"); //$NON-NLS-1$//$NON-NLS-2$
				script.println("</unzip>"); //$NON-NLS-1$

				//	Delete the source zip where it has been gathered since we extracted it
				script.printDeleteTask(null, null, new FileSet[] {new FileSet(targetLocation, null, "**/*src.zip", null, null, null, "false")}); //$NON-NLS-1$ //$NON-NLS-2$//$NON-bNLS-3$
			}
		}

		properties = new HashMap<>(1);
		properties.put(PROPERTY_FEATURE_BASE, Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE));
		for (BuildTimeFeature feature : features) {
			String placeToGather = feature.getRootLocation();
			script.printAntTask(DEFAULT_BUILD_SCRIPT_FILENAME, Utils.makeRelative(IPath.fromOSString(placeToGather), IPath.fromOSString(workingDirectory)).toOSString(), TARGET_GATHER_SOURCES, null, null, properties);
		}

		script.printTargetEnd();
		script.println();
	}

	protected void generatePackagingTargets() {
		String fileName = Utils.getPropertyFormat(PROPERTY_SOURCE) + '/' + Utils.getPropertyFormat(PROPERTY_ELEMENT_NAME);
		String fileExists = Utils.getPropertyFormat(PROPERTY_SOURCE) + '/' + Utils.getPropertyFormat(PROPERTY_ELEMENT_NAME) + "_exists"; //$NON-NLS-1$

		script.printComment("Beginning of the jarUp task"); //$NON-NLS-1$
		script.printTargetDeclaration(TARGET_JARUP, null, null, null, Messages.assemble_jarUp);
		script.printAvailableTask(fileExists, fileName);
		Map<String, String> params = new HashMap<>(2);
		params.put(PROPERTY_SOURCE, Utils.getPropertyFormat(PROPERTY_SOURCE));
		params.put(PROPERTY_ELEMENT_NAME, Utils.getPropertyFormat(PROPERTY_ELEMENT_NAME));
		script.printAvailableTask(PROPERTY_JARING_MANIFEST, fileName + '/' + JarFile.MANIFEST_NAME);
		script.printConditionIsSet(PROPERTY_JARING_TASK, TARGET_JARING, PROPERTY_JARING_MANIFEST, TARGET_JARING + "_NoManifest"); //$NON-NLS-1$
		script.printAntCallTask(Utils.getPropertyFormat(PROPERTY_JARING_TASK), true, params);
		script.printTargetEnd();
		script.println();

		script.printTargetDeclaration(TARGET_JARING, null, fileExists, null, null);
		script.printJarTask(fileName + ".jar", fileName, fileName + '/' + JarFile.MANIFEST_NAME, "skip"); //$NON-NLS-1$ //$NON-NLS-2$
		script.printDeleteTask(fileName, null, null);
		script.printTargetEnd();
		script.println();

		script.printTargetDeclaration(TARGET_JARING + "_NoManifest", null, fileExists, null, null); //$NON-NLS-1$
		script.printJarTask(fileName + ".jar", fileName, null, "merge"); //$NON-NLS-1$ //$NON-NLS-2$
		script.printDeleteTask(fileName, null, null);
		script.printTargetEnd();
		script.printComment("End of the jarUp task"); //$NON-NLS-1$
		script.println();

		script.printComment("Beginning of the jar signing  target"); //$NON-NLS-1$
		script.printTargetDeclaration(TARGET_JARSIGNING, null, null, null, Messages.sign_Jar);
		printCustomAssemblyAntCall(PROPERTY_PRE + TARGET_JARSIGNING, null);
		if (generateJnlp)
			script.printProperty(PROPERTY_UNSIGN, "true"); //$NON-NLS-1$
		script.println("<eclipse.jarProcessor sign=\"" + Utils.getPropertyFormat(PROPERTY_SIGN) + "\" unsign=\"" + Utils.getPropertyFormat(PROPERTY_UNSIGN) + "\" jar=\"" + fileName + ".jar" + "\" alias=\"" + Utils.getPropertyFormat(PROPERTY_SIGN_ALIAS) + "\" keystore=\"" + Utils.getPropertyFormat(PROPERTY_SIGN_KEYSTORE) + "\" storepass=\"" + Utils.getPropertyFormat(PROPERTY_SIGN_STOREPASS) + "\" keypass=\"" + Utils.getPropertyFormat(PROPERTY_SIGN_KEYPASS) + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ 
		script.printTargetEnd();
		script.printComment("End of the jarUp task"); //$NON-NLS-1$
		script.println();
	}

	protected void generateGZipTarget(boolean assembling) {
		//during the assemble stage, only zip if we aren't running the packager
		script.printTargetDeclaration(TARGET_GZIP_RESULTS, null, null, assembling ? PROPERTY_RUN_PACKAGER : null, null);
		script.println("<move file=\"" //$NON-NLS-1$
				+ Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH) + "\" tofile=\"" //$NON-NLS-1$
				+ Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP) + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER) + "/tmp.tar\"/>"); //$NON-NLS-1$
		script.printGZip(Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP) + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER) + "/tmp.tar", //$NON-NLS-1$ 
				Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH));
		script.printTargetEnd();
	}

	protected void generatePrologue() {
		script.printProjectDeclaration("Assemble " + featureId, TARGET_MAIN, null); //$NON-NLS-1$  
		script.printProperty(PROPERTY_ARCHIVE_NAME, computeArchiveName());
		script.printProperty(PROPERTY_OS, configInfo.getOs());
		script.printProperty(PROPERTY_WS, configInfo.getWs());
		script.printProperty(PROPERTY_ARCH, configInfo.getArch());
		script.printProperty(PROPERTY_SIGN, (signJars ? Boolean.TRUE : Boolean.FALSE).toString());
		script.printProperty(PROPERTY_ASSEMBLY_TMP, Utils.getPropertyFormat(PROPERTY_BUILD_DIRECTORY) + "/tmp"); //$NON-NLS-1$
		script.printProperty(PROPERTY_ECLIPSE_BASE, Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP) + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER));
		script.printProperty(PROPERTY_ECLIPSE_PLUGINS, Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + DEFAULT_PLUGIN_LOCATION);
		script.printProperty(PROPERTY_ECLIPSE_FEATURES, Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + DEFAULT_FEATURE_LOCATION);
		script.printProperty(PROPERTY_ARCHIVE_FULLPATH, Utils.getPropertyFormat(PROPERTY_BASEDIR) + '/' + Utils.getPropertyFormat(PROPERTY_BUILD_LABEL) + '/' + Utils.getPropertyFormat(PROPERTY_ARCHIVE_NAME));
		printLauncherJarProperty();
		script.printProperty(PROPERTY_P2_BUILD_REPO, "file:" + Utils.getPropertyFormat(PROPERTY_BUILD_DIRECTORY) + "/buildRepo"); //$NON-NLS-1$ //$NON-NLS-2$
		script.printProperty(PROPERTY_GENERIC_TARGETS, Utils.getPropertyFormat("eclipse.pdebuild.scripts") + '/' + "/genericTargets.xml"); //$NON-NLS-1$//$NON-NLS-2$
		script.printAvailableTask(PROPERTY_CUSTOM_ASSEMBLY, "${builder}/customAssembly.xml", "${builder}/customAssembly.xml"); //$NON-NLS-1$ //$NON-NLS-2$
		if (productQualifier != null)
			script.printProperty(PROPERTY_P2_PRODUCT_QUALIFIER, productQualifier);

		if (productFile != null && productFile.getLauncherName() != null)
			script.printProperty(PROPERTY_LAUNCHER_NAME, productFile.getLauncherName());
		script.printProperty(PROPERTY_TAR_ARGS, ""); //$NON-NLS-1$
		script.println();

		generateCustomGatherMacro();
		generatePackagingTargets();
	}

	protected void printLauncherJarProperty() {
		Bundle launcherBundle = Platform.getBundle(BUNDLE_EQUINOX_LAUNCHER);
		Optional<File> bundleFile = FileLocator.getBundleFileLocation(launcherBundle);
		if (bundleFile.isPresent()) {
			script.printProperty(PROPERTY_LAUNCHER_JAR, bundleFile.get().getAbsolutePath());
		}
	}

	protected void generateCustomGatherMacro() {
		List<String> attributes = new ArrayList<>(5);
		attributes.add("dir"); //$NON-NLS-1$
		attributes.add("propertyName"); //$NON-NLS-1$
		attributes.add("propertyValue"); //$NON-NLS-1$
		attributes.add("subFolder"); //$NON-NLS-1$
		attributes.add(PROPERTY_PROJECT_NAME);
		script.printMacroDef(PROPERTY_CUSTOM_GATHER, attributes);

		Map<String, String> params = new HashMap<>();
		params.put("@{propertyName}", "@{propertyValue}"); //$NON-NLS-1$//$NON-NLS-2$
		script.printAntTask(DEFAULT_BUILD_SCRIPT_FILENAME, "@{dir}", TARGET_GATHER_BIN_PARTS, null, null, params); //$NON-NLS-1$

		params.put(PROPERTY_PROJECT_LOCATION, "${basedir}/@{dir}"); //$NON-NLS-1$
		params.put(PROPERTY_PROJECT_NAME, "@{projectName}"); //$NON-NLS-1$
		params.put(PROPERTY_TARGET_FOLDER, "@{propertyValue}@{subFolder}"); //$NON-NLS-1$
		printCustomAssemblyAntCall(TARGET_GATHER_BIN_PARTS, params);

		script.printEndMacroDef();
		script.println();
	}

	protected void printCustomGatherCall(String fullName, String dir, String propertyName, String propertyValue, String subFolder) {
		script.println("<" + PROPERTY_CUSTOM_GATHER + " dir=\"" + dir + "\" projectName=\"" + fullName + "\" propertyName=\"" + propertyName + "\" propertyValue=\"" + propertyValue + "\" subFolder=\"" + (subFolder != null ? subFolder : "") + "\" />"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
	}

	private void generateInitializationSteps() {
		if (BundleHelper.getDefault().isDebugging()) {
			script.printEchoTask("basedir : " + Utils.getPropertyFormat(PROPERTY_BASEDIR)); //$NON-NLS-1$
			script.printEchoTask("assemblyTempDir : " + Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP)); //$NON-NLS-1$
			script.printEchoTask("eclipse.base : " + Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE)); //$NON-NLS-1$
			script.printEchoTask("collectingFolder : " + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER)); //$NON-NLS-1$
			script.printEchoTask("archivePrefix : " + Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX)); //$NON-NLS-1$
		}

		script.println("<condition property=\"" + PROPERTY_PLUGIN_ARCHIVE_PREFIX + "\" value=\"" + DEFAULT_PLUGIN_LOCATION + "\">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		script.println("\t<equals arg1=\"" + Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + "\"  arg2=\"\" trim=\"true\"/>"); //$NON-NLS-1$ //$NON-NLS-2$ 
		script.println("</condition>"); //$NON-NLS-1$
		script.printProperty(PROPERTY_PLUGIN_ARCHIVE_PREFIX, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + '/' + DEFAULT_PLUGIN_LOCATION);

		script.println();
		script.println("<condition property=\"" + PROPERTY_FEATURE_ARCHIVE_PREFIX + "\" value=\"" + DEFAULT_FEATURE_LOCATION + "\">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		script.println("\t<equals arg1=\"" + Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + "\"  arg2=\"\" trim=\"true\"/>"); //$NON-NLS-1$ //$NON-NLS-2$ 
		script.println("</condition>"); //$NON-NLS-1$
		script.printProperty(PROPERTY_FEATURE_ARCHIVE_PREFIX, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + '/' + DEFAULT_FEATURE_LOCATION);

		script.println();

		script.printDirName(PROPERTY_ARCHIVE_PARENT, Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH));
		script.printMkdirTask(Utils.getPropertyFormat(PROPERTY_ARCHIVE_PARENT));
		script.printMkdirTask(Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP));
		script.printMkdirTask(Utils.getPropertyFormat(PROPERTY_BUILD_LABEL));
		script.println();
	}

	protected void generatePostProcessingTarget() {
		script.printTargetDeclaration(TARGET_JAR_PROCESSING, null, null, null, null);
		for (BundleDescription plugin : plugins) {
			generatePostProcessingSteps(plugin.getSymbolicName(), plugin.getVersion().toString(), (String) shapeAdvisor.getFinalShape(plugin)[1], BUNDLE_TYPE);
		}

		for (BuildTimeFeature feature : features) {
			generatePostProcessingSteps(feature.getId(), feature.getVersion(), (String) shapeAdvisor.getFinalShape(feature)[1], FEATURE_TYPE);
		}
		printCustomAssemblyAntCall(PROPERTY_POST + TARGET_JARUP, null);
		script.printTargetEnd();
		script.println();
	}

	protected void generateGatherBinPartsTarget() {
		script.printTargetDeclaration(TARGET_GATHER_BIN_PARTS, null, null, null, null);
		for (BundleDescription plugin : plugins) {
			String placeToGather = getLocation(plugin);
			printCustomGatherCall(ModelBuildScriptGenerator.getNormalizedName(plugin), Utils.makeRelative(IPath.fromOSString(placeToGather), IPath.fromOSString(workingDirectory)).toOSString(), PROPERTY_DESTINATION_TEMP_FOLDER, Utils.getPropertyFormat(PROPERTY_ECLIPSE_PLUGINS), null);
		}

		Set<BuildTimeFeature> featureSet = new HashSet<>();
		for (BuildTimeFeature feature : features) {
			String placeToGather = feature.getRootLocation();
			String featureFullName = feature.getId() + "_" + feature.getVersion(); //$NON-NLS-1$
			printCustomGatherCall(featureFullName, Utils.makeRelative(IPath.fromOSString(placeToGather), IPath.fromOSString(workingDirectory)).toOSString(), PROPERTY_FEATURE_BASE, Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), '/' + DEFAULT_FEATURE_LOCATION);
			featureSet.add(feature);
		}

		//This will generate gather.bin.parts call to features that provides files for the root
		for (BuildTimeFeature feature : rootFileProviders) {
			if (featureSet.contains(feature))
				continue;
			String placeToGather = feature.getRootLocation();
			String featureFullName = feature.getId() + "_" + feature.getVersion(); //$NON-NLS-1$
			printCustomGatherCall(featureFullName, Utils.makeRelative(IPath.fromOSString(placeToGather), IPath.fromOSString(workingDirectory)).toOSString(), PROPERTY_FEATURE_BASE, Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), '/' + DEFAULT_FEATURE_LOCATION);
		}
		script.printTargetEnd();
		script.println();
	}

	private void generateSignJarCall(String name, String version, byte type) {
		if (!signJars)
			return;
		Map<String, String> properties = new HashMap<>(2);
		properties.put(PROPERTY_SOURCE, type == BUNDLE_TYPE ? Utils.getPropertyFormat(PROPERTY_ECLIPSE_PLUGINS) : Utils.getPropertyFormat(PROPERTY_ECLIPSE_FEATURES));
		properties.put(PROPERTY_ELEMENT_NAME, name + '_' + version);
		script.printAntCallTask(TARGET_JARSIGNING, true, properties);
	}

	//generate the appropriate postProcessingCall
	private void generatePostProcessingSteps(String name, String version, String style, byte type) {
		if (ShapeAdvisor.FOLDER.equalsIgnoreCase(style))
			return;
		if (ShapeAdvisor.FILE.equalsIgnoreCase(style)) {
			generateJarUpCall(name, version, type);
			generateSignJarCall(name, version, type);
			generateJNLPCall(name, version, type);
			return;
		}
	}

	private void generateJNLPCall(String name, String version, byte type) {
		if (generateJnlp == false)
			return;
		if (type != FEATURE_TYPE)
			return;

		String dir = type == BUNDLE_TYPE ? Utils.getPropertyFormat(PROPERTY_ECLIPSE_PLUGINS) : Utils.getPropertyFormat(PROPERTY_ECLIPSE_FEATURES);
		String location = dir + '/' + name + '_' + version + ".jar"; //$NON-NLS-1$
		script.println("<eclipse.jnlpGenerator feature=\"" + AntScript.getEscaped(location) + "\"  codebase=\"" + Utils.getPropertyFormat(PROPERTY_JNLP_CODEBASE) + "\" j2se=\"" + Utils.getPropertyFormat(PROPERTY_JNLP_J2SE) + "\" locale=\"" + Utils.getPropertyFormat(PROPERTY_JNLP_LOCALE) + "\" generateOfflineAllowed=\"" + Utils.getPropertyFormat(PROPERTY_JNLP_GENOFFLINE) + "\" configInfo=\"" + Utils.getPropertyFormat(PROPERTY_JNLP_CONFIGS) + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
	}

	private void generateJarUpCall(String name, String version, byte type) {
		Map<String, String> properties = new HashMap<>(2);
		properties.put(PROPERTY_SOURCE, type == BUNDLE_TYPE ? Utils.getPropertyFormat(PROPERTY_ECLIPSE_PLUGINS) : Utils.getPropertyFormat(PROPERTY_ECLIPSE_FEATURES));
		properties.put(PROPERTY_ELEMENT_NAME, name + '_' + version);
		script.printAntCallTask(TARGET_JARUP, true, properties);
	}

	private void generateEpilogue() {
		generateGatherBinPartsTarget();
		if (embeddedSource)
			generateGatherSourceTarget();
		generatePostProcessingTarget();
		generateArchivingTarget(true);
		generateCleanupAssembly(true);
		if (FORMAT_TAR.equalsIgnoreCase(archiveFormat))
			generateGZipTarget(true);

		generateCustomAssemblyTarget();
		generateMetadataTarget();
		generateDirectorTarget(true);
		if (BuildDirector.p2Gathering) {
			generateMirrorTask(true);
			generateMirrorProductTask();
		}

		script.printProjectEnd();
		script.close();
		script = null;
	}

	public String getTargetName() {
		String config = getTargetConfig();
		return DEFAULT_ASSEMBLE_NAME + '.' + getTargetElement() + (config.length() > 0 ? "." : "") + config; //$NON-NLS-1$ //$NON-NLS-2$
	}

	public String getTargetConfig() {
		return (configInfo.equals(Config.genericConfig()) ? "" : configInfo.toStringReplacingAny(".", ANY_STRING)); //$NON-NLS-1$ //$NON-NLS-2$
	}

	public String getTargetElement() {
		return (featureId.equals("") ? "" : featureId); //$NON-NLS-1$ //$NON-NLS-2$
	}

	protected void printCustomAssemblyAntCall(String customTarget, Map<String, String> properties) {
		Map<String, String> params = (properties != null) ? new HashMap<>(properties) : new HashMap<>(1);
		params.put(PROPERTY_CUSTOM_TARGET, customTarget);
		script.printAntCallTask(TARGET_CUSTOM_ASSEMBLY, true, params);
	}

	protected void generateCustomAssemblyTarget() {
		script.printTargetDeclaration(TARGET_CUSTOM_ASSEMBLY, null, PROPERTY_CUSTOM_ASSEMBLY, null, null);
		script.printAntTask(Utils.getPropertyFormat(PROPERTY_CUSTOM_ASSEMBLY), null, Utils.getPropertyFormat(PROPERTY_CUSTOM_TARGET), null, TRUE, null);
		script.printTargetEnd();
		script.println();
	}

	private void generateMetadataTarget() {
		if (haveP2Bundles()) {
			script.printTargetDeclaration(TARGET_P2_METADATA, null, TARGET_P2_METADATA, null, null);
			script.printProperty(PROPERTY_P2_APPEND, "true"); //$NON-NLS-1$
			script.printProperty(PROPERTY_P2_COMPRESS, "false"); //$NON-NLS-1$
			script.printProperty(PROPERTY_P2_METADATA_REPO_NAME, ""); //$NON-NLS-1$
			script.printProperty(PROPERTY_P2_ARTIFACT_REPO_NAME, ""); //$NON-NLS-1$

			if (havePDEUIState()) {
				//during feature export we need to override the "mode"
				printP2GenerationModeCondition();
			}
			if (!BuildDirector.p2Gathering) {
				script.printTab();
				script.print("<p2.generator "); //$NON-NLS-1$
				script.printAttribute("source", Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), true); //$NON-NLS-1$
				script.printAttribute("append", Utils.getPropertyFormat(PROPERTY_P2_APPEND), true); //$NON-NLS-1$
				script.printAttribute("flavor", Utils.getPropertyFormat(PROPERTY_P2_FLAVOR), true); //$NON-NLS-1$
				script.printAttribute("compress", Utils.getPropertyFormat(PROPERTY_P2_COMPRESS), true); //$NON-NLS-1$ 
				script.printAttribute("metadataRepository", Utils.getPropertyFormat(PROPERTY_P2_METADATA_REPO), true); //$NON-NLS-1$ 
				script.printAttribute("artifactRepository", Utils.getPropertyFormat(PROPERTY_P2_ARTIFACT_REPO), true); //$NON-NLS-1$ 
				script.printAttribute("metadataRepositoryName", Utils.getPropertyFormat(PROPERTY_P2_METADATA_REPO_NAME), true); //$NON-NLS-1$
				script.printAttribute("artifactRepositoryName", Utils.getPropertyFormat(PROPERTY_P2_ARTIFACT_REPO_NAME), true); //$NON-NLS-1$
				script.printAttribute("publishArtifacts", Utils.getPropertyFormat(PROPERTY_P2_PUBLISH_ARTIFACTS), true); //$NON-NLS-1$
				script.printAttribute("site", Utils.getPropertyFormat(PROPERTY_P2_CATEGORY_SITE), true); //$NON-NLS-1$
				script.printAttribute("siteVersion", Utils.getPropertyFormat(PROPERTY_P2_CATEGORY_VERSION), true); //$NON-NLS-1$
				script.printAttribute("p2OS", configInfo.getOs(), true); //$NON-NLS-1$
				if (!havePDEUIState() || rootFileProviders.size() > 0)
					script.printAttribute("mode", "incremental", true); //$NON-NLS-1$ //$NON-NLS-2$
				else
					script.printAttribute("mode", Utils.getPropertyFormat(PROPERTY_P2_GENERATION_MODE), true); //$NON-NLS-1$
				script.println("/>"); //$NON-NLS-1$
			}
			if (rootFileProviders.size() > 0) {
				if (productFile != null) {
					script.println();
					File modFile = productFile.getLocation();
					String modLocation = Utils.getPropertyFormat(PROPERTY_BUILD_DIRECTORY) + '/' + DEFAULT_FEATURE_LOCATION + '/' + CONTAINER_FEATURE + "/product/" + modFile.getName(); //$NON-NLS-1$
					script.printAvailableTask(PROPERTY_P2_PRODUCT_MOD, modLocation, modLocation);
					script.printProperty(PROPERTY_P2_PRODUCT_MOD, productFile.getLocation().getPath());
				}
				script.printTab();
				script.print("<p2.generator "); //$NON-NLS-1$
				script.printAttribute("config", rootFolder, true); //$NON-NLS-1$
				script.printAttribute("append", Utils.getPropertyFormat(PROPERTY_P2_APPEND), true); //$NON-NLS-1$ 
				script.printAttribute("flavor", Utils.getPropertyFormat(PROPERTY_P2_FLAVOR), true); //$NON-NLS-1$
				script.printAttribute("compress", Utils.getPropertyFormat(PROPERTY_P2_COMPRESS), true); //$NON-NLS-1$ 
				script.printAttribute("metadataRepository", Utils.getPropertyFormat(PROPERTY_P2_METADATA_REPO), true); //$NON-NLS-1$ 
				script.printAttribute("artifactRepository", Utils.getPropertyFormat(PROPERTY_P2_ARTIFACT_REPO), true); //$NON-NLS-1$ 
				script.printAttribute("metadataRepositoryName", Utils.getPropertyFormat(PROPERTY_P2_METADATA_REPO_NAME), true); //$NON-NLS-1$
				script.printAttribute("artifactRepositoryName", Utils.getPropertyFormat(PROPERTY_P2_ARTIFACT_REPO_NAME), true); //$NON-NLS-1$
				script.printAttribute("launcherConfig", configInfo.toString(), true); //$NON-NLS-1$
				script.printAttribute("p2OS", configInfo.getOs(), true); //$NON-NLS-1$
				script.printAttribute("publishArtifacts", Utils.getPropertyFormat(PROPERTY_P2_PUBLISH_ARTIFACTS), true); //$NON-NLS-1$ 
				if (!havePDEUIState())
					script.printAttribute("mode", "incremental", true); //$NON-NLS-1$ //$NON-NLS-2$
				else
					script.printAttribute("mode", Utils.getPropertyFormat(PROPERTY_P2_GENERATION_MODE), true); //$NON-NLS-1$
				if (productFile != null) {
					script.printAttribute("exe", rootFolder + '/' + Utils.getPropertyFormat(PROPERTY_LAUNCHER_NAME), true); //$NON-NLS-1$
					script.printAttribute("productFile", Utils.getPropertyFormat(PROPERTY_P2_PRODUCT_MOD), true); //$NON-NLS-1$
				} else {
					script.printAttribute("root", Utils.getPropertyFormat(PROPERTY_P2_ROOT_NAME), true); //$NON-NLS-1$
					script.printAttribute("rootVersion", Utils.getPropertyFormat(PROPERTY_P2_ROOT_VERSION), true); //$NON-NLS-1$
				}
				script.println("/>"); //$NON-NLS-1$
			}

			script.printTargetEnd();
			script.println();
		}
	}

	protected void printP2GenerationModeCondition() {
		// "final" if not running packager and we are overriding, else "incremental"
		script.print("<condition"); //$NON-NLS-1$
		script.printAttribute("property", PROPERTY_P2_GENERATION_MODE, true); //$NON-NLS-1$
		script.printAttribute("value", "final", true); //$NON-NLS-1$ //$NON-NLS-2$
		script.printAttribute("else", "incremental", false); //$NON-NLS-1$ //$NON-NLS-2$
		script.println(">"); //$NON-NLS-1$
		script.println("\t<and>"); //$NON-NLS-1$
		script.println("\t\t<not>"); //$NON-NLS-1$
		script.println("\t\t\t<isset property=\"runPackager\"/>"); //$NON-NLS-1$
		script.println("\t\t</not>"); //$NON-NLS-1$
		script.println("\t\t<isset property=\"" + PROPERTY_P2_FINAL_MODE_OVERRIDE + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
		script.println("\t</and>"); //$NON-NLS-1$
		script.printEndTag("condition"); //$NON-NLS-1$
	}

	public boolean haveP2Bundles() {
		if (p2Bundles != null)
			return p2Bundles.booleanValue();

		p2Bundles = Boolean.valueOf(loadP2Class());
		return p2Bundles.booleanValue();
	}

	private void generateZipTarget() {
		final int parameterSize = 15;
		List<String> parameters = new ArrayList<>(parameterSize + 1);

		if (BuildDirector.p2Gathering) {
			parameters.add(Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX));
			createZipExecCommand(parameters);
			return;
		}

		for (int i = 0; i < plugins.length; i++) {
			parameters.add(Utils.getPropertyFormat(PROPERTY_PLUGIN_ARCHIVE_PREFIX) + '/' + (String) shapeAdvisor.getFinalShape(plugins[i])[0]);
			if (i % parameterSize == 0) {
				createZipExecCommand(parameters);
				parameters.clear();
			}
		}
		if (!parameters.isEmpty()) {
			createZipExecCommand(parameters);
			parameters.clear();
		}

		if (!parameters.isEmpty()) {
			createZipExecCommand(parameters);
			parameters.clear();
		}

		for (int i = 0; i < features.length; i++) {
			parameters.add(Utils.getPropertyFormat(PROPERTY_FEATURE_ARCHIVE_PREFIX) + '/' + (String) shapeAdvisor.getFinalShape(features[i])[0]);
			if (i % parameterSize == 0) {
				createZipExecCommand(parameters);
				parameters.clear();
			}
		}
		if (!parameters.isEmpty()) {
			createZipExecCommand(parameters);
			parameters.clear();
		}

		createZipRootFileCommand();
	}

	/**
	 *  Zip the root files
	 */
	private void createZipRootFileCommand() {
		if (rootFileProviders.size() == 0)
			return;

		List<String> parameters = new ArrayList<>(1);
		parameters.add("-r -q ${zipargs} '" + Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH) + "' . "); //$NON-NLS-1$ //$NON-NLS-2$
		script.printExecTask("zip", Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + configInfo.toStringReplacingAny(".", ANY_STRING), parameters, null); //$NON-NLS-1$ //$NON-NLS-2$ 
	}

	private void createZipExecCommand(List<String> parameters) {
		parameters.add(0, "-r -q " + Utils.getPropertyFormat(PROPERTY_ZIP_ARGS) + " '" + Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH) + '\''); //$NON-NLS-1$ //$NON-NLS-2$
		script.printExecTask("zip", Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP), parameters, null); //$NON-NLS-1$ 
	}

	protected String computeArchiveName() {
		String extension = (FORMAT_TAR.equalsIgnoreCase(archiveFormat) || FORMAT_ANTTAR.equalsIgnoreCase(archiveFormat)) ? ".tar.gz" : ".zip"; //$NON-NLS-1$ //$NON-NLS-2$
		return featureId + "-" + Utils.getPropertyFormat(PROPERTY_BUILD_ID_PARAM) + (configInfo.equals(Config.genericConfig()) ? "" : ("-" + configInfo.toStringReplacingAny(".", ANY_STRING))) + extension; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
	}

	public void generateTarGZTasks(boolean assembling) {
		//This task only supports creation of archive with eclipse at the root 
		//Need to do the copy using cp because of the link
		List<String> parameters = new ArrayList<>(2);
		if (rootFileProviders.size() > 0) {
			parameters.add("-r '" + Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP) + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER) + '/' + configInfo.toStringReplacingAny(".", ANY_STRING) + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER) + "' '" + Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP) + '\''); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$  
			script.printExecTask("cp", Utils.getPropertyFormat(PROPERTY_BASEDIR), parameters, null); //$NON-NLS-1$

			parameters.clear();
			parameters.add("-rf '" + Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP) + '/' + Utils.getPropertyFormat(PROPERTY_COLLECTING_FOLDER) + '/' + configInfo.toStringReplacingAny(".", ANY_STRING) + '\''); //$NON-NLS-1$ //$NON-NLS-2$
			script.printExecTask("rm", Utils.getPropertyFormat(PROPERTY_BASEDIR), parameters, null); //$NON-NLS-1$
		}
		parameters.clear();
		String tarArgs = assembling ? "-cvf '" : "-rvf '"; //$NON-NLS-1$//$NON-NLS-2$
		parameters.add(Utils.getPropertyFormat(PROPERTY_TAR_ARGS) + tarArgs + Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH) + "' " + ((BuildDirector.p2Gathering && productFile == null) ? "." : Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX)) + ' '); //$NON-NLS-1$ //$NON-NLS-2$
		String folder = (BuildDirector.p2Gathering && productFile == null) ? Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) : Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP);
		script.printExecTask("tar", folder, parameters, null); //$NON-NLS-1$ 

		script.printAntCallTask(TARGET_GZIP_RESULTS, true, null);

		List<String> args = new ArrayList<>(2);
		args.add("-rf"); //$NON-NLS-1$
		args.add('\'' + Utils.getPropertyFormat(PROPERTY_ASSEMBLY_TMP) + '\'');
		script.printExecTask("rm", null, args, null); //$NON-NLS-1$
	}

	//TODO this code and the generateAntTarTarget() should be refactored using a factory or something like that.
	protected void generateAntZipTarget() {
		List<FileSet> fileSets = new ArrayList<>();

		if (BuildDirector.p2Gathering) {
			//TODO permissions
			FileSet[] permissions = generatePermissions(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), true);
			String toExcludeFromArchive = Utils.getStringFromCollection(this.addedByPermissions, ","); //$NON-NLS-1$
			fileSets.add(new ZipFileSet(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), false, null, "**/**", null, toExcludeFromArchive, null, productFile != null ? Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) : null, null, null)); //$NON-NLS-1$
			fileSets.addAll(Arrays.asList(permissions));
		} else {
			for (BundleDescription plugin2 : plugins) {
				Object[] shape = shapeAdvisor.getFinalShape(plugin2);
				fileSets.add(new ZipFileSet(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + DEFAULT_PLUGIN_LOCATION + '/' + (String) shape[0], shape[1] == ShapeAdvisor.FILE, null, null, null, null, null, Utils.getPropertyFormat(PROPERTY_PLUGIN_ARCHIVE_PREFIX) + '/' + (String) shape[0], null, null));
			}

			for (BuildTimeFeature feature2 : features) {
				Object[] shape = shapeAdvisor.getFinalShape(feature2);
				fileSets.add(new ZipFileSet(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + DEFAULT_FEATURE_LOCATION + '/' + (String) shape[0], shape[1] == ShapeAdvisor.FILE, null, null, null, null, null, Utils.getPropertyFormat(PROPERTY_FEATURE_ARCHIVE_PREFIX) + '/' + (String) shape[0], null, null));
			}

			if (rootFileProviders.size() > 0) {
				if (groupConfigs) {
					List<Config> allConfigs = getConfigInfos();
					for (Config elt : allConfigs) {
						fileSets.add(new ZipFileSet(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + elt.toStringReplacingAny(".", ANY_STRING), false, null, "**/**", null, null, null, elt.toStringReplacingAny(".", ANY_STRING), null, null)); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
					}
				} else {
					FileSet[] permissions = generatePermissions(rootFolder, true);
					String toExcludeFromArchive = Utils.getStringFromCollection(this.addedByPermissions, ","); //$NON-NLS-1$
					fileSets.add(new ZipFileSet(rootFolder, false, null, "**/**", null, toExcludeFromArchive, null, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX), null, null)); //$NON-NLS-1$
					fileSets.addAll(Arrays.asList(permissions));
				}
			}
		}
		if (fileSets.size() > 0) {
			FileSet[] sets = fileSets.toArray(new FileSet[fileSets.size()]);
			script.printZipTask(Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH), null, false, true, sets);
		}
	}

	protected Collection<BuildTimeFeature> getArchiveRootFileProviders() {
		return rootFileProviders != null ? rootFileProviders : new ArrayList<>(0);
	}

	protected FileSet[] generatePermissions(String root, boolean zip) {
		String configInfix = configInfo.toString("."); //$NON-NLS-1$
		String prefixPermissions = ROOT_PREFIX + configInfix + '.' + PERMISSIONS + '.';
		String commonPermissions = ROOT_PREFIX + PERMISSIONS + '.';
		ArrayList<ZipFileSet> fileSets = new ArrayList<>();

		for (BuildTimeFeature buildTimeFeature : getArchiveRootFileProviders()) {
			Properties featureProperties = getFeatureBuildProperties(buildTimeFeature);
			for (Entry<Object, Object> permission : featureProperties.entrySet()) {
				String instruction = (String) permission.getKey();
				String parameters = (String) permission.getValue();
				String[] values = Utils.getArrayFromString(parameters);
				for (String value : values) {
					boolean isFile = !value.endsWith("/"); //$NON-NLS-1$
					if (instruction.startsWith(prefixPermissions)) {
						addedByPermissions.add(value);
						if (zip)
							fileSets.add(new ZipFileSet(root + (isFile ? '/' + value : ""), isFile, null, isFile ? null : value + "/**", null, null, null, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + (isFile ? '/' + value : ""), null, instruction.substring(prefixPermissions.length()))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
						else
							fileSets.add(new TarFileSet(root + (isFile ? '/' + value : ""), isFile, null, isFile ? null : value + "/**", null, null, null, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + (isFile ? '/' + value : ""), null, instruction.substring(prefixPermissions.length()))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
						continue;
					}
					if (instruction.startsWith(commonPermissions)) {
						addedByPermissions.add(value);
						if (zip)
							fileSets.add(new ZipFileSet(root + (isFile ? '/' + value : ""), isFile, null, isFile ? null : value + "/**", null, null, null, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + (isFile ? '/' + value : ""), null, instruction.substring(commonPermissions.length()))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
						else
							fileSets.add(new TarFileSet(root + (isFile ? '/' + value : ""), isFile, null, isFile ? null : value + "/**", null, null, null, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) + (isFile ? '/' + value : ""), null, instruction.substring(commonPermissions.length()))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
						continue;
					}
				}
			}
		}
		return fileSets.toArray(new FileSet[fileSets.size()]);
	}

	//TODO this code andn the generateAntZipTarget() should be refactored using a factory or something like that.
	private void generateAntTarTarget() {
		List<Object> fileSets = new ArrayList<>();

		if (BuildDirector.p2Gathering) {
			FileSet[] permissions = generatePermissions(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), false);
			String toExcludeFromArchive = Utils.getStringFromCollection(this.addedByPermissions, ","); //$NON-NLS-1$
			fileSets.add(new TarFileSet(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE), false, null, "**/**", null, toExcludeFromArchive, null, productFile != null ? Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX) : null, null, null)); //$NON-NLS-1$
			fileSets.addAll(Arrays.asList(permissions));
		} else {
			//FileSet[] filesPlugins = new FileSet[plugins.length];
			for (BundleDescription plugin2 : plugins) {
				Object[] shape = shapeAdvisor.getFinalShape(plugin2);
				fileSets.add(new TarFileSet(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + DEFAULT_PLUGIN_LOCATION + '/' + (String) shape[0], shape[1] == ShapeAdvisor.FILE, null, null, null, null, null, Utils.getPropertyFormat(PROPERTY_PLUGIN_ARCHIVE_PREFIX) + '/' + (String) shape[0], null, null));
			}

			for (BuildTimeFeature feature2 : features) {
				Object[] shape = shapeAdvisor.getFinalShape(feature2);
				fileSets.add(new TarFileSet(Utils.getPropertyFormat(PROPERTY_ECLIPSE_BASE) + '/' + DEFAULT_FEATURE_LOCATION + '/' + (String) shape[0], shape[1] == ShapeAdvisor.FILE, null, null, null, null, null, Utils.getPropertyFormat(PROPERTY_FEATURE_ARCHIVE_PREFIX) + '/' + (String) shape[0], null, null));
			}

			if (rootFileProviders.size() > 0) {
				FileSet[] permissionSets = generatePermissions(rootFolder, false);
				fileSets.add(new TarFileSet(rootFolder, false, null, "**/**", null, null, null, Utils.getPropertyFormat(PROPERTY_ARCHIVE_PREFIX), null, null)); //$NON-NLS-1$
				fileSets.add(Arrays.asList(permissionSets));
			}
		}
		if (fileSets.size() > 0) {
			FileSet[] sets = fileSets.toArray(new FileSet[fileSets.size()]);
			script.printTarTask(Utils.getPropertyFormat(PROPERTY_ARCHIVE_FULLPATH), null, false, true, sets);
		}
	}

	public void setGenerateJnlp(boolean value) {
		generateJnlp = value;
	}

	public void setSignJars(boolean value) {
		signJars = value;
	}

	public boolean isSigning() {
		return signJars;
	}

	public void setProduct(String value) {
		product = value;
	}

	public void setDirectory(String directory) {
		this.directory = directory;
	}

	public ProductFile getProductFile() {
		if (productFile == null && product != null) {
			try {
				// Note that we must pass the OS information in to load product
				// so that any icon files can be calculated for the 
				// generateBrandingCalls.
				productFile = loadProduct(product, configInfo != null ? configInfo.getOs() : null);
			} catch (CoreException e) {
				//ignore
			}
		}
		return productFile;
	}

	public void setArchiveFormat(String archiveFormat) {
		this.archiveFormat = archiveFormat;
	}

	public void setGroupConfigs(boolean group) {
		groupConfigs = group;
	}
}
