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, \