blob: 01a9ae57a185e1f334a27ff46739b398ac7ddafd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.resolver;
import java.util.*;
import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.BundleException;
import org.osgi.framework.Version;
/**
* This class builds bundle description objects from manifests
*/
class StateBuilder {
static String[] DEFINED_MATCHING_ATTRS = {
Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE,
Constants.BUNDLE_VERSION_ATTRIBUTE,
Constants.PACKAGE_SPECIFICATION_VERSION,
Constants.VERSION_ATTRIBUTE
};
static BundleDescription createBundleDescription(Dictionary manifest, String location) throws BundleException {
BundleDescriptionImpl result = new BundleDescriptionImpl();
String manifestVersionHeader = (String) manifest.get(Constants.BUNDLE_MANIFESTVERSION);
int manifestVersion = 1;
if (manifestVersionHeader != null)
manifestVersion = Integer.parseInt(manifestVersionHeader);
// retrieve the symbolic-name and the singleton status
String symbolicNameHeader = (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME);
if (symbolicNameHeader != null) {
ManifestElement[] symbolicNameElements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, symbolicNameHeader);
if (symbolicNameElements.length > 0) {
result.setSymbolicName(symbolicNameElements[0].getValue());
String singleton = symbolicNameElements[0].getDirective(Constants.SINGLETON_DIRECTIVE);
if (singleton == null) // TODO this is for backward compatibility; need to check manifest version < 2 to allow this after everyone has converted to new syntax
singleton = symbolicNameElements[0].getAttribute(Constants.SINGLETON_DIRECTIVE);
result.setStateBit(BundleDescriptionImpl.SINGLETON, "true".equals(singleton)); //$NON-NLS-1$
}
}
// retrieve other headers
String version = (String) manifest.get(Constants.BUNDLE_VERSION);
result.setVersion((version != null) ? Version.parseVersion(version) : Version.emptyVersion);
result.setLocation(location);
result.setPlatformFilter((String) manifest.get(Constants.ECLIPSE_PLATFORMFILTER));
ManifestElement[] host = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, (String) manifest.get(Constants.FRAGMENT_HOST));
if (host != null)
result.setHost(createHostSpecification(host[0]));
ManifestElement[] exports = ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, (String) manifest.get(Constants.EXPORT_PACKAGE));
ManifestElement[] reexports = ManifestElement.parseHeader(Constants.REEXPORT_PACKAGE, (String) manifest.get(Constants.REEXPORT_PACKAGE));
ManifestElement[] provides = ManifestElement.parseHeader(Constants.PROVIDE_PACKAGE, (String) manifest.get(Constants.PROVIDE_PACKAGE)); // TODO this is null for now until the framwork is updated to handle the new re-export semantics
ArrayList providedExports = new ArrayList(provides == null ? 0 : provides.length);
result.setExportPackages(createExportPackages(result, exports, reexports, provides, providedExports, manifestVersion));
ManifestElement[] imports = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, (String) manifest.get(Constants.IMPORT_PACKAGE));
ManifestElement[] dynamicImports = ManifestElement.parseHeader(Constants.DYNAMICIMPORT_PACKAGE, (String) manifest.get(Constants.DYNAMICIMPORT_PACKAGE));
result.setImportPackages(createImportPackages(result.getExportPackages(), providedExports, imports, dynamicImports, manifestVersion));
ManifestElement[] requires = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, (String) manifest.get(Constants.REQUIRE_BUNDLE));
result.setRequiredBundles(createRequiredBundles(requires));
return result;
}
private static BundleSpecification[] createRequiredBundles(ManifestElement[] specs) {
if (specs == null)
return null;
BundleSpecification[] result = new BundleSpecification[specs.length];
for (int i = 0; i < specs.length; i++)
result[i] = createRequiredBundle(specs[i]);
return result;
}
private static BundleSpecification createRequiredBundle(ManifestElement spec) {
BundleSpecificationImpl result = new BundleSpecificationImpl();
result.setName(spec.getValue());
result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
result.setExported(Constants.VISIBILITY_REEXPORT.equals(spec.getDirective(Constants.VISIBILITY_DIRECTIVE)) || "true".equals(spec.getAttribute(Constants.REPROVIDE_ATTRIBUTE))); //$NON-NLS-1$
result.setOptional(Constants.RESOLUTION_OPTIONAL.equals(spec.getDirective(Constants.RESOLUTION_DIRECTIVE)) || "true".equals(spec.getAttribute(Constants.OPTIONAL_ATTRIBUTE))); //$NON-NLS-1$
return result;
}
private static String[] createProvidedPackages(ManifestElement[] specs) {
if (specs == null || specs.length == 0)
return null;
String[] result = new String[specs.length];
for (int i = 0; i < specs.length; i++)
result[i] = specs[i].getValue();
return result;
}
private static ImportPackageSpecification[] createImportPackages(ExportPackageDescription[] exported, ArrayList providedExports, ManifestElement[] imported, ManifestElement[] dynamicImported, int manifestVersion) throws BundleException {
ArrayList allImports = null;
if (manifestVersion < 2) {
// add implicit imports for each exported package if manifest verions is less than 2.
if (exported.length == 0 && imported == null && dynamicImported == null)
return null;
allImports = new ArrayList(exported.length + (imported == null ? 0 : imported.length));
for (int i = 0; i < exported.length; i++) {
if (providedExports.contains(exported[i].getName()))
continue;
ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl();
result.setName(exported[i].getName());
result.setVersionRange(getVersionRange(exported[i].getVersion().toString()));
result.setResolution(ImportPackageSpecification.RESOLUTION_STATIC);
allImports.add(result);
}
}
else {
allImports = new ArrayList(imported == null ? 0 :imported.length);
}
// add dynamics first so they will get overriden by static imports if
// the same package is dyanamically imported and statically imported.
if (dynamicImported != null)
for (int i = 0; i < dynamicImported.length; i++)
addImportPackages(dynamicImported[i], allImports, manifestVersion, true);
if (imported != null)
for (int i = 0; i < imported.length; i++)
addImportPackages(imported[i], allImports, manifestVersion, false);
return (ImportPackageSpecification[]) allImports.toArray(new ImportPackageSpecification[allImports.size()]);
}
private static void addImportPackages(ManifestElement importPackage, ArrayList allImports, int manifestVersion, boolean dynamic) throws BundleException {
String[] importNames = importPackage.getValueComponents();
for (int i = 0; i < importNames.length; i++) {
// do not allow for multiple imports of same package of manifest version < 2
if (manifestVersion < 2) {
Iterator iter = allImports.iterator();
while(iter.hasNext())
if (importNames[i].equals(((ImportPackageSpecification)iter.next()).getName()))
iter.remove();
}
ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl();
result.setName(importNames[i]);
// set common attributes for both dynamic and static imports
result.setVersionRange(getVersionRange(manifestVersion < 2 ? importPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION) : importPackage.getAttribute(Constants.VERSION_ATTRIBUTE)));
result.setBundleSymbolicName(importPackage.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
result.setBundleVersionRange(getVersionRange(importPackage.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
result.setAttributes(getAttributes(importPackage,DEFINED_MATCHING_ATTRS));
if (dynamic) {
result.setResolution(ImportPackageSpecification.RESOLUTION_DYNAMIC);
}
else {
result.setPropagate(ManifestElement.getArrayFromList(importPackage.getDirective(Constants.GROUPING_DIRECTIVE)));
result.setResolution(getResolution(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE)));
}
allImports.add(result);
}
}
private static int getResolution(String resolution) {
int result = ImportPackageSpecification.RESOLUTION_STATIC;
if (Constants.RESOLUTION_OPTIONAL.equals(resolution))
result = ImportPackageSpecification.RESOLUTION_OPTIONAL;
return result;
}
private static ExportPackageDescription[] createExportPackages(BundleDescriptionImpl bundle, ManifestElement[] exported, ManifestElement[] reexported, ManifestElement[] provides, ArrayList providedExports, int manifestVersion) throws BundleException {
int numExports = (exported == null ? 0 : exported.length) + (reexported == null ? 0 : reexported.length) + (provides == null ? 0 : provides.length);
if (numExports == 0)
return null;
ArrayList allExports = new ArrayList(numExports);
if (exported != null)
for (int i = 0; i < exported.length; i++)
addExportPackages(exported[i], allExports, manifestVersion, false);
if (reexported != null)
for (int i = 0; i < reexported.length; i++)
addExportPackages(reexported[i], allExports, manifestVersion, true);
if (provides != null)
addProvidePackages(provides, allExports, providedExports);
return (ExportPackageDescription[]) allExports.toArray(new ExportPackageDescription[allExports.size()]);
}
private static void addExportPackages(ManifestElement exportPackage, ArrayList allExports, int manifestVersion, boolean reexported) throws BundleException {
String[] exportNames = exportPackage.getValueComponents();
for (int i = 0; i < exportNames.length; i++) {
ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl();
result.setName(exportNames[i]);
String versionString = manifestVersion < 2 ? exportPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION) : exportPackage.getAttribute(Constants.VERSION_ATTRIBUTE);
if (versionString != null)
result.setVersion(Version.parseVersion(versionString));
// alway setting the grouping here even for manifestVersion==1 because if it is null
// the result will return a grouping equal to the package name which is unique and will
// give the same behavior as OSGi R3.
// TODO remove grouping !!!
result.setGrouping(exportPackage.getDirective(Constants.GROUPING_DIRECTIVE));
result.setUses(exportPackage.getDirectives(Constants.USES_DIRECTIVE));
// set the rest of the attributes
result.setInclude(exportPackage.getDirective(Constants.INCLUDE_DIRECTIVE));
result.setExclude(exportPackage.getDirective(Constants.EXCLUDE_DIRECTIVE));
result.setAttributes(getAttributes(exportPackage,DEFINED_MATCHING_ATTRS));
result.setMandatory(ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.MANDATORY_DIRECTIVE)));
result.setRoot(!reexported);
allExports.add(result);
}
}
private static void addProvidePackages(ManifestElement[] provides, ArrayList allExports, ArrayList providedExports) throws BundleException {
ExportPackageDescription[] currentExports = (ExportPackageDescription[]) allExports.toArray(new ExportPackageDescription[allExports.size()]);
for (int i = 0; i < provides.length; i++) {
boolean duplicate = false;
for (int j = 0; j < currentExports.length; j++)
if (provides[i].getValue().equals(currentExports[j].getName())) {
duplicate = true;
break;
}
if (!duplicate) {
ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl();
result.setName(provides[i].getValue());
result.setRoot(true);
allExports.add(result);
}
providedExports.add(provides[i].getValue());
}
}
private static Map getAttributes(ManifestElement exportPackage, String[] definedAttrs) {
Enumeration keys = exportPackage.getKeys();
Map arbitraryAttrs = null;
if (keys == null)
return null;
while (keys.hasMoreElements()) {
boolean definedAttr = false;
String key = (String) keys.nextElement();
for (int i = 0; i < definedAttrs.length; i++) {
if (definedAttrs[i].equals(key)) {
definedAttr = true;
break;
}
}
if (!definedAttr) {
if (arbitraryAttrs == null)
arbitraryAttrs = new HashMap();
arbitraryAttrs.put(key, exportPackage.getAttribute(key));
}
}
return arbitraryAttrs;
}
private static HostSpecification createHostSpecification(ManifestElement spec) {
if (spec == null)
return null;
HostSpecificationImpl result = new HostSpecificationImpl();
result.setName(spec.getValue());
result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
return result;
}
private static VersionRange getVersionRange(String versionRange) {
if (versionRange == null)
return null;
return new VersionRange(versionRange);
}
}