Bug 448101 - support fragments generation.

This is one giant hack and needs total rewrite, but it would be good if
the repo2runnable could write the .info file on the basis of metadata.

Change-Id: Iacb22ccdf9e72a9415bb845f1cf2944f59382f0d
Signed-off-by: Krzysztof Daniel <krzysztof.daniel@gmail.com>
[rgrunber@redhat.com: Use SimpleConfiguratiorManipulatorImpl for
fragment.info. Remove fragment.profile from p2 Droplets format.]
Signed-off-by: Roland Grunberg <rgrunber@redhat.com>
diff --git a/bundles/org.eclipse.equinox.p2.repository.tools/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.repository.tools/META-INF/MANIFEST.MF
index 41f4121..e7b1d37 100644
--- a/bundles/org.eclipse.equinox.p2.repository.tools/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.p2.repository.tools/META-INF/MANIFEST.MF
@@ -16,6 +16,7 @@
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Bundle-ActivationPolicy: lazy
 Import-Package: org.eclipse.equinox.app;version="1.0.0",
+ org.eclipse.equinox.frameworkadmin;version="2.0.0",
  org.eclipse.equinox.internal.p2.artifact.repository,
  org.eclipse.equinox.internal.p2.artifact.repository.simple,
  org.eclipse.equinox.internal.p2.core.helpers,
@@ -41,5 +42,6 @@
  org.eclipse.equinox.p2.repository.artifact;version="[2.3.0,3.0.0)",
  org.eclipse.equinox.p2.repository.artifact.spi;version="[2.0.0,3.0.0)",
  org.eclipse.equinox.p2.repository.metadata;version="[2.0.0,3.0.0)",
+ org.eclipse.equinox.simpleconfigurator.manipulator;version="2.0.0",
  org.eclipse.osgi.util;version="1.1.0",
  org.osgi.framework;version="1.4.0"
diff --git a/bundles/org.eclipse.equinox.p2.repository.tools/src/org/eclipse/equinox/p2/internal/repository/tools/Repo2Runnable.java b/bundles/org.eclipse.equinox.p2.repository.tools/src/org/eclipse/equinox/p2/internal/repository/tools/Repo2Runnable.java
index 91d6d42..87d0aaf 100644
--- a/bundles/org.eclipse.equinox.p2.repository.tools/src/org/eclipse/equinox/p2/internal/repository/tools/Repo2Runnable.java
+++ b/bundles/org.eclipse.equinox.p2.repository.tools/src/org/eclipse/equinox/p2/internal/repository/tools/Repo2Runnable.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2012 IBM Corporation and others.
+ * Copyright (c) 2009, 2013 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials 
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -8,27 +8,31 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Sonatype, Inc. - ongoing development
+ *     Red Hat, Inc. - fragment creation
  *******************************************************************************/
 package org.eclipse.equinox.p2.internal.repository.tools;
 
+import java.io.*;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.*;
 import org.eclipse.core.runtime.*;
 import org.eclipse.equinox.app.IApplication;
 import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.equinox.frameworkadmin.BundleInfo;
+import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
 import org.eclipse.equinox.internal.p2.engine.*;
 import org.eclipse.equinox.internal.p2.engine.phases.Collect;
 import org.eclipse.equinox.p2.core.IProvisioningAgent;
 import org.eclipse.equinox.p2.core.ProvisionException;
 import org.eclipse.equinox.p2.engine.*;
 import org.eclipse.equinox.p2.engine.spi.ProvisioningAction;
-import org.eclipse.equinox.p2.metadata.IArtifactKey;
-import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.*;
 import org.eclipse.equinox.p2.query.IQueryResult;
 import org.eclipse.equinox.p2.query.QueryUtil;
 import org.eclipse.equinox.p2.repository.artifact.*;
 import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
