diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/tmp/BundlesAction.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/tmp/BundlesAction.java
new file mode 100644
index 0000000000..9731a12b14
--- /dev/null
+++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/tmp/BundlesAction.java
@@ -0,0 +1,1177 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2021 Code 9 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:
+ * Code 9 - initial API and implementation
+ * IBM - ongoing development
+ * SAP AG - make optional dependencies non-greedy by default; allow setting greedy through directive (bug 247099)
+ * Red Hat Inc. - Bug 460967
+ * Christoph Läubrich - Bug 574952 p2 should distinguish between "product plugins" and "configuration plugins" (gently sponsored by Compart AG)
+ ******************************************************************************/
+package org.eclipse.tycho.p2maven.tmp;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.frameworkadmin.BundleInfo;
+import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
+import org.eclipse.equinox.internal.p2.publisher.Messages;
+import org.eclipse.equinox.internal.p2.publisher.eclipse.GeneratorBundleInfo;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.IInstallableUnitFragment;
+import org.eclipse.equinox.p2.metadata.IProvidedCapability;
+import org.eclipse.equinox.p2.metadata.IRequirement;
+import org.eclipse.equinox.p2.metadata.IUpdateDescriptor;
+import org.eclipse.equinox.p2.metadata.MetadataFactory;
+import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
+import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitFragmentDescription;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.VersionRange;
+import org.eclipse.equinox.p2.metadata.expression.IMatchExpression;
+import org.eclipse.equinox.p2.publisher.AbstractPublisherAction;
+import org.eclipse.equinox.p2.publisher.AdviceFileAdvice;
+import org.eclipse.equinox.p2.publisher.IPublisherInfo;
+import org.eclipse.equinox.p2.publisher.IPublisherResult;
+import org.eclipse.equinox.p2.publisher.actions.IAdditionalInstallableUnitAdvice;
+import org.eclipse.equinox.p2.publisher.actions.ICapabilityAdvice;
+import org.eclipse.equinox.p2.publisher.actions.IPropertyAdvice;
+import org.eclipse.equinox.p2.publisher.actions.ITouchpointAdvice;
+import org.eclipse.equinox.p2.publisher.eclipse.IBundleShapeAdvice;
+import org.eclipse.equinox.p2.query.IQueryResult;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
+import org.eclipse.equinox.spi.p2.publisher.LocalizationHelper;
+import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
+import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.GenericDescription;
+import org.eclipse.osgi.service.resolver.GenericSpecification;
+import org.eclipse.osgi.service.resolver.HostSpecification;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.StateObjectFactory;
+import org.eclipse.osgi.util.ManifestElement;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.pde.internal.publishing.Activator;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.resource.Namespace;
+
+/**
+ * Publish IUs for all of the bundles in a given set of locations or described
+ * by a set of bundle descriptions. The locations can be actual locations of the
+ * bundles or folders of bundles.
+ *
+ * This action consults the following types of advice:
+ *
+ * - {@link IAdditionalInstallableUnitAdvice }
+ * - {@link IBundleShapeAdvice}
+ * - {@link ICapabilityAdvice}
+ * - {@link IPropertyAdvice}
+ * - {@link ITouchpointAdvice}
+ *
+ */
+@SuppressWarnings("restriction")
+public class BundlesAction extends AbstractPublisherAction {
+
+ public static final String FILTER_PROPERTY_INSTALL_SOURCE = "org.eclipse.update.install.sources"; //$NON-NLS-1$
+
+ public static final String INSTALL_SOURCE_FILTER = String.format("(%s=true)", FILTER_PROPERTY_INSTALL_SOURCE); //$NON-NLS-1$
+
+ /**
+ * A suffix used to match a bundle IU to its source
+ */
+ public static final String SOURCE_SUFFIX = ".source"; //$NON-NLS-1$
+
+ /**
+ * A capability name in the {@link PublisherHelper#NAMESPACE_ECLIPSE_TYPE}
+ * namespace representing and OSGi bundle resource
+ *
+ * @see IProvidedCapability#getName()
+ */
+ public static final String TYPE_ECLIPSE_BUNDLE = "bundle"; //$NON-NLS-1$
+
+ /**
+ * A capability name in the {@link PublisherHelper#NAMESPACE_ECLIPSE_TYPE}
+ * namespace representing a source bundle
+ *
+ * @see IProvidedCapability#getName()
+ */
+ public static final String TYPE_ECLIPSE_SOURCE = "source"; //$NON-NLS-1$
+
+ public static final String OSGI_BUNDLE_CLASSIFIER = "osgi.bundle"; //$NON-NLS-1$
+ public static final String CAPABILITY_NS_OSGI_BUNDLE = "osgi.bundle"; //$NON-NLS-1$
+ public static final String CAPABILITY_NS_OSGI_FRAGMENT = "osgi.fragment"; //$NON-NLS-1$
+
+ public static final IProvidedCapability BUNDLE_CAPABILITY = MetadataFactory.createProvidedCapability(
+ PublisherHelper.NAMESPACE_ECLIPSE_TYPE, TYPE_ECLIPSE_BUNDLE, Version.createOSGi(1, 0, 0));
+ public static final IProvidedCapability SOURCE_BUNDLE_CAPABILITY = MetadataFactory.createProvidedCapability(
+ PublisherHelper.NAMESPACE_ECLIPSE_TYPE, TYPE_ECLIPSE_SOURCE, Version.createOSGi(1, 0, 0));
+
+ static final String DEFAULT_BUNDLE_LOCALIZATION = "OSGI-INF/l10n/bundle"; //$NON-NLS-1$
+
+ private static final String[] BUNDLE_IU_PROPERTY_MAP = { Constants.BUNDLE_NAME, IInstallableUnit.PROP_NAME,
+ Constants.BUNDLE_DESCRIPTION, IInstallableUnit.PROP_DESCRIPTION, Constants.BUNDLE_VENDOR,
+ IInstallableUnit.PROP_PROVIDER, Constants.BUNDLE_CONTACTADDRESS, IInstallableUnit.PROP_CONTACT,
+ Constants.BUNDLE_DOCURL, IInstallableUnit.PROP_DOC_URL, Constants.BUNDLE_UPDATELOCATION,
+ IInstallableUnit.PROP_BUNDLE_LOCALIZATION, Constants.BUNDLE_LOCALIZATION,
+ IInstallableUnit.PROP_BUNDLE_LOCALIZATION };
+ public static final int BUNDLE_LOCALIZATION_INDEX = PublisherHelper.BUNDLE_LOCALIZED_PROPERTIES.length - 1;
+ public static final String DIR = "dir"; //$NON-NLS-1$
+ public static final String JAR = "jar"; //$NON-NLS-1$
+ public static final String BUNDLE_SHAPE = "Eclipse-BundleShape"; //$NON-NLS-1$
+
+ /**
+ * Manifest header directive for specifying how optional runtime requirements
+ * shall be handled during installation.
+ *
+ * @see #INSTALLATION_GREEDY
+ */
+ public static final String INSTALLATION_DIRECTIVE = "x-installation"; //$NON-NLS-1$
+
+ /**
+ * Value for {@link #INSTALLATION_DIRECTIVE} indicating that an optional
+ * requirement shall be installed unless this is prevented by other mandatory
+ * requirements. Optional requirements without this directive value are ignored
+ * during installation.
+ */
+ public static final String INSTALLATION_GREEDY = "greedy"; //$NON-NLS-1$
+
+ private File[] locations;
+ private BundleDescription[] bundles;
+ protected MultiStatus finalStatus;
+
+ public static IArtifactKey createBundleArtifactKey(String bsn, String version) {
+ return new ArtifactKey(OSGI_BUNDLE_CLASSIFIER, bsn, Version.parseVersion(version));
+ }
+
+ public static IInstallableUnit createBundleConfigurationUnit(String hostId, Version cuVersion,
+ boolean isBundleFragment, GeneratorBundleInfo configInfo, String configurationFlavor,
+ IMatchExpression filter) {
+ return createBundleConfigurationUnit(hostId, cuVersion, isBundleFragment, configInfo, configurationFlavor,
+ filter, false);
+ }
+
+ static IInstallableUnit createBundleConfigurationUnit(String hostId, Version cuVersion,
+ boolean isBundleFragment, GeneratorBundleInfo configInfo, String configurationFlavor,
+ IMatchExpression filter, boolean configOnly) {
+ if (configInfo == null)
+ return null;
+
+ InstallableUnitFragmentDescription cu = new InstallableUnitFragmentDescription();
+ String configUnitId = configurationFlavor + hostId;
+ cu.setId(configUnitId);
+ cu.setVersion(cuVersion);
+
+ // Indicate the IU to which this CU apply
+ Version hostVersion = configOnly ? Version.emptyVersion : Version.parseVersion(configInfo.getVersion());
+ VersionRange range = hostVersion == Version.emptyVersion ? VersionRange.emptyRange
+ : new VersionRange(hostVersion, true, Version.MAX_VERSION, true);
+ cu.setHost(new IRequirement[] { //
+ MetadataFactory.createRequirement(CAPABILITY_NS_OSGI_BUNDLE, hostId, range, null, false, false, true), //
+ MetadataFactory.createRequirement(PublisherHelper.NAMESPACE_ECLIPSE_TYPE, TYPE_ECLIPSE_BUNDLE,
+ new VersionRange(Version.createOSGi(1, 0, 0), true, Version.createOSGi(2, 0, 0), false), null,
+ false, false, false) });
+
+ // Adds capabilities for fragment, self, and describing the flavor supported
+ cu.setProperty(InstallableUnitDescription.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString());
+ cu.setCapabilities(new IProvidedCapability[] { PublisherHelper.createSelfCapability(configUnitId, cuVersion),
+ MetadataFactory.createProvidedCapability(PublisherHelper.NAMESPACE_FLAVOR, configurationFlavor,
+ Version.createOSGi(1, 0, 0)) });
+
+ Map touchpointData = new HashMap<>();
+ touchpointData.put("install", "installBundle(bundle:${artifact})"); //$NON-NLS-1$ //$NON-NLS-2$
+ touchpointData.put("uninstall", "uninstallBundle(bundle:${artifact})"); //$NON-NLS-1$ //$NON-NLS-2$
+ touchpointData.put("configure", createConfigScript(configInfo, isBundleFragment)); //$NON-NLS-1$
+ touchpointData.put("unconfigure", createUnconfigScript(configInfo, isBundleFragment)); //$NON-NLS-1$
+ cu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
+ cu.setFilter(filter);
+ return MetadataFactory.createInstallableUnit(cu);
+ }
+
+ public static IInstallableUnit createBundleIU(BundleDescription bd, IArtifactKey key, IPublisherInfo info) {
+ return new BundlesAction(new BundleDescription[] { bd }).doCreateBundleIU(bd, key, info);
+ }
+
+ protected IInstallableUnit doCreateBundleIU(BundleDescription bd, IArtifactKey key, IPublisherInfo publisherInfo) {
+ @SuppressWarnings("unchecked")
+ Map manifest = (Map) bd.getUserObject();
+
+ Map> manifestLocalizations = null;
+ if (manifest != null && bd.getLocation() != null) {
+ manifestLocalizations = getManifestLocalizations(manifest, new File(bd.getLocation()));
+ }
+
+ InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
+ iu.setSingleton(bd.isSingleton());
+ iu.setId(bd.getSymbolicName());
+ iu.setVersion(PublisherHelper.fromOSGiVersion(bd.getVersion()));
+ iu.setFilter(bd.getPlatformFilter());
+ iu.setUpdateDescriptor(MetadataFactory.createUpdateDescriptor(bd.getSymbolicName(),
+ computeUpdateRange(bd.getVersion()), IUpdateDescriptor.NORMAL, null));
+ iu.setArtifacts(new IArtifactKey[] { key });
+ iu.setTouchpointType(PublisherHelper.TOUCHPOINT_OSGI);
+
+ boolean isFragment = (bd.getHost() != null);
+
+ // Gather requirements here
+ List requirements = new ArrayList<>();
+
+ // Process required fragment host
+ if (isFragment) {
+ requirements.add(MetadataFactory.createRequirement(CAPABILITY_NS_OSGI_BUNDLE, bd.getHost().getName(),
+ PublisherHelper.fromOSGiVersionRange(bd.getHost().getVersionRange()), null, false, false));
+ }
+
+ // Process required bundles
+ ManifestElement[] rawRequireBundleHeader = parseManifestHeader(Constants.REQUIRE_BUNDLE, manifest,
+ bd.getLocation());
+ for (BundleSpecification requiredBundle : bd.getRequiredBundles()) {
+ addRequireBundleRequirement(requirements, requiredBundle, rawRequireBundleHeader);
+ }
+
+ // Process the import packages
+ ManifestElement[] rawImportPackageHeader = parseManifestHeader(Constants.IMPORT_PACKAGE, manifest,
+ bd.getLocation());
+ for (ImportPackageSpecification importedPackage : bd.getImportPackages()) {
+ if (!isDynamicImport(importedPackage)) {
+ addImportPackageRequirement(requirements, importedPackage, rawImportPackageHeader);
+ }
+ }
+
+ // Process generic requirements
+ ManifestElement[] rawRequireCapHeader = parseManifestHeader(Constants.REQUIRE_CAPABILITY, manifest,
+ bd.getLocation());
+ for (GenericSpecification requiredCap : bd.getGenericRequires()) {
+ addRequirement(requirements, requiredCap, rawRequireCapHeader, bd);
+ }
+
+ // Create set of provided capabilities
+ List providedCapabilities = new ArrayList<>();
+
+ // Add identification capabilities
+ providedCapabilities.add(PublisherHelper.createSelfCapability(bd.getSymbolicName(),
+ PublisherHelper.fromOSGiVersion(bd.getVersion())));
+ providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_OSGI_BUNDLE,
+ bd.getSymbolicName(), PublisherHelper.fromOSGiVersion(bd.getVersion())));
+
+ // Process exported packages
+ for (ExportPackageDescription packageExport : bd.getExportPackages()) {
+ providedCapabilities
+ .add(MetadataFactory.createProvidedCapability(PublisherHelper.CAPABILITY_NS_JAVA_PACKAGE,
+ packageExport.getName(), PublisherHelper.fromOSGiVersion(packageExport.getVersion())));
+ }
+
+ // Process generic capabilities
+
+ // TODO
+ // IProvidedCapability may have to be extended to contain the OSGi directives as
+ // well which may be needed for
+ // Bug 360659, Bug 525368. E.g. with IProvidedCapability.getDirectives()
+
+ // TODO
+ // It may be possible map the "osgi.identity" capability to elements of the IU
+ // like the id, the license, etc.
+ // It may be better to derive it at runtime.
+
+ int capNo = 0;
+ for (GenericDescription genericCap : bd.getGenericCapabilities()) {
+ addCapability(providedCapabilities, genericCap, iu, capNo);
+ capNo++;
+ }
+
+ // Add capability to describe the type of bundle
+ if (manifest != null && manifest.containsKey("Eclipse-SourceBundle")) { //$NON-NLS-1$
+ providedCapabilities.add(SOURCE_BUNDLE_CAPABILITY);
+ } else {
+ providedCapabilities.add(BUNDLE_CAPABILITY);
+ // add an optional greedy disabled by default requirement to the source so a
+ // product or install agent can choose to include sources from a bundle
+ VersionRange strictRange = new VersionRange(iu.getVersion(), true, iu.getVersion(), true);
+ String sourceIu = iu.getId() + SOURCE_SUFFIX;
+ requirements.add(MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, sourceIu, strictRange,
+ INSTALL_SOURCE_FILTER, true, false, true));
+ }
+
+ // If needed add an additional capability to identify this as an OSGi fragment
+ if (isFragment) {
+ providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_OSGI_FRAGMENT,
+ bd.getHost().getName(), PublisherHelper.fromOSGiVersion(bd.getVersion())));
+ }
+
+ if (manifestLocalizations != null) {
+ for (Entry> locEntry : manifestLocalizations.entrySet()) {
+ Locale locale = locEntry.getKey();
+ Map translatedStrings = locEntry.getValue();
+ for (Entry entry : translatedStrings.entrySet()) {
+ iu.setProperty(locale.toString() + '.' + entry.getKey(), entry.getValue());
+ }
+ providedCapabilities.add(PublisherHelper.makeTranslationCapability(bd.getSymbolicName(), locale));
+ }
+ }
+ iu.setRequirements(requirements.toArray(new IRequirement[requirements.size()]));
+ iu.setCapabilities(providedCapabilities.toArray(new IProvidedCapability[providedCapabilities.size()]));
+
+ // Process advice
+ processUpdateDescriptorAdvice(iu, publisherInfo);
+ processCapabilityAdvice(iu, publisherInfo);
+
+ // Set certain properties from the manifest header attributes as IU properties.
+ // The values of these attributes may be localized (strings starting with '%')
+ // with the translated values appearing in the localization IU fragments
+ // associated with the bundle IU.
+ if (manifest != null) {
+ int i = 0;
+ while (i < BUNDLE_IU_PROPERTY_MAP.length) {
+ if (manifest.containsKey(BUNDLE_IU_PROPERTY_MAP[i])) {
+ String value = manifest.get(BUNDLE_IU_PROPERTY_MAP[i]);
+ if (value != null && value.length() > 0) {
+ iu.setProperty(BUNDLE_IU_PROPERTY_MAP[i + 1], value);
+ }
+ }
+ i += 2;
+ }
+ }
+
+ // Define the immutable metadata for this IU. In this case immutable means
+ // that this is something that will not impact the configuration.
+ Map touchpointData = new HashMap<>();
+ touchpointData.put("manifest", toManifestString(manifest)); //$NON-NLS-1$
+ if (isDir(bd, publisherInfo)) {
+ touchpointData.put("zipped", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ // Process more advice
+ processTouchpointAdvice(iu, touchpointData, publisherInfo);
+ processInstallableUnitPropertiesAdvice(iu, publisherInfo);
+
+ return MetadataFactory.createInstallableUnit(iu);
+ }
+
+ @Deprecated
+ protected void addImportPackageRequirement(ArrayList reqsDeps, ImportPackageSpecification importSpec,
+ ManifestElement[] rawImportPackageHeader) {
+ addImportPackageRequirement((List) reqsDeps, importSpec, rawImportPackageHeader);
+ }
+
+ protected void addImportPackageRequirement(List reqsDeps, ImportPackageSpecification importSpec,
+ ManifestElement[] rawImportPackageHeader) {
+ VersionRange versionRange = PublisherHelper.fromOSGiVersionRange(importSpec.getVersionRange());
+ final boolean optional = isOptional(importSpec);
+ final boolean greedy;
+ if (optional) {
+ greedy = INSTALLATION_GREEDY.equals(getInstallationDirective(importSpec.getName(), rawImportPackageHeader));
+ } else {
+ greedy = true;
+ }
+ // TODO this needs to be refined to take into account all the attribute handled
+ // by imports
+ reqsDeps.add(MetadataFactory.createRequirement(PublisherHelper.CAPABILITY_NS_JAVA_PACKAGE, importSpec.getName(),
+ versionRange, null, optional, false, greedy));
+ }
+
+ @Deprecated
+ protected void addRequireBundleRequirement(ArrayList reqsDeps, BundleSpecification requiredBundle,
+ ManifestElement[] rawRequireBundleHeader) {
+ addRequireBundleRequirement((List) reqsDeps, requiredBundle, rawRequireBundleHeader);
+ }
+
+ protected void addRequireBundleRequirement(List reqsDeps, BundleSpecification requiredBundle,
+ ManifestElement[] rawRequireBundleHeader) {
+ final boolean optional = requiredBundle.isOptional();
+ final boolean greedy;
+ if (optional) {
+ greedy = INSTALLATION_GREEDY
+ .equals(getInstallationDirective(requiredBundle.getName(), rawRequireBundleHeader));
+ } else {
+ greedy = true;
+ }
+ reqsDeps.add(MetadataFactory.createRequirement(CAPABILITY_NS_OSGI_BUNDLE, requiredBundle.getName(),
+ PublisherHelper.fromOSGiVersionRange(requiredBundle.getVersionRange()), null, optional ? 0 : 1, 1,
+ greedy));
+ }
+
+ // TODO Handle the "effective:=" directive somehow?
+ protected void addRequirement(List reqsDeps, GenericSpecification requireCapSpec,
+ ManifestElement[] rawRequireCapabilities) {
+ BundleRequirement req = requireCapSpec.getRequirement();
+
+ String namespace = req.getNamespace();
+ Map directives = req.getDirectives();
+
+ String capFilter = directives.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+ boolean optional = directives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE) == Namespace.RESOLUTION_OPTIONAL;
+ boolean greedy = optional ? INSTALLATION_GREEDY.equals(directives.get(INSTALLATION_DIRECTIVE)) : true;
+
+ IRequirement requireCap = MetadataFactory.createRequirement(namespace, capFilter, null, optional ? 0 : 1, 1,
+ greedy);
+ reqsDeps.add(requireCap);
+ }
+
+ protected void addRequirement(List reqsDeps, GenericSpecification requireCapSpec,
+ ManifestElement[] rawRequireCapabilities, BundleDescription bd) {
+ BundleRequirement req = requireCapSpec.getRequirement();
+
+ String namespace = req.getNamespace();
+ Map directives = req.getDirectives();
+
+ String capFilter = directives.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+ boolean optional = directives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE) == Namespace.RESOLUTION_OPTIONAL;
+ boolean greedy = optional ? INSTALLATION_GREEDY.equals(directives.get(INSTALLATION_DIRECTIVE)) : true;
+
+ IRequirement requireCap = MetadataFactory.createRequirement(namespace, capFilter, null, optional ? 0 : 1, 1,
+ greedy, bd.getSymbolicName());
+ reqsDeps.add(requireCap);
+ }
+
+ protected void addCapability(List caps, GenericDescription provideCapDesc,
+ InstallableUnitDescription iu, int capNo) {
+ // Convert the values to String, Version, List of String or Version
+ Map capAttrs = provideCapDesc.getDeclaredAttributes().entrySet().stream()
+ .collect(toMap(Entry::getKey, e -> convertAttribute(e.getValue())));
+
+ // Resolve the namespace
+ String capNs = provideCapDesc.getType();
+
+ // Resolve the mandatory p2 name
+ // By convention OSGi capabilities have an attribute named like the capability
+ // namespace.
+ // If this is not the case synthesize a unique name (e.g. "osgi.service" has an
+ // "objectClass" attribute instead).
+ // TODO If present but not a String log a warning somehow that it is ignored? Or
+ // fail the publication?
+ capAttrs.compute(capNs,
+ (k, v) -> (v instanceof String) ? v : String.format("%s_%s-%s", iu.getId(), iu.getVersion(), capNo)); //$NON-NLS-1$
+
+ for (Version version : getVersions(capAttrs)) {
+ capAttrs.put(IProvidedCapability.PROPERTY_VERSION, version); // created capability contains a copy
+ caps.add(MetadataFactory.createProvidedCapability(capNs, capAttrs));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Collection getVersions(Map capAttrs) {
+ // Resolve the mandatory p2 version
+ // By convention versioned OSGi capabilities have a "version" attribute
+ // containing the OSGi Version object
+ // If this is not the case use an empty version (e.g. "osgi.ee" has a list of
+ // versions).
+ Object versionValue = capAttrs.get(IProvidedCapability.PROPERTY_VERSION);
+ if (versionValue instanceof Version) {
+ return List.of((Version) versionValue);
+ } else if (versionValue instanceof Collection
+ && ((Collection>) versionValue).stream().allMatch(Version.class::isInstance)) {
+ return (Collection) versionValue;
+ } else {
+ // TODO If present but not a Version log a warning somehow that it is ignored?
+ // Or fail the publication?
+ return List.of(Version.emptyVersion);
+ }
+ }
+
+ private Object convertAttribute(Object attr) {
+ if (attr instanceof Collection>) {
+ return ((Collection>) attr).stream().map(this::convertScalarAttribute).collect(toList());
+ }
+ return convertScalarAttribute(attr);
+ }
+
+ private Object convertScalarAttribute(Object attr) {
+ if (attr instanceof org.osgi.framework.Version) {
+ org.osgi.framework.Version osgiVer = (org.osgi.framework.Version) attr;
+ return Version.createOSGi(osgiVer.getMajor(), osgiVer.getMinor(), osgiVer.getMicro(),
+ osgiVer.getQualifier());
+ }
+ return attr.toString();
+ }
+
+ static VersionRange computeUpdateRange(org.osgi.framework.Version base) {
+ VersionRange updateRange = null;
+ if (!base.equals(org.osgi.framework.Version.emptyVersion)) {
+ updateRange = new VersionRange(Version.emptyVersion, true, PublisherHelper.fromOSGiVersion(base), false);
+ } else {
+ updateRange = VersionRange.emptyRange;
+ }
+ return updateRange;
+ }
+
+ private IInstallableUnitFragment createHostLocalizationFragment(IInstallableUnit bundleIU, BundleDescription bd,
+ String hostId, String[] hostBundleManifestValues) {
+ Map> hostLocalizations = getHostLocalizations(new File(bd.getLocation()),
+ hostBundleManifestValues);
+ if (hostLocalizations == null || hostLocalizations.isEmpty())
+ return null;
+ return createLocalizationFragmentOfHost(bd, hostId, hostBundleManifestValues, hostLocalizations);
+ }
+
+ /*
+ * @param hostId
+ *
+ * @param bd
+ *
+ * @param locale
+ *
+ * @param localizedStrings
+ *
+ * @return installableUnitFragment
+ */
+ private static IInstallableUnitFragment createLocalizationFragmentOfHost(BundleDescription bd, String hostId,
+ String[] hostManifestValues, Map> hostLocalizations) {
+ InstallableUnitFragmentDescription fragment = new MetadataFactory.InstallableUnitFragmentDescription();
+ String fragmentId = makeHostLocalizationFragmentId(bd.getSymbolicName());
+ fragment.setId(fragmentId);
+ fragment.setVersion(PublisherHelper.fromOSGiVersion(bd.getVersion())); // TODO: is this a meaningful version?
+
+ HostSpecification hostSpec = bd.getHost();
+ IRequirement[] hostReqs = new IRequirement[] {
+ MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, hostSpec.getName(),
+ PublisherHelper.fromOSGiVersionRange(hostSpec.getVersionRange()), null, false, false, false) };
+ fragment.setHost(hostReqs);
+
+ fragment.setSingleton(true);
+ fragment.setProperty(InstallableUnitDescription.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString());
+
+ // Create a provided capability for each locale and add the translated
+ // properties.
+ ArrayList providedCapabilities = new ArrayList<>(hostLocalizations.size());
+ providedCapabilities.add(PublisherHelper.createSelfCapability(fragmentId, fragment.getVersion()));
+ for (Entry> localeEntry : hostLocalizations.entrySet()) {
+ Locale locale = localeEntry.getKey();
+ Map translatedStrings = localeEntry.getValue();
+ for (Entry entry : translatedStrings.entrySet()) {
+ fragment.setProperty(locale.toString() + '.' + entry.getKey(), entry.getValue());
+ }
+ providedCapabilities.add(PublisherHelper.makeTranslationCapability(hostId, locale));
+ }
+ fragment.setCapabilities(providedCapabilities.toArray(new IProvidedCapability[providedCapabilities.size()]));
+
+ return MetadataFactory.createInstallableUnitFragment(fragment);
+ }
+
+ /**
+ * @return the id for the iu fragment containing localized properties for the
+ * fragment with the given id.
+ */
+ private static String makeHostLocalizationFragmentId(String id) {
+ return id + ".translated_host_properties"; //$NON-NLS-1$
+ }
+
+ private static String createConfigScript(GeneratorBundleInfo configInfo, boolean isBundleFragment) {
+ if (configInfo == null)
+ return ""; //$NON-NLS-1$
+
+ String configScript = "";//$NON-NLS-1$
+ if (!isBundleFragment && configInfo.getStartLevel() != BundleInfo.NO_LEVEL) {
+ configScript += "setStartLevel(startLevel:" + configInfo.getStartLevel() + ");"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (!isBundleFragment && configInfo.isMarkedAsStarted()) {
+ configScript += "markStarted(started: true);"; //$NON-NLS-1$
+ }
+
+ if (configInfo.getSpecialConfigCommands() != null) {
+ configScript += configInfo.getSpecialConfigCommands();
+ }
+
+ return configScript;
+ }
+
+ private static String createDefaultBundleConfigScript(GeneratorBundleInfo configInfo) {
+ return createConfigScript(configInfo, false);
+ }
+
+ public static IInstallableUnit createDefaultBundleConfigurationUnit(GeneratorBundleInfo configInfo,
+ GeneratorBundleInfo unconfigInfo, String configurationFlavor) {
+ InstallableUnitFragmentDescription cu = new InstallableUnitFragmentDescription();
+ String configUnitId = PublisherHelper.createDefaultConfigUnitId(OSGI_BUNDLE_CLASSIFIER, configurationFlavor);
+ cu.setId(configUnitId);
+ Version configUnitVersion = Version.createOSGi(1, 0, 0);
+ cu.setVersion(configUnitVersion);
+
+ // Add capabilities for fragment, self, and describing the flavor supported
+ cu.setProperty(InstallableUnitDescription.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString());
+ cu.setCapabilities(
+ new IProvidedCapability[] { PublisherHelper.createSelfCapability(configUnitId, configUnitVersion),
+ MetadataFactory.createProvidedCapability(PublisherHelper.NAMESPACE_FLAVOR, configurationFlavor,
+ Version.createOSGi(1, 0, 0)) });
+
+ // Create a required capability on bundles
+ IRequirement[] reqs = new IRequirement[] {
+ MetadataFactory.createRequirement(PublisherHelper.NAMESPACE_ECLIPSE_TYPE, TYPE_ECLIPSE_BUNDLE,
+ VersionRange.emptyRange, null, false, true, false) };
+ cu.setHost(reqs);
+ Map touchpointData = new HashMap<>();
+
+ touchpointData.put("install", "installBundle(bundle:${artifact})"); //$NON-NLS-1$ //$NON-NLS-2$
+ touchpointData.put("uninstall", "uninstallBundle(bundle:${artifact})"); //$NON-NLS-1$ //$NON-NLS-2$
+ touchpointData.put("configure", createDefaultBundleConfigScript(configInfo)); //$NON-NLS-1$
+ touchpointData.put("unconfigure", createDefaultBundleUnconfigScript(unconfigInfo)); //$NON-NLS-1$
+
+ cu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
+ return MetadataFactory.createInstallableUnit(cu);
+ }
+
+ private static String createDefaultBundleUnconfigScript(GeneratorBundleInfo unconfigInfo) {
+ return createUnconfigScript(unconfigInfo, false);
+ }
+
+ private static String createUnconfigScript(GeneratorBundleInfo unconfigInfo, boolean isBundleFragment) {
+ if (unconfigInfo == null)
+ return ""; //$NON-NLS-1$
+ String unconfigScript = "";//$NON-NLS-1$
+ if (!isBundleFragment && unconfigInfo.getStartLevel() != BundleInfo.NO_LEVEL) {
+ unconfigScript += "setStartLevel(startLevel:" + BundleInfo.NO_LEVEL + ");"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (!isBundleFragment && unconfigInfo.isMarkedAsStarted()) {
+ unconfigScript += "markStarted(started: false);"; //$NON-NLS-1$
+ }
+
+ if (unconfigInfo.getSpecialUnconfigCommands() != null) {
+ unconfigScript += unconfigInfo.getSpecialUnconfigCommands();
+ }
+ return unconfigScript;
+ }
+
+ private static boolean isDynamicImport(ImportPackageSpecification importedPackage) {
+ return importedPackage.getDirective(Constants.RESOLUTION_DIRECTIVE)
+ .equals(ImportPackageSpecification.RESOLUTION_DYNAMIC);
+ }
+
+ protected static boolean isOptional(ImportPackageSpecification importedPackage) {
+ return importedPackage.getDirective(Constants.RESOLUTION_DIRECTIVE)
+ .equals(ImportPackageSpecification.RESOLUTION_OPTIONAL);
+ }
+
+ private static String toManifestString(Map p) {
+ if (p == null)
+ return null;
+ StringBuilder result = new StringBuilder();
+ // See https://bugs.eclipse.org/329386. We are trying to reduce the size of the
+ // manifest data in
+ // the eclipse touchpoint. We've removed the code that requires it but in order
+ // for old clients
+ // to still be able to use recent repositories, we're going to keep around the
+ // manifest properties
+ // they need.
+ final String[] interestingKeys = new String[] { Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_VERSION,
+ Constants.FRAGMENT_HOST };
+ for (String key : interestingKeys) {
+ String value = p.get(key);
+ if (value != null)
+ result.append(key).append(": ").append(value).append('\n'); //$NON-NLS-1$
+ }
+ return result.length() == 0 ? null : result.toString();
+ }
+
+ // Return a map from locale to property set for the manifest localizations
+ // from the given bundle directory and given bundle localization path/name
+ // manifest property value.
+ private static Map> getManifestLocalizations(Map manifest,
+ File bundleLocation) {
+ Map> localizations;
+ Locale defaultLocale = null; // = Locale.ENGLISH; // TODO: get this from GeneratorInfo
+ String[] bundleManifestValues = getManifestCachedValues(manifest);
+ String bundleLocalization = bundleManifestValues[BUNDLE_LOCALIZATION_INDEX]; // Bundle localization is the last
+ // one in the list
+
+ if ("jar".equalsIgnoreCase(IPath.fromOSString(bundleLocation.getName()).getFileExtension()) && //$NON-NLS-1$
+ bundleLocation.isFile()) {
+ localizations = LocalizationHelper.getJarPropertyLocalizations(bundleLocation, bundleLocalization,
+ defaultLocale, bundleManifestValues);
+ // localizations = getJarManifestLocalization(bundleLocation,
+ // bundleLocalization, defaultLocale, bundleManifestValues);
+ } else {
+ localizations = LocalizationHelper.getDirPropertyLocalizations(bundleLocation, bundleLocalization,
+ defaultLocale, bundleManifestValues);
+ // localizations = getDirManifestLocalization(bundleLocation,
+ // bundleLocalization, defaultLocale, bundleManifestValues);
+ }
+
+ return localizations;
+ }
+
+ public static String[] getExternalizedStrings(IInstallableUnit iu) {
+ String[] result = new String[PublisherHelper.BUNDLE_LOCALIZED_PROPERTIES.length];
+ int j = 0;
+ for (int i = 1; i < BUNDLE_IU_PROPERTY_MAP.length - 1; i += 2) {
+ if (iu.getProperty(BUNDLE_IU_PROPERTY_MAP[i]) != null
+ && iu.getProperty(BUNDLE_IU_PROPERTY_MAP[i]).length() > 0
+ && iu.getProperty(BUNDLE_IU_PROPERTY_MAP[i]).charAt(0) == '%')
+ result[j++] = iu.getProperty(BUNDLE_IU_PROPERTY_MAP[i]).substring(1);
+ else
+ j++;
+ }
+ // The last string is the location
+ result[BUNDLE_LOCALIZATION_INDEX] = iu.getProperty(IInstallableUnit.PROP_BUNDLE_LOCALIZATION);
+
+ return result;
+ }
+
+ public static String[] getManifestCachedValues(Map manifest) {
+ String[] cachedValues = new String[PublisherHelper.BUNDLE_LOCALIZED_PROPERTIES.length];
+ for (int j = 0; j < PublisherHelper.BUNDLE_LOCALIZED_PROPERTIES.length; j++) {
+ String value = manifest.get(PublisherHelper.BUNDLE_LOCALIZED_PROPERTIES[j]);
+ if (PublisherHelper.BUNDLE_LOCALIZED_PROPERTIES[j].equals(Constants.BUNDLE_LOCALIZATION)) {
+ if (value == null)
+ value = DEFAULT_BUNDLE_LOCALIZATION;
+ cachedValues[j] = value;
+ } else if (value != null && value.length() > 1 && value.charAt(0) == '%') {
+ cachedValues[j] = value.substring(1);
+ }
+ }
+ return cachedValues;
+ }
+
+ // Return a map from locale to property set for the manifest localizations
+ // from the given bundle directory and given bundle localization path/name
+ // manifest property value.
+ public static Map> getHostLocalizations(File bundleLocation,
+ String[] hostBundleManifestValues) {
+ Map> localizations;
+ Locale defaultLocale = null; // = Locale.ENGLISH; // TODO: get this from GeneratorInfo
+ String hostBundleLocalization = hostBundleManifestValues[BUNDLE_LOCALIZATION_INDEX];
+ if (hostBundleLocalization == null)
+ return null;
+
+ if ("jar".equalsIgnoreCase(IPath.fromOSString(bundleLocation.getName()).getFileExtension()) && //$NON-NLS-1$
+ bundleLocation.isFile()) {
+ localizations = LocalizationHelper.getJarPropertyLocalizations(bundleLocation, hostBundleLocalization,
+ defaultLocale, hostBundleManifestValues);
+ // localizations = getJarManifestLocalization(bundleLocation,
+ // hostBundleLocalization, defaultLocale, hostBundleManifestValues);
+ } else {
+ localizations = LocalizationHelper.getDirPropertyLocalizations(bundleLocation, hostBundleLocalization,
+ defaultLocale, hostBundleManifestValues);
+ // localizations = getDirManifestLocalization(bundleLocation,
+ // hostBundleLocalization, defaultLocale, hostBundleManifestValues);
+ }
+
+ return localizations;
+ }
+
+ public static BundleDescription createBundleDescription(Dictionary enhancedManifest,
+ File bundleLocation) {
+ try {
+ BundleDescription descriptor = StateObjectFactory.defaultFactory.createBundleDescription(null,
+ enhancedManifest, bundleLocation == null ? null : bundleLocation.getAbsolutePath(), 1); // TODO Do
+ // we need
+ // to have a
+ // real
+ // bundle id
+ descriptor.setUserObject(enhancedManifest);
+ return descriptor;
+ } catch (BundleException e) {
+ String message = NLS.bind(Messages.exception_stateAddition,
+ bundleLocation == null ? null : bundleLocation.getAbsoluteFile());
+ IStatus status = new Status(IStatus.WARNING, Activator.ID, message, e);
+ LogHelper.log(status);
+ return null;
+ }
+ }
+
+ /**
+ * @deprecated use {@link #createBundleDescription(File)} instead.
+ */
+ @Deprecated
+ public static BundleDescription createBundleDescriptionIgnoringExceptions(File bundleLocation) {
+ try {
+ return createBundleDescription(bundleLocation);
+ } catch (IOException e) {
+ logWarning(bundleLocation, e);
+ return null;
+ } catch (BundleException e) {
+ logWarning(bundleLocation, e);
+ return null;
+ }
+ }
+
+ private static void logWarning(File bundleLocation, Throwable t) {
+ String message = NLS.bind(Messages.exception_errorLoadingManifest, bundleLocation);
+ LogHelper.log(new Status(IStatus.WARNING, Activator.ID, message, t));
+ }
+
+ public static BundleDescription createBundleDescription(File bundleLocation) throws IOException, BundleException {
+ Dictionary manifest = loadManifest(bundleLocation);
+ if (manifest == null)
+ return null;
+ return createBundleDescription(manifest, bundleLocation);
+ }
+
+ /**
+ * @deprecated use {@link #loadManifest(File)} instead.
+ */
+ @Deprecated
+ public static Dictionary loadManifestIgnoringExceptions(File bundleLocation) {
+ try {
+ return loadManifest(bundleLocation);
+ } catch (IOException e) {
+ logWarning(bundleLocation, e);
+ return null;
+ } catch (BundleException e) {
+ logWarning(bundleLocation, e);
+ return null;
+ }
+ }
+
+ public static Dictionary loadManifest(File bundleLocation) throws IOException, BundleException {
+ Dictionary manifest = basicLoadManifest(bundleLocation);
+ if (manifest == null)
+ return null;
+ // if the bundle itself does not define its shape, infer the shape from the
+ // current form
+ if (manifest.get(BUNDLE_SHAPE) == null)
+ manifest.put(BUNDLE_SHAPE, bundleLocation.isDirectory() ? DIR : JAR);
+ return manifest;
+ }
+
+ /**
+ * @deprecated use {@link #basicLoadManifest(File)} instead.
+ */
+ @Deprecated
+ public static Dictionary basicLoadManifestIgnoringExceptions(File bundleLocation) {
+ try {
+ return basicLoadManifest(bundleLocation);
+ } catch (IOException e) {
+ logWarning(bundleLocation, e);
+ return null;
+ } catch (BundleException e) {
+ logWarning(bundleLocation, e);
+ return null;
+ }
+ }
+
+ public static Dictionary basicLoadManifest(File bundleLocation)
+ throws IOException, BundleException {
+ InputStream manifestStream = null;
+ ZipFile jarFile = null;
+ if ("jar".equalsIgnoreCase(IPath.fromOSString(bundleLocation.getName()).getFileExtension()) && bundleLocation.isFile()) { //$NON-NLS-1$
+ jarFile = new ZipFile(bundleLocation, ZipFile.OPEN_READ);
+ ZipEntry manifestEntry = jarFile.getEntry(JarFile.MANIFEST_NAME);
+ if (manifestEntry != null) {
+ manifestStream = jarFile.getInputStream(manifestEntry);
+ }
+ } else {
+ File manifestFile = new File(bundleLocation, JarFile.MANIFEST_NAME);
+ if (manifestFile.exists()) {
+ manifestStream = new BufferedInputStream(new FileInputStream(manifestFile));
+ }
+ }
+ try {
+ if (manifestStream != null) {
+ return parseBundleManifestIntoModifyableDictionaryWithCaseInsensitiveKeys(manifestStream);
+ }
+ } finally {
+ try {
+ if (jarFile != null)
+ jarFile.close();
+ } catch (IOException e2) {
+ // Ignore
+ }
+ }
+
+ return null;
+
+ }
+
+ private static Dictionary parseBundleManifestIntoModifyableDictionaryWithCaseInsensitiveKeys(
+ InputStream manifestStream) throws IOException, BundleException {
+ CaseInsensitiveDictionaryMap map = new CaseInsensitiveDictionaryMap<>(10);
+ ManifestElement.parseBundleManifest(manifestStream, map);
+ return map;
+ }
+
+ private static ManifestElement[] parseManifestHeader(String header, Map manifest,
+ String bundleLocation) {
+ try {
+ return ManifestElement.parseHeader(header, manifest.get(header));
+ } catch (BundleException e) {
+ String message = NLS.bind(Messages.exception_errorReadingManifest, bundleLocation, e.getMessage());
+ LogHelper.log(new Status(IStatus.ERROR, Activator.ID, message, e));
+ return null;
+ }
+ }
+
+ private static String getInstallationDirective(String requirementId, ManifestElement[] correspondingBundleHeader) {
+ for (ManifestElement manifestElement : correspondingBundleHeader) {
+ String[] packages = manifestElement.getValueComponents();
+ for (String pckg : packages) {
+ if (requirementId.equals(pckg)) {
+ return manifestElement.getDirective(INSTALLATION_DIRECTIVE);
+ }
+ }
+ }
+ // TODO this case indicates an internal error -> return assertion error status
+ return null;
+ }
+
+ public BundlesAction(File[] locations) {
+ this.locations = locations;
+ }
+
+ public BundlesAction(BundleDescription[] bundles) {
+ this.bundles = bundles;
+ }
+
+ @Override
+ public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
+ if (bundles == null && locations == null)
+ throw new IllegalStateException(Messages.exception_noBundlesOrLocations);
+
+ setPublisherInfo(publisherInfo);
+ finalStatus = new MultiStatus(Activator.ID, IStatus.OK, Messages.message_bundlesPublisherMultistatus, null);
+
+ try {
+ if (bundles == null)
+ bundles = getBundleDescriptions(expandLocations(locations), monitor);
+ generateBundleIUs(bundles, publisherInfo, results, monitor);
+ bundles = null;
+ } catch (OperationCanceledException e) {
+ return Status.CANCEL_STATUS;
+ }
+ if (!finalStatus.isOK()) {
+ return finalStatus;
+ }
+ return Status.OK_STATUS;
+ }
+
+ protected void publishArtifact(IArtifactDescriptor descriptor, File base, File[] inclusions,
+ IPublisherInfo publisherInfo) {
+ IArtifactRepository destination = publisherInfo.getArtifactRepository();
+ if (descriptor == null || destination == null)
+ return;
+
+ // publish the given files
+ publishArtifact(descriptor, inclusions, null, publisherInfo, createRootPrefixComputer(base));
+ }
+
+ @Override
+ protected void publishArtifact(IArtifactDescriptor descriptor, File jarFile, IPublisherInfo publisherInfo) {
+ // no files to publish so this is done.
+ if (jarFile == null || publisherInfo == null)
+ return;
+
+ // if the destination already contains the descriptor, there is nothing to do.
+ IArtifactRepository destination = publisherInfo.getArtifactRepository();
+ if (destination == null || destination.contains(descriptor))
+ return;
+
+ super.publishArtifact(descriptor, jarFile, publisherInfo);
+
+ }
+
+ private File[] expandLocations(File[] list) {
+ ArrayList result = new ArrayList<>();
+ expandLocations(list, result);
+ return result.toArray(new File[result.size()]);
+ }
+
+ private void expandLocations(File[] list, ArrayList result) {
+ if (list == null)
+ return;
+ for (File location : list) {
+ if (location.isDirectory()) {
+ // if the location is itself a bundle, just add it. Otherwise r down
+ if (new File(location, JarFile.MANIFEST_NAME).exists())
+ result.add(location);
+ else if (new File(location, "plugin.xml").exists() || new File(location, "fragment.xml").exists()) //$NON-NLS-1$ //$NON-NLS-2$
+ result.add(location); // old style bundle without manifest
+ else
+ expandLocations(location.listFiles(), result);
+ } else {
+ result.add(location);
+ }
+ }
+ }
+
+ /**
+ * Publishes bundle IUs to the p2 metadata and artifact repositories.
+ *
+ * @param bundleDescriptions Equinox framework descriptions of the bundles to
+ * publish.
+ * @param result Used to attach status for the publication
+ * operation.
+ * @param monitor Used to fire progress events.
+ *
+ * @deprecated Use
+ * {@link #generateBundleIUs(BundleDescription[] bundleDescriptions, IPublisherInfo info, IPublisherResult result, IProgressMonitor monitor)}
+ * with {@link IPublisherInfo} set to null
+ */
+ @Deprecated
+ protected void generateBundleIUs(BundleDescription[] bundleDescriptions, IPublisherResult result,
+ IProgressMonitor monitor) {
+ generateBundleIUs(bundleDescriptions, null, result, monitor);
+ }
+
+ /**
+ * Publishes bundle IUs to the p2 metadata and artifact repositories.
+ *
+ * @param bundleDescriptions Equinox framework descriptions of the bundles to
+ * publish.
+ * @param publisherInfo Configuration and publication advice information.
+ * @param result Used to attach status for the publication
+ * operation.
+ * @param monitor Used to fire progress events.
+ */
+ protected void generateBundleIUs(BundleDescription[] bundleDescriptions, IPublisherInfo publisherInfo,
+ IPublisherResult result, IProgressMonitor monitor) {
+ // This assumes that hosts are processed before fragments because for each
+ // fragment the host
+ // is queried for the strings that should be translated.
+ for (BundleDescription bd : bundleDescriptions) {
+ if (monitor.isCanceled()) {
+ throw new OperationCanceledException();
+ }
+
+ if (bd == null || bd.getSymbolicName() == null || bd.getVersion() == null) {
+ continue;
+ }
+
+ // First check to see if there is already an IU around for this
+ IInstallableUnit bundleIU = queryForIU(result, bd.getSymbolicName(),
+ PublisherHelper.fromOSGiVersion(bd.getVersion()));
+ IArtifactKey bundleArtKey = createBundleArtifactKey(bd.getSymbolicName(), bd.getVersion().toString());
+ if (bundleIU == null) {
+ createAdviceFileAdvice(bd, publisherInfo);
+ // Create the bundle IU according to any shape advice we have
+ bundleIU = doCreateBundleIU(bd, bundleArtKey, publisherInfo);
+ }
+
+ File bundleLocation = new File(bd.getLocation());
+ IArtifactDescriptor ad = PublisherHelper.createArtifactDescriptor(publisherInfo, bundleArtKey, bundleLocation);
+ processArtifactPropertiesAdvice(bundleIU, ad, publisherInfo);
+
+ // Publish according to the shape on disk
+ if (bundleLocation.isDirectory()) {
+ publishArtifact(ad, bundleLocation, bundleLocation.listFiles(), publisherInfo);
+ } else {
+ publishArtifact(ad, bundleLocation, publisherInfo);
+ }
+
+ IInstallableUnit fragment = null;
+ if (isFragment(bd)) {
+ String hostId = bd.getHost().getName();
+ VersionRange hostVersionRange = PublisherHelper.fromOSGiVersionRange(bd.getHost().getVersionRange());
+
+ IQueryResult hosts = queryForIUs(result, hostId, hostVersionRange);
+
+ for (IInstallableUnit host : hosts) {
+ String fragmentId = makeHostLocalizationFragmentId(bd.getSymbolicName());
+ fragment = queryForIU(result, fragmentId, PublisherHelper.fromOSGiVersion(bd.getVersion()));
+ if (fragment == null) {
+ String[] externalizedStrings = getExternalizedStrings(host);
+ fragment = createHostLocalizationFragment(bundleIU, bd, hostId, externalizedStrings);
+ }
+ }
+ }
+
+ result.addIU(bundleIU, IPublisherResult.ROOT);
+ if (fragment != null) {
+ result.addIU(fragment, IPublisherResult.NON_ROOT);
+ }
+
+ InstallableUnitDescription[] others = processAdditionalInstallableUnitsAdvice(bundleIU, publisherInfo);
+ for (int iuIndex = 0; others != null && iuIndex < others.length; iuIndex++) {
+ result.addIU(MetadataFactory.createInstallableUnit(others[iuIndex]), IPublisherResult.ROOT);
+ }
+ }
+ }
+
+ /**
+ * Adds advice for any p2.inf file found in this bundle.
+ */
+ protected void createAdviceFileAdvice(BundleDescription bundleDescription, IPublisherInfo publisherInfo) {
+ String location = bundleDescription.getLocation();
+ if (location == null)
+ return;
+
+ AdviceFileAdvice advice = new AdviceFileAdvice(bundleDescription.getSymbolicName(),
+ PublisherHelper.fromOSGiVersion(bundleDescription.getVersion()), IPath.fromOSString(location),
+ AdviceFileAdvice.BUNDLE_ADVICE_FILE);
+ if (advice.containsAdvice())
+ publisherInfo.addAdvice(advice);
+
+ }
+
+ private static boolean isDir(BundleDescription bundle, IPublisherInfo info) {
+ Collection advice = info.getAdvice(null, true, bundle.getSymbolicName(),
+ PublisherHelper.fromOSGiVersion(bundle.getVersion()), IBundleShapeAdvice.class);
+ // if the advice has a shape, use it
+ if (advice != null && !advice.isEmpty()) {
+ // we know there is some advice but if there is more than one, take the first.
+ String shape = advice.iterator().next().getShape();
+ if (shape != null)
+ return shape.equals(IBundleShapeAdvice.DIR);
+ }
+ // otherwise go with whatever we figured out from the manifest or the shape on
+ // disk
+ @SuppressWarnings("unchecked")
+ Map manifest = (Map) bundle.getUserObject();
+ String format = manifest.get(BUNDLE_SHAPE);
+ return DIR.equals(format);
+ }
+
+ private boolean isFragment(BundleDescription bd) {
+ return (bd.getHost() != null ? true : false);
+ }
+
+ protected BundleDescription[] getBundleDescriptions(File[] bundleLocations, IProgressMonitor monitor) {
+ if (bundleLocations == null)
+ return new BundleDescription[0];
+ List result = new ArrayList<>(bundleLocations.length);
+ for (File bundleLocation : bundleLocations) {
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ BundleDescription description = null;
+ try {
+ description = createBundleDescription(bundleLocation);
+ } catch (IOException e) {
+ addPublishingErrorToFinalStatus(e, bundleLocation);
+ } catch (BundleException e) {
+ addPublishingErrorToFinalStatus(e, bundleLocation);
+ }
+ if (description != null) {
+ result.add(description);
+ }
+ }
+ return result.toArray(new BundleDescription[0]);
+ }
+
+ private void addPublishingErrorToFinalStatus(Throwable t, File bundleLocation) {
+ finalStatus.add(new Status(IStatus.ERROR, Activator.ID,
+ NLS.bind(Messages.exception_errorPublishingBundle, bundleLocation, t.getMessage()), t));
+ }
+}