blob: 88894d784f61099cbe026571bd1c0d2d44a4ffe1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.framework;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.felix.framework.ModuleImpl;
import org.apache.felix.framework.searchpolicy.Resolver;
import org.apache.felix.framework.searchpolicy.PackageSource;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.R4Attribute;
import org.apache.felix.framework.util.manifestparser.R4Directive;
import org.apache.felix.framework.util.manifestparser.Requirement;
import org.apache.felix.moduleloader.ICapability;
import org.apache.felix.moduleloader.IModule;
import org.apache.felix.moduleloader.IRequirement;
import org.apache.felix.moduleloader.IWire;
import org.osgi.framework.Constants;
import org.osgi.framework.PackagePermission;
import org.osgi.framework.Version;
public class FelixResolverState implements Resolver.ResolverState
{
private final Logger m_logger;
// List of all modules.
private final List m_moduleList = new ArrayList();
// Map of fragment symbolic names to array of modules sorted version.
private final Map m_fragmentMap = new HashMap();
// Maps a package name to an array of modules.
private final Map m_unresolvedPkgIndexMap = new HashMap();
// Maps a package name to an array of modules.
private final Map m_resolvedPkgIndexMap = new HashMap();
// Maps a module to an array of capabilities.
private final Map m_resolvedCapMap = new HashMap();
// Reusable empty array.
private static final IModule[] m_emptyModules = new IModule[0];
private static final PackageSource[] m_emptySources = new PackageSource[0];
public FelixResolverState(Logger logger)
{
m_logger = logger;
}
public synchronized IModule mergeFragments(IModule rootModule) throws Exception
{
// TODO: FRAGMENT - This should check to make sure that the host allows fragments.
IModule newRootModule = rootModule;
for (int hostIdx = 0; hostIdx < m_moduleList.size(); hostIdx++)
{
IModule host = (IModule) m_moduleList.get(hostIdx);
if (!host.isResolved() && !Util.isFragment(host))
{
ICapability[] caps = host.getCapabilities();
ICapability hostCap = null;
for (int capIdx = 0; capIdx < caps.length; capIdx++)
{
if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
{
hostCap = caps[capIdx];
break;
}
}
// If there is no host capability in the current module,
// then just ignore it.
if (hostCap == null)
{
continue;
}
// Need to remove any previously attached, but not resolved fragments.
// TODO: FRAGMENT - Would be better to have the previous resolves
// not leave fragments attached.
((ModuleImpl) host).attachFragments(null);
// Fragments are grouped by symbolic name and descending version.
// Attach the first matching fragment from each group if possible,
// since only one version of a given fragment may attach to a host.
List fragmentList = new ArrayList();
for (Iterator it = m_fragmentMap.entrySet().iterator(); it.hasNext(); )
{
Map.Entry entry = (Map.Entry) it.next();
IModule[] fragments = (IModule[]) entry.getValue();
done: for (int fragIdx = 0; fragIdx < fragments.length; fragIdx++)
{
IRequirement[] reqs = fragments[fragIdx].getRequirements();
for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
{
if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
&& reqs[reqIdx].isSatisfied(hostCap)
&& !((BundleImpl) fragments[fragIdx].getBundle()).isStale()
&& !((BundleImpl) fragments[fragIdx].getBundle()).isRemovalPending())
{
// Fragments are attached in bundle ID order.
int index = -1;
for (int listIdx = 0;
(index < 0) && (listIdx < fragmentList.size());
listIdx++)
{
if (fragments[fragIdx].getBundle().getBundleId()
< ((IModule) fragmentList.get(listIdx)).getBundle().getBundleId())
{
index = listIdx;
}
}
fragmentList.add(
(index < 0) ? fragmentList.size() : index,
fragments[fragIdx]);
break done;
}
}
}
}
if (fragmentList.size() > 0)
{
// Check if fragment conflicts with existing metadata.
checkForConflicts(host, fragmentList);
// Attach the fragments to the host.
IModule[] fragments = (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
((ModuleImpl) host).attachFragments(fragments);
for (int fragIdx = 0; fragIdx < fragments.length; fragIdx++)
{
// Check to see if the root module is actually a fragment,
// if so then we want to return the host for resolving.
if (rootModule == fragments[fragIdx])
{
newRootModule = host;
}
// Add each fragment capabililty to the resolver state
// data structures.
ICapability[] fragCaps = fragments[fragIdx].getCapabilities();
for (int capIdx = 0; (fragCaps != null) && (capIdx < fragCaps.length); capIdx++)
{
if (fragCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
indexPackageCapability(
m_unresolvedPkgIndexMap, host, fragCaps[capIdx]);
}
}
}
}
}
}
return newRootModule;
}
private void checkForConflicts(IModule host, List fragmentList)
{
// Verify the fragments do not have conflicting imports.
// For now, just check for duplicate imports, but in the
// future we might want to make this more fine grained.
// First get the host's imported packages.
final int MODULE_IDX = 0, REQ_IDX = 1;
Map ipMerged = new HashMap();
Map rbMerged = new HashMap();
IRequirement[] reqs = host.getRequirements();
for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
{
if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
ipMerged.put(
((Requirement) reqs[reqIdx]).getTargetName(),
new Object[] { host, reqs[reqIdx] });
}
else if (reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
{
rbMerged.put(
((Requirement) reqs[reqIdx]).getTargetName(),
new Object[] { host, reqs[reqIdx] });
}
}
// Loop through each fragment verifying it does no conflict,
// adding its package and bundle dependencies if they do not
// conflict and removing the fragment if it does conflict.
for (Iterator it = fragmentList.iterator(); it.hasNext(); )
{
IModule fragment = (IModule) it.next();
reqs = fragment.getRequirements();
Map ipFragment = new HashMap();
Map rbFragment = new HashMap();
boolean conflicting = false;
for (int reqIdx = 0;
!conflicting && (reqs != null) && (reqIdx < reqs.length);
reqIdx++)
{
if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
|| reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
{
String targetName = ((Requirement) reqs[reqIdx]).getTargetName();
Map mergedReqMap = (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
? ipMerged : rbMerged;
Map fragmentReqMap = (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
? ipFragment : rbFragment;
Object[] existing = (Object[]) mergedReqMap.get(targetName);
if (existing == null)
{
fragmentReqMap.put(targetName, new Object[] { fragment, reqs[reqIdx] });
}
else if (isRequirementConflicting(
(Requirement) existing[REQ_IDX], (Requirement) reqs[reqIdx]))
{
conflicting = true;
}
if (conflicting)
{
ipFragment.clear();
rbFragment.clear();
it.remove();
m_logger.log(
Logger.LOG_DEBUG,
"Excluding fragment " + fragment.getSymbolicName()
+ " from " + host.getSymbolicName()
+ " due to conflict with "
+ (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
? "imported package " : "required bundle ")
+ targetName + " from "
+ ((IModule) existing[MODULE_IDX]).getSymbolicName());
}
}
}
// Merge non-conflicting requirements into overall set
// of requirements and continue checking for conflicts
// with the next fragment.
for (Iterator it2 = ipFragment.entrySet().iterator(); it2.hasNext(); )
{
Map.Entry entry = (Map.Entry) it2.next();
ipMerged.put(entry.getKey(), entry.getValue());
}
for (Iterator it2 = rbFragment.entrySet().iterator(); it2.hasNext(); )
{
Map.Entry entry = (Map.Entry) it2.next();
rbMerged.put(entry.getKey(), entry.getValue());
}
}
}
private boolean isRequirementConflicting(
Requirement existing, Requirement additional)
{
// If the namespace is not the same, then they do NOT conflict.
if (!existing.getNamespace().equals(additional.getNamespace()))
{
return false;
}
// If the target name is not the same, then they do NOT conflict.
if (!existing.getTargetName().equals(additional.getTargetName()))
{
return false;
}
// If the target version range is not the same, then they conflict.
if (!existing.getTargetVersionRange().equals(additional.getTargetVersionRange()))
{
return true;
}
// If optionality is not the same, then they conflict, unless
// the existing requirement is not optional, then it doesn't matter
// what subsequent requirements are since non-optional is stronger
// than optional.
if (existing.isOptional() && (existing.isOptional() != additional.isOptional()))
{
return true;
}
// Verify directives are the same.
// This is sort of ugly, but we need to remove
// the resolution directive, since it is effectively
// test above when checking optionality.
// Put directives in a map, since ordering is arbitrary.
final R4Directive[] exDirs = (existing.getDirectives() == null)
? new R4Directive[0] : existing.getDirectives();
final Map exDirMap = new HashMap();
for (int i = 0; i < exDirs.length; i++)
{
if (!exDirs[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
{
exDirMap.put(exDirs[i].getName(), exDirs[i]);
}
}
final R4Directive[] addDirs = (additional.getDirectives() == null)
? new R4Directive[0] : additional.getDirectives();
final Map addDirMap = new HashMap();
for (int i = 0; i < addDirs.length; i++)
{
if (!addDirs[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
{
addDirMap.put(addDirs[i].getName(), addDirs[i]);
}
}
// If different number of directives, then they conflict.
if (exDirMap.size() != addDirMap.size())
{
return true;
}
// If directive values do not match, then they conflict.
for (Iterator it = addDirMap.entrySet().iterator(); it.hasNext(); )
{
final Map.Entry entry = (Map.Entry) it.next();
final String name = (String) entry.getKey();
final R4Directive addDir = (R4Directive) entry.getValue();
final R4Directive exDir = (R4Directive) exDirMap.get(name);
if ((exDir == null) ||
!exDir.getValue().equals(addDir.getValue()))
{
return true;
}
}
// Verify attributes are the same.
final R4Attribute[] exAttrs = (existing.getAttributes() == null)
? new R4Attribute[0] : existing.getAttributes();
final R4Attribute[] addAttrs = (additional.getAttributes() == null)
? new R4Attribute[0] : additional.getAttributes();
// If different number of attributes, then they conflict.
if (exAttrs.length != addAttrs.length)
{
return true;
}
// Put attributes in a map, since ordering is arbitrary.
final Map exAttrMap = new HashMap();
for (int i = 0; i < exAttrs.length; i++)
{
exAttrMap.put(exAttrs[i].getName(), exAttrs[i]);
}
// If attribute values do not match, then they conflict.
for (int i = 0; i < addAttrs.length; i++)
{
final R4Attribute exAttr = (R4Attribute) exAttrMap.get(addAttrs[i].getName());
if ((exAttr == null) ||
!exAttr.getValue().equals(addAttrs[i].getValue()) ||
(exAttr.isMandatory() != addAttrs[i].isMandatory()))
{
return true;
}
}
// They do no conflict.
return false;
}
private void addFragment(IModule module)
{
indexFragment(m_fragmentMap, module);
// System.out.println("+++ BEGIN FRAGMENT DUMP");
// dumpModuleIndexMap(m_fragmentMap);
// System.out.println("+++ END FRAGMENT DUMP");
}
public synchronized void addModule(IModule module)
{
if (Util.isFragment(module))
{
addFragment(module);
}
else
{
// When a module is added, create an aggregated list of unresolved
// exports to simplify later processing when resolving bundles.
m_moduleList.add(module);
ICapability[] caps = module.getCapabilities();
// Add exports to unresolved package map.
for (int i = 0; (caps != null) && (i < caps.length); i++)
{
if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
indexPackageCapability(m_unresolvedPkgIndexMap, module, caps[i]);
}
}
}
//System.out.println("UNRESOLVED PACKAGES:");
//dumpPackageIndexMap(m_unresolvedPkgIndexMap);
//System.out.println("RESOLVED PACKAGES:");
//dumpPackageIndexMap(m_resolvedPkgIndexMap);
}
public synchronized void removeModule(IModule module)
{
// Depending on whether the module is a fragment or not,
// we need to do different things.
// If module is a fragment, then remove from fragment map.
if (Util.isFragment(module))
{
IModule[] fragments = (IModule[]) m_fragmentMap.get(module.getSymbolicName());
fragments = removeModuleFromArray(fragments, module);
if (fragments.length == 0)
{
m_fragmentMap.remove(module.getSymbolicName());
}
else
{
m_fragmentMap.put(module.getSymbolicName(), fragments);
}
}
// If it is not a fragment, then we need remove its exports
// from the "resolved" and "unresolved" package maps, remove
// the module's dependencies on fragments and exporters,
// and remove the module from the module list.
else
{
m_moduleList.remove(module);
// Remove exports from package maps.
ICapability[] caps = module.getCapabilities();
for (int i = 0; (caps != null) && (i < caps.length); i++)
{
if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
// Get package name.
String pkgName = (String)
caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
// Remove from "unresolved" package map.
IModule[] modules = (IModule[]) m_unresolvedPkgIndexMap.get(pkgName);
if (modules != null)
{
modules = removeModuleFromArray(modules, module);
m_unresolvedPkgIndexMap.put(pkgName, modules);
}
// Remove from "resolved" package map.
modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
if (modules != null)
{
modules = removeModuleFromArray(modules, module);
m_resolvedPkgIndexMap.put(pkgName, modules);
}
}
}
// Remove the module from the "resolved" map.
m_resolvedCapMap.remove(module);
// Set fragments to null, which will remove the module from all
// of its dependent fragment modules.
try
{
((ModuleImpl) module).attachFragments(null);
}
catch (Exception ex)
{
m_logger.log(Logger.LOG_ERROR, "Error detaching fragments.", ex);
}
// Set wires to null, which will remove the module from all
// of its dependent modules.
((ModuleImpl) module).setWires(null);
}
// Close the module's content.
((ModuleImpl) module).close();
}
/**
* This method is used for installing system bundle extensions. It actually
* refreshes the system bundle module's capabilities in the resolver state
* to capture additional capabilities.
* @param module The module being refresh, which should always be the system bundle.
**/
synchronized void refreshSystemBundleModule(IModule module)
{
// The system bundle module should always be resolved, so we only need
// to update the resolved capability map.
ICapability[] caps = module.getCapabilities();
for (int i = 0; (caps != null) && (i < caps.length); i++)
{
ICapability[] resolvedCaps = (ICapability[]) m_resolvedCapMap.get(module);
resolvedCaps = addCapabilityToArray(resolvedCaps, caps[i]);
m_resolvedCapMap.put(module, resolvedCaps);
// If the capability is a package, then add the exporter module
// of the wire to the "resolved" package index and remove it
// from the "unresolved" package index.
if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
// Add to "resolved" package index.
indexPackageCapability(
m_resolvedPkgIndexMap,
module,
caps[i]);
}
}
}
private void dumpModuleIndexMap(Map moduleIndexMap)
{
for (Iterator i = moduleIndexMap.entrySet().iterator(); i.hasNext(); )
{
Map.Entry entry = (Map.Entry) i.next();
IModule[] modules = (IModule[]) entry.getValue();
if ((modules != null) && (modules.length > 0))
{
if (!((modules.length == 1) && modules[0].getId().equals("0")))
{
System.out.println(" " + entry.getKey());
for (int j = 0; j < modules.length; j++)
{
System.out.println(" " + modules[j]);
}
}
}
}
}
private void dumpPackageIndexMap(Map pkgIndexMap)
{
for (Iterator i = pkgIndexMap.entrySet().iterator(); i.hasNext(); )
{
Map.Entry entry = (Map.Entry) i.next();
IModule[] modules = (IModule[]) entry.getValue();
if ((modules != null) && (modules.length > 0))
{
if (!((modules.length == 1) && modules[0].getId().equals("0")))
{
System.out.println(" " + entry.getKey());
for (int j = 0; j < modules.length; j++)
{
System.out.println(" " + modules[j]);
}
}
}
}
}
public synchronized IModule[] getModules()
{
return (IModule[]) m_moduleList.toArray(new IModule[m_moduleList.size()]);
}
public synchronized void moduleResolved(IModule module)
{
if (module.isResolved())
{
// At this point, we need to remove all of the resolved module's
// capabilities from the "unresolved" package map and put them in
// in the "resolved" package map, with the exception of any
// package exports that are also imported. In that case we need
// to make sure that the import actually points to the resolved
// module and not another module. If it points to another module
// then the capability should be ignored, since the framework
// decided to honor the import and discard the export.
ICapability[] caps = module.getCapabilities();
// First remove all existing capabilities from the "unresolved" map.
for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
{
if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
// Get package name.
String pkgName = (String)
caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
// Remove the module's capability for the package.
m_unresolvedPkgIndexMap.put(
pkgName,
removeModuleFromArray(
(IModule[]) m_unresolvedPkgIndexMap.get(pkgName),
module));
}
}
// Next create a copy of the module's capabilities so we can
// null out any capabilities that should be ignored.
ICapability[] capsCopy = (caps == null) ? null : new ICapability[caps.length];
if (capsCopy != null)
{
System.arraycopy(caps, 0, capsCopy, 0, caps.length);
}
// Loop through the module's capabilities to determine which ones
// can be ignored by seeing which ones satifies the wire requirements.
// TODO: RB - Bug here because a requirement for a package need not overlap the
// capability for that package and this assumes it does. This might
// require us to introduce the notion of a substitutable capability.
IWire[] wires = module.getWires();
for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
{
// Loop through all wires to see if the current capability
// satisfies any of the wire requirements.
for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
{
// If one of the module's capabilities satifies the requirement
// for an existing wire, this means the capability was
// substituted with another provider by the resolver and
// the module's capability was not used. Therefore, we should
// null it here so it doesn't get added the list of resolved
// capabilities for this module.
if (wires[wireIdx].getRequirement().isSatisfied(capsCopy[capIdx]))
{
capsCopy[capIdx] = null;
break;
}
}
}
// Now loop through all capabilities and add them to the "resolved"
// capability and package index maps, ignoring any that were nulled out.
for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
{
if (capsCopy[capIdx] != null)
{
ICapability[] resolvedCaps = (ICapability[]) m_resolvedCapMap.get(module);
resolvedCaps = addCapabilityToArray(resolvedCaps, capsCopy[capIdx]);
m_resolvedCapMap.put(module, resolvedCaps);
// If the capability is a package, then add the exporter module
// of the wire to the "resolved" package index and remove it
// from the "unresolved" package index.
if (capsCopy[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
// Add to "resolved" package index.
indexPackageCapability(
m_resolvedPkgIndexMap,
module,
capsCopy[capIdx]);
}
}
}
}
//System.out.println("UNRESOLVED PACKAGES:");
//dumpPackageIndexMap(m_unresolvedPkgIndexMap);
//System.out.println("RESOLVED PACKAGES:");
//dumpPackageIndexMap(m_resolvedPkgIndexMap);
}
public synchronized PackageSource[] getResolvedCandidates(IRequirement req)
{
// Synchronized on the module manager to make sure that no
// modules are added, removed, or resolved.
PackageSource[] candidates = m_emptySources;
if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
&& (((Requirement) req).getTargetName() != null))
{
String pkgName = ((Requirement) req).getTargetName();
IModule[] modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
{
ICapability resolvedCap = Util.getSatisfyingCapability(modules[modIdx], req);
if (resolvedCap != null)
{
// TODO: RB - Is this permission check correct.
if ((System.getSecurityManager() != null) &&
!((BundleProtectionDomain) modules[modIdx].getSecurityContext()).impliesDirect(
new PackagePermission(pkgName,
PackagePermission.EXPORT)))
{
m_logger.log(Logger.LOG_DEBUG,
"PackagePermission.EXPORT denied for "
+ pkgName
+ "from " + modules[modIdx].getId());
}
else
{
PackageSource[] tmp = new PackageSource[candidates.length + 1];
System.arraycopy(candidates, 0, tmp, 0, candidates.length);
tmp[candidates.length] =
new PackageSource(modules[modIdx], resolvedCap);
candidates = tmp;
}
}
}
}
else
{
Iterator i = m_resolvedCapMap.entrySet().iterator();
while (i.hasNext())
{
Map.Entry entry = (Map.Entry) i.next();
IModule module = (IModule) entry.getKey();
ICapability[] resolvedCaps = (ICapability[]) entry.getValue();
for (int capIdx = 0; capIdx < resolvedCaps.length; capIdx++)
{
if (req.isSatisfied(resolvedCaps[capIdx]))
{
// TODO: RB - Is this permission check correct.
if (resolvedCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
(System.getSecurityManager() != null) &&
!((BundleProtectionDomain) module.getSecurityContext()).impliesDirect(
new PackagePermission(
(String) resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY),
PackagePermission.EXPORT)))
{
m_logger.log(Logger.LOG_DEBUG,
"PackagePermission.EXPORT denied for "
+ resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY)
+ "from " + module.getId());
}
else
{
PackageSource[] tmp = new PackageSource[candidates.length + 1];
System.arraycopy(candidates, 0, tmp, 0, candidates.length);
tmp[candidates.length] = new PackageSource(module, resolvedCaps[capIdx]);
candidates = tmp;
}
}
}
}
}
Arrays.sort(candidates);
return candidates;
}
public synchronized PackageSource[] getUnresolvedCandidates(IRequirement req)
{
// Get all modules.
IModule[] modules = null;
if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
(((Requirement) req).getTargetName() != null))
{
modules = (IModule[]) m_unresolvedPkgIndexMap.get(((Requirement) req).getTargetName());
}
else
{
modules = getModules();
}
// Create list of compatible providers.
PackageSource[] candidates = m_emptySources;
for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
{
// Get the module's export package for the target package.
ICapability cap = Util.getSatisfyingCapability(modules[modIdx], req);
// If compatible and it is not currently resolved, then add
// the unresolved candidate to the list.
if ((cap != null) && !modules[modIdx].isResolved())
{
PackageSource[] tmp = new PackageSource[candidates.length + 1];
System.arraycopy(candidates, 0, tmp, 0, candidates.length);
tmp[candidates.length] = new PackageSource(modules[modIdx], cap);
candidates = tmp;
}
}
Arrays.sort(candidates);
return candidates;
}
//
// Utility methods.
//
private void indexPackageCapability(Map map, IModule module, ICapability capability)
{
if (capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
String pkgName = (String)
capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
IModule[] modules = (IModule[]) map.get(pkgName);
// We want to add the module into the list of exporters
// in sorted order (descending version and ascending bundle
// identifier). Insert using a simple binary search algorithm.
if (modules == null)
{
modules = new IModule[] { module };
}
else
{
Version version = (Version)
capability.getProperties().get(ICapability.VERSION_PROPERTY);
Version middleVersion = null;
int top = 0, bottom = modules.length - 1, middle = 0;
while (top <= bottom)
{
middle = (bottom - top) / 2 + top;
middleVersion = (Version)
getExportPackageCapability(
modules[middle], pkgName)
.getProperties()
.get(ICapability.VERSION_PROPERTY);
// Sort in reverse version order.
int cmp = middleVersion.compareTo(version);
if (cmp < 0)
{
bottom = middle - 1;
}
else if (cmp == 0)
{
// Sort further by ascending bundle ID.
long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
long exportId = Util.getBundleIdFromModuleId(module.getId());
if (middleId < exportId)
{
top = middle + 1;
}
else
{
bottom = middle - 1;
}
}
else
{
top = middle + 1;
}
}
// Ignore duplicates.
if ((top >= modules.length) || (modules[top] != module))
{
IModule[] newMods = new IModule[modules.length + 1];
System.arraycopy(modules, 0, newMods, 0, top);
System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
newMods[top] = module;
modules = newMods;
}
}
map.put(pkgName, modules);
}
}
private void indexFragment(Map map, IModule module)
{
IModule[] modules = (IModule[]) map.get(module.getSymbolicName());
// We want to add the fragment into the list of matching
// fragments in sorted order (descending version and
// ascending bundle identifier). Insert using a simple
// binary search algorithm.
if (modules == null)
{
modules = new IModule[] { module };
}
else
{
Version version = module.getVersion();
Version middleVersion = null;
int top = 0, bottom = modules.length - 1, middle = 0;
while (top <= bottom)
{
middle = (bottom - top) / 2 + top;
middleVersion = modules[middle].getVersion();
// Sort in reverse version order.
int cmp = middleVersion.compareTo(version);
if (cmp < 0)
{
bottom = middle - 1;
}
else if (cmp == 0)
{
// Sort further by ascending bundle ID.
long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
long exportId = Util.getBundleIdFromModuleId(module.getId());
if (middleId < exportId)
{
top = middle + 1;
}
else
{
bottom = middle - 1;
}
}
else
{
top = middle + 1;
}
}
// Ignore duplicates.
if ((top >= modules.length) || (modules[top] != module))
{
IModule[] newMods = new IModule[modules.length + 1];
System.arraycopy(modules, 0, newMods, 0, top);
System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
newMods[top] = module;
modules = newMods;
}
}
map.put(module.getSymbolicName(), modules);
}
private static IModule[] removeModuleFromArray(IModule[] modules, IModule m)
{
if (modules == null)
{
return m_emptyModules;
}
int idx = -1;
do
{
idx = -1;
for (int i = 0; i < modules.length; i++)
{
if (modules[i] == m)
{
idx = i;
break;
}
}
if (idx >= 0)
{
// If this is the module, then point to empty list.
if ((modules.length - 1) == 0)
{
modules = m_emptyModules;
}
// Otherwise, we need to do some array copying.
else
{
IModule[] newModules = new IModule[modules.length - 1];
System.arraycopy(modules, 0, newModules, 0, idx);
if (idx < newModules.length)
{
System.arraycopy(
modules, idx + 1, newModules, idx, newModules.length - idx);
}
modules = newModules;
}
}
}
while (idx >= 0);
return modules;
}
public static ICapability getExportPackageCapability(IModule m, String pkgName)
{
ICapability[] caps = m.getCapabilities();
for (int i = 0; (caps != null) && (i < caps.length); i++)
{
if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
{
return caps[i];
}
}
return null;
}
private static ICapability[] addCapabilityToArray(ICapability[] caps, ICapability cap)
{
// Verify that the capability is not already in the array.
for (int i = 0; (caps != null) && (i < caps.length); i++)
{
if (caps[i].equals(cap))
{
return caps;
}
}
if (caps != null)
{
ICapability[] newCaps = new ICapability[caps.length + 1];
System.arraycopy(caps, 0, newCaps, 0, caps.length);
newCaps[caps.length] = cap;
caps = newCaps;
}
else
{
caps = new ICapability[] { cap };
}
return caps;
}
}