| /* |
| * 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.framework.util.manifestparser; |
| |
| import org.apache.felix.framework.BundleRevisionImpl; |
| import org.apache.felix.framework.Logger; |
| import org.apache.felix.framework.capabilityset.SimpleFilter; |
| import org.apache.felix.framework.util.FelixConstants; |
| import org.apache.felix.framework.wiring.BundleCapabilityImpl; |
| import org.apache.felix.framework.wiring.BundleRequirementImpl; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.Version; |
| import org.osgi.framework.VersionRange; |
| import org.osgi.framework.namespace.BundleNamespace; |
| import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; |
| import org.osgi.framework.namespace.IdentityNamespace; |
| import org.osgi.framework.namespace.NativeNamespace; |
| import org.osgi.framework.wiring.BundleCapability; |
| import org.osgi.framework.wiring.BundleRequirement; |
| import org.osgi.framework.wiring.BundleRevision; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| public class ManifestParser |
| { |
| private static final String BUNDLE_LICENSE_HEADER = "Bundle-License"; // No constant defined by OSGi... |
| |
| private final Logger m_logger; |
| private final Map<String, Object> m_configMap; |
| private final Map<String, Object> m_headerMap; |
| private volatile int m_activationPolicy = BundleRevisionImpl.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 List<BundleCapability> m_capabilities; |
| private volatile List<BundleRequirement> m_requirements; |
| private volatile List<NativeLibraryClause> m_libraryClauses; |
| private volatile boolean m_libraryHeadersOptional = false; |
| |
| public ManifestParser(Logger logger, Map<String, Object> configMap, BundleRevision owner, Map<String, Object> headerMap) |
| throws BundleException |
| { |
| m_logger = logger; |
| m_configMap = configMap; |
| m_headerMap = headerMap; |
| |
| // Verify that only manifest version 2 is specified. |
| String manifestVersion = getManifestVersion(m_headerMap); |
| if ((manifestVersion != null) && !manifestVersion.equals("2")) |
| { |
| throw new BundleException( |
| "Unknown 'Bundle-ManifestVersion' value: " + manifestVersion); |
| } |
| |
| // Create lists to hold capabilities and requirements. |
| List<BundleCapabilityImpl> capList = new ArrayList<BundleCapabilityImpl>(); |
| |
| // |
| // 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. |
| // |
| |
| BundleCapabilityImpl bundleCap = parseBundleSymbolicName(owner, m_headerMap); |
| if (bundleCap != null) |
| { |
| m_bundleSymbolicName = (String) |
| bundleCap.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE); |
| |
| // Add a bundle capability and a host capability to all |
| // non-fragment bundles. A host capability is the same |
| // as a require capability, but with a different capability |
| // namespace. Bundle capabilities resolve required-bundle |
| // dependencies, while host capabilities resolve fragment-host |
| // dependencies. |
| if (headerMap.get(Constants.FRAGMENT_HOST) == null) |
| { |
| // All non-fragment bundles have host capabilities. |
| capList.add(bundleCap); |
| // A non-fragment bundle can choose to not have a host capability. |
| String attachment = |
| bundleCap.getDirectives().get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE); |
| attachment = (attachment == null) |
| ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME |
| : attachment; |
| if (!attachment.equalsIgnoreCase(Constants.FRAGMENT_ATTACHMENT_NEVER)) |
| { |
| Map<String, Object> hostAttrs = |
| new HashMap<String, Object>(bundleCap.getAttributes()); |
| Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE); |
| hostAttrs.put(BundleRevision.HOST_NAMESPACE, value); |
| capList.add(new BundleCapabilityImpl( |
| owner, BundleRevision.HOST_NAMESPACE, |
| bundleCap.getDirectives(), |
| hostAttrs)); |
| } |
| } |
| |
| // |
| // Add the osgi.identity capability. |
| // |
| capList.add(addIdentityCapability(owner, headerMap, bundleCap)); |
| } |
| |
| // Verify that bundle symbolic name is specified. |
| if (getManifestVersion().equals("2") && (m_bundleSymbolicName == null)) |
| { |
| throw new BundleException( |
| "R4 bundle manifests must include bundle symbolic name."); |
| } |
| |
| m_isExtension = checkExtensionBundle(headerMap); |
| |
| // |
| // Parse Fragment-Host. |
| // |
| |
| List<BundleRequirementImpl> hostReqs = parseFragmentHost(m_logger, owner, m_headerMap); |
| |
| // |
| // Parse Require-Bundle |
| // |
| |
| List<ParsedHeaderClause> rbClauses = |
| parseStandardHeader((String) headerMap.get(Constants.REQUIRE_BUNDLE)); |
| rbClauses = normalizeRequireClauses(m_logger, rbClauses, getManifestVersion()); |
| List<BundleRequirementImpl> rbReqs = convertRequires(rbClauses, owner); |
| |
| // |
| // Parse Import-Package. |
| // |
| |
| List<ParsedHeaderClause> importClauses = |
| parseStandardHeader((String) headerMap.get(Constants.IMPORT_PACKAGE)); |
| importClauses = normalizeImportClauses(m_logger, importClauses, getManifestVersion()); |
| List<BundleRequirement> importReqs = convertImports(importClauses, owner); |
| |
| // |
| // Parse DynamicImport-Package. |
| // |
| |
| List<ParsedHeaderClause> dynamicClauses = |
| parseStandardHeader((String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE)); |
| dynamicClauses = normalizeDynamicImportClauses(m_logger, dynamicClauses, getManifestVersion()); |
| List<BundleRequirement> dynamicReqs = convertImports(dynamicClauses, owner); |
| |
| // |
| // Parse Require-Capability. |
| // |
| |
| List<ParsedHeaderClause> requireClauses = |
| parseStandardHeader((String) headerMap.get(Constants.REQUIRE_CAPABILITY)); |
| importClauses = normalizeCapabilityClauses( |
| m_logger, requireClauses, getManifestVersion()); |
| List<BundleRequirement> requireReqs = convertRequireCapabilities(importClauses, owner); |
| |
| // |
| // Parse Bundle-RequiredExecutionEnvironment. |
| // |
| List<BundleRequirement> breeReqs = |
| parseBreeHeader((String) headerMap.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT), owner); |
| |
| // |
| // Parse Export-Package. |
| // |
| |
| List<ParsedHeaderClause> exportClauses = |
| parseStandardHeader((String) headerMap.get(Constants.EXPORT_PACKAGE)); |
| exportClauses = normalizeExportClauses(logger, exportClauses, |
| getManifestVersion(), m_bundleSymbolicName, m_bundleVersion); |
| List<BundleCapability> exportCaps = convertExports(exportClauses, owner); |
| |
| // |
| // Parse Provide-Capability. |
| // |
| |
| List<ParsedHeaderClause> provideClauses = |
| parseStandardHeader((String) headerMap.get(Constants.PROVIDE_CAPABILITY)); |
| provideClauses = normalizeCapabilityClauses( |
| logger, provideClauses, getManifestVersion()); |
| List<BundleCapability> provideCaps = convertProvideCapabilities(provideClauses, owner); |
| |
| // |
| // Calculate implicit imports. |
| // |
| |
| if (!getManifestVersion().equals("2")) |
| { |
| List<ParsedHeaderClause> implicitClauses = |
| calculateImplicitImports(exportCaps, importClauses); |
| importReqs.addAll(convertImports(implicitClauses, owner)); |
| |
| List<ParsedHeaderClause> allImportClauses = |
| new ArrayList<ParsedHeaderClause>(implicitClauses.size() + importClauses.size()); |
| allImportClauses.addAll(importClauses); |
| allImportClauses.addAll(implicitClauses); |
| |
| exportCaps = calculateImplicitUses(exportCaps, allImportClauses); |
| } |
| |
| // |
| // Parse Bundle-NativeCode. |
| // |
| |
| // Parse native library clauses. |
| m_libraryClauses = |
| parseLibraryStrings( |
| m_logger, |
| 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_libraryClauses.isEmpty() && |
| (m_libraryClauses.get(m_libraryClauses.size() - 1).getLibraryEntries() == null)) |
| { |
| m_libraryHeadersOptional = true; |
| m_libraryClauses.remove(m_libraryClauses.size() - 1); |
| } |
| |
| List<BundleRequirement> nativeCodeReqs = convertNativeCode(owner, m_libraryClauses, m_libraryHeadersOptional); |
| |
| // Combine all requirements. |
| m_requirements = new ArrayList<BundleRequirement>( |
| hostReqs.size() + importReqs.size() + rbReqs.size() |
| + requireReqs.size() + dynamicReqs.size() + breeReqs.size()); |
| m_requirements.addAll(hostReqs); |
| m_requirements.addAll(importReqs); |
| m_requirements.addAll(rbReqs); |
| m_requirements.addAll(requireReqs); |
| m_requirements.addAll(dynamicReqs); |
| m_requirements.addAll(breeReqs); |
| m_requirements.addAll(nativeCodeReqs); |
| |
| // Combine all capabilities. |
| m_capabilities = new ArrayList<BundleCapability>( |
| capList.size() + exportCaps.size() + provideCaps.size()); |
| m_capabilities.addAll(capList); |
| m_capabilities.addAll(exportCaps); |
| m_capabilities.addAll(provideCaps); |
| |
| // |
| // Parse activation policy. |
| // |
| |
| // This sets m_activationPolicy, m_includedPolicyClasses, and |
| // m_excludedPolicyClasses. |
| parseActivationPolicy(headerMap); |
| } |
| |
| private static List<ParsedHeaderClause> normalizeImportClauses( |
| Logger logger, List<ParsedHeaderClause> clauses, String mv) |
| throws BundleException |
| { |
| // Verify that the values are equals if the package specifies |
| // both version and specification-version attributes. |
| Set<String> dupeSet = new HashSet<String>(); |
| for (ParsedHeaderClause clause : clauses) |
| { |
| // Check for "version" and "specification-version" attributes |
| // and verify they are the same if both are specified. |
| Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE); |
| Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION); |
| if ((v != null) && (sv != null)) |
| { |
| // Verify they are equal. |
| if (!((String) v).trim().equals(((String) sv).trim())) |
| { |
| throw new IllegalArgumentException( |
| "Both version and specification-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)) |
| { |
| clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION); |
| v = (v == null) ? sv : v; |
| clause.m_attrs.put( |
| Constants.VERSION_ATTRIBUTE, |
| new VersionRange(v.toString())); |
| } |
| |
| // If bundle version is specified, then convert its type to VersionRange. |
| v = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE); |
| if (v != null) |
| { |
| clause.m_attrs.put( |
| Constants.BUNDLE_VERSION_ATTRIBUTE, |
| new VersionRange(v.toString())); |
| } |
| |
| // Verify no duplicate imports. |
| for (String pkgName : clause.m_paths) |
| { |
| if (!dupeSet.contains(pkgName)) |
| { |
| |
| // The character "." has no meaning in the OSGi spec except |
| // when placed on the bundle class path. Some people, however, |
| // mistakenly think it means the default package when imported |
| // or exported. This is not correct. It is invalid. |
| if (pkgName.equals(".")) |
| { |
| throw new BundleException("Imporing '.' is invalid."); |
| } |
| // Make sure a package name was specified. |
| else if (pkgName.length() == 0) |
| { |
| throw new BundleException( |
| "Imported package names cannot be zero length."); |
| } |
| dupeSet.add(pkgName); |
| } |
| else |
| { |
| throw new BundleException("Duplicate import: " + pkgName); |
| } |
| } |
| |
| if (!mv.equals("2")) |
| { |
| // R3 bundles cannot have directives on their imports. |
| if (!clause.m_dirs.isEmpty()) |
| { |
| throw new BundleException("R3 imports cannot contain directives."); |
| } |
| |
| // Remove and ignore all attributes other than version. |
| // NOTE: This is checking for "version" rather than "specification-version" |
| // because the package class normalizes to "version" to avoid having |
| // future special cases. This could be changed if more strict behavior |
| // is required. |
| if (!clause.m_attrs.isEmpty()) |
| { |
| // R3 package requirements should only have version attributes. |
| Object pkgVersion = clause.m_attrs.get(BundleCapabilityImpl.VERSION_ATTR); |
| pkgVersion = (pkgVersion == null) |
| ? new VersionRange(VersionRange.LEFT_CLOSED, Version.emptyVersion, null, VersionRange.RIGHT_CLOSED) |
| : pkgVersion; |
| for (Entry<String, Object> entry : clause.m_attrs.entrySet()) |
| { |
| if (!entry.getKey().equals(BundleCapabilityImpl.VERSION_ATTR)) |
| { |
| logger.log(Logger.LOG_WARNING, |
| "Unknown R3 import attribute: " |
| + entry.getKey()); |
| } |
| } |
| |
| // Remove all other attributes except package version. |
| clause.m_attrs.clear(); |
| clause.m_attrs.put(BundleCapabilityImpl.VERSION_ATTR, pkgVersion); |
| } |
| } |
| } |
| |
| return clauses; |
| } |
| |
| public static List<BundleRequirement> parseDynamicImportHeader( |
| Logger logger, BundleRevision owner, String header) |
| throws BundleException |
| { |
| |
| List<ParsedHeaderClause> importClauses = parseStandardHeader(header); |
| importClauses = normalizeDynamicImportClauses(logger, importClauses, "2"); |
| List<BundleRequirement> reqs = convertImports(importClauses, owner); |
| return reqs; |
| } |
| |
| private static List<BundleRequirement> convertImports( |
| List<ParsedHeaderClause> clauses, BundleRevision owner) |
| { |
| // Now convert generic header clauses into requirements. |
| List<BundleRequirement> reqList = new ArrayList<BundleRequirement>(); |
| for (ParsedHeaderClause clause : clauses) |
| { |
| for (String path : clause.m_paths) |
| { |
| // Prepend the package name to the array of attributes. |
| 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. |
| // TODO: OSGi R4.3 - This is ordering is kind of hacky. |
| // Prepend the package name to the array of attributes. |
| Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1); |
| // We want this first from an indexing perspective. |
| newAttrs.put( |
| BundleRevision.PACKAGE_NAMESPACE, |
| path); |
| newAttrs.putAll(attrs); |
| // But we need to put it again to make sure it wasn't overwritten. |
| newAttrs.put( |
| BundleRevision.PACKAGE_NAMESPACE, |
| path); |
| |
| // Create filter now so we can inject filter directive. |
| SimpleFilter sf = SimpleFilter.convert(newAttrs); |
| |
| // Inject filter directive. |
| // TODO: OSGi R4.3 - Can we insert this on demand somehow? |
| Map<String, String> dirs = clause.m_dirs; |
| Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1); |
| newDirs.putAll(dirs); |
| newDirs.put( |
| Constants.FILTER_DIRECTIVE, |
| sf.toString()); |
| |
| // Create package requirement and add to requirement list. |
| reqList.add( |
| new BundleRequirementImpl( |
| owner, |
| BundleRevision.PACKAGE_NAMESPACE, |
| newDirs, |
| Collections.EMPTY_MAP, |
| sf)); |
| } |
| } |
| |
| return reqList; |
| } |
| |
| private static List<ParsedHeaderClause> normalizeDynamicImportClauses( |
| Logger logger, List<ParsedHeaderClause> clauses, String mv) |
| throws BundleException |
| { |
| // Verify that the values are equals if the package specifies |
| // both version and specification-version attributes. |
| for (ParsedHeaderClause clause : clauses) |
| { |
| if (!mv.equals("2")) |
| { |
| // R3 bundles cannot have directives on their imports. |
| if (!clause.m_dirs.isEmpty()) |
| { |
| throw new BundleException("R3 imports cannot contain directives."); |
| } |
| } |
| |
| // Add the resolution directive to indicate that these are |
| // dynamic imports. |
| clause.m_dirs.put(Constants.RESOLUTION_DIRECTIVE, |
| FelixConstants.RESOLUTION_DYNAMIC); |
| |
| // Check for "version" and "specification-version" attributes |
| // and verify they are the same if both are specified. |
| Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE); |
| Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION); |
| if ((v != null) && (sv != null)) |
| { |
| // Verify they are equal. |
| if (!((String) v).trim().equals(((String) sv).trim())) |
| { |
| throw new IllegalArgumentException( |
| "Both version and specification-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)) |
| { |
| clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION); |
| v = (v == null) ? sv : v; |
| clause.m_attrs.put( |
| Constants.VERSION_ATTRIBUTE, |
| new VersionRange(v.toString())); |
| } |
| |
| // If bundle version is specified, then convert its type to VersionRange. |
| v = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE); |
| if (v != null) |
| { |
| clause.m_attrs.put( |
| Constants.BUNDLE_VERSION_ATTRIBUTE, |
| new VersionRange(v.toString())); |
| } |
| |
| // Dynamic imports can have duplicates, verify that no partial package name wild carding is used |
| for (String pkgName : clause.m_paths) |
| { |
| if (!pkgName.equals("*") && pkgName.endsWith("*") && !pkgName.endsWith(".*")) |
| { |
| throw new BundleException( |
| "Partial package name wild carding is not allowed: " + pkgName); |
| } |
| } |
| } |
| |
| return clauses; |
| } |
| |
| private static List<BundleRequirement> convertRequireCapabilities( |
| List<ParsedHeaderClause> clauses, BundleRevision owner) |
| throws BundleException |
| { |
| // Now convert generic header clauses into requirements. |
| List<BundleRequirement> reqList = new ArrayList<BundleRequirement>(); |
| for (ParsedHeaderClause clause : clauses) |
| { |
| try |
| { |
| String filterStr = clause.m_dirs.get(Constants.FILTER_DIRECTIVE); |
| SimpleFilter sf = (filterStr != null) |
| ? SimpleFilter.parse(filterStr) |
| : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL); |
| for (String path : clause.m_paths) |
| { |
| if (path.startsWith("osgi.wiring.")) |
| { |
| throw new BundleException("Manifest cannot use Require-Capability for '" |
| + path |
| + "' namespace."); |
| } |
| |
| // 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; |
| } |
| |
| static List<BundleRequirement> convertNativeCode(BundleRevision owner, List<NativeLibraryClause> nativeLibraryClauses, boolean hasOptionalLibraryDirective) |
| { |
| List<BundleRequirement> result = new ArrayList<BundleRequirement>(); |
| |
| List<SimpleFilter> nativeFilterClauseList = new ArrayList<SimpleFilter>(); |
| |
| if(nativeLibraryClauses != null && !nativeLibraryClauses.isEmpty()) |
| { |
| for(NativeLibraryClause clause: nativeLibraryClauses) |
| { |
| String[] osNameArray = clause.getOSNames(); |
| String[] osVersionArray = clause.getOSVersions(); |
| String[] processorArray = clause.getProcessors(); |
| String[] languageArray = clause.getLanguages(); |
| |
| String currentSelectionFilter = clause.getSelectionFilter(); |
| |
| List<SimpleFilter> nativeFilterList = new ArrayList<SimpleFilter>(); |
| if(osNameArray != null && osNameArray.length > 0) |
| { |
| nativeFilterList.add(buildFilterFromArray(NativeNamespace.CAPABILITY_OSNAME_ATTRIBUTE, osNameArray, SimpleFilter.APPROX)); |
| } |
| |
| if(osVersionArray != null && osVersionArray.length > 0) |
| { |
| nativeFilterList.add(buildFilterFromArray(NativeNamespace.CAPABILITY_OSVERSION_ATTRIBUTE, osVersionArray, SimpleFilter.EQ)); |
| } |
| |
| if(processorArray != null && processorArray.length > 0) |
| { |
| nativeFilterList.add(buildFilterFromArray(NativeNamespace.CAPABILITY_PROCESSOR_ATTRIBUTE, processorArray, SimpleFilter.APPROX)); |
| } |
| |
| if(languageArray != null && languageArray.length > 0) |
| { |
| nativeFilterList.add(buildFilterFromArray(NativeNamespace.CAPABILITY_LANGUAGE_ATTRIBUTE, languageArray, SimpleFilter.APPROX)); |
| } |
| |
| if(currentSelectionFilter != null) |
| { |
| nativeFilterList.add(SimpleFilter.parse(currentSelectionFilter)); |
| } |
| |
| if(!nativeFilterList.isEmpty()) |
| { |
| SimpleFilter nativeClauseFilter = new SimpleFilter(null, nativeFilterList, SimpleFilter.AND); |
| nativeFilterClauseList.add(nativeClauseFilter); |
| } |
| } |
| |
| Map<String, String> requirementDirectives = new HashMap<String, String>(); |
| |
| SimpleFilter consolidatedNativeFilter = null; |
| |
| if(hasOptionalLibraryDirective) |
| { |
| requirementDirectives.put(NativeNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE, NativeNamespace.RESOLUTION_OPTIONAL); |
| } |
| |
| if(nativeFilterClauseList.size() > 1) |
| { |
| consolidatedNativeFilter = new SimpleFilter(null, nativeFilterClauseList, SimpleFilter.OR); |
| |
| requirementDirectives.put(NativeNamespace.REQUIREMENT_FILTER_DIRECTIVE, consolidatedNativeFilter.toString()); |
| } |
| else if(nativeFilterClauseList.size() == 1) |
| { |
| consolidatedNativeFilter = nativeFilterClauseList.get(0); |
| |
| requirementDirectives.put(NativeNamespace.REQUIREMENT_FILTER_DIRECTIVE, consolidatedNativeFilter.toString()); |
| } |
| |
| if(requirementDirectives.size() > 0) |
| { |
| result.add(new BundleRequirementImpl(owner, NativeNamespace.NATIVE_NAMESPACE, requirementDirectives, |
| Collections.<String, Object>emptyMap(), |
| consolidatedNativeFilter)); |
| } |
| |
| } |
| |
| return result; |
| } |
| |
| private static SimpleFilter buildFilterFromArray(String attributeName, String[] stringArray, int operation) |
| { |
| SimpleFilter result = null; |
| List<SimpleFilter> filterSet = new ArrayList<SimpleFilter>(); |
| |
| if(stringArray != null) |
| { |
| for(String currentValue : stringArray) |
| { |
| filterSet.add(new SimpleFilter(attributeName, currentValue.toLowerCase(), operation)); |
| } |
| |
| if(filterSet.size() == 1) |
| { |
| result = filterSet.get(0); |
| } |
| else |
| { |
| result = new SimpleFilter(null, filterSet, SimpleFilter.OR); |
| } |
| } |
| |
| return result; |
| } |
| |
| private static List<ParsedHeaderClause> normalizeCapabilityClauses( |
| 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) |
| throws BundleException |
| { |
| List<BundleCapability> capList = new ArrayList<BundleCapability>(); |
| for (ParsedHeaderClause clause : clauses) |
| { |
| for (String path : clause.m_paths) |
| { |
| if (path.startsWith("osgi.wiring.")) |
| { |
| throw new BundleException("Manifest cannot use Provide-Capability for '" |
| + path |
| + "' namespace."); |
| } |
| |
| if((path.startsWith(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE) || |
| path.startsWith(NativeNamespace.NATIVE_NAMESPACE)) && (owner == null || |
| !FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(owner.getSymbolicName()))) |
| { |
| throw new BundleException("Only System Bundle can use Provide-Capability for '" |
| + path |
| + "' namespace.", BundleException.MANIFEST_ERROR); |
| } |
| |
| // 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) |
| throws BundleException |
| { |
| for (ParsedHeaderClause clause : clauses) |
| { |
| // Verify that the named package has not already been declared. |
| for (String pkgName : clause.m_paths) |
| { |
| // Verify that java.* packages are not exported (except from the system bundle). |
| if (!FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(bsn) && pkgName.startsWith("java.")) |
| { |
| throw new BundleException( |
| "Exporting java.* packages not allowed: " |
| + pkgName, BundleException.MANIFEST_ERROR); |
| } |
| // The character "." has no meaning in the OSGi spec except |
| // when placed on the bundle class path. Some people, however, |
| // mistakenly think it means the default package when imported |
| // or exported. This is not correct. It is invalid. |
| else if (pkgName.equals(".")) |
| { |
| throw new BundleException("Exporing '.' is invalid."); |
| } |
| // Make sure a package name was specified. |
| else if (pkgName.length() == 0) |
| { |
| throw new BundleException( |
| "Exported package names cannot be zero length."); |
| } |
| } |
| |
| // Check for "version" and "specification-version" attributes |
| // and verify they are the same if both are specified. |
| Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE); |
| Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION); |
| if ((v != null) && (sv != null)) |
| { |
| // Verify they are equal. |
| if (!((String) v).trim().equals(((String) sv).trim())) |
| { |
| throw new IllegalArgumentException( |
| "Both version and specification-version are specified, but they are not equal."); |
| } |
| } |
| |
| // Always add the default version if not specified. |
| if ((v == null) && (sv == null)) |
| { |
| v = Version.emptyVersion; |
| } |
| |
| // 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. |
| clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION); |
| v = (v == null) ? sv : v; |
| clause.m_attrs.put( |
| Constants.VERSION_ATTRIBUTE, |
| Version.parseVersion(v.toString())); |
| } |
| |
| // If this is an R4 bundle, then make sure it doesn't specify |
| // bundle symbolic name or bundle version attributes. |
| if (mv.equals("2")) |
| { |
| // Find symbolic name and version attribute, if present. |
| if (clause.m_attrs.containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE) |
| || clause.m_attrs.containsKey(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)) |
| { |
| throw new BundleException( |
| "Exports must not specify bundle symbolic name or bundle version."); |
| } |
| |
| // Now that we know that there are no bundle symbolic name and version |
| // attributes, add them since the spec says they are there implicitly. |
| clause.m_attrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, bsn); |
| clause.m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bv); |
| } |
| else if (!mv.equals("2")) |
| { |
| // R3 bundles cannot have directives on their exports. |
| if (!clause.m_dirs.isEmpty()) |
| { |
| throw new BundleException("R3 exports cannot contain directives."); |
| } |
| |
| // Remove and ignore all attributes other than version. |
| // NOTE: This is checking for "version" rather than "specification-version" |
| // because the package class normalizes to "version" to avoid having |
| // future special cases. This could be changed if more strict behavior |
| // is required. |
| if (!clause.m_attrs.isEmpty()) |
| { |
| // R3 package capabilities should only have a version attribute. |
| Object pkgVersion = clause.m_attrs.get(BundleCapabilityImpl.VERSION_ATTR); |
| pkgVersion = (pkgVersion == null) |
| ? Version.emptyVersion |
| : pkgVersion; |
| for (Entry<String, Object> entry : clause.m_attrs.entrySet()) |
| { |
| if (!entry.getKey().equals(BundleCapabilityImpl.VERSION_ATTR)) |
| { |
| logger.log( |
| Logger.LOG_WARNING, |
| "Unknown R3 export attribute: " |
| + entry.getKey()); |
| } |
| } |
| |
| // Remove all other attributes except package version. |
| clause.m_attrs.clear(); |
| clause.m_attrs.put(BundleCapabilityImpl.VERSION_ATTR, pkgVersion); |
| } |
| } |
| } |
| |
| return clauses; |
| } |
| |
| private static List<BundleCapability> convertExports( |
| List<ParsedHeaderClause> clauses, BundleRevision owner) |
| { |
| List<BundleCapability> capList = new ArrayList<BundleCapability>(); |
| 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.putAll(attrs); |
| newAttrs.put( |
| BundleRevision.PACKAGE_NAMESPACE, |
| pkgName); |
| |
| // Create package capability and add to capability list. |
| capList.add( |
| new BundleCapabilityImpl( |
| owner, |
| BundleRevision.PACKAGE_NAMESPACE, |
| clause.m_dirs, |
| newAttrs)); |
| } |
| } |
| |
| return capList; |
| } |
| |
| public String getManifestVersion() |
| { |
| String manifestVersion = getManifestVersion(m_headerMap); |
| return (manifestVersion == null) ? "1" : manifestVersion; |
| } |
| |
| private static String getManifestVersion(Map<String, Object> headerMap) |
| { |
| String manifestVersion = (String) headerMap.get(Constants.BUNDLE_MANIFESTVERSION); |
| return (manifestVersion == null) ? null : 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 List<BundleCapability> getCapabilities() |
| { |
| return m_capabilities; |
| } |
| |
| public List<BundleRequirement> getRequirements() |
| { |
| return m_requirements; |
| } |
| |
| /** |
| * <p> |
| * This method returns the selected native library metadata from |
| * the manifest. The information is not the raw metadata from the |
| * manifest, but is the native library clause selected according |
| * to the OSGi native library clause selection policy. The metadata |
| * returned by this method will be attached directly to a module and |
| * used for finding its native libraries at run time. To inspect the |
| * raw native library metadata refer to <tt>getLibraryClauses()</tt>. |
| * </p> |
| * <p> |
| * This method returns one of three values: |
| * </p> |
| * <ul> |
| * <li><tt>null</tt> - if the are no native libraries for this module; |
| * this may also indicate the native libraries are optional and |
| * did not match the current platform.</li> |
| * <li>Zero-length <tt>NativeLibrary</tt> array - if no matching native library |
| * clause was found; this bundle should not resolve.</li> |
| * <li>Nonzero-length <tt>NativeLibrary</tt> array - the native libraries |
| * associated with the matching native library clause.</li> |
| * </ul> |
| * |
| * @return <tt>null</tt> if there are no native libraries, a zero-length |
| * array if no libraries matched, or an array of selected libraries. |
| **/ |
| public List<NativeLibrary> getLibraries() |
| { |
| ArrayList<NativeLibrary> libs = null; |
| try |
| { |
| NativeLibraryClause clause = getSelectedLibraryClause(); |
| if (clause != null) |
| { |
| String[] entries = clause.getLibraryEntries(); |
| libs = new ArrayList<NativeLibrary>(entries.length); |
| int current = 0; |
| for (int i = 0; i < entries.length; i++) |
| { |
| String name = getName(entries[i]); |
| boolean found = false; |
| for (int j = 0; !found && (j < current); j++) |
| { |
| found = getName(entries[j]).equals(name); |
| } |
| if (!found) |
| { |
| libs.add(new NativeLibrary( |
| clause.getLibraryEntries()[i], |
| clause.getOSNames(), clause.getProcessors(), clause.getOSVersions(), |
| clause.getLanguages(), clause.getSelectionFilter())); |
| } |
| } |
| libs.trimToSize(); |
| } |
| } |
| catch (Exception ex) |
| { |
| libs = new ArrayList<NativeLibrary>(0); |
| } |
| return libs; |
| } |
| |
| private String getName(String path) |
| { |
| int idx = path.lastIndexOf('/'); |
| if (idx > -1) |
| { |
| return path.substring(idx); |
| } |
| return path; |
| } |
| |
| private NativeLibraryClause getSelectedLibraryClause() throws BundleException |
| { |
| if ((m_libraryClauses != null) && (m_libraryClauses.size() > 0)) |
| { |
| List<NativeLibraryClause> clauseList = new ArrayList<NativeLibraryClause>(); |
| |
| // Search for matching native clauses. |
| for (NativeLibraryClause libraryClause : m_libraryClauses) |
| { |
| if (libraryClause.match(m_configMap)) |
| { |
| clauseList.add(libraryClause); |
| } |
| } |
| |
| // Select the matching native clause. |
| int selected = 0; |
| if (clauseList.isEmpty()) |
| { |
| // If optional clause exists, no error thrown. |
| if (m_libraryHeadersOptional) |
| { |
| return null; |
| } |
| else |
| { |
| throw new BundleException("Unable to select a native library clause."); |
| } |
| } |
| else if (clauseList.size() == 1) |
| { |
| selected = 0; |
| } |
| else if (clauseList.size() > 1) |
| { |
| selected = firstSortedClause(clauseList); |
| } |
| return ((NativeLibraryClause) clauseList.get(selected)); |
| } |
| |
| return null; |
| } |
| |
| private int firstSortedClause(List<NativeLibraryClause> clauseList) |
| { |
| ArrayList<String> indexList = new ArrayList<String>(); |
| ArrayList<String> selection = new ArrayList<String>(); |
| |
| // Init index list |
| for (int i = 0; i < clauseList.size(); i++) |
| { |
| indexList.add("" + i); |
| } |
| |
| // Select clause with 'osversion' range declared |
| // and get back the max floor of 'osversion' ranges. |
| Version osVersionRangeMaxFloor = new Version(0, 0, 0); |
| for (int i = 0; i < indexList.size(); i++) |
| { |
| int index = Integer.parseInt(indexList.get(i).toString()); |
| String[] osversions = ((NativeLibraryClause) clauseList.get(index)).getOSVersions(); |
| if (osversions != null) |
| { |
| selection.add("" + indexList.get(i)); |
| } |
| for (int k = 0; (osversions != null) && (k < osversions.length); k++) |
| { |
| VersionRange range = new VersionRange(osversions[k]); |
| if ((range.getLeft()).compareTo(osVersionRangeMaxFloor) >= 0) |
| { |
| osVersionRangeMaxFloor = range.getLeft(); |
| } |
| } |
| } |
| |
| if (selection.size() == 1) |
| { |
| return Integer.parseInt(selection.get(0).toString()); |
| } |
| else if (selection.size() > 1) |
| { |
| // Keep only selected clauses with an 'osversion' |
| // equal to the max floor of 'osversion' ranges. |
| indexList = selection; |
| selection = new ArrayList<String>(); |
| for (int i = 0; i < indexList.size(); i++) |
| { |
| int index = Integer.parseInt(indexList.get(i).toString()); |
| String[] osversions = ((NativeLibraryClause) clauseList.get(index)).getOSVersions(); |
| for (int k = 0; k < osversions.length; k++) |
| { |
| VersionRange range = new VersionRange(osversions[k]); |
| if ((range.getLeft()).compareTo(osVersionRangeMaxFloor) >= 0) |
| { |
| selection.add("" + indexList.get(i)); |
| } |
| } |
| } |
| } |
| |
| if (selection.isEmpty()) |
| { |
| // Re-init index list. |
| selection.clear(); |
| indexList.clear(); |
| for (int i = 0; i < clauseList.size(); i++) |
| { |
| indexList.add("" + i); |
| } |
| } |
| else if (selection.size() == 1) |
| { |
| return Integer.parseInt(selection.get(0).toString()); |
| } |
| else |
| { |
| indexList = selection; |
| selection.clear(); |
| } |
| |
| // Keep only clauses with 'language' declared. |
| for (int i = 0; i < indexList.size(); i++) |
| { |
| int index = Integer.parseInt(indexList.get(i).toString()); |
| if (((NativeLibraryClause) clauseList.get(index)).getLanguages() != null) |
| { |
| selection.add("" + indexList.get(i)); |
| } |
| } |
| |
| // Return the first sorted clause |
| if (selection.isEmpty()) |
| { |
| return 0; |
| } |
| else |
| { |
| return Integer.parseInt(selection.get(0).toString()); |
| } |
| } |
| |
| private static List<ParsedHeaderClause> calculateImplicitImports( |
| List<BundleCapability> exports, List<ParsedHeaderClause> imports) |
| throws BundleException |
| { |
| List<ParsedHeaderClause> clauseList = new ArrayList<ParsedHeaderClause>(); |
| |
| // Since all R3 exports imply an import, add a corresponding |
| // requirement for each existing export capability. Do not |
| // duplicate imports. |
| Map<String, String> map = new HashMap<String, String>(); |
| // Add existing imports. |
| for (int impIdx = 0; impIdx < imports.size(); impIdx++) |
| { |
| for (int pathIdx = 0; pathIdx < imports.get(impIdx).m_paths.size(); pathIdx++) |
| { |
| map.put( |
| imports.get(impIdx).m_paths.get(pathIdx), |
| imports.get(impIdx).m_paths.get(pathIdx)); |
| } |
| } |
| // Add import requirement for each export capability. |
| for (int i = 0; i < exports.size(); i++) |
| { |
| if (map.get(exports.get(i).getAttributes() |
| .get(BundleRevision.PACKAGE_NAMESPACE)) == null) |
| { |
| // Convert Version to VersionRange. |
| Map<String, Object> attrs = new HashMap<String, Object>(); |
| Object version = exports.get(i).getAttributes().get(Constants.VERSION_ATTRIBUTE); |
| if (version != null) |
| { |
| attrs.put( |
| Constants.VERSION_ATTRIBUTE, |
| new VersionRange(version.toString())); |
| } |
| |
| List<String> paths = new ArrayList<String>(); |
| paths.add((String) |
| exports.get(i).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)); |
| clauseList.add( |
| new ParsedHeaderClause( |
| paths, Collections.EMPTY_MAP, attrs, Collections.EMPTY_MAP)); |
| } |
| } |
| |
| return clauseList; |
| } |
| |
| private static List<BundleCapability> calculateImplicitUses( |
| List<BundleCapability> exports, List<ParsedHeaderClause> imports) |
| throws BundleException |
| { |
| // 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; i < imports.size(); i++) |
| { |
| for (int pathIdx = 0; pathIdx < imports.get(i).m_paths.size(); pathIdx++) |
| { |
| usesValue = usesValue |
| + ((usesValue.length() > 0) ? "," : "") |
| + imports.get(i).m_paths.get(pathIdx); |
| } |
| } |
| for (int i = 0; i < exports.size(); i++) |
| { |
| Map<String, String> dirs = new HashMap<String, String>(1); |
| dirs.put(Constants.USES_DIRECTIVE, usesValue); |
| exports.set(i, new BundleCapabilityImpl( |
| exports.get(i).getRevision(), |
| BundleRevision.PACKAGE_NAMESPACE, |
| dirs, |
| exports.get(i).getAttributes())); |
| } |
| |
| return exports; |
| } |
| |
| private static boolean checkExtensionBundle(Map<String, Object> headerMap) throws BundleException |
| { |
| Object extension = parseExtensionBundleHeader( |
| (String) headerMap.get(Constants.FRAGMENT_HOST)); |
| |
| if (extension != null) |
| { |
| if (!(Constants.EXTENSION_FRAMEWORK.equals(extension) || |
| Constants.EXTENSION_BOOTCLASSPATH.equals(extension))) |
| { |
| throw new BundleException( |
| "Extension bundle must have either 'extension:=framework' or 'extension:=bootclasspath'"); |
| } |
| if (headerMap.containsKey(Constants.REQUIRE_BUNDLE) || |
| headerMap.containsKey(Constants.BUNDLE_NATIVECODE) || |
| headerMap.containsKey(Constants.DYNAMICIMPORT_PACKAGE) || |
| headerMap.containsKey(Constants.BUNDLE_ACTIVATOR)) |
| { |
| throw new BundleException("Invalid extension bundle manifest"); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| private static BundleCapabilityImpl parseBundleSymbolicName( |
| BundleRevision owner, Map<String, Object> headerMap) |
| throws BundleException |
| { |
| List<ParsedHeaderClause> clauses = parseStandardHeader( |
| (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME)); |
| if (clauses.size() > 0) |
| { |
| if (clauses.size() > 1) |
| { |
| throw new BundleException( |
| "Cannot have multiple symbolic names: " |
| + headerMap.get(Constants.BUNDLE_SYMBOLICNAME)); |
| } |
| else if (clauses.get(0).m_paths.size() > 1) |
| { |
| throw new BundleException( |
| "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. |
| String mv = getManifestVersion(headerMap); |
| if (mv != null) |
| { |
| throw ex; |
| } |
| bundleVersion = Version.emptyVersion; |
| } |
| } |
| |
| // Create a require capability and return it. |
| String symName = (String) clauses.get(0).m_paths.get(0); |
| clauses.get(0).m_attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName); |
| clauses.get(0).m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion); |
| return new BundleCapabilityImpl( |
| owner, |
| BundleRevision.BUNDLE_NAMESPACE, |
| clauses.get(0).m_dirs, |
| clauses.get(0).m_attrs); |
| } |
| |
| return null; |
| } |
| |
| private static BundleCapabilityImpl addIdentityCapability(BundleRevision owner, |
| Map<String, Object> headerMap, BundleCapabilityImpl bundleCap) |
| { |
| Map<String, Object> attrs = new HashMap<String, Object>(); |
| |
| attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, |
| bundleCap.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE)); |
| attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, |
| headerMap.get(Constants.FRAGMENT_HOST) == null |
| ? IdentityNamespace.TYPE_BUNDLE |
| : IdentityNamespace.TYPE_FRAGMENT); |
| attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, |
| bundleCap.getAttributes().get(Constants.BUNDLE_VERSION_ATTRIBUTE)); |
| |
| if (headerMap.get(Constants.BUNDLE_COPYRIGHT) != null) |
| { |
| attrs.put(IdentityNamespace.CAPABILITY_COPYRIGHT_ATTRIBUTE, |
| headerMap.get(Constants.BUNDLE_COPYRIGHT)); |
| } |
| |
| if (headerMap.get(Constants.BUNDLE_DESCRIPTION) != null) |
| { |
| attrs.put(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE, |
| headerMap.get(Constants.BUNDLE_DESCRIPTION)); |
| } |
| if (headerMap.get(Constants.BUNDLE_DOCURL) != null) |
| { |
| attrs.put(IdentityNamespace.CAPABILITY_DOCUMENTATION_ATTRIBUTE, |
| headerMap.get(Constants.BUNDLE_DOCURL)); |
| } |
| if (headerMap.get(BUNDLE_LICENSE_HEADER) != null) |
| { |
| attrs.put(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE, |
| headerMap.get(BUNDLE_LICENSE_HEADER)); |
| } |
| |
| Map<String, String> dirs; |
| if (bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE) != null) |
| { |
| dirs = Collections.singletonMap(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE, |
| bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE)); |
| } |
| else |
| { |
| dirs = Collections.emptyMap(); |
| } |
| return new BundleCapabilityImpl(owner, IdentityNamespace.IDENTITY_NAMESPACE, dirs, attrs); |
| } |
| |
| private static List<BundleRequirementImpl> parseFragmentHost( |
| Logger logger, BundleRevision owner, Map<String, Object> headerMap) |
| throws BundleException |
| { |
| List<BundleRequirementImpl> reqs = new ArrayList<BundleRequirementImpl>(); |
| |
| String mv = getManifestVersion(headerMap); |
| if ((mv != null) && mv.equals("2")) |
| { |
| List<ParsedHeaderClause> clauses = parseStandardHeader( |
| (String) headerMap.get(Constants.FRAGMENT_HOST)); |
| if (clauses.size() > 0) |
| { |
| // Make sure that only one fragment host symbolic name is specified. |
| if (clauses.size() > 1) |
| { |
| throw new BundleException( |
| "Fragments cannot have multiple hosts: " |
| + headerMap.get(Constants.FRAGMENT_HOST)); |
| } |
| else if (clauses.get(0).m_paths.size() > 1) |
| { |
| throw new BundleException( |
| "Fragments cannot have multiple hosts: " |
| + headerMap.get(Constants.FRAGMENT_HOST)); |
| } |
| |
| // If the bundle-version attribute is specified, then convert |
| // it to the proper type. |
| Object value = clauses.get(0).m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE); |
| value = (value == null) ? "0.0.0" : value; |
| if (value != null) |
| { |
| clauses.get(0).m_attrs.put( |
| Constants.BUNDLE_VERSION_ATTRIBUTE, |
| new VersionRange(value.toString())); |
| } |
| |
| // Note that we use a linked hash map here to ensure the |
| // host symbolic name is first, which will make indexing |
| // more efficient. |
| // TODO: OSGi R4.3 - This is ordering is kind of hacky. |
| // Prepend the host symbolic name to the map of attributes. |
| Map<String, Object> attrs = clauses.get(0).m_attrs; |
| Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1); |
| // We want this first from an indexing perspective. |
| newAttrs.put( |
| BundleRevision.HOST_NAMESPACE, |
| clauses.get(0).m_paths.get(0)); |
| newAttrs.putAll(attrs); |
| // But we need to put it again to make sure it wasn't overwritten. |
| newAttrs.put( |
| BundleRevision.HOST_NAMESPACE, |
| clauses.get(0).m_paths.get(0)); |
| |
| // Create filter now so we can inject filter directive. |
| SimpleFilter sf = SimpleFilter.convert(newAttrs); |
| |
| // Inject filter directive. |
| // TODO: OSGi R4.3 - Can we insert this on demand somehow? |
| Map<String, String> dirs = clauses.get(0).m_dirs; |
| Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1); |
| newDirs.putAll(dirs); |
| newDirs.put( |
| Constants.FILTER_DIRECTIVE, |
| sf.toString()); |
| |
| reqs.add(new BundleRequirementImpl( |
| owner, BundleRevision.HOST_NAMESPACE, |
| newDirs, |
| newAttrs)); |
| } |
| } |
| else if (headerMap.get(Constants.FRAGMENT_HOST) != null) |
| { |
| String s = (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME); |
| s = (s == null) ? (String) headerMap.get(Constants.BUNDLE_NAME) : s; |
| s = (s == null) ? headerMap.toString() : s; |
| logger.log( |
| Logger.LOG_WARNING, |
| "Only R4 bundles can be fragments: " + s); |
| } |
| |
| return reqs; |
| } |
| |
| private static List<BundleRequirement> parseBreeHeader(String header, BundleRevision owner) |
| { |
| List<String> filters = new ArrayList<String>(); |
| for (String entry : parseDelimitedString(header, ",")) |
| { |
| List<String> names = parseDelimitedString(entry, "/"); |
| List<String> left = parseDelimitedString(names.get(0), "-"); |
| |
| String lName = left.get(0); |
| Version lVer; |
| try |
| { |
| lVer = Version.parseVersion(left.get(1)); |
| } |
| catch (Exception ex) |
| { |
| // Version doesn't parse. Make it part of the name. |
| lName = names.get(0); |
| lVer = null; |
| } |
| |
| String rName = null; |
| Version rVer = null; |
| if (names.size() > 1) |
| { |
| List<String> right = parseDelimitedString(names.get(1), "-"); |
| rName = right.get(0); |
| try |
| { |
| rVer = Version.parseVersion(right.get(1)); |
| } |
| catch (Exception ex) |
| { |
| rName = names.get(1); |
| rVer = null; |
| } |
| } |
| |
| String versionClause; |
| if (lVer != null) |
| { |
| if ((rVer != null) && (!rVer.equals(lVer))) |
| { |
| // Both versions are defined, but different. Make each of them part of the name |
| lName = names.get(0); |
| rName = names.get(1); |
| versionClause = null; |
| } |
| else |
| { |
| versionClause = getBreeVersionClause(lVer); |
| } |
| } |
| else |
| { |
| versionClause = getBreeVersionClause(rVer); |
| } |
| |
| if ("J2SE".equals(lName)) |
| { |
| // J2SE is not used in the Capability variant of BREE, use JavaSE here |
| // This can only happen with the lName part... |
| lName = "JavaSE"; |
| } |
| |
| String nameClause; |
| if (rName != null) |
| nameClause = "(" + ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE + "=" + lName + "/" + rName + ")"; |
| else |
| nameClause = "(" + ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE + "=" + lName + ")"; |
| |
| String filter; |
| if (versionClause != null) |
| filter = "(&" + nameClause + versionClause + ")"; |
| else |
| filter = nameClause; |
| |
| filters.add(filter); |
| } |
| |
| if (filters.size() == 0) |
| { |
| return Collections.emptyList(); |
| } |
| else |
| { |
| String reqFilter; |
| if (filters.size() == 1) |
| { |
| reqFilter = filters.get(0); |
| } |
| else |
| { |
| // If there are more BREE filters, we need to or them together |
| StringBuilder sb = new StringBuilder("(|"); |
| for (String f : filters) |
| { |
| sb.append(f); |
| } |
| sb.append(")"); |
| reqFilter = sb.toString(); |
| } |
| |
| SimpleFilter sf = SimpleFilter.parse(reqFilter); |
| return Collections.<BundleRequirement>singletonList(new BundleRequirementImpl( |
| owner, |
| ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, |
| Collections.singletonMap(ExecutionEnvironmentNamespace.REQUIREMENT_FILTER_DIRECTIVE, reqFilter), |
| Collections.<String, Object>emptyMap(), |
| sf)); |
| } |
| } |
| |
| private static String getBreeVersionClause(Version ver) |
| { |
| if (ver == null) |
| return null; |
| |
| return "(" + ExecutionEnvironmentNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=" + ver + ")"; |
| } |
| |
| private static List<ParsedHeaderClause> normalizeRequireClauses( |
| Logger logger, List<ParsedHeaderClause> clauses, String mv) |
| { |
| // R3 bundles cannot require other bundles. |
| if (!mv.equals("2")) |
| { |
| clauses.clear(); |
| } |
| else |
| { |
| // Convert bundle version attribute to VersionRange type. |
| for (ParsedHeaderClause clause : clauses) |
| { |
| Object value = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE); |
| if (value != null) |
| { |
| clause.m_attrs.put( |
| Constants.BUNDLE_VERSION_ATTRIBUTE, |
| new VersionRange(value.toString())); |
| } |
| } |
| } |
| |
| return clauses; |
| } |
| |
| private static List<BundleRequirementImpl> convertRequires( |
| List<ParsedHeaderClause> clauses, BundleRevision owner) |
| { |
| List<BundleRequirementImpl> reqList = new ArrayList<BundleRequirementImpl>(); |
| for (ParsedHeaderClause clause : clauses) |
| { |
| for (String path : clause.m_paths) |
| { |
| // Prepend the bundle symbolic name to the array of attributes. |
| Map<String, Object> attrs = clause.m_attrs; |
| // Note that we use a linked hash map here to ensure the |
| // symbolic name attribute is first, which will make indexing |
| // more efficient. |
| // TODO: OSGi R4.3 - This is ordering is kind of hacky. |
| // Prepend the symbolic name to the array of attributes. |
| Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1); |
| // We want this first from an indexing perspective. |
| newAttrs.put( |
| BundleRevision.BUNDLE_NAMESPACE, |
| path); |
| newAttrs.putAll(attrs); |
| // But we need to put it again to make sure it wasn't overwritten. |
| newAttrs.put( |
| BundleRevision.BUNDLE_NAMESPACE, |
| path); |
| |
| // Create filter now so we can inject filter directive. |
| SimpleFilter sf = SimpleFilter.convert(newAttrs); |
| |
| // Inject filter directive. |
| // TODO: OSGi R4.3 - Can we insert this on demand somehow? |
| Map<String, String> dirs = clause.m_dirs; |
| Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1); |
| newDirs.putAll(dirs); |
| newDirs.put( |
| Constants.FILTER_DIRECTIVE, |
| sf.toString()); |
| |
| // Create package requirement and add to requirement list. |
| reqList.add( |
| new BundleRequirementImpl( |
| owner, |
| BundleRevision.BUNDLE_NAMESPACE, |
| newDirs, |
| newAttrs)); |
| } |
| } |
| |
| return reqList; |
| } |
| |
| public static String parseExtensionBundleHeader(String header) |
| throws BundleException |
| { |
| List<ParsedHeaderClause> clauses = parseStandardHeader(header); |
| |
| String result = null; |
| |
| if (clauses.size() == 1) |
| { |
| for (Entry<String, String> entry : clauses.get(0).m_dirs.entrySet()) |
| { |
| if (Constants.EXTENSION_DIRECTIVE.equals(entry.getKey())) |
| { |
| result = entry.getValue(); |
| } |
| } |
| |
| if (FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(clauses.get(0).m_paths.get(0)) || |
| Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(clauses.get(0).m_paths.get(0))) |
| { |
| result = (result == null) ? Constants.EXTENSION_FRAMEWORK : result; |
| } |
| else if (result != null) |
| { |
| throw new BundleException( |
| "Only the system bundle can have extension bundles."); |
| } |
| } |
| |
| return result; |
| } |
| |
| private void parseActivationPolicy(Map<String, Object> headerMap) |
| { |
| m_activationPolicy = BundleRevisionImpl.EAGER_ACTIVATION; |
| |
| List<ParsedHeaderClause> clauses = parseStandardHeader( |
| (String) headerMap.get(Constants.BUNDLE_ACTIVATIONPOLICY)); |
| |
| if (clauses.size() > 0) |
| { |
| // Just look for a "path" matching the lazy policy, ignore |
| // everything else. |
| for (String path : clauses.get(0).m_paths) |
| { |
| if (path.equals(Constants.ACTIVATION_LAZY)) |
| { |
| m_activationPolicy = BundleRevisionImpl.LAZY_ACTIVATION; |
| for (Entry<String, String> entry : clauses.get(0).m_dirs.entrySet()) |
| { |
| if (entry.getKey().equalsIgnoreCase(Constants.INCLUDE_DIRECTIVE)) |
| { |
| m_activationIncludeDir = entry.getValue(); |
| } |
| else if (entry.getKey().equalsIgnoreCase(Constants.EXCLUDE_DIRECTIVE)) |
| { |
| m_activationExcludeDir = entry.getValue(); |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2, |
| // path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2 |
| public static void main(String[] headers) |
| { |
| String header = headers[0]; |
| if (header != null) |
| { |
| if (header.length() == 0) |
| { |
| throw new IllegalArgumentException( |
| "A header cannot be an empty string."); |
| } |
| List<ParsedHeaderClause> clauses = parseStandardHeader(header); |
| |
| for (ParsedHeaderClause clause : clauses) |
| { |
| 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); |
| } |
| |
| } |
| } |
| |
| private static final char EOF = (char) -1; |
| |
| private static char charAt(int pos, String headers, int length) |
| { |
| if (pos >= length) |
| { |
| return EOF; |
| } |
| return headers.charAt(pos); |
| } |
| |
| private static final int CLAUSE_START = 0; |
| private static final int PARAMETER_START = 1; |
| private static final int KEY = 2; |
| private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4; |
| private static final int ARGUMENT = 8; |
| private static final int VALUE = 16; |
| |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| private static List<ParsedHeaderClause> parseStandardHeader(String header) |
| { |
| List<ParsedHeaderClause> clauses = new ArrayList<ParsedHeaderClause>(); |
| if (header == null) |
| { |
| return clauses; |
| } |
| ParsedHeaderClause clause = null; |
| String key = null; |
| Map targetMap = null; |
| int state = CLAUSE_START; |
| int currentPosition = 0; |
| int startPosition = 0; |
| int length = header.length(); |
| boolean quoted = false; |
| boolean escaped = false; |
| |
| char currentChar = EOF; |
| do |
| { |
| currentChar = charAt(currentPosition, header, length); |
| switch (state) |
| { |
| case CLAUSE_START: |
| clause = new ParsedHeaderClause( |
| new ArrayList<String>(), |
| new HashMap<String, String>(), |
| new HashMap<String, Object>(), |
| new HashMap<String, String>()); |
| clauses.add(clause); |
| state = PARAMETER_START; |
| case PARAMETER_START: |
| startPosition = currentPosition; |
| state = KEY; |
| case KEY: |
| switch (currentChar) |
| { |
| case ':': |
| case '=': |
| key = header.substring(startPosition, currentPosition).trim(); |
| startPosition = currentPosition + 1; |
| targetMap = clause.m_attrs; |
| state = currentChar == ':' ? DIRECTIVE_OR_TYPEDATTRIBUTE : ARGUMENT; |
| break; |
| case EOF: |
| case ',': |
| case ';': |
| clause.m_paths.add(header.substring(startPosition, currentPosition).trim()); |
| state = currentChar == ',' ? CLAUSE_START : PARAMETER_START; |
| break; |
| default: |
| break; |
| } |
| currentPosition++; |
| break; |
| case DIRECTIVE_OR_TYPEDATTRIBUTE: |
| switch(currentChar) |
| { |
| case '=': |
| if (startPosition != currentPosition) |
| { |
| clause.m_types.put(key, header.substring(startPosition, currentPosition).trim()); |
| } |
| else |
| { |
| targetMap = clause.m_dirs; |
| } |
| state = ARGUMENT; |
| startPosition = currentPosition + 1; |
| break; |
| default: |
| break; |
| } |
| currentPosition++; |
| break; |
| case ARGUMENT: |
| if (currentChar == '\"') |
| { |
| quoted = true; |
| currentPosition++; |
| } |
| else |
| { |
| quoted = false; |
| } |
| if (!Character.isWhitespace(currentChar)) { |
| state = VALUE; |
| } |
| else { |
| currentPosition++; |
| } |
| break; |
| case VALUE: |
| if (escaped) |
| { |
| escaped = false; |
| } |
| else |
| { |
| if (currentChar == '\\' ) |
| { |
| escaped = true; |
| } |
| else if (quoted && currentChar == '\"') |
| { |
| quoted = false; |
| } |
| else if (!quoted) |
| { |
| String value = null; |
| switch(currentChar) |
| { |
| case EOF: |
| case ';': |
| case ',': |
| value = header.substring(startPosition, currentPosition).trim(); |
| if (value.startsWith("\"") && value.endsWith("\"")) |
| { |
| value = value.substring(1, value.length() - 1); |
| } |
| if (targetMap.put(key, value) != null) |
| { |
| throw new IllegalArgumentException( |
| "Duplicate '" + key + "' in: " + header); |
| } |
| state = currentChar == ';' ? PARAMETER_START : CLAUSE_START; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| currentPosition++; |
| break; |
| default: |
| break; |
| } |
| } while ( currentChar != EOF); |
| |
| if (state > PARAMETER_START) |
| { |
| throw new IllegalArgumentException("Unable to parse header: " + header); |
| } |
| return clauses; |
| } |
| |
| public static List<String> parseDelimitedString(String value, String delim) |
| { |
| return parseDelimitedString(value, delim, true); |
| } |
| |
| /** |
| * 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 a list of string or an empty list if there are none. |
| **/ |
| public static List<String> parseDelimitedString(String value, String delim, boolean trim) |
| { |
| if (value == null) |
| { |
| value = ""; |
| } |
| |
| List<String> list = new ArrayList<String>(); |
| |
| int CHAR = 1; |
| int DELIMITER = 2; |
| int STARTQUOTE = 4; |
| int ENDQUOTE = 8; |
| |
| StringBuilder sb = new StringBuilder(); |
| |
| 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); |
| |
| if (!isEscaped && (c == '\\')) |
| { |
| 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 ((c == '"') && ((expecting & STARTQUOTE) > 0)) |
| { |
| sb.append(c); |
| expecting = CHAR | ENDQUOTE; |
| } |
| else if ((c == '"') && ((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); |
| } |
| |
| isEscaped = false; |
| } |
| |
| if (sb.length() > 0) |
| { |
| if (trim) |
| { |
| list.add(sb.toString().trim()); |
| } |
| else |
| { |
| list.add(sb.toString()); |
| } |
| } |
| |
| return list; |
| } |
| |
| /** |
| * 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 List<NativeLibraryClause> parseLibraryStrings( |
| Logger logger, List<String> libStrs) |
| throws IllegalArgumentException |
| { |
| if (libStrs == null) |
| { |
| return new ArrayList<NativeLibraryClause>(0); |
| } |
| |
| List<NativeLibraryClause> libList = new ArrayList<NativeLibraryClause>(libStrs.size()); |
| |
| for (int i = 0; i < libStrs.size(); i++) |
| { |
| NativeLibraryClause clause = NativeLibraryClause.parse(logger, libStrs.get(i)); |
| libList.add(clause); |
| } |
| |
| return libList; |
| } |
| |
| public static List<BundleCapability> aliasSymbolicName(List<BundleCapability> caps, BundleRevision owner) |
| { |
| if (caps == null) |
| { |
| return new ArrayList<BundleCapability>(0); |
| } |
| |
| List<BundleCapability> aliasCaps = new ArrayList<BundleCapability>(caps); |
| |
| String[] aliases = { |
| FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME, |
| Constants.SYSTEM_BUNDLE_SYMBOLICNAME }; |
| |
| for (int capIdx = 0; capIdx < aliasCaps.size(); capIdx++) |
| { |
| BundleCapability cap = aliasCaps.get(capIdx); |
| |
| // Need to alias bundle and host capabilities. |
| if (cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) |
| || cap.getNamespace().equals(BundleRevision.HOST_NAMESPACE)) |
| { |
| // Make a copy of the attribute array. |
| Map<String, Object> aliasAttrs = |
| new HashMap<String, Object>(cap.getAttributes()); |
| // Add the aliased value. |
| aliasAttrs.put(cap.getNamespace(), aliases); |
| // Create the aliased capability to replace the old capability. |
| cap = new BundleCapabilityImpl( |
| owner, |
| cap.getNamespace(), |
| cap.getDirectives(), |
| aliasAttrs); |
| aliasCaps.set(capIdx, cap); |
| } |
| |
| // Further, search attributes for bundle symbolic name and alias it too. |
| for (Entry<String, Object> entry : cap.getAttributes().entrySet()) |
| { |
| // If there is a bundle symbolic name attribute, add the |
| // standard alias as a value. |
| if (entry.getKey().equalsIgnoreCase(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)) |
| { |
| // Make a copy of the attribute array. |
| Map<String, Object> aliasAttrs = |
| new HashMap<String, Object>(cap.getAttributes()); |
| // Add the aliased value. |
| aliasAttrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, aliases); |
| // Create the aliased capability to replace the old capability. |
| aliasCaps.set(capIdx, new BundleCapabilityImpl( |
| owner, |
| cap.getNamespace(), |
| cap.getDirectives(), |
| aliasAttrs)); |
| // Continue with the next capability. |
| break; |
| } |
| } |
| } |
| |
| return aliasCaps; |
| } |
| } |