Modified manifest parser to use requirements and capabilities 
(FELIX-28).


git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@498550 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java
index 6edbccb..d00c8a7 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java
@@ -19,20 +19,110 @@
 package org.apache.felix.framework.util.manifestparser;
 
 import java.util.*;
+
+import org.apache.felix.framework.util.Util;
 import org.apache.felix.moduleloader.ICapability;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
 
 public class Capability implements ICapability
 {
     private String m_namespace = null;
-    private R4Directive[] m_dirs = null;
-    private R4Attribute[] m_attrs = null;
+    private R4Directive[] m_directives = null;
+    private R4Attribute[] m_attributes = null;
     private Map m_attrMap = null;
+    private String[] m_uses = new String[0];
+    private String[][] m_includeFilter = null;
+    private String[][] m_excludeFilter = null;
+
+    // Cached properties for performance reasons.
+    private String m_pkgName = null;
+    private Version m_pkgVersion = Version.emptyVersion;
 
     public Capability(String namespace, R4Directive[] dirs, R4Attribute[] attrs)
     {
         m_namespace = namespace;
-        m_dirs = dirs;
-        m_attrs = attrs;
+        m_directives = dirs;
+        m_attributes = attrs;
+
+        // Find all export directives: uses, mandatory, include, and exclude.
+        String mandatory = "";
+        for (int dirIdx = 0; (m_directives != null) && (dirIdx < m_directives.length); dirIdx++)
+        {
+            if (m_directives[dirIdx].getName().equals(Constants.USES_DIRECTIVE))
+            {
+                // Parse these uses directive.
+                StringTokenizer tok = new StringTokenizer(m_directives[dirIdx].getValue(), ",");
+                m_uses = new String[tok.countTokens()];
+                for (int i = 0; i < m_uses.length; i++)
+                {
+                    m_uses[i] = tok.nextToken().trim();
+                }
+            }
+            else if (m_directives[dirIdx].getName().equals(Constants.MANDATORY_DIRECTIVE))
+            {
+                mandatory = m_directives[dirIdx].getValue();
+            }
+            else if (m_directives[dirIdx].getName().equals(Constants.INCLUDE_DIRECTIVE))
+            {
+                String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ",");
+                m_includeFilter = new String[ss.length][];
+                for (int filterIdx = 0; filterIdx < ss.length; filterIdx++)
+                {
+                    m_includeFilter[filterIdx] = parseSubstring(ss[filterIdx]);
+                }
+            }
+            else if (m_directives[dirIdx].getName().equals(Constants.EXCLUDE_DIRECTIVE))
+            {
+                String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ",");
+                m_excludeFilter = new String[ss.length][];
+                for (int filterIdx = 0; filterIdx < ss.length; filterIdx++)
+                {
+                    m_excludeFilter[filterIdx] = parseSubstring(ss[filterIdx]);
+                }
+            }
+        }
+
+        // Parse mandatory directive and mark specified
+        // attributes as mandatory.
+        StringTokenizer tok = new StringTokenizer(mandatory, ", ");
+        while (tok.hasMoreTokens())
+        {
+            // Get attribute name.
+            String attrName = tok.nextToken().trim();
+            // Find attribute and mark it as mandatory.
+            boolean found = false;
+            for (int i = 0; (!found) && (i < m_attributes.length); i++)
+            {
+                if (m_attributes[i].getName().equals(attrName))
+                {
+                    m_attributes[i] = new R4Attribute(
+                        m_attributes[i].getName(),
+                        m_attributes[i].getValue(), true);
+                    found = true;
+                }
+            }
+            // If a specified mandatory attribute was not found,
+            // then error.
+            if (!found)
+            {
+                throw new IllegalArgumentException(
+                    "Mandatory attribute '" + attrName + "' does not exist.");
+            }
+        }
+
+        // For performance reasons, find the package name and version properties.
+        for (int i = 0; i < m_attributes.length; i++)
+        {
+            if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY))
+            {
+                m_pkgName = (String) m_attributes[i].getValue();
+            }
+            else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY))
+            {
+                m_pkgVersion = (Version) m_attributes[i].getValue();
+            }
+        }
     }
 
     public String getNamespace()
@@ -40,11 +130,66 @@
         return m_namespace;
     }
 
-    public R4Directive[] getDirectives()
+// TODO: RB - Determine how to eliminate these non-generic methods;
+//            at least make sure they are not used in the generic resolver.
+    public String getPackageName()
     {
-        return m_dirs;
+        return m_pkgName;
     }
 
