Author: rickhall
Date: Tue Dec 22 19:44:53 2009
New Revision: 893287
URL: http://svn.apache.org/viewvc?rev=893287&view=rev
Log:
Rewrite of new resolver algorithm to try to clean it up and make it
work better for require-bundle.
Added:
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto2/
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto2/Proto2Resolver.java
Modified:
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java
Modified:
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java
URL:
http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java?rev=893287&r1=893286&r2=893287&view=diff
==============================================================================
---
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java
(original)
+++
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java
Tue Dec 22 19:44:53 2009
@@ -27,6 +27,7 @@
import java.util.Map.Entry;
import org.apache.felix.resolver.cs.Capability;
import org.apache.felix.resolver.felix.FelixResolver;
+import org.apache.felix.resolver.proto2.Proto2Resolver;
import org.apache.felix.resolver.prototype.ProtoResolver;
public class Main
@@ -37,18 +38,23 @@
{
if (args.length > 2)
{
- System.out.println("[-legacy] [scenario-number]");
+ System.out.println("[-legacy | -proto] [scenario-number]");
System.exit(0);
}
String scenario = "1";
boolean legacy = false;
+ boolean proto = false;
for (int i = 0; i < args.length; i++)
{
if (args[i].equals("-legacy"))
{
legacy = true;
}
+ else if (args[i].equals("-proto"))
+ {
+ legacy = true;
+ }
else
{
scenario = args[i];
@@ -58,7 +64,19 @@
List<Module> moduleList = new ArrayList<Module>();
Module targetModule = setupScenario(moduleList, scenario);
- Resolver resolver = (legacy) ? new FelixResolver(moduleList) : new
ProtoResolver(moduleList);
+ Resolver resolver = null;
+ if (legacy)
+ {
+ resolver = new FelixResolver(moduleList);
+ }
+ else if (proto)
+ {
+ resolver = new ProtoResolver(moduleList);
+ }
+ else
+ {
+ resolver = new Proto2Resolver(moduleList);
+ }
try
{
@@ -101,7 +119,7 @@
// SOLUTION:
// A: bar->B, baz->E
- // B: woz->D
+ // B: woz->C
// E: dit->F
private static Module scenario1(List<Module> moduleList)
{
@@ -143,7 +161,7 @@
}
// SOLUTION:
- // A: bar->B, woz->E
+ // A: bar->B, woz->C
private static Module scenario2(List<Module> moduleList)
{
Module m, target;
@@ -611,7 +629,8 @@
moduleList.add(
(m = new Module("B"))
.providing(new CapabilityImpl(m,
Capability.MODULE_NAMESPACE).with("bundle-symbolic-name=B"))
- .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=bar").using("foo"))
+// .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=bar").using("foo"))
+ .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=bar"))
.requiring(new
RequirementImpl(Capability.PACKAGE_NAMESPACE).with("package=foo").with("version=[1.0.0,2.0.0)")));
// Bundle C
moduleList.add(
@@ -622,7 +641,8 @@
// Bundle D
moduleList.add(
(m = new Module("D"))
- .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=foo").with("version=1.0.0")));
+ .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=foo").with("version=1.0.0"))
+ .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=woz").with("version=1.0.0")));
// Bundle E
moduleList.add(
(m = new Module("E"))
@@ -630,4 +650,36 @@
return target;
}
+
+ private static Module scenario13(List<Module> moduleList)
+ {
+ Module m, target;
+
+ // Bundle A
+ moduleList.add(
+ target = (m = new Module("A"))
+ .requiring(new
RequirementImpl(Capability.PACKAGE_NAMESPACE).with("package=foo"))
+ .requiring(new
RequirementImpl(Capability.PACKAGE_NAMESPACE).with("package=bar"))
+ .requiring(new
RequirementImpl(Capability.PACKAGE_NAMESPACE).with("package=woz")));
+ // Bundle B
+ moduleList.add(
+ (m = new Module("B"))
+ .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=foo").using("woz"))
+ .requiring(new
RequirementImpl(Capability.PACKAGE_NAMESPACE).with("package=woz").with("version=1.0.0")));
+ // Bundle C
+ moduleList.add(
+ (m = new Module("C"))
+ .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=bar").using("woz"))
+ .requiring(new
RequirementImpl(Capability.PACKAGE_NAMESPACE).with("package=woz").with("version=2.0.0")));
+ // Bundle D
+ moduleList.add(
+ (m = new Module("D"))
+ .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=woz").with("version=1.0.0")));
+ // Bundle E
+ moduleList.add(
+ (m = new Module("E"))
+ .providing(new CapabilityImpl(m,
Capability.PACKAGE_NAMESPACE).with("package=woz").with("version=2.0.0")));
+
+ return target;
+ }
}
\ No newline at end of file
Added:
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto2/Proto2Resolver.java
URL:
http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto2/Proto2Resolver.java?rev=893287&view=auto
==============================================================================
---
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto2/Proto2Resolver.java
(added)
+++
felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto2/Proto2Resolver.java
Tue Dec 22 19:44:53 2009
@@ -0,0 +1,587 @@
+/*
+ * 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.resolver.proto2;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import org.apache.felix.resolver.Module;
+import org.apache.felix.resolver.ResolveException;
+import org.apache.felix.resolver.Resolver;
+import org.apache.felix.resolver.Version;
+import org.apache.felix.resolver.Wire;
+import org.apache.felix.resolver.cs.Capability;
+import org.apache.felix.resolver.cs.CapabilitySet;
+import org.apache.felix.resolver.cs.Requirement;
+import org.apache.felix.resolver.manifestparser.Constants;
+
+// 1. Treat hard pkg constraints separately from implied package constraints
+// 2. Map pkg constraints to a set of capabilities, not a single capability.
+// 3. Uses constraints cannot conflict with other uses constraints, only with
hard constraints.
+public class Proto2Resolver implements Resolver
+{
+ private final List<Module> m_modules;
+ private final CapabilitySet m_pkgCapSet;
+ private final CapabilitySet m_modCapSet;
+
+ private static final Map<String, Long> m_invokeCounts = new
HashMap<String, Long>();
+
+ private static boolean m_isInvokeCount = false;
+
+ public Proto2Resolver(List<Module> modules)
+ {
+System.out.println("+++ PROTO2 RESOLVER");
+ m_modules = modules;
+
+ List indices = new ArrayList();
+ indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ m_modCapSet = new CapabilitySet(indices);
+
+ indices = new ArrayList();
+ indices.add(Capability.PACKAGE_ATTR);
+ m_pkgCapSet = new CapabilitySet(indices);
+
+ for (int modIdx = 0; modIdx < m_modules.size(); modIdx++)
+ {
+ List<Capability> caps = m_modules.get(modIdx).getCapabilities();
+ for (int capIdx = 0; capIdx < caps.size(); capIdx++)
+ {
+ if
(caps.get(capIdx).getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ m_modCapSet.addCapability(caps.get(capIdx));
+ }
+ else if
(caps.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.addCapability(caps.get(capIdx));
+ }
+ }
+ }
+
+ String v = System.getProperty("invoke.count");
+ m_isInvokeCount = (v == null) ? false : Boolean.valueOf(v);
+ }
+
+ public Map<Module, List<Wire>> resolve(Module module)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ Map<Module, Packages> modulePkgMap;
+ resolveRoot(
+ module,
+ new HashMap<Requirement, Set<Capability>>(),
+ modulePkgMap = new HashMap<Module, Packages>(),
+ new HashMap<Module, Object>());
+
+dumpModulePkgMap(modulePkgMap);
+
+ return null;
+ }
+
+ private void dumpModulePkgMap(Map<Module, Packages> modulePkgMap)
+ {
+ System.out.println("+++MODULE PKG MAP+++");
+ for (Entry<Module, Packages> entry : modulePkgMap.entrySet())
+ {
+ dumpModulePkgs(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private void dumpModulePkgs(Module module, Packages packages)
+ {
+ System.out.println(module);
+ System.out.println(" EXPORTED");
+ for (Entry<String, Constraint> entry :
packages.m_exportedPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " +
entry.getValue());
+ }
+ System.out.println(" IMPORTED");
+ for (Entry<String, Constraint> entry :
packages.m_importedPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " +
entry.getValue());
+ }
+ System.out.println(" REQUIRED");
+ for (Entry<String, Constraint> entry :
packages.m_requiredPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " +
entry.getValue());
+ }
+ System.out.println(" USED");
+ for (Entry<String, List<Constraint>> entry :
packages.m_usedPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " +
entry.getValue());
+ }
+ }
+
+ private void resolveRoot(
+ Module module, Map<Requirement, Set<Capability>> candidateMap,
+ Map<Module, Packages> modulePkgMap, Map<Module, Object> cycleMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ if (!module.isResolved())
+ {
+ if (cycleMap.containsKey(module))
+ {
+ return;
+ }
+ cycleMap.put(module, module);
+System.out.println("+++ RESOLVING " + module);
+ calculateExportedPackages(module, modulePkgMap);
+
+ populateCandidates(module, candidateMap);
+
+ List<Requirement> reqs = module.getRequirements();
+ for (Requirement req : reqs)
+ {
+ // Get the candidates for the current requirement.
+ Set<Capability> candCaps = candidateMap.get(req);
+ // Optional requirements may not have any candidates.
+ if (candCaps == null)
+ {
+ continue;
+ }
+ for (Iterator<Capability> it = candCaps.iterator();
it.hasNext(); )
+ {
+ Capability candCap = it.next();
+System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
+ try
+ {
+ // Try to resolve the candidate.
+ resolveRoot(candCap.getModule(), candidateMap,
modulePkgMap, cycleMap);
+
+ // If we are here, the candidate resolved. Try to merge
+ // the candidate's into the target module's packages.
+ mergeCandidatePackages(module, candCap, modulePkgMap);
+
+ // If we are here, we merged the candidate
successfully,
+ // so we can continue with the next requirement
+ break;
+ }
+ catch (ResolveException ex)
+ {
+System.out.println("RE: " + ex);
+ex.printStackTrace();
+ it.remove();
+ if (!it.hasNext() && !req.isOptional())
+ {
+ candidateMap.remove(req);
+ throw new ResolveException("Unresolved constraint "
+ + req + " in " + module);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void mergeCandidatePackages(
+ Module current, Capability candCap, Map<Module, Packages> modulePkgMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+System.out.println("+++ MERGING " + candCap + " INTO " + current);
+ String pkgName = (String)
+ candCap.getAttribute(Capability.PACKAGE_ATTR).getValue();
+
+ Packages candPkgs = modulePkgMap.get(candCap.getModule());
+ Constraint candConstraint = candPkgs.m_exportedPkgs.get(pkgName);
+
+ Packages currentPkgs = modulePkgMap.get(current);
+ Constraint currentExportedConstraint =
currentPkgs.m_exportedPkgs.get(pkgName);
+ Constraint currentImportedConstraint =
currentPkgs.m_importedPkgs.get(pkgName);
+ Constraint currentRequiredConstraint =
currentPkgs.m_requiredPkgs.get(pkgName);
+
+ if (((currentExportedConstraint != null)
+ &&
!currentExportedConstraint.m_sources.containsAll(candConstraint.m_sources))
+ || ((currentImportedConstraint != null)
+ &&
!currentImportedConstraint.m_sources.containsAll(candConstraint.m_sources))
+ || ((currentRequiredConstraint != null)
+ &&
!currentRequiredConstraint.m_sources.containsAll(candConstraint.m_sources)))
+ {
+ throw new ResolveException("Constraint violation between "
+ + current + " and " + candCap.getModule()
+ + " for " + pkgName);
+ }
+
+ List<Constraint> currentUsedSources =
currentPkgs.m_usedPkgs.get(pkgName);
+ checkExistingUsesConstraints(current, pkgName, currentUsedSources,
candConstraint);
+
+ Packages currentPkgsCopy = new Packages(currentPkgs);
+
+ // Add the candidate package to the current set of packages.
+ currentPkgsCopy.m_importedPkgs.put(pkgName, candConstraint);
+
+ // Verify the candidate's uses constraints do not conflict.
+ for (Capability cap : candConstraint.m_sources)
+ {
+ for (String usedPkg : cap.getUses())
+ {
+ verifyUses(current, currentPkgsCopy, candCap,
modulePkgMap, usedPkg);
+ }
+ }
+
+ currentPkgs.m_exportedPkgs.putAll(currentPkgsCopy.m_exportedPkgs);
+ currentPkgs.m_importedPkgs.putAll(currentPkgsCopy.m_importedPkgs);
+ currentPkgs.m_requiredPkgs.putAll(currentPkgsCopy.m_requiredPkgs);
+ currentPkgs.m_usedPkgs.putAll(currentPkgsCopy.m_usedPkgs);
+dumpModulePkgs(current, currentPkgs);
+ }
+ }
+
+ private void checkExistingUsesConstraints(
+ Module current, String pkgName, List<Constraint> existingConstraints,
+ Constraint candConstraint)
+ {
+if (pkgName.equals("foo"))
+{
+ System.out.println("+++ CHECKING " + pkgName + " - " + existingConstraints
+ " - " + candConstraint.m_sources);
+}
+ for (int i = 0; (existingConstraints != null) && (i <
existingConstraints.size()); i++)
+ {
+ if
(!existingConstraints.get(i).m_sources.containsAll(candConstraint.m_sources))
+ {
+ throw new ResolveException("Constraint violation for package
'" + pkgName
+ + "' when resolving module " + current
+ + " between existing constraint "
+ + existingConstraints.get(i).m_capability.getModule()
+ + " " + existingConstraints.get(i).m_sources
+ + " and candidate constraint " +
candConstraint.m_capability.getModule()
+ + " " + candConstraint.m_sources);
+ }
+ }
+ }
+
+// TODO: PROTO2 RESOLVER - We end up with duplicates in uses constraints,
+// see scenario 2 for an example.
+ private void verifyUses(
+ Module current, Packages currentPkgs,
+ Capability candCap, Map<Module, Packages> modulePkgMap,
+ String pkgName)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+ Constraint currentExportedConstraint =
currentPkgs.m_exportedPkgs.get(pkgName);
+ Constraint currentImportedConstraint =
currentPkgs.m_importedPkgs.get(pkgName);
+ Constraint currentRequiredConstraint =
currentPkgs.m_requiredPkgs.get(pkgName);
+
+ Packages candPkgs = modulePkgMap.get(candCap.getModule());
+ Constraint candConstraint = candPkgs.m_exportedPkgs.get(pkgName);
+ candConstraint = (candConstraint != null)
+ ? candConstraint
+ : candPkgs.m_importedPkgs.get(pkgName);
+ candConstraint = (candConstraint != null)
+ ? candConstraint
+ : candPkgs.m_requiredPkgs.get(pkgName);
+
+ // If the candidate doesn't actually have a constraint for
+ // the used package, then just ignore it since this is likely
+ // an error in its metadata.
+ if (candConstraint == null)
+ {
+ return;
+ }
+
+ // If there is no current mapping for this package, then
+ // we can just return.
+ if ((currentExportedConstraint == null)
+ && (currentImportedConstraint == null)
+ && (currentRequiredConstraint == null))
+ {
+ List<Constraint> constraints = currentPkgs.m_usedPkgs.get(pkgName);
+ if (constraints == null)
+ {
+ constraints = new ArrayList<Constraint>();
+ currentPkgs.m_usedPkgs.put(pkgName, constraints);
+ }
+ constraints.add(candConstraint);
+ return;
+ }
+
+ if ((currentExportedConstraint != null)
+ &&
!currentExportedConstraint.m_sources.containsAll(candConstraint.m_sources))
+ {
+ throw new ResolveException("Constraint violation for package '" +
pkgName
+ + "' when resolving module " + current
+ + " between existing constraint "
+ + currentExportedConstraint.m_capability.getModule()
+ + " " + currentExportedConstraint.m_sources
+ + " and candidate constraint " +
candConstraint.m_capability.getModule()
+ + " " + candConstraint.m_sources);
+ }
+ else if ((currentImportedConstraint != null)
+ &&
!currentImportedConstraint.m_sources.containsAll(candConstraint.m_sources))
+ {
+ throw new ResolveException("Constraint violation for package '" +
pkgName
+ + "' when resolving module " + current
+ + " between existing constraint "
+ + currentImportedConstraint.m_capability.getModule()
+ + " " + currentImportedConstraint.m_sources
+ + " and candidate constraint " +
candConstraint.m_capability.getModule()
+ + " " + candConstraint.m_sources);
+ }
+
+ // Verify the candidate's uses constraints do not conflict.
+ for (Capability cap : candConstraint.m_sources)
+ {
+ for (String usedPkg : cap.getUses())
+ {
+ verifyUses(current, currentPkgs, cap, modulePkgMap, usedPkg);
+ }
+ }
+ }
+
+ private void populateCandidates(Module module, Map<Requirement,
Set<Capability>> candidateMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ // Find candidates for all requirements for the target module.
+ List<Requirement> reqs = module.getRequirements();
+ for (Requirement req : reqs)
+ {
+ // If we are using a permutated candidate map, then the target
+ // module's candidates may have already been calculated, so use
+ // those instead, otherwise find the matching providers.
+ if (candidateMap.get(req) == null)
+ {
+ Set<Capability> candidates = findCandidates(req);
+ if ((candidates.size() == 0) && !req.isOptional())
+ {
+ throw new RuntimeException("Unable to resolve " + module
+ + ": missing requirement " + req);
+ }
+ else if (candidates.size() > 0)
+ {
+ candidateMap.put(req, candidates);
+ }
+System.out.println("+++ " + req + " = " + candidates);
+ }
+ }
+ }
+
+ private Set<Capability> findCandidates(Requirement req)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ Set<Capability> result = new TreeSet(new Comparator() {
+ public int compare(Object arg1, Object arg2)
+ {
+ Capability cap1 = (Capability) arg1;
+ Capability cap2 = (Capability) arg2;
+ if (cap1.getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ int c = ((Comparable)
cap1.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)
+
.getValue()).compareTo(cap2.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)
+ .getValue());
+ if (c == 0)
+ {
+ Version v1 =
(cap1.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) == null)
+ ? Version.emptyVersion
+ : (Version)
cap1.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE).getValue();
+ Version v2 =
(cap1.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) == null)
+ ? Version.emptyVersion
+ : (Version)
cap1.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE).getValue();
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = v2.compareTo(v1);
+ if (c == 0)
+ {
+ c =
cap1.getModule().getName().compareTo(cap2.getModule().getName());
+ }
+ }
+ return c;
+ }
+// TODO: PROTO2 RESOLVER - Need to change this to handle arbitrary capabilities
+// that may not have a natural ordering.
+ // Assume everything else is a package capability.
+ else
+ {
+ int c = ((Comparable)
cap1.getAttribute(Capability.PACKAGE_ATTR).getValue())
+
.compareTo(cap2.getAttribute(Capability.PACKAGE_ATTR).getValue());
+ if (c == 0)
+ {
+ Version v1 =
(cap1.getAttribute(Capability.VERSION_ATTR) == null)
+ ? Version.emptyVersion
+ : (Version)
cap1.getAttribute(Capability.VERSION_ATTR).getValue();
+ Version v2 =
(cap1.getAttribute(Capability.VERSION_ATTR) == null)
+ ? Version.emptyVersion
+ : (Version)
cap1.getAttribute(Capability.VERSION_ATTR).getValue();
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = v2.compareTo(v1);
+ if (c == 0)
+ {
+ c =
cap1.getModule().getName().compareTo(cap2.getModule().getName());
+ }
+ }
+ return c;
+ }
+ }
+ });
+
+ if (req.getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ result.addAll(m_modCapSet.match(req.getFilter()));
+ }
+ else if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ result.addAll(m_pkgCapSet.match(req.getFilter()));
+ }
+ return result;
+ }
+
+ private void calculateExportedPackages(
+ Module module, Map<Module, Packages> modulePkgMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ Packages packages = new Packages();
+
+ List<Capability> caps = module.getCapabilities();
+
+ if (caps.size() > 0)
+ {
+ for (int i = 0; i < caps.size(); i++)
+ {
+// TODO: PROTO2 RESOLVER - Assume if a module imports the same package it
+// exports that the import will overlap the export.
+ if
(caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ && !hasOverlappingImport(module, caps.get(i)))
+ {
+ List<Capability> sources = new ArrayList<Capability>();
+ sources.add(caps.get(i));
+ packages.m_exportedPkgs.put(
+ (String)
caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue(),
+ new Constraint(caps.get(i), sources));
+ }
+ }
+ }
+
+ modulePkgMap.put(module, packages);
+ }
+
+ private boolean hasOverlappingImport(Module module, Capability cap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new
Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue()
+ 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ List<Requirement> reqs = module.getRequirements();
+ for (int i = 0; i < reqs.size(); i++)
+ {
+ if (reqs.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ && m_pkgCapSet.matches(cap, reqs.get(i).getFilter()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class Packages
+ {
+ public Packages()
+ {
+ }
+
+ public Packages(Packages packages)
+ {
+ m_exportedPkgs.putAll(packages.m_exportedPkgs);
+ m_importedPkgs.putAll(packages.m_importedPkgs);
+ m_requiredPkgs.putAll(packages.m_requiredPkgs);
+ m_usedPkgs.putAll(packages.m_usedPkgs);
+ }
+
+ public final Map<String, Constraint> m_exportedPkgs
+ = new HashMap<String, Constraint>();
+ public final Map<String, Constraint> m_importedPkgs
+ = new HashMap<String, Constraint>();
+ public final Map<String, Constraint> m_requiredPkgs
+ = new HashMap<String, Constraint>();
+ public final Map<String, List<Constraint>> m_usedPkgs
+ = new HashMap<String, List<Constraint>>();
+ }
+
+ private class Constraint
+ {
+ public final Capability m_capability;
+ public final List<Capability> m_sources;
+ public Constraint(Capability capability, List<Capability> sources)
+ {
+ m_capability = capability;
+ m_sources = sources;
+ }
+
+ public String toString()
+ {
+ return m_capability + " SOURCES " + m_sources;
+ }
+ }
+}
\ No newline at end of file