Very rough support for generic capabilities and requirements. The parser
needs to be improved. (FELIX-2973)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1132743 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index 17396fb..2d7a7b4 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -41,6 +41,7 @@
 import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
 
 class BundleImpl implements Bundle
 {
@@ -1007,6 +1008,10 @@
             return (A) getFramework().adapt(FrameworkStartLevelImpl.class)
                 .createBundleStartLevel(this);
         }
+        else if (type == BundleWiring.class)
+        {
+            return (A) getCurrentRevision().getWiring();
+        }
         return null;
     }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index e2a1464..d45490e 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -82,6 +82,8 @@
     private final StatefulResolver m_resolver;
     private final BundleRevisionImpl m_revision;
     private final List<BundleRevision> m_fragments;
+// TODO: OSGi R,4.3 - Perhaps we should make m_wires and m_importedPkgs volatile
+//       and copy-on-write instead of protecting them with object lock.
     private final List<BundleWire> m_wires;
     private final Map<String, BundleRevision> m_importedPkgs;
     private final Map<String, List<BundleRevision>> m_requiredPkgs;
@@ -386,9 +388,21 @@
         return Collections.EMPTY_LIST;
     }
 
-    public List<BundleWire> getRequiredWires(String namespace)
+    public synchronized List<BundleWire> getRequiredWires(String namespace)
     {
-        return m_wires;
+        List<BundleWire> result = m_wires;
+        if (namespace != null)
+        {
+            result = new ArrayList<BundleWire>();
+            for (BundleWire bw : m_wires)
+            {
+                if (bw.getRequirement().getNamespace().equals(namespace))
+                {
+                    result.add(bw);
+                }
+            }
+        }
+        return result;
     }
 
     public synchronized void addDynamicWire(BundleWire wire)
@@ -568,6 +582,7 @@
         // Look in the revisions's imported packages. If the package is
         // imported, then we stop searching no matter the result since
         // imported packages cannot be split.
