Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory Maven / Gradle / Ivy
Go to download
AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during
compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based
@AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step.
This library is a superset of AspectJ weaver and hence also of AspectJ runtime.
/*******************************************************************************
* Copyright (c) 2012, 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
*******************************************************************************/
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 {@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 SYSTEM_CAPABILITIES = Collections.unmodifiableCollection(Arrays.asList(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, NativeNamespace.NATIVE_NAMESPACE));
private static final Collection PROHIBITED_CAPABILITIES = Collections.unmodifiableCollection(Arrays.asList(IdentityNamespace.IDENTITY_NAMESPACE));
/**
* 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 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 {@link Constants#FRAMEWORK_SYSTEMPACKAGES_EXTRA} and
* {@link Constants#FRAMEWORK_SYSTEMCAPABILITIES_EXTRA}.
* @param manifest the bundle manifest
* @param symbolicNameAlias the symbolic name alias. A null
value is allowed.
* @param extraExports the extra package exports. A null
value is allowed.
* @param extraCapabilities the extra provided capabilities. A null
value is allowed.
* @return a builder for the specified bundle manifest
* @throws BundleException if the bundle manifest is invalid
*/
public static ModuleRevisionBuilder createBuilder(Map manifest, String symbolicNameAlias, String extraExports, String extraCapabilities) throws BundleException {
ModuleRevisionBuilder builder = new ModuleRevisionBuilder();
int manifestVersion = getManifestVersion(manifest);
if (manifestVersion >= 2) {
validateHeaders(manifest, extraExports != null);
}
Object symbolicName = getSymbolicNameAndVersion(builder, manifest, symbolicNameAlias, manifestVersion);
Collection> 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 manifest, boolean allowJavaExports) 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, false);
}
if (definedOSGiValidateHeader == Constants.DYNAMICIMPORT_PACKAGE) {
checkImportExportSyntax(definedOSGiValidateHeader, elements, false, true, false);
}
if (definedOSGiValidateHeader == Constants.EXPORT_PACKAGE) {
checkImportExportSyntax(definedOSGiValidateHeader, elements, true, false, allowJavaExports);
}
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, boolean allowJavaExports) 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(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 && !allowJavaExports && 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-version attributes on exports
// (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 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 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 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 manifest) {
String manifestVersionHeader = manifest.get(Constants.BUNDLE_MANIFESTVERSION);
return manifestVersionHeader == null ? 1 : Integer.parseInt(manifestVersionHeader);
}
private static Object getSymbolicNameAndVersion(ModuleRevisionBuilder builder, Map 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 result = new ArrayList<>();
result.add(builder.getSymbolicName());
result.add(symbolicNameAlias);
symbolicName = result;
} else {
symbolicName = builder.getSymbolicName();
}
Map directives = getDirectives(bsnElement);
directives.remove(BundleNamespace.CAPABILITY_USES_DIRECTIVE);
directives.remove(BundleNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE);
Map 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 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 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 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> exportedPackages) throws BundleException {
if (exportElements == null)
return;
for (ManifestElement exportElement : exportElements) {
String[] packageNames = exportElement.getValueComponents();
Map attributes = getAttributes(exportElement);
Map 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 ? parsePackageVersion(packageNames, specVersionAttr)
: parsePackageVersion(packageNames, 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 packageAttrs = new HashMap<>(attributes);
packageAttrs.put(PackageNamespace.PACKAGE_NAMESPACE, packageName);
builder.addCapability(PackageNamespace.PACKAGE_NAMESPACE, directives, packageAttrs);
exportedPackages.add(packageAttrs);
}
}
}
private static Version parsePackageVersion(String[] packageNames, String versionSpec) throws BundleException {
try {
return versionSpec == null ? Version.emptyVersion : Version.parseVersion(versionSpec);
} catch (IllegalArgumentException e) {
throw new BundleException(
"Invalid value for version specified for package(s) " + Arrays.toString(packageNames), //$NON-NLS-1$
BundleException.MANIFEST_ERROR, e);
}
}
private static void getPackageImports(ModuleRevisionBuilder builder, Map manifest, Collection> exportedPackages, int manifestVersion) throws BundleException {
Collection 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 importPackageNames, boolean dynamic) throws BundleException {
if (importElements == null)
return;
for (ManifestElement importElement : importElements) {
String[] packageNames = importElement.getValueComponents();
Map attributes = getAttributes(importElement);
Map 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 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 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<>(0));
}
}
}
private static void addImplicitImports(ModuleRevisionBuilder builder, Collection> exportedPackages, Collection importPackageNames) {
for (Map 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 directives = new HashMap<>(1);
directives.put(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
builder.addRequirement(PackageNamespace.PACKAGE_NAMESPACE, directives, new HashMap<>(0));
}
}
private static Map getDirectives(ManifestElement element) {
Map directives = new HashMap<>();
Enumeration 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 attributes = getAttributes(requireElement);
Map 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 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 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<>(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 attributes = getAttributes(fragmentHost);
Map 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 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<>(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.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 attributes = getAttributes(provideElement);
Map 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 attributes = getAttributes(requireElement);
Map directives = getDirectives(requireElement);
for (String namespace : namespaces) {
builder.addRequirement(namespace, directives, attributes);
}
}
}
private static void addRequireEclipsePlatform(ModuleRevisionBuilder builder, Map manifest) {
String platformFilter = manifest.get(EclipsePlatformNamespace.ECLIPSE_PLATFORM_FILTER_HEADER);
if (platformFilter == null) {
return;
}
// only support one
HashMap directives = new HashMap<>();
directives.put(EclipsePlatformNamespace.REQUIREMENT_FILTER_DIRECTIVE, platformFilter);
builder.addRequirement(EclipsePlatformNamespace.ECLIPSE_PLATFORM_NAMESPACE, directives, Collections.emptyMap());
}
@SuppressWarnings("deprecation")
private static void getEquinoxDataCapability(ModuleRevisionBuilder builder, Map manifest) throws BundleException {
Map 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", includeSpec)); //$NON-NLS-1$
}
String excludeSpec = policy.getDirective(Constants.EXCLUDE_DIRECTIVE);
if (excludeSpec != null) {
attributes.put(EquinoxModuleDataNamespace.CAPABILITY_LAZY_EXCLUDE_ATTRIBUTE, convertValueWithNoWhitespace("List", 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", 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", 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 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 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 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 directives = Collections.singletonMap(EquinoxModuleDataNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE, EquinoxModuleDataNamespace.EFFECTIVE_INFORMATION);
builder.addCapability(EquinoxModuleDataNamespace.MODULE_DATA_NAMESPACE, directives, attributes);
}
}
private static Map getAttributes(ManifestElement element) throws BundleException {
Enumeration keys = element.getKeys();
Map 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 Double.valueOf(trimmed);
} else if (ATTR_TYPE_LONG.equalsIgnoreCase(type)) {
return Long.valueOf(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 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 tokens = new Tokenizer(value).getEscapedTokens(","); //$NON-NLS-1$
List components = new ArrayList<>();
for (String component : tokens) {
components.add(convertValue(componentType, component));
}
return Collections.unmodifiableList(components);
}
private static void convertBREEs(ModuleRevisionBuilder builder, Map manifest) throws BundleException {
@SuppressWarnings("deprecation")
String[] brees = ManifestElement.getArrayFromList(manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT));
if (brees == null || brees.length == 0)
return;
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$
}
Map directives = new HashMap<>(1);
directives.put(ExecutionEnvironmentNamespace.REQUIREMENT_FILTER_DIRECTIVE, filterSpec);
builder.addRequirement(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, directives, new HashMap<>(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 {
private final int manifestIndex;
final List 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 manifest) throws BundleException {
ManifestElement[] elements = ManifestElement.parseHeader(Constants.BUNDLE_NATIVECODE, manifest.get(Constants.BUNDLE_NATIVECODE));
if (elements == null) {
return;
}
boolean optional = false;
List 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 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 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);
}
}