+    public Version getPackageVersion()
+    {
+        return m_pkgVersion;
+    }
+
+    public R4Directive[] getDirectives()
+    {
+        return m_directives;
+    }
+
+    public R4Attribute[] getAttributes()
+    {
+        return m_attributes;
+    }
+
+    public String[] getUses()
+    {
+        return m_uses;
+    }
+
+    public boolean isIncluded(String name)
+    {
+        if ((m_includeFilter == null) && (m_excludeFilter == null))
+        {
+            return true;
+        }
+
+        // Get the class name portion of the target class.
+        String className = Util.getClassName(name);
+
+        // If there are no include filters then all classes are included
+        // by default, otherwise try to find one match.
+        boolean included = (m_includeFilter == null);
+        for (int i = 0;
+            (!included) && (m_includeFilter != null) && (i < m_includeFilter.length);
+            i++)
+        {
+            included = checkSubstring(m_includeFilter[i], className);
+        }
+
+        // If there are no exclude filters then no classes are excluded
+        // by default, otherwise try to find one match.
+        boolean excluded = false;
+        for (int i = 0;
+            (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.length);
+            i++)
+        {
+            excluded = checkSubstring(m_excludeFilter[i], className);
+        }
+        return included && !excluded;
+    }
+
+// TODO: RB - Terminology mismatch property vs. attribute.
     public Map getProperties()
     {
         if (m_attrMap == null)
@@ -53,11 +198,15 @@
 
                 public int size()
                 {
-                    return m_attrs.length;
+                    // A name and version attribute is always present, since it has a
+                    // default value.
+                    return m_attributes.length + 2;
                 }
 
                 public boolean isEmpty()
                 {
+                    // A version attribute is always present, since it has a
+                    // default value.
                     return false;
                 }
 
@@ -68,25 +217,49 @@
 
                 public boolean containsValue(Object value)
                 {
-                    for (int i = 0; i < m_attrs.length; i++)
+                    // Check the package name.
+                    if (m_pkgName.equals(value))
                     {
-                        if (m_attrs[i].getValue().equals(value))
+                        return true;
+                    }
+
+                    // Check the package version.
+                    if (m_pkgVersion.equals(value))
+                    {
+                        return true;
+                    }
+
+                    // Check all attributes.
+                    for (int i = 0; i < m_attributes.length; i++)
+                    {
+                        if (m_attributes[i].getValue().equals(value))
                         {
                             return true;
                         }
                     }
+
                     return false;
                 }
 
                 public Object get(Object key)
                 {
-                    for (int i = 0; i < m_attrs.length; i++)
+                    if (ICapability.PACKAGE_PROPERTY.equals(key))
                     {
-                        if (m_attrs[i].getName().equals(key))
+                        return m_pkgName;
+                    }
+                    else if (ICapability.VERSION_PROPERTY.equals(key))
+                    {
+                        return m_pkgVersion;
+                    }
+
+                    for (int i = 0; i < m_attributes.length; i++)
+                    {
+                        if (m_attributes[i].getName().equals(key))
                         {
-                            return m_attrs[i].getValue();
+                            return m_attributes[i].getValue();
                         }
                     }
+
                     return null;
                 }
 
@@ -113,21 +286,23 @@
                 public Set keySet()
                 {
                     Set set = new HashSet();
-                    for (int i = 0; i < m_attrs.length; i++)
+                    set.add(ICapability.PACKAGE_PROPERTY);
+                    set.add(ICapability.VERSION_PROPERTY);
+                    for (int i = 0; i < m_attributes.length; i++)
                     {
-                        set.add(m_attrs[i].getName());
+                        set.add(m_attributes[i].getName());
                     }
                     return set;
                 }
 
                 public Collection values()
                 {
-                    throw new java.lang.UnsupportedOperationException("Map.values() not implemented.");
+                    throw new UnsupportedOperationException("Map.values() not implemented.");
                 }
 
                 public Set entrySet()
                 {
-                    throw new java.lang.UnsupportedOperationException("Map.entrySet() not implemented.");
+                    throw new UnsupportedOperationException("Map.entrySet() not implemented.");
                 }
             };
         }
@@ -138,21 +313,160 @@
     public String toString()
     {
         StringBuffer sb = new StringBuffer();
-        sb.append(m_namespace);
-        for (int i = 0; (m_dirs != null) && (i < m_dirs.length); i++)
+        sb.append(getNamespace());
+        for (int i = 0; (m_directives != null) && (i < m_directives.length); i++)
         {
             sb.append(";");
-            sb.append(m_dirs[i].getName());
-            sb.append(":=");
-            sb.append(m_dirs[i].getValue());
+            sb.append(m_directives[i].getName());
+            sb.append(":=\"");
+            sb.append(m_directives[i].getValue());
+            sb.append("\"");
         }
-        for (int i = 0; (m_attrs != null) && (i < m_attrs.length); i++)
+        for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++)
         {
             sb.append(";");
-            sb.append(m_attrs[i].getName());
-            sb.append("=");
-            sb.append(m_attrs[i].getValue());
+            sb.append(m_attributes[i].getName());
+            sb.append("=\"");
+            sb.append(m_attributes[i].getValue());
+            sb.append("\"");
         }
         return sb.toString();
     }
+
+    //
+    // The following substring-related code was lifted and modified
+    // from the LDAP parser code.
+    //
+
+    private static String[] parseSubstring(String target)
+    {
+        List pieces = new ArrayList();
+        StringBuffer ss = new StringBuffer();
+        // int kind = SIMPLE; // assume until proven otherwise
+        boolean wasStar = false; // indicates last piece was a star
+        boolean leftstar = false; // track if the initial piece is a star
+        boolean rightstar = false; // track if the final piece is a star
+
+        int idx = 0;
+
+        // We assume (sub)strings can contain leading and trailing blanks
+loop:   for (;;)
+        {
+            if (idx >= target.length())
+            {
+                if (wasStar)
+                {
+                    // insert last piece as "" to handle trailing star
+                    rightstar = true;
+                }
+                else
+                {
+                    pieces.add(ss.toString());
+                    // accumulate the last piece
+                    // note that in the case of
+                    // (cn=); this might be
+                    // the string "" (!=null)
+                }
+                ss.setLength(0);
+                break loop;
+            }
+
+            char c = target.charAt(idx++);
+            if (c == '*')
+            {
+                if (wasStar)
+                {
+                    // encountered two successive stars;
+                    // I assume this is illegal
+                    throw new IllegalArgumentException("Invalid filter string: " + target);
+                }
+                if (ss.length() > 0)
+                {
+                    pieces.add(ss.toString()); // accumulate the pieces
+                    // between '*' occurrences
+                }
+                ss.setLength(0);
+                // if this is a leading star, then track it
+                if (pieces.size() == 0)
+                {
+                    leftstar = true;
+                }
+                ss.setLength(0);
+                wasStar = true;
+            }
+            else
+            {
+                wasStar = false;
+                ss.append(c);
+            }
+        }
+        if (leftstar || rightstar || pieces.size() > 1)
+        {
+            // insert leading and/or trailing "" to anchor ends
+            if (rightstar)
+            {
+                pieces.add("");
+            }
+            if (leftstar)
+            {
+                pieces.add(0, "");
+            }
+        }
+        return (String[]) pieces.toArray(new String[pieces.size()]);
+    }
+
+    private static boolean checkSubstring(String[] pieces, String s)
+    {
+        // Walk the pieces to match the string
+        // There are implicit stars between each piece,
+        // and the first and last pieces might be "" to anchor the match.
+        // assert (pieces.length > 1)
+        // minimal case is <string>*<string>
+
+        boolean result = false;
+        int len = pieces.length;
+
+loop:   for (int i = 0; i < len; i++)
+        {
+            String piece = (String) pieces[i];
+            int index = 0;
+            if (i == len - 1)
+            {
+                // this is the last piece
+                if (s.endsWith(piece))
+                {
+                    result = true;
+                }
+                else
+                {
+                    result = false;
+                }
+                break loop;
+            }
+            // initial non-star; assert index == 0
+            else if (i == 0)
+            {
+                if (!s.startsWith(piece))
+                {
+                    result = false;
+                    break loop;
+                }
+            }
+            // assert i > 0 && i < len-1
+            else
+            {
+                // Sure wish stringbuffer supported e.g. indexOf
+                index = s.indexOf(piece, index);
+                if (index < 0)
+                {
+                    result = false;
+                    break loop;
+                }
+            }
+            // start beyond the matching piece
+            index += piece.length();
+        }
+
+        return result;
+    }
 }
