Modified manifest parser to generate generic capabilities and
requirements for exports and imports, respectively. The generic
capabilities/requirements are currently not used, but this is ongoing
work for require-bundle support (FELIX-28).
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@490526 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 05ccff1..6edbccb 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
@@ -18,19 +18,21 @@
*/
package org.apache.felix.framework.util.manifestparser;
-import java.util.Collections;
-import java.util.Map;
+import java.util.*;
import org.apache.felix.moduleloader.ICapability;
public class Capability implements ICapability
{
private String m_namespace = null;
- private Map m_propMap = null;
+ private R4Directive[] m_dirs = null;
+ private R4Attribute[] m_attrs = null;
+ private Map m_attrMap = null;
- public Capability(String namespace, Map propMap)
+ public Capability(String namespace, R4Directive[] dirs, R4Attribute[] attrs)
{
m_namespace = namespace;
- m_propMap = Collections.unmodifiableMap(propMap);
+ m_dirs = dirs;
+ m_attrs = attrs;
}
public String getNamespace()
@@ -38,13 +40,119 @@
return m_namespace;
}
- public Map getProperties()
+ public R4Directive[] getDirectives()
{
- return m_propMap;
+ return m_dirs;
}
- public String[] getUses()
+ public Map getProperties()
{
- return null;
+ if (m_attrMap == null)
+ {
+ m_attrMap = new Map() {
+
+ public int size()
+ {
+ return m_attrs.length;
+ }
+
+ public boolean isEmpty()
+ {
+ return false;
+ }
+
+ public boolean containsKey(Object key)
+ {
+ return (get(key) != null);
+ }
+
+ public boolean containsValue(Object value)
+ {
+ for (int i = 0; i < m_attrs.length; i++)
+ {
+ if (m_attrs[i].getValue().equals(value))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Object get(Object key)
+ {
+ for (int i = 0; i < m_attrs.length; i++)
+ {
+ if (m_attrs[i].getName().equals(key))
+ {
+ return m_attrs[i].getValue();
+ }
+ }
+ return null;
+ }
+
+ public Object put(Object key, Object value)
+ {
+ throw new UnsupportedOperationException("Map.put() not implemented.");
+ }
+
+ public Object remove(Object key)
+ {
+ throw new UnsupportedOperationException("Map.remove() not implemented.");
+ }
+
+ public void putAll(Map t)
+ {
+ throw new UnsupportedOperationException("Map.putAll() not implemented.");
+ }
+
+ public void clear()
+ {
+ throw new UnsupportedOperationException("Map.clear() not implemented.");
+ }
+
+ public Set keySet()
+ {
+ Set set = new HashSet();
+ for (int i = 0; i < m_attrs.length; i++)
+ {
+ set.add(m_attrs[i].getName());
+ }
+ return set;
+ }
+
+ public Collection values()
+ {
+ throw new java.lang.UnsupportedOperationException("Map.values() not implemented.");
+ }
+
+ public Set entrySet()
+ {
+ throw new java.lang.UnsupportedOperationException("Map.entrySet() not implemented.");
+ }
+ };
+ }
+ return m_attrMap;
+ }
+
+// TODO: RB - Remove or simplify toString() for final version.
+ 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(";");
+ sb.append(m_dirs[i].getName());
+ sb.append(":=");
+ sb.append(m_dirs[i].getValue());
+ }
+ for (int i = 0; (m_attrs != null) && (i < m_attrs.length); i++)
+ {
+ sb.append(";");
+ sb.append(m_attrs[i].getName());
+ sb.append("=");
+ sb.append(m_attrs[i].getValue());
+ }
+ return sb.toString();
}
}
\ 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 b47f7a7..3081466 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,8 @@
import org.apache.felix.framework.cache.BundleRevision;
import org.apache.felix.framework.searchpolicy.*;
import org.apache.felix.framework.util.*;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IRequirement;
import org.osgi.framework.*;
public class ManifestParser
@@ -33,6 +35,9 @@
private Map m_headerMap = null;
private String m_bundleSymbolicName = null;
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;
@@ -54,7 +59,16 @@
"Unknown 'Bundle-ManifestVersion' value: " + manifestVersion);
}
- // Verify bundle version syntax.
+ // Create map to check for duplicate imports/exports
+ // and lists to hold capabilities and requirements.
+ List capList = new ArrayList();
+ List reqList = new ArrayList();
+ Map dupeMap = new HashMap();
+
+ //
+ // Get bundle version.
+ //
+
if (m_headerMap.get(Constants.BUNDLE_VERSION) != null)
{
try
@@ -72,13 +86,6 @@
}
}
- // Create map to check for duplicate imports/exports.
- Map dupeMap = new HashMap();
-
- //
- // Get bundle version.
- //
-
//
// Parse bundle symbolic name.
//
@@ -100,10 +107,10 @@
+ headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
}
m_bundleSymbolicName = (String) clauses[0][CLAUSE_PATHS_INDEX][0];
-// Map propMap = new HashMap();
-// propMap.put("symbolicname", m_bundleSymbolicName);
-// propMap.put("version", m_bundleVersion);
-// capList.add(new Capability(ICapability.MODULE_NAMESPACE, propMap));
+ R4Attribute[] attrs = new R4Attribute[2];
+ attrs[0] = new R4Attribute("symbolicname", m_bundleSymbolicName, false);
+ attrs[1] = new R4Attribute("version", m_bundleVersion, false);
+ capList.add(new Capability(ICapability.MODULE_NAMESPACE, null, attrs));
}
//
@@ -114,6 +121,15 @@
m_exports = (R4Export[]) parseImportExportHeader(
(String) headerMap.get(Constants.EXPORT_PACKAGE), true);
+ // Get export 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]);
+//}
+
// Create non-duplicated export array.
dupeMap.clear();
for (int pkgIdx = 0; pkgIdx < m_exports.length; pkgIdx++)
@@ -174,6 +190,15 @@
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]);
+//}
+
// Create non-duplicated import array.
dupeMap.clear();
for (int pkgIdx = 0; pkgIdx < m_imports.length; pkgIdx++)
@@ -205,6 +230,15 @@
m_dynamics = (R4Import[]) parseImportExportHeader(
(String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE), false);
+ // Get import packages from bundle manifest.
+ IRequirement[] dynReqs = 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]);
+//}
+
// Dynamic imports can have duplicates, so just check for import
// of java.*.
List dynList = new ArrayList();
@@ -682,6 +716,183 @@
}
}
+ public static ICapability[] parseExportHeader(String header)
+ {
+ Object[][][] clauses = parseStandardHeader(header);
+
+// TODO: FRAMEWORK - Perhaps verification/normalization should be completely
+// separated from parsing, since verification/normalization may vary.
+
+ // If both version and specification-version attributes are specified,
+ // then verify that the values are equal.
+ 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))
+ {
+ // Convert version attribute to type Version.
+ attrMap.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+ v = (v == null) ? sv : v;
+ attrMap.put(Constants.VERSION_ATTRIBUTE,
+ new R4Attribute(
+ Constants.VERSION_ATTRIBUTE,
+ Version.parseVersion(v.getValue().toString()),
+ v.isMandatory()));
+
+ // Re-copy the attribute array since it has changed.
+ clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX] =
+ attrMap.values().toArray(new R4Attribute[attrMap.size()]);
+ }
+ }
+
+ // Now convert generic header clauses into capabilities.
+ List capList = new ArrayList();
+ for (int clauseIdx = 0; clauseIdx < clauses.length; clauseIdx++)
+ {
+ for (int pathIdx = 0;
+ pathIdx < clauses[clauseIdx][CLAUSE_PATHS_INDEX].length;
+ pathIdx++)
+ {
+ // Prepend the package name to the array of attributes.
+ R4Attribute[] attrs = (R4Attribute[]) clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX];
+ R4Attribute[] newAttrs = new R4Attribute[attrs.length + 1];
+ newAttrs[0] = new R4Attribute(
+ ICapability.PACKAGE_PROPERTY,
+ (String) clauses[clauseIdx][CLAUSE_PATHS_INDEX][pathIdx], false);
+ System.arraycopy(attrs, 0, newAttrs, 1, attrs.length);
+
+ // Create package capability and add to capability list.
+ capList.add(
+ new Capability(
+ ICapability.PACKAGE_NAMESPACE,
+ (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
+ newAttrs));
+ }
+ }
+
+ return (ICapability[]) capList.toArray(new ICapability[capList.size()]);
+ }
+
+ public static IRequirement[] parseImportHeader(String header)
+ {
+ 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;
+ 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 (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 requirements.
+ List reqList = new ArrayList();
+ for (int clauseIdx = 0; clauseIdx < clauses.length; clauseIdx++)
+ {
+ for (int pathIdx = 0;
+ pathIdx < clauses[clauseIdx][CLAUSE_PATHS_INDEX].length;
+ pathIdx++)
+ {
+ // Prepend the package name to the array of attributes.
+ R4Attribute[] attrs = (R4Attribute[]) clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX];
+ R4Attribute[] newAttrs = new R4Attribute[attrs.length + 1];
+ newAttrs[0] = new R4Attribute(
+ ICapability.PACKAGE_PROPERTY,
+ (String) clauses[clauseIdx][CLAUSE_PATHS_INDEX][pathIdx], false);
+ System.arraycopy(attrs, 0, newAttrs, 1, attrs.length);
+
+ // Create package requirement and add to requirement list.
+ reqList.add(
+ new Requirement(
+ ICapability.PACKAGE_NAMESPACE,
+ (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
+ newAttrs));
+ }
+ }
+
+ return (IRequirement[]) reqList.toArray(new IRequirement[reqList.size()]);
+ }
+
public static R4Package[] parseImportExportHeader(String header, boolean export)
{
Object[][][] clauses = parseStandardHeader(header);
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 740b2b9..5933c49 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
@@ -20,6 +20,7 @@
import org.apache.felix.framework.FilterImpl;
import org.apache.felix.framework.util.MapToDictionary;
+import org.apache.felix.framework.util.VersionRange;
import org.apache.felix.moduleloader.ICapability;
import org.apache.felix.moduleloader.IRequirement;
import org.osgi.framework.Filter;
@@ -28,12 +29,16 @@
public class Requirement implements IRequirement
{
private String m_namespace = null;
+ private R4Directive[] m_dirs = null;
+ private R4Attribute[] m_attrs = null;
private Filter m_filter = null;
- public Requirement(String namespace, String filterStr) throws InvalidSyntaxException
+ public Requirement(String namespace, R4Directive[] dirs, R4Attribute[] attrs)
{
m_namespace = namespace;
- m_filter = new FilterImpl(filterStr);
+ m_dirs = dirs;
+ m_attrs = attrs;
+ m_filter = convertToFilter();
}
public String getNamespace()
@@ -68,6 +73,83 @@
public String toString()
{
- return m_filter.toString();
+ return getFilter().toString();
+ }
+
+ private Filter convertToFilter()
+ {
+ String filterStr = null;
+
+ StringBuffer sb = new StringBuffer("(&");
+
+ for (int i = 0; (m_attrs != null) && (i < m_attrs.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(".*"))
+ {
+ int idx = m_attrs[i].getValue().toString().indexOf(".*");
+ sb.append("(|(package=");
+ sb.append(m_attrs[i].getValue().toString().substring(0, idx));
+ sb.append(")(package=");
+ sb.append(m_attrs[i].getValue().toString());
+ sb.append("))");
+ }
+ else if (m_attrs[i].getValue() instanceof VersionRange)
+ {
+ VersionRange vr = (VersionRange) m_attrs[i].getValue();
+ if (vr.isLowInclusive())
+ {
+ sb.append("(version>=");
+ sb.append(vr.getLow().toString());
+ sb.append(")");
+ }
+ else
+ {
+ sb.append("(!(version<=");
+ sb.append(vr.getLow().toString());
+ sb.append("))");
+ }
+
+ if (vr.getHigh() != null)
+ {
+ if (vr.isHighInclusive())
+ {
+ sb.append("(version<=");
+ sb.append(vr.getHigh().toString());
+ sb.append(")");
+ }
+ else
+ {
+ sb.append("(!(version>=");
+ sb.append(vr.getHigh().toString());
+ sb.append("))");
+ }
+ }
+ }
+ else
+ {
+ sb.append("(");
+ sb.append(m_attrs[i].getName());
+ sb.append("=");
+ sb.append(m_attrs[i].getValue().toString());
+ sb.append(")");
+ }
+ }
+
+ sb.append(")");
+
+ try
+ {
+ return new FilterImpl(sb.toString());
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ // This should never happen, so we can safely ignore.
+ }
+
+ return null;
}
}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java b/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java
index bc271d1..935cd7b 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java
@@ -30,5 +30,4 @@
String getNamespace();
Map getProperties();
- String[] getUses();
}
\ No newline at end of file