Copyright (c) 2012, 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
/******************************************************************************* * Copyright (c) 2012, 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 *******************************************************************************/
package org.eclipse.osgi.container.builders; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.osgi.container.ModuleRevisionBuilder; import org.eclipse.osgi.container.namespaces.EclipsePlatformNamespace; import org.eclipse.osgi.container.namespaces.EquinoxFragmentNamespace; import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace; import org.eclipse.osgi.internal.framework.EquinoxContainer; import org.eclipse.osgi.internal.framework.FilterImpl; import org.eclipse.osgi.internal.messages.Msg; import org.eclipse.osgi.internal.util.Tokenizer; import org.eclipse.osgi.storage.NativeCodeFinder; import org.eclipse.osgi.util.ManifestElement; import org.eclipse.osgi.util.NLS; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.Version; import org.osgi.framework.VersionRange; import org.osgi.framework.namespace.BundleNamespace; import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; import org.osgi.framework.namespace.HostNamespace; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.framework.namespace.NativeNamespace; import org.osgi.framework.namespace.PackageNamespace; import org.osgi.framework.wiring.BundleRevision; import org.osgi.resource.Namespace;
A factory for creating ModuleRevisionBuilders based on OSGi bundle manifests.
Since:3.10
@noinstantiateThis class is not intended to be instantiated by clients.
/** * A factory for creating {@link ModuleRevisionBuilder}s based on OSGi bundle manifests. * @since 3.10 * @noinstantiate This class is not intended to be instantiated by clients. */
public final class OSGiManifestBuilderFactory { 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 ATTR_OLD_REPRIVIDE = "reprovide"; //$NON-NLS-1$ private static final String HEADER_OLD_PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$ 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}; private static final Collection<String> SYSTEM_CAPABILITIES = Collections.unmodifiableCollection(Arrays.asList(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, NativeNamespace.NATIVE_NAMESPACE)); private static final Collection<String> PROHIBITED_CAPABILITIES = Collections.unmodifiableCollection(Arrays.asList(IdentityNamespace.IDENTITY_NAMESPACE));
Creates a builder for the specified bundle manifest
Params:
  • manifest – the bundle manifest
Throws:
Returns:a builder for the specified bundle manifest
/** * Creates a builder for the specified bundle manifest * @param manifest the bundle manifest * @return a builder for the specified bundle manifest * @throws BundleException if the bundle manifest is invalid */
public static ModuleRevisionBuilder createBuilder(Map<String, String> manifest) throws BundleException { return createBuilder(manifest, null, null, null); }
Creates a builder for the specified bundle manifest. An alias can be supplied for the symbolic name. Also extra package exports and extra provided capabilities may be specified outside of the supplied manifest. This is useful for creating a builder for the system module which takes into account the configuration properties Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA and Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA.
Params:
  • manifest – the bundle manifest
  • symbolicNameAlias – the symbolic name alias. A null value is allowed.
  • extraExports – the extra package exports. A null value is allowed.
  • extraCapabilities – the extra provided capabilities. A null value is allowed.
Throws:
Returns:a builder for the specified bundle manifest
/** * Creates a builder for the specified bundle manifest. An alias can be supplied * for the symbolic name. Also extra package exports and extra provided capabilities * may be specified outside of the supplied manifest. This is useful for creating * a builder for the system module which takes into account the configuration * properties {@link Constants#FRAMEWORK_SYSTEMPACKAGES_EXTRA} and * {@link Constants#FRAMEWORK_SYSTEMCAPABILITIES_EXTRA}. * @param manifest the bundle manifest * @param symbolicNameAlias the symbolic name alias. A <code>null</code> value is allowed. * @param extraExports the extra package exports. A <code>null</code> value is allowed. * @param extraCapabilities the extra provided capabilities. A <code>null</code> value is allowed. * @return a builder for the specified bundle manifest * @throws BundleException if the bundle manifest is invalid */
public static ModuleRevisionBuilder createBuilder(Map<String, String> manifest, String symbolicNameAlias, String extraExports, String extraCapabilities) throws BundleException { ModuleRevisionBuilder builder = new ModuleRevisionBuilder(); int manifestVersion = getManifestVersion(manifest); if (manifestVersion >= 2) { validateHeaders(manifest); } Object symbolicName = getSymbolicNameAndVersion(builder, manifest, symbolicNameAlias, manifestVersion); Collection<Map<String, Object>> exportedPackages = new ArrayList<>(); getPackageExports(builder, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, manifest.get(Constants.EXPORT_PACKAGE)), symbolicName, exportedPackages); getPackageExports(builder, ManifestElement.parseHeader(HEADER_OLD_PROVIDE_PACKAGE, manifest.get(HEADER_OLD_PROVIDE_PACKAGE)), symbolicName, exportedPackages); if (extraExports != null && !extraExports.isEmpty()) { getPackageExports(builder, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, extraExports), symbolicName, exportedPackages); } getPackageImports(builder, manifest, exportedPackages, manifestVersion); getRequireBundle(builder, ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, manifest.get(Constants.REQUIRE_BUNDLE))); getProvideCapabilities(builder, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, manifest.get(Constants.PROVIDE_CAPABILITY)), extraCapabilities == null); if (extraCapabilities != null && !extraCapabilities.isEmpty()) { getProvideCapabilities(builder, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, extraCapabilities), false); } getRequireCapabilities(builder, ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, manifest.get(Constants.REQUIRE_CAPABILITY))); addRequireEclipsePlatform(builder, manifest); getEquinoxDataCapability(builder, manifest); getFragmentHost(builder, ManifestElement.parseHeader(Constants.FRAGMENT_HOST, manifest.get(Constants.FRAGMENT_HOST))); convertBREEs(builder, manifest); getNativeCode(builder, manifest); return builder; } private static void validateHeaders(Map<String, String> manifest) 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); } if (definedOSGiValidateHeader == Constants.DYNAMICIMPORT_PACKAGE) { checkImportExportSyntax(definedOSGiValidateHeader, elements, false, true); } if (definedOSGiValidateHeader == Constants.EXPORT_PACKAGE) { checkImportExportSyntax(definedOSGiValidateHeader, elements, true, false); } if (definedOSGiValidateHeader == Constants.FRAGMENT_HOST) { checkExtensionBundle(definedOSGiValidateHeader, elements, manifest); } } else if (definedOSGiValidateHeader == Constants.BUNDLE_SYMBOLICNAME) { throw new BundleException(Constants.BUNDLE_SYMBOLICNAME + " header is required.", BundleException.MANIFEST_ERROR); //$NON-NLS-1$ } } } @SuppressWarnings("deprecation") private static void checkImportExportSyntax(String headerKey, ManifestElement[] elements, boolean export, boolean dynamic) throws BundleException { if (elements == null) return; int length = elements.length; Set<String> 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(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); throw new BundleException(message + " : " + NLS.bind(Msg.HEADER_PACKAGE_DUPLICATES, packageName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ } // check for java.* if (export && packageName.startsWith("java.")) { //$NON-NLS-1$ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); throw new BundleException(message + " : " + NLS.bind(Msg.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(Constants.PACKAGE_SPECIFICATION_VERSION); if (specVersion != null && !specVersion.equals(version)) throw new BundleException(NLS.bind(Msg.HEADER_VERSION_ERROR, Constants.VERSION_ATTRIBUTE, Constants.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(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); throw new BundleException(message + " : " + NLS.bind(Msg.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(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); throw new BundleException(NLS.bind(message + " : " + Msg.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<String> 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(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString()); throw new BundleException(NLS.bind(message + " : " + Msg.HEADER_DIRECTIVE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ } } } Enumeration<String> 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(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString()); throw new BundleException(message + " : " + NLS.bind(Msg.HEADER_ATTRIBUTE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ } } } } } private static void checkExtensionBundle(String headerKey, ManifestElement[] elements, Map<String, String> manifest) throws BundleException { if (elements.length == 0) 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(EquinoxContainer.NAME)) { if (elements[0].getDirective(Constants.EXTENSION_DIRECTIVE) != null) { String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[0].toString()); throw new BundleException(message + " : " + NLS.bind(Msg.HEADER_EXTENSION_ERROR, hostName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ } } else { if (manifest.get(Constants.REQUIRE_BUNDLE) != null) throw new BundleException(Msg.OSGiManifestBuilderFactory_ExtensionReqBundleError, BundleException.MANIFEST_ERROR); if (manifest.get(Constants.BUNDLE_NATIVECODE) != null) throw new BundleException(Msg.OSGiManifestBuilderFactory_ExtensionNativeError, BundleException.MANIFEST_ERROR); } } private static int getManifestVersion(Map<String, String> manifest) { String manifestVersionHeader = manifest.get(Constants.BUNDLE_MANIFESTVERSION); return manifestVersionHeader == null ? 1 : Integer.parseInt(manifestVersionHeader); } private static Object getSymbolicNameAndVersion(ModuleRevisionBuilder builder, Map<String, String> manifest, String symbolicNameAlias, int manifestVersion) throws BundleException { boolean isFragment = manifest.get(Constants.FRAGMENT_HOST) != null; builder.setTypes(isFragment ? BundleRevision.TYPE_FRAGMENT : 0); String version = manifest.get(Constants.BUNDLE_VERSION); try { builder.setVersion((version != null) ? Version.parseVersion(version) : Version.emptyVersion); } catch (IllegalArgumentException ex) { if (manifestVersion >= 2) { String message = NLS.bind(Msg.OSGiManifestBuilderFactory_InvalidManifestError, Constants.BUNDLE_VERSION, version); throw new BundleException(message, BundleException.MANIFEST_ERROR, ex); } // prior to R4 the Bundle-Version header was not interpreted by the Framework; // must not fail for old R3 style bundles } Object symbolicName = null; 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]; builder.setSymbolicName(bsnElement.getValue()); if (symbolicNameAlias != null) { List<String> result = new ArrayList<>(); result.add(builder.getSymbolicName()); result.add(symbolicNameAlias); symbolicName = result; } else { symbolicName = builder.getSymbolicName(); } Map<String, String> directives = getDirectives(bsnElement); directives.remove(BundleNamespace.CAPABILITY_USES_DIRECTIVE); directives.remove(BundleNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE); Map<String, Object> attributes = getAttributes(bsnElement); if (!directives.containsKey(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE)) { // previous versions of equinox treated the singleton attribute as a directive Object singletonAttr = attributes.get(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE); if ("true".equals(singletonAttr)) { //$NON-NLS-1$ directives.put(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE, (String) singletonAttr); } } if (!isFragment) { // create the bundle namespace Map<String, Object> bundleAttributes = new HashMap<>(attributes); bundleAttributes.put(BundleNamespace.BUNDLE_NAMESPACE, symbolicName); bundleAttributes.put(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, builder.getVersion()); builder.addCapability(BundleNamespace.BUNDLE_NAMESPACE, directives, bundleAttributes); // create the host namespace // only if the directive is not never if (!HostNamespace.FRAGMENT_ATTACHMENT_NEVER.equals(directives.get(HostNamespace.CAPABILITY_FRAGMENT_ATTACHMENT_DIRECTIVE))) { Map<String, Object> hostAttributes = new HashMap<>(attributes); hostAttributes.put(HostNamespace.HOST_NAMESPACE, symbolicName); hostAttributes.put(HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, builder.getVersion()); builder.addCapability(HostNamespace.HOST_NAMESPACE, directives, hostAttributes); } } // every bundle that has a symbolic name gets an identity; // never use the symbolic name alias for the identity namespace Map<String, Object> identityAttributes = new HashMap<>(attributes); identityAttributes.put(IdentityNamespace.IDENTITY_NAMESPACE, builder.getSymbolicName()); identityAttributes.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, builder.getVersion()); identityAttributes.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, isFragment ? IdentityNamespace.TYPE_FRAGMENT : IdentityNamespace.TYPE_BUNDLE); builder.addCapability(IdentityNamespace.IDENTITY_NAMESPACE, directives, identityAttributes); } } return symbolicName == null ? symbolicNameAlias : symbolicName; } private static void getPackageExports(ModuleRevisionBuilder builder, ManifestElement[] exportElements, Object symbolicName, Collection<Map<String, Object>> exportedPackages) throws BundleException { if (exportElements == null) return; for (ManifestElement exportElement : exportElements) { String[] packageNames = exportElement.getValueComponents(); Map<String, Object> attributes = getAttributes(exportElement); Map<String, String> directives = getDirectives(exportElement); directives.remove(PackageNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE); String versionAttr = (String) attributes.remove(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE); @SuppressWarnings("deprecation") String specVersionAttr = (String) attributes.remove(Constants.PACKAGE_SPECIFICATION_VERSION); Version version = versionAttr == null ? (specVersionAttr == null ? Version.emptyVersion : Version.parseVersion(specVersionAttr)) : Version.parseVersion(versionAttr); attributes.put(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, version); if (symbolicName != null) { attributes.put(PackageNamespace.CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName); } attributes.put(PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, builder.getVersion()); for (String packageName : packageNames) { Map<String, Object> packageAttrs = new HashMap<>(attributes); packageAttrs.put(PackageNamespace.PACKAGE_NAMESPACE, packageName); builder.addCapability(PackageNamespace.PACKAGE_NAMESPACE, directives, packageAttrs); exportedPackages.add(packageAttrs); } } } private static void getPackageImports(ModuleRevisionBuilder builder, Map<String, String> manifest, Collection<Map<String, Object>> exportedPackages, int manifestVersion) throws BundleException { Collection<String> importPackageNames = new ArrayList<>(); ManifestElement[] importElements = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, manifest.get(Constants.IMPORT_PACKAGE)); ManifestElement[] dynamicImportElements = ManifestElement.parseHeader(Constants.DYNAMICIMPORT_PACKAGE, manifest.get(Constants.DYNAMICIMPORT_PACKAGE)); addPackageImports(builder, importElements, importPackageNames, false); addPackageImports(builder, dynamicImportElements, importPackageNames, true); if (manifestVersion < 2) addImplicitImports(builder, exportedPackages, importPackageNames); } private static void addPackageImports(ModuleRevisionBuilder builder, ManifestElement[] importElements, Collection<String> importPackageNames, boolean dynamic) throws BundleException { if (importElements == null) return; for (ManifestElement importElement : importElements) { String[] packageNames = importElement.getValueComponents(); Map<String, Object> attributes = getAttributes(importElement); Map<String, String> directives = getDirectives(importElement); directives.remove(PackageNamespace.REQUIREMENT_EFFECTIVE_DIRECTIVE); directives.remove(PackageNamespace.REQUIREMENT_CARDINALITY_DIRECTIVE); if (dynamic) { directives.put(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE, PackageNamespace.RESOLUTION_DYNAMIC); } String versionRangeAttr = (String) attributes.remove(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE); @SuppressWarnings("deprecation") String specVersionRangeAttr = (String) attributes.remove(Constants.PACKAGE_SPECIFICATION_VERSION); VersionRange versionRange = versionRangeAttr == null ? (specVersionRangeAttr == null ? null : new VersionRange(specVersionRangeAttr)) : new VersionRange(versionRangeAttr); String bundleVersionRangeAttr = (String) attributes.remove(PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); VersionRange bundleVersionRange = bundleVersionRangeAttr == null ? null : new VersionRange(bundleVersionRangeAttr); // the attribute "optional" used to be used in old versions of equinox to specify optional imports // preserving behavior for compatibility Object optionalAttr = attributes.remove(Namespace.RESOLUTION_OPTIONAL); for (String packageName : packageNames) { if (!dynamic) { importPackageNames.add(packageName); } // fill in the filter directive based on the attributes Map<String, String> packageDirectives = new HashMap<>(directives); StringBuilder filter = new StringBuilder(); filter.append('(').append(PackageNamespace.PACKAGE_NAMESPACE).append('=').append(packageName).append(')'); int size = filter.length(); for (Map.Entry<String, Object> attribute : attributes.entrySet()) filter.append('(').append(attribute.getKey()).append('=').append(attribute.getValue()).append(')'); if (versionRange != null) filter.append(versionRange.toFilterString(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE)); if (bundleVersionRange != null) filter.append(bundleVersionRange.toFilterString(PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE)); if (size != filter.length()) // need to add (&...) filter.insert(0, "(&").append(')'); //$NON-NLS-1$ packageDirectives.put(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString()); // fill in cardinality for dynamic wild cards if (dynamic && packageName.indexOf('*') >= 0) packageDirectives.put(PackageNamespace.REQUIREMENT_CARDINALITY_DIRECTIVE, PackageNamespace.CARDINALITY_MULTIPLE); // check the old optional attribute if ("true".equals(optionalAttr) && packageDirectives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE) == null) { //$NON-NLS-1$ packageDirectives.put(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE, Namespace.RESOLUTION_OPTIONAL); } builder.addRequirement(PackageNamespace.PACKAGE_NAMESPACE, packageDirectives, new HashMap<String, Object>(0)); } } } private static void addImplicitImports(ModuleRevisionBuilder builder, Collection<Map<String, Object>> exportedPackages, Collection<String> importPackageNames) { for (Map<String, Object> exportAttributes : exportedPackages) { String packageName = (String) exportAttributes.get(PackageNamespace.PACKAGE_NAMESPACE); if (importPackageNames.contains(packageName)) continue; importPackageNames.add(packageName); Version packageVersion = (Version) exportAttributes.get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE); StringBuilder filter = new StringBuilder(); filter.append("(&(").append(PackageNamespace.PACKAGE_NAMESPACE).append('=').append(packageName).append(')'); //$NON-NLS-1$ filter.append('(').append(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE).append(">=").append(packageVersion).append("))"); //$NON-NLS-1$//$NON-NLS-2$ Map<String, String> directives = new HashMap<>(1); directives.put(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString()); builder.addRequirement(PackageNamespace.PACKAGE_NAMESPACE, directives, new HashMap<String, Object>(0)); } } private static Map<String, String> getDirectives(ManifestElement element) { Map<String, String> directives = new HashMap<>(); Enumeration<String> keys = element.getDirectiveKeys(); if (keys == null) return directives; while (keys.hasMoreElements()) { String key = keys.nextElement(); directives.put(key, element.getDirective(key)); } return directives; } private static void getRequireBundle(ModuleRevisionBuilder builder, ManifestElement[] requireBundles) throws BundleException { if (requireBundles == null) return; for (ManifestElement requireElement : requireBundles) { String[] bundleNames = requireElement.getValueComponents(); Map<String, Object> attributes = getAttributes(requireElement); Map<String, String> directives = getDirectives(requireElement); directives.remove(BundleNamespace.REQUIREMENT_CARDINALITY_DIRECTIVE); directives.remove(BundleNamespace.REQUIREMENT_EFFECTIVE_DIRECTIVE); String versionRangeAttr = (String) attributes.remove(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); VersionRange versionRange = versionRangeAttr == null ? null : new VersionRange(versionRangeAttr); // These two attrs are used as directives in previous versions of equinox // Preserving behavior for compatibility reasons. Object optionalAttr = attributes.remove(Namespace.RESOLUTION_OPTIONAL); Object reprovideAttr = attributes.remove(ATTR_OLD_REPRIVIDE); for (String bundleName : bundleNames) { if (bundleName.equals(builder.getSymbolicName())) { // ignore requirements to ourself continue; } // fill in the filter directive based on the attributes Map<String, String> bundleDirectives = new HashMap<>(directives); StringBuilder filter = new StringBuilder(); filter.append('(').append(BundleNamespace.BUNDLE_NAMESPACE).append('=').append(bundleName).append(')'); int size = filter.length(); for (Map.Entry<String, Object> attribute : attributes.entrySet()) filter.append('(').append(attribute.getKey()).append('=').append(attribute.getValue()).append(')'); if (versionRange != null) filter.append(versionRange.toFilterString(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE)); if (size != filter.length()) // need to add (&...) filter.insert(0, "(&").append(')'); //$NON-NLS-1$ bundleDirectives.put(BundleNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString()); // check the old compatibility attributes if ("true".equals(optionalAttr) && bundleDirectives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE) == null) { //$NON-NLS-1$ bundleDirectives.put(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE, Namespace.RESOLUTION_OPTIONAL); } if ("true".equals(reprovideAttr) && bundleDirectives.get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE) == null) { //$NON-NLS-1$ bundleDirectives.put(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE, BundleNamespace.VISIBILITY_REEXPORT); } builder.addRequirement(BundleNamespace.BUNDLE_NAMESPACE, bundleDirectives, new HashMap<String, Object>(0)); } } } private static void getFragmentHost(ModuleRevisionBuilder builder, ManifestElement[] fragmentHosts) throws BundleException { if (fragmentHosts == null || fragmentHosts.length == 0) return; ManifestElement fragmentHost = fragmentHosts[0]; String hostName = fragmentHost.getValue(); Map<String, Object> attributes = getAttributes(fragmentHost); Map<String, String> directives = getDirectives(fragmentHost); directives.remove(HostNamespace.REQUIREMENT_CARDINALITY_DIRECTIVE); directives.remove(HostNamespace.REQUIREMENT_EFFECTIVE_DIRECTIVE); String versionRangeAttr = (String) attributes.remove(HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); VersionRange versionRange = versionRangeAttr == null ? null : new VersionRange(versionRangeAttr); // fill in the filter directive based on the attributes StringBuilder filter = new StringBuilder(); filter.append('(').append(HostNamespace.HOST_NAMESPACE).append('=').append(hostName).append(')'); int size = filter.length(); for (Map.Entry<String, Object> attribute : attributes.entrySet()) filter.append('(').append(attribute.getKey()).append('=').append(attribute.getValue()).append(')'); if (versionRange != null) filter.append(versionRange.toFilterString(HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE)); if (size != filter.length()) // need to add (&...) filter.insert(0, "(&").append(')'); //$NON-NLS-1$ directives.put(BundleNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString()); builder.addRequirement(HostNamespace.HOST_NAMESPACE, directives, new HashMap<String, Object>(0)); // Add a fragment capability to advertise what host this resource is providing a fragment for directives = Collections.singletonMap(EquinoxModuleDataNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE, EquinoxModuleDataNamespace.EFFECTIVE_INFORMATION); builder.addCapability(EquinoxFragmentNamespace.FRAGMENT_NAMESPACE, directives, Collections.<String, Object> singletonMap(EquinoxFragmentNamespace.FRAGMENT_NAMESPACE, hostName)); } private static void getProvideCapabilities(ModuleRevisionBuilder builder, ManifestElement[] provideElements, boolean checkSystemCapabilities) throws BundleException { if (provideElements == null) return; for (ManifestElement provideElement : provideElements) { String[] namespaces = provideElement.getValueComponents(); Map<String, Object> attributes = getAttributes(provideElement); Map<String, String> directives = getDirectives(provideElement); for (String namespace : namespaces) { if (PROHIBITED_CAPABILITIES.contains(namespace) || (checkSystemCapabilities && SYSTEM_CAPABILITIES.contains(namespace))) { throw new BundleException("A bundle is not allowed to define a capability in the " + namespace + " name space.", BundleException.MANIFEST_ERROR); //$NON-NLS-1$ //$NON-NLS-2$ } builder.addCapability(namespace, directives, attributes); } } } private static void getRequireCapabilities(ModuleRevisionBuilder builder, ManifestElement[] requireElements) throws BundleException { if (requireElements == null) return; for (ManifestElement requireElement : requireElements) { String[] namespaces = requireElement.getValueComponents(); Map<String, Object> attributes = getAttributes(requireElement); Map<String, String> directives = getDirectives(requireElement); for (String namespace : namespaces) { builder.addRequirement(namespace, directives, attributes); } } } private static void addRequireEclipsePlatform(ModuleRevisionBuilder builder, Map<String, String> manifest) { String platformFilter = manifest.get(EclipsePlatformNamespace.ECLIPSE_PLATFORM_FILTER_HEADER); if (platformFilter == null) { return; } // only support one HashMap<String, String> directives = new HashMap<>(); directives.put(EclipsePlatformNamespace.REQUIREMENT_FILTER_DIRECTIVE, platformFilter); builder.addRequirement(EclipsePlatformNamespace.ECLIPSE_PLATFORM_NAMESPACE, directives, Collections.<String, Object> emptyMap()); } @SuppressWarnings("deprecation") private static void getEquinoxDataCapability(ModuleRevisionBuilder builder, Map<String, String> manifest) throws BundleException { Map<String, Object> attributes = new HashMap<>(); // Get the activation policy attributes ManifestElement[] policyElements = ManifestElement.parseHeader(Constants.BUNDLE_ACTIVATIONPOLICY, manifest.get(Constants.BUNDLE_ACTIVATIONPOLICY)); if (policyElements != null) { ManifestElement policy = policyElements[0]; String policyName = policy.getValue(); if (EquinoxModuleDataNamespace.CAPABILITY_ACTIVATION_POLICY_LAZY.equals(policyName)) { attributes.put(EquinoxModuleDataNamespace.CAPABILITY_ACTIVATION_POLICY, policyName); String includeSpec = policy.getDirective(Constants.INCLUDE_DIRECTIVE); if (includeSpec != null) { attributes.put(EquinoxModuleDataNamespace.CAPABILITY_LAZY_INCLUDE_ATTRIBUTE, convertValueWithNoWhitespace("List<String>", includeSpec)); //$NON-NLS-1$ } String excludeSpec = policy.getDirective(Constants.EXCLUDE_DIRECTIVE); if (excludeSpec != null) { attributes.put(EquinoxModuleDataNamespace.CAPABILITY_LAZY_EXCLUDE_ATTRIBUTE, convertValueWithNoWhitespace("List<String>", excludeSpec)); //$NON-NLS-1$ } } } else { policyElements = ManifestElement.parseHeader(EquinoxModuleDataNamespace.LAZYSTART_HEADER, manifest.get(EquinoxModuleDataNamespace.LAZYSTART_HEADER)); if (policyElements == null) { policyElements = ManifestElement.parseHeader(EquinoxModuleDataNamespace.AUTOSTART_HEADER, manifest.get(EquinoxModuleDataNamespace.AUTOSTART_HEADER)); } if (policyElements != null) { ManifestElement policy = policyElements[0]; String excludeSpec = policy.getAttribute(EquinoxModuleDataNamespace.LAZYSTART_EXCEPTIONS_ATTRIBUTE); if ("true".equals(policy.getValue())) { //$NON-NLS-1$ attributes.put(EquinoxModuleDataNamespace.CAPABILITY_ACTIVATION_POLICY, EquinoxModuleDataNamespace.CAPABILITY_ACTIVATION_POLICY_LAZY); if (excludeSpec != null) { attributes.put(EquinoxModuleDataNamespace.CAPABILITY_LAZY_EXCLUDE_ATTRIBUTE, convertValueWithNoWhitespace("List<String>", excludeSpec)); //$NON-NLS-1$ } } else { // NOTICE - the exclude list gets converted to an include list when the header is not true if (excludeSpec != null) { attributes.put(EquinoxModuleDataNamespace.CAPABILITY_ACTIVATION_POLICY, EquinoxModuleDataNamespace.CAPABILITY_ACTIVATION_POLICY_LAZY); attributes.put(EquinoxModuleDataNamespace.CAPABILITY_LAZY_INCLUDE_ATTRIBUTE, convertValueWithNoWhitespace("List<String>", excludeSpec)); //$NON-NLS-1$ } } } } // Get the activator String activator = manifest.get(Constants.BUNDLE_ACTIVATOR); if (activator == null && manifest.get(Constants.FRAGMENT_HOST) != null) { // we look for the extension activator for fragments // probably should do this only for framework extensions, but there is no harm to check for others // it is only acted upon for framework extension fragments activator = manifest.get(Constants.EXTENSION_BUNDLE_ACTIVATOR); } if (activator != null) { attributes.put(EquinoxModuleDataNamespace.CAPABILITY_ACTIVATOR, activator); } // Get the class path ManifestElement[] classpathElements = ManifestElement.parseHeader(Constants.BUNDLE_CLASSPATH, manifest.get(Constants.BUNDLE_CLASSPATH)); if (classpathElements != null) { List<String> classpath = new ArrayList<>(); for (ManifestElement element : classpathElements) { String[] components = element.getValueComponents(); Collections.addAll(classpath, components); } attributes.put(EquinoxModuleDataNamespace.CAPABILITY_CLASSPATH, classpath); } // Get the buddy policy list ManifestElement[] buddyPolicies = ManifestElement.parseHeader(EquinoxModuleDataNamespace.BUDDY_POLICY_HEADER, manifest.get(EquinoxModuleDataNamespace.BUDDY_POLICY_HEADER)); if (buddyPolicies != null) { List<String> policies = new ArrayList<>(); for (ManifestElement element : buddyPolicies) { Collections.addAll(policies, element.getValueComponents()); } attributes.put(EquinoxModuleDataNamespace.CAPABILITY_BUDDY_POLICY, policies); } // Get the registered buddy list ManifestElement[] registeredBuddies = ManifestElement.parseHeader(EquinoxModuleDataNamespace.REGISTERED_BUDDY_HEADER, manifest.get(EquinoxModuleDataNamespace.REGISTERED_BUDDY_HEADER)); if (registeredBuddies != null) { List<String> buddies = new ArrayList<>(); for (ManifestElement element : registeredBuddies) { Collections.addAll(buddies, element.getValueComponents()); } attributes.put(EquinoxModuleDataNamespace.CAPABILITY_BUDDY_REGISTERED, buddies); } // only create the capability if the attributes is not empty if (!attributes.isEmpty()) { Map<String, String> directives = Collections.singletonMap(EquinoxModuleDataNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE, EquinoxModuleDataNamespace.EFFECTIVE_INFORMATION); builder.addCapability(EquinoxModuleDataNamespace.MODULE_DATA_NAMESPACE, directives, attributes); } } private static Map<String, Object> getAttributes(ManifestElement element) throws BundleException { Enumeration<String> keys = element.getKeys(); Map<String, Object> attributes = new HashMap<>(); if (keys == null) return attributes; while (keys.hasMoreElements()) { String key = keys.nextElement(); 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(); } attributes.put(key, convertValue(type, value)); } return attributes; } private static Object convertValueWithNoWhitespace(String type, String value) throws BundleException { value = value.replaceAll("\\s", ""); //$NON-NLS-1$//$NON-NLS-2$ return convertValue(type, value); } private static Object convertValue(String type, String value) throws BundleException { if (ATTR_TYPE_STRING.equalsIgnoreCase(type)) { return value; } String trimmed = value.trim(); if (ATTR_TYPE_DOUBLE.equalsIgnoreCase(type)) { return new Double(trimmed); } else if (ATTR_TYPE_LONG.equalsIgnoreCase(type)) { return new Long(trimmed); } else if (ATTR_TYPE_URI.equalsIgnoreCase(type)) { // we no longer actually create URIs here; just use the string return trimmed; } else if (ATTR_TYPE_VERSION.equalsIgnoreCase(type)) { return new Version(trimmed); } else if (ATTR_TYPE_SET.equalsIgnoreCase(type)) { // just use List<String> here so we don't have to deal with String[] in other places return Collections.unmodifiableList(Arrays.asList(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 BundleException("Unsupported type: " + type, BundleException.MANIFEST_ERROR); //$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 BundleException("Invalid type, missing ending '>' : " + type, BundleException.MANIFEST_ERROR); //$NON-NLS-1$ } List<String> tokens = new Tokenizer(value).getEscapedTokens(","); //$NON-NLS-1$ List<Object> components = new ArrayList<>(); for (String component : tokens) { components.add(convertValue(componentType, component)); } return Collections.unmodifiableList(components); } private static void convertBREEs(ModuleRevisionBuilder builder, Map<String, String> manifest) throws BundleException { @SuppressWarnings("deprecation") String[] brees = ManifestElement.getArrayFromList(manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT)); if (brees == null || brees.length == 0) return; List<String> 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$ } Map<String, String> directives = new HashMap<>(1); directives.put(ExecutionEnvironmentNamespace.REQUIREMENT_FILTER_DIRECTIVE, filterSpec); builder.addRequirement(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, directives, new HashMap<String, Object>(0)); } static String escapeFilterInput(final String value) { 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 '*' : case '\\' : case '(' : case ')' : output[cursor] = '\\'; cursor++; escaped = true; break; } output[cursor] = c; cursor++; } return escaped ? new String(output, 0, cursor) : value; } 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 FilterImpl.newInstance(filterSpec); } catch (InvalidSyntaxException e) { filterSpec = "(osgi.ee=" + bree + ")"; //$NON-NLS-1$ //$NON-NLS-2$ try { // do another sanity check FilterImpl.newInstance(filterSpec); } catch (InvalidSyntaxException e1) { throw new BundleException("Error converting required execution environment.", BundleException.MANIFEST_ERROR, e1); //$NON-NLS-1$ } } return filterSpec; } private 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[] {escapeFilterInput(eeName), v1}; } static class NativeClause implements Comparable<NativeClause> { private final int manifestIndex; final List<String> nativePaths; final String filter; private final Version highestFloor; private final boolean hasLanguage; NativeClause(int manifestIndex, ManifestElement clause) throws BundleException { this.manifestIndex = manifestIndex; this.nativePaths = new ArrayList<>(Arrays.asList(clause.getValueComponents())); StringBuilder sb = new StringBuilder(); sb.append("(&"); //$NON-NLS-1$ addToNativeCodeFilter(sb, clause, Constants.BUNDLE_NATIVECODE_OSNAME); addToNativeCodeFilter(sb, clause, Constants.BUNDLE_NATIVECODE_PROCESSOR); this.highestFloor = (Version) addToNativeCodeFilter(sb, clause, Constants.BUNDLE_NATIVECODE_OSVERSION); this.hasLanguage = ((Boolean) addToNativeCodeFilter(sb, clause, Constants.BUNDLE_NATIVECODE_LANGUAGE)).booleanValue(); String selectionFilter = clause.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE); if (selectionFilter != null) { // do a sanity check to make sure the filter is valid try { FrameworkUtil.createFilter(selectionFilter); } catch (InvalidSyntaxException e) { throw new BundleException("Bad native code selection-filter.", BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$ } sb.append(selectionFilter); } sb.append(')'); String filterResult = sb.toString(); if (filterResult.equals("(&)")) { //$NON-NLS-1$ // no matching attributes found just match all osnames filterResult = "(" + NativeNamespace.CAPABILITY_OSNAME_ATTRIBUTE + "=*)"; //$NON-NLS-1$ //$NON-NLS-2$ } this.filter = filterResult; } private static Object addToNativeCodeFilter(StringBuilder filter, ManifestElement nativeCode, String attribute) { Boolean hasLanguage = Boolean.FALSE; Version highestFloor = null; String[] attrValues = nativeCode.getAttributes(attribute); if (attrValues != null) { String filterAttribute = attribute; if (Constants.BUNDLE_NATIVECODE_OSNAME.equals(attribute)) { filterAttribute = NativeNamespace.CAPABILITY_OSNAME_ATTRIBUTE; } else if (Constants.BUNDLE_NATIVECODE_PROCESSOR.equals(attribute)) { filterAttribute = NativeNamespace.CAPABILITY_PROCESSOR_ATTRIBUTE; } else if (Constants.BUNDLE_NATIVECODE_LANGUAGE.equals(attribute)) { filterAttribute = NativeNamespace.CAPABILITY_LANGUAGE_ATTRIBUTE; hasLanguage = attrValues.length > 0; } else if (Constants.BUNDLE_NATIVECODE_OSVERSION.equals(attribute)) { filterAttribute = NativeNamespace.CAPABILITY_OSVERSION_ATTRIBUTE; } if (attrValues.length > 1) { filter.append("(|"); //$NON-NLS-1$ } for (String attrAlias : attrValues) { if (NativeNamespace.CAPABILITY_OSVERSION_ATTRIBUTE.equals(filterAttribute)) { VersionRange range = new VersionRange(attrAlias); if (highestFloor == null || highestFloor.compareTo(range.getLeft()) < 0) { highestFloor = range.getLeft(); } filter.append(range.toFilterString(filterAttribute)); } else { filter.append('(').append(filterAttribute).append("~=").append(escapeFilterInput(attrAlias)).append(')'); //$NON-NLS-1$ } } if (attrValues.length > 1) { filter.append(')'); } } return Constants.BUNDLE_NATIVECODE_LANGUAGE.equals(attribute) ? hasLanguage : highestFloor; } @Override public int compareTo(NativeClause other) { if (this.highestFloor != null) { if (other == null) { return -1; } if (other.highestFloor == null) { return -1; } int compareVersions = this.highestFloor.compareTo(other.highestFloor); if (compareVersions != 0) { return -(compareVersions); } } else if (other.highestFloor != null) { return 1; } if (this.hasLanguage) { return other.hasLanguage ? this.manifestIndex - other.manifestIndex : 1; } return other.hasLanguage ? -1 : this.manifestIndex - other.manifestIndex; } } private static void getNativeCode(ModuleRevisionBuilder builder, Map<String, String> manifest) throws BundleException { ManifestElement[] elements = ManifestElement.parseHeader(Constants.BUNDLE_NATIVECODE, manifest.get(Constants.BUNDLE_NATIVECODE)); if (elements == null) { return; } boolean optional = false; List<NativeClause> nativeClauses = new ArrayList<>(); for (int i = 0; i < elements.length; i++) { if (i == elements.length - 1) { optional = elements[i].getValue().equals("*"); //$NON-NLS-1$ } if (!optional) { nativeClauses.add(new NativeClause(i, elements[i])); } } Collections.sort(nativeClauses); int numNativePaths = nativeClauses.size(); if (numNativePaths == 0) { String msg = "No native code clauses found in the value of " + Constants.BUNDLE_NATIVECODE + ": " + manifest.get(Constants.BUNDLE_NATIVECODE); //$NON-NLS-1$//$NON-NLS-2$ throw new BundleException(msg, BundleException.MANIFEST_ERROR); } StringBuilder allNativeFilters = new StringBuilder(); if (numNativePaths > 1) { allNativeFilters.append("(|"); //$NON-NLS-1$ } Map<String, Object> attributes = new HashMap<>(2); for (int i = 0; i < numNativePaths; i++) { NativeClause nativeClause = nativeClauses.get(i); if (numNativePaths == 1) { attributes.put(NativeCodeFinder.REQUIREMENT_NATIVE_PATHS_ATTRIBUTE, nativeClause.nativePaths); } else { attributes.put(NativeCodeFinder.REQUIREMENT_NATIVE_PATHS_ATTRIBUTE + '.' + i, nativeClause.nativePaths); } allNativeFilters.append(nativeClauses.get(i).filter); } if (numNativePaths > 1) { allNativeFilters.append(')'); } Map<String, String> directives = new HashMap<>(2); directives.put(NativeNamespace.REQUIREMENT_FILTER_DIRECTIVE, allNativeFilters.toString()); if (optional) { directives.put(NativeNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE, NativeNamespace.RESOLUTION_OPTIONAL); } builder.addRequirement(NativeNamespace.NATIVE_NAMESPACE, directives, attributes); } }