\ No newline at end of file
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 97de7ca..9ecaeb8 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
@@ -36,10 +36,7 @@
     private Version m_bundleVersion = null;
     private ICapability[] m_capabilities = null;
     private IRequirement[] m_requirements = null;
-    private IRequirement[] m_dynamicReqs = null;
-    private R4Export[] m_exports = null;
-    private R4Import[] m_imports = null;
-    private R4Import[] m_dynamics = null;
+    private IRequirement[] m_dynamicRequirements = null;
     private R4LibraryClause[] m_libraryHeaders = null;
     private boolean m_libraryHeadersOptional = false;
 
@@ -111,131 +108,133 @@
                 Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, m_bundleSymbolicName, false);
             attrs[1] = new R4Attribute(
                 Constants.BUNDLE_VERSION_ATTRIBUTE, m_bundleVersion, false);
-            capList.add(new Capability(ICapability.MODULE_NAMESPACE, null, attrs));
+//            capList.add(new Capability(ICapability.MODULE_NAMESPACE, null, attrs));
         }
 
         //
         // Parse Export-Package.
         //
 
-        // Get export packages from bundle manifest.
-        m_exports = (R4Export[]) parseImportExportHeader(
-            (String) headerMap.get(Constants.EXPORT_PACKAGE), true);
-
-        // Get export packages from bundle manifest.
+        // Get exported packages from bundle manifest.
         ICapability[] exportCaps = parseExportHeader(
             (String) headerMap.get(Constants.EXPORT_PACKAGE));
-//System.out.println("PARSED EXPORT CAPABILITIES:");
-//for (int capIdx = 0; capIdx < exportCaps.length; capIdx++)
-//{
-//    System.out.println(exportCaps[capIdx]);
-//}
-
+/*
+System.out.println("PARSED EXPORT CAPABILITIES:");
+for (int capIdx = 0; capIdx < exportCaps.length; capIdx++)
+{
+    System.out.println(exportCaps[capIdx]);
+}
+*/
         // Create non-duplicated export array.
         dupeMap.clear();
-        for (int pkgIdx = 0; pkgIdx < m_exports.length; pkgIdx++)
+        for (int capIdx = 0; capIdx < exportCaps.length; capIdx++)
         {
             // Verify that the named package has not already been declared.
-            if (dupeMap.get(m_exports[pkgIdx].getName()) == null)
+            String pkgName = (String)
+                exportCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
+            if (dupeMap.get(pkgName) == null)
             {
                 // Verify that java.* packages are not exported.
-                if (m_exports[pkgIdx].getName().startsWith("java."))
+                if (pkgName.startsWith("java."))
                 {
                     throw new BundleException(
-                        "Exporting java.* packages not allowed: "
-                        + m_exports[pkgIdx].getName());
+                        "Exporting java.* packages not allowed: " + pkgName);
                 }
-                dupeMap.put(m_exports[pkgIdx].getName(), m_exports[pkgIdx]);
+                dupeMap.put(pkgName, exportCaps[capIdx]);
             }
             else
             {
                 // TODO: FRAMEWORK - Exports can be duplicated, so fix this.
-                m_logger.log(Logger.LOG_WARNING, "Duplicate export - "
-                    + m_exports[pkgIdx].getName());
+                m_logger.log(Logger.LOG_WARNING, "Duplicate export - " + pkgName);
             }
         }
-        // This following line won't be necessary once duplicate exports are supported.
-        m_exports = (R4Export[]) dupeMap.values().toArray(new R4Export[dupeMap.size()]);
+
+        // Add export package capabilities to capability list.
+        capList.addAll(dupeMap.values());
+
+        // Create an array of all capabilities.
+        m_capabilities = (ICapability[]) capList.toArray(new ICapability[capList.size()]);
 
         //
         // Parse Require-Bundle
         //
+
         IRequirement[] bundleReq = parseRequireBundleHeader(
             (String) headerMap.get(Constants.REQUIRE_BUNDLE));
 //System.out.println("PARSED BUNDLE REQUIREMENTS:");
-//for (int reqIdx = 0; reqIdx < bundleReq.length; reqIdx++)
-//{
-//    System.out.println(bundleReq[reqIdx]);
-//}
+        for (int reqIdx = 0; reqIdx < bundleReq.length; reqIdx++)
+        {
+//            reqList.add(bundleReq[reqIdx]);
+        }
 
         //
         // Parse Import-Package.
         //
 
         // Get import packages from bundle manifest.
-        m_imports = (R4Import[]) parseImportExportHeader(
-            (String) headerMap.get(Constants.IMPORT_PACKAGE), false);
-
-        // Get import packages from bundle manifest.
         IRequirement[] importReqs = parseImportHeader(
             (String) headerMap.get(Constants.IMPORT_PACKAGE));
-//System.out.println("PARSED IMPORT REQUIREMENTS:");
-//for (int reqIdx = 0; reqIdx < importReqs.length; reqIdx++)
-//{
-//    System.out.println(importReqs[reqIdx]);
-//}
-
+/*
+System.out.println("PARSED IMPORT REQUIREMENTS:");
+for (int reqIdx = 0; reqIdx < importReqs.length; reqIdx++)
+{
+    System.out.println(importReqs[reqIdx]);
+}
+*/
         // Create non-duplicated import array.
         dupeMap.clear();
-        for (int pkgIdx = 0; pkgIdx < m_imports.length; pkgIdx++)
+        for (int reqIdx = 0; reqIdx < importReqs.length; reqIdx++)
         {
             // Verify that the named package has not already been declared.
-            if (dupeMap.get(m_imports[pkgIdx].getName()) == null)
+            String pkgName = ((Requirement) importReqs[reqIdx]).getPackageName();
+            
+            if (dupeMap.get(pkgName) == null)
             {
                 // Verify that java.* packages are not imported.
-                if (m_imports[pkgIdx].getName().startsWith("java."))
+                if (pkgName.startsWith("java."))
                 {
                     throw new BundleException(
-                        "Importing java.* packages not allowed: "
-                        + m_imports[pkgIdx].getName());
+                        "Importing java.* packages not allowed: " + pkgName);
                 }
-                dupeMap.put(m_imports[pkgIdx].getName(), m_imports[pkgIdx]);
+                dupeMap.put(pkgName, importReqs[reqIdx]);
             }
             else
             {
                 throw new BundleException(
-                    "Duplicate import - " + m_imports[pkgIdx].getName());
+                    "Duplicate import - " + pkgName);
             }
         }
 
