You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2009/08/25 22:30:34 UTC
svn commit: r807795 [4/5] - in /felix/sandbox/rickhall/resolver: ./ src/
src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/felix/ src/main/java/org/apache/felix/resolver/
src/main/java/org/apache/felix/resol...
Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java?rev=807795&view=auto
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java (added)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java Tue Aug 25 20:30:33 2009
@@ -0,0 +1,1223 @@
+/*
+ * 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
+ *
+ * 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.resolver.manifestparser;
+
+import org.apache.felix.resolver.VersionRange;
+import org.apache.felix.resolver.Version;
+import java.util.*;
+
+public class ManifestParser
+{
+ final static int EAGER_ACTIVATION = 0;
+ final static int LAZY_ACTIVATION = 1;
+
+ private final Map m_headerMap;
+ private volatile int m_activationPolicy = EAGER_ACTIVATION;
+ private volatile String m_activationIncludeDir;
+ private volatile String m_activationExcludeDir;
+ private volatile boolean m_isExtension = false;
+ private volatile String m_bundleSymbolicName;
+ private volatile Version m_bundleVersion;
+ private volatile Capability[] m_capabilities;
+ private volatile Requirement[] m_requirements;
+ private volatile Requirement[] m_dynamicRequirements;
+ private volatile R4LibraryClause[] m_libraryHeaders;
+ private volatile boolean m_libraryHeadersOptional = false;
+
+ public ManifestParser(Map headerMap)
+ throws Exception
+ {
+ m_headerMap = headerMap;
+
+ // Verify that only manifest version 2 is specified.
+ String manifestVersion = (String) m_headerMap.get(Constants.BUNDLE_MANIFESTVERSION);
+ manifestVersion = (manifestVersion == null) ? null : manifestVersion.trim();
+ if ((manifestVersion != null) && !manifestVersion.equals("2"))
+ {
+ throw new Exception(
+ "Unknown 'Bundle-ManifestVersion' value: " + manifestVersion);
+ }
+
+ // Create lists to hold capabilities and requirements.
+ List capList = new ArrayList();
+ List reqList = new ArrayList();
+
+ //
+ // Parse bundle version.
+ //
+
+ m_bundleVersion = Version.emptyVersion;
+ if (headerMap.get(Constants.BUNDLE_VERSION) != null)
+ {
+ try
+ {
+ m_bundleVersion = Version.parseVersion((String) headerMap.get(Constants.BUNDLE_VERSION));
+ }
+ catch (RuntimeException ex)
+ {
+ // R4 bundle versions must parse, R3 bundle version may not.
+ if (getManifestVersion().equals("2"))
+ {
+ throw ex;
+ }
+ m_bundleVersion = Version.emptyVersion;
+ }
+ }
+
+ //
+ // Parse bundle symbolic name.
+ //
+
+ Capability moduleCap = parseBundleSymbolicName(m_headerMap);
+ if (moduleCap != null)
+ {
+ m_bundleSymbolicName = (String)
+ moduleCap.getProperties().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+
+ // Add a module capability and a host capability to all
+ // non-fragment bundles. A host capability is the same
+ // as a module capability, but with a different capability
+ // namespace. Module capabilities resolve required-bundle
+ // dependencies, while host capabilities resolve fragment-host
+ // dependencies.
+ if (headerMap.get(Constants.FRAGMENT_HOST) == null)
+ {
+ capList.add(moduleCap);
+ capList.add(new Capability(
+ Capability.HOST_NAMESPACE, null,
+ ((Capability) moduleCap).getAttributes()));
+ }
+ }
+
+ //
+ // Parse Fragment-Host.
+ //
+ Object[][][] clauses = parseStandardHeader(
+ (String) headerMap.get(Constants.FRAGMENT_HOST));
+ if (clauses.length > 0)
+ {
+ // IGNORE FRAGMENT-HOST FOR NOW!
+ }
+
+ //
+ // Parse Export-Package.
+ //
+
+ // Get exported packages from bundle manifest.
+ Capability[] exportCaps = parseExportHeader(
+ (String) headerMap.get(Constants.EXPORT_PACKAGE));
+
+ // Verify that "java.*" packages are not exported.
+ for (int capIdx = 0; capIdx < exportCaps.length; capIdx++)
+ {
+ // Verify that the named package has not already been declared.
+ String pkgName = (String)
+ exportCaps[capIdx].getProperties().get(Capability.PACKAGE_PROPERTY);
+ // Verify that java.* packages are not exported.
+ if (pkgName.startsWith("java."))
+ {
+ throw new Exception(
+ "Exporting java.* packages not allowed: " + pkgName);
+ }
+ capList.add(exportCaps[capIdx]);
+ }
+
+ // Create an array of all capabilities.
+ m_capabilities = (Capability[]) capList.toArray(new Capability[capList.size()]);
+
+ //
+ // Parse Require-Bundle
+ //
+
+ Requirement[] bundleReq = parseRequireBundleHeader(
+ (String) headerMap.get(Constants.REQUIRE_BUNDLE));
+ for (int reqIdx = 0; reqIdx < bundleReq.length; reqIdx++)
+ {
+ reqList.add(bundleReq[reqIdx]);
+ }
+
+ //
+ // Parse Import-Package.
+ //
+
+ // Get import packages from bundle manifest.
+ Requirement[] importReqs = parseImportHeader(
+ (String) headerMap.get(Constants.IMPORT_PACKAGE));
+
+ // Verify there are no duplicate import declarations.
+ Set dupeSet = new HashSet();
+ for (int reqIdx = 0; reqIdx < importReqs.length; reqIdx++)
+ {
+ // Verify that the named package has not already been declared.
+ String pkgName = ((Requirement) importReqs[reqIdx]).getTargetName();
+ if (!dupeSet.contains(pkgName))
+ {
+ // Verify that java.* packages are not imported.
+ if (pkgName.startsWith("java."))
+ {
+ throw new Exception(
+ "Importing java.* packages not allowed: " + pkgName);
+ }
+ dupeSet.add(pkgName);
+ }
+ else
+ {
+ throw new Exception("Duplicate import - " + pkgName);
+ }
+ // If it has not already been imported, then add it to the list
+ // of requirements.
+ reqList.add(importReqs[reqIdx]);
+ }
+
+ // Create an array of all requirements.
+ m_requirements = (Requirement[]) reqList.toArray(new Requirement[reqList.size()]);
+
+ //
+ // Parse DynamicImport-Package.
+ //
+
+ // Get dynamic import packages from bundle manifest.
+ m_dynamicRequirements = parseImportHeader(
+ (String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
+
+ // Dynamic imports can have duplicates, so just check for import
+ // of java.*.
+ for (int reqIdx = 0; reqIdx < m_dynamicRequirements.length; reqIdx++)
+ {
+ // Verify that java.* packages are not imported.
+ String pkgName = ((Requirement) m_dynamicRequirements[reqIdx]).getTargetName();
+ if (pkgName.startsWith("java."))
+ {
+ throw new Exception(
+ "Dynamically importing java.* packages not allowed: " + pkgName);
+ }
+ else if (!pkgName.equals("*") && pkgName.endsWith("*") && !pkgName.endsWith(".*"))
+ {
+ throw new Exception(
+ "Partial package name wild carding is not allowed: " + pkgName);
+ }
+ }
+
+ //
+ // Parse Bundle-NativeCode.
+ //
+
+ // Get native library entry names for module library sources.
+ m_libraryHeaders =
+ parseLibraryStrings(
+ parseDelimitedString((String) m_headerMap.get(Constants.BUNDLE_NATIVECODE), ","));
+
+ // 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_libraryHeaders.length > 0) &&
+ (m_libraryHeaders[m_libraryHeaders.length - 1].getLibraryEntries() == null))
+ {
+ m_libraryHeadersOptional = true;
+ R4LibraryClause[] tmp = new R4LibraryClause[m_libraryHeaders.length - 1];
+ System.arraycopy(m_libraryHeaders, 0, tmp, 0, m_libraryHeaders.length - 1);
+ m_libraryHeaders = tmp;
+ }
+
+ //
+ // Parse activation policy.
+ //
+
+ // This sets m_activationPolicy, m_includedPolicyClasses, and
+ // m_excludedPolicyClasses.
+ parseActivationPolicy(headerMap);
+
+ // Do final checks and normalization of manifest.
+ if (getManifestVersion().equals("2"))
+ {
+ checkAndNormalizeR4();
+ }
+ else
+ {
+ checkAndNormalizeR3();
+ }
+ }
+
+ public String getManifestVersion()
+ {
+ String manifestVersion = (String) m_headerMap.get(Constants.BUNDLE_MANIFESTVERSION);
+ return (manifestVersion == null) ? "1" : manifestVersion.trim();
+ }
+
+ public int getActivationPolicy()
+ {
+ return m_activationPolicy;
+ }
+
+ public String getActivationIncludeDirective()
+ {
+ return m_activationIncludeDir;
+ }
+
+ public String getActivationExcludeDirective()
+ {
+ return m_activationExcludeDir;
+ }
+
+ public boolean isExtension()
+ {
+ return m_isExtension;
+ }
+
+ public String getSymbolicName()
+ {
+ return m_bundleSymbolicName;
+ }
+
+ public Version getBundleVersion()
+ {
+ return m_bundleVersion;
+ }
+
+ public Capability[] getCapabilities()
+ {
+ return m_capabilities;
+ }
+
+ public Requirement[] getRequirements()
+ {
+ return m_requirements;
+ }
+
+ public Requirement[] getDynamicRequirements()
+ {
+ return m_dynamicRequirements;
+ }
+
+ public R4LibraryClause[] getLibraryClauses()
+ {
+ return m_libraryHeaders;
+ }
+
+ private String getName(String path)
+ {
+ int idx = path.lastIndexOf('/');
+ if (idx > -1)
+ {
+ return path.substring(idx);
+ }
+ return path;
+ }
+
+ private void checkAndNormalizeR3() throws Exception
+ {
+ // 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 capIdx = 0;
+ (m_capabilities != null) && (capIdx < m_capabilities.length);
+ capIdx++)
+ {
+ if (m_capabilities[capIdx].getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ // R3 bundles cannot have directives on their exports.
+ if (((Capability) m_capabilities[capIdx]).getDirectives().length != 0)
+ {
+ throw new Exception("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 (((Capability) m_capabilities[capIdx]).getAttributes() != null)
+ {
+ // R3 package capabilities should only have name and
+ // version attributes.
+ R4Attribute pkgName = null;
+ R4Attribute pkgVersion = new R4Attribute(Capability.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(Capability.PACKAGE_PROPERTY))
+ {
+ pkgName = ((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx];
+ }
+ else if (((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx]
+ .getName().equals(Capability.VERSION_PROPERTY))
+ {
+ pkgVersion = ((Capability) m_capabilities[capIdx]).getAttributes()[attrIdx];
+ }
+ else
+ {
+ System.err.println(
+ "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(
+ Capability.PACKAGE_NAMESPACE,
+ null,
+ new R4Attribute[] { pkgName, pkgVersion } );
+ }
+ }
+ }
+
+ // 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 reqIdx = 0; (m_requirements != null) && (reqIdx < m_requirements.length); reqIdx++)
+ {
+ if (m_requirements[reqIdx].getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ // R3 bundles cannot have directives on their imports.
+ if (((Requirement) m_requirements[reqIdx]).getDirectives().length != 0)
+ {
+ throw new Exception("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 (((Requirement) m_requirements[reqIdx]).getAttributes() != null)
+ {
+ // R3 package requirements should only have name and
+ // version attributes.
+ R4Attribute pkgName = null;
+ R4Attribute pkgVersion =
+ new R4Attribute(Capability.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(Capability.PACKAGE_PROPERTY))
+ {
+ pkgName = ((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx];
+ }
+ else if (((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx]
+ .getName().equals(Capability.VERSION_PROPERTY))
+ {
+ pkgVersion = ((Requirement) m_requirements[reqIdx]).getAttributes()[attrIdx];
+ }
+ else
+ {
+ System.out.println(
+ "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(
+ Capability.PACKAGE_NAMESPACE,
+ null,
+ new R4Attribute[] { pkgName, pkgVersion });
+ }
+ }
+ }
+
+ // Since all R3 exports imply an import, add a corresponding
+ // requirement for each existing export capability. Do not
+ // duplicate imports.
+ Map map = new HashMap();
+ // Add existing imports.
+ for (int i = 0; i < m_requirements.length; i++)
+ {
+ if (m_requirements[i].getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ map.put(
+ ((Requirement) m_requirements[i]).getTargetName(),
+ m_requirements[i]);
+ }
+ }
+ // Add import requirement for each export capability.
+ for (int i = 0; i < m_capabilities.length; i++)
+ {
+ if (m_capabilities[i].getNamespace().equals(Capability.PACKAGE_NAMESPACE) &&
+ (map.get(m_capabilities[i].getProperties().get(Capability.PACKAGE_PROPERTY)) == null))
+ {
+ // Convert Version to VersionRange.
+ 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))
+ {
+ attrs[attrIdx] = new R4Attribute(
+ attrs[attrIdx].getName(),
+ VersionRange.parse(attrs[attrIdx].getValue().toString()),
+ attrs[attrIdx].isMandatory());
+ }
+ }
+
+ map.put(
+ m_capabilities[i].getProperties().get(Capability.PACKAGE_PROPERTY),
+ new Requirement(Capability.PACKAGE_NAMESPACE, null, attrs));
+ }
+ }
+ m_requirements =
+ (Requirement[]) map.values().toArray(new Requirement[map.size()]);
+
+ // Add a "uses" directive onto each export of R3 bundles
+ // that references every other import (which will include
+ // exports, since export implies import); this is
+ // necessary since R3 bundles assumed a single class space,
+ // but R4 allows for multiple class spaces.
+ String usesValue = "";
+ for (int i = 0; (m_requirements != null) && (i < m_requirements.length); i++)
+ {
+ if (m_requirements[i].getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ usesValue = usesValue
+ + ((usesValue.length() > 0) ? "," : "")
+ + ((Requirement) m_requirements[i]).getTargetName();
+ }
+ }
+ R4Directive uses = new R4Directive(
+ Constants.USES_DIRECTIVE, usesValue);
+ for (int i = 0; (m_capabilities != null) && (i < m_capabilities.length); i++)
+ {
+ if (m_capabilities[i].getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_capabilities[i] = new Capability(
+ Capability.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_dynamicRequirements != null) && (i < m_dynamicRequirements.length);
+ i++)
+ {
+ if (((Requirement) m_dynamicRequirements[i]).getDirectives().length != 0)
+ {
+ throw new Exception("R3 dynamic imports cannot contain directives.");
+ }
+ if (((Requirement) m_dynamicRequirements[i]).getAttributes().length != 0)
+ {
+// throw new BundleException("R3 dynamic imports cannot contain attributes.");
+ }
+ }
+ }
+
+ private void checkAndNormalizeR4() throws Exception
+ {
+ // Verify that bundle symbolic name is specified.
+ if (m_bundleSymbolicName == null)
+ {
+ throw new Exception("R4 bundle manifests must include bundle symbolic name.");
+ }
+
+ m_capabilities = checkAndNormalizeR4Exports(
+ m_capabilities, m_bundleSymbolicName, m_bundleVersion);
+
+ R4Directive extension = parseExtensionBundleHeader((String)
+ m_headerMap.get(Constants.FRAGMENT_HOST));
+
+ if (extension != null)
+ {
+ if (!(Constants.EXTENSION_FRAMEWORK.equals(extension.getValue()) ||
+ Constants.EXTENSION_BOOTCLASSPATH.equals(extension.getValue())))
+ {
+ throw new Exception(
+ "Extension bundle must have either 'extension:=framework' or 'extension:=bootclasspath'");
+ }
+ checkExtensionBundle();
+ m_isExtension = true;
+ }
+ }
+
+ private static Capability[] checkAndNormalizeR4Exports(
+ Capability[] caps, String bsn, Version bv)
+ throws Exception
+ {
+ // Verify that the exports do not specify bundle symbolic name
+ // or bundle version.
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ R4Attribute[] attrs = ((Capability) caps[i]).getAttributes();
+ for (int attrIdx = 0; attrIdx < attrs.length; attrIdx++)
+ {
+ // 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 Exception(
+ "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, bsn, false);
+ newAttrs[attrs.length + 1] = new R4Attribute(
+ Constants.BUNDLE_VERSION_ATTRIBUTE, bv, false);
+ caps[i] = new Capability(
+ Capability.PACKAGE_NAMESPACE,
+ ((Capability) caps[i]).getDirectives(),
+ newAttrs);
+ }
+ }
+
+ return caps;
+ }
+
+ private void checkExtensionBundle() throws Exception
+ {
+ if (m_headerMap.containsKey(Constants.IMPORT_PACKAGE) ||
+ m_headerMap.containsKey(Constants.REQUIRE_BUNDLE) ||
+ m_headerMap.containsKey(Constants.BUNDLE_NATIVECODE) ||
+ m_headerMap.containsKey(Constants.DYNAMICIMPORT_PACKAGE) ||
+ m_headerMap.containsKey(Constants.BUNDLE_ACTIVATOR))
+ {
+ throw new Exception("Invalid extension bundle manifest");
+ }
+ }
+
+ private static Capability parseBundleSymbolicName(Map headerMap)
+ throws Exception
+ {
+ Object[][][] clauses = parseStandardHeader(
+ (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ if (clauses.length > 0)
+ {
+ if (clauses.length > 1)
+ {
+ throw new Exception(
+ "Cannot have multiple symbolic names: "
+ + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ }
+ else if (clauses[0][CLAUSE_PATHS_INDEX].length > 1)
+ {
+ throw new Exception(
+ "Cannot have multiple symbolic names: "
+ + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ }
+
+ // Get bundle version.
+ Version bundleVersion = Version.emptyVersion;
+ if (headerMap.get(Constants.BUNDLE_VERSION) != null)
+ {
+ try
+ {
+ bundleVersion = Version.parseVersion((String) headerMap.get(Constants.BUNDLE_VERSION));
+ }
+ catch (RuntimeException ex)
+ {
+ // R4 bundle versions must parse, R3 bundle version may not.
+ if (((String) headerMap.get(Constants.BUNDLE_MANIFESTVERSION)).equals("2"))
+ {
+ throw ex;
+ }
+ bundleVersion = Version.emptyVersion;
+ }
+ }
+
+ // Create a module capability and return it.
+ String symName = (String) clauses[0][CLAUSE_PATHS_INDEX][0];
+ R4Attribute[] attrs = new R4Attribute[2];
+ attrs[0] = new R4Attribute(
+ Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symName, false);
+ attrs[1] = new R4Attribute(
+ Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion, false);
+ return new Capability(Capability.MODULE_NAMESPACE, (R4Directive[]) clauses[0][CLAUSE_DIRECTIVES_INDEX], attrs);
+ }
+
+ return null;
+ }
+
+ public static Capability[] parseExportHeader(String header, String bsn, Version bv)
+ throws Exception
+ {
+ Capability[] caps = parseExportHeader(header);
+ try
+ {
+ caps = checkAndNormalizeR4Exports(caps, bsn, bv);
+ }
+ catch (Exception ex)
+ {
+ caps = null;
+ }
+ return caps;
+ }
+
+ private static Capability[] 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.");
+ }
+ }
+
+ // Always add the default version if not specified.
+ if ((v == null) && (sv == null))
+ {
+ v = new R4Attribute(
+ Constants.VERSION_ATTRIBUTE, Version.emptyVersion, false);
+ }
+
+ // 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++)
+ {
+ // Make sure a package name was specified.
+ if (((String) clauses[clauseIdx][CLAUSE_PATHS_INDEX][pathIdx]).length() == 0)
+ {
+ throw new IllegalArgumentException(
+ "An empty package name was specified: " + header);
+ }
+ // 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(
+ Capability.PACKAGE_PROPERTY,
+ 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(
+ Capability.PACKAGE_NAMESPACE,
+ (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
+ newAttrs));
+ }
+ }
+
+ return (Capability[]) capList.toArray(new Capability[capList.size()]);
+ }
+
+ private static Requirement[] 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 VersionRange 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 VersionRange.
+ 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++)
+ {
+ // Make sure a package name was specified.
+ if (((String) clauses[clauseIdx][CLAUSE_PATHS_INDEX][pathIdx]).length() == 0)
+ {
+ throw new IllegalArgumentException(
+ "An empty package name was specified: " + header);
+ }
+ // 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(
+ Capability.PACKAGE_PROPERTY,
+ 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(
+ Capability.PACKAGE_NAMESPACE,
+ (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
+ newAttrs));
+ }
+ }
+
+ return (Requirement[]) reqList.toArray(new Requirement[reqList.size()]);
+ }
+
+ private static Requirement[] parseRequireBundleHeader(String header)
+ {
+ Object[][][] clauses = parseStandardHeader(header);
+
+// TODO: FRAMEWORK - Perhaps verification/normalization should be completely
+// separated from parsing, since verification/normalization may vary.
+
+ // Convert bundle version attribute to VersionRange type.
+ for (int clauseIdx = 0; clauseIdx < clauses.length; clauseIdx++)
+ {
+ for (int attrIdx = 0;
+ attrIdx < clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX].length;
+ attrIdx++)
+ {
+ R4Attribute attr = (R4Attribute) clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX][attrIdx];
+ if (attr.getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
+ {
+ clauses[clauseIdx][CLAUSE_ATTRIBUTES_INDEX][attrIdx] =
+ new R4Attribute(
+ Constants.BUNDLE_VERSION_ATTRIBUTE,
+ VersionRange.parse(attr.getValue().toString()),
+ attr.isMandatory());
+ }
+ }
+ }
+
+ // 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 symbolic 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(
+ Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE,
+ 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(
+ Capability.MODULE_NAMESPACE,
+ (R4Directive[]) clauses[clauseIdx][CLAUSE_DIRECTIVES_INDEX],
+ newAttrs));
+ }
+ }
+
+ return (Requirement[]) reqList.toArray(new Requirement[reqList.size()]);
+ }
+
+ public static R4Directive parseExtensionBundleHeader(String header)
+ throws Exception
+ {
+ Object[][][] clauses = parseStandardHeader(header);
+
+ R4Directive result = null;
+
+ if (clauses.length == 1)
+ {
+ // See if there is the "extension" directive.
+ for (int i = 0;
+ (result == null) && (i < clauses[0][CLAUSE_DIRECTIVES_INDEX].length);
+ i++)
+ {
+ if (Constants.EXTENSION_DIRECTIVE.equals(((R4Directive)
+ clauses[0][CLAUSE_DIRECTIVES_INDEX][i]).getName()))
+ {
+ // If the extension directive is specified, make sure
+ // the target is the system bundle.
+ if ("org.apache.felix.framework".equals(clauses[0][CLAUSE_PATHS_INDEX][0]) ||
+ Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(clauses[0][CLAUSE_PATHS_INDEX][0]))
+ {
+ result = (R4Directive) clauses[0][CLAUSE_DIRECTIVES_INDEX][i];
+ }
+ else
+ {
+ throw new Exception(
+ "Only the system bundle can have extension bundles.");
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private void parseActivationPolicy(Map headerMap)
+ {
+ m_activationPolicy = EAGER_ACTIVATION;
+
+ Object[][][] clauses = parseStandardHeader(
+ (String) headerMap.get(Constants.BUNDLE_ACTIVATIONPOLICY));
+
+ if (clauses.length > 0)
+ {
+ // Just look for a "path" matching the lazy policy, ignore
+ // everything else.
+ for (int i = 0;
+ i < clauses[0][CLAUSE_PATHS_INDEX].length;
+ i++)
+ {
+ if (clauses[0][CLAUSE_PATHS_INDEX][i].equals(Constants.ACTIVATION_LAZY))
+ {
+ m_activationPolicy = LAZY_ACTIVATION;
+ for (int j = 0; j < clauses[0][CLAUSE_DIRECTIVES_INDEX].length; j++)
+ {
+ R4Directive dir = (R4Directive) clauses[0][CLAUSE_DIRECTIVES_INDEX][j];
+ if (dir.getName().equalsIgnoreCase(Constants.INCLUDE_DIRECTIVE))
+ {
+ m_activationIncludeDir = dir.getValue();
+ }
+ else if (dir.getName().equalsIgnoreCase(Constants.EXCLUDE_DIRECTIVE))
+ {
+ m_activationExcludeDir = dir.getValue();
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ 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 Object[][][] parseStandardHeader(String header)
+ {
+ Object[][][] clauses = null;
+
+ if (header != null)
+ {
+ if (header.length() == 0)
+ {
+ throw new IllegalArgumentException(
+ "A header cannot be an empty string.");
+ }
+
+ String[] clauseStrings = parseDelimitedString(
+ header, ",");
+
+ List completeList = new ArrayList();
+ for (int i = 0; (clauseStrings != null) && (i < clauseStrings.length); i++)
+ {
+ completeList.add(parseStandardHeaderClause(clauseStrings[i]));
+ }
+ clauses = (Object[][][]) completeList.toArray(new Object[completeList.size()][][]);
+ }
+
+ return (clauses == null) ? new Object[0][][] : clauses;
+ }
+
+ // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
+ private static Object[][] parseStandardHeaderClause(String clauseString)
+ throws IllegalArgumentException
+ {
+ // Break string into semi-colon delimited pieces.
+ String[] pieces = parseDelimitedString(
+ clauseString, ";");
+
+ // 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.length; pieceIdx++)
+ {
+ if (pieces[pieceIdx].indexOf('=') >= 0)
+ {
+ break;
+ }
+ pathCount++;
+ }
+
+ // Error if no paths were specified.
+ if (pathCount == 0)
+ {
+ throw new IllegalArgumentException(
+ "No paths specified in header: " + clauseString);
+ }
+
+ // Create an array of paths.
+ String[] paths = new String[pathCount];
+ System.arraycopy(pieces, 0, paths, 0, pathCount);
+
+ // Parse the directives/attributes.
+ Map dirsMap = new HashMap();
+ Map attrsMap = new HashMap();
+ int idx = -1;
+ String sep = null;
+ for (int pieceIdx = pathCount; pieceIdx < pieces.length; pieceIdx++)
+ {
+ // Check if it is a directive.
+ if ((idx = pieces[pieceIdx].indexOf(":=")) >= 0)
+ {
+ sep = ":=";
+ }
+ // Check if it is an attribute.
+ else if ((idx = pieces[pieceIdx].indexOf("=")) >= 0)
+ {
+ sep = "=";
+ }
+ // It is an error.
+ else
+ {
+ throw new IllegalArgumentException("Not a directive/attribute: " + clauseString);
+ }
+
+ String key = pieces[pieceIdx].substring(0, idx).trim();
+ String value = pieces[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(":="))
+ {
+ // Check for duplicates.
+ if (dirsMap.get(key) != null)
+ {
+ throw new IllegalArgumentException(
+ "Duplicate directive: " + key);
+ }
+ dirsMap.put(key, new R4Directive(key, value));
+ }
+ else
+ {
+ // Check for duplicates.
+ if (attrsMap.get(key) != null)
+ {
+ throw new IllegalArgumentException(
+ "Duplicate attribute: " + key);
+ }
+ attrsMap.put(key, new R4Attribute(key, value, false));
+ }
+ }
+
+ // Create directive array.
+ R4Directive[] dirs = (R4Directive[])
+ dirsMap.values().toArray(new R4Directive[dirsMap.size()]);
+
+ // Create attribute array.
+ R4Attribute[] attrs = (R4Attribute[])
+ attrsMap.values().toArray(new R4Attribute[attrsMap.size()]);
+
+ // Create an array to hold the parsed paths, directives, and attributes.
+ Object[][] clause = new Object[3][];
+ clause[CLAUSE_PATHS_INDEX] = paths;
+ clause[CLAUSE_DIRECTIVES_INDEX] = dirs;
+ clause[CLAUSE_ATTRIBUTES_INDEX] = attrs;
+
+ return clause;
+ }
+
+ /**
+ * Parses delimited string and returns an array containing the tokens. This
+ * parser obeys quotes, so the delimiter character will be ignored if it is
+ * inside of a quote. This method assumes that the quote character is not
+ * included in the set of delimiter characters.
+ * @param value the delimited string to parse.
+ * @param delim the characters delimiting the tokens.
+ * @return an array of string tokens or null if there were no tokens.
+ **/
+ public static String[] parseDelimitedString(String value, String delim)
+ {
+ if (value == null)
+ {
+ value = "";
+ }
+
+ List list = new ArrayList();
+
+ int CHAR = 1;
+ int DELIMITER = 2;
+ int STARTQUOTE = 4;
+ int ENDQUOTE = 8;
+
+ StringBuffer sb = new StringBuffer();
+
+ int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+ 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))
+ {
+ list.add(sb.toString().trim());
+ sb.delete(0, sb.length());
+ expecting = (CHAR | DELIMITER | STARTQUOTE);
+ }
+ else if (isQuote && ((expecting & STARTQUOTE) > 0))
+ {
+ sb.append(c);
+ expecting = CHAR | ENDQUOTE;
+ }
+ else if (isQuote && ((expecting & ENDQUOTE) > 0))
+ {
+ sb.append(c);
+ expecting = (CHAR | STARTQUOTE | DELIMITER);
+ }
+ else if ((expecting & CHAR) > 0)
+ {
+ sb.append(c);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid delimited string: " + value);
+ }
+ }
+
+ if (sb.length() > 0)
+ {
+ list.add(sb.toString().trim());
+ }
+
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Parses native code manifest headers.
+ * @param libStrs an array of native library manifest header
+ * strings from the bundle manifest.
+ * @return an array of <tt>LibraryInfo</tt> objects for the
+ * passed in strings.
+ **/
+ private static R4LibraryClause[] parseLibraryStrings(String[] libStrs)
+ throws IllegalArgumentException
+ {
+ if (libStrs == null)
+ {
+ return new R4LibraryClause[0];
+ }
+
+ List libList = new ArrayList();
+
+ for (int i = 0; i < libStrs.length; i++)
+ {
+ R4LibraryClause clause = R4LibraryClause.parse(libStrs[i]);
+ libList.add(clause);
+ }
+
+ return (R4LibraryClause[]) libList.toArray(new R4LibraryClause[libList.size()]);
+ }
+}
Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Attribute.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Attribute.java?rev=807795&view=auto
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Attribute.java (added)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Attribute.java Tue Aug 25 20:30:33 2009
@@ -0,0 +1,48 @@
+/*
+ * 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
+ *
+ * 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.resolver.manifestparser;
+
+public class R4Attribute
+{
+ private final String m_name;
+ private final Object m_value;
+ private final boolean m_isMandatory;
+
+ public R4Attribute(String name, Object value, boolean isMandatory)
+ {
+ m_name = name;
+ m_value = value;
+ m_isMandatory = isMandatory;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public Object getValue()
+ {
+ return m_value;
+ }
+
+ public boolean isMandatory()
+ {
+ return m_isMandatory;
+ }
+}
\ No newline at end of file
Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Directive.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Directive.java?rev=807795&view=auto
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Directive.java (added)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Directive.java Tue Aug 25 20:30:33 2009
@@ -0,0 +1,41 @@
+/*
+ * 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
+ *
+ * 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.resolver.manifestparser;
+
+public class R4Directive
+{
+ private final String m_name;
+ private final String m_value;
+
+ public R4Directive(String name, String value)
+ {
+ m_name = name;
+ m_value = value;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public String getValue()
+ {
+ return m_value;
+ }
+}
\ No newline at end of file
Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Library.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Library.java?rev=807795&view=auto
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Library.java (added)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4Library.java Tue Aug 25 20:30:33 2009
@@ -0,0 +1,168 @@
+/*
+ * 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
+ *
+ * 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.resolver.manifestparser;
+
+import java.util.Map;
+
+public class R4Library
+{
+ private String m_libraryFile;
+ private String[] m_osnames;
+ private String[] m_processors;
+ private String[] m_osversions;
+ private String[] m_languages;
+ private String m_selectionFilter;
+
+ public R4Library(
+ String libraryFile, String[] osnames, String[] processors, String[] osversions,
+ String[] languages, String selectionFilter) throws Exception
+ {
+ m_libraryFile = libraryFile;
+ m_osnames = osnames;
+ m_processors = processors;
+ m_osversions = osversions;
+ m_languages = languages;
+ m_selectionFilter = selectionFilter;
+ }
+
+ public String getEntryName()
+ {
+ return m_libraryFile;
+ }
+
+ public String[] getOSNames()
+ {
+ return m_osnames;
+ }
+
+ public String[] getProcessors()
+ {
+ return m_processors;
+ }
+
+ public String[] getOSVersions()
+ {
+ return m_osversions;
+ }
+
+ public String[] getLanguages()
+ {
+ return m_languages;
+ }
+
+ public String getSelectionFilter()
+ {
+ return m_selectionFilter;
+ }
+
+ /**
+ * <p>
+ * Determines if the specified native library name matches this native
+ * library definition.
+ * </p>
+ * @param name the native library name to try to match.
+ * @return <tt>true</tt> if this native library name matches this native
+ * library definition; <tt>false</tt> otherwise.
+ **/
+ public boolean match(Map configMap, String name)
+ {
+ String libname = System.mapLibraryName(name);
+ String[] exts = ManifestParser.parseDelimitedString(
+ (String) configMap.get(Constants.FRAMEWORK_LIBRARY_EXTENSIONS), ",");
+ int extIdx = 0;
+
+ // First try to match the default name, then try to match any additionally
+ // specified library extensions.
+ do
+ {
+ if (m_libraryFile.equals(libname) || m_libraryFile.endsWith("/" + libname))
+ {
+ return true;
+ }
+ else if (libname.endsWith(".jnilib") && m_libraryFile.endsWith(".dylib"))
+ {
+ libname = libname.substring(0, libname.length() - 6) + "dylib";
+ if (m_libraryFile.equals(libname) || m_libraryFile.endsWith("/" + libname))
+ {
+ return true;
+ }
+ }
+ else if (m_libraryFile.equals(name) || m_libraryFile.endsWith("/" + name))
+ {
+ return true;
+ }
+
+ int idx = libname.lastIndexOf(".");
+ libname = (idx < 0)
+ ? libname + "." + exts[extIdx++]
+ : libname.substring(0, idx) + "." + exts[extIdx++];
+ }
+ while ((exts != null) && (extIdx < exts.length));
+
+ return false;
+ }
+
+ public String toString()
+ {
+ if (m_libraryFile != null)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(m_libraryFile);
+ for (int i = 0; (m_osnames != null) && (i < m_osnames.length); i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_OSNAME);
+ sb.append('=');
+ sb.append(m_osnames[i]);
+ }
+ for (int i = 0; (m_processors != null) && (i < m_processors.length); i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_PROCESSOR);
+ sb.append('=');
+ sb.append(m_processors[i]);
+ }
+ for (int i = 0; (m_osversions != null) && (i < m_osversions.length); i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_OSVERSION);
+ sb.append('=');
+ sb.append(m_osversions[i]);
+ }
+ for (int i = 0; (m_languages != null) && (i < m_languages.length); i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_LANGUAGE);
+ sb.append('=');
+ sb.append(m_languages[i]);
+ }
+ if (m_selectionFilter != null)
+ {
+ sb.append(';');
+ sb.append(Constants.SELECTION_FILTER_ATTRIBUTE);
+ sb.append('=');
+ sb.append('\'');
+ sb.append(m_selectionFilter);
+ }
+
+ return sb.toString();
+ }
+ return "*";
+ }
+}
\ No newline at end of file
Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4LibraryClause.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4LibraryClause.java?rev=807795&view=auto
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4LibraryClause.java (added)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/R4LibraryClause.java Tue Aug 25 20:30:33 2009
@@ -0,0 +1,490 @@
+/*
+ * 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
+ *
+ * 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.resolver.manifestparser;
+
+import org.apache.felix.resolver.VersionRange;
+import org.apache.felix.resolver.Version;
+import java.util.*;
+
+public class R4LibraryClause
+{
+ private final String[] m_libraryEntries;
+ private final String[] m_osnames;
+ private final String[] m_processors;
+ private final String[] m_osversions;
+ private final String[] m_languages;
+ private final String m_selectionFilter;
+
+ public R4LibraryClause(String[] libraryEntries, String[] osnames,
+ String[] processors, String[] osversions, String[] languages,
+ String selectionFilter)
+ {
+ m_libraryEntries = libraryEntries;
+ m_osnames = osnames;
+ m_processors = processors;
+ m_osversions = osversions;
+ m_languages = languages;
+ m_selectionFilter = selectionFilter;
+ }
+
+ public R4LibraryClause(R4LibraryClause library)
+ {
+ m_libraryEntries = library.m_libraryEntries;
+ m_osnames = library.m_osnames;
+ m_osversions = library.m_osversions;
+ m_processors = library.m_processors;
+ m_languages = library.m_languages;
+ m_selectionFilter = library.m_selectionFilter;
+ }
+
+ public String[] getLibraryEntries()
+ {
+ return m_libraryEntries;
+ }
+
+ public String[] getOSNames()
+ {
+ return m_osnames;
+ }
+
+ public String[] getProcessors()
+ {
+ return m_processors;
+ }
+
+ public String[] getOSVersions()
+ {
+ return m_osversions;
+ }
+
+ public String[] getLanguages()
+ {
+ return m_languages;
+ }
+
+ public String getSelectionFilter()
+ {
+ return m_selectionFilter;
+ }
+
+ public boolean match(Map configMap) throws Exception
+ {
+ String normal_osname = normalizeOSName((String) configMap.get(Constants.FRAMEWORK_OS_NAME));
+ String normal_processor = normalizeProcessor((String) configMap.get(Constants.FRAMEWORK_PROCESSOR));
+ String normal_osversion = normalizeOSVersion((String) configMap.get(Constants.FRAMEWORK_OS_VERSION));
+ String normal_language = (String) configMap.get(Constants.FRAMEWORK_LANGUAGE);
+
+ // Check library's osname.
+ if (!checkOSNames(normal_osname, getOSNames()))
+ {
+ return false;
+ }
+
+ // Check library's processor.
+ if (!checkProcessors(normal_processor, getProcessors()))
+ {
+ return false;
+ }
+
+ // Check library's osversion if specified.
+ if ((getOSVersions() != null) &&
+ (getOSVersions().length > 0) &&
+ !checkOSVersions(normal_osversion, getOSVersions()))
+ {
+ return false;
+ }
+
+ // Check library's language if specified.
+ if ((getLanguages() != null) &&
+ (getLanguages().length > 0) &&
+ !checkLanguages(normal_language, getLanguages()))
+ {
+ return false;
+ }
+
+ // Check library's selection-filter if specified.
+ if ((getSelectionFilter() != null) &&
+ (getSelectionFilter().length() >= 0) &&
+ !checkSelectionFilter(configMap, getSelectionFilter()))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean checkOSNames(String currentOSName, String[] osnames)
+ {
+ boolean win32 = currentOSName.startsWith("win") &&
+ (currentOSName.equals("windows95") ||
+ currentOSName.equals("windows98") ||
+ currentOSName.equals("windowsnt") ||
+ currentOSName.equals("windows2000") ||
+ currentOSName.equals("windowsxp") ||
+ currentOSName.equals("windowsce") ||
+ currentOSName.equals("windowsvista"));
+
+ for (int i = 0; (osnames != null) && (i < osnames.length); i++)
+ {
+ if (osnames[i].equals(currentOSName) ||
+ ("win32".equals(osnames[i]) && win32))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkProcessors(String currentProcessor, String[] processors)
+ {
+ for (int i = 0; (processors != null) && (i < processors.length); i++)
+ {
+ if (processors[i].equals(currentProcessor))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkOSVersions(String currentOSVersion, String[] osversions)
+ throws Exception
+ {
+ for (int i = 0; (osversions != null) && (i < osversions.length); i++)
+ {
+ try
+ {
+ VersionRange range = VersionRange.parse(osversions[i]);
+ if (range.isInRange(new Version(currentOSVersion)))
+ {
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new Exception(
+ "Error evaluating osversion: " + osversions[i], ex);
+ }
+ }
+ return false;
+ }
+
+ private boolean checkLanguages(String currentLanguage, String[] languages)
+ {
+ for (int i = 0; (languages != null) && (i < languages.length); i++)
+ {
+ if (languages[i].equals(currentLanguage))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkSelectionFilter(Map configMap, String expr)
+ throws Exception
+ {
+ return false;
+ }
+
+ public static R4LibraryClause parse(String s)
+ {
+ try
+ {
+ if ((s == null) || (s.length() == 0))
+ {
+ return null;
+ }
+
+ if (s.equals("*"))
+ {
+ return new R4LibraryClause(null, null, null, null, null, null);
+ }
+
+ // The tokens are separated by semicolons and may include
+ // any number of libraries along with one set of associated
+ // properties.
+ StringTokenizer st = new StringTokenizer(s, ";");
+ String[] libEntries = new String[st.countTokens()];
+ List osNameList = new ArrayList();
+ List osVersionList = new ArrayList();
+ List processorList = new ArrayList();
+ List languageList = new ArrayList();
+ String selectionFilter = null;
+ int libCount = 0;
+ while (st.hasMoreTokens())
+ {
+ String token = st.nextToken().trim();
+ if (token.indexOf('=') < 0)
+ {
+ // Remove the slash, if necessary.
+ libEntries[libCount] = (token.charAt(0) == '/')
+ ? token.substring(1)
+ : token;
+ libCount++;
+ }
+ else
+ {
+ // Check for valid native library properties; defined as
+ // a property name, an equal sign, and a value.
+ // NOTE: StringTokenizer can not be used here because
+ // a value can contain one or more "=" too, e.g.,
+ // selection-filter="(org.osgi.framework.windowing.system=gtk)"
+ String property = null;
+ String value = null;
+ if (!(token.indexOf("=") > 1))
+ {
+ throw new IllegalArgumentException(
+ "Bundle manifest native library entry malformed: " + token);
+ }
+ else
+ {
+ property = (token.substring(0, token.indexOf("=")))
+ .trim().toLowerCase();
+ value = (token.substring(token.indexOf("=") + 1, token
+ .length())).trim();
+ }
+
+ // Values may be quoted, so remove quotes if present.
+ if (value.charAt(0) == '"')
+ {
+ // This should always be true, otherwise the
+ // value wouldn't be properly quoted, but we
+ // will check for safety.
+ if (value.charAt(value.length() - 1) == '"')
+ {
+ value = value.substring(1, value.length() - 1);
+ }
+ else
+ {
+ value = value.substring(1);
+ }
+ }
+ // Add the value to its corresponding property list.
+ if (property.equals(Constants.BUNDLE_NATIVECODE_OSNAME))
+ {
+ osNameList.add(normalizeOSName(value));
+ }
+ else if (property.equals(Constants.BUNDLE_NATIVECODE_OSVERSION))
+ {
+ osVersionList.add(normalizeOSVersion(value));
+ }
+ else if (property.equals(Constants.BUNDLE_NATIVECODE_PROCESSOR))
+ {
+ processorList.add(normalizeProcessor(value));
+ }
+ else if (property.equals(Constants.BUNDLE_NATIVECODE_LANGUAGE))
+ {
+ languageList.add(value);
+ }
+ else if (property.equals(Constants.SELECTION_FILTER_ATTRIBUTE))
+ {
+// TODO: NATIVE - I believe we can have multiple selection filters too.
+ selectionFilter = value;
+ }
+ }
+ }
+
+ if (libCount == 0)
+ {
+ return null;
+ }
+
+ // Shrink lib file array.
+ String[] actualLibEntries = new String[libCount];
+ System.arraycopy(libEntries, 0, actualLibEntries, 0, libCount);
+ return new R4LibraryClause(
+ actualLibEntries,
+ (String[]) osNameList.toArray(new String[osNameList.size()]),
+ (String[]) processorList.toArray(new String[processorList.size()]),
+ (String[]) osVersionList.toArray(new String[osVersionList.size()]),
+ (String[]) languageList.toArray(new String[languageList.size()]),
+ selectionFilter);
+ }
+ catch (RuntimeException ex)
+ {
+ System.err.println("Error parsing native library header: " + ex);
+ throw ex;
+ }
+ }
+
+ public static String normalizeOSName(String value)
+ {
+ value = value.toLowerCase();
+
+ if (value.startsWith("win"))
+ {
+ String os = "win";
+ if (value.indexOf("32") >= 0 || value.indexOf("*") >= 0)
+ {
+ os = "win32";
+ }
+ else if (value.indexOf("95") >= 0)
+ {
+ os = "windows95";
+ }
+ else if (value.indexOf("98") >= 0)
+ {
+ os = "windows98";
+ }
+ else if (value.indexOf("nt") >= 0)
+ {
+ os = "windowsnt";
+ }
+ else if (value.indexOf("2000") >= 0)
+ {
+ os = "windows2000";
+ }
+ else if (value.indexOf("xp") >= 0)
+ {
+ os = "windowsxp";
+ }
+ else if (value.indexOf("ce") >= 0)
+ {
+ os = "windowsce";
+ }
+ else if (value.indexOf("vista") >= 0)
+ {
+ os = "windowsvista";
+ }
+ return os;
+ }
+ else if (value.startsWith("linux"))
+ {
+ return "linux";
+ }
+ else if (value.startsWith("aix"))
+ {
+ return "aix";
+ }
+ else if (value.startsWith("digitalunix"))
+ {
+ return "digitalunix";
+ }
+ else if (value.startsWith("hpux"))
+ {
+ return "hpux";
+ }
+ else if (value.startsWith("irix"))
+ {
+ return "irix";
+ }
+ else if (value.startsWith("macos") || value.startsWith("mac os"))
+ {
+ return "macos";
+ }
+ else if (value.startsWith("netware"))
+ {
+ return "netware";
+ }
+ else if (value.startsWith("openbsd"))
+ {
+ return "openbsd";
+ }
+ else if (value.startsWith("netbsd"))
+ {
+ return "netbsd";
+ }
+ else if (value.startsWith("os2") || value.startsWith("os/2"))
+ {
+ return "os2";
+ }
+ else if (value.startsWith("qnx") || value.startsWith("procnto"))
+ {
+ return "qnx";
+ }
+ else if (value.startsWith("solaris"))
+ {
+ return "solaris";
+ }
+ else if (value.startsWith("sunos"))
+ {
+ return "sunos";
+ }
+ else if (value.startsWith("vxworks"))
+ {
+ return "vxworks";
+ }
+ return value;
+ }
+
+ public static String normalizeProcessor(String value)
+ {
+ value = value.toLowerCase();
+
+ if (value.startsWith("x86-64") || value.startsWith("amd64"))
+ {
+ return "x86-64";
+ }
+ else if (value.startsWith("x86") || value.startsWith("pentium")
+ || value.startsWith("i386") || value.startsWith("i486")
+ || value.startsWith("i586") || value.startsWith("i686"))
+ {
+ return "x86";
+ }
+ else if (value.startsWith("68k"))
+ {
+ return "68k";
+ }
+ else if (value.startsWith("arm"))
+ {
+ return "arm";
+ }
+ else if (value.startsWith("alpha"))
+ {
+ return "alpha";
+ }
+ else if (value.startsWith("ignite") || value.startsWith("psc1k"))
+ {
+ return "ignite";
+ }
+ else if (value.startsWith("mips"))
+ {
+ return "mips";
+ }
+ else if (value.startsWith("parisc"))
+ {
+ return "parisc";
+ }
+ else if (value.startsWith("powerpc") || value.startsWith("power")
+ || value.startsWith("ppc"))
+ {
+ return "powerpc";
+ }
+ else if (value.startsWith("sparc"))
+ {
+ return "sparc";
+ }
+ return value;
+ }
+
+ public static String normalizeOSVersion(String value)
+ {
+ // Header: 'Bundle-NativeCode', Parameter: 'osversion'
+ // Standardized 'osversion': major.minor.micro, only digits
+ try
+ {
+ return VersionRange.parse(value).toString();
+ }
+ catch (Exception ex)
+ {
+ return Version.emptyVersion.toString();
+ }
+ }
+}
\ No newline at end of file
Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Requirement.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Requirement.java?rev=807795&view=auto
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Requirement.java (added)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Requirement.java Tue Aug 25 20:30:33 2009
@@ -0,0 +1,244 @@
+/*
+ * 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
+ *
+ * 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.resolver.manifestparser;
+
+import org.apache.felix.resolver.VersionRange;
+import org.apache.felix.resolver.Version;
+
+public class Requirement
+{
+ private final String m_namespace;
+ private final R4Directive[] m_directives;
+ private final R4Attribute[] m_attributes;
+ private final boolean m_isOptional;
+
+ private final String m_targetName;
+ private final VersionRange m_targetVersionRange;
+
+ public Requirement(String namespace, R4Directive[] directives, R4Attribute[] attributes)
+ {
+ m_namespace = namespace;
+ m_directives = directives;
+ m_attributes = attributes;
+
+ // Find all import directives: resolution.
+ boolean optional = false;
+ for (int i = 0; (m_directives != null) && (i < m_directives.length); i++)
+ {
+ if (m_directives[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
+ {
+ optional = m_directives[i].getValue().equals(Constants.RESOLUTION_OPTIONAL);
+ }
+ }
+ m_isOptional = optional;
+
+ String targetName = null;
+ VersionRange targetVersionRange = VersionRange.infiniteRange;
+ for (int i = 0; i < m_attributes.length; i++)
+ {
+ if (m_namespace.equals(Capability.MODULE_NAMESPACE))
+ {
+ if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
+ {
+ targetName = (String) m_attributes[i].getValue();
+ }
+ else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
+ {
+ targetVersionRange = (VersionRange) m_attributes[i].getValue();
+ }
+ }
+ else if (m_namespace.equals(Capability.PACKAGE_NAMESPACE))
+ {
+ if (m_attributes[i].getName().equals(Capability.PACKAGE_PROPERTY))
+ {
+ targetName = (String) m_attributes[i].getValue();
+ }
+ else if (m_attributes[i].getName().equals(Capability.VERSION_PROPERTY))
+ {
+ targetVersionRange = (VersionRange) m_attributes[i].getValue();
+ }
+ }
+ else if (m_namespace.equals(Capability.HOST_NAMESPACE))
+ {
+ if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
+ {
+ targetName = (String) m_attributes[i].getValue();
+ }
+ else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
+ {
+ targetVersionRange = (VersionRange) m_attributes[i].getValue();
+ }
+ }
+ }
+ m_targetName = targetName;
+ m_targetVersionRange = targetVersionRange;
+ }
+
+ public String getNamespace()
+ {
+ return m_namespace;
+ }
+
+// TODO: RB - We need to verify that the resolver code does not
+// touch these implementation-specific methods.
+
+ public String getTargetName()
+ {
+ return m_targetName;
+ }
+
+ public VersionRange getTargetVersionRange()
+ {
+ return m_targetVersionRange;
+ }
+
+ public R4Directive[] getDirectives()
+ {
+ // TODO: RB - We should return copies of the arrays probably.
+ return m_directives;
+ }
+
+ public R4Attribute[] getAttributes()
+ {
+ // TODO: RB - We should return copies of the arrays probably.
+ return m_attributes;
+ }
+
+ public boolean isMultiple()
+ {
+ return false;
+ }
+
+ public boolean isOptional()
+ {
+ return m_isOptional;
+ }
+
+ public String getComment()
+ {
+ return "Comment for " + toString();
+ }
+
+ public boolean isSatisfied(Capability capability)
+ {
+ return capability.getNamespace().equals(getNamespace()) &&
+ doAttributesMatch((Capability) capability);
+ }
+
+ private boolean doAttributesMatch(Capability ec)
+ {
+ // 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];
+
+ // 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 (capAttr.getValue() instanceof Object[])
+ {
+ Object[] values = (Object[]) capAttr.getValue();
+ boolean matched = false;
+ for (int valIdx = 0; !matched && (valIdx < values.length); valIdx++)
+ {
+ if (reqAttr.getValue().equals(values[valIdx]))
+ {
+ matched = true;
+ }
+ }
+ if (!matched)
+ {
+ 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;
+ }
+}
\ No newline at end of file