You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2008/12/23 21:07:49 UTC
svn commit: r729083 [3/3] - in
/felix/trunk/framework/src/main/java/org/apache/felix/framework: ./
searchpolicy/
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java?rev=729083&r1=729082&r2=729083&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java Tue Dec 23 12:07:49 2008
@@ -21,8 +21,8 @@
import java.net.URL;
import java.util.*;
-import org.apache.felix.framework.searchpolicy.R4SearchPolicyCore.ResolvedPackage;
-import org.apache.felix.framework.searchpolicy.R4SearchPolicyCore.PackageSource;
+import org.apache.felix.framework.searchpolicy.PackageSource;
+import org.apache.felix.framework.searchpolicy.ResolvedPackage;
import org.apache.felix.framework.util.CompoundEnumeration;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.Capability;
Added: felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java?rev=729083&view=auto
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java (added)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java Tue Dec 23 12:07:49 2008
@@ -0,0 +1,98 @@
+/*
+ * 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.searchpolicy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This utility class is a resolved package, which is comprised of a
+ * set of <tt>PackageSource</tt>s that is calculated by the resolver
+ * algorithm. A given resolved package may have a single package source,
+ * as is the case with imported packages, or it may have multiple
+ * package sources, as is the case with required bundles.
+ */
+class ResolvedPackage
+{
+ public String m_name = null;
+ public List m_sourceList = new ArrayList();
+
+ public ResolvedPackage(String name)
+ {
+ super();
+ m_name = name;
+ }
+
+ public boolean isSubset(ResolvedPackage rp)
+ {
+ if (m_sourceList.size() > rp.m_sourceList.size())
+ {
+ return false;
+ }
+ else if (!m_name.equals(rp.m_name))
+ {
+ return false;
+ }
+ // Determine if the target set of source modules is a subset.
+ return rp.m_sourceList.containsAll(m_sourceList);
+ }
+
+ public Object clone()
+ {
+ ResolvedPackage rp = new ResolvedPackage(m_name);
+ rp.m_sourceList.addAll(m_sourceList);
+ return rp;
+ }
+
+ public void merge(ResolvedPackage rp)
+ {
+ // Merge required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ if (!m_sourceList.contains(rp.m_sourceList.get(srcIdx)))
+ {
+ m_sourceList.add(rp.m_sourceList.get(srcIdx));
+ }
+ }
+ }
+
+ public String toString()
+ {
+ return toString("", new StringBuffer()).toString();
+ }
+
+ public StringBuffer toString(String padding, StringBuffer sb)
+ {
+ sb.append(padding);
+ sb.append(m_name);
+ sb.append(" from [");
+ for (int i = 0; i < m_sourceList.size(); i++)
+ {
+ PackageSource ps = (PackageSource) m_sourceList.get(i);
+ sb.append(ps.m_module);
+ if ((i + 1) < m_sourceList.size())
+ {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ return sb;
+ }
+}
Added: felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java?rev=729083&view=auto
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java (added)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java Tue Dec 23 12:07:49 2008
@@ -0,0 +1,1552 @@
+/*
+ * 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.searchpolicy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.Capability;
+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;
+
+public class Resolver
+{
+ private final Logger m_logger;
+
+ // Reusable empty array.
+ private static final PackageSource[] m_emptySources = new PackageSource[0];
+
+ public Resolver(Logger logger)
+ {
+ m_logger = logger;
+ }
+
+ // Returns a map of resolved bundles where the key is the module
+ // and the value is an array of wires.
+ // TODO: RESOLVER - The caller must ensure this is not called currently;
+ // this may not be important if no state is shared.
+ public Result resolve(ResolverState state, IModule rootModule) throws ResolveException
+ {
+ // This map will be used to hold the final wires for all
+ // resolved modules, which can then be used to fire resolved
+ // events outside of the synchronized block.
+ Map fragmentMap = null;
+
+ // If the module is already resolved, then we can just return.
+ if (state.isResolved(rootModule))
+ {
+ return null;
+ }
+
+ // The root module is either a host or a fragment. If it is a host,
+ // then we want to go ahead and resolve it. If it is a fragment, then
+ // we want to select a host and resolve the host instead.
+ IModule targetFragment = null;
+// TODO: FRAGMENT - Currently we just make a single selection of the available
+// fragments or hosts and try to resolve. In case of failure, we do not
+// backtrack. We will likely want to add backtracking.
+ if (Util.isFragment(rootModule))
+ {
+ targetFragment = rootModule;
+ List hostList = state.getPotentialHosts(targetFragment);
+ if (hostList.size() == 0)
+ {
+ throw new ResolveException("Unable to find host for fragment.", targetFragment, null);
+ }
+ rootModule = (IModule) hostList.get(0);
+ }
+
+ // Get the available fragments for the host.
+ fragmentMap = state.getPotentialFragments(rootModule);
+
+ // If the resolve was for a specific fragment, then
+ // eliminate all other potential candidate fragments
+ // of the same symbolic name.
+ if (targetFragment != null)
+ {
+ fragmentMap.put(
+ state.getBundleSymbolicName(targetFragment),
+ new IModule[] { targetFragment });
+ }
+
+ // This variable maps an unresolved module to a list of candidate
+ // sets, where there is one candidate set for each requirement that
+ // must be resolved. A candidate set contains the potential canidates
+ // available to resolve the requirement and the currently selected
+ // candidate index.
+ Map candidatesMap = new HashMap();
+
+ // The first step is to populate the candidates map. This
+ // will use the target module to populate the candidates map
+ // with all potential modules that need to be resolved as a
+ // result of resolving the target module. The key of the
+ // map is a potential module to be resolved and the value is
+ // a list of candidate sets, one for each of the module's
+ // requirements, where each candidate set contains the potential
+ // candidates for resolving the requirement. Not all modules in
+ // this map will be resolved, only the target module and
+ // any candidates selected to resolve its requirements and the
+ // transitive requirements this implies.
+ populateCandidatesMap(state, candidatesMap, rootModule);
+
+ // The next step is to use the candidates map to determine if
+ // the class space for the root module is consistent. This
+ // is an iterative process that transitively walks the "uses"
+ // relationships of all packages visible from the root module
+ // checking for conflicts. If a conflict is found, it "increments"
+ // the configuration of currently selected potential candidates
+ // and tests them again. If this method returns, then it has found
+ // a consistent set of candidates; otherwise, a resolve exception
+ // is thrown if it exhausts all possible combinations and could
+ // not find a consistent class space.
+ findConsistentClassSpace(state, candidatesMap, rootModule);
+
+ // The final step is to create the wires for the root module and
+ // transitively all modules that are to be resolved from the
+ // selected candidates for resolving the root module's imports.
+ // When this call returns, each module's wiring and resolved
+ // attributes are set. The resulting wiring map is used below
+ // to fire resolved events outside of the synchronized block.
+ // The resolved module wire map maps a module to its array of
+ // wires.
+ // Get a map of all modules and their resolved wires.
+ Map resolvedModuleWireMap =
+ populateWireMap(state, candidatesMap, rootModule, new HashMap());
+ return new Result(rootModule, fragmentMap, resolvedModuleWireMap);
+ }
+
+ // TODO: RESOLVER - Fix this return type.
+ // Return candidate wire in result[0] and wire map in result[1]
+ public Object[] resolveDynamicImport(ResolverState state, IModule importer, String pkgName)
+ throws ResolveException
+ {
+ PackageSource candidate = null;
+ Map resolvedModuleWireMap = null;
+
+ // We can only search dynamic imports if the bundle
+ // doesn't import, export, nor require the package in
+ // question. Check these conditions first.
+ if (isDynamicImportAllowed(importer, pkgName))
+ {
+ // Loop through the importer's dynamic requirements to determine if
+ // there is a matching one for the package from which we want to
+ // load a class.
+ IRequirement[] dynamics = importer.getDefinition().getDynamicRequirements();
+ for (int dynIdx = 0; (dynamics != null) && (dynIdx < dynamics.length); dynIdx++)
+ {
+ IRequirement target =
+ createDynamicRequirement(dynamics[dynIdx], pkgName);
+ if (target != null)
+ {
+ // See if there is a candidate exporter that satisfies the
+ // constrained dynamic requirement.
+ try
+ {
+ // Get "resolved" and "unresolved" candidates and put
+ // the "resolved" candidates first.
+ PackageSource[] resolved = state.getResolvedCandidates(target);
+ PackageSource[] unresolved = state.getUnresolvedCandidates(target);
+ PackageSource[] candidates = new PackageSource[resolved.length + unresolved.length];
+ System.arraycopy(resolved, 0, candidates, 0, resolved.length);
+ System.arraycopy(unresolved, 0, candidates, resolved.length, unresolved.length);
+
+ // Take the first candidate that can resolve.
+ for (int candIdx = 0;
+ (candidate == null) && (candIdx < candidates.length);
+ candIdx++)
+ {
+ try
+ {
+ // If a map is returned, then the candidate resolved
+ // consistently with the importer.
+ resolvedModuleWireMap =
+ resolveDynamicImportCandidate(
+ state, candidates[candIdx].m_module, importer);
+ if (resolvedModuleWireMap != null)
+ {
+ candidate = candidates[candIdx];
+ }
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore candidates that cannot resolve.
+ }
+ }
+
+ if (candidate != null)
+ {
+ // Create the wire and add it to the module.
+ Object[] result = new Object[2];
+ result[0] = new R4Wire(
+ importer, dynamics[dynIdx], candidate.m_module, candidate.m_capability);
+ result[1] = resolvedModuleWireMap;
+ return result;
+ }
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(Logger.LOG_ERROR, "Unable to dynamically import package.", ex);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean isDynamicImportAllowed(IModule importer, String pkgName)
+ {
+ // If any of the module exports this package, then we cannot
+ // attempt to dynamically import it.
+ ICapability[] caps = importer.getDefinition().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 false;
+ }
+ }
+ // If any of our wires have this package, then we cannot
+ // attempt to dynamically import it.
+ IWire[] wires = importer.getWires();
+ for (int i = 0; (wires != null) && (i < wires.length); i++)
+ {
+ if (wires[i].hasPackage(pkgName))
+ {
+ return false;
+ }
+ }
+ // Ok to attempt to dynamically import the package.
+ return true;
+ }
+
+ private static IRequirement createDynamicRequirement(IRequirement dynReq, String pkgName)
+ {
+ IRequirement req = null;
+
+ // First check to see if the dynamic requirement matches the
+ // package name; this means we have to do wildcard matching.
+ String dynPkgName = ((Requirement) dynReq).getPackageName();
+ boolean wildcard = (dynPkgName.lastIndexOf(".*") >= 0);
+ dynPkgName = (wildcard)
+ ? dynPkgName.substring(0, dynPkgName.length() - 2) : dynPkgName;
+ // If the dynamic requirement matches the package name, then
+ // create a new requirement for the specific package.
+ if (dynPkgName.equals("*") ||
+ pkgName.equals(dynPkgName) ||
+ (wildcard && pkgName.startsWith(dynPkgName + ".")))
+ {
+ // Create a new requirement based on the dynamic requirement,
+ // but substitute the precise package name for which we are
+ // looking, because it is not possible to use the potentially
+ // wildcarded version in the dynamic requirement.
+ R4Directive[] dirs = ((Requirement) dynReq).getDirectives();
+ R4Attribute[] attrs = ((Requirement) dynReq).getAttributes();
+ R4Attribute[] newAttrs = new R4Attribute[attrs.length];
+ System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
+ for (int attrIdx = 0; attrIdx < newAttrs.length; attrIdx++)
+ {
+ if (newAttrs[attrIdx].getName().equals(ICapability.PACKAGE_PROPERTY))
+ {
+ newAttrs[attrIdx] = new R4Attribute(
+ ICapability.PACKAGE_PROPERTY, pkgName, false);
+ break;
+ }
+ }
+ req = new Requirement(ICapability.PACKAGE_NAMESPACE, dirs, newAttrs);
+ }
+
+ return req;
+ }
+
+ private Map resolveDynamicImportCandidate(
+ ResolverState state, IModule provider, IModule importer)
+ throws ResolveException
+ {
+ // If the provider of the dynamically imported package is not
+ // resolved, then we need to calculate the candidates to resolve
+ // it and see if there is a consistent class space for the
+ // provider. If there is no consistent class space, then a resolve
+ // exception is thrown.
+ Map candidatesMap = new HashMap();
+ if (!state.isResolved(provider))
+ {
+ populateCandidatesMap(state, candidatesMap, provider);
+ findConsistentClassSpace(state, candidatesMap, provider);
+ }
+
+ // If the provider can be successfully resolved, then verify that
+ // its class space is consistent with the existing class space of the
+ // module that instigated the dynamic import.
+ Map moduleMap = new HashMap();
+ Map importerPkgMap = getModulePackages(moduleMap, importer, candidatesMap);
+
+ // Now we need to calculate the "uses" constraints of every package
+ // accessible to the provider module based on its current candidates.
+ Map usesMap = calculateUsesConstraints(provider, moduleMap, candidatesMap);
+
+ // Verify that none of the provider's implied "uses" constraints
+ // in the uses map conflict with anything in the importing module's
+ // package map.
+ for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+
+ // For the given "used" package, get that package from the
+ // importing module's package map, if present.
+ ResolvedPackage rp = (ResolvedPackage) importerPkgMap.get(entry.getKey());
+
+ // If the "used" package is also visible to the importing
+ // module, make sure there is no conflicts in the implied
+ // "uses" constraints.
+ if (rp != null)
+ {
+ // Clone the resolve package so we can modify it.
+ rp = (ResolvedPackage) rp.clone();
+
+ // Loop through all implied "uses" constraints for the current
+ // "used" package and verify that all package sources are
+ // compatible with the package source of the importing module's
+ // package map.
+ List constraintList = (List) entry.getValue();
+ for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
+ {
+ // Get a specific "uses" constraint for the current "used"
+ // package.
+ ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
+ // Determine if the implied "uses" constraint is compatible with
+ // the improting module's package sources for the given "used"
+ // package. They are compatible if one is the subset of the other.
+ // Retain the union of the two sets if they are compatible.
+ if (rpUses.isSubset(rp))
+ {
+ // Do nothing because we already have the superset.
+ }
+ else if (rp.isSubset(rpUses))
+ {
+ // Keep the superset, i.e., the union.
+ rp.m_sourceList.clear();
+ rp.m_sourceList.addAll(rpUses.m_sourceList);
+ }
+ else
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + importer
+ + " detected; module can see "
+ + rp + " and " + rpUses);
+ return null;
+ }
+ }
+ }
+ }
+
+ return populateWireMap(state, candidatesMap, provider, new HashMap());
+ }
+
+ private static void populateCandidatesMap(
+ ResolverState state, Map candidatesMap, IModule module)
+ throws ResolveException
+ {
+ // Detect cycles.
+ if (candidatesMap.get(module) != null)
+ {
+ return;
+ }
+
+ // List to hold the resolving candidate sets for the module's
+ // requirements.
+ List candSetList = new ArrayList();
+
+ // Even though the candidate set list is currently empty, we
+ // record it in the candidates map early so we can use it to
+ // detect cycles.
+ candidatesMap.put(module, candSetList);
+
+ // Loop through each requirement and calculate its resolving
+ // set of candidates.
+ IRequirement[] reqs = module.getDefinition().getRequirements();
+ for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
+ {
+ // Get the candidates from the "resolved" and "unresolved"
+ // package maps. The "resolved" candidates have higher priority
+ // than "unresolved" ones, so put the "resolved" candidates
+ // at the front of the list of candidates.
+ PackageSource[] resolved = state.getResolvedCandidates(reqs[reqIdx]);
+ PackageSource[] unresolved = state.getUnresolvedCandidates(reqs[reqIdx]);
+ PackageSource[] candidates = new PackageSource[resolved.length + unresolved.length];
+ System.arraycopy(resolved, 0, candidates, 0, resolved.length);
+ System.arraycopy(unresolved, 0, candidates, resolved.length, unresolved.length);
+
+ // If we have candidates, then we need to recursively populate
+ // the resolver map with each of them.
+ ResolveException rethrow = null;
+ if (candidates.length > 0)
+ {
+ for (int candIdx = 0; candIdx < candidates.length; candIdx++)
+ {
+ try
+ {
+ // Only populate the resolver map with modules that
+ // are not already resolved.
+ if (!state.isResolved(candidates[candIdx].m_module))
+ {
+ populateCandidatesMap(state, candidatesMap, candidates[candIdx].m_module);
+ }
+ }
+ catch (ResolveException ex)
+ {
+ // If we received a resolve exception, then the
+ // current candidate is not resolvable for some
+ // reason and should be removed from the list of
+ // candidates. For now, just null it.
+ candidates[candIdx] = null;
+ rethrow = ex;
+ }
+ }
+
+ // Remove any nulled candidates to create the final list
+ // of available candidates.
+ candidates = shrinkCandidateArray(candidates);
+ }
+
+ // If no candidates exist at this point, then throw a
+ // resolve exception unless the import is optional.
+ if ((candidates.length == 0) && !reqs[reqIdx].isOptional())
+ {
+ // If we have received an exception while trying to populate
+ // the resolver map, rethrow that exception since it might
+ // be useful. NOTE: This is not necessarily the "only"
+ // correct exception, since it is possible that multiple
+ // candidates were not resolvable, but it is better than
+ // nothing.
+ if (rethrow != null)
+ {
+ throw rethrow;
+ }
+ else
+ {
+ throw new ResolveException(
+ "Unable to resolve.", module, reqs[reqIdx]);
+ }
+ }
+ else if (candidates.length > 0)
+ {
+ candSetList.add(
+ new CandidateSet(module, reqs[reqIdx], candidates));
+ }
+ }
+ }
+
+ private void findConsistentClassSpace(
+ ResolverState state, Map candidatesMap, IModule rootModule)
+ throws ResolveException
+ {
+ List candidatesList = null;
+
+ // The reusable module map maps a module to a map of
+ // resolved packages that are accessible by the given
+ // module. The set of resolved packages is calculated
+ // from the current candidates of the candidates map
+ // and the module's metadata.
+ Map moduleMap = new HashMap();
+
+ // Reusable map used to test for cycles.
+ Map cycleMap = new HashMap();
+
+ // Test the current potential candidates to determine if they
+ // are consistent. Keep looping until we find a consistent
+ // set or an exception is thrown.
+ while (!isSingletonConsistent(state, rootModule, moduleMap, candidatesMap) ||
+ !isClassSpaceConsistent(rootModule, moduleMap, cycleMap, candidatesMap))
+ {
+ // The incrementCandidateConfiguration() method requires
+ // ordered access to the candidates map, so we will create
+ // a reusable list once right here.
+ if (candidatesList == null)
+ {
+ candidatesList = new ArrayList();
+ for (Iterator iter = candidatesMap.entrySet().iterator();
+ iter.hasNext(); )
+ {
+ candidatesList.add(((Map.Entry) iter.next()).getValue());
+ }
+ }
+
+ // Increment the candidate configuration so we can test again.
+ incrementCandidateConfiguration(candidatesList);
+
+ // Clear the module map.
+ moduleMap.clear();
+
+ // Clear the cycle map.
+ cycleMap.clear();
+ }
+ }
+
+ /**
+ * This methd checks to see if the target module and any of the candidate
+ * modules to resolve its dependencies violate any singleton constraints.
+ * Actually, it just creates a map of resolved singleton modules and then
+ * delegates all checking to another recursive method.
+ *
+ * @param targetModule the module that is the root of the tree of modules to check.
+ * @param moduleMap a map to cache the package space of each module.
+ * @param candidatesMap a map containing the all candidates to resolve all
+ * dependencies for all modules.
+ * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
+ * <tt>false</tt> otherwise.
+ **/
+ private boolean isSingletonConsistent(
+ ResolverState state, IModule targetModule, Map moduleMap, Map candidatesMap)
+ {
+ // Create a map of all resolved singleton modules.
+ Map singletonMap = new HashMap();
+ IModule[] modules = state.getModules();
+ for (int i = 0; (modules != null) && (i < modules.length); i++)
+ {
+ if (state.isResolved(modules[i]) && isSingleton(modules[i]))
+ {
+ String symName = state.getBundleSymbolicName(modules[i]);
+ singletonMap.put(symName, symName);
+ }
+ }
+
+ return areCandidatesSingletonConsistent(
+ state, targetModule, singletonMap, moduleMap, new HashMap(), candidatesMap);
+ }
+
+ /**
+ * This method recursive checks the target module and all of its transitive
+ * dependency modules to verify that they do not violate a singleton constraint.
+ * If the target module is a singleton, then it checks that againts existing
+ * singletons. Then it checks all current unresolved candidates recursively.
+ *
+ * @param targetModule the module that is the root of the tree of modules to check.
+ * @param singletonMap the current map of singleton symbolic names.
+ * @param moduleMap a map to cache the package space of each module.
+ * @param cycleMap a map to detect cycles.
+ * @param candidatesMap a map containing the all candidates to resolve all
+ * dependencies for all modules.
+ * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
+ * <tt>false</tt> otherwise.
+ **/
+ private boolean areCandidatesSingletonConsistent(
+ ResolverState state, IModule targetModule,
+ Map singletonMap, Map moduleMap, Map cycleMap, Map candidatesMap)
+ {
+ // If we are in a cycle, then assume true for now.
+ if (cycleMap.get(targetModule) != null)
+ {
+ return true;
+ }
+
+ // Record the target module in the cycle map.
+ cycleMap.put(targetModule, targetModule);
+
+ // Check to see if the targetModule violates a singleton.
+ // If not and it is a singleton, then add it to the singleton
+ // map since it will constrain other singletons.
+ String symName = state.getBundleSymbolicName(targetModule);
+ boolean isSingleton = isSingleton(targetModule);
+ if (isSingleton && singletonMap.containsKey(symName))
+ {
+ return false;
+ }
+ else if (isSingleton)
+ {
+ singletonMap.put(symName, symName);
+ }
+
+ // Get the package space of the target module.
+ Map pkgMap = null;
+ try
+ {
+ pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
+ }
+ catch (ResolveException ex)
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule + " detected.",
+ ex);
+ return false;
+ }
+
+ // Loop through all of the target module's accessible packages and
+ // verify that all package sources are consistent.
+ for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ // Get the resolved package, which contains the set of all
+ // package sources for the given package.
+ ResolvedPackage rp = (ResolvedPackage) entry.getValue();
+ // Loop through each package source and test if it is consistent.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ // If the module for this package source is not resolved, then
+ // we have to see if resolving it would violate a singleton
+ // constraint.
+ PackageSource ps = (PackageSource) rp.m_sourceList.get(srcIdx);
+ if (!state.isResolved(ps.m_module))
+ {
+ return areCandidatesSingletonConsistent(state, ps.m_module, singletonMap, moduleMap, cycleMap, candidatesMap);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if the specified module is a singleton
+ * (i.e., directive singleton:=true in Bundle-SymbolicName).
+ *
+ * @param module the module to check for singleton status.
+ * @return true if the module is a singleton, false otherwise.
+ **/
+ private static boolean isSingleton(IModule module)
+ {
+ final ICapability[] modCaps = Util.getCapabilityByNamespace(
+ module, Capability.MODULE_NAMESPACE);
+ if (modCaps == null || modCaps.length == 0)
+ {
+ // this should never happen?
+ return false;
+ }
+ final R4Directive[] dirs = ((Capability) modCaps[0]).getDirectives();
+ for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.length); dirIdx++)
+ {
+ if (dirs[dirIdx].getName().equalsIgnoreCase(Constants.SINGLETON_DIRECTIVE)
+ && Boolean.valueOf(dirs[dirIdx].getValue()).booleanValue())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isClassSpaceConsistent(
+ IModule targetModule, Map moduleMap, Map cycleMap, Map candidatesMap)
+ {
+//System.out.println("isClassSpaceConsistent("+targetModule+")");
+ // If we are in a cycle, then assume true for now.
+ if (cycleMap.get(targetModule) != null)
+ {
+ return true;
+ }
+
+ // Record the target module in the cycle map.
+ cycleMap.put(targetModule, targetModule);
+
+ // Get the package map for the target module, which is a
+ // map of all packages accessible to the module and their
+ // associated package sources.
+ Map pkgMap = null;
+ try
+ {
+ pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
+ }
+ catch (ResolveException ex)
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule + " detected.",
+ ex);
+ return false;
+ }
+
+ // Loop through all of the target module's accessible packages and
+ // verify that all package sources are consistent.
+ for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ // Get the resolved package, which contains the set of all
+ // package sources for the given package.
+ ResolvedPackage rp = (ResolvedPackage) entry.getValue();
+ // Loop through each package source and test if it is consistent.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ PackageSource ps = (PackageSource) rp.m_sourceList.get(srcIdx);
+ if (!isClassSpaceConsistent(ps.m_module, moduleMap, cycleMap, candidatesMap))
+ {
+ return false;
+ }
+ }
+ }
+
+ // Now we need to calculate the "uses" constraints of every package
+ // accessible to the target module based on the current candidates.
+ Map usesMap = null;
+ try
+ {
+ usesMap = calculateUsesConstraints(targetModule, moduleMap, candidatesMap);
+ }
+ catch (ResolveException ex)
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule + " detected.",
+ ex);
+ return false;
+ }
+
+ // Verify that none of the implied "uses" constraints in the uses map
+ // conflict with anything in the target module's package map.
+ for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+
+ // For the given "used" package, get that package from the
+ // target module's package map, if present.
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
+
+ // If the "used" package is also visible to the target module,
+ // make sure there is no conflicts in the implied "uses"
+ // constraints.
+ if (rp != null)
+ {
+ // Clone the resolve package so we can modify it.
+ rp = (ResolvedPackage) rp.clone();
+
+ // Loop through all implied "uses" constraints for the current
+ // "used" package and verify that all package sources are
+ // compatible with the package source of the root module's
+ // package map.
+ List constraintList = (List) entry.getValue();
+ for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
+ {
+ // Get a specific "uses" constraint for the current "used"
+ // package.
+ ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
+ // Determine if the implied "uses" constraint is compatible with
+ // the target module's package sources for the given "used"
+ // package. They are compatible if one is the subset of the other.
+ // Retain the union of the two sets if they are compatible.
+ if (rpUses.isSubset(rp))
+ {
+ // Do nothing because we already have the superset.
+ }
+ else if (rp.isSubset(rpUses))
+ {
+ // Keep the superset, i.e., the union.
+ rp.m_sourceList.clear();
+ rp.m_sourceList.addAll(rpUses.m_sourceList);
+ }
+ else
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule
+ + " detected; module can see "
+ + rp + " and " + rpUses);
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static Map calculateUsesConstraints(
+ IModule targetModule, Map moduleMap, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateUsesConstraints("+targetModule+")");
+ // Map to store calculated uses constraints. This maps a
+ // package name to a list of resolved packages, where each
+ // resolved package represents a constraint on anyone
+ // importing the given package name. This map is returned
+ // by this method.
+ Map usesMap = new HashMap();
+
+ // Re-usable map to detect cycles.
+ Map cycleMap = new HashMap();
+
+ // Get all packages accessible by the target module.
+ Map pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
+
+ // Each package accessible from the target module is potentially
+ // comprised of one or more modules, called package sources. The
+ // "uses" constraints implied by all package sources must be
+ // calculated and combined to determine the complete set of implied
+ // "uses" constraints for each package accessible by the target module.
+ for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ResolvedPackage rp = (ResolvedPackage) entry.getValue();
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ usesMap = calculateUsesConstraints(
+ (PackageSource) rp.m_sourceList.get(srcIdx),
+ moduleMap, usesMap, cycleMap, candidatesMap);
+ }
+ }
+ return usesMap;
+ }
+
+ private static Map calculateUsesConstraints(
+ PackageSource psTarget, Map moduleMap, Map usesMap,
+ Map cycleMap, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateUsesConstraints2("+psTarget.m_module+")");
+ // If we are in a cycle, then return for now.
+ if (cycleMap.get(psTarget) != null)
+ {
+ return usesMap;
+ }
+
+ // Record the target package source in the cycle map.
+ cycleMap.put(psTarget, psTarget);
+
+ // Get all packages accessible from the module of the
+ // target package source.
+ Map pkgMap = getModulePackages(moduleMap, psTarget.m_module, candidatesMap);
+
+ // Get capability (i.e., package) of the target package source.
+ Capability cap = (Capability) psTarget.m_capability;
+
+ // Loop through all "used" packages of the capability.
+ for (int i = 0; i < cap.getUses().length; i++)
+ {
+ // The target package source module should have a resolved package
+ // for the "used" package in its set of accessible packages,
+ // since it claims to use it, so get the associated resolved
+ // package.
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(cap.getUses()[i]);
+
+ // In general, the resolved package should not be null,
+ // but check for safety.
+ if (rp != null)
+ {
+ // First, iterate through all package sources for the resolved
+ // package associated with the current "used" package and calculate
+ // and combine the "uses" constraints for each package source.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ usesMap = calculateUsesConstraints(
+ (PackageSource) rp.m_sourceList.get(srcIdx),
+ moduleMap, usesMap, cycleMap, candidatesMap);
+ }
+
+ // Then, add the resolved package for the current "used" package
+ // as a "uses" constraint too; add it to an existing constraint
+ // list if the current "used" package is already in the uses map.
+ List constraintList = (List) usesMap.get(cap.getUses()[i]);
+ if (constraintList == null)
+ {
+ constraintList = new ArrayList();
+ }
+ constraintList.add(rp);
+ usesMap.put(cap.getUses()[i], constraintList);
+ }
+ }
+
+ return usesMap;
+ }
+
+ private static Map getModulePackages(Map moduleMap, IModule module, Map candidatesMap)
+ throws ResolveException
+ {
+ Map map = (Map) moduleMap.get(module);
+
+ if (map == null)
+ {
+ map = calculateModulePackages(module, candidatesMap);
+ moduleMap.put(module, map);
+ }
+ return map;
+ }
+
+ /**
+ * <p>
+ * Calculates the module's set of accessible packages and their
+ * assocaited package sources. This method uses the current candidates
+ * for resolving the module's requirements from the candidate map
+ * to calculate the module's accessible packages.
+ * </p>
+ * @param module the module whose package map is to be calculated.
+ * @param candidatesMap the map of potential candidates for resolving
+ * the module's requirements.
+ * @return a map of the packages accessible to the specified module where
+ * the key of the map is the package name and the value of the map
+ * is a ResolvedPackage.
+ **/
+ private static Map calculateModulePackages(IModule module, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateModulePackages("+module+")");
+ Map importedPackages = calculateImportedPackages(module, candidatesMap);
+ Map exportedPackages = calculateExportedPackages(module);
+ Map requiredPackages = calculateRequiredPackages(module, candidatesMap);
+
+ // Merge exported packages into required packages. If a package is both
+ // exported and required, then append the exported source to the end of
+ // the require package sources; otherwise just add it to the package map.
+ for (Iterator i = exportedPackages.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ ResolvedPackage rpReq = (ResolvedPackage) requiredPackages.get(entry.getKey());
+ if (rpReq != null)
+ {
+ // Merge exported and required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ ResolvedPackage rpExport = (ResolvedPackage) entry.getValue();
+ rpReq.merge(rpExport);
+ }
+ else
+ {
+ requiredPackages.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ // Merge imported packages into required packages. Imports overwrite
+ // any required and/or exported package.
+ for (Iterator i = importedPackages.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ requiredPackages.put(entry.getKey(), entry.getValue());
+ }
+
+ return requiredPackages;
+ }
+
+ private static Map calculateImportedPackages(IModule targetModule, Map candidatesMap)
+ throws ResolveException
+ {
+ return (candidatesMap.get(targetModule) == null)
+ ? calculateImportedPackagesResolved(targetModule)
+ : calculateImportedPackagesUnresolved(targetModule, candidatesMap);
+ }
+
+ private static Map calculateImportedPackagesUnresolved(IModule targetModule, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateImportedPackagesUnresolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Get the candidate set list to get all candidates for
+ // all of the target module's requirements.
+ List candSetList = (List) candidatesMap.get(targetModule);
+
+ // Loop through all candidate sets that represent import dependencies
+ // for the target module and add the current candidate's package source
+ // to the imported package map.
+ for (int candSetIdx = 0; (candSetList != null) && (candSetIdx < candSetList.size()); candSetIdx++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+ PackageSource ps = cs.m_candidates[cs.m_idx];
+
+ if (ps.m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ ps.m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
+
+ ResolvedPackage rp = new ResolvedPackage(pkgName);
+ rp.m_sourceList.add(ps);
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateImportedPackagesResolved(IModule targetModule)
+ throws ResolveException
+ {
+//System.out.println("calculateImportedPackagesResolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through the target module's wires for package
+ // dependencies and add the resolved package source to the
+ // imported package map.
+ IWire[] wires = targetModule.getWires();
+ for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
+ {
+ if (wires[wireIdx].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ wires[wireIdx].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(wires[wireIdx].getExporter(), wires[wireIdx].getCapability()));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateExportedPackages(IModule targetModule)
+ {
+//System.out.println("calculateExportedPackages("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through the target module's capabilities that represent
+ // exported packages and add them to the exported package map.
+ ICapability[] caps = targetModule.getDefinition().getCapabilities();
+ for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+ {
+ if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(targetModule, caps[capIdx]));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateRequiredPackages(IModule targetModule, Map candidatesMap)
+ {
+ return (candidatesMap.get(targetModule) == null)
+ ? calculateRequiredPackagesResolved(targetModule)
+ : calculateRequiredPackagesUnresolved(targetModule, candidatesMap);
+ }
+
+ private static Map calculateRequiredPackagesUnresolved(IModule targetModule, Map candidatesMap)
+ {
+//System.out.println("calculateRequiredPackagesUnresolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through target module's candidate list for candidates
+ // for its module dependencies and merge re-exported packages.
+ List candSetList = (List) candidatesMap.get(targetModule);
+ for (int candSetIdx = 0; (candSetList != null) && (candSetIdx < candSetList.size()); candSetIdx++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+ PackageSource ps = cs.m_candidates[cs.m_idx];
+
+ // If the capabaility is a module dependency, then flatten it to packages.
+ if (ps.m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Calculate transitively required packages.
+ Map cycleMap = new HashMap();
+ cycleMap.put(targetModule, targetModule);
+ Map requireMap =
+ calculateExportedAndReexportedPackages(
+ ps, candidatesMap, cycleMap);
+
+ // Take the flattened required package map for the current
+ // module dependency and merge it into the existing map
+ // of required packages.
+ for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
+ if (rp != null)
+ {
+ // Merge required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ pkgMap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateRequiredPackagesResolved(IModule targetModule)
+ {
+//System.out.println("calculateRequiredPackagesResolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through target module's wires for module dependencies
+ // and merge re-exported packages.
+ IWire[] wires = targetModule.getWires();
+ for (int i = 0; (wires != null) && (i < wires.length); i++)
+ {
+ // If the wire is a module dependency, then flatten it to packages.
+ if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Calculate transitively required packages.
+ // We can call calculateExportedAndReexportedPackagesResolved()
+ // directly, since we know all dependencies have to be resolved
+ // because this module itself is resolved.
+ Map cycleMap = new HashMap();
+ cycleMap.put(targetModule, targetModule);
+ Map requireMap =
+ calculateExportedAndReexportedPackagesResolved(
+ wires[i].getExporter(), cycleMap);
+
+ // Take the flattened required package map for the current
+ // module dependency and merge it into the existing map
+ // of required packages.
+ for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
+ if (rp != null)
+ {
+ // Merge required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ pkgMap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateExportedAndReexportedPackages(
+ PackageSource psTarget, Map candidatesMap, Map cycleMap)
+ {
+ return (candidatesMap.get(psTarget.m_module) == null)
+ ? calculateExportedAndReexportedPackagesResolved(psTarget.m_module, cycleMap)
+ : calculateExportedAndReexportedPackagesUnresolved(psTarget, candidatesMap, cycleMap);
+ }
+
+ private static Map calculateExportedAndReexportedPackagesUnresolved(
+ PackageSource psTarget, Map candidatesMap, Map cycleMap)
+ {
+//System.out.println("calculateExportedAndReexportedPackagesUnresolved("+psTarget.m_module+")");
+ Map pkgMap = new HashMap();
+
+ if (cycleMap.get(psTarget.m_module) != null)
+ {
+ return pkgMap;
+ }
+
+ cycleMap.put(psTarget.m_module, psTarget.m_module);
+
+ // Loop through all current candidates for target module's dependencies
+ // and calculate the module's complete set of required packages (and
+ // their associated package sources) and the complete set of required
+ // packages to be re-exported.
+ Map allRequiredMap = new HashMap();
+ Map reexportedPkgMap = new HashMap();
+ List candSetList = (List) candidatesMap.get(psTarget.m_module);
+ for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+ PackageSource ps = cs.m_candidates[cs.m_idx];
+
+ // If the candidate is resolving a module dependency, then
+ // flatten the required packages if they are re-exported.
+ if (ps.m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Determine if required packages are re-exported.
+ boolean reexport = false;
+ R4Directive[] dirs = ((Requirement) cs.m_requirement).getDirectives();
+ for (int dirIdx = 0;
+ !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
+ {
+ if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
+ && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
+ {
+ reexport = true;
+ }
+ }
+
+ // Recursively calculate the required packages for the
+ // current candidate.
+ Map requiredMap = calculateExportedAndReexportedPackages(ps, candidatesMap, cycleMap);
+
+ // Merge the candidate's exported and required packages
+ // into the complete set of required packages.
+ for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ String pkgName = (String) entry.getKey();
+
+ // Merge the current set of required packages into
+ // the overall complete set of required packages.
+ // We calculate all the required packages, because
+ // despite the fact that some packages will be required
+ // "privately" and some will be required "reexport", any
+ // re-exported package sources will ultimately need to
+ // be combined with privately required package sources,
+ // if the required packages overlap. This is one of the
+ // bad things about require-bundle behavior, it does not
+ // necessarily obey the visibility rules declared in the
+ // dependency.
+ ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
+ if (rp != null)
+ {
+ // Create the union of all package sources.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ // Add package to required map.
+ allRequiredMap.put(pkgName, entry.getValue());
+ }
+
+ // Keep track of all required packages to be re-exported.
+ // All re-exported packages will need to be merged into the
+ // target module's package map and become part of its overall
+ // export signature.
+ if (reexport)
+ {
+ reexportedPkgMap.put(pkgName, pkgName);
+ }
+ }
+ }
+ }
+
+ // For the target module we have now calculated its entire set
+ // of required packages and their associated package sources in
+ // allRequiredMap and have calculated all packages to be re-exported
+ // in reexportedPkgMap. Add all re-exported required packages to the
+ // target module's package map since they will be part of its export
+ // signature.
+ for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ String pkgName = (String) ((Map.Entry) iter.next()).getKey();
+ pkgMap.put(pkgName, allRequiredMap.get(pkgName));
+ }
+
+ // Now loop through the target module's export package capabilities and
+ // add the target module as a package source for any exported packages.
+ ICapability[] candCaps = psTarget.m_module.getDefinition().getCapabilities();
+ for (int capIdx = 0; (candCaps != null) && (capIdx < candCaps.length); capIdx++)
+ {
+ if (candCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ candCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(psTarget.m_module, candCaps[capIdx]));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateExportedAndReexportedPackagesResolved(
+ IModule targetModule, Map cycleMap)
+ {
+//System.out.println("calculateExportedAndRequiredPackagesResolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ if (cycleMap.get(targetModule) != null)
+ {
+ return pkgMap;
+ }
+
+ cycleMap.put(targetModule, targetModule);
+
+ // Loop through all wires for the target module's module dependencies
+ // and calculate the module's complete set of required packages (and
+ // their associated package sources) and the complete set of required
+ // packages to be re-exported.
+ Map allRequiredMap = new HashMap();
+ Map reexportedPkgMap = new HashMap();
+ IWire[] wires = targetModule.getWires();
+ for (int i = 0; (wires != null) && (i < wires.length); i++)
+ {
+ // If the wire is a module dependency, then flatten it to packages.
+ if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Determine if required packages are re-exported.
+ boolean reexport = false;
+ R4Directive[] dirs = ((Requirement) wires[i].getRequirement()).getDirectives();
+ for (int dirIdx = 0;
+ !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
+ {
+ if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
+ && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
+ {
+ reexport = true;
+ }
+ }
+
+ // Recursively calculate the required packages for the
+ // wire's exporting module.
+ Map requiredMap = calculateExportedAndReexportedPackagesResolved(wires[i].getExporter(), cycleMap);
+
+ // Merge the wires exported and re-exported packages
+ // into the complete set of required packages.
+ for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ String pkgName = (String) entry.getKey();
+
+ // Merge the current set of required packages into
+ // the overall complete set of required packages.
+ // We calculate all the required packages, because
+ // despite the fact that some packages will be required
+ // "privately" and some will be required "reexport", any
+ // re-exported package sources will ultimately need to
+ // be combined with privately required package sources,
+ // if the required packages overlap. This is one of the
+ // bad things about require-bundle behavior, it does not
+ // necessarily obey the visibility rules declared in the
+ // dependency.
+ ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
+ if (rp != null)
+ {
+ // Create the union of all package sources.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ // Add package to required map.
+ allRequiredMap.put(pkgName, entry.getValue());
+ }
+
+ // Keep track of all required packages to be re-exported.
+ // All re-exported packages will need to be merged into the
+ // target module's package map and become part of its overall
+ // export signature.
+ if (reexport)
+ {
+ reexportedPkgMap.put(pkgName, pkgName);
+ }
+ }
+ }
+ }
+
+ // For the target module we have now calculated its entire set
+ // of required packages and their associated package sources in
+ // allRequiredMap and have calculated all packages to be re-exported
+ // in reexportedPkgMap. Add all re-exported required packages to the
+ // target module's package map since they will be part of its export
+ // signature.
+ for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ String pkgName = (String) ((Map.Entry) iter.next()).getKey();
+ pkgMap.put(pkgName, allRequiredMap.get(pkgName));
+ }
+
+ // Now loop through the target module's export package capabilities and
+ // add the target module as a package source for any exported packages.
+ ICapability[] caps = targetModule.getDefinition().getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(targetModule, caps[i]));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateCandidateRequiredPackages(IModule module, PackageSource psTarget, Map candidatesMap)
+ {
+//System.out.println("calculateCandidateRequiredPackages("+module+")");
+ Map cycleMap = new HashMap();
+ cycleMap.put(module, module);
+ return calculateExportedAndReexportedPackages(psTarget, candidatesMap, cycleMap);
+ }
+
+ private static void incrementCandidateConfiguration(List resolverList)
+ throws ResolveException
+ {
+ for (int i = 0; i < resolverList.size(); i++)
+ {
+ List candSetList = (List) resolverList.get(i);
+ for (int j = 0; j < candSetList.size(); j++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(j);
+ // See if we can increment the candidate set, without overflowing
+ // the candidate array bounds.
+ if ((cs.m_idx + 1) < cs.m_candidates.length)
+ {
+ cs.m_idx++;
+ return;
+ }
+ // If the index will overflow the candidate array bounds,
+ // then set the index back to zero and try to increment
+ // the next candidate.
+ else
+ {
+ cs.m_idx = 0;
+ }
+ }
+ }
+ throw new ResolveException(
+ "Unable to resolve due to constraint violation.", null, null);
+ }
+
+ private static Map populateWireMap(
+ ResolverState state, Map candidatesMap, IModule importer, Map wireMap)
+ {
+ // If the module is already resolved or it is part of
+ // a cycle, then just return the wire map.
+ if (state.isResolved(importer) || (wireMap.get(importer) != null))
+ {
+ return wireMap;
+ }
+
+ List candSetList = (List) candidatesMap.get(importer);
+ List moduleWires = new ArrayList();
+ List packageWires = new ArrayList();
+ IWire[] wires = new IWire[candSetList.size()];
+
+ // Put the module in the wireMap with an empty wire array;
+ // we do this early so we can use it to detect cycles.
+ wireMap.put(importer, wires);
+
+ // Loop through each candidate Set and create a wire
+ // for the selected candidate for the associated import.
+ for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
+ {
+ // Get the current candidate set.
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+
+ // Create a wire for the current candidate based on the type
+ // of requirement it resolves.
+ if (cs.m_requirement.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ moduleWires.add(new R4WireModule(
+ importer,
+ cs.m_requirement,
+ cs.m_candidates[cs.m_idx].m_module,
+ cs.m_candidates[cs.m_idx].m_capability,
+ calculateCandidateRequiredPackages(importer, cs.m_candidates[cs.m_idx], candidatesMap)));
+ }
+ else
+ {
+ // Add wire for imported package.
+ packageWires.add(new R4Wire(
+ importer,
+ cs.m_requirement,
+ cs.m_candidates[cs.m_idx].m_module,
+ cs.m_candidates[cs.m_idx].m_capability));
+ }
+
+ // Create any necessary wires for the selected candidate module.
+ wireMap = populateWireMap(
+ state, candidatesMap, cs.m_candidates[cs.m_idx].m_module, wireMap);
+ }
+
+ packageWires.addAll(moduleWires);
+ wireMap.put(importer, packageWires.toArray(wires));
+
+ return wireMap;
+ }
+
+ //
+ // Utility methods.
+ //
+
+ private static PackageSource[] shrinkCandidateArray(PackageSource[] candidates)
+ {
+ if (candidates == null)
+ {
+ return m_emptySources;
+ }
+
+ // Move all non-null values to one end of the array.
+ int lower = 0;
+ for (int i = 0; i < candidates.length; i++)
+ {
+ if (candidates[i] != null)
+ {
+ candidates[lower++] = candidates[i];
+ }
+ }
+
+ if (lower == 0)
+ {
+ return m_emptySources;
+ }
+
+ // Copy non-null values into a new array and return.
+ PackageSource[] newCandidates= new PackageSource[lower];
+ System.arraycopy(candidates, 0, newCandidates, 0, lower);
+ return newCandidates;
+ }
+
+ //
+ // Inner classes.
+ //
+
+ public static interface ResolverState
+ {
+ IModule[] getModules();
+ // TODO: RESOLVER - This should be on module.
+ String getBundleSymbolicName(IModule module);
+ // TODO: RESOLVER - This should be on module.
+ boolean isResolved(IModule module);
+ Map getPotentialFragments(IModule module);
+ List getPotentialHosts(IModule module);
+ PackageSource[] getResolvedCandidates(IRequirement req);
+ PackageSource[] getUnresolvedCandidates(IRequirement req);
+ }
+
+ private static class CandidateSet
+ {
+ public IModule m_module = null;
+ public IRequirement m_requirement = null;
+ public PackageSource[] m_candidates = null;
+ public int m_idx = 0;
+ public CandidateSet(IModule module, IRequirement requirement, PackageSource[] candidates)
+ {
+ m_module = module;
+ m_requirement = requirement;
+ m_candidates = candidates;
+ }
+ }
+
+ // TODO: RESOLVER - This is a hack, we need to calcualte fragments differently.
+ public class Result
+ {
+ public final IModule m_host;
+ public final Map m_fragmentMap;
+ public final Map m_resolvedModuleWireMap;
+
+ public Result(IModule host, Map fragmentMap, Map resolvedModuleWireMap)
+ {
+ m_host = host;
+ m_fragmentMap = fragmentMap;
+ m_resolvedModuleWireMap = resolvedModuleWireMap;
+ }
+ }
+}
\ No newline at end of file