diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/.api_filters b/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/.api_filters new file mode 100644 index 0000000000..194ca406ab --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/.api_filters @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF index e2322da38e..c9c7f195b5 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF @@ -39,6 +39,7 @@ Import-Package: org.eclipse.equinox.app;version="[1.0.0,2.0.0)", org.eclipse.osgi.service.resolver;version="1.5.0", org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.3.0", + org.osgi.framework.namespace;version="[1.2.0,2.0.0]", org.osgi.framework.wiring;version="1.2.0", org.osgi.resource;version="1.0.0", org.osgi.service.application;version="1.1.0", diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BaseDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BaseDescriptionImpl.java new file mode 100644 index 0000000000..d8f0bfe634 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BaseDescriptionImpl.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * 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 java.util.Map.Entry; +import org.eclipse.osgi.service.resolver.BaseDescription; +import org.osgi.framework.Version; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.BundleRevision; + +abstract class BaseDescriptionImpl implements BaseDescription { + + protected final Object monitor = new Object(); + + private volatile String name; + + private volatile Version version; + + private volatile Object userObject; + + @Override + public String getName() { + return name; + } + + @Override + public Version getVersion() { + synchronized (this.monitor) { + if (version == null) + return Version.emptyVersion; + return version; + } + } + + protected void setName(String name) { + this.name = name; + } + + protected void setVersion(Version version) { + this.version = version; + } + + static String toString(Map map, boolean directives) { + if (map.size() == 0) + return ""; //$NON-NLS-1$ + String assignment = directives ? ":=" : "="; //$NON-NLS-1$//$NON-NLS-2$ + Set> set = map.entrySet(); + StringBuilder sb = new StringBuilder(); + for (Entry entry : set) { + sb.append("; "); //$NON-NLS-1$ + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof List) { + @SuppressWarnings("unchecked") + List list = (List) value; + if (list.size() == 0) + continue; + Object component = list.get(0); + String className = component.getClass().getName(); + String type = className.substring(className.lastIndexOf('.') + 1); + sb.append(key).append(':').append("List<").append(type).append(">").append(assignment).append('"'); //$NON-NLS-1$ //$NON-NLS-2$ + for (Object object : list) + sb.append(object).append(','); + sb.setLength(sb.length() - 1); + sb.append('"'); + } else { + String type = ""; //$NON-NLS-1$ + if (!(value instanceof String)) { + String className = value.getClass().getName(); + type = ":" + className.substring(className.lastIndexOf('.') + 1); //$NON-NLS-1$ + } + sb.append(key).append(type).append(assignment).append('"').append(value).append('"'); + } + } + return sb.toString(); + } + + String getInternalNameSpace() { + return null; + } + + public BaseDescription getFragmentDeclaration() { + return null; + } + + @Override + public BundleCapability getCapability() { + return getCapability(null); + } + + BundleCapability getCapability(String namespace) { + BaseDescriptionImpl fragmentDeclaration = (BaseDescriptionImpl) getFragmentDeclaration(); + if (fragmentDeclaration != null) + return fragmentDeclaration.getCapability(namespace); + if (namespace == null) + namespace = getInternalNameSpace(); + if (namespace == null) + return null; + return new BaseCapability(namespace); + } + + @Override + public Object getUserObject() { + return userObject; + } + + @Override + public void setUserObject(Object userObject) { + this.userObject = userObject; + } + + public class BaseCapability implements BundleCapability { + private final String namespace; + + public BaseCapability(String namespace) { + super(); + this.namespace = namespace; + } + + @Override + public BundleRevision getRevision() { + return getSupplier(); + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public Map getDirectives() { + return getDeclaredDirectives(); + } + + @Override + public Map getAttributes() { + Map attrs = getDeclaredAttributes(); + String internalName = BaseDescriptionImpl.this.getInternalNameSpace(); + if (namespace.equals(internalName)) + return attrs; + // we are doing an alias, must remove internal Name and add alias + attrs = new HashMap<>(attrs); + Object nameValue = attrs.remove(internalName); + if (nameValue != null) + attrs.put(namespace, nameValue); + return Collections.unmodifiableMap(attrs); + } + + @Override + public int hashCode() { + return System.identityHashCode(BaseDescriptionImpl.this); + } + + public BaseDescriptionImpl getBaseDescription() { + return BaseDescriptionImpl.this; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof BaseCapability)) + return false; + return (((BaseCapability) obj).getBaseDescription() == BaseDescriptionImpl.this) && namespace.equals(((BaseCapability) obj).getNamespace()); + } + + @Override + public String toString() { + return getNamespace() + BaseDescriptionImpl.toString(getAttributes(), false) + BaseDescriptionImpl.toString(getDirectives(), true); + } + + @Override + public BundleRevision getResource() { + return getRevision(); + } + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleDescriptionBuilder.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleDescriptionBuilder.java new file mode 100644 index 0000000000..c4289926ca --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/bundledescription/BundleDescriptionBuilder.java @@ -0,0 +1,944 @@ +/******************************************************************************* + * Copyright (c) 2003, 2020 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 + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.publisher.eclipse.bundledescription; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.*; +import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.eclipse.osgi.util.ManifestElement; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.*; +import org.osgi.framework.namespace.BundleNamespace; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.resource.Namespace; + +/** + * This class builds bundle description objects from manifests + */ +public class BundleDescriptionBuilder { + @SuppressWarnings("deprecation") + private static final String BUNDLE_REQUIREDEXECUTIONENVIRONMENT = Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT; + @SuppressWarnings("deprecation") + private static final String PACKAGE_SPECIFICATION_VERSION = Constants.PACKAGE_SPECIFICATION_VERSION; + private static final String[] DEFINED_EXPORT_PACKAGE_DIRECTIVES = {Constants.USES_DIRECTIVE, Constants.INCLUDE_DIRECTIVE, Constants.EXCLUDE_DIRECTIVE, BundleDescriptionBuilder.FRIENDS_DIRECTIVE, BundleDescriptionBuilder.INTERNAL_DIRECTIVE, Constants.MANDATORY_DIRECTIVE}; + private static final String[] DEFINED_IMPORT_PACKAGE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE}; + private static final String[] DEFINED_PACKAGE_MATCHING_ATTRS = { Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, + Constants.BUNDLE_VERSION_ATTRIBUTE, PACKAGE_SPECIFICATION_VERSION, Constants.VERSION_ATTRIBUTE }; + private static final String[] DEFINED_REQUIRE_BUNDLE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.VISIBILITY_DIRECTIVE}; + private static final String[] DEFINED_FRAGMENT_HOST_DIRECTIVES = {Constants.EXTENSION_DIRECTIVE}; + static final String[] DEFINED_BSN_DIRECTIVES = {Constants.SINGLETON_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.MANDATORY_DIRECTIVE}; + static final String[] DEFINED_BSN_MATCHING_ATTRS = {Constants.BUNDLE_VERSION_ATTRIBUTE, BundleDescriptionBuilder.OPTIONAL_ATTRIBUTE, BundleDescriptionBuilder.REPROVIDE_ATTRIBUTE}; + private static final String[] DEFINED_REQUIRE_CAPABILITY_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.FILTER_DIRECTIVE, Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE}; + private static final String[] DEFINED_REQUIRE_CAPABILITY_ATTRS = {}; + private static final String[] DEFINED_OSGI_VALIDATE_HEADERS = {Constants.IMPORT_PACKAGE, Constants.DYNAMICIMPORT_PACKAGE, Constants.EXPORT_PACKAGE, Constants.FRAGMENT_HOST, Constants.BUNDLE_SYMBOLICNAME, Constants.REQUIRE_BUNDLE}; + static final String GENERIC_REQUIRE = "Eclipse-GenericRequire"; //$NON-NLS-1$ + static final String GENERIC_CAPABILITY = "Eclipse-GenericCapability"; //$NON-NLS-1$ + + private static final String ATTR_TYPE_STRING = "string"; //$NON-NLS-1$ + private static final String ATTR_TYPE_VERSION = "version"; //$NON-NLS-1$ + private static final String ATTR_TYPE_URI = "uri"; //$NON-NLS-1$ + private static final String ATTR_TYPE_LONG = "long"; //$NON-NLS-1$ + private static final String ATTR_TYPE_DOUBLE = "double"; //$NON-NLS-1$ + private static final String ATTR_TYPE_SET = "set"; //$NON-NLS-1$ + private static final String ATTR_TYPE_LIST = "List"; //$NON-NLS-1$ + private static final String OPTIONAL_ATTR = "optional"; //$NON-NLS-1$ + private static final String MULTIPLE_ATTR = "multiple"; //$NON-NLS-1$ + private static final String TRUE = "true"; //$NON-NLS-1$ + + public static BundleDescriptionImpl createBundleDescription(Dictionary manifest, + String location) throws BundleException { + BundleDescriptionImpl result = new BundleDescriptionImpl(); + String manifestVersionHeader = manifest.get(Constants.BUNDLE_MANIFESTVERSION); + boolean jreBundle = "true".equals(manifest.get(BundleDescriptionBuilder.Eclipse_JREBUNDLE)); //$NON-NLS-1$ + int manifestVersion = 1; + if (manifestVersionHeader != null) + manifestVersion = Integer.parseInt(manifestVersionHeader); + if (manifestVersion >= 2) + validateHeaders(manifest, jreBundle); + + // retrieve the symbolic-name and the singleton status + String symbolicNameHeader = manifest.get(Constants.BUNDLE_SYMBOLICNAME); + if (symbolicNameHeader != null) { + ManifestElement[] symbolicNameElements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, symbolicNameHeader); + if (symbolicNameElements.length > 0) { + ManifestElement bsnElement = symbolicNameElements[0]; + result.setSymbolicName(bsnElement.getValue()); + String singleton = bsnElement.getDirective(Constants.SINGLETON_DIRECTIVE); + if (singleton == null) // TODO this is for backward compatibility; need to check manifest version < 2 to allow this after everyone has converted to new syntax + singleton = bsnElement.getAttribute(Constants.SINGLETON_DIRECTIVE); + result.setStateBit(BundleDescriptionImpl.SINGLETON, "true".equals(singleton)); //$NON-NLS-1$ + String fragmentAttachment = bsnElement.getDirective(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE); + if (fragmentAttachment != null) { + if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_RESOLVETIME)) { + result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, true); + result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false); + } else if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_NEVER)) { + result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, false); + result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false); + } + } + result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(bsnElement.getDirective(Constants.MANDATORY_DIRECTIVE))); + result.setAttributes(getAttributes(bsnElement, DEFINED_BSN_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(bsnElement, DEFINED_BSN_DIRECTIVES)); + } + } + // retrieve other headers + String version = manifest.get(Constants.BUNDLE_VERSION); + try { + result.setVersion((version != null) ? Version.parseVersion(version) : Version.emptyVersion); + } catch (IllegalArgumentException ex) { + if (manifestVersion >= 2) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.BUNDLE_VERSION, version); + throw new BundleException(message + " : " + ex.getMessage(), BundleException.MANIFEST_ERROR, ex); //$NON-NLS-1$ + } + // prior to R4 the Bundle-Version header was not interpreted by the Framework; + // must not fail for old R3 style bundles + } + result.setLocation(location); + result.setPlatformFilter(manifest.get(BundleDescriptionBuilder.ECLIPSE_PLATFORMFILTER)); + String[] brees = ManifestElement.getArrayFromList(manifest.get(BUNDLE_REQUIREDEXECUTIONENVIRONMENT)); + result.setExecutionEnvironments(brees); + ManifestElement[] host = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, manifest.get(Constants.FRAGMENT_HOST)); + if (host != null) + result.setHost(createHostSpecification(host[0])); + ManifestElement[] exports = ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, manifest.get(Constants.EXPORT_PACKAGE)); + ManifestElement[] provides = ManifestElement.parseHeader(BundleDescriptionBuilder.PROVIDE_PACKAGE, manifest.get(BundleDescriptionBuilder.PROVIDE_PACKAGE)); + boolean strict = false; + List providedExports = new ArrayList<>(provides == null ? 0 : provides.length); + result.setExportPackages(createExportPackages(exports, provides, providedExports, strict)); + ManifestElement[] imports = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, manifest.get(Constants.IMPORT_PACKAGE)); + ManifestElement[] dynamicImports = ManifestElement.parseHeader(Constants.DYNAMICIMPORT_PACKAGE, manifest.get(Constants.DYNAMICIMPORT_PACKAGE)); + result.setImportPackages(createImportPackages(result.getExportPackages(), providedExports, imports, dynamicImports, manifestVersion)); + ManifestElement[] requires = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, manifest.get(Constants.REQUIRE_BUNDLE)); + result.setRequiredBundles(createRequiredBundles(requires)); + String[][] genericAliases = getGenericAliases(); + ManifestElement[] genericRequires = getGenericRequires(manifest, genericAliases); + ManifestElement[] osgiRequires = ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, manifest.get(Constants.REQUIRE_CAPABILITY)); + result.setGenericRequires(createGenericRequires(genericRequires, osgiRequires, brees)); + ManifestElement[] genericCapabilities = getGenericCapabilities(manifest, genericAliases); + ManifestElement[] osgiCapabilities = ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, manifest.get(Constants.PROVIDE_CAPABILITY)); + result.setGenericCapabilities(createGenericCapabilities(genericCapabilities, osgiCapabilities, result)); + result.setBundleId(1); + return result; + } + + private static ManifestElement[] getGenericRequires(Dictionary manifest, String[][] genericAliases) throws BundleException { + ManifestElement[] genericRequires = ManifestElement.parseHeader(GENERIC_REQUIRE, manifest.get(GENERIC_REQUIRE)); + List aliasList = null; + if (genericAliases.length > 0) { + aliasList = new ArrayList<>(genericRequires == null ? 0 : genericRequires.length); + for (String[] genericAlias : genericAliases) { + ManifestElement[] aliasReqs = ManifestElement.parseHeader(genericAlias[1], manifest.get(genericAlias[1])); + if (aliasReqs == null) + continue; + for (ManifestElement aliasReq : aliasReqs) { + StringBuilder strBuf = new StringBuilder(); + strBuf.append(aliasReq.getValue()).append(':').append(genericAlias[2]); + String filter = aliasReq.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE); + if (filter != null) + strBuf.append("; ").append(Constants.SELECTION_FILTER_ATTRIBUTE).append(filter).append("=\"").append(filter).append("\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ManifestElement[] withType = ManifestElement.parseHeader(genericAlias[1], strBuf.toString()); + aliasList.add(withType[0]); + } + } + } + if (aliasList == null || aliasList.size() == 0) + return genericRequires; + if (genericRequires != null) + Collections.addAll(aliasList, genericRequires); + return aliasList.toArray(new ManifestElement[aliasList.size()]); + } + + private static ManifestElement[] getGenericCapabilities(Dictionary manifest, String[][] genericAliases) throws BundleException { + ManifestElement[] genericCapabilities = ManifestElement.parseHeader(GENERIC_CAPABILITY, manifest.get(GENERIC_CAPABILITY)); + List aliasList = null; + if (genericAliases.length > 0) { + aliasList = new ArrayList<>(genericCapabilities == null ? 0 : genericCapabilities.length); + for (String[] genericAlias : genericAliases) { + ManifestElement[] aliasCapabilities = ManifestElement.parseHeader(genericAlias[0], manifest.get(genericAlias[0])); + if (aliasCapabilities == null) + continue; + for (ManifestElement aliasCapability : aliasCapabilities) { + StringBuilder strBuf = new StringBuilder(); + strBuf.append(aliasCapability.getValue()).append(':').append(genericAlias[2]); + for (Enumeration keys = aliasCapability.getKeys(); keys != null && keys.hasMoreElements();) { + String key = keys.nextElement(); + strBuf.append("; ").append(key).append("=\"").append(aliasCapability.getAttribute(key)).append("\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + ManifestElement[] withTypes = ManifestElement.parseHeader(genericAlias[0], strBuf.toString()); + aliasList.add(withTypes[0]); + } + } + } + if (aliasList == null || aliasList.size() == 0) + return genericCapabilities; + if (genericCapabilities != null) + Collections.addAll(aliasList, genericCapabilities); + return aliasList.toArray(new ManifestElement[aliasList.size()]); + } + + private static String[][] getGenericAliases() { + return new String[0][0]; + } + + private static void validateHeaders(Dictionary manifest, boolean jreBundle) throws BundleException { + for (String definedOSGiValidateHeader : DEFINED_OSGI_VALIDATE_HEADERS) { + String header = manifest.get(definedOSGiValidateHeader); + if (header != null) { + ManifestElement[] elements = ManifestElement.parseHeader(definedOSGiValidateHeader, header); + checkForDuplicateDirectivesAttributes(definedOSGiValidateHeader, elements); + if (definedOSGiValidateHeader == Constants.IMPORT_PACKAGE) { + checkImportExportSyntax(definedOSGiValidateHeader, elements, false, false, jreBundle); + } + if (definedOSGiValidateHeader == Constants.DYNAMICIMPORT_PACKAGE) { + checkImportExportSyntax(definedOSGiValidateHeader, elements, false, true, jreBundle); + } + if (definedOSGiValidateHeader == Constants.EXPORT_PACKAGE) { + checkImportExportSyntax(definedOSGiValidateHeader, elements, true, false, jreBundle); + } + if (definedOSGiValidateHeader == Constants.FRAGMENT_HOST) { + checkExtensionBundle(definedOSGiValidateHeader, elements); + } + } else if (definedOSGiValidateHeader == Constants.BUNDLE_SYMBOLICNAME) { + throw new BundleException(NLS.bind(StateMsg.HEADER_REQUIRED, Constants.BUNDLE_SYMBOLICNAME), BundleException.MANIFEST_ERROR); + } + } + } + + private static BundleSpecification[] createRequiredBundles(ManifestElement[] specs) { + if (specs == null) + return null; + BundleSpecification[] result = new BundleSpecification[specs.length]; + for (int i = 0; i < specs.length; i++) + result[i] = createRequiredBundle(specs[i]); + return result; + } + + static BundleSpecification createRequiredBundle(ManifestElement spec) { + BundleSpecificationImpl result = new BundleSpecificationImpl(); + result.setName(spec.getValue()); + result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE))); + result.setExported(Constants.VISIBILITY_REEXPORT.equals(spec.getDirective(Constants.VISIBILITY_DIRECTIVE)) || "true".equals(spec.getAttribute(BundleDescriptionBuilder.REPROVIDE_ATTRIBUTE))); //$NON-NLS-1$ + result.setOptional(Constants.RESOLUTION_OPTIONAL.equals(spec.getDirective(Constants.RESOLUTION_DIRECTIVE)) || "true".equals(spec.getAttribute(BundleDescriptionBuilder.OPTIONAL_ATTRIBUTE))); //$NON-NLS-1$ + result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(spec, DEFINED_REQUIRE_BUNDLE_DIRECTIVES)); + return result; + } + + private static ImportPackageSpecification[] createImportPackages(ExportPackageDescription[] exported, List providedExports, ManifestElement[] imported, ManifestElement[] dynamicImported, int manifestVersion) { + List allImports = null; + if (manifestVersion < 2) { + // add implicit imports for each exported package if manifest verions is less than 2. + if (exported.length == 0 && imported == null && dynamicImported == null) + return null; + allImports = new ArrayList<>(exported.length + (imported == null ? 0 : imported.length)); + for (ExportPackageDescription exportDescription : exported) { + if (providedExports.contains(exportDescription.getName())) { + continue; + } + ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl(); + result.setName(exportDescription.getName()); + result.setVersionRange(getVersionRange(exportDescription.getVersion().toString())); + result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_STATIC); + allImports.add(result); + } + } else { + allImports = new ArrayList<>(imported == null ? 0 : imported.length); + } + + // add dynamics first so they will get overriden by static imports if + // the same package is dyanamically imported and statically imported. + if (dynamicImported != null) + for (ManifestElement dynamicImport : dynamicImported) { + addImportPackages(dynamicImport, allImports, manifestVersion, true); + } + if (imported != null) + for (ManifestElement pkgImport : imported) { + addImportPackages(pkgImport, allImports, manifestVersion, false); + } + return allImports.toArray(new ImportPackageSpecification[allImports.size()]); + } + + public static void addImportPackages(ManifestElement importPackage, List allImports, int manifestVersion, boolean dynamic) { + String[] importNames = importPackage.getValueComponents(); + for (String importName : importNames) { + // do not allow for multiple imports of same package of manifest version < 2 + if (manifestVersion < 2) { + Iterator iter = allImports.iterator(); + while (iter.hasNext()) { + if (importName.equals(iter.next().getName())) { + iter.remove(); + } + } + } + ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl(); + result.setName(importName); + // set common attributes for both dynamic and static imports + String versionString = importPackage.getAttribute(Constants.VERSION_ATTRIBUTE); + if (versionString == null) // specification-version aliases to version + versionString = importPackage.getAttribute(PACKAGE_SPECIFICATION_VERSION); + result.setVersionRange(getVersionRange(versionString)); + result.setBundleSymbolicName(importPackage.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)); + result.setBundleVersionRange(getVersionRange(importPackage.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE))); + // only set the matching attributes if manifest version >= 2 + if (manifestVersion >= 2) + result.setAttributes(getAttributes(importPackage, DEFINED_PACKAGE_MATCHING_ATTRS)); + if (dynamic) + result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_DYNAMIC); + else + result.setDirective(Constants.RESOLUTION_DIRECTIVE, getResolution(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))); + result.setArbitraryDirectives(getDirectives(importPackage, DEFINED_IMPORT_PACKAGE_DIRECTIVES)); + allImports.add(result); + } + } + + private static String getResolution(String resolution) { + String result = ImportPackageSpecification.RESOLUTION_STATIC; + if (Constants.RESOLUTION_OPTIONAL.equals(resolution) || ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(resolution)) + result = resolution; + return result; + } + + static ExportPackageDescription[] createExportPackages(ManifestElement[] exported, ManifestElement[] provides, List providedExports, boolean strict) { + int numExports = (exported == null ? 0 : exported.length) + (provides == null ? 0 : provides.length); + if (numExports == 0) + return null; + List allExports = new ArrayList<>(numExports); + if (exported != null) + for (ManifestElement packageExport : exported) { + addExportPackages(packageExport, allExports, strict); + } + if (provides != null) + addProvidePackages(provides, allExports, providedExports); + return allExports.toArray(new ExportPackageDescription[allExports.size()]); + } + + static void addExportPackages(ManifestElement exportPackage, List allExports, boolean strict) { + String[] exportNames = exportPackage.getValueComponents(); + for (String exportName : exportNames) { + // if we are in strict mode and the package is marked as internal, skip it. + if (strict && "true".equals(exportPackage.getDirective(BundleDescriptionBuilder.INTERNAL_DIRECTIVE))) //$NON-NLS-1$ + continue; + ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl(); + result.setName(exportName); + String versionString = exportPackage.getAttribute(Constants.VERSION_ATTRIBUTE); + if (versionString == null) // specification-version aliases to version + versionString = exportPackage.getAttribute(PACKAGE_SPECIFICATION_VERSION); + if (versionString != null) + result.setVersion(Version.parseVersion(versionString)); + result.setDirective(Constants.USES_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.USES_DIRECTIVE))); + result.setDirective(Constants.INCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.INCLUDE_DIRECTIVE)); + result.setDirective(Constants.EXCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.EXCLUDE_DIRECTIVE)); + result.setDirective(BundleDescriptionBuilder.FRIENDS_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(BundleDescriptionBuilder.FRIENDS_DIRECTIVE))); + result.setDirective(BundleDescriptionBuilder.INTERNAL_DIRECTIVE, Boolean.valueOf(exportPackage.getDirective(BundleDescriptionBuilder.INTERNAL_DIRECTIVE))); + result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.MANDATORY_DIRECTIVE))); + result.setAttributes(getAttributes(exportPackage, DEFINED_PACKAGE_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(exportPackage, DEFINED_EXPORT_PACKAGE_DIRECTIVES)); + allExports.add(result); + } + } + + private static void addProvidePackages(ManifestElement[] provides, List allExports, List providedExports) { + ExportPackageDescription[] currentExports = allExports.toArray(new ExportPackageDescription[allExports.size()]); + for (ManifestElement provide : provides) { + boolean duplicate = false; + for (ExportPackageDescription currentExport : currentExports) { + if (provide.getValue().equals(currentExport.getName())) { + duplicate = true; + break; + } + } + if (!duplicate) { + ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl(); + result.setName(provide.getValue()); + allExports.add(result); + } + providedExports.add(provide.getValue()); + } + } + + static Map getDirectives(ManifestElement element, String[] definedDirectives) { + Enumeration keys = element.getDirectiveKeys(); + if (keys == null) + return null; + Map arbitraryDirectives = null; + keyloop: while (keys.hasMoreElements()) { + String key = keys.nextElement(); + for (String definedDirective : definedDirectives) { + if (definedDirective.equals(key)) + continue keyloop; + } + if (arbitraryDirectives == null) + arbitraryDirectives = new HashMap<>(); + arbitraryDirectives.put(key, element.getDirective(key)); + } + return arbitraryDirectives; + } + + static Map getAttributes(ManifestElement element, String[] definedAttrs) { + Enumeration keys = element.getKeys(); + Map arbitraryAttrs = null; + if (keys == null) + return null; + while (keys.hasMoreElements()) { + boolean definedAttr = false; + String key = keys.nextElement(); + for (String attr : definedAttrs) { + if (attr.equals(key)) { + definedAttr = true; + break; + } + } + String value = element.getAttribute(key); + int colonIndex = key.indexOf(':'); + String type = ATTR_TYPE_STRING; + if (colonIndex > 0) { + type = key.substring(colonIndex + 1).trim(); + key = key.substring(0, colonIndex).trim(); + } + if (!definedAttr) { + if (arbitraryAttrs == null) + arbitraryAttrs = new HashMap<>(); + arbitraryAttrs.put(key, convertValue(type, value)); + } + } + return arbitraryAttrs; + } + + private static Object convertValue(String type, String value) { + + if (ATTR_TYPE_STRING.equalsIgnoreCase(type)) + return value; + + String trimmed = value.trim(); + if (ATTR_TYPE_DOUBLE.equalsIgnoreCase(type)) + return Double.valueOf(trimmed); + else if (ATTR_TYPE_LONG.equalsIgnoreCase(type)) + return Long.valueOf(trimmed); + else if (ATTR_TYPE_URI.equalsIgnoreCase(type)) + try { + Class uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$ + Constructor constructor = uriClazz.getConstructor(new Class[] {String.class}); + return constructor.newInstance(new Object[] {trimmed}); + } catch (ClassNotFoundException e) { + // oh well cannot support; just use string + return value; + } catch (RuntimeException e) { // got some reflection exception + throw e; + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + else if (ATTR_TYPE_VERSION.equalsIgnoreCase(type)) + return new Version(trimmed); + else if (ATTR_TYPE_SET.equalsIgnoreCase(type)) + return ManifestElement.getArrayFromList(trimmed, ","); //$NON-NLS-1$ + + // assume list type, anything else will throw an exception + Tokenizer listTokenizer = new Tokenizer(type); + String listType = listTokenizer.getToken("<"); //$NON-NLS-1$ + if (!ATTR_TYPE_LIST.equalsIgnoreCase(listType)) + throw new RuntimeException("Unsupported type: " + type); //$NON-NLS-1$ + char c = listTokenizer.getChar(); + String componentType = ATTR_TYPE_STRING; + if (c == '<') { + componentType = listTokenizer.getToken(">"); //$NON-NLS-1$ + if (listTokenizer.getChar() != '>') + throw new RuntimeException("Invalid type, missing ending '>' : " + type); //$NON-NLS-1$ + } + List tokens = new Tokenizer(value).getEscapedTokens(","); //$NON-NLS-1$ + List components = new ArrayList<>(); + for (String component : tokens) { + components.add(convertValue(componentType, component)); + } + return components; + } + + static HostSpecification createHostSpecification(ManifestElement spec) { + if (spec == null) + return null; + HostSpecificationImpl result = new HostSpecificationImpl(); + result.setName(spec.getValue()); + result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE))); + String multiple = spec.getDirective("multiple-hosts"); //$NON-NLS-1$ + result.setIsMultiHost("true".equals(multiple)); //$NON-NLS-1$ + result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(spec, DEFINED_FRAGMENT_HOST_DIRECTIVES)); + return result; + } + + private static GenericSpecification[] createGenericRequires(ManifestElement[] equinoxRequires, ManifestElement[] osgiRequires, String[] brees) throws BundleException { + List result = createEquinoxRequires(equinoxRequires); + result = createOSGiRequires(osgiRequires, result); + result = convertBREEs(brees, result); + return result == null ? null : result.toArray(new GenericSpecification[result.size()]); + } + + static List convertBREEs(String[] brees, List result) throws BundleException { + if (brees == null || brees.length == 0) + return result; + if (result == null) + result = new ArrayList<>(brees.length); + List breeFilters = new ArrayList<>(); + for (String bree : brees) + breeFilters.add(createOSGiEERequirementFilter(bree)); + String filterSpec; + if (breeFilters.size() == 1) { + filterSpec = breeFilters.get(0); + } else { + StringBuilder filterBuf = new StringBuilder("(|"); //$NON-NLS-1$ + for (String breeFilter : breeFilters) { + filterBuf.append(breeFilter); + } + filterSpec = filterBuf.append(")").toString(); //$NON-NLS-1$ + } + GenericSpecificationImpl spec = new GenericSpecificationImpl(); + spec.setResolution(GenericSpecificationImpl.RESOLUTION_FROM_BREE); + spec.setType(BundleDescriptionBuilder.OSGI_EE_NAMESPACE); + try { + Filter filter = FrameworkUtil.createFilter(filterSpec); + spec.setMatchingFilter(filter); + String name = getPrimaryKeyValue(filter, spec.getType()); + if (name != null) + spec.setName(name); + } catch (InvalidSyntaxException e) { + throw new BundleException("Error converting required execution environment.", e); //$NON-NLS-1$ + } + result.add(spec); + return result; + } + + private static String getPrimaryKeyValue(Filter filter, String key) { + try { + Method method = filter.getClass().getMethod("getPrimaryKeyValue", String.class); //$NON-NLS-1$ + return (String) method.invoke(filter, key); + } catch (Exception e) { + return null; + } + } + + private static String createOSGiEERequirementFilter(String bree) throws BundleException { + String[] nameVersion = getOSGiEENameVersion(bree); + String eeName = nameVersion[0]; + String v = nameVersion[1]; + String filterSpec; + if (v == null) + filterSpec = "(osgi.ee=" + eeName + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + else + filterSpec = "(&(osgi.ee=" + eeName + ")(version=" + v + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + try { + // do a sanity check + FrameworkUtil.createFilter(filterSpec); + } catch (InvalidSyntaxException e) { + filterSpec = "(osgi.ee=" + bree + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + try { + // do another sanity check + FrameworkUtil.createFilter(filterSpec); + } catch (InvalidSyntaxException e1) { + throw new BundleException("Error converting required execution environment.", e1); //$NON-NLS-1$ + } + } + return filterSpec; + } + + static String[] getOSGiEENameVersion(String bree) { + String ee1 = null; + String ee2 = null; + String v1 = null; + String v2 = null; + int separator = bree.indexOf('/'); + if (separator <= 0 || separator == bree.length() - 1) { + ee1 = bree; + } else { + ee1 = bree.substring(0, separator); + ee2 = bree.substring(separator + 1); + } + int v1idx = ee1.indexOf('-'); + if (v1idx > 0 && v1idx < ee1.length() - 1) { + // check for > 0 to avoid EEs starting with - + // check for < len - 1 to avoid ending with - + try { + v1 = ee1.substring(v1idx + 1); + // sanity check version format + Version.parseVersion(v1); + ee1 = ee1.substring(0, v1idx); + } catch (IllegalArgumentException e) { + v1 = null; + } + } + + int v2idx = ee2 == null ? -1 : ee2.indexOf('-'); + if (v2idx > 0 && v2idx < ee2.length() - 1) { + // check for > 0 to avoid EEs starting with - + // check for < len - 1 to avoid ending with - + try { + v2 = ee2.substring(v2idx + 1); + Version.parseVersion(v2); + ee2 = ee2.substring(0, v2idx); + } catch (IllegalArgumentException e) { + v2 = null; + } + } + + if (v1 == null) + v1 = v2; + if (v1 != null && v2 != null && !v1.equals(v2)) { + ee1 = bree; + ee2 = null; + v1 = null; + v2 = null; + } + if ("J2SE".equals(ee1)) //$NON-NLS-1$ + ee1 = "JavaSE"; //$NON-NLS-1$ + if ("J2SE".equals(ee2)) //$NON-NLS-1$ + ee2 = "JavaSE"; //$NON-NLS-1$ + + String eeName = ee1 + (ee2 == null ? "" : '/' + ee2); //$NON-NLS-1$ + + return new String[] {eeName, v1}; + } + + static List createOSGiRequires(ManifestElement[] osgiRequires, List result) throws BundleException { + if (osgiRequires == null) + return result; + if (result == null) + result = new ArrayList<>(); + for (ManifestElement element : osgiRequires) { + String[] namespaces = element.getValueComponents(); + for (String namespace : namespaces) { + GenericSpecificationImpl spec = new GenericSpecificationImpl(); + spec.setType(namespace); + String filterSpec = element.getDirective(Constants.FILTER_DIRECTIVE); + if (filterSpec != null) { + try { + Filter filter = FrameworkUtil.createFilter(filterSpec); + spec.setMatchingFilter(filter); + String name = getPrimaryKeyValue(filter, namespace); + if (name != null) + spec.setName(name); + } catch (InvalidSyntaxException e) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.REQUIRE_CAPABILITY, element.toString()); + throw new BundleException(message + " : filter", BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$ + } + } + String resolutionDirective = element.getDirective(Constants.RESOLUTION_DIRECTIVE); + int resolution = 0; + if (Constants.RESOLUTION_OPTIONAL.equals(resolutionDirective)) + resolution |= GenericSpecification.RESOLUTION_OPTIONAL; + String cardinality = element.getDirective(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE); + if (Namespace.CARDINALITY_MULTIPLE.equals(cardinality)) + resolution |= GenericSpecification.RESOLUTION_MULTIPLE; + spec.setResolution(resolution); + spec.setAttributes(getAttributes(element, DEFINED_REQUIRE_CAPABILITY_ATTRS)); + spec.setArbitraryDirectives(getDirectives(element, DEFINED_REQUIRE_CAPABILITY_DIRECTIVES)); + result.add(spec); + } + } + return result; + } + + private static List createEquinoxRequires(ManifestElement[] equinoxRequires) throws BundleException { + if (equinoxRequires == null) + return null; + ArrayList results = new ArrayList<>(equinoxRequires.length); + for (ManifestElement equinoxRequire : equinoxRequires) { + String[] genericNames = equinoxRequire.getValueComponents(); + for (String genericName : genericNames) { + GenericSpecificationImpl spec = new GenericSpecificationImpl(); + int colonIdx = genericName.indexOf(':'); + if (colonIdx > 0) { + spec.setName(genericName.substring(0, colonIdx)); + spec.setType(genericName.substring(colonIdx + 1)); + } else { + spec.setName(genericName); + } + try { + spec.setMatchingFilter(equinoxRequire.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE), true); + } catch (InvalidSyntaxException e) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, GENERIC_REQUIRE, equinoxRequire.toString()); + throw new BundleException(message + " : " + Constants.SELECTION_FILTER_ATTRIBUTE, BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$ + } + String optional = equinoxRequire.getAttribute(OPTIONAL_ATTR); + String multiple = equinoxRequire.getAttribute(MULTIPLE_ATTR); + int resolution = 0; + if (TRUE.equals(optional)) + resolution |= GenericSpecification.RESOLUTION_OPTIONAL; + if (TRUE.equals(multiple)) + resolution |= GenericSpecification.RESOLUTION_MULTIPLE; + spec.setResolution(resolution); + results.add(spec); + } + } + return results; + } + + private static GenericDescription[] createGenericCapabilities(ManifestElement[] equinoxCapabilities, ManifestElement[] osgiCapabilities, BundleDescription description) throws BundleException { + List result = createEquinoxCapabilities(equinoxCapabilities); + result = createOSGiCapabilities(osgiCapabilities, result, description); + return result == null ? null : result.toArray(new GenericDescription[result.size()]); + } + + static List createOSGiCapabilities(ManifestElement[] osgiCapabilities, List result, BundleDescription description) throws BundleException { + if (result == null) + result = new ArrayList<>(osgiCapabilities == null ? 1 : osgiCapabilities.length + 1); + // Always have an osgi.identity capability if there is a symbolic name. + GenericDescription osgiIdentity = createOsgiIdentityCapability(description); + if (osgiIdentity != null) + // always add the capability to the front + result.add(0, osgiIdentity); + return createOSGiCapabilities(osgiCapabilities, result, (Integer) null); + } + + static List createOSGiCapabilities(ManifestElement[] osgiCapabilities, List result, Integer profileIndex) throws BundleException { + if (osgiCapabilities == null) + return result; + if (result == null) + result = new ArrayList<>(osgiCapabilities.length); + + for (ManifestElement element : osgiCapabilities) { + String[] namespaces = element.getValueComponents(); + for (String namespace : namespaces) { + if (IdentityNamespace.IDENTITY_NAMESPACE.equals(namespace)) + throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$ + + GenericDescriptionImpl desc = new GenericDescriptionImpl(); + desc.setType(namespace); + Map mapAttrs = getAttributes(element, new String[0]); + if (profileIndex != null) + mapAttrs.put(ExportPackageDescriptionImpl.EQUINOX_EE, profileIndex); + Dictionary attrs = mapAttrs == null ? new Hashtable<>() : new Hashtable<>(mapAttrs); + desc.setAttributes(attrs); + Map directives = new HashMap<>(); + Enumeration keys = element.getDirectiveKeys(); + if (keys != null) + for (keys = element.getDirectiveKeys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + directives.put(key, element.getDirective(key)); + } + desc.setDirectives(directives); + result.add(desc); + } + } + return result; + } + + private static List createEquinoxCapabilities(ManifestElement[] equinoxCapabilities) throws BundleException { + if (equinoxCapabilities == null) + return null; + ArrayList results = new ArrayList<>(equinoxCapabilities.length); + for (ManifestElement equinoxCapability : equinoxCapabilities) { + String[] genericNames = equinoxCapability.getValueComponents(); + for (String genericName : genericNames) { + GenericDescriptionImpl desc = new GenericDescriptionImpl(); + String name = genericName; + int colonIdx = genericName.indexOf(':'); + if (colonIdx > 0) { + name = genericName.substring(0, colonIdx); + desc.setType(genericName.substring(colonIdx + 1)); + if (IdentityNamespace.IDENTITY_NAMESPACE.equals(desc.getType())) + throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$ + } + Map mapAttrs = getAttributes(equinoxCapability, new String[] {Constants.VERSION_ATTRIBUTE}); + Dictionary attrs = mapAttrs == null ? new Hashtable<>() : new Hashtable<>(mapAttrs); + attrs.put(desc.getType(), name); + String versionString = equinoxCapability.getAttribute(Constants.VERSION_ATTRIBUTE); + if (versionString != null) + attrs.put(Constants.VERSION_ATTRIBUTE, Version.parseVersion(versionString)); + desc.setAttributes(attrs); + results.add(desc); + } + } + return results; + } + + private static VersionRange getVersionRange(String versionRange) { + if (versionRange == null) + return null; + return new VersionRange(versionRange); + } + + public static void checkImportExportSyntax(String headerKey, ManifestElement[] elements, boolean export, boolean dynamic, boolean jreBundle) throws BundleException { + if (elements == null) + return; + int length = elements.length; + Set packages = new HashSet<>(length); + for (int i = 0; i < length; i++) { + // check for duplicate imports + String[] packageNames = elements[i].getValueComponents(); + for (String packageName : packageNames) { + if (!export && !dynamic && packages.contains(packageName)) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_DUPLICATES, packageName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + // check for java.* + if (export && !jreBundle && packageName.startsWith("java.")) { //$NON-NLS-1$ + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_JAVA, packageName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + packages.add(packageName); + } + // check for version/specification version mismatch + String version = elements[i].getAttribute(Constants.VERSION_ATTRIBUTE); + if (version != null) { + String specVersion = elements[i].getAttribute(PACKAGE_SPECIFICATION_VERSION); + if (specVersion != null && !specVersion.equals(version)) + throw new BundleException(NLS.bind(StateMsg.HEADER_VERSION_ERROR, Constants.VERSION_ATTRIBUTE, + PACKAGE_SPECIFICATION_VERSION), BundleException.MANIFEST_ERROR); + } + // check for bundle-symbolic-name and bundle-verion attibures + // (failure) + if (export) { + if (elements[i].getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE) != null) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + if (elements[i].getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) != null) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + } + } + + private static void checkForDuplicateDirectivesAttributes(String headerKey, ManifestElement[] elements) throws BundleException { + // check for duplicate directives + for (ManifestElement element : elements) { + Enumeration directiveKeys = element.getDirectiveKeys(); + if (directiveKeys != null) { + while (directiveKeys.hasMoreElements()) { + String key = directiveKeys.nextElement(); + String[] directives = element.getDirectives(key); + if (directives.length > 1) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString()); + throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_DIRECTIVE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + } + Enumeration attrKeys = element.getKeys(); + if (attrKeys != null) { + while (attrKeys.hasMoreElements()) { + String key = attrKeys.nextElement(); + String[] attrs = element.getAttributes(key); + if (attrs.length > 1) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_ATTRIBUTE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + } + } + } + + private static void checkExtensionBundle(String headerKey, ManifestElement[] elements) throws BundleException { + if (elements.length == 0 || elements[0].getDirective(Constants.EXTENSION_DIRECTIVE) == null) + return; + String hostName = elements[0].getValue(); + // XXX: The extension bundle check is done against system.bundle and org.eclipse.osgi + if (!hostName.equals(Constants.SYSTEM_BUNDLE_SYMBOLICNAME) && !hostName.equals(BundleDescriptionBuilder.NAME)) { + String message = NLS.bind(StateMsg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[0].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXTENSION_ERROR, hostName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + + static GenericDescription createOsgiIdentityCapability(BundleDescription description) { + if (description.getSymbolicName() == null) + return null; + GenericDescriptionImpl result = new GenericDescriptionImpl(); + result.setType(IdentityNamespace.IDENTITY_NAMESPACE); + Dictionary attributes = new Hashtable<>(description.getDeclaredAttributes()); + // remove osgi.wiring.bundle and bundle-version attributes + attributes.remove(BundleNamespace.BUNDLE_NAMESPACE); + attributes.remove(Constants.BUNDLE_VERSION_ATTRIBUTE); + attributes.put(IdentityNamespace.IDENTITY_NAMESPACE, description.getSymbolicName()); + attributes.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, description.getHost() == null ? IdentityNamespace.TYPE_BUNDLE : IdentityNamespace.TYPE_FRAGMENT); + attributes.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, description.getVersion()); + result.setAttributes(attributes); + Map 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 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 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 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) {