FELIX-2144: add global requirements and capabilities to the resolver

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@917434 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java
index 3976832..a5e059d 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java
@@ -66,6 +66,43 @@
      */
     Requirement[] getAddedRequirements();
 
+    /**
+     * Add a global capability.
+     *
+     * A global capability is one capability provided by the environment
+     * but not reflected in local resources.
+     *
+     * @param capability the new global capability
+     */
+    void addGlobalCapability(Capability capability);
+
+    /**
+     * Returns the list of global capabilities
+     * @return
+     */
+    Capability[] getGlobalCapabilities();
+
+    /**
+     * Add a global requirement.
+     *
+     * A global requirement is a requirement that must be satisfied by all
+     * resources.  Such requirements are usually built using an
+     *    IF x then Y
+     * which can be expressed using the following logical expression
+     *    !X OR (X AND Y)
+     * which can be translated to the following filter
+     *    (|(!(x))(&(x)(y))
+     *
+     * @param requirement
+     */
+    void addGlobalRequirement(Requirement requirement);
+
+    /**
+     * Returns a list of global requirements
+     * @return
+     */
+    Requirement[] getGlobalRequirements();
+
    /**
      * Start the resolution process and return whether the constraints have
      * been successfully met or not.
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java
index 07993ef..56bfad3 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java
@@ -32,6 +32,8 @@
     private final Repository[] m_repositories;
     private final Set m_addedSet = new HashSet();
     private final Set m_addedRequirementSet = new HashSet();
+    private final Set m_globalCapabilities = new HashSet();
+    private final Set m_globalRequirements = new HashSet();
     private final Set m_failedSet = new HashSet();
     private final Set m_resolveSet = new HashSet();
     private final Set m_requiredSet = new HashSet();
@@ -72,6 +74,27 @@
         return (Requirement[]) m_addedRequirementSet.toArray(new Requirement[m_addedRequirementSet.size()]);
     }
 
+    public void addGlobalCapability(Capability capability)
+    {
+        m_globalCapabilities.add(capability);
+    }
+
+    public Capability[] getGlobalCapabilities()
+    {
+        return (Capability[]) m_globalCapabilities.toArray(new Capability[m_globalCapabilities.size()]);
+    }
+
+    public void addGlobalRequirement(Requirement requirement)
+    {
+        m_resolved = false;
+        m_globalRequirements.add(requirement);
+    }
+
+    public Requirement[] getGlobalRequirements()
+    {
+        return (Requirement[]) m_globalRequirements.toArray(new Requirement[m_globalRequirements.size()]);
+    }
+
     public synchronized Requirement[] getUnsatisfiedRequirements()
     {
         if (m_resolved)
@@ -154,6 +177,7 @@
         // Find resources
         Resource[] locals = getResources(true);
         Resource[] remotes = getResources(false);
+        remotes = filter(remotes, (Requirement[]) m_globalRequirements.toArray(new Requirement[m_globalRequirements.size()]));
 
         // time of the resolution process start
         m_resolveTimeStamp = 0;
@@ -178,10 +202,16 @@
         boolean result = true;
 
         // Add a fake resource if needed
-        if (!m_addedRequirementSet.isEmpty())
+        if (!m_addedRequirementSet.isEmpty() || !m_globalCapabilities.isEmpty())
         {
             ResourceImpl fake = new ResourceImpl();
-            for (Iterator iter = m_addedRequirementSet.iterator(); iter.hasNext(); )
+            for (Iterator iter = m_globalCapabilities.iterator(); iter.hasNext();)
+            {
+                Capability cap = (Capability) iter.next();
+                fake.addCapability(cap);
+                ((CapabilityImpl) cap).setResource(null);
+            }
+            for (Iterator iter = m_addedRequirementSet.iterator(); iter.hasNext();)
             {
                 Requirement req = (Requirement) iter.next();
                 fake.addRequire(req);
@@ -215,6 +245,40 @@
         return result;
     }
 
+    private Resource[] filter(Resource[] resources, Requirement[] requirements)
+    {
+        if (requirements == null || requirements.length == 0)
+        {
+            return resources;
+        }
+        List res = new ArrayList();
+        for (int resIdx = 0; (resources != null) && resIdx < resources.length; resIdx++)
+        {
+            boolean resOk = true;
+            for (int reqIdx = 0; (requirements != null) && reqIdx < requirements.length; reqIdx++)
+            {
+                boolean reqOk = false;
+                Capability[] caps = resources[resIdx].getCapabilities();
+                for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+                {
+                    if (requirements[reqIdx].isSatisfied(caps[capIdx]))
+                    {
+                        reqOk = true;
+                        break;
+                    }
+                }
+                if (!reqOk) {
+                    resOk = false;
+                    break;
+                }
+            }
+            if (resOk) {
+                res.add(resources[resIdx]);
+            }
+        }
+        return (Resource[]) res.toArray(new Resource[res.size()]);
+    }
+
     private boolean resolve(Resource resource, Resource[] locals, Resource[] remotes, boolean optional)
     {
         boolean result = true;