+import org.eclipse.equinox.simpleconfigurator.manipulator.SimpleConfiguratorManipulator;
 
 /**
  * The transformer takes an existing p2 repository (local or remote), iterates over 
@@ -44,6 +48,7 @@
 	private static final String PARM_OPERAND = "operand"; //$NON-NLS-1$
 	private static final String PARM_PROFILE = "profile"; //$NON-NLS-1$
 
+	private boolean createFragments;
 	private boolean flagAsRunnable = false;
 
 	protected class CollectNativesAction extends ProvisioningAction {
@@ -149,6 +154,44 @@
 
 			setRunnableProperty(destinationArtifactRepository);
 			// return the resulting status
+
+			if (createFragments) {
+				File parentDir = new File(destinationArtifactRepository.getLocation().toString().substring(5));
+				File pluginsDir = new File(parentDir, "plugins");
+				File fragmentInfo = new File(parentDir, "fragment.info");
+				HashSet<BundleInfo> bundles = new HashSet<BundleInfo>();
+				try {
+					for (Iterator<IInstallableUnit> iterator = processedIUs.iterator(); iterator.hasNext();) {
+						IInstallableUnit unit = iterator.next();
+						if (unit.getId().equals("a.jre"))
+							continue;
+						Collection<IProvidedCapability> providedCapabilities = unit.getProvidedCapabilities();
+						for (IProvidedCapability cap : providedCapabilities) {
+							if ("org.eclipse.equinox.p2.eclipse.type".equals(cap.getNamespace())) {
+								if ("bundle".equals(cap.getName())) {
+									File candidate = new File(pluginsDir, unit.getId() + "_" + unit.getVersion());
+									if (candidate.exists()) {
+										bundles.add(new BundleInfo(unit.getId(), unit.getVersion().toString(), candidate.toURI(), 4, false));
+									}
+									candidate = new File(pluginsDir, unit.getId() + "_" + unit.getVersion() + ".jar");
+									if (candidate.exists()) {
+										bundles.add(new BundleInfo(unit.getId(), unit.getVersion().toString(), candidate.toURI(), 4, false));
+									}
+									break;
+								}
+							}
+						}
+					}
+					SimpleConfiguratorManipulator simpleManipulator = (SimpleConfiguratorManipulator) ServiceHelper.getService(Activator.getBundleContext(), SimpleConfiguratorManipulator.class.getName());
+					simpleManipulator.saveConfiguration(bundles.toArray(new BundleInfo[0]), fragmentInfo, parentDir.toURI());
+				} catch (FileNotFoundException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				} catch (IOException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
 			return result;
 		} finally {
 			// cleanup by removing the temporary profile and unloading the repos which were new
@@ -157,6 +200,13 @@
 		}
 	}
 
+	static class Writer extends ProfileWriter {
+
+		public Writer(OutputStream output) throws IOException {
+			super(output, new ProcessingInstruction[] {ProcessingInstruction.makeTargetVersionInstruction(PROFILE_TARGET, ProfileXMLConstants.CURRENT_VERSION)});
+		}
+	}
+
 	private void setRunnableProperty(IArtifactRepository destinationArtifactRepository) {
 		if (flagAsRunnable)
 			destinationArtifactRepository.setProperty(IArtifactRepository.PROP_RUNNABLE, Boolean.TRUE.toString(), new NullProgressMonitor());
@@ -261,9 +311,10 @@
 			return;
 		for (int i = 0; i < args.length; i++) {
 			String option = args[i];
-			if (i == args.length - 1 || args[i + 1].startsWith("-")) //$NON-NLS-1$
-				continue;
-			String arg = args[++i];
+			String arg = null;
+			if (i != args.length - 1 && !args[i + 1].startsWith("-")) { //$NON-NLS-1$
+				arg = args[++i];
+			}
 
 			if (option.equalsIgnoreCase("-source")) { //$NON-NLS-1$
 				RepositoryDescriptor source = new RepositoryDescriptor();
@@ -280,6 +331,10 @@
 			if (option.equalsIgnoreCase("-flagAsRunnable")) { //$NON-NLS-1$
 				setFlagAsRunnable(true);
 			}
+
+			if (option.equalsIgnoreCase("-createFragments")) { //$NON-NLS-1$
+				setCreateFragments(true);
+			}
 		}
 	}
 
@@ -306,4 +361,9 @@
 	public void stop() {
 		// nothing to do
 	}
+
+	public void setCreateFragments(boolean createFragments) {
+		this.createFragments = createFragments;
+
+	}
 }
diff --git a/bundles/org.eclipse.equinox.p2.repository.tools/src_ant/org/eclipse/equinox/p2/internal/repository/tools/tasks/Repo2RunnableTask.java b/bundles/org.eclipse.equinox.p2.repository.tools/src_ant/org/eclipse/equinox/p2/internal/repository/tools/tasks/Repo2RunnableTask.java
index f782f62..492d99e 100644
--- a/bundles/org.eclipse.equinox.p2.repository.tools/src_ant/org/eclipse/equinox/p2/internal/repository/tools/tasks/Repo2RunnableTask.java
+++ b/bundles/org.eclipse.equinox.p2.repository.tools/src_ant/org/eclipse/equinox/p2/internal/repository/tools/tasks/Repo2RunnableTask.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2010 IBM Corporation and others.
+ * Copyright (c) 2009, 2013 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials 
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  * 
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Red Hat, Inc. - fragment creation
  *******************************************************************************/
 package org.eclipse.equinox.p2.internal.repository.tools.tasks;
 
