blob: 29f4e2b26634b7558b8a7d368a58817f956f12b3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2012 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
* Rob Harrop - SpringSource Inc. (bug 247522)
*******************************************************************************/
package org.eclipse.osgi.internal.resolver;
import java.util.*;
import org.eclipse.osgi.framework.util.ArrayMap;
import org.eclipse.osgi.internal.container.ComputeNodeOrder;
import org.eclipse.osgi.service.resolver.*;
import org.osgi.framework.Constants;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
/**
* An implementation for the StateHelper API. Access to this implementation is
* provided by the PlatformAdmin. Since this helper is a general facility for
* state manipulation, it should not be tied to any implementation details.
*/
public final class StateHelperImpl implements StateHelper {
private static final StateHelper instance = new StateHelperImpl();
/**
* @see StateHelper
*/
public BundleDescription[] getDependentBundles(BundleDescription[] bundles) {
if (bundles == null || bundles.length == 0)
return new BundleDescription[0];
Set<BundleDescription> reachable = new HashSet<BundleDescription>(bundles.length);
for (int i = 0; i < bundles.length; i++) {
if (!bundles[i].isResolved())
continue;
addDependentBundles(bundles[i], reachable);
}
return reachable.toArray(new BundleDescription[reachable.size()]);
}
private void addDependentBundles(BundleDescription bundle, Set<BundleDescription> reachable) {
if (reachable.contains(bundle))
return;
reachable.add(bundle);
BundleDescription[] dependents = bundle.getDependents();
for (int i = 0; i < dependents.length; i++)
addDependentBundles(dependents[i], reachable);
}
public BundleDescription[] getPrerequisites(BundleDescription[] bundles) {
if (bundles == null || bundles.length == 0)
return new BundleDescription[0];
Set<BundleDescription> reachable = new HashSet<BundleDescription>(bundles.length);
for (int i = 0; i < bundles.length; i++)
addPrerequisites(bundles[i], reachable);
return reachable.toArray(new BundleDescription[reachable.size()]);
}
private void addPrerequisites(BundleDescription bundle, Set<BundleDescription> reachable) {
if (reachable.contains(bundle))
return;
reachable.add(bundle);
List<BundleDescription> depList = ((BundleDescriptionImpl) bundle).getBundleDependencies();
BundleDescription[] dependencies = depList.toArray(new BundleDescription[depList.size()]);
for (int i = 0; i < dependencies.length; i++)
addPrerequisites(dependencies[i], reachable);
}
private Map<String, List<ExportPackageDescription>> getExportedPackageMap(State state) {
Map<String, List<ExportPackageDescription>> result = new HashMap<String, List<ExportPackageDescription>>();
BundleDescription[] bundles = state.getBundles();
for (int i = 0; i < bundles.length; i++) {
ExportPackageDescription[] packages = bundles[i].getExportPackages();
for (int j = 0; j < packages.length; j++) {
ExportPackageDescription description = packages[j];
List<ExportPackageDescription> exports = result.get(description.getName());
if (exports == null) {
exports = new ArrayList<ExportPackageDescription>();
result.put(description.getName(), exports);
}
exports.add(description);
}
}
return result;
}
private Map<String, List<GenericDescription>> getGenericsMap(State state, boolean resolved) {
Map<String, List<GenericDescription>> result = new HashMap<String, List<GenericDescription>>();
BundleDescription[] bundles = state.getBundles();
for (int i = 0; i < bundles.length; i++) {
if (resolved && !bundles[i].isResolved())
continue; // discard unresolved bundles
GenericDescription[] generics = bundles[i].getGenericCapabilities();
for (int j = 0; j < generics.length; j++) {
GenericDescription description = generics[j];
List<GenericDescription> genericList = result.get(description.getName());
if (genericList == null) {
genericList = new ArrayList<GenericDescription>(1);
result.put(description.getName(), genericList);
}
genericList.add(description);
}
}
return result;
}
private VersionConstraint[] getUnsatisfiedLeaves(State state, BundleDescription[] bundles, ResolverHook hook) {
Map<String, List<ExportPackageDescription>> packages = getExportedPackageMap(state);
Map<String, List<GenericDescription>> generics = getGenericsMap(state, false);
Set<VersionConstraint> result = new HashSet<VersionConstraint>();
List<BundleDescription> bundleList = new ArrayList<BundleDescription>(bundles.length);
for (int i = 0; i < bundles.length; i++)
bundleList.add(bundles[i]);
for (int i = 0; i < bundleList.size(); i++) {
BundleDescription description = bundleList.get(i);
VersionConstraint[] constraints = getUnsatisfiedConstraints(description, hook);
for (int j = 0; j < constraints.length; j++) {
VersionConstraint constraint = constraints[j];
Collection<BaseDescription> satisfied = null;
if (constraint instanceof BundleSpecification || constraint instanceof HostSpecification) {
BundleDescription[] suppliers = state.getBundles(constraint.getName());
satisfied = getPossibleCandidates(constraint, suppliers, constraint instanceof HostSpecification ? BundleRevision.HOST_NAMESPACE : null, hook, false);
} else if (constraint instanceof ImportPackageSpecification) {
List<ExportPackageDescription> exports = packages.get(constraint.getName());
if (exports != null)
satisfied = getPossibleCandidates(constraint, exports.toArray(new BaseDescription[exports.size()]), null, hook, false);
} else if (constraint instanceof GenericSpecification) {
List<GenericDescription> genericSet = generics.get(constraint.getName());
if (genericSet != null)
satisfied = getPossibleCandidates(constraint, genericSet.toArray(new BaseDescription[genericSet.size()]), null, hook, false);
}
if (satisfied == null || satisfied.isEmpty()) {
result.add(constraint);
} else {
for (BaseDescription baseDescription : satisfied) {
if (!baseDescription.getSupplier().isResolved() && !bundleList.contains(baseDescription.getSupplier())) {
bundleList.add(baseDescription.getSupplier());
// TODO only add the first supplier that is not resolved;
// this is the previous behavior before the fix for bug 333071; should consider adding all unresolved
break;
}
}
}
}
}
return result.toArray(new VersionConstraint[result.size()]);
}
public VersionConstraint[] getUnsatisfiedLeaves(BundleDescription[] bundles) {
if (bundles.length == 0)
return new VersionConstraint[0];
State state = bundles[0].getContainingState();
ResolverHook hook = beginHook(state, Arrays.asList((BundleRevision[]) bundles));
try {
return getUnsatisfiedLeaves(state, bundles, hook);
} finally {
if (hook != null)
hook.end();
}
}
private ResolverHook beginHook(State state, Collection<BundleRevision> triggers) {
if (!(state instanceof StateImpl))
return null;
ResolverHookFactory hookFactory = ((StateImpl) state).getResolverHookFactory();
return hookFactory == null ? null : hookFactory.begin(triggers);
}
/**
* @see StateHelper
*/
public VersionConstraint[] getUnsatisfiedConstraints(BundleDescription bundle) {
ResolverHook hook = beginHook(bundle.getContainingState(), Arrays.asList(new BundleRevision[] {bundle}));
try {
return getUnsatisfiedConstraints(bundle, hook);
} finally {
if (hook != null)
hook.end();
}
}
private VersionConstraint[] getUnsatisfiedConstraints(BundleDescription bundle, ResolverHook hook) {
State containingState = bundle.getContainingState();
if (containingState == null)
// it is a bug in the client to call this method when not attached to a state
throw new IllegalStateException("Does not belong to a state"); //$NON-NLS-1$
List<VersionConstraint> unsatisfied = new ArrayList<VersionConstraint>();
HostSpecification host = bundle.getHost();
if (host != null)
if (!host.isResolved() && !isBundleConstraintResolvable(host, BundleRevision.HOST_NAMESPACE, hook))
unsatisfied.add(host);
BundleSpecification[] requiredBundles = bundle.getRequiredBundles();
for (int i = 0; i < requiredBundles.length; i++)
if (!requiredBundles[i].isResolved() && !isBundleConstraintResolvable(requiredBundles[i], null, hook))
unsatisfied.add(requiredBundles[i]);
ImportPackageSpecification[] packages = bundle.getImportPackages();
for (int i = 0; i < packages.length; i++)
if (!packages[i].isResolved() && !isResolvable(packages[i], hook)) {
if (bundle.isResolved()) {
// if the bundle is resolved the check if the import is option.
// Here we assume that an unresolved mandatory import must have been dropped
// in favor of an export from the same bundle (bug 338240)
if (!ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(packages[i].getDirective(Constants.RESOLUTION_DIRECTIVE)))
continue;
}
unsatisfied.add(packages[i]);
}
GenericSpecification[] generics = bundle.getGenericRequires();
for (int i = 0; i < generics.length; i++)
if (!generics[i].isResolved() && !isResolvable(generics[i], hook))
unsatisfied.add(generics[i]);
NativeCodeSpecification nativeCode = bundle.getNativeCodeSpecification();
if (nativeCode != null && !nativeCode.isResolved())
unsatisfied.add(nativeCode);
return unsatisfied.toArray(new VersionConstraint[unsatisfied.size()]);
}
private ArrayMap<BundleCapability, BaseDescription> asArrayMap(List<BaseDescription> descriptions, String namespace) {
List<BundleCapability> capabilities = new ArrayList<BundleCapability>(descriptions.size());
for (BaseDescription description : descriptions)
capabilities.add(((BaseDescriptionImpl) description).getCapability(namespace));
return new ArrayMap<BundleCapability, BaseDescription>(capabilities, descriptions);
}
private List<BaseDescription> getPossibleCandidates(VersionConstraint constraint, BaseDescription[] descriptions, String namespace, ResolverHook hook, boolean resolved) {
List<BaseDescription> candidates = new ArrayList<BaseDescription>();
for (int i = 0; i < descriptions.length; i++)
if ((!resolved || descriptions[i].getSupplier().isResolved()) && constraint.isSatisfiedBy(descriptions[i]))
candidates.add(descriptions[i]);
if (hook != null)
hook.filterMatches(constraint.getRequirement(), asArrayMap(candidates, namespace));
return candidates;
}
/**
* @see StateHelper
*/
public boolean isResolvable(ImportPackageSpecification constraint) {
ResolverHook hook = beginHook(constraint.getBundle().getContainingState(), Arrays.asList(new BundleRevision[] {constraint.getBundle()}));
try {
return isResolvable(constraint, hook);
} finally {
if (hook != null)
hook.end();
}
}
private boolean isResolvable(ImportPackageSpecification constraint, ResolverHook hook) {
ExportPackageDescription[] exports = constraint.getBundle().getContainingState().getExportedPackages();
return getPossibleCandidates(constraint, exports, null, hook, true).size() > 0;
}
private boolean isResolvable(GenericSpecification constraint, ResolverHook hook) {
Map<String, List<GenericDescription>> genericCapabilities = getGenericsMap(constraint.getBundle().getContainingState(), true);
List<GenericDescription> genericList = genericCapabilities.get(constraint.getName());
if (genericList == null)
return false;
return getPossibleCandidates(constraint, genericList.toArray(new BaseDescription[genericList.size()]), null, hook, true).size() > 0;
}
/**
* @see StateHelper
*/
public boolean isResolvable(BundleSpecification specification) {
return isBundleConstraintResolvable(specification, null);
}
/**
* @see StateHelper
*/
public boolean isResolvable(HostSpecification specification) {
return isBundleConstraintResolvable(specification, BundleRevision.HOST_NAMESPACE);
}
/*
* Returns whether a bundle specification/host specification can be resolved.
*/
private boolean isBundleConstraintResolvable(VersionConstraint constraint, String namespace) {
ResolverHook hook = beginHook(constraint.getBundle().getContainingState(), Arrays.asList(new BundleRevision[] {constraint.getBundle()}));
try {
return isBundleConstraintResolvable(constraint, namespace, hook);
} finally {
if (hook != null)
hook.end();
}
}
private boolean isBundleConstraintResolvable(VersionConstraint constraint, String namespace, ResolverHook hook) {
BundleDescription[] availableBundles = constraint.getBundle().getContainingState().getBundles(constraint.getName());
return getPossibleCandidates(constraint, availableBundles, namespace, hook, true).size() > 0;
}
public Object[][] sortBundles(BundleDescription[] toSort) {
List<Object[]> references = new ArrayList<Object[]>(toSort.length);
for (int i = 0; i < toSort.length; i++)
if (toSort[i].isResolved())
buildReferences(toSort[i], references);
Object[][] cycles = ComputeNodeOrder.computeNodeOrder(toSort, references.toArray(new Object[references.size()][]));
if (cycles.length == 0)
return cycles;
// fix up host/fragment orders (bug 184127)
for (int i = 0; i < cycles.length; i++) {
for (int j = 0; j < cycles[i].length; j++) {
BundleDescription fragment = (BundleDescription) cycles[i][j];
if (fragment.getHost() == null)
continue;
BundleDescription host = (BundleDescription) fragment.getHost().getSupplier();
if (host == null)
continue;
fixFragmentOrder(host, fragment, toSort);
}
}
return cycles;
}
private void fixFragmentOrder(BundleDescription host, BundleDescription fragment, BundleDescription[] toSort) {
int hostIndex = -1;
int fragIndex = -1;
for (int i = 0; i < toSort.length && (hostIndex == -1 || fragIndex == -1); i++) {
if (toSort[i] == host)
hostIndex = i;
else if (toSort[i] == fragment)
fragIndex = i;
}
if (fragIndex > -1 && fragIndex < hostIndex) {
for (int i = fragIndex; i < hostIndex; i++)
toSort[i] = toSort[i + 1];
toSort[hostIndex] = fragment;
}
}
private void buildReferences(BundleDescription description, List<Object[]> references) {
HostSpecification host = description.getHost();
// it is a fragment
if (host != null) {
// just create a dependencies for non-payload requirements (osgi.wiring.host and osgi.ee)
if (host.getHosts() != null) {
BundleDescription[] hosts = host.getHosts();
for (int i = 0; i < hosts.length; i++)
if (hosts[i] != description)
references.add(new Object[] {description, hosts[i]});
}
GenericDescription[] genericDependencies = description.getResolvedGenericRequires();
for (GenericDescription dependency : genericDependencies) {
if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE.equals(dependency.getType())) {
references.add(new Object[] {description, dependency.getSupplier()});
}
}
} else {
// it is a host
buildReferences(description, ((BundleDescriptionImpl) description).getBundleDependencies(), references);
}
}
private void buildReferences(BundleDescription description, List<BundleDescription> dependencies, List<Object[]> references) {
for (Iterator<BundleDescription> iter = dependencies.iterator(); iter.hasNext();)
addReference(description, iter.next(), references);
}
private void addReference(BundleDescription description, BundleDescription reference, List<Object[]> references) {
// build the reference from the description
if (description == reference || reference == null)
return;
BundleDescription[] fragments = reference.getFragments();
for (int i = 0; i < fragments.length; i++) {
if (fragments[i].isResolved()) {
ExportPackageDescription[] exports = fragments[i].getExportPackages();
if (exports.length > 0)
references.add(new Object[] {description, fragments[i]});
}
}
references.add(new Object[] {description, reference});
}
public ExportPackageDescription[] getVisiblePackages(BundleDescription bundle) {
return getVisiblePackages(bundle, 0);
}
public ExportPackageDescription[] getVisiblePackages(BundleDescription bundle, int options) {
StateImpl state = (StateImpl) bundle.getContainingState();
boolean strict = false;
if (state != null)
strict = state.inStrictMode();
BundleDescription host = (BundleDescription) (bundle.getHost() == null ? bundle : bundle.getHost().getSupplier());
List<ExportPackageDescription> orderedPkgList = new ArrayList<ExportPackageDescription>(); // list of all ExportPackageDescriptions that are visible (ArrayList is used to keep order)
Set<ExportPackageDescription> pkgSet = new HashSet<ExportPackageDescription>();
Set<String> importList = new HashSet<String>(); // list of package names which are directly imported
// get the list of directly imported packages first.
ImportsHolder imports = new ImportsHolder(bundle, options);
for (int i = 0; i < imports.getSize(); i++) {
ExportPackageDescription pkgSupplier = imports.getSupplier(i);
if (pkgSupplier == null || pkgSupplier.getExporter() == host) // do not return the bundle'sr own imports
continue;
if (!isSystemExport(pkgSupplier, options) && !pkgSet.contains(pkgSupplier)) {
orderedPkgList.add(pkgSupplier);
pkgSet.add(pkgSupplier);
}
// get the sources of the required bundles of the exporter
BundleSpecification[] requires = pkgSupplier.getExporter().getRequiredBundles();
Set<BundleDescription> visited = new HashSet<BundleDescription>();
visited.add(bundle); // always add self to prevent recursing into self
Set<String> importNames = new HashSet<String>(1);
importNames.add(imports.getName(i));
for (int j = 0; j < requires.length; j++) {
BundleDescription bundleSupplier = (BundleDescription) requires[j].getSupplier();
if (bundleSupplier != null)
getPackages(bundleSupplier, bundle.getSymbolicName(), importList, orderedPkgList, pkgSet, visited, strict, importNames, options);
}
importList.add(imports.getName(i)); // be sure to add to direct import list
}
// now find all the packages that are visible from required bundles
RequiresHolder requires = new RequiresHolder(bundle, options);
Set<BundleDescription> visited = new HashSet<BundleDescription>(requires.getSize());
visited.add(bundle); // always add self to prevent recursing into self
for (int i = 0; i < requires.getSize(); i++) {
BundleDescription bundleSupplier = requires.getSupplier(i);
if (bundleSupplier != null)
getPackages(bundleSupplier, bundle.getSymbolicName(), importList, orderedPkgList, pkgSet, visited, strict, null, options);
}
return orderedPkgList.toArray(new ExportPackageDescription[orderedPkgList.size()]);
}
private void getPackages(BundleDescription requiredBundle, String symbolicName, Set<String> importList, List<ExportPackageDescription> orderedPkgList, Set<ExportPackageDescription> pkgSet, Set<BundleDescription> visited, boolean strict, Set<String> pkgNames, int options) {
if (visited.contains(requiredBundle))
return; // prevent duplicate entries and infinate loops incase of cycles
visited.add(requiredBundle);
// add all the exported packages from the required bundle; take x-friends into account.
ExportPackageDescription[] substitutedExports = requiredBundle.getSubstitutedExports();
ExportPackageDescription[] imports = requiredBundle.getResolvedImports();
Set<String> substituteNames = null; // a temporary set used to scope packages we get from getPackages
for (int i = 0; i < substitutedExports.length; i++) {
if ((pkgNames == null || pkgNames.contains(substitutedExports[i].getName()))) {
for (int j = 0; j < imports.length; j++) {
if (substitutedExports[i].getName().equals(imports[j].getName()) && !pkgSet.contains(imports[j])) {
if (substituteNames == null)
substituteNames = new HashSet<String>(1);
else
substituteNames.clear();
// substituteNames is a set of one package containing the single substitute we are trying to get the source for
substituteNames.add(substitutedExports[i].getName());
getPackages(imports[j].getSupplier(), symbolicName, importList, orderedPkgList, pkgSet, new HashSet<BundleDescription>(0), strict, substituteNames, options);
}
}
}
}
importList = substitutedExports.length == 0 ? importList : new HashSet<String>(importList);
for (int i = 0; i < substitutedExports.length; i++)
// we add the package name to the import list to prevent required bundles from adding more sources
importList.add(substitutedExports[i].getName());
ExportPackageDescription[] exports = requiredBundle.getSelectedExports();
HashSet<String> exportNames = new HashSet<String>(exports.length); // set is used to improve performance of duplicate check.
for (int i = 0; i < exports.length; i++)
if ((pkgNames == null || pkgNames.contains(exports[i].getName())) && !isSystemExport(exports[i], options) && isFriend(symbolicName, exports[i], strict) && !importList.contains(exports[i].getName()) && !pkgSet.contains(exports[i])) {
if (!exportNames.contains(exports[i].getName())) {
// only add the first export
orderedPkgList.add(exports[i]);
pkgSet.add(exports[i]);
exportNames.add(exports[i].getName());
}
}
// now look for exports from the required bundle.
RequiresHolder requiredBundles = new RequiresHolder(requiredBundle, options);
for (int i = 0; i < requiredBundles.getSize(); i++) {
if (requiredBundles.getSupplier(i) == null)
continue;
if (requiredBundles.isExported(i)) {
// looking for a specific package and that package is exported by this bundle or adding all packages from a reexported bundle
getPackages(requiredBundles.getSupplier(i), symbolicName, importList, orderedPkgList, pkgSet, visited, strict, pkgNames, options);
} else if (exportNames.size() > 0) {
// adding any exports from required bundles which we also export
Set<BundleDescription> tmpVisited = new HashSet<BundleDescription>();
getPackages(requiredBundles.getSupplier(i), symbolicName, importList, orderedPkgList, pkgSet, tmpVisited, strict, exportNames, options);
}
}
}
private boolean isSystemExport(ExportPackageDescription export, int options) {
if ((options & VISIBLE_INCLUDE_EE_PACKAGES) != 0)
return false;
return ((Integer) export.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() >= 0;
}
private boolean isFriend(String consumerBSN, ExportPackageDescription export, boolean strict) {
if (!strict)
return true; // ignore friends rules if not in strict mode
String[] friends = (String[]) export.getDirective(StateImpl.FRIENDS_DIRECTIVE);
if (friends == null)
return true; // no x-friends means it is wide open
for (int i = 0; i < friends.length; i++)
if (friends[i].equals(consumerBSN))
return true; // the consumer is a friend
return false;
}
public int getAccessCode(BundleDescription bundle, ExportPackageDescription export) {
if (((Boolean) export.getDirective(StateImpl.INTERNAL_DIRECTIVE)).booleanValue())
return ACCESS_DISCOURAGED;
if (!isFriend(bundle.getSymbolicName(), export, true)) // pass strict here so that x-friends is processed
return ACCESS_DISCOURAGED;
return ACCESS_ENCOURAGED;
}
public static StateHelper getInstance() {
return instance;
}
}
/*
* This class is used to encapsulate the import packages of a bundle used by getVisiblePackages(). If the method is called with the option
* VISIBLE_INCLUDE_ALL_HOST_WIRES, it uses resolved import packages to find all visible packages by a bundle. Called without this option,
* it uses imported packages instead of resolved imported packages and does not consider resolved dynamic imports.
* ImportsHolder serves to hide which of these is used, so that the body of getVisiblePackages() does not become full of checks.
*
*/
class ImportsHolder {
private final ImportPackageSpecification[] importedPackages;
private final ExportPackageDescription[] resolvedImports;
private final boolean isUsingResolved;
// Depending on the options used, either importedPackages or resolvedImports is initialize, but not both.
ImportsHolder(BundleDescription bundle, int options) {
isUsingResolved = (options & StateHelper.VISIBLE_INCLUDE_ALL_HOST_WIRES) != 0;
if (isUsingResolved) {
importedPackages = null;
resolvedImports = bundle.getResolvedImports();
} else {
importedPackages = bundle.getImportPackages();
resolvedImports = null;
}
}
ExportPackageDescription getSupplier(int index) {
if (isUsingResolved)
return resolvedImports[index];
return (ExportPackageDescription) importedPackages[index].getSupplier();
}
String getName(int index) {
if (isUsingResolved)
return resolvedImports[index].getName();
return importedPackages[index].getName();
}
int getSize() {
if (isUsingResolved)
return resolvedImports.length;
return importedPackages.length;
}
}
/*
* This class is used to encapsulate the required bundles by a bundle, used by getVisiblePackages(). If the method is called with the option
* VISIBLE_INCLUDE_ALL_HOST_WIRES, it uses resolved required bundles to find all visible packages by a bundle. Called without this option,
* it uses required bundles instead of resolved required bundles and does not consider the constraints from fragments.
* RequiresHolder serves to hide which of these is used.
*/
class RequiresHolder {
private final BundleSpecification[] requiredBundles;
private final BundleDescription[] resolvedRequires;
private final boolean isUsingResolved;
private final Map<BundleDescription, Boolean> resolvedBundlesExported;
// Depending on the options used, either requiredBundles or resolvedRequires is initialize, but not both.
RequiresHolder(BundleDescription bundle, int options) {
isUsingResolved = (options & StateHelper.VISIBLE_INCLUDE_ALL_HOST_WIRES) != 0;
if (isUsingResolved) {
requiredBundles = null;
resolvedBundlesExported = new HashMap<BundleDescription, Boolean>();
resolvedRequires = bundle.getResolvedRequires();
determineRequiresVisibility(bundle);
} else {
requiredBundles = bundle.getRequiredBundles();
resolvedBundlesExported = null;
resolvedRequires = null;
}
}
BundleDescription getSupplier(int index) {
if (isUsingResolved)
return resolvedRequires[index];
return (BundleDescription) requiredBundles[index].getSupplier();
}
boolean isExported(int index) {
if (isUsingResolved)
return resolvedBundlesExported.get(resolvedRequires[index]).booleanValue();
return requiredBundles[index].isExported();
}
int getSize() {
if (isUsingResolved)
return resolvedRequires.length;
return requiredBundles.length;
}
/*
* This method determines for all resolved required bundles if they are reexported.
* Fragment bundles are also considered.
*/
private void determineRequiresVisibility(BundleDescription bundle) {
BundleSpecification[] required = bundle.getRequiredBundles();
Set<BundleDescription> resolved = new HashSet<BundleDescription>();
for (int i = 0; i < resolvedRequires.length; i++) {
resolved.add(resolvedRequires[i]);
}
// Get the visibility of all directly required bundles
for (int i = 0; i < required.length; i++) {
if (required[i].getSupplier() != null) {
resolvedBundlesExported.put((BundleDescription) required[i].getSupplier(), Boolean.valueOf(required[i].isExported()));
resolved.remove(required[i].getSupplier());
}
}
BundleDescription[] fragments = bundle.getFragments();
// Get the visibility of resolved required bundles, which come from fragments
if (resolved.size() > 0) {
for (int i = 0; i < fragments.length; i++) {
BundleSpecification[] fragmentRequiredBundles = fragments[i].getRequiredBundles();
for (int j = 0; j < fragmentRequiredBundles.length; j++) {
if (resolved.contains(fragmentRequiredBundles[j].getSupplier())) {
resolvedBundlesExported.put((BundleDescription) fragmentRequiredBundles[j].getSupplier(), Boolean.valueOf(fragmentRequiredBundles[j].isExported()));
resolved.remove(fragmentRequiredBundles[j].getSupplier());
}
}
if (resolved.size() == 0) {
break;
}
}
}
}
}