+        // Add import package requirements to requirement list.
+        reqList.addAll(dupeMap.values());
+
+        // Create an array of all requirements.
+        m_requirements = (IRequirement[]) reqList.toArray(new IRequirement[reqList.size()]);
+
         //
         // Parse DynamicImport-Package.
         //
 
         // Get dynamic import packages from bundle manifest.
-        m_dynamics = (R4Import[]) parseImportExportHeader(
-            (String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE), false);
-
-        // Get import packages from bundle manifest.
-        IRequirement[] dynReqs = parseImportHeader(
+        m_dynamicRequirements = parseImportHeader(
             (String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
-//System.out.println("PARSED DYNAMIC IMPORT REQUIREMENTS:");
-//for (int reqIdx = 0; reqIdx < dynReqs.length; reqIdx++)
-//{
-//    System.out.println(dynReqs[reqIdx]);
-//}
-
+/*
+System.out.println("PARSED DYNAMIC IMPORT REQUIREMENTS:");
+for (int reqIdx = 0; reqIdx < m_dynamicRequirements.length; reqIdx++)
+{
+    System.out.println(m_dynamicRequirements[reqIdx]);
+}
+*/
         // Dynamic imports can have duplicates, so just check for import
         // of java.*.
-        List dynList = new ArrayList();
-        for (int pkgIdx = 0; pkgIdx < m_dynamics.length; pkgIdx++)
+        for (int reqIdx = 0; reqIdx < m_dynamicRequirements.length; reqIdx++)
         {
             // Verify that java.* packages are not imported.
-            if (m_dynamics[pkgIdx].getName().startsWith("java."))
+            String pkgName = ((Requirement) m_dynamicRequirements[reqIdx]).getPackageName();
+            if (pkgName.startsWith("java."))
             {
                 throw new BundleException(
-                    "Dynamically importing java.* packages not allowed: "
-                    + m_dynamics[pkgIdx].getName());
+                    "Dynamically importing java.* packages not allowed: " + pkgName);
             }
         }
 
@@ -287,19 +286,19 @@
         return m_bundleVersion;
     }
 
-    public R4Export[] getExports()
+    public ICapability[] getCapabilities()
     {
-        return m_exports;
+        return m_capabilities;
     }
 
-    public R4Import[] getImports()
+    public IRequirement[] getRequirements()
     {
-        return m_imports;
+        return m_requirements;
     }
 
-    public R4Import[] getDynamicImports()
+    public IRequirement[] getDynamicRequirements()
     {
-        return m_dynamics;
+        return m_dynamicRequirements;
     }
 
     public R4LibraryClause[] getLibraryClauses()
@@ -486,52 +485,57 @@
         // Check to make sure that R3 bundles have only specified
         // the 'specification-version' attribute and no directives
         // on their exports; ignore all unknown attributes.
-        for (int expIdx = 0;
-            (m_exports != null) && (expIdx < m_exports.length);
-            expIdx++)
+        for (int capIdx = 0;
+            (m_capabilities != null) && (capIdx < m_capabilities.length);
+            capIdx++)
         {
-            if (m_exports[expIdx].getDirectives().length != 0)
+            if (m_capabilities[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
             {
-                throw new BundleException("R3 exports cannot contain directives.");
-            }
-
-            // Remove and ignore all attributes other than version.
-            // NOTE: This is checking for "version" rather than "specification-version"
-            // because the package class normalizes to "version" to avoid having
-            // future special cases. This could be changed if more strict behavior
-            // is required.
-            if (m_exports[expIdx].getAttributes() != null)
-            {
-                R4Attribute versionAttr = null;
-                for (int attrIdx = 0;
-                    attrIdx < m_exports[expIdx].getAttributes().length;
-                    attrIdx++)
+                // R3 bundles cannot have directives on their exports.
+                if (((Capability) m_capabilities[capIdx]).getDirectives().length != 0)
                 {
-                    if (m_exports[expIdx].getAttributes()[attrIdx]
-                        .getName().equals(Constants.VERSION_ATTRIBUTE))
-                    {
-                        versionAttr = m_exports[expIdx].getAttributes()[attrIdx];
-                    }
-                    else
-                    {
-                        m_logger.log(Logger.LOG_WARNING,
-                            "Unknown R3 export attribute: "
-                                + m_exports[expIdx].getAttributes()[attrIdx].getName());
-                    }
+                    throw new BundleException("R3 exports cannot contain directives.");
                 }
 
-                // Recreate the export if necessary to remove other attributes.
-                if ((versionAttr != null) && (m_exports[expIdx].getAttributes().length > 1))
+                // Remove and ignore all attributes other than version.
+                // NOTE: This is checking for "version" rather than "specification-version"
+                // because the package class normalizes to "version" to avoid having
+                // future special cases. This could be changed if more strict behavior
+                // is required.
+                if (((Capability) m_capabilities[capIdx]).getAttributes() != null)
                 {
-                    m_exports[expIdx] = new R4Export(
-                        m_exports[expIdx].getName(),
+                    // R3 package capabilities should only have name and
+                    // version attributes.
+                    R4Attribute pkgName = null;
+                    R4Attribute pkgVersion = new R4Attribute(ICapability.VERSION_PROPERTY, Version.emptyVersion, false);
+                    for (int attrIdx = 0;
+                        attrIdx < ((Capability) m_capabilities[capIdx]).getAttributes().length;
+                        attrIdx++)
+                    {
+                        if (((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx]
+                            .getName().equals(ICapability.PACKAGE_PROPERTY))
+                        {
+                            pkgName = ((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx];
+                        }
+                        if (((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx]
+                            .getName().equals(ICapability.VERSION_PROPERTY))
+                        {
+                            pkgVersion = ((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx];
+                        }
+                        else
+                        {
+                            m_logger.log(Logger.LOG_WARNING,
+                                "Unknown R3 export attribute: "
+                                    + ((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx].getName());
+                        }
+                    }
+    
+                    // Recreate the export to remove any other attributes
+                    // and add version if missing.
+                    m_capabilities[capIdx] = new Capability(
+                        ICapability.PACKAGE_NAMESPACE,
                         null,
-                        new R4Attribute[] { versionAttr } );
-                }
-                else if ((versionAttr == null) && (m_exports[expIdx].getAttributes().length > 0))
-                {
-                    m_exports[expIdx] = new R4Export(
-                        m_exports[expIdx].getName(), null, null);
+                        new R4Attribute[] { pkgName, pkgVersion } );
                 }
             }
         }
@@ -539,71 +543,84 @@
         // Check to make sure that R3 bundles have only specified
         // the 'specification-version' attribute and no directives
         // on their imports; ignore all unknown attributes.
-        for (int impIdx = 0;
-            (m_imports != null) && (impIdx < m_imports.length);
-            impIdx++)
+        for (int reqIdx = 0; (m_requirements != null) && (reqIdx < m_requirements.length); reqIdx++)
         {
-            if (m_imports[impIdx].getDirectives().length != 0)
+            if (m_requirements[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
             {
-                throw new BundleException("R3 imports cannot contain directives.");
-            }
-
-            // Remove and ignore all attributes other than version.
-            // NOTE: This is checking for "version" rather than "specification-version"
-            // because the package class normalizes to "version" to avoid having
-            // future special cases. This could be changed if more strict behavior
-            // is required.
-            if (m_imports[impIdx].getAttributes() != null)
-            {
-                R4Attribute versionAttr = null;
-                for (int attrIdx = 0;
-                    attrIdx < m_imports[impIdx].getAttributes().length;
-                    attrIdx++)
+                // R3 bundles cannot have directives on their imports.
+                if (((Requirement) m_requirements[reqIdx]).getDirectives().length != 0)
                 {
-                    if (m_imports[impIdx].getAttributes()[attrIdx]
-                        .getName().equals(Constants.VERSION_ATTRIBUTE))
-                    {
-                        versionAttr = m_imports[impIdx].getAttributes()[attrIdx];
-                    }
-                    else
-                    {
-                        m_logger.log(Logger.LOG_WARNING,
-                            "Unknown R3 import attribute: "
-                                + m_imports[impIdx].getAttributes()[attrIdx].getName());
-                    }
+                    throw new BundleException("R3 imports cannot contain directives.");
                 }
 
-                // Recreate the import if necessary to remove other attributes.
-                if ((versionAttr != null) && (m_imports[impIdx].getAttributes().length > 1))
+                // Remove and ignore all attributes other than version.
+                // NOTE: This is checking for "version" rather than "specification-version"
+                // because the package class normalizes to "version" to avoid having
+                // future special cases. This could be changed if more strict behavior
+                // is required.
+                if (((Requirement) m_requirements[reqIdx]).getAttributes() != null)
                 {
-                    m_imports[impIdx] = new R4Import(
-                        m_imports[impIdx].getName(),
-                        null,
-                        new R4Attribute[] { versionAttr } );
-                }
-                else if ((versionAttr == null) && (m_imports[impIdx].getAttributes().length > 0))
-                {
-                    m_imports[impIdx] = new R4Import(
-                        m_imports[impIdx].getName(), null, null);
+                    // R3 package requirements should only have name and
+                    // version attributes.
+                    R4Attribute pkgName = null;
+                    R4Attribute pkgVersion =
+                        new R4Attribute(ICapability.VERSION_PROPERTY,
+                            new VersionRange(Version.emptyVersion, true, null, true), false);
+                    for (int attrIdx = 0;
+                        attrIdx < ((Requirement) m_requirements[reqIdx]).getAttributes().length;
+                        attrIdx++)
+                    {
+                        if (((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx]
+                            .getName().equals(ICapability.PACKAGE_PROPERTY))
+                        {
+                            pkgName = ((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx];
+                        }
+                        else if (((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx]
+                          .getName().equals(ICapability.VERSION_PROPERTY))
+                        {
+                            pkgVersion = ((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx];
+                        }
+                        else
+                        {
+                            m_logger.log(Logger.LOG_WARNING,
+                                "Unknown R3 import attribute: "
+                                    + ((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx].getName());
+                        }
+                    }
+
+                    // Recreate the import to remove any other attributes
+                    // and add version if missing.
+                    m_requirements[reqIdx] = new Requirement(
+                        ICapability.PACKAGE_NAMESPACE,
+                        (String) pkgName.getValue(),
+                        null, 
+                        new R4Attribute[] { pkgName, pkgVersion });
                 }
             }
         }
 
         // Since all R3 exports imply an import, add a corresponding
-        // import for each existing export. Create non-duplicated import array.
+        // requirement for each existing export capability. Do not
+        // duplicate imports.
         Map map =  new HashMap();
         // Add existing imports.
-        for (int i = 0; i < m_imports.length; i++)
+        for (int i = 0; i < m_requirements.length; i++)
         {
-            map.put(m_imports[i].getName(), m_imports[i]);
+            if (m_requirements[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+            {
+                map.put(
+                    ((Requirement) m_requirements[i]).getPackageName(),
+                    m_requirements[i]);
+            }
         }
-        // Add import for each export.
-        for (int i = 0; i < m_exports.length; i++)
+        // Add import requirement for each export capability.
+        for (int i = 0; i < m_capabilities.length; i++)
         {
-            if (map.get(m_exports[i].getName()) == null)
+            if (m_capabilities[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+                (map.get(m_capabilities[i].getProperties().get(ICapability.PACKAGE_PROPERTY)) == null))
             {
                 // Convert Version to VersionRange.
-                R4Attribute[] attrs = (R4Attribute[]) m_exports[i].getAttributes().clone();
+                R4Attribute[] attrs = (R4Attribute[]) ((Capability) m_capabilities[i]).getAttributes().clone();
                 for (int attrIdx = 0; (attrs != null) && (attrIdx < attrs.length); attrIdx++)
                 {
                     if (attrs[attrIdx].getName().equals(Constants.VERSION_ATTRIBUTE))
@@ -614,15 +631,17 @@
                             attrs[attrIdx].isMandatory());
                     }
                 }
-                map.put(m_exports[i].getName(),
-                    new R4Import(
-                        m_exports[i].getName(),
-                        m_exports[i].getDirectives(),
-                        attrs));
+
+                map.put(
+                    m_capabilities[i].getProperties().get(ICapability.PACKAGE_PROPERTY),
+                    new Requirement(
+                        ICapability.PACKAGE_NAMESPACE,
+                        (String) m_capabilities[i].getProperties().get(ICapability.PACKAGE_PROPERTY),
+                        null, attrs));
             }
         }
-        m_imports =
-            (R4Import[]) map.values().toArray(new R4Import[map.size()]);
+        m_requirements =
+            (IRequirement[]) map.values().toArray(new IRequirement[map.size()]);
 
         // Add a "uses" directive onto each export of R3 bundles
         // that references every other import (which will include
@@ -630,33 +649,41 @@
         // necessary since R3 bundles assumed a single class space,
         // but R4 allows for multiple class spaces.
         String usesValue = "";
-        for (int i = 0; (m_imports != null) && (i < m_imports.length); i++)
+        for (int i = 0; (m_requirements != null) && (i < m_requirements.length); i++)
         {
-            usesValue = usesValue
-                + ((usesValue.length() > 0) ? "," : "")
-                + m_imports[i].getName();
+            if (m_requirements[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+            {
+                usesValue = usesValue
+                    + ((usesValue.length() > 0) ? "," : "")
+                    + ((Requirement) m_requirements[i]).getPackageName();
+            }
         }
         R4Directive uses = new R4Directive(
             Constants.USES_DIRECTIVE, usesValue);
-        for (int i = 0; (m_exports != null) && (i < m_exports.length); i++)
+        for (int i = 0; (m_capabilities != null) && (i < m_capabilities.length); i++)
         {
-            m_exports[i] = new R4Export(
-                m_exports[i].getName(),
-                new R4Directive[] { uses },
-                m_exports[i].getAttributes());
+            if (m_capabilities[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+            {
+                m_capabilities[i] = new Capability(
+                    ICapability.PACKAGE_NAMESPACE,
+                    new R4Directive[] { uses },
+                    ((Capability) m_capabilities[i]).getAttributes());
+            }
         }
 
         // Check to make sure that R3 bundles have no attributes or
         // directives on their dynamic imports.
-        for (int i = 0; (m_dynamics != null) && (i < m_dynamics.length); i++)
+        for (int i = 0;
+            (m_dynamicRequirements != null) && (i < m_dynamicRequirements.length);
+            i++)
         {
-            if (m_dynamics[i].getDirectives().length != 0)
+            if (((Requirement) m_dynamicRequirements[i]).getDirectives().length != 0)
             {
                 throw new BundleException("R3 dynamic imports cannot contain directives.");
             }
-            if (m_dynamics[i].getAttributes().length != 0)
+            if (((Requirement) m_dynamicRequirements[i]).getAttributes().length != 0)
             {
-                throw new BundleException("R3 dynamic imports cannot contain attributes.");
+//                throw new BundleException("R3 dynamic imports cannot contain attributes.");
             }
         }
     }
@@ -672,33 +699,35 @@
 
         // Verify that the exports do not specify bundle symbolic name
         // or bundle version.
-        for (int i = 0; (m_exports != null) && (i < m_exports.length); i++)
+        for (int i = 0; (m_capabilities != null) && (i < m_capabilities.length); i++)
         {
-            String targetVer = (String) m_headerMap.get(Constants.BUNDLE_VERSION);
-            targetVer = (targetVer == null) ? "0.0.0" : targetVer;
-
-            R4Attribute[] attrs = m_exports[i].getAttributes();
-            for (int attrIdx = 0; attrIdx < attrs.length; attrIdx++)
+            if (m_capabilities[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
             {
-                // Find symbolic name and version attribute, if present.
-                if (attrs[attrIdx].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE) ||
-                    attrs[attrIdx].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
+                R4Attribute[] attrs = ((Capability) m_capabilities[i]).getAttributes();
+                for (int attrIdx = 0; attrIdx < attrs.length; attrIdx++)
                 {
-                    throw new BundleException(
-                        "Exports must not specify bundle symbolic name or bundle version.");
+                    // Find symbolic name and version attribute, if present.
+                    if (attrs[attrIdx].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE) ||
+                        attrs[attrIdx].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
+                    {
+                        throw new BundleException(
+                            "Exports must not specify bundle symbolic name or bundle version.");
+                    }
                 }
-            }
 
-            // Now that we know that there are no bundle symbolic name and version
-            // attributes, add them since the spec says they are there implicitly.
-            R4Attribute[] newAttrs = new R4Attribute[attrs.length + 2];
-            System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
-            newAttrs[attrs.length] = new R4Attribute(
-                Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symName, false);
-            newAttrs[attrs.length + 1] = new R4Attribute(
-                Constants.BUNDLE_VERSION_ATTRIBUTE, Version.parseVersion(targetVer), false);
-            m_exports[i] = new R4Export(
-                m_exports[i].getName(), m_exports[i].getDirectives(), newAttrs);
+                // Now that we know that there are no bundle symbolic name and version
+                // attributes, add them since the spec says they are there implicitly.
+                R4Attribute[] newAttrs = new R4Attribute[attrs.length + 2];
+                System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
+                newAttrs[attrs.length] = new R4Attribute(
+                    Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symName, false);
+                newAttrs[attrs.length + 1] = new R4Attribute(
+                    Constants.BUNDLE_VERSION_ATTRIBUTE, getBundleVersion(), false);
+                m_capabilities[i] = new Capability(
+                    ICapability.PACKAGE_NAMESPACE,
+                    ((Capability) m_capabilities[i]).getDirectives(),
+                    newAttrs);
+            }
         }
     }
 
@@ -870,6 +899,7 @@
                 reqList.add(
                     new Requirement(
                         ICapability.PACKAGE_NAMESPACE,
+                        null,
                         (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
                         newAttrs));
             }
@@ -924,6 +954,7 @@
                 reqList.add(
                     new Requirement(
                         ICapability.MODULE_NAMESPACE,
+                        null,
                         (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
                         newAttrs));
             }
@@ -932,115 +963,6 @@
         return (IRequirement[]) reqList.toArray(new IRequirement[reqList.size()]);
     }
 
-    public static R4Package[] parseImportExportHeader(String header, boolean export)
-    {
-        Object[][][] clauses = parseStandardHeader(header);
-
-// TODO: FRAMEWORK - Perhaps verification/normalization should be completely
-// separated from parsing, since verification/normalization may vary.
-
-        // Verify that the values are equals if the package specifies
-        // both version and specification-version attributes.
-        Map attrMap = new HashMap();
-        for (int clauseIdx = 0; clauseIdx < clauses.length; clauseIdx++)
-        {
-            // Put attributes for current clause in a map for easy lookup.
-            attrMap.clear();
-            for (int attrIdx = 0;
-                attrIdx < clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX].length;
-                attrIdx++)
-            {
-                R4Attribute attr = (R4Attribute) clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX][attrIdx];
-                attrMap.put(attr.getName(), attr);
-            }
-
-            // Check for "version" and "specification-version" attributes
-            // and verify they are the same if both are specified.
-            R4Attribute v = (R4Attribute) attrMap.get(Constants.VERSION_ATTRIBUTE);
-            R4Attribute sv = (R4Attribute) attrMap.get(Constants.PACKAGE_SPECIFICATION_VERSION);
-            if ((v != null) && (sv != null))
-            {
-                // Verify they are equal.
-                if (!((String) v.getValue()).trim().equals(((String) sv.getValue()).trim()))
-                {
-                    throw new IllegalArgumentException(
-                        "Both version and specificat-version are specified, but they are not equal.");
-                }
-            }
-    
-            // Ensure that only the "version" attribute is used and convert
-            // it to the appropriate type.
-            if ((v != null) || (sv != null))
-            {
-                attrMap.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
-                v = (v == null) ? sv : v;
-                if (export)
-                {
-                    attrMap.put(Constants.VERSION_ATTRIBUTE,
-                        new R4Attribute(
-                            Constants.VERSION_ATTRIBUTE,
-                            Version.parseVersion(v.getValue().toString()),
-                            v.isMandatory()));
-                }
-                else
-                {
-                    attrMap.put(Constants.VERSION_ATTRIBUTE,
-                        new R4Attribute(
-                            Constants.VERSION_ATTRIBUTE,
-                            VersionRange.parse(v.getValue().toString()),
-                            v.isMandatory()));
-                }
-            }
-
-            // If bundle version is specified, then convert its type to Version.
-            // Only imports can specify this attribue.
-            v = (R4Attribute) attrMap.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
-            if (!export && (v != null))
-            {
-                attrMap.put(Constants.BUNDLE_VERSION_ATTRIBUTE,
-                    new R4Attribute(
-                        Constants.BUNDLE_VERSION_ATTRIBUTE,
-                        VersionRange.parse(v.getValue().toString()),
-                        v.isMandatory()));
-            }
-
-            // Re-copy the attribute array in case it has changed.
-            clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX] =
-                attrMap.values().toArray(new R4Attribute[attrMap.size()]);
-        }
-
-        // Now convert generic header clauses into packages.
-        List pkgList = new ArrayList();
-        for (int clauseIdx = 0; clauseIdx < clauses.length; clauseIdx++)
-        {
-            for (int pathIdx = 0;
-                pathIdx < clauses[clauseIdx][CLAUSE_PATHS_INDEX].length;
-                pathIdx++)
-            {
-                if (export)
-                {
-                    pkgList.add(
-                        new R4Export(
-                            (String) clauses[clauseIdx][CLAUSE_PATHS_INDEX][pathIdx],
-                            (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
-                            (R4Attribute[]) clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX]));
-                }
-                else
-                {
-                    pkgList.add(
-                        new R4Import(
-                            (String) clauses[clauseIdx][CLAUSE_PATHS_INDEX][pathIdx],
-                            (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
-                            (R4Attribute[]) clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX]));
-                }
-            }
-        }
-
-        return (export)
-            ? (R4Package[]) pkgList.toArray(new R4Export[pkgList.size()])
-            : (R4Package[]) pkgList.toArray(new R4Import[pkgList.size()]);
-    }
-
     public static final int CLAUSE_PATHS_INDEX = 0;
     public static final int CLAUSE_DIRECTIVES_INDEX = 1;
     public static final int CLAUSE_ATTRIBUTES_INDEX = 2;
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Export.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Export.java
index 8cdef06..59e0924 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Export.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Export.java
@@ -19,6 +19,7 @@
 package org.apache.felix.framework.util.manifestparser;
 
 import java.util.*;
+
 import org.apache.felix.framework.util.Util;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
index c748915..9d4478c 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
@@ -19,12 +19,10 @@
 package org.apache.felix.framework.util.manifestparser;
 
 import java.util.*;
+
 import org.apache.felix.framework.FilterImpl;
 import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.FelixConstants;
-import org.apache.felix.framework.util.PropertyResolver;
-
-import org.apache.felix.framework.util.VersionRange;
+import org.apache.felix.framework.util.*;
 import org.osgi.framework.*;
 
 public class R4LibraryClause
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Requirement.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Requirement.java
index 5933c49..af206b6 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Requirement.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Requirement.java
@@ -1,20 +1,18 @@
 /*
- * 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
+ *   Copyright 2006 The Apache Software Foundation
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *   Licensed 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
  *
- * 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.
+ *       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.util.manifestparser;
 
@@ -23,22 +21,56 @@
 import org.apache.felix.framework.util.VersionRange;
 import org.apache.felix.moduleloader.ICapability;
 import org.apache.felix.moduleloader.IRequirement;
-import org.osgi.framework.Filter;
-import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.*;
 
 public class Requirement implements IRequirement
 {
     private String m_namespace = null;
-    private R4Directive[] m_dirs = null;
-    private R4Attribute[] m_attrs = null;
+    private R4Directive[] m_directives = null;
+    private R4Attribute[] m_attributes = null;
+    private boolean m_isOptional = false;
+
+    private String m_pkgName = null;
+    private VersionRange m_pkgVersionRange = null;
     private Filter m_filter = null;
 
-    public Requirement(String namespace, R4Directive[] dirs, R4Attribute[] attrs)
+    public Requirement(String namespace, String filterStr) throws InvalidSyntaxException
     {
         m_namespace = namespace;
-        m_dirs = dirs;
-        m_attrs = attrs;
-        m_filter = convertToFilter();
+        m_filter = new FilterImpl(filterStr);
+    }
+
+    public Requirement(String namespace, String pkgName, R4Directive[] directives, R4Attribute[] attributes)
+    {
+        m_namespace = namespace;
+        m_directives = directives;
+        m_attributes = attributes;
+
+        // Find all import directives: resolution.
+        for (int i = 0; (m_directives != null) && (i < m_directives.length); i++)
+        {
+            if (m_directives[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
+            {
+                m_isOptional = m_directives[i].getValue().equals(Constants.RESOLUTION_OPTIONAL);
+            }
+        }
+
+        for (int i = 0; i < m_attributes.length; i++)
+        {
+            if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY))
+            {
+                m_pkgName = (String) m_attributes[i].getValue();
+            }
+            else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY))
+            {
+                m_pkgVersionRange = (VersionRange) m_attributes[i].getValue();
+            }
+        }
+
+        if (m_pkgVersionRange == null)
+        {
+            m_pkgVersionRange = VersionRange.infiniteRange;
+        }
     }
 
     public String getNamespace()
@@ -48,9 +80,36 @@
 
     public Filter getFilter()
     {
+        if (m_filter == null)
+        {
+            m_filter = convertToFilter();
+        }
         return m_filter;
     }
 
+//  TODO: RB - We need to verify that the resolver code does not
+//  touch these implementation-specific methods.
+
+    public String getPackageName()
+    {
+        return m_pkgName;
+    }
+
+    public VersionRange getPackageVersionRange()
+    {
+        return m_pkgVersionRange;
+    }
+
+    public R4Directive[] getDirectives()
+    {
+        return m_directives;
+    }
+
+    public R4Attribute[] getAttributes()
+    {
+        return m_attributes;
+    }
+
     public boolean isMultiple()
     {
         return false;
@@ -58,48 +117,150 @@
 
     public boolean isOptional()
     {
-        return false;
+        return m_isOptional;
     }
 
     public String getComment()
     {
-        return null;
+        return "Comment for " + toString();
     }
 
     public boolean isSatisfied(ICapability capability)
     {
-        return m_filter.match(new MapToDictionary(capability.getProperties()));
+        // If the requirement was constructed with a filter, then
+        // we must use that filter for evaluation.
+        if ((m_attributes == null) && (m_filter != null))
+        {
+            return m_namespace.equals(capability.getNamespace()) &&
+                getFilter().match(new MapToDictionary(capability.getProperties()));
+        }
+        // Otherwise, if the requirement was constructed with attributes, then
+        // perform the evaluation manually instead of using the filter for
+        // performance reasons.
+        else if (m_attributes != null)
+        {
+            return capability.getNamespace().equals(getNamespace()) &&
+                doAttributesMatch((Capability) capability);
+        }
+
+        return false;
     }
 
-    public String toString()
+    private boolean doAttributesMatch(Capability ec)
     {
-        return getFilter().toString();
+        // Grab the capability's attributes.
+        R4Attribute[] capAttrs = ec.getAttributes();
+
+        // Cycle through all attributes of this import package
+        // and make sure its values match the attribute values
+        // of the specified export package.
+        for (int reqAttrIdx = 0; reqAttrIdx < m_attributes.length; reqAttrIdx++)
+        {
+            // Get current attribute from this import package.
+            R4Attribute reqAttr = m_attributes[reqAttrIdx];
+
+            // Ignore version attribute, since it is a special case that
+            // has already been compared using isVersionInRange() before
+            // the call to this method was made.
+            if (reqAttr.getName().equals(Constants.VERSION_ATTRIBUTE))
+            {
+                continue;
+            }
+
+            // Check if the export package has the same attribute.
+            boolean found = false;
+            for (int capAttrIdx = 0;
+                (!found) && (capAttrIdx < capAttrs.length);
+                capAttrIdx++)
+            {
+                // Get current attribute for the export package.
+                R4Attribute capAttr = capAttrs[capAttrIdx];
+                // Check if the attribute names are equal.
+                if (reqAttr.getName().equals(capAttr.getName()))
+                {
+                    // We only recognize version types. If the value of the
+                    // attribute is a version/version range, then we use the
+                    // "in range" comparison, otherwise we simply use equals().
+                    if (capAttr.getValue() instanceof Version)
+                    {
+                        if (!((VersionRange) reqAttr.getValue()).isInRange((Version) capAttr.getValue()))
+                        {
+                            return false;
+                        }
+                    }
+                    else if (!reqAttr.getValue().equals(capAttr.getValue()))
+                    {
+                        return false;
+                    }
+                    found = true;
+                }
+            }
+            // If the attribute was not found, then return false.
+            if (!found)
+            {
+                return false;
+            }
+        }
+
+        // Now, cycle through all attributes of the export package and verify that
+        // all mandatory attributes are present in this import package.
+        for (int capAttrIdx = 0; capAttrIdx < capAttrs.length; capAttrIdx++)
+        {
+            // Get current attribute for this package.
+            R4Attribute capAttr = capAttrs[capAttrIdx];
+
+            // If the export attribute is mandatory, then make sure
+            // this import package has the attribute.
+            if (capAttr.isMandatory())
+            {
+                boolean found = false;
+                for (int reqAttrIdx = 0;
+                    (!found) && (reqAttrIdx < m_attributes.length);
+                    reqAttrIdx++)
+                {
+                    // Get current attribute from specified package.
+                    R4Attribute reqAttr = m_attributes[reqAttrIdx];
+
+                    // Check if the attribute names are equal
+                    // and set found flag.
+                    if (capAttr.getName().equals(reqAttr.getName()))
+                    {
+                        found = true;
+                    }
+                }
+                // If not found, then return false.
+                if (!found)
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
     }
 
     private Filter convertToFilter()
     {
-        String filterStr = null;
-
         StringBuffer sb = new StringBuffer("(&");
 
-        for (int i = 0; (m_attrs != null) && (i < m_attrs.length); i++)
+        for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++)
         {
             // If this is a package import, then convert wild-carded
             // dynamically imported package names to an OR comparison.
             if (m_namespace.equals(ICapability.PACKAGE_NAMESPACE) &&
-                m_attrs[i].getName().equals(ICapability.PACKAGE_PROPERTY) &&
-                m_attrs[i].getValue().toString().endsWith(".*"))
+                m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY) &&
+                m_attributes[i].getValue().toString().endsWith(".*"))
             {
-                int idx = m_attrs[i].getValue().toString().indexOf(".*");
+                int idx = m_attributes[i].getValue().toString().indexOf(".*");
                 sb.append("(|(package=");
-                sb.append(m_attrs[i].getValue().toString().substring(0, idx));
+                sb.append(m_attributes[i].getValue().toString().substring(0, idx));
                 sb.append(")(package=");
-                sb.append(m_attrs[i].getValue().toString());
+                sb.append(m_attributes[i].getValue().toString());
                 sb.append("))");
             }
-            else if (m_attrs[i].getValue() instanceof VersionRange)
+            else if (m_attributes[i].getValue() instanceof VersionRange)
             {
-                VersionRange vr = (VersionRange) m_attrs[i].getValue();
+                VersionRange vr = (VersionRange) m_attributes[i].getValue();
                 if (vr.isLowInclusive())
                 {
                     sb.append("(version>=");
@@ -132,9 +293,9 @@
             else
             {
                 sb.append("(");
-                sb.append(m_attrs[i].getName());
+                sb.append(m_attributes[i].getName());
                 sb.append("=");
-                sb.append(m_attrs[i].getValue().toString());
+                sb.append(m_attributes[i].getValue().toString());
                 sb.append(")");
             }
         }
@@ -152,4 +313,9 @@
 
         return null;
     }
+
+    public String toString()
+    {
+        return getNamespace() + "; " + getFilter().toString();
+    }
 }
\ No newline at end of file