+// TODO: OSGi R4.3 - Access should be guarded by object lock.
         BundleRevision provider = m_importedPkgs.get(pkgName);
         if (provider != null)
         {
@@ -943,6 +958,7 @@
         throws ClassNotFoundException, ResourceNotFoundException
     {
         // Check if the package is imported.
+// TODO: OSGi R4.3 - Access should be guarded by object lock.
         BundleRevision provider = m_importedPkgs.get(pkgName);
         if (provider != null)
         {
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 3c58ab7..a8031c0 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -4605,7 +4605,7 @@
                         }
                         else
                         {
-                            m_logger.log(Logger.LOG_DEBUG, "WIRE: " + rw);
+                            m_logger.log(Logger.LOG_DEBUG, "WIRE: " + rw.toString());
 
                             if (rw.getCapability().getNamespace()
                                 .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
index 7d2bd16..fedc1b2 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
@@ -266,7 +266,11 @@
     {
         boolean matched = true;
 
-        if (sf.getOperation() == SimpleFilter.AND)
+        if (sf.getOperation() == SimpleFilter.MATCH_ALL)
+        {
+            matched = true;
+        }
+        else if (sf.getOperation() == SimpleFilter.AND)
         {
             // Evaluate each subfilter against the remaining capabilities.
             // For AND we calculate the intersection of each subfilter.
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
index 822d9d0..20ac13f 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
@@ -92,6 +92,9 @@
             case APPROX:
                 s = "(" + m_name + "~=" + toEncodedString(m_value) + ")";
                 break;
+            case MATCH_ALL:
+                s = "(*)";
+                break;
         }
         return s;
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
index 80cb321..070ee85 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -550,6 +550,28 @@
         // import is consistent with the existing package space.
         if ((revision.getWiring() == null) || isDynamicImporting)
         {
+            // Merge uses constraints from required capabilities.
+            for (int i = 0; i < reqs.size(); i++)
+            {
+                BundleRequirement req = reqs.get(i);
+                BundleCapability cap = caps.get(i);
+                // Ignore revisions that import from themselves.
+                if (!cap.getRevision().equals(revision))
+                {
+                    List<BundleRequirement> blameReqs = new ArrayList();
+                    blameReqs.add(req);
+
+                    mergeUses(
+                        revision,
+                        revisionPkgs,
+                        cap,
+                        blameReqs,
+                        revisionPkgMap,
+                        allCandidates,
+                        usesCycleMap);
+                }
+            }
+            // Merge uses constraints from imported packages.
             for (Entry<String, List<Blame>> entry : revisionPkgs.m_importedPkgs.entrySet())
             {
                 for (Blame blame : entry.getValue())
@@ -571,6 +593,7 @@
                     }
                 }
             }
+            // Merge uses constraints from required bundles.
             for (Entry<String, List<Blame>> entry : revisionPkgs.m_requiredPkgs.entrySet())
             {
                 for (Blame blame : entry.getValue())
@@ -655,44 +678,27 @@
     {
         if (candCap.getNamespace().equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
         {
+            // Merge the candidate capability into the revision's package space
+            // for imported or required packages, appropriately.
+
             String pkgName = (String)
                 candCap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
 
-            // Since this capability represents a package, it will become
-            // a hard constraint on the revisions's package space, so we need
-            // to make sure it doesn't conflict with any other hard constraints
-            // or any other uses constraints.
-
             List blameReqs = new ArrayList();
             blameReqs.add(currentReq);
 
-            //
-            // First, check to see if the capability conflicts with
-            // any existing hard constraints.
-            //
-
             Packages currentPkgs = revisionPkgMap.get(current);
 
-            if (requires)
+            Map<String, List<Blame>> packages = (requires)
+                ? currentPkgs.m_requiredPkgs
+                : currentPkgs.m_importedPkgs;
+            List<Blame> blames = currentPkgs.m_requiredPkgs.get(pkgName);
+            if (blames == null)
             {
-                List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(pkgName);
-                if (currentRequiredBlames == null)
-                {
-                    currentRequiredBlames = new ArrayList<Blame>();
-                    currentPkgs.m_requiredPkgs.put(pkgName, currentRequiredBlames);
-                }
-                currentRequiredBlames.add(new Blame(candCap, blameReqs));
+                blames = new ArrayList<Blame>();
+                packages.put(pkgName, blames);
             }
-            else
-            {
-                List<Blame> currentImportedBlames = currentPkgs.m_importedPkgs.get(pkgName);
-                if (currentImportedBlames == null)
-                {
-                    currentImportedBlames = new ArrayList<Blame>();
-                    currentPkgs.m_importedPkgs.put(pkgName, currentImportedBlames);
-                }
-                currentImportedBlames.add(new Blame(candCap, blameReqs));
-            }
+            blames.add(new Blame(candCap, blameReqs));
 
 //dumpRevisionPkgs(current, currentPkgs);
         }
@@ -705,14 +711,11 @@
         Candidates allCandidates,
         Map<BundleCapability, List<BundleRevision>> cycleMap)
     {
-        if (!mergeCap.getNamespace().equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
-        {
-            return;
-        }
+        // If there are no uses, then just return.
         // If the candidate revision is the same as the current revision,
         // then we don't need to verify and merge the uses constraints
         // since this will happen as we build up the package space.
-        else if (current.equals(mergeCap.getRevision()))
+        if (current.equals(mergeCap.getRevision()))
         {
             return;
         }
@@ -1236,6 +1239,13 @@
             return sources;
         }
 
+        if (!((BundleCapabilityImpl) cap).getUses().isEmpty())
+        {
+            List<BundleCapability> caps = new ArrayList<BundleCapability>(1);
+            caps.add(cap);
+            return caps;
+        }
+
         return Collections.EMPTY_LIST;
     }
 
@@ -1323,7 +1333,8 @@
             wireMap.put(unwrappedRevision, (List<ResolverWire>) Collections.EMPTY_LIST);
 
             List<ResolverWire> packageWires = new ArrayList<ResolverWire>();
-            List<ResolverWire> requireWires = new ArrayList<ResolverWire>();
+            List<ResolverWire> bundleWires = new ArrayList<ResolverWire>();
+            List<ResolverWire> capabilityWires = new ArrayList<ResolverWire>();
 
             for (BundleRequirement req : revision.getDeclaredRequirements(null))
             {
@@ -1351,14 +1362,19 @@
                         }
                         else if (req.getNamespace().equals(BundleCapabilityImpl.BUNDLE_NAMESPACE))
                         {
-                            requireWires.add(wire);
+                            bundleWires.add(wire);
+                        }
+                        else
+                        {
+                            capabilityWires.add(wire);
                         }
                     }
                 }
             }
 
             // Combine package wires with require wires last.
-            packageWires.addAll(requireWires);
+            packageWires.addAll(bundleWires);
+            packageWires.addAll(capabilityWires);
             wireMap.put(unwrappedRevision, packageWires);
 
             // Add host wire for any fragments.
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverWireImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverWireImpl.java
index d89012e..4f8420f 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverWireImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverWireImpl.java
@@ -61,8 +61,7 @@
 
     public String toString()
     {
-        return "[" + m_requirer + "] "
-            + m_req
+        return m_req
             + " -> "
             + "[" + m_provider + "]";
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index a6cd125..78073f2 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -24,6 +24,7 @@
 import org.apache.felix.framework.BundleRevisionImpl;
 
 import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
 import org.apache.felix.framework.wiring.BundleCapabilityImpl;
 import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.VersionRange;
@@ -149,10 +150,10 @@
         // Parse Require-Bundle
         //
 
-        List<ParsedHeaderClause> requireClauses =
+        List<ParsedHeaderClause> rbClauses =
             parseStandardHeader((String) headerMap.get(Constants.REQUIRE_BUNDLE));
-        requireClauses = normalizeRequireClauses(m_logger, requireClauses, getManifestVersion());
-        List<BundleRequirementImpl> requireReqs = convertRequires(requireClauses, owner);
+        rbClauses = normalizeRequireClauses(m_logger, rbClauses, getManifestVersion());
+        List<BundleRequirementImpl> rbReqs = convertRequires(rbClauses, owner);
 
         //
         // Parse Import-Package.
@@ -173,10 +174,19 @@
         List<BundleRequirement> dynamicReqs = convertImports(dynamicClauses, owner);
 
         //
+        // Parse Require-Capability.
+        //
+
+        List<ParsedHeaderClause> requireClauses =
+            parseStandardHeader((String) headerMap.get(Constants.REQUIRE_CAPABILITY));
+        importClauses = normalizeRequireCapabilityClauses(
+            m_logger, requireClauses, getManifestVersion());
+        List<BundleRequirement> requireReqs = convertRequireCapabilities(importClauses, owner);
+
+        //
         // Parse Export-Package.
         //
 
-        // Get exported packages from bundle manifest.
         List<ParsedHeaderClause> exportClauses =
             parseStandardHeader((String) headerMap.get(Constants.EXPORT_PACKAGE));
         exportClauses = normalizeExportClauses(logger, exportClauses,
@@ -184,6 +194,16 @@
         List<BundleCapability> exportCaps = convertExports(exportClauses, owner);
 
         //
+        // Parse Provide-Capability.
+        //
+
+        List<ParsedHeaderClause> provideClauses =
+            parseStandardHeader((String) headerMap.get(Constants.PROVIDE_CAPABILITY));
+        exportClauses = normalizeProvideCapabilityClauses(
+            logger, provideClauses, getManifestVersion());
+        List<BundleCapability> provideCaps = convertProvideCapabilities(provideClauses, owner);
+
+        //
         // Calculate implicit imports.
         //
 
@@ -203,16 +223,19 @@
 
         // Combine all capabilities.
         m_capabilities = new ArrayList(
-             capList.size() + exportCaps.size());
+             capList.size() + exportCaps.size() + provideCaps.size());
         m_capabilities.addAll(capList);
         m_capabilities.addAll(exportCaps);
+        m_capabilities.addAll(provideCaps);
 
         // Combine all requirements.
         m_requirements = new ArrayList(
-             importReqs.size() + requireReqs.size() + hostReqs.size() + dynamicReqs.size());
+            importReqs.size() + rbReqs.size() + hostReqs.size()
+            + requireReqs.size() + dynamicReqs.size());
         m_requirements.addAll(importReqs);
-        m_requirements.addAll(requireReqs);
+        m_requirements.addAll(rbReqs);
         m_requirements.addAll(hostReqs);
+        m_requirements.addAll(requireReqs);
         m_requirements.addAll(dynamicReqs);
 
         //
@@ -227,7 +250,7 @@
 
         // Check to see if there was an optional native library clause, which is
         // represented by a null library header; if so, record it and remove it.
-        if ((m_libraryClauses.size() > 0) &&
+        if (!m_libraryClauses.isEmpty() &&
             (m_libraryClauses.get(m_libraryClauses.size() - 1).getLibraryEntries() == null))
         {
             m_libraryHeadersOptional = true;
@@ -302,9 +325,8 @@
             }
 
             // Verify java.* is not imported, nor any duplicate imports.
-            for (int pathIdx = 0; pathIdx < clause.m_paths.size(); pathIdx++)
+            for (String pkgName : clause.m_paths)
             {
-                String pkgName = clause.m_paths.get(pathIdx);
                 if (!dupeSet.contains(pkgName))
                 {
                     // Verify that java.* packages are not imported.
@@ -314,7 +336,7 @@
                             "Importing java.* packages not allowed: " + pkgName);
                     }
                     // Make sure a package name was specified.
-                    else if (clause.m_paths.get(pathIdx).length() == 0)
+                    else if (pkgName.length() == 0)
                     {
                         throw new BundleException(
                             "Imported package names cannot be zero length.");
@@ -383,14 +405,12 @@
     {
         // Now convert generic header clauses into requirements.
         List reqList = new ArrayList();
-        for (int clauseIdx = 0; clauseIdx < clauses.size(); clauseIdx++)
+        for (ParsedHeaderClause clause : clauses)
         {
-            for (int pathIdx = 0;
-                pathIdx < clauses.get(clauseIdx).m_paths.size();
-                pathIdx++)
+            for (String path : clause.m_paths)
             {
                 // Prepend the package name to the array of attributes.
-                Map<String, Object> attrs = clauses.get(clauseIdx).m_attrs;
+                Map<String, Object> attrs = clause.m_attrs;
                 // Note that we use a linked hash map here to ensure the
                 // package attribute is first, which will make indexing
                 // more efficient.
@@ -399,7 +419,7 @@
                 Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
                 newAttrs.put(
                     BundleCapabilityImpl.PACKAGE_ATTR,
-                    clauses.get(clauseIdx).m_paths.get(pathIdx));
+                    path);
                 newAttrs.putAll(attrs);
 
                 // Create package requirement and add to requirement list.
@@ -407,7 +427,7 @@
                     new BundleRequirementImpl(
                         owner,
                         BundleCapabilityImpl.PACKAGE_NAMESPACE,
-                        clauses.get(clauseIdx).m_dirs,
+                        clause.m_dirs,
                         newAttrs));
             }
         }
@@ -473,9 +493,8 @@
 
             // Dynamic imports can have duplicates, so verify that java.*
             // packages are not imported.
-            for (int pathIdx = 0; pathIdx < clause.m_paths.size(); pathIdx++)
+            for (String pkgName : clause.m_paths)
             {
-                String pkgName = clause.m_paths.get(pathIdx);
                 if (pkgName.startsWith("java."))
                 {
                     throw new BundleException(
@@ -492,6 +511,180 @@
         return clauses;
     }
 
+    private static List<ParsedHeaderClause> normalizeRequireCapabilityClauses(
+        Logger logger, List<ParsedHeaderClause> clauses, String mv)
+        throws BundleException
+    {
+
+        if (!mv.equals("2") && !clauses.isEmpty())
+        {
+            // Should we error here if we are not an R4 bundle?
+        }
+
+        return clauses;
+    }
+
+    private static List<BundleRequirement> convertRequireCapabilities(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+        throws BundleException
+    {
+        // Now convert generic header clauses into requirements.
+        List reqList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            try
+            {
+                String filterStr = clause.m_dirs.get("filter");
+                SimpleFilter sf = (filterStr != null)
+                    ? SimpleFilter.parse(filterStr)
+                    : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+                for (String path : clause.m_paths)
+                {
+                    // Create requirement and add to requirement list.
+                    reqList.add(
+                        new BundleRequirementImpl(
+                            owner,
+                            path,
+                            clause.m_dirs,
+                            clause.m_attrs,
+                            sf));
+                }
+            }
+            catch (Exception ex)
+            {
+                throw new BundleException("Error creating requirement: " + ex);
+            }
+        }
+
+        return reqList;
+    }
+
+    private static List<ParsedHeaderClause> normalizeProvideCapabilityClauses(
+        Logger logger, List<ParsedHeaderClause> clauses, String mv)
+        throws BundleException
+    {
+
+        if (!mv.equals("2") && !clauses.isEmpty())
+        {
+            // Should we error here if we are not an R4 bundle?
+        }
+
+        // Convert attributes into specified types.
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (Entry<String, String> entry : clause.m_types.entrySet())
+            {
+                String type = entry.getValue();
+                if (!type.equals("String"))
+                {
+                    if (type.equals("Double"))
+                    {
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            new Double(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Version"))
+                    {
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            new Version(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Long"))
+                    {
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            new Long(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.startsWith("List"))
+                    {
+                        int startIdx = type.indexOf('<');
+                        int endIdx = type.indexOf('>');
+                        if (((startIdx > 0) && (endIdx <= startIdx))
+                            || ((startIdx < 0) && (endIdx > 0)))
+                        {
+                            throw new BundleException(
+                                "Invalid Provide-Capability attribute list type for '"
+                                + entry.getKey()
+                                + "' : "
+                                + type);
+                        }
+
+                        String listType = "String";
+                        if (endIdx > startIdx)
+                        {
+                            listType = type.substring(startIdx + 1, endIdx).trim();
+                        }
+
+                        List<String> tokens = parseDelimitedString(
+                            clause.m_attrs.get(entry.getKey()).toString(), ",", false);
+                        List<Object> values = new ArrayList<Object>(tokens.size());
+                        for (String token : tokens)
+                        {
+                            if (listType.equals("String"))
+                            {
+                                values.add(token);
+                            }
+                            else if (listType.equals("Double"))
+                            {
+                                values.add(new Double(token.trim()));
+                            }
+                            else if (listType.equals("Version"))
+                            {
+                                values.add(new Version(token.trim()));
+                            }
+                            else if (listType.equals("Long"))
+                            {
+                                values.add(new Long(token.trim()));
+                            }
+                            else
+                            {
+                                throw new BundleException(
+                                    "Unknown Provide-Capability attribute list type for '"
+                                    + entry.getKey()
+                                    + "' : "
+                                    + type);
+                            }
+                        }
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            values);
+                    }
+                    else
+                    {
+                        throw new BundleException(
+                            "Unknown Provide-Capability attribute type for '"
+                            + entry.getKey()
+                            + "' : "
+                            + type);
+                    }
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static List<BundleCapability> convertProvideCapabilities(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+    {
+        List<BundleCapability> capList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                // Create package capability and add to capability list.
+                capList.add(
+                    new BundleCapabilityImpl(
+                        owner,
+                        path,
+                        clause.m_dirs,
+                        clause.m_attrs));
+            }
+        }
+
+        return capList;
+    }
+
     private static List<ParsedHeaderClause> normalizeExportClauses(
         Logger logger, List<ParsedHeaderClause> clauses,
         String mv, String bsn, Version bv)
@@ -501,16 +694,16 @@
         for (ParsedHeaderClause clause : clauses)
         {
             // Verify that the named package has not already been declared.
-            for (int pathIdx = 0; pathIdx < clause.m_paths.size(); pathIdx++)
+            for (String pkgName : clause.m_paths)
             {
                 // Verify that java.* packages are not exported.
-                if (clause.m_paths.get(pathIdx).startsWith("java."))
+                if (pkgName.startsWith("java."))
                 {
                     throw new BundleException(
                         "Exporting java.* packages not allowed: "
-                        + clause.m_paths.get(pathIdx));
+                        + pkgName);
                 }
-                else if (clause.m_paths.get(pathIdx).length() == 0)
+                else if (pkgName.length() == 0)
                 {
                     throw new BundleException(
                         "Exported package names cannot be zero length.");
@@ -607,6 +800,35 @@
         return clauses;
     }
 
+    private static List<BundleCapability> convertExports(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+    {
+        List<BundleCapability> capList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String pkgName : clause.m_paths)
+            {
+                // Prepend the package name to the array of attributes.
+                Map<String, Object> attrs = clause.m_attrs;
+                Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
+                newAttrs.put(
+                    BundleCapabilityImpl.PACKAGE_ATTR,
+                    pkgName);
+                newAttrs.putAll(attrs);
+
+                // Create package capability and add to capability list.
+                capList.add(
+                    new BundleCapabilityImpl(
+                        owner,
+                        BundleCapabilityImpl.PACKAGE_NAMESPACE,
+                        clause.m_dirs,
+                        newAttrs));
+            }
+        }
+
+        return capList;
+    }
+
     public String getManifestVersion()
     {
         String manifestVersion = getManifestVersion(m_headerMap);
@@ -744,11 +966,11 @@
             List clauseList = new ArrayList();
 
             // Search for matching native clauses.
-            for (int i = 0; i < m_libraryClauses.size(); i++)
+            for (R4LibraryClause libraryClause : m_libraryClauses)
             {
-                if (m_libraryClauses.get(i).match(m_configMap))
+                if (libraryClause.match(m_configMap))
                 {
-                    clauseList.add(m_libraryClauses.get(i));
+                    clauseList.add(libraryClause);
                 }
             }
 
@@ -780,7 +1002,7 @@
         return null;
     }
 
-    private int firstSortedClause(List clauseList)
+    private int firstSortedClause(List<R4LibraryClause> clauseList)
     {
         ArrayList indexList = new ArrayList();
         ArrayList selection = new ArrayList();
@@ -918,7 +1140,8 @@
                 paths.add((String)
                     exports.get(i).getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
                 clauseList.add(
-                    new ParsedHeaderClause(paths, Collections.EMPTY_MAP, attrs));
+                    new ParsedHeaderClause(
+                        paths, Collections.EMPTY_MAP, attrs, Collections.EMPTY_MAP));
             }
         }
 
@@ -1136,37 +1359,6 @@
         return caps;
     }
 
-    private static List<BundleCapability> convertExports(
-        List<ParsedHeaderClause> clauses, BundleRevision owner)
-    {
-        List<BundleCapability> capList = new ArrayList();
-        for (int clauseIdx = 0; clauseIdx < clauses.size(); clauseIdx++)
-        {
-            for (int pathIdx = 0;
-                pathIdx < clauses.get(clauseIdx).m_paths.size();
-                pathIdx++)
-            {
-                // Prepend the package name to the array of attributes.
-                Map<String, Object> attrs = clauses.get(clauseIdx).m_attrs;
-                Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
-                newAttrs.put(
-                    BundleCapabilityImpl.PACKAGE_ATTR,
-                    clauses.get(clauseIdx).m_paths.get(pathIdx));
-                newAttrs.putAll(attrs);
-
-                // Create package capability and add to capability list.
-                capList.add(
-                    new BundleCapabilityImpl(
-                        owner,
-                        BundleCapabilityImpl.PACKAGE_NAMESPACE,
-                        clauses.get(clauseIdx).m_dirs,
-                        newAttrs));
-            }
-        }
-
-        return capList;
-    }
-
     private static List<ParsedHeaderClause> normalizeRequireClauses(
         Logger logger, List<ParsedHeaderClause> clauses, String mv)
     {
@@ -1178,13 +1370,12 @@
         else
         {
             // Convert bundle version attribute to VersionRange type.
-            for (int clauseIdx = 0; clauseIdx < clauses.size(); clauseIdx++)
+            for (ParsedHeaderClause clause : clauses)
             {
-                Object value = clauses.get(clauseIdx).m_attrs.get(
-                    Constants.BUNDLE_VERSION_ATTRIBUTE);
+                Object value = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
                 if (value != null)
                 {
-                    clauses.get(clauseIdx).m_attrs.put(
+                    clause.m_attrs.put(
                         Constants.BUNDLE_VERSION_ATTRIBUTE,
                         VersionRange.parse(value.toString()));
                 }
@@ -1198,14 +1389,12 @@
         List<ParsedHeaderClause> clauses, BundleRevision owner)
     {
         List<BundleRequirementImpl> reqList = new ArrayList();
-        for (int clauseIdx = 0; clauseIdx < clauses.size(); clauseIdx++)
+        for (ParsedHeaderClause clause : clauses)
         {
-            for (int pathIdx = 0;
-                pathIdx < clauses.get(clauseIdx).m_paths.size();
-                pathIdx++)
+            for (String path : clause.m_paths)
             {
                 // Prepend the bundle symbolic name to the array of attributes.
-                Map<String, Object> attrs = clauses.get(clauseIdx).m_attrs;
+                Map<String, Object> attrs = clause.m_attrs;
                 // Note that we use a linked hash map here to ensure the
                 // package attribute is first, which will make indexing
                 // more efficient.
@@ -1215,7 +1404,7 @@
                 Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
                 newAttrs.put(
                     Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE,
-                    clauses.get(clauseIdx).m_paths.get(pathIdx));
+                    path);
                 newAttrs.putAll(attrs);
 
                 // Create package requirement and add to requirement list.
@@ -1223,7 +1412,7 @@
                     new BundleRequirementImpl(
                         owner,
                         BundleCapabilityImpl.BUNDLE_NAMESPACE,
-                        clauses.get(clauseIdx).m_dirs,
+                        clause.m_dirs,
                         newAttrs));
             }
         }
@@ -1275,9 +1464,9 @@
         {
             // Just look for a "path" matching the lazy policy, ignore
             // everything else.
-            for (int clauseIdx = 0; clauseIdx < clauses.get(0).m_paths.size(); clauseIdx++)
+            for (String path : clauses.get(0).m_paths)
             {
-                if (clauses.get(0).m_paths.get(clauseIdx).equals(Constants.ACTIVATION_LAZY))
+                if (path.equals(Constants.ACTIVATION_LAZY))
                 {
                     m_activationPolicy = BundleRevisionImpl.LAZY_ACTIVATION;
                     for (Entry<String, String> entry : clauses.get(0).m_dirs.entrySet())
@@ -1297,15 +1486,11 @@
         }
     }
 
-    public static final int CLAUSE_PATHS_INDEX = 0;
-    public static final int CLAUSE_DIRECTIVES_INDEX = 1;
-    public static final int CLAUSE_ATTRIBUTES_INDEX = 2;
-
     // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2,
     //            path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
-    private static List<ParsedHeaderClause> parseStandardHeader(String header)
+    public static void main(String[] headers)
     {
-        List<ParsedHeaderClause> clauses = new ArrayList();
+        String header = headers[0];
 
         if (header != null)
         {
@@ -1315,110 +1500,279 @@
                     "A header cannot be an empty string.");
             }
 
-            List<String> clauseStrings = parseDelimitedString(
-                header, FelixConstants.CLASS_PATH_SEPARATOR);
-
-            for (int i = 0; (clauseStrings != null) && (i < clauseStrings.size()); i++)
+            List<ParsedHeaderClause> clauses = parseStandardHeader(header);
+            for (ParsedHeaderClause clause : clauses)
             {
-                clauses.add(parseStandardHeaderClause(clauseStrings.get(i)));
+                System.out.println("PATHS " + clause.m_paths);
+                System.out.println("    DIRS  " + clause.m_dirs);
+                System.out.println("    ATTRS " + clause.m_attrs);
+                System.out.println("    TYPES " + clause.m_types);
             }
         }
 
+//        return clauses;
+    }
+
+    private static List<ParsedHeaderClause> parseStandardHeader(String header)
+    {
+        List<ParsedHeaderClause> clauses = new ArrayList<ParsedHeaderClause>();
+        if (header != null)
+        {
+            int[] startIdx = new int[1];
+            startIdx[0] = 0;
+            for (int i = 0; i < header.length(); i++)
+            {
+                clauses.add(parseClause(startIdx, header));
+                i = startIdx[0];
+            }
+        }
         return clauses;
     }
 
-    // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
-    private static ParsedHeaderClause parseStandardHeaderClause(String clauseString)
-        throws IllegalArgumentException
+    private static ParsedHeaderClause parseClause(int[] startIdx, String header)
     {
-        // Break string into semi-colon delimited pieces.
-        List<String> pieces = parseDelimitedString(
-            clauseString, FelixConstants.PACKAGE_SEPARATOR);
-
-        // Count the number of different paths; paths
-        // will not have an '=' in their string. This assumes
-        // that paths come first, before directives and
-        // attributes.
-        int pathCount = 0;
-        for (int pieceIdx = 0; pieceIdx < pieces.size(); pieceIdx++)
+        ParsedHeaderClause clause = new ParsedHeaderClause(
+            new ArrayList<String>(),
+            new HashMap<String, String>(),
+            new HashMap<String, Object>(),
+            new HashMap<String, String>());
+        for (int i = startIdx[0]; i < header.length(); i++)
         {
-            if (pieces.get(pieceIdx).indexOf('=') >= 0)
+            char c = header.charAt(i);
+            if ((c == ':') || (c == '='))
             {
+                parseClauseParameters(startIdx, header, clause);
+                i = startIdx[0];
                 break;
             }
-            pathCount++;
+            else if ((c == ';') || (c == ',') || (i == (header.length() - 1)))
+            {
+                String path;
+                if (i == (header.length() - 1))
+                {
+                    path = header.substring(startIdx[0], header.length());
+                }
+                else
+                {
+                    path = header.substring(startIdx[0], i);
+                }
+                clause.m_paths.add(path.trim());
+                startIdx[0] = i + 1;
+                if (c == ',')
+                {
+                    break;
+                }
+            }
+        }
+        return clause;
+    }
+
+    private static void parseClauseParameters(
+        int[] startIdx, String header, ParsedHeaderClause clause)
+    {
+        for (int i = startIdx[0]; i < header.length(); i++)
+        {
+            char c = header.charAt(i);
+            if ((c == ':') && (header.charAt(i + 1) == '='))
+            {
+                parseClauseDirective(startIdx, header, clause);
+                i = startIdx[0];
+            }
+            else if ((c == ':') || (c == '='))
+            {
+                parseClauseAttribute(startIdx, header, clause);
+                i = startIdx[0];
+            }
+            else if (c == ',')
+            {
+                startIdx[0] = i + 1;
+                break;
+            }
+        }
+    }
+
+    private static void parseClauseDirective(
+        int[] startIdx, String header, ParsedHeaderClause clause)
+    {
+        String name = null;
+        String value = null;
+        boolean isQuoted = false;
+        boolean isEscaped = false;
+        for (int i = startIdx[0]; i < header.length(); i++)
+        {
+            char c = header.charAt(i);
+            if (!isEscaped && (c == '"'))
+            {
+                isQuoted = !isQuoted;
+            }
+
+            if (!isEscaped
+                && !isQuoted && (c == ':'))
+            {
+                name = header.substring(startIdx[0], i);
+                startIdx[0] = i + 2;
+            }
+            else if (!isEscaped
+                && !isQuoted && ((c == ';') || (c == ',') || (i == (header.length() - 1))))
+            {
+                if (i == (header.length() - 1))
+                {
+                    value = header.substring(startIdx[0], header.length());
+                }
+                else
+                {
+                    value = header.substring(startIdx[0], i);
+                }
+                if (c == ',')
+                {
+                    startIdx[0] = i - 1;
+                }
+                else
+                {
+                    startIdx[0] = i + 1;
+                }
+                break;
+            }
+
+            isEscaped = (c == '\\');
         }
 
-        // Error if no paths were specified.
-        if (pathCount == 0)
+        // Trim whitespace.
+        name = name.trim();
+        value = value.trim();
+
+        // Remove quotes, if value is quoted.
+        if (value.startsWith("\"") && value.endsWith("\""))
+        {
+            value = value.substring(1, value.length() - 1);
+        }
+
+        // Check for dupes.
+        if (clause.m_dirs.get(name) != null)
         {
             throw new IllegalArgumentException(
-                "No paths specified in header: " + clauseString);
+                "Duplicate directive '" + name + "' in: " + header);
         }
 
-        // Create an array of paths.
-        List<String> paths = new ArrayList<String>(pathCount);
-        for (int pathIdx = 0; pathIdx < pathCount; pathIdx++)
+        clause.m_dirs.put(name, value);
+    }
+
+    private static void parseClauseAttribute(
+        int[] startIdx, String header, ParsedHeaderClause clause)
+    {
+        String type = null;
+
+        String name = parseClauseAttributeName(startIdx, header);
+        char c = header.charAt(startIdx[0]);
+        startIdx[0]++;
+        if (c == ':')
         {
-            paths.add(pieces.get(pathIdx));
+            type = parseClauseAttributeType(startIdx, header);
         }
 
-        // Parse the directives/attributes.
-        Map<String, String> dirs = new HashMap<String, String>();
-        Map<String, Object> attrs = new HashMap<String, Object>();
-        int idx = -1;
-        String sep = null;
-        for (int pieceIdx = pathCount; pieceIdx < pieces.size(); pieceIdx++)
+        String value = parseClauseAttributeValue(startIdx, header);
+
+        // Trim whitespace.
+        name = name.trim();
+        value = value.trim();
+        if (type != null)
         {
-            // Check if it is a directive.
-            if ((idx = pieces.get(pieceIdx).indexOf(FelixConstants.DIRECTIVE_SEPARATOR)) >= 0)
-            {
-                sep = FelixConstants.DIRECTIVE_SEPARATOR;
-            }
-            // Check if it is an attribute.
-            else if ((idx = pieces.get(pieceIdx).indexOf(FelixConstants.ATTRIBUTE_SEPARATOR)) >= 0)
-            {
-                sep = FelixConstants.ATTRIBUTE_SEPARATOR;
-            }
-            // It is an error.
-            else
-            {
-                throw new IllegalArgumentException("Not a directive/attribute: " + clauseString);
-            }
-
-            String key = pieces.get(pieceIdx).substring(0, idx).trim();
-            String value = pieces.get(pieceIdx).substring(idx + sep.length()).trim();
-
-            // Remove quotes, if value is quoted.
-            if (value.startsWith("\"") && value.endsWith("\""))
-            {
-                value = value.substring(1, value.length() - 1);
-            }
-
-            // Save the directive/attribute in the appropriate array.
-            if (sep.equals(FelixConstants.DIRECTIVE_SEPARATOR))
-            {
-                // Check for duplicates.
-                if (dirs.get(key) != null)
-                {
-                    throw new IllegalArgumentException(
-                        "Duplicate directive: " + key);
-                }
-                dirs.put(key, value);
-            }
-            else
-            {
-                // Check for duplicates.
-                if (attrs.get(key) != null)
-                {
-                    throw new IllegalArgumentException(
-                        "Duplicate attribute: " + key);
-                }
-                attrs.put(key, value);
-            }
+            type = type.trim();
         }
 
-        return new ParsedHeaderClause(paths, dirs, attrs);
+        // Remove quotes, if value is quoted.
+        if (value.startsWith("\"") && value.endsWith("\""))
+        {
+            value = value.substring(1, value.length() - 1);
+        }
+
+        // Check for dupes.
+        if (clause.m_attrs.get(name) != null)
+        {
+            throw new IllegalArgumentException(
+                "Duplicate attribute '" + name + "' in: " + header);
+        }
+
+        clause.m_attrs.put(name, value);
+        if (type != null)
+        {
+            clause.m_types.put(name, type);
+        }
+    }
+
+    private static String parseClauseAttributeName(int[] startIdx, String header)
+    {
+        for (int i = startIdx[0]; i < header.length(); i++)
+        {
+            char c = header.charAt(i);
+            if ((c == '=') || (c == ':'))
+            {
+                String name = header.substring(startIdx[0], i);
+                startIdx[0] = i;
+                return name;
+            }
+        }
+        return null;
+    }
+
+    private static String parseClauseAttributeType(int[] startIdx, String header)
+    {
+        for (int i = startIdx[0]; i < header.length(); i++)
+        {
+            char c = header.charAt(i);
+            if (c == '=')
+            {
+                String type = header.substring(startIdx[0], i);
+                startIdx[0] = i + 1;
+                return type;
+            }
+        }
+        return null;
+    }
+
+    private static String parseClauseAttributeValue(int[] startIdx, String header)
+    {
+        boolean isQuoted = false;
+        boolean isEscaped = false;
+        for (int i = startIdx[0]; i < header.length(); i++)
+        {
+            char c = header.charAt(i);
+            if (!isEscaped && (c == '"'))
+            {
+                isQuoted = !isQuoted;
+            }
+
+            if (!isEscaped &&
+                !isQuoted && ((c == ';') || (c == ',') || (i == (header.length() - 1))))
+            {
+                String value;
+                if (i == (header.length() - 1))
+                {
+                    value = header.substring(startIdx[0], header.length());
+                }
+                else
+                {
+                    value = header.substring(startIdx[0], i);
+                }
+                if (c == ',')
+                {
+                    startIdx[0] = i - 1;
+                }
+                else
+                {
+                    startIdx[0] = i + 1;
+                }
+                return value;
+            }
+
+            isEscaped = (c == '\\');
+        }
+        return null;
+    }
+
+    public static List<String> parseDelimitedString(String value, String delim)
+    {
+        return parseDelimitedString(value, delim, true);
     }
 
     /**
@@ -1430,7 +1784,7 @@
      * @param delim the characters delimiting the tokens.
      * @return a list of string or an empty list if there are none.
     **/
-    public static List<String> parseDelimitedString(String value, String delim)
+    public static List<String> parseDelimitedString(String value, String delim, boolean trim)
     {
         if (value == null)
         {
@@ -1448,25 +1802,42 @@
 
         int expecting = (CHAR | DELIMITER | STARTQUOTE);
 
+        boolean isEscaped = false;
         for (int i = 0; i < value.length(); i++)
         {
             char c = value.charAt(i);
 
             boolean isDelimiter = (delim.indexOf(c) >= 0);
-            boolean isQuote = (c == '"');
 
-            if (isDelimiter && ((expecting & DELIMITER) > 0))
+            if (c == '\\')
             {
-                list.add(sb.toString().trim());
+                isEscaped = true;
+                continue;
+            }
+
+            if (isEscaped)
+            {
+                sb.append(c);
+            }
+            else if (isDelimiter && ((expecting & DELIMITER) > 0))
+            {
+                if (trim)
+                {
+                    list.add(sb.toString().trim());
+                }
+                else
+                {
+                    list.add(sb.toString());
+                }
                 sb.delete(0, sb.length());
                 expecting = (CHAR | DELIMITER | STARTQUOTE);
             }
-            else if (isQuote && ((expecting & STARTQUOTE) > 0))
+            else if ((c == '"') && ((expecting & STARTQUOTE) > 0))
             {
                 sb.append(c);
                 expecting = CHAR | ENDQUOTE;
             }
-            else if (isQuote && ((expecting & ENDQUOTE) > 0))
+            else if ((c == '"') && ((expecting & ENDQUOTE) > 0))
             {
                 sb.append(c);
                 expecting = (CHAR | STARTQUOTE | DELIMITER);
@@ -1479,11 +1850,20 @@
             {
                 throw new IllegalArgumentException("Invalid delimited string: " + value);
             }
+
+            isEscaped = false;
         }
 
         if (sb.length() > 0)
         {
-            list.add(sb.toString().trim());
+            if (trim)
+            {
+                list.add(sb.toString().trim());
+            }
+            else
+            {
+                list.add(sb.toString());
+            }
         }
 
         return list;
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ParsedHeaderClause.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ParsedHeaderClause.java
index ab095e5..7c393f2 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ParsedHeaderClause.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ParsedHeaderClause.java
@@ -26,12 +26,15 @@
     public final List<String> m_paths;
     public final Map<String, String> m_dirs;
     public final Map<String, Object> m_attrs;
+    public final Map<String, String> m_types;
 
     public ParsedHeaderClause(
-        List<String> paths, Map<String, String> dirs, Map<String, Object> attrs)
+        List<String> paths, Map<String, String> dirs, Map<String, Object> attrs,
+        Map<String, String> types)
     {
         m_paths = paths;
         m_dirs = dirs;
         m_attrs = attrs;
+        m_types = types;
     }
 }
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
index e8ffef7..f7400b4 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
@@ -60,18 +60,20 @@
         m_attrs = Collections.unmodifiableMap(attrs);
 
         // Find all export directives: uses, mandatory, include, and exclude.
-        
-        m_uses = new ArrayList(0);
+
+        List<String> uses = Collections.EMPTY_LIST;
         String value = m_dirs.get(Constants.USES_DIRECTIVE);
         if (value != null)
         {
             // Parse these uses directive.
             StringTokenizer tok = new StringTokenizer(value, ",");
+            uses = new ArrayList(tok.countTokens());
             while (tok.hasMoreTokens())
             {
-                m_uses.add(tok.nextToken().trim());
+                uses.add(tok.nextToken().trim());
             }
         }
+        m_uses = uses;
 
         value = m_dirs.get(Constants.INCLUDE_DIRECTIVE);
         if (value != null)
@@ -105,17 +107,18 @@
             m_excludeFilter = null;
         }
 
-        m_mandatory = new HashSet<String>(0);
+        Set<String> mandatory = Collections.EMPTY_SET;
         value = m_dirs.get(Constants.MANDATORY_DIRECTIVE);
         if (value != null)
         {
             List<String> names = ManifestParser.parseDelimitedString(value, ",");
+            mandatory = new HashSet<String>(names.size());
             for (String name : names)
             {
                 // If attribute exists, then record it as mandatory.
                 if (m_attrs.containsKey(name))
                 {
-                    m_mandatory.add(name);
+                    mandatory.add(name);
                 }
                 // Otherwise, report an error.
                 else
@@ -125,6 +128,7 @@
                 }
             }
         }
+        m_mandatory = mandatory;
     }
 
     public BundleRevision getRevision()
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
index 00f579f..6f267b3 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
@@ -6,9 +6,9 @@
  * 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
@@ -42,13 +42,13 @@
 
     public BundleRequirementImpl(
         BundleRevision revision, String namespace,
-        Map<String, String> dirs, Map<String, Object> attrs)
+        Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter filter)
     {
         m_revision = revision;
         m_namespace = namespace;
         m_dirs = Collections.unmodifiableMap(dirs);
         m_attrs = Collections.unmodifiableMap(attrs);
-        m_filter = convertToFilter(attrs);
+        m_filter = filter;
 
         // Find resolution import directives.
         boolean optional = false;
@@ -60,6 +60,13 @@
         m_optional = optional;
     }
 
+    public BundleRequirementImpl(
+        BundleRevision revision, String namespace,
+        Map<String, String> dirs, Map<String, Object> attrs)
+    {
+        this(revision, namespace, dirs, attrs, convertToFilter(attrs));
+    }
+
     public String getNamespace()
     {
         return m_namespace;
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleWireImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleWireImpl.java
index 2909b0a..51afe24 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleWireImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleWireImpl.java
@@ -63,8 +63,7 @@
 
     public String toString()
     {
-        return "[" + m_requirer + "] "
-            + m_req
+        return m_req
             + " -> "
             + "[" + m_provider + "]";
     }
diff --git a/framework/src/main/resources/default.properties b/framework/src/main/resources/default.properties
index c210a03..f9cd8e5 100644
--- a/framework/src/main/resources/default.properties
+++ b/framework/src/main/resources/default.properties
@@ -28,9 +28,10 @@
 ee-1.4=J2SE-1.4,J2SE-1.3,OSGi/Minimum-1.2,OSGi/Minimum-1.1,OSGi/Minimum-1.0
 ee-1.3=J2SE-1.3,OSGi/Minimum-1.1,OSGi/Minimum-1.0
 
-org.osgi.framework.system.packages=org.osgi.framework; version=1.5.0, \
+org.osgi.framework.system.packages=org.osgi.framework; version=1.6.0, \
  org.osgi.framework.launch; version=1.0.0, \
  org.osgi.framework.hooks.service; version=1.0.0, \
+ org.osgi.framework.wiring; version=1.0.0, \
  org.osgi.service.packageadmin; version=1.2.0, \
  org.osgi.service.startlevel; version=1.1.0, \
  org.osgi.service.url; version=1.0.0, \