@@ -33,6 +34,7 @@
 
 	private boolean failOnError = true;
 	private boolean flagAsRunnable = false;
+	private boolean createFragments = false;
 
 	/*
 	 * Constructor for the class. Create a new instance of the application
@@ -55,6 +57,7 @@
 				throw new BuildException(Messages.exception_needIUsOrNonEmptyRepo);
 			application.setSourceIUs(ius);
 			((Repo2Runnable) application).setFlagAsRunnable(flagAsRunnable);
+			((Repo2Runnable) application).setCreateFragments(createFragments);
 			IStatus result = application.run(null);
 			if (failOnError && result.matches(IStatus.ERROR))
 				throw new ProvisionException(result);
@@ -74,4 +77,8 @@
 	public void setFlagAsRunnable(boolean runnable) {
 		this.flagAsRunnable = runnable;
 	}
+
+	public void setCreateFragments(boolean fragments) {
+		this.createFragments = fragments;
+	}
 }
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ant/Repo2RunnableTaskTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ant/Repo2RunnableTaskTests.java
index 1bc3999..b94e5c3 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ant/Repo2RunnableTaskTests.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ant/Repo2RunnableTaskTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- *  Copyright (c) 2009, 2010 IBM Corporation and others.
+ *  Copyright (c) 2009, 2013 IBM Corporation and others.
  *  All rights reserved. This program and the accompanying materials
  *  are made available under the terms of the Eclipse Public License v1.0
  *  which accompanies this distribution, and is available at
@@ -7,10 +7,11 @@
  * 
  *  Contributors:
  *      IBM Corporation - initial API and implementation
+ *      Red Hat, Inc. - fragments support added.
  *******************************************************************************/
 package org.eclipse.equinox.p2.tests.ant;
 
-import java.io.File;
+import java.io.*;
 import java.net.URI;
 import java.util.Iterator;
 import org.eclipse.core.runtime.NullProgressMonitor;
@@ -53,6 +54,22 @@
 		assertTrue("Unexpected format", expectedFormat(destination));
 	}
 
+	public void testRepo2RunnableFragments() throws IOException {
+		createRepo2RunnableTaskElementFragments(TYPE_BOTH);
+
+		runAntTask();
+		assertEquals("Number of artifact keys differs", getArtifactKeyCount(source), getArtifactKeyCount(destination));
+		assertTrue("Unexpected format", expectedFormat(destination));
+		File f = new File(destination);
+		assertTrue("Missing content.jar", new File(f, "content.jar").exists());
+		assertTrue("Missing artifacts.jar", new File(f, "artifacts.jar").exists());
+		assertTrue("Missing fragment.info", new File(f, "fragment.info").exists());
+		BufferedReader br = new BufferedReader(new FileReader(new File(f, "fragment.info")));
+		while (br.ready())
+			System.out.println(br.readLine());
+		br.close();
+	}
+
 	/*
 	 * Test that when an IU is specified that it is used
 	 */
@@ -157,4 +174,20 @@
 
 		return task;
 	}
+
+	protected AntTaskElement createRepo2RunnableTaskElementFragments(String type) {
+		AntTaskElement task = createRepo2RunnableTaskElement();
+		task.addElement(getRepositoryElement(destination, type));
+
+		AntTaskElement sourceElement = new AntTaskElement("source");
+		sourceElement.addElement(getRepositoryElement(source, type));
+		task.addElement(sourceElement);
+
+		//		AntTaskElement fragmentsElement = new AntTaskElement("createFragments");
+		//		fragmentsElement.a
+		//		task.addElement(fragmentsElement);
+
+		task.addAttribute("createFragments", "true");
+		return task;
+	}
 }