directives = new HashMap<>(description.getDeclaredDirectives());
+ // remove defaults directive values
+ if (!description.isSingleton())
+ directives.remove(Constants.SINGLETON_DIRECTIVE);
+ if (description.attachFragments() && description.dynamicFragments())
+ directives.remove(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+ result.setDirectives(directives);
+ return result;
+ }
+
+ static final String NAME = "org.eclipse.osgi"; //$NON-NLS-1$
+ /**
+ * Manifest header (named "Provide-Package")
+ * identifying the packages name
+ * provided to other bundles which require the bundle.
+ *
+ *
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this header.
+ *
+ *
The attribute value may be retrieved from the
+ * Dictionary
object returned by the Bundle.getHeaders
method.
+ * @deprecated
+ */
+ @Deprecated
+ public final static String PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$
+ static final String ECLIPSE_PLATFORMFILTER = "Eclipse-PlatformFilter"; //$NON-NLS-1$
+ static final String Eclipse_JREBUNDLE = "Eclipse-JREBundle"; //$NON-NLS-1$
+ /**
+ * Manifest Export-Package directive indicating that the exported package should only
+ * be made available when the resolver is not in strict mode.
+ */
+ static final String INTERNAL_DIRECTIVE = "x-internal"; //$NON-NLS-1$
+ /**
+ * Manifest Export-Package directive indicating that the exported package should only
+ * be made available to friends of the exporting bundle.
+ */
+ static final String FRIENDS_DIRECTIVE = "x-friends"; //$NON-NLS-1$
+ /**
+ * Manifest header attribute (named "reprovide")
+ * for Require-Bundle
+ * identifying that any packages that are provided
+ * by the required bundle must be reprovided by the requiring bundle.
+ * The default value is false
.
+ *
+ * The attribute value is encoded in the Require-Bundle manifest
+ * header like:
+ *
+ * Require-Bundle: com.acme.module.test; reprovide="true"
+ *
+ *
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this attribute.
+ */
+ final static String REPROVIDE_ATTRIBUTE = "reprovide"; //$NON-NLS-1$
+ /**
+ * Manifest header attribute (named "optional")
+ * for Require-Bundle
+ * identifying that a required bundle is optional and that
+ * the requiring bundle can be resolved if there is no
+ * suitable required bundle.
+ * The default value is false
.
+ *
+ *
The attribute value is encoded in the Require-Bundle manifest
+ * header like:
+ *
+ * Require-Bundle: com.acme.module.test; optional="true"
+ *
+ *
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this attribute.
+ * @since 1.3 EXPERIMENTAL
+ */
+ final static String OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$
+ static final String OSGI_EE_NAMESPACE = "osgi.ee"; //$NON-NLS-1$
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleDescriptionImpl.java
new file mode 100644
index 0000000000..ec08531127
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleDescriptionImpl.java
@@ -0,0 +1,950 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2021 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 Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522 and 255520)
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.net.URL;
+import java.util.*;
+import org.eclipse.osgi.framework.util.KeyedElement;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+import org.osgi.framework.wiring.*;
+import org.osgi.resource.*;
+
+@SuppressWarnings("restriction")
+final class BundleDescriptionImpl extends BaseDescriptionImpl implements BundleDescription, KeyedElement {
+ static final String[] EMPTY_STRING = new String[0];
+ static final ImportPackageSpecification[] EMPTY_IMPORTS = new ImportPackageSpecification[0];
+ static final BundleSpecification[] EMPTY_BUNDLESPECS = new BundleSpecification[0];
+ static final ExportPackageDescription[] EMPTY_EXPORTS = new ExportPackageDescription[0];
+ static final BundleDescription[] EMPTY_BUNDLEDESCS = new BundleDescription[0];
+ static final GenericSpecification[] EMPTY_GENERICSPECS = new GenericSpecification[0];
+ static final GenericDescription[] EMPTY_GENERICDESCS = new GenericDescription[0];
+
+ static final int RESOLVED = 0x01;
+ static final int SINGLETON = 0x02;
+ static final int REMOVAL_PENDING = 0x04;
+ static final int FULLY_LOADED = 0x08;
+ static final int LAZY_LOADED = 0x10;
+ static final int HAS_DYNAMICIMPORT = 0x20;
+ static final int ATTACH_FRAGMENTS = 0x40;
+ static final int DYNAMIC_FRAGMENTS = 0x80;
+
+ // set to fully loaded and allow dynamic fragments by default
+ private volatile int stateBits = FULLY_LOADED | ATTACH_FRAGMENTS | DYNAMIC_FRAGMENTS;
+
+ private volatile long bundleId = -1;
+ volatile HostSpecification host; //null if the bundle is not a fragment. volatile to allow unsynchronized checks for null
+
+ private List dependents;
+ private String[] mandatory;
+ private Map attributes;
+ private Map arbitraryDirectives;
+
+ private volatile LazyData lazyData;
+ private volatile int equinox_ee = -1;
+
+ private DescriptionWiring bundleWiring;
+
+ public BundleDescriptionImpl() {
+ //
+ }
+
+ @Override
+ public long getBundleId() {
+ return bundleId;
+ }
+
+ @Override
+ public String getSymbolicName() {
+ return getName();
+ }
+
+ @Override
+ public BundleDescription getSupplier() {
+ return this;
+ }
+
+ @Override
+ public String getLocation() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.location;
+ }
+ }
+
+ @Override
+ public String getPlatformFilter() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.platformFilter;
+ }
+ }
+
+ @Override
+ public String[] getExecutionEnvironments() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.executionEnvironments == null)
+ return EMPTY_STRING;
+ return currentData.executionEnvironments;
+ }
+ }
+
+ @Override
+ public ImportPackageSpecification[] getImportPackages() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.importPackages == null)
+ return EMPTY_IMPORTS;
+ return currentData.importPackages;
+ }
+ }
+
+ @Override
+ public ImportPackageSpecification[] getAddedDynamicImportPackages() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.addedDynamicImports == null)
+ return EMPTY_IMPORTS;
+ return currentData.addedDynamicImports
+ .toArray(new ImportPackageSpecification[currentData.addedDynamicImports.size()]);
+ }
+ }
+
+ @Override
+ public BundleSpecification[] getRequiredBundles() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.requiredBundles == null)
+ return EMPTY_BUNDLESPECS;
+ return currentData.requiredBundles;
+ }
+ }
+
+ @Override
+ public GenericSpecification[] getGenericRequires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.genericRequires == null)
+ return EMPTY_GENERICSPECS;
+ return currentData.genericRequires;
+ }
+ }
+
+ @Override
+ public GenericDescription[] getGenericCapabilities() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.genericCapabilities == null)
+ return EMPTY_GENERICDESCS;
+ return currentData.genericCapabilities;
+ }
+ }
+
+ @Override
+ public NativeCodeSpecification getNativeCodeSpecification() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ExportPackageDescription[] getExportPackages() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.exportPackages == null ? EMPTY_EXPORTS : currentData.exportPackages;
+ }
+ }
+
+ @Override
+ public boolean isResolved() {
+ return (stateBits & RESOLVED) != 0;
+ }
+
+ @Override
+ public State getContainingState() {
+ return null;
+ }
+
+ @Override
+ public BundleDescription[] getFragments() {
+ if (host != null)
+ return EMPTY_BUNDLEDESCS;
+ throw new IllegalStateException("BundleDescription does not belong to a state."); //$NON-NLS-1$
+ }
+
+ @Override
+ public HostSpecification getHost() {
+ return host;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return (stateBits & SINGLETON) != 0;
+ }
+
+ @Override
+ public boolean isRemovalPending() {
+ return (stateBits & REMOVAL_PENDING) != 0;
+ }
+
+ @Override
+ public boolean hasDynamicImports() {
+ return (stateBits & HAS_DYNAMICIMPORT) != 0;
+ }
+
+ @Override
+ public boolean attachFragments() {
+ return (stateBits & ATTACH_FRAGMENTS) != 0;
+ }
+
+ @Override
+ public boolean dynamicFragments() {
+ return (stateBits & DYNAMIC_FRAGMENTS) != 0;
+ }
+
+ @Override
+ public ExportPackageDescription[] getSelectedExports() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.selectedExports == null)
+ return EMPTY_EXPORTS;
+ return currentData.selectedExports;
+ }
+ }
+
+ @Override
+ public GenericDescription[] getSelectedGenericCapabilities() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.selectedCapabilities == null)
+ return EMPTY_GENERICDESCS;
+ return currentData.selectedCapabilities;
+ }
+ }
+
+ @Override
+ public ExportPackageDescription[] getSubstitutedExports() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.substitutedExports == null)
+ return EMPTY_EXPORTS;
+ return currentData.substitutedExports;
+ }
+ }
+
+ @Override
+ public BundleDescription[] getResolvedRequires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.resolvedRequires == null)
+ return EMPTY_BUNDLEDESCS;
+ return currentData.resolvedRequires;
+ }
+ }
+
+ @Override
+ public ExportPackageDescription[] getResolvedImports() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.resolvedImports == null)
+ return EMPTY_EXPORTS;
+ return currentData.resolvedImports;
+ }
+ }
+
+ @Override
+ public GenericDescription[] getResolvedGenericRequires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.resolvedCapabilities == null)
+ return EMPTY_GENERICDESCS;
+ return currentData.resolvedCapabilities;
+ }
+ }
+
+ public Map> getWires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.stateWires == null) {
+ currentData.stateWires = new HashMap<>(0);
+ }
+ return currentData.stateWires;
+ }
+ }
+
+ protected void setBundleId(long bundleId) {
+ this.bundleId = bundleId;
+ }
+
+ protected void setSymbolicName(String symbolicName) {
+ setName(symbolicName);
+ }
+
+ protected void setLocation(String location) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.location = location;
+ }
+ }
+
+ protected void setPlatformFilter(String platformFilter) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.platformFilter = platformFilter;
+ }
+ }
+
+ protected void setExecutionEnvironments(String[] executionEnvironments) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.executionEnvironments = executionEnvironments == null || executionEnvironments.length > 0 ? executionEnvironments : EMPTY_STRING;
+ }
+ }
+
+ protected void setExportPackages(ExportPackageDescription[] exportPackages) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.exportPackages = exportPackages == null || exportPackages.length > 0 ? exportPackages : EMPTY_EXPORTS;
+ if (exportPackages != null) {
+ for (ExportPackageDescription exportPackage : exportPackages) {
+ ((ExportPackageDescriptionImpl) exportPackage).setExporter(this);
+ }
+ }
+ }
+ }
+
+ protected void setImportPackages(ImportPackageSpecification[] importPackages) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.importPackages = importPackages == null || importPackages.length > 0 ? importPackages : EMPTY_IMPORTS;
+ if (importPackages != null) {
+ for (ImportPackageSpecification importPackage : importPackages) {
+ ((ImportPackageSpecificationImpl) importPackage).setBundle(this);
+ if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))) {
+ stateBits |= HAS_DYNAMICIMPORT;
+ }
+ }
+ }
+ }
+ }
+
+ protected void setRequiredBundles(BundleSpecification[] requiredBundles) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.requiredBundles = requiredBundles == null || requiredBundles.length > 0 ? requiredBundles : EMPTY_BUNDLESPECS;
+ if (requiredBundles != null)
+ for (BundleSpecification requiredBundle : requiredBundles) {
+ ((VersionConstraintImpl) requiredBundle).setBundle(this);
+ }
+ }
+ }
+
+ protected void setGenericCapabilities(GenericDescription[] genericCapabilities) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.genericCapabilities = genericCapabilities == null || genericCapabilities.length > 0 ? genericCapabilities : EMPTY_GENERICDESCS;
+ if (genericCapabilities != null)
+ for (GenericDescription genericCapability : genericCapabilities) {
+ ((GenericDescriptionImpl) genericCapability).setSupplier(this);
+ }
+ }
+ }
+
+ protected void setGenericRequires(GenericSpecification[] genericRequires) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.genericRequires = genericRequires == null || genericRequires.length > 0 ? genericRequires : EMPTY_GENERICSPECS;
+ if (genericRequires != null)
+ for (GenericSpecification genericRequire : genericRequires) {
+ ((VersionConstraintImpl) genericRequire).setBundle(this);
+ }
+ }
+ }
+
+ protected void setStateBit(int stateBit, boolean on) {
+ synchronized (this.monitor) {
+ if (on) {
+ stateBits |= stateBit;
+ } else {
+ stateBits &= ~stateBit;
+ if (stateBit == RESOLVED) {
+ if (bundleWiring != null)
+ bundleWiring.invalidate();
+ bundleWiring = null;
+ }
+ }
+ }
+ }
+
+ protected void setHost(HostSpecification host) {
+ synchronized (this.monitor) {
+ this.host = host;
+ if (host != null) {
+ ((VersionConstraintImpl) host).setBundle(this);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (getSymbolicName() == null)
+ return "[" + getBundleId() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ return getSymbolicName() + "_" + getVersion(); //$NON-NLS-1$
+ }
+
+ @Override
+ public Object getKey() {
+ return Long.valueOf(bundleId);
+ }
+
+ @Override
+ public boolean compare(KeyedElement other) {
+ if (!(other instanceof BundleDescriptionImpl))
+ return false;
+ BundleDescriptionImpl otherBundleDescription = (BundleDescriptionImpl) other;
+ return bundleId == otherBundleDescription.bundleId;
+ }
+
+ @Override
+ public int getKeyHashCode() {
+ return (int) (bundleId ^ (bundleId >>> 32));
+ }
+
+ @Override
+ public BundleDescription[] getDependents() {
+ synchronized (this.monitor) {
+ if (dependents == null)
+ return EMPTY_BUNDLEDESCS;
+ return dependents.toArray(new BundleDescription[dependents.size()]);
+ }
+ }
+
+ boolean hasDependents() {
+ synchronized (this.monitor) {
+ return dependents == null ? false : dependents.size() > 0;
+ }
+ }
+
+ // DO NOT call while holding this.monitor
+ private LazyData loadLazyData() {
+ if ((stateBits & LAZY_LOADED) == 0)
+ return this.lazyData;
+
+ throw new IllegalStateException("No valid reader for the bundle description"); //$NON-NLS-1$
+
+ }
+
+ public void setEquinoxEE(int equinox_ee) {
+ this.equinox_ee = equinox_ee;
+ }
+
+ public int getEquinoxEE() {
+ return equinox_ee;
+ }
+
+ private void checkLazyData() {
+ if (lazyData == null)
+ lazyData = new LazyData();
+ }
+
+ final class LazyData {
+ String location;
+ String platformFilter;
+
+ BundleSpecification[] requiredBundles;
+ ExportPackageDescription[] exportPackages;
+ ImportPackageSpecification[] importPackages;
+ GenericDescription[] genericCapabilities;
+ GenericSpecification[] genericRequires;
+ ExportPackageDescription[] selectedExports;
+ GenericDescription[] selectedCapabilities;
+ BundleDescription[] resolvedRequires;
+ ExportPackageDescription[] resolvedImports;
+ GenericDescription[] resolvedCapabilities;
+ ExportPackageDescription[] substitutedExports;
+ String[] executionEnvironments;
+
+ Map> stateWires;
+ // Note that this is not persisted in the state cache
+ List addedDynamicImports;
+ }
+
+ @Override
+ public Map getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map) attributes;
+ }
+ }
+
+ Object getDirective(String key) {
+ synchronized (this.monitor) {
+ if (Constants.MANDATORY_DIRECTIVE.equals(key))
+ return mandatory;
+ if (Constants.SINGLETON_DIRECTIVE.equals(key))
+ return isSingleton() ? Boolean.TRUE : Boolean.FALSE;
+ if (Constants.FRAGMENT_ATTACHMENT_DIRECTIVE.equals(key)) {
+ if (!attachFragments())
+ return Constants.FRAGMENT_ATTACHMENT_NEVER;
+ if (dynamicFragments())
+ return Constants.FRAGMENT_ATTACHMENT_ALWAYS;
+ return Constants.FRAGMENT_ATTACHMENT_RESOLVETIME;
+ }
+ }
+ return null;
+ }
+
+ void setDirective(String key, Object value) {
+ // only pay attention to mandatory directive for now; others are set with setState method
+ if (Constants.MANDATORY_DIRECTIVE.equals(key))
+ mandatory = (String[]) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map) directives;
+ }
+ }
+
+ Map getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ @Override
+ public Map getDeclaredDirectives() {
+ Map result = new HashMap<>(2);
+ Map arbitrary = getArbitraryDirectives();
+ if (arbitrary != null)
+ result.putAll(arbitrary);
+ if (!attachFragments()) {
+ result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_NEVER);
+ } else {
+ if (dynamicFragments())
+ result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_ALWAYS);
+ else
+ result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_RESOLVETIME);
+ }
+ if (isSingleton())
+ result.put(Constants.SINGLETON_DIRECTIVE, Boolean.TRUE.toString());
+ String[] mandatoryDirective = (String[]) getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (mandatoryDirective != null)
+ result.put(Constants.MANDATORY_DIRECTIVE, ExportPackageDescriptionImpl.toString(mandatoryDirective));
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public Map getDeclaredAttributes() {
+ Map result = new HashMap<>(1);
+ synchronized (this.monitor) {
+ if (attributes != null)
+ result.putAll(attributes);
+ }
+ result.put(BundleRevision.BUNDLE_NAMESPACE, getName());
+ result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, getVersion());
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public List getDeclaredRequirements(String namespace) {
+ List result = new ArrayList<>();
+ if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) {
+ BundleSpecification[] requires = getRequiredBundles();
+ for (BundleSpecification require : requires) {
+ result.add(require.getRequirement());
+ }
+ }
+ if (host != null && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) {
+ result.add(host.getRequirement());
+ }
+ if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
+ ImportPackageSpecification[] imports = getImportPackages();
+ for (ImportPackageSpecification importPkg : imports)
+ result.add(importPkg.getRequirement());
+ }
+ GenericSpecification[] genericSpecifications = getGenericRequires();
+ for (GenericSpecification requirement : genericSpecifications) {
+ if (namespace == null || namespace.equals(requirement.getType()))
+ result.add(requirement.getRequirement());
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ @Override
+ public List getDeclaredCapabilities(String namespace) {
+ List result = new ArrayList<>();
+ if (host == null) {
+ if (getSymbolicName() != null) {
+ if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) {
+ result.add(BundleDescriptionImpl.this.getCapability());
+ }
+ if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) {
+ result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE));
+ }
+ }
+
+ } else {
+ // may need to have a osgi.wiring.fragment capability
+ }
+ if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
+ ExportPackageDescription[] exports = getExportPackages();
+ for (ExportPackageDescription exportPkg : exports)
+ result.add(exportPkg.getCapability());
+ }
+ GenericDescription[] genericCapabilities = getGenericCapabilities();
+ for (GenericDescription capabilitiy : genericCapabilities) {
+ if (namespace == null || namespace.equals(capabilitiy.getType()))
+ result.add(capabilitiy.getCapability());
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ @Override
+ public int getTypes() {
+ return getHost() != null ? BundleRevision.TYPE_FRAGMENT : 0;
+ }
+
+ @Override
+ public Bundle getBundle() {
+ Object ref = getUserObject();
+ if (ref instanceof BundleReference)
+ return ((BundleReference) ref).getBundle();
+ return null;
+ }
+
+ @Override
+ String getInternalNameSpace() {
+ return BundleRevision.BUNDLE_NAMESPACE;
+ }
+
+ @Override
+ public BundleWiring getWiring() {
+ synchronized (this.monitor) {
+ if (bundleWiring != null || !isResolved())
+ return bundleWiring;
+ return bundleWiring = new DescriptionWiring();
+ }
+ }
+
+ static class BundleWireImpl implements BundleWire {
+ private final BundleCapability capability;
+ private final BundleWiring provider;
+ private final BundleRequirement requirement;
+ private final BundleWiring requirer;
+
+ public BundleWireImpl(StateWire wire) {
+ VersionConstraint declaredRequirement = wire.getDeclaredRequirement();
+ if (declaredRequirement instanceof HostSpecification)
+ this.capability = ((BaseDescriptionImpl) wire.getDeclaredCapability()).getCapability(BundleRevision.HOST_NAMESPACE);
+ else
+ this.capability = wire.getDeclaredCapability().getCapability();
+ this.provider = wire.getCapabilityHost().getWiring();
+ this.requirement = declaredRequirement.getRequirement();
+ this.requirer = wire.getRequirementHost().getWiring();
+ }
+
+ @Override
+ public BundleCapability getCapability() {
+ return capability;
+ }
+
+ @Override
+ public BundleRequirement getRequirement() {
+ return requirement;
+ }
+
+ @Override
+ public BundleWiring getProviderWiring() {
+ return provider;
+ }
+
+ @Override
+ public BundleWiring getRequirerWiring() {
+ return requirer;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashcode = 31 + capability.hashCode();
+ hashcode = hashcode * 31 + requirement.hashCode();
+ hashcode = hashcode * 31 + provider.hashCode();
+ hashcode = hashcode * 31 + requirer.hashCode();
+ return hashcode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof BundleWireImpl))
+ return false;
+ BundleWireImpl other = (BundleWireImpl) obj;
+ return capability.equals(other.getCapability()) && requirement.equals(other.getRequirement()) && provider.equals(other.getProviderWiring()) && requirer.equals(other.getRequirerWiring());
+ }
+
+ @Override
+ public String toString() {
+ return getRequirement() + " -> " + getCapability(); //$NON-NLS-1$
+ }
+
+ @Override
+ public BundleRevision getProvider() {
+ return provider.getRevision();
+ }
+
+ @Override
+ public BundleRevision getRequirer() {
+ return requirer.getRevision();
+ }
+ }
+
+ /**
+ * Coerce the generic type of a list from List
+ * to List
+ * @param l List to be coerced.
+ * @return l coerced to List
+ */
+ @SuppressWarnings("unchecked")
+ static List asListWire(List extends Wire> l) {
+ return (List) l;
+ }
+
+ /**
+ * Coerce the generic type of a list from List
+ * to List
+ * @param l List to be coerced.
+ * @return l coerced to List
+ */
+ @SuppressWarnings("unchecked")
+ static List asListCapability(List extends Capability> l) {
+ return (List) l;
+ }
+
+ /**
+ * Coerce the generic type of a list from List
+ * to List
+ * @param l List to be coerced.
+ * @return l coerced to List
+ */
+ @SuppressWarnings("unchecked")
+ static List asListRequirement(List extends Requirement> l) {
+ return (List) l;
+ }
+
+ // Note that description wiring are identity equality based
+ class DescriptionWiring implements BundleWiring {
+ private volatile boolean valid = true;
+
+ @Override
+ public Bundle getBundle() {
+ return BundleDescriptionImpl.this.getBundle();
+ }
+
+ @Override
+ public boolean isInUse() {
+ return valid && (isCurrent() || BundleDescriptionImpl.this.hasDependents());
+ }
+
+ void invalidate() {
+ valid = false;
+ }
+
+ @Override
+ public boolean isCurrent() {
+ return valid && !BundleDescriptionImpl.this.isRemovalPending();
+ }
+
+ @Override
+ public List getCapabilities(String namespace) {
+ if (!isInUse())
+ return null;
+ List result = new ArrayList<>();
+ GenericDescription[] genericCapabilities = getSelectedGenericCapabilities();
+ for (GenericDescription capabilitiy : genericCapabilities) {
+ if (namespace == null || namespace.equals(capabilitiy.getType()))
+ result.add(capabilitiy.getCapability());
+ }
+ if (host != null)
+ return result;
+ if (getSymbolicName() != null) {
+ if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) {
+ result.add(BundleDescriptionImpl.this.getCapability());
+ }
+ if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) {
+ result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE));
+ }
+ }
+ if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
+ ExportPackageDescription[] exports = getSelectedExports();
+ for (ExportPackageDescription exportPkg : exports)
+ result.add(exportPkg.getCapability());
+ }
+ return result;
+ }
+
+ @Override
+ public List getResourceCapabilities(String namespace) {
+ return asListCapability(getCapabilities(namespace));
+ }
+
+ @Override
+ public List getRequirements(String namespace) {
+ List requiredWires = getRequiredWires(namespace);
+ if (requiredWires == null)
+ // happens if not in use
+ return null;
+ List requirements = new ArrayList<>(requiredWires.size());
+ for (BundleWire wire : requiredWires) {
+ if (!requirements.contains(wire.getRequirement()))
+ requirements.add(wire.getRequirement());
+ }
+ // get dynamic imports
+ if (getHost() == null && (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace))) {
+ // TODO need to handle fragments that add dynamic imports
+ if (hasDynamicImports()) {
+ ImportPackageSpecification[] imports = getImportPackages();
+ for (ImportPackageSpecification impPackage : imports) {
+ if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(impPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))) {
+ BundleRequirement req = impPackage.getRequirement();
+ if (!requirements.contains(req))
+ requirements.add(req);
+ }
+ }
+ }
+ ImportPackageSpecification[] addedDynamic = getAddedDynamicImportPackages();
+ for (ImportPackageSpecification dynamicImport : addedDynamic) {
+ BundleRequirement req = dynamicImport.getRequirement();
+ if (!requirements.contains(req))
+ requirements.add(req);
+ }
+ }
+ return requirements;
+ }
+
+ @Override
+ public List getResourceRequirements(String namespace) {
+ return asListRequirement(getRequirements(namespace));
+ }
+
+ @Override
+ public List getProvidedWires(String namespace) {
+ if (!isInUse())
+ return null;
+ BundleDescription[] dependentBundles = getDependents();
+ List unorderedResult = new ArrayList<>();
+ for (BundleDescription dependent : dependentBundles) {
+ List dependentWires = dependent.getWiring().getRequiredWires(namespace);
+ if (dependentWires != null)
+ for (BundleWire bundleWire : dependentWires) {
+ if (bundleWire.getProviderWiring() == this)
+ unorderedResult.add(bundleWire);
+ }
+ }
+ List orderedResult = new ArrayList<>(unorderedResult.size());
+ List capabilities = getCapabilities(namespace);
+ for (BundleCapability capability : capabilities) {
+ for (Iterator wires = unorderedResult.iterator(); wires.hasNext();) {
+ BundleWire wire = wires.next();
+ if (wire.getCapability().equals(capability)) {
+ wires.remove();
+ orderedResult.add(wire);
+ }
+ }
+ }
+ return orderedResult;
+ }
+
+ @Override
+ public List getProvidedResourceWires(String namespace) {
+ return asListWire(getProvidedWires(namespace));
+ }
+
+ @Override
+ public List getRequiredWires(String namespace) {
+ if (!isInUse())
+ return null;
+ List result = Collections.emptyList();
+ Map> wireMap = getWires();
+ if (namespace == null) {
+ result = new ArrayList<>();
+ for (List wires : wireMap.values()) {
+ for (StateWire wire : wires) {
+ result.add(new BundleWireImpl(wire));
+ }
+ }
+ return result;
+ }
+ List wires = wireMap.get(namespace);
+ if (wires == null)
+ return result;
+ result = new ArrayList<>(wires.size());
+ for (StateWire wire : wires) {
+ result.add(new BundleWireImpl(wire));
+ }
+ return result;
+ }
+
+ @Override
+ public List getRequiredResourceWires(String namespace) {
+ return asListWire(getRequiredWires(namespace));
+ }
+
+ @Override
+ public BundleRevision getRevision() {
+ return BundleDescriptionImpl.this;
+ }
+
+ @Override
+ public BundleRevision getResource() {
+ return getRevision();
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List findEntries(String path, String filePattern, int options) {
+ return null;
+ }
+
+ @Override
+ public Collection listResources(String path, String filePattern, int options) {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return BundleDescriptionImpl.this.toString();
+ }
+ }
+
+ @Override
+ public List getCapabilities(String namespace) {
+ return asListCapability(getDeclaredCapabilities(namespace));
+ }
+
+ @Override
+ public List getRequirements(String namespace) {
+ return asListRequirement(getDeclaredRequirements(namespace));
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleSpecificationImpl.java
new file mode 100644
index 0000000000..3b76e3ba6b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleSpecificationImpl.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2021 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 Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRevision;
+
+class BundleSpecificationImpl extends VersionConstraintImpl implements BundleSpecification {
+ private boolean exported;
+ private boolean optional;
+ private Map attributes;
+ private Map arbitraryDirectives;
+
+ protected void setExported(boolean exported) {
+ synchronized (this.monitor) {
+ this.exported = exported;
+ }
+ }
+
+ protected void setOptional(boolean optional) {
+ synchronized (this.monitor) {
+ this.optional = optional;
+ }
+ }
+
+ @Override
+ public boolean isExported() {
+ synchronized (this.monitor) {
+ return exported;
+ }
+ }
+
+ @Override
+ public boolean isOptional() {
+ synchronized (this.monitor) {
+ return optional;
+ }
+ }
+
+ Map getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map) attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map) directives;
+ }
+ }
+
+ @Override
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ if (!(supplier instanceof BundleDescriptionImpl))
+ return false;
+ BundleDescriptionImpl candidate = (BundleDescriptionImpl) supplier;
+ if (candidate.getHost() != null)
+ return false;
+ Map requiredAttrs = getAttributes();
+ if (requiredAttrs != null) {
+ Map prividerAttrs = candidate.getAttributes();
+ if (prividerAttrs == null)
+ return false;
+ for (String key : requiredAttrs.keySet()) {
+ Object requiredValue = requiredAttrs.get(key);
+ Object prividedValue = prividerAttrs.get(key);
+ if (prividedValue == null || !requiredValue.equals(prividedValue))
+ return false;
+ }
+ }
+ String[] mandatory = (String[]) candidate.getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (!hasMandatoryAttributes(mandatory))
+ return false;
+ if (getName() != null && getName().equals(candidate.getSymbolicName()) && (getVersionRange() == null || getVersionRange().isIncluded(candidate.getVersion())))
+ return true;
+ return false;
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ if (mandatory != null) {
+ Map requiredAttrs = getAttributes();
+ for (String key : mandatory) {
+ if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(key))
+ continue; // has a default value of 0.0.0
+ if (requiredAttrs == null || requiredAttrs.get(key) == null)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Require-Bundle: " + getName() + "; bundle-version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ protected Map getInternalDirectives() {
+ Map result = new HashMap<>(2);
+ synchronized (this.monitor) {
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ if (exported)
+ result.put(Constants.VISIBILITY_DIRECTIVE, Constants.VISIBILITY_REEXPORT);
+ if (optional)
+ result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL);
+ result.put(Constants.FILTER_DIRECTIVE, createFilterDirective());
+ return result;
+ }
+ }
+
+ private String createFilterDirective() {
+ StringBuilder filter = new StringBuilder();
+ filter.append("(&"); //$NON-NLS-1$
+ synchronized (this.monitor) {
+ addFilterAttribute(filter, BundleRevision.BUNDLE_NAMESPACE, getName());
+ VersionRange range = getVersionRange();
+ if (range != null && range != VersionRange.emptyRange)
+ addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, range);
+ if (attributes != null)
+ addFilterAttributes(filter, attributes);
+ }
+ filter.append(')');
+ return filter.toString();
+ }
+
+ @Override
+ protected Map getInteralAttributes() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return BundleRevision.BUNDLE_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/ExportPackageDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/ExportPackageDescriptionImpl.java
new file mode 100644
index 0000000000..024af70c33
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/ExportPackageDescriptionImpl.java
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2016 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 Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleRevision;
+
+class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements ExportPackageDescription {
+ public static final String EQUINOX_EE = "x-equinox-ee"; //$NON-NLS-1$
+ private static final Integer EQUINOX_EE_DEFAULT = Integer.valueOf(-1);
+ private String[] uses;
+ private Map attributes;
+ private Map arbitraryDirectives;
+ private volatile BundleDescription exporter;
+ private String exclude;
+ private String include;
+ private String[] friends;
+ private String[] mandatory;
+ private Boolean internal = Boolean.FALSE;
+ private int equinox_ee = -1;
+ private final ExportPackageDescription fragmentDeclaration = null;
+
+ public ExportPackageDescriptionImpl() {
+ super();
+ }
+
+ @Override
+ public Map getDirectives() {
+ synchronized (this.monitor) {
+ Map result = new HashMap<>(7);
+ if (uses != null)
+ result.put(Constants.USES_DIRECTIVE, uses);
+ if (exclude != null)
+ result.put(Constants.EXCLUDE_DIRECTIVE, exclude);
+ if (include != null)
+ result.put(Constants.INCLUDE_DIRECTIVE, include);
+ if (mandatory != null)
+ result.put(Constants.MANDATORY_DIRECTIVE, mandatory);
+ if (friends != null)
+ result.put(BundleDescriptionBuilder.FRIENDS_DIRECTIVE, friends);
+ result.put(BundleDescriptionBuilder.INTERNAL_DIRECTIVE, internal);
+ result.put(EQUINOX_EE, equinox_ee == -1 ? EQUINOX_EE_DEFAULT : Integer.valueOf(equinox_ee));
+ return result;
+ }
+ }
+
+ @Override
+ public Map getDeclaredDirectives() {
+ Map result = new HashMap<>(6);
+ synchronized (this.monitor) {
+ Map arbitrary = getArbitraryDirectives();
+ if (arbitrary != null)
+ result.putAll(arbitrary);
+ if (uses != null)
+ result.put(Constants.USES_DIRECTIVE, toString(uses));
+ if (exclude != null)
+ result.put(Constants.EXCLUDE_DIRECTIVE, exclude);
+ if (include != null)
+ result.put(Constants.INCLUDE_DIRECTIVE, include);
+ if (mandatory != null)
+ result.put(Constants.MANDATORY_DIRECTIVE, toString(mandatory));
+ if (friends != null)
+ result.put(BundleDescriptionBuilder.FRIENDS_DIRECTIVE, toString(friends));
+ if (internal != null)
+ result.put(BundleDescriptionBuilder.INTERNAL_DIRECTIVE, internal.toString());
+ return Collections.unmodifiableMap(result);
+ }
+ }
+
+ @Override
+ public Map getDeclaredAttributes() {
+ Map result = new HashMap<>(2);
+ synchronized (this.monitor) {
+ if (attributes != null)
+ result.putAll(attributes);
+ result.put(BundleRevision.PACKAGE_NAMESPACE, getName());
+ result.put(Constants.VERSION_ATTRIBUTE, getVersion());
+ Version bundleVersion = getSupplier().getVersion();
+ if (bundleVersion != null)
+ result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
+ String symbolicName = getSupplier().getSymbolicName();
+ if (symbolicName != null) {
+ if (symbolicName.equals(BundleDescriptionBuilder.NAME))
+ result.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Arrays.asList(Constants.SYSTEM_BUNDLE_SYMBOLICNAME, symbolicName));
+ else
+ result.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName);
+ }
+ return Collections.unmodifiableMap(result);
+ }
+ }
+
+ static String toString(String[] list) {
+ StringBuilder buffer = new StringBuilder();
+ for (String string : list)
+ buffer.append(string).append(',');
+ if (buffer.length() > 0)
+ buffer.setLength(buffer.length() - 1);
+ return buffer.toString();
+ }
+
+ @Override
+ public Object getDirective(String key) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.USES_DIRECTIVE))
+ return uses;
+ if (key.equals(Constants.EXCLUDE_DIRECTIVE))
+ return exclude;
+ if (key.equals(Constants.INCLUDE_DIRECTIVE))
+ return include;
+ if (key.equals(Constants.MANDATORY_DIRECTIVE))
+ return mandatory;
+ if (key.equals(BundleDescriptionBuilder.FRIENDS_DIRECTIVE))
+ return friends;
+ if (key.equals(BundleDescriptionBuilder.INTERNAL_DIRECTIVE))
+ return internal;
+ if (key.equals(EQUINOX_EE))
+ return equinox_ee == -1 ? EQUINOX_EE_DEFAULT : Integer.valueOf(equinox_ee);
+ return null;
+ }
+ }
+
+ public Object setDirective(String key, Object value) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.USES_DIRECTIVE))
+ return uses = (String[]) value;
+ if (key.equals(Constants.EXCLUDE_DIRECTIVE))
+ return exclude = (String) value;
+ if (key.equals(Constants.INCLUDE_DIRECTIVE))
+ return include = (String) value;
+ if (key.equals(Constants.MANDATORY_DIRECTIVE))
+ return mandatory = (String[]) value;
+ if (key.equals(BundleDescriptionBuilder.FRIENDS_DIRECTIVE))
+ return friends = (String[]) value;
+ if (key.equals(BundleDescriptionBuilder.INTERNAL_DIRECTIVE))
+ return internal = (Boolean) value;
+ if (key.equals(EQUINOX_EE)) {
+ equinox_ee = ((Integer) value).intValue();
+ return value;
+ }
+ return null;
+ }
+ }
+
+ public void setDirectives(Map directives) {
+ synchronized (this.monitor) {
+ if (directives == null)
+ return;
+ uses = (String[]) directives.get(Constants.USES_DIRECTIVE);
+ exclude = (String) directives.get(Constants.EXCLUDE_DIRECTIVE);
+ include = (String) directives.get(Constants.INCLUDE_DIRECTIVE);
+ mandatory = (String[]) directives.get(Constants.MANDATORY_DIRECTIVE);
+ friends = (String[]) directives.get(BundleDescriptionBuilder.FRIENDS_DIRECTIVE);
+ internal = (Boolean) directives.get(BundleDescriptionBuilder.INTERNAL_DIRECTIVE);
+ equinox_ee = ((Integer) directives.get(EQUINOX_EE)).intValue();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map) directives;
+ }
+ }
+
+ Map getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ @Override
+ public Map getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @Override
+ public BundleDescription getSupplier() {
+ return getExporter();
+ }
+
+ @Override
+ public BundleDescription getExporter() {
+ return exporter;
+ }
+
+ /**
+ * @deprecated
+ */
+ @Override
+ @Deprecated
+ public boolean isRoot() {
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void setAttributes(Map attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map) attributes;
+ }
+ }
+
+ protected void setExporter(BundleDescription exporter) {
+ this.exporter = exporter;
+ }
+
+ @Override
+ public BaseDescription getFragmentDeclaration() {
+ return fragmentDeclaration;
+ }
+
+ @Override
+ public String toString() {
+ return "Export-Package: " + getName() + "; version=\"" + getVersion() + "\""; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ String getInternalNameSpace() {
+ return BundleRevision.PACKAGE_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/GenericDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/GenericDescriptionImpl.java
new file mode 100644
index 0000000000..4be7c603e6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/GenericDescriptionImpl.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2021 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 Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+class GenericDescriptionImpl extends BaseDescriptionImpl implements GenericDescription {
+ private Dictionary attributes;
+ private volatile BundleDescription supplier;
+ private volatile String type = GenericDescription.DEFAULT_TYPE;
+ private Map directives;
+ private GenericDescription fragmentDeclaration;
+
+ public GenericDescriptionImpl() {
+ super();
+ }
+
+ @Override
+ public Dictionary getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @Override
+ public BundleDescription getSupplier() {
+ return supplier;
+ }
+
+ void setAttributes(Dictionary attributes) {
+ synchronized (this.monitor) {
+ this.attributes = attributes;
+ }
+ }
+
+ void setDirectives(Map directives) {
+ synchronized (this.monitor) {
+ this.directives = directives;
+ }
+ }
+
+ void setSupplier(BundleDescription supplier) {
+ this.supplier = supplier;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Constants.PROVIDE_CAPABILITY).append(": ").append(getType()); //$NON-NLS-1$
+ Map attrs = getDeclaredAttributes();
+ sb.append(toString(attrs, false));
+ return sb.toString();
+ }
+
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ @Override
+ public String getName() {
+ synchronized (this.monitor) {
+ Object name = attributes != null ? attributes.get(getType()) : null;
+ return name instanceof String ? (String) name : null;
+ }
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ void setType(String type) {
+ if (type == null || type.equals(GenericDescription.DEFAULT_TYPE))
+ this.type = GenericDescription.DEFAULT_TYPE;
+ else
+ this.type = type;
+ }
+
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ @Override
+ public Version getVersion() {
+ Object version = attributes != null ? attributes.get(Constants.VERSION_ATTRIBUTE) : null;
+ return version instanceof Version ? (Version) version : super.getVersion();
+ }
+
+ @Override
+ public Map getDeclaredDirectives() {
+ synchronized (this.monitor) {
+ if (directives == null)
+ return Collections.emptyMap();
+ return Collections.unmodifiableMap(directives);
+ }
+ }
+
+ @Override
+ public Map getDeclaredAttributes() {
+ synchronized (this.monitor) {
+ Map result = new HashMap<>(5);
+ if (attributes != null)
+ for (Enumeration keys = attributes.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ Object value = attributes.get(key);
+ if (value instanceof List)
+ value = Collections.unmodifiableList((List>) value);
+ result.put(key, value);
+ }
+ return Collections.unmodifiableMap(result);
+ }
+ }
+
+ @Override
+ String getInternalNameSpace() {
+ return getType();
+ }
+
+ @Override
+ public BaseDescription getFragmentDeclaration() {
+ return fragmentDeclaration;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/GenericSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/GenericSpecificationImpl.java
new file mode 100644
index 0000000000..329936b878
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/GenericSpecificationImpl.java
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2021 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 Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+import org.osgi.resource.Namespace;
+
+class GenericSpecificationImpl extends VersionConstraintImpl implements GenericSpecification {
+ private Filter matchingFilter;
+ private String type = GenericDescription.DEFAULT_TYPE;
+ private int resolution = 0;
+ private GenericDescription[] suppliers;
+ private Map attributes;
+ private Map arbitraryDirectives;
+ /*
+ * Indicates that a generic constraint was from converting the BREE header
+ */
+ public static final int RESOLUTION_FROM_BREE = 0x04;
+
+ @Override
+ public String getMatchingFilter() {
+ synchronized (this.monitor) {
+ return matchingFilter == null ? null : matchingFilter.toString();
+ }
+ }
+
+ void setMatchingFilter(String matchingFilter, boolean matchName) throws InvalidSyntaxException {
+ synchronized (this.monitor) {
+ String name = getName();
+ if (matchName && name != null && !"*".equals(name)) { //$NON-NLS-1$
+ String nameFilter = "(" + getType() + "=" + getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ matchingFilter = matchingFilter == null ? nameFilter : "(&" + nameFilter + matchingFilter + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ this.matchingFilter = matchingFilter == null ? null : FrameworkUtil.createFilter(matchingFilter);
+ }
+ }
+
+ void setMatchingFilter(Filter matchingFilter) {
+ synchronized (this.monitor) {
+ this.matchingFilter = matchingFilter;
+ }
+ }
+
+ @Override
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ if (!(supplier instanceof GenericDescription))
+ return false;
+ GenericDescription candidate = (GenericDescription) supplier;
+ if (!getType().equals(candidate.getType()))
+ return false;
+ // Note that names and versions are only matched by including them in the filter
+ return matchingFilter == null || matchingFilter.match(candidate.getAttributes());
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Constants.REQUIRE_CAPABILITY).append(": ").append(getType()); //$NON-NLS-1$
+ if (matchingFilter != null)
+ sb.append("; filter=\"").append(getMatchingFilter()).append('"'); //$NON-NLS-1$
+ return sb.toString();
+ }
+
+ @Override
+ public String getType() {
+ synchronized (this.monitor) {
+ return type;
+ }
+ }
+
+ void setType(String type) {
+ synchronized (this.monitor) {
+ if (type == null || type.equals(GenericDescription.DEFAULT_TYPE))
+ this.type = GenericDescription.DEFAULT_TYPE;
+ else
+ this.type = type;
+ }
+ }
+
+ @Override
+ public int getResolution() {
+ synchronized (this.monitor) {
+ return resolution;
+ }
+ }
+
+ @Override
+ public boolean isResolved() {
+ synchronized (this.monitor) {
+ return suppliers != null && suppliers.length > 0;
+ }
+ }
+
+ void setResolution(int resolution) {
+ synchronized (this.monitor) {
+ this.resolution = resolution;
+ }
+ }
+
+ @Override
+ public BaseDescription getSupplier() {
+ synchronized (this.monitor) {
+ return suppliers == null || suppliers.length == 0 ? null : suppliers[0];
+ }
+ }
+
+ @Override
+ protected void setSupplier(BaseDescription supplier) {
+ synchronized (this.monitor) {
+ if (supplier == null) {
+ suppliers = null;
+ return;
+ }
+ int len = suppliers == null ? 0 : suppliers.length;
+ GenericDescription[] temp = new GenericDescription[len + 1];
+ if (suppliers != null)
+ System.arraycopy(suppliers, 0, temp, 0, len);
+ temp[len] = (GenericDescription) supplier;
+ suppliers = temp;
+ }
+ }
+
+ @Override
+ public GenericDescription[] getSuppliers() {
+ synchronized (this.monitor) {
+ return suppliers;
+ }
+ }
+
+ Map getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map) attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map) directives;
+ }
+ }
+
+ @Override
+ protected Map getInternalDirectives() {
+ Map result = new HashMap<>(2);
+ synchronized (this.monitor) {
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ if ((resolution & GenericSpecification.RESOLUTION_OPTIONAL) != 0)
+ result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL);
+ if ((resolution & GenericSpecification.RESOLUTION_MULTIPLE) != 0)
+ result.put(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE, Namespace.CARDINALITY_MULTIPLE);
+ if (matchingFilter != null) {
+ result.put(Constants.FILTER_DIRECTIVE, matchingFilter.toString());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ protected Map getInteralAttributes() {
+ synchronized (this.monitor) {
+ return attributes == null ? Collections.emptyMap() : new HashMap<>(attributes);
+ }
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return getType();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/HostSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/HostSpecificationImpl.java
new file mode 100644
index 0000000000..bd06c7821f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/HostSpecificationImpl.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2021 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 Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRevision;
+
+class HostSpecificationImpl extends VersionConstraintImpl implements HostSpecification {
+
+ private BundleDescription[] hosts;
+ private boolean multihost = false;
+ private Map attributes;
+ private Map arbitraryDirectives;
+
+ Map getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map) attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map) directives;
+ }
+ }
+
+ @Override
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ if (!(supplier instanceof BundleDescriptionImpl))
+ return false;
+ BundleDescriptionImpl candidate = (BundleDescriptionImpl) supplier;
+ if (candidate.getHost() != null)
+ return false;
+ Map requiredAttrs = getAttributes();
+ if (requiredAttrs != null) {
+ Map prividerAttrs = candidate.getAttributes();
+ if (prividerAttrs == null)
+ return false;
+ for (String key : requiredAttrs.keySet()) {
+ Object requiredValue = requiredAttrs.get(key);
+ Object prividedValue = prividerAttrs.get(key);
+ if (prividedValue == null || !requiredValue.equals(prividedValue))
+ return false;
+ }
+ }
+ String[] mandatory = (String[]) candidate.getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (!hasMandatoryAttributes(mandatory))
+ return false;
+ if (getName() != null && getName().equals(candidate.getSymbolicName()) && (getVersionRange() == null || getVersionRange().isIncluded(candidate.getVersion())))
+ return true;
+ return false;
+ }
+
+ @Override
+ public BundleDescription[] getHosts() {
+ synchronized (this.monitor) {
+ return hosts == null ? BundleDescriptionImpl.EMPTY_BUNDLEDESCS : hosts;
+ }
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ if (mandatory != null) {
+ Map requiredAttrs = getAttributes();
+ for (String key : mandatory) {
+ if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(key))
+ continue; // has a default value of 0.0.0
+ if (requiredAttrs == null || requiredAttrs.get(key) == null)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isResolved() {
+ synchronized (this.monitor) {
+ return hosts != null && hosts.length > 0;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Fragment-Host: " + getName() + "; bundle-version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ public BaseDescription getSupplier() {
+ synchronized (this.monitor) {
+ if (hosts == null || hosts.length == 0)
+ return null;
+ return hosts[0];
+ }
+ }
+
+ @Override
+ public boolean isMultiHost() {
+ synchronized (this.monitor) {
+ return multihost;
+ }
+ }
+
+ void setIsMultiHost(boolean multihost) {
+ synchronized (this.monitor) {
+ this.multihost = multihost;
+ }
+ }
+
+ @Override
+ protected Map getInternalDirectives() {
+ Map result = new HashMap<>(2);
+ synchronized (this.monitor) {
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ result.put(Constants.FILTER_DIRECTIVE, createFilterDirective());
+ return result;
+ }
+ }
+
+ private String createFilterDirective() {
+ StringBuilder filter = new StringBuilder();
+ filter.append("(&"); //$NON-NLS-1$
+ synchronized (this.monitor) {
+ addFilterAttribute(filter, BundleRevision.HOST_NAMESPACE, getName());
+ VersionRange range = getVersionRange();
+ if (range != null && range != VersionRange.emptyRange)
+ addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, range);
+ if (attributes != null)
+ addFilterAttributes(filter, attributes);
+ }
+ filter.append(')');
+ return filter.toString();
+ }
+
+ @Override
+ protected Map getInteralAttributes() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return BundleRevision.HOST_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/ImportPackageSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/ImportPackageSpecificationImpl.java
new file mode 100644
index 0000000000..84d48a78c7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/ImportPackageSpecificationImpl.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2021 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 Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRevision;
+
+class ImportPackageSpecificationImpl extends VersionConstraintImpl implements ImportPackageSpecification {
+ private String resolution = ImportPackageSpecification.RESOLUTION_STATIC; // the default is static
+ private String symbolicName;
+ private VersionRange bundleVersionRange;
+ private Map attributes;
+ private Map arbitraryDirectives;
+
+ @Override
+ public Map getDirectives() {
+ synchronized (this.monitor) {
+ Map result = new HashMap<>(5);
+ if (resolution != null)
+ result.put(Constants.RESOLUTION_DIRECTIVE, resolution);
+ return result;
+ }
+ }
+
+ @Override
+ public Object getDirective(String key) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.RESOLUTION_DIRECTIVE))
+ return resolution;
+ return null;
+ }
+ }
+
+ Object setDirective(String key, Object value) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.RESOLUTION_DIRECTIVE))
+ return resolution = (String) value;
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map) directives;
+ }
+ }
+
+ @Override
+ public String getBundleSymbolicName() {
+ synchronized (this.monitor) {
+ if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) {
+ return BundleDescriptionBuilder.NAME;
+ }
+ return symbolicName;
+ }
+ }
+
+ @Override
+ public VersionRange getBundleVersionRange() {
+ synchronized (this.monitor) {
+ if (bundleVersionRange == null)
+ return VersionRange.emptyRange;
+ return bundleVersionRange;
+ }
+ }
+
+ @Override
+ public Map getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @Override
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ return isSatisfiedBy(supplier, true);
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier, boolean checkEE) {
+ if (!(supplier instanceof ExportPackageDescription))
+ return false;
+ ExportPackageDescriptionImpl pkgDes = (ExportPackageDescriptionImpl) supplier;
+
+ // If we are in strict mode, check to see if the export specifies friends.
+ // If it does, are we one of the friends
+ String[] friends = (String[]) pkgDes.getDirective(BundleDescriptionBuilder.FRIENDS_DIRECTIVE);
+ Boolean internal = (Boolean) pkgDes.getDirective(BundleDescriptionBuilder.INTERNAL_DIRECTIVE);
+ if (internal.booleanValue() || friends != null) {
+ boolean strict = false;
+ if (strict) {
+ if (internal.booleanValue())
+ return false;
+ boolean found = false;
+ if (friends != null && getBundle().getSymbolicName() != null)
+ for (String friend : friends) {
+ if (getBundle().getSymbolicName().equals(friend)) {
+ found = true;
+ }
+ }
+ if (!found)
+ return false;
+ }
+ }
+ String exporterSymbolicName = getBundleSymbolicName();
+ if (exporterSymbolicName != null) {
+ BundleDescription exporter = pkgDes.getExporter();
+ if (!exporterSymbolicName.equals(exporter.getSymbolicName()))
+ return false;
+ if (getBundleVersionRange() != null && !getBundleVersionRange().isIncluded(exporter.getVersion()))
+ return false;
+ }
+
+ String name = getName();
+ // shortcut '*'
+ // NOTE: wildcards are supported only in cases where this is a dynamic import
+ if (!"*".equals(name) && !(name.endsWith(".*") && pkgDes.getName().startsWith(name.substring(0, name.length() - 1))) && !pkgDes.getName().equals(name)) //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ if (getVersionRange() != null && !getVersionRange().isIncluded(pkgDes.getVersion()))
+ return false;
+
+ Map importAttrs = getAttributes();
+ if (importAttrs != null) {
+ Map exportAttrs = pkgDes.getAttributes();
+ if (exportAttrs == null)
+ return false;
+ for (String importKey : importAttrs.keySet()) {
+ Object importValue = importAttrs.get(importKey);
+ Object exportValue = exportAttrs.get(importKey);
+ if (exportValue == null || !importValue.equals(exportValue))
+ return false;
+ }
+ }
+ String[] mandatory = (String[]) pkgDes.getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (!hasMandatoryAttributes(mandatory))
+ return false;
+ // finally check the ee index
+ if (!checkEE)
+ return true;
+ if (((BundleDescriptionImpl) getBundle()).getEquinoxEE() < 0)
+ return true;
+ int eeIndex = ((Integer) pkgDes.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue();
+ return eeIndex < 0 || eeIndex == ((BundleDescriptionImpl) getBundle()).getEquinoxEE();
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected boolean hasMandatoryAttributes(String[] checkMandatory) {
+ if (checkMandatory != null) {
+ Map importAttrs = getAttributes();
+ for (String mandatory : checkMandatory) {
+ if (Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE.equals(mandatory)) {
+ if (getBundleSymbolicName() == null)
+ return false;
+ } else if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(mandatory)) {
+ if (bundleVersionRange == null)
+ return false;
+ } else if (Constants.PACKAGE_SPECIFICATION_VERSION.equals(mandatory) || Constants.VERSION_ATTRIBUTE.equals(mandatory)) {
+ if (getVersionRange() == null)
+ return false;
+ } else { // arbitrary attribute
+ if (importAttrs == null)
+ return false;
+ if (importAttrs.get(mandatory) == null) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ protected void setBundleSymbolicName(String symbolicName) {
+ synchronized (this.monitor) {
+ this.symbolicName = symbolicName;
+ }
+ }
+
+ protected void setBundleVersionRange(VersionRange bundleVersionRange) {
+ synchronized (this.monitor) {
+ this.bundleVersionRange = bundleVersionRange;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void setAttributes(Map attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map) attributes;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Import-Package: " + getName() + "; version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ protected Map getInternalDirectives() {
+ synchronized (this.monitor) {
+ Map result = new HashMap<>(5);
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ if (resolution != null) {
+ if (ImportPackageSpecification.RESOLUTION_STATIC.equals(resolution))
+ result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_MANDATORY);
+ else
+ result.put(Constants.RESOLUTION_DIRECTIVE, resolution);
+ }
+ result.put(Constants.FILTER_DIRECTIVE, createFilterDirective());
+ return result;
+ }
+ }
+
+ private String createFilterDirective() {
+ StringBuilder filter = new StringBuilder();
+ filter.append("(&"); //$NON-NLS-1$
+ synchronized (this.monitor) {
+ addFilterAttribute(filter, BundleRevision.PACKAGE_NAMESPACE, getName(), false);
+ VersionRange range = getVersionRange();
+ if (range != null && range != VersionRange.emptyRange)
+ addFilterAttribute(filter, Constants.VERSION_ATTRIBUTE, range);
+ if (symbolicName != null)
+ addFilterAttribute(filter, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName);
+ if (bundleVersionRange != null)
+ addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersionRange);
+ if (attributes != null)
+ addFilterAttributes(filter, attributes);
+ }
+ filter.append(')');
+ return filter.toString();
+ }
+
+ @Override
+ protected Map getInteralAttributes() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return BundleRevision.PACKAGE_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/StateMessages.properties b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/StateMessages.properties
new file mode 100644
index 0000000000..6955d90636
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/StateMessages.properties
@@ -0,0 +1,24 @@
+###############################################################################
+# Copyright (c) 2004, 2018 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 Corporation - initial API and implementation
+###############################################################################
+
+#State/Resolver Messages for EN locale
+HEADER_REQUIRED=The \"{0}\" header must be specified
+HEADER_PACKAGE_DUPLICATES=Cannot import a package more than once \"{0}\"
+HEADER_PACKAGE_JAVA=Cannot specify java.* packages in Export headers \"{0}\"
+HEADER_VERSION_ERROR=The attributes \"{0}\" and \"{1}\" must match
+HEADER_EXPORT_ATTR_ERROR=Specifying \"{0}\" in the \"{1}\" header is not permitted
+HEADER_DIRECTIVE_DUPLICATES=Duplicate directives are not permitted \"{0}\"
+HEADER_ATTRIBUTE_DUPLICATES=Duplicate attributes are not permitted \"{0}\"
+HEADER_EXTENSION_ERROR=Extension bundle is not a fragment to the system bundle \"{0}\"
+MANIFEST_INVALID_HEADER_EXCEPTION=Invalid manifest header {0}: \"{1}\"
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/StateMsg.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/StateMsg.java
new file mode 100644
index 0000000000..90fddc9252
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/StateMsg.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 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 Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import org.eclipse.osgi.util.NLS;
+
+public class StateMsg extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.osgi.internal.resolver.StateMessages"; //$NON-NLS-1$
+
+ public static String HEADER_REQUIRED;
+ public static String HEADER_PACKAGE_DUPLICATES;
+ public static String HEADER_PACKAGE_JAVA;
+ public static String HEADER_VERSION_ERROR;
+ public static String HEADER_EXPORT_ATTR_ERROR;
+ public static String HEADER_DIRECTIVE_DUPLICATES;
+ public static String HEADER_ATTRIBUTE_DUPLICATES;
+ public static String HEADER_EXTENSION_ERROR;
+
+ static {
+ // initialize resource bundles
+ NLS.initializeMessages(BUNDLE_NAME, StateMsg.class);
+ }
+
+ public static String MANIFEST_INVALID_HEADER_EXCEPTION;
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/Tokenizer.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/Tokenizer.java
new file mode 100644
index 0000000000..f3e1d637f1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/Tokenizer.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2016 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 Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Simple tokenizer class. Used to parse data.
+ */
+class Tokenizer {
+ protected char value[];
+ protected int max;
+ protected int cursor;
+
+ public Tokenizer(String value) {
+ this.value = value.toCharArray();
+ max = this.value.length;
+ cursor = 0;
+ }
+
+ private void skipWhiteSpace() {
+ char[] val = value;
+ int cur = cursor;
+
+ for (; cur < max; cur++) {
+ char c = val[cur];
+ if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')) {
+ continue;
+ }
+ break;
+ }
+ cursor = cur;
+ }
+
+ public String getToken(String terminals) {
+ skipWhiteSpace();
+ char[] val = value;
+ int cur = cursor;
+
+ int begin = cur;
+ for (; cur < max; cur++) {
+ char c = val[cur];
+ if ((terminals.indexOf(c) != -1)) {
+ break;
+ }
+ }
+ cursor = cur;
+ int count = cur - begin;
+ if (count > 0) {
+ skipWhiteSpace();
+ while (count > 0 && (val[begin + count - 1] == ' ' || val[begin + count - 1] == '\t'))
+ count--;
+ return (new String(val, begin, count));
+ }
+ return (null);
+ }
+
+ public String getEscapedToken(String terminals) {
+ char[] val = value;
+ int cur = cursor;
+ if (cur >= max)
+ return null;
+ StringBuilder sb = new StringBuilder();
+ char c;
+ for (; cur < max; cur++) {
+ c = val[cur];
+ // this is an escaped char
+ if (c == '\\') {
+ cur++; // skip the escape char
+ if (cur == max)
+ break;
+ c = val[cur]; // include the escaped char
+ } else if (terminals.indexOf(c) != -1) {
+ break;
+ }
+ sb.append(c);
+ }
+
+ cursor = cur;
+ return sb.toString();
+ }
+
+ public List getEscapedTokens(String terminals) {
+ List result = new ArrayList<>();
+ for (String token = getEscapedToken(terminals); token != null; token = getEscapedToken(terminals)) {
+ result.add(token);
+ getChar(); // consume terminal
+ }
+ return result;
+ }
+
+ public char getChar() {
+ int cur = cursor;
+ if (cur < max) {
+ cursor = cur + 1;
+ return (value[cur]);
+ }
+ return ('\0'); /* end of value */
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/VersionConstraintImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/VersionConstraintImpl.java
new file mode 100644
index 0000000000..ccced4296f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/VersionConstraintImpl.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 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 Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription;
+
+import java.util.Collections;
+import java.util.Map;
+import org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription.BaseDescriptionImpl.BaseCapability;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.*;
+
+abstract class VersionConstraintImpl implements VersionConstraint {
+
+ protected final Object monitor = new Object();
+
+ private String name;
+ private VersionRange versionRange;
+ private BundleDescription bundle;
+ private BaseDescription supplier;
+ private volatile Object userObject;
+
+ @Override
+ public String getName() {
+ synchronized (this.monitor) {
+ if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(name)) {
+ return BundleDescriptionBuilder.NAME;
+ }
+ return name;
+ }
+ }
+
+ @Override
+ public VersionRange getVersionRange() {
+ synchronized (this.monitor) {
+ if (versionRange == null)
+ return VersionRange.emptyRange;
+ return versionRange;
+ }
+ }
+
+ @Override
+ public BundleDescription getBundle() {
+ synchronized (this.monitor) {
+ return bundle;
+ }
+ }
+
+ @Override
+ public boolean isResolved() {
+ synchronized (this.monitor) {
+ return supplier != null;
+ }
+ }
+
+ @Override
+ public BaseDescription getSupplier() {
+ synchronized (this.monitor) {
+ return supplier;
+ }
+ }
+
+ @Override
+ public boolean isSatisfiedBy(BaseDescription candidate) {
+ synchronized (this.monitor) {
+ return false;
+ }
+ }
+
+ protected void setName(String name) {
+ synchronized (this.monitor) {
+ this.name = name;
+ }
+ }
+
+ protected void setVersionRange(VersionRange versionRange) {
+ synchronized (this.monitor) {
+ this.versionRange = versionRange;
+ }
+ }
+
+ protected void setBundle(BundleDescription bundle) {
+ synchronized (this.monitor) {
+ this.bundle = bundle;
+ }
+ }
+
+ protected void setSupplier(BaseDescription supplier) {
+ synchronized (this.monitor) {
+ this.supplier = supplier;
+ }
+ }
+
+ protected abstract String getInternalNameSpace();
+
+ protected abstract Map getInternalDirectives();
+
+ protected abstract Map getInteralAttributes();
+
+ protected abstract boolean hasMandatoryAttributes(String[] mandatory);
+
+ @Override
+ public BundleRequirement getRequirement() {
+ String namespace = getInternalNameSpace();
+ if (namespace == null)
+ return null;
+ return new BundleRequirementImpl(namespace);
+ }
+
+ @Override
+ public Object getUserObject() {
+ return userObject;
+ }
+
+ @Override
+ public void setUserObject(Object userObject) {
+ this.userObject = userObject;
+ }
+
+ class BundleRequirementImpl implements BundleRequirement {
+ private final String namespace;
+
+ public BundleRequirementImpl(String namespace) {
+ this.namespace = namespace;
+ }
+
+ @Override
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @Override
+ public Map getDirectives() {
+ return Collections.unmodifiableMap(getInternalDirectives());
+ }
+
+ @Override
+ public Map getAttributes() {
+ return Collections.unmodifiableMap(getInteralAttributes());
+ }
+
+ @Override
+ public BundleRevision getRevision() {
+ return getBundle();
+ }
+
+ @Override
+ public boolean matches(BundleCapability capability) { // NO_UCD
+ return isSatisfiedBy(((BaseCapability) capability).getBaseDescription());
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(VersionConstraintImpl.this);
+ }
+
+ private VersionConstraintImpl getVersionConstraint() {
+ return VersionConstraintImpl.this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof BundleRequirementImpl))
+ return false;
+ return ((BundleRequirementImpl) obj).getVersionConstraint() == VersionConstraintImpl.this;
+ }
+
+ @Override
+ public String toString() {
+ return getNamespace() + BaseDescriptionImpl.toString(getAttributes(), false) + BaseDescriptionImpl.toString(getDirectives(), true);
+ }
+
+ @Override
+ public BundleRevision getResource() {
+ return getRevision();
+ }
+ }
+
+ static StringBuilder addFilterAttributes(StringBuilder filter, Map attributes) {
+ for (Map.Entry entry : attributes.entrySet()) {
+ addFilterAttribute(filter, entry.getKey(), entry.getValue());
+ }
+ return filter;
+ }
+
+ static StringBuilder addFilterAttribute(StringBuilder filter, String attr, Object value) {
+ return addFilterAttribute(filter, attr, value, true);
+ }
+
+ static StringBuilder addFilterAttribute(StringBuilder filter, String attr, Object value, boolean escapeWildCard) {
+ if (value instanceof VersionRange) {
+ VersionRange range = (VersionRange) value;
+ filter.append(range.toFilterString(attr));
+ } else {
+ filter.append('(').append(attr).append('=').append(escapeValue(value, escapeWildCard)).append(')');
+ }
+ return filter;
+ }
+
+ private static String escapeValue(Object o, boolean escapeWildCard) {
+ String value = o.toString();
+ boolean escaped = false;
+ int inlen = value.length();
+ int outlen = inlen << 1; /* inlen * 2 */
+
+ char[] output = new char[outlen];
+ value.getChars(0, inlen, output, inlen);
+
+ int cursor = 0;
+ for (int i = inlen; i < outlen; i++) {
+ char c = output[i];
+ switch (c) {
+ case '*' :
+ if (!escapeWildCard)
+ break;
+ case '\\' :
+ case '(' :
+ case ')' :
+ output[cursor] = '\\';
+ cursor++;
+ escaped = true;
+ break;
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return escaped ? new String(output, 0, cursor) : value;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java
index 04c7558e49..3efd6304b6 100644
--- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java
@@ -33,6 +33,7 @@
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.internal.p2.publisher.eclipse.bundledescription.BundleDescriptionBuilder;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitFragmentDescription;
@@ -766,12 +767,8 @@ public static Map> getHostLocalizations(File bundleL
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
+ BundleDescription descriptor = BundleDescriptionBuilder.createBundleDescription(enhancedManifest,
+ bundleLocation == null ? null : bundleLocation.getAbsolutePath());
descriptor.setUserObject(enhancedManifest);
return descriptor;
} catch (BundleException e) {