blob: 55bcb3d5f2f0c16fa055fd6b5db42b008230c23e [file] [log] [blame]
Richard S. Hall5ace92e2006-09-21 20:37:54 +00001/*
Richard S. Hallb3951672006-09-19 17:04:53 +00002 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
Richard S. Halla2878c12006-07-21 14:50:07 +00009 *
Richard S. Hallb3951672006-09-19 17:04:53 +000010 * http://www.apache.org/licenses/LICENSE-2.0
Richard S. Halla2878c12006-07-21 14:50:07 +000011 *
Richard S. Hallb3951672006-09-19 17:04:53 +000012 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
Richard S. Halla2878c12006-07-21 14:50:07 +000018 */
Richard S. Hall78605422006-12-18 20:47:55 +000019package org.apache.felix.framework.util.manifestparser;
Richard S. Halla2878c12006-07-21 14:50:07 +000020
Richard S. Hall8d116d42006-09-01 19:23:28 +000021import java.util.*;
Richard S. Hall1320d472010-03-03 14:58:21 +000022import java.util.ArrayList;
23import java.util.Map.Entry;
Richard S. Hall4ecc6772011-05-17 21:20:32 +000024import org.apache.felix.framework.BundleRevisionImpl;
Richard S. Halla2878c12006-07-21 14:50:07 +000025
26import org.apache.felix.framework.Logger;
Richard S. Hall831b7712011-06-06 20:11:46 +000027import org.apache.felix.framework.capabilityset.SimpleFilter;
Richard S. Hall4ecc6772011-05-17 21:20:32 +000028import org.apache.felix.framework.wiring.BundleCapabilityImpl;
Carsten Ziegelerfc8029a2008-05-14 16:26:03 +000029import org.apache.felix.framework.util.FelixConstants;
30import org.apache.felix.framework.util.VersionRange;
Richard S. Hall4ecc6772011-05-17 21:20:32 +000031import org.apache.felix.framework.wiring.BundleRequirementImpl;
Richard S. Hall9b940412011-07-20 18:19:02 +000032import org.osgi.framework.BundleException;
33import org.osgi.framework.Constants;
34import org.osgi.framework.Version;
Richard S. Hall4ecc6772011-05-17 21:20:32 +000035import org.osgi.framework.wiring.BundleCapability;
36import org.osgi.framework.wiring.BundleRequirement;
37import org.osgi.framework.wiring.BundleRevision;
Richard S. Halla2878c12006-07-21 14:50:07 +000038
39public class ManifestParser
40{
Karl Pauls71294432009-03-16 23:34:01 +000041 private final Logger m_logger;
42 private final Map m_configMap;
43 private final Map m_headerMap;
Richard S. Hall4ecc6772011-05-17 21:20:32 +000044 private volatile int m_activationPolicy = BundleRevisionImpl.EAGER_ACTIVATION;
Richard S. Hall10d9d2c2009-06-12 20:21:59 +000045 private volatile String m_activationIncludeDir;
46 private volatile String m_activationExcludeDir;
Karl Pauls71294432009-03-16 23:34:01 +000047 private volatile boolean m_isExtension = false;
48 private volatile String m_bundleSymbolicName;
49 private volatile Version m_bundleVersion;
Richard S. Hall4ecc6772011-05-17 21:20:32 +000050 private volatile List<BundleCapability> m_capabilities;
51 private volatile List<BundleRequirement> m_requirements;
Richard S. Hall1320d472010-03-03 14:58:21 +000052 private volatile List<R4LibraryClause> m_libraryClauses;
Karl Pauls71294432009-03-16 23:34:01 +000053 private volatile boolean m_libraryHeadersOptional = false;
Richard S. Halla2878c12006-07-21 14:50:07 +000054
Richard S. Hall4ecc6772011-05-17 21:20:32 +000055 public ManifestParser(Logger logger, Map configMap, BundleRevision owner, Map headerMap)
Richard S. Hall8d116d42006-09-01 19:23:28 +000056 throws BundleException
Richard S. Halla2878c12006-07-21 14:50:07 +000057 {
58 m_logger = logger;
Richard S. Hall471e3e62007-07-11 19:25:33 +000059 m_configMap = configMap;
Richard S. Halla2878c12006-07-21 14:50:07 +000060 m_headerMap = headerMap;
61
62 // Verify that only manifest version 2 is specified.
Richard S. Hall1563de22009-10-22 16:46:48 +000063 String manifestVersion = getManifestVersion(m_headerMap);
Richard S. Halla2878c12006-07-21 14:50:07 +000064 if ((manifestVersion != null) && !manifestVersion.equals("2"))
65 {
66 throw new BundleException(
67 "Unknown 'Bundle-ManifestVersion' value: " + manifestVersion);
68 }
69
Richard S. Hall9d4dcb02009-07-30 16:00:39 +000070 // Create lists to hold capabilities and requirements.
Richard S. Hall4ecc6772011-05-17 21:20:32 +000071 List<BundleCapabilityImpl> capList = new ArrayList();
Richard S. Hall4151e902006-12-27 16:11:16 +000072
73 //
Richard S. Hall7561a372009-01-27 19:34:12 +000074 // Parse bundle version.
75 //
76
77 m_bundleVersion = Version.emptyVersion;
78 if (headerMap.get(Constants.BUNDLE_VERSION) != null)
79 {
80 try
81 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +000082 m_bundleVersion = Version.parseVersion(
83 (String) headerMap.get(Constants.BUNDLE_VERSION));
Richard S. Hall7561a372009-01-27 19:34:12 +000084 }
85 catch (RuntimeException ex)
86 {
87 // R4 bundle versions must parse, R3 bundle version may not.
Richard S. Hall0a239a32009-01-29 22:21:19 +000088 if (getManifestVersion().equals("2"))
Richard S. Hall7561a372009-01-27 19:34:12 +000089 {
90 throw ex;
91 }
92 m_bundleVersion = Version.emptyVersion;
93 }
94 }
95
96 //
Richard S. Hall56b11942006-10-26 14:40:07 +000097 // Parse bundle symbolic name.
98 //
99
Richard S. Hallc4e75992011-06-15 18:57:20 +0000100 BundleCapabilityImpl bundleCap = parseBundleSymbolicName(owner, m_headerMap);
101 if (bundleCap != null)
Richard S. Hall56b11942006-10-26 14:40:07 +0000102 {
Richard S. Hall9802e3f2008-07-07 01:37:22 +0000103 m_bundleSymbolicName = (String)
Richard S. Hallc4e75992011-06-15 18:57:20 +0000104 bundleCap.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE);
Richard S. Hall9802e3f2008-07-07 01:37:22 +0000105
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000106 // Add a bundle capability and a host capability to all
Richard S. Hall5dc4bb72009-05-05 15:09:28 +0000107 // non-fragment bundles. A host capability is the same
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000108 // as a require capability, but with a different capability
109 // namespace. Bundle capabilities resolve required-bundle
Richard S. Hall5dc4bb72009-05-05 15:09:28 +0000110 // dependencies, while host capabilities resolve fragment-host
111 // dependencies.
Richard S. Hall2846a2b2008-06-01 03:08:17 +0000112 if (headerMap.get(Constants.FRAGMENT_HOST) == null)
113 {
Richard S. Hall577b2c92011-06-20 16:29:53 +0000114 // All non-fragment bundles have host capabilities.
Richard S. Hallc4e75992011-06-15 18:57:20 +0000115 capList.add(bundleCap);
Richard S. Hall577b2c92011-06-20 16:29:53 +0000116 // A non-fragment bundle can choose to not have a host capability.
117 String attachment =
118 bundleCap.getDirectives().get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
119 attachment = (attachment == null)
120 ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME
121 : attachment;
122 if (!attachment.equalsIgnoreCase(Constants.FRAGMENT_ATTACHMENT_NEVER))
123 {
124 Map<String, Object> hostAttrs =
125 new HashMap<String, Object>(bundleCap.getAttributes());
126 Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
127 hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
128 capList.add(new BundleCapabilityImpl(
129 owner, BundleRevision.HOST_NAMESPACE,
130 Collections.EMPTY_MAP,
131 hostAttrs));
132 }
Richard S. Hall2846a2b2008-06-01 03:08:17 +0000133 }
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000134
135 // Add a singleton capability if the bundle is a singleton.
136 // This is sort of a hack, but we need this for the resolver
137 // to be able to resolve singletons. It is not possible to
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000138 // attach this information to the bundle or host capabilities
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000139 // because fragments don't have those capabilities, but fragments
140 // can be singletons too.
Richard S. Hallc4e75992011-06-15 18:57:20 +0000141 if (isSingleton(bundleCap))
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000142 {
Richard S. Hallc4e75992011-06-15 18:57:20 +0000143 Map<String, Object> singletonAttrs =
144 new HashMap<String, Object>(bundleCap.getAttributes());
145 Object value = singletonAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
146 singletonAttrs.put(BundleCapabilityImpl.SINGLETON_NAMESPACE, value);
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000147 capList.add(new BundleCapabilityImpl(
148 owner, BundleCapabilityImpl.SINGLETON_NAMESPACE,
149 Collections.EMPTY_MAP,
Richard S. Hallc4e75992011-06-15 18:57:20 +0000150 singletonAttrs));
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000151 }
Richard S. Hall2846a2b2008-06-01 03:08:17 +0000152 }
153
Richard S. Hall1320d472010-03-03 14:58:21 +0000154 // Verify that bundle symbolic name is specified.
155 if (getManifestVersion().equals("2") && (m_bundleSymbolicName == null))
156 {
157 throw new BundleException(
158 "R4 bundle manifests must include bundle symbolic name.");
159 }
160
161 //
162 // Parse Fragment-Host.
163 //
164
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000165 List<BundleRequirementImpl> hostReqs = parseFragmentHost(m_logger, owner, m_headerMap);
Richard S. Hall1320d472010-03-03 14:58:21 +0000166
167 //
168 // Parse Require-Bundle
169 //
170
Richard S. Hall831b7712011-06-06 20:11:46 +0000171 List<ParsedHeaderClause> rbClauses =
Richard S. Hall1320d472010-03-03 14:58:21 +0000172 parseStandardHeader((String) headerMap.get(Constants.REQUIRE_BUNDLE));
Richard S. Hall831b7712011-06-06 20:11:46 +0000173 rbClauses = normalizeRequireClauses(m_logger, rbClauses, getManifestVersion());
174 List<BundleRequirementImpl> rbReqs = convertRequires(rbClauses, owner);
Richard S. Hall1320d472010-03-03 14:58:21 +0000175
176 //
177 // Parse Import-Package.
178 //
179
180 List<ParsedHeaderClause> importClauses =
181 parseStandardHeader((String) headerMap.get(Constants.IMPORT_PACKAGE));
182 importClauses = normalizeImportClauses(m_logger, importClauses, getManifestVersion());
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000183 List<BundleRequirement> importReqs = convertImports(importClauses, owner);
Richard S. Hall1320d472010-03-03 14:58:21 +0000184
185 //
186 // Parse DynamicImport-Package.
187 //
188
189 List<ParsedHeaderClause> dynamicClauses =
190 parseStandardHeader((String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
191 dynamicClauses = normalizeDynamicImportClauses(m_logger, dynamicClauses, getManifestVersion());
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000192 List<BundleRequirement> dynamicReqs = convertImports(dynamicClauses, owner);
Richard S. Hall1320d472010-03-03 14:58:21 +0000193
Richard S. Hall2846a2b2008-06-01 03:08:17 +0000194 //
Richard S. Hall831b7712011-06-06 20:11:46 +0000195 // Parse Require-Capability.
196 //
197
198 List<ParsedHeaderClause> requireClauses =
199 parseStandardHeader((String) headerMap.get(Constants.REQUIRE_CAPABILITY));
200 importClauses = normalizeRequireCapabilityClauses(
201 m_logger, requireClauses, getManifestVersion());
202 List<BundleRequirement> requireReqs = convertRequireCapabilities(importClauses, owner);
203
204 //
Richard S. Halla2878c12006-07-21 14:50:07 +0000205 // Parse Export-Package.
206 //
207
Richard S. Hall1320d472010-03-03 14:58:21 +0000208 List<ParsedHeaderClause> exportClauses =
209 parseStandardHeader((String) headerMap.get(Constants.EXPORT_PACKAGE));
210 exportClauses = normalizeExportClauses(logger, exportClauses,
211 getManifestVersion(), m_bundleSymbolicName, m_bundleVersion);
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000212 List<BundleCapability> exportCaps = convertExports(exportClauses, owner);
Richard S. Hall7baf7b82007-01-31 21:11:11 +0000213
Richard S. Hall1320d472010-03-03 14:58:21 +0000214 //
Richard S. Hall831b7712011-06-06 20:11:46 +0000215 // Parse Provide-Capability.
216 //
217
218 List<ParsedHeaderClause> provideClauses =
219 parseStandardHeader((String) headerMap.get(Constants.PROVIDE_CAPABILITY));
220 exportClauses = normalizeProvideCapabilityClauses(
221 logger, provideClauses, getManifestVersion());
222 List<BundleCapability> provideCaps = convertProvideCapabilities(provideClauses, owner);
223
224 //
Richard S. Hall1320d472010-03-03 14:58:21 +0000225 // Calculate implicit imports.
226 //
227
228 if (!getManifestVersion().equals("2"))
Richard S. Halla2878c12006-07-21 14:50:07 +0000229 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000230 List<ParsedHeaderClause> implicitClauses =
231 calculateImplicitImports(exportCaps, importClauses);
Richard S. Hall1a879e52010-04-18 19:22:48 +0000232 importReqs.addAll(convertImports(implicitClauses, owner));
Richard S. Hall1320d472010-03-03 14:58:21 +0000233
234 List<ParsedHeaderClause> allImportClauses =
235 new ArrayList<ParsedHeaderClause>(implicitClauses.size() + importClauses.size());
236 allImportClauses.addAll(importClauses);
237 allImportClauses.addAll(implicitClauses);
238
239 exportCaps = calculateImplicitUses(exportCaps, allImportClauses);
Richard S. Halla2878c12006-07-21 14:50:07 +0000240 }
Richard S. Hall862eca12007-01-22 07:09:25 +0000241
Richard S. Hall1320d472010-03-03 14:58:21 +0000242 // Combine all capabilities.
243 m_capabilities = new ArrayList(
Richard S. Hall831b7712011-06-06 20:11:46 +0000244 capList.size() + exportCaps.size() + provideCaps.size());
Richard S. Hall1320d472010-03-03 14:58:21 +0000245 m_capabilities.addAll(capList);
246 m_capabilities.addAll(exportCaps);
Richard S. Hall831b7712011-06-06 20:11:46 +0000247 m_capabilities.addAll(provideCaps);
Richard S. Halla2878c12006-07-21 14:50:07 +0000248
Richard S. Hall1320d472010-03-03 14:58:21 +0000249 // Combine all requirements.
250 m_requirements = new ArrayList(
Richard S. Hall831b7712011-06-06 20:11:46 +0000251 importReqs.size() + rbReqs.size() + hostReqs.size()
252 + requireReqs.size() + dynamicReqs.size());
Richard S. Hall1320d472010-03-03 14:58:21 +0000253 m_requirements.addAll(importReqs);
Richard S. Hall831b7712011-06-06 20:11:46 +0000254 m_requirements.addAll(rbReqs);
Richard S. Hall1320d472010-03-03 14:58:21 +0000255 m_requirements.addAll(hostReqs);
Richard S. Hall831b7712011-06-06 20:11:46 +0000256 m_requirements.addAll(requireReqs);
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000257 m_requirements.addAll(dynamicReqs);
Richard S. Halla2878c12006-07-21 14:50:07 +0000258
259 //
260 // Parse Bundle-NativeCode.
261 //
262
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000263 // Parse native library clauses.
Richard S. Hall1320d472010-03-03 14:58:21 +0000264 m_libraryClauses =
Richard S. Hall78605422006-12-18 20:47:55 +0000265 parseLibraryStrings(
Richard S. Halla2878c12006-07-21 14:50:07 +0000266 m_logger,
Richard S. Hall78605422006-12-18 20:47:55 +0000267 parseDelimitedString((String) m_headerMap.get(Constants.BUNDLE_NATIVECODE), ","));
Richard S. Halla2878c12006-07-21 14:50:07 +0000268
Richard S. Hall8d116d42006-09-01 19:23:28 +0000269 // Check to see if there was an optional native library clause, which is
270 // represented by a null library header; if so, record it and remove it.
Richard S. Hall831b7712011-06-06 20:11:46 +0000271 if (!m_libraryClauses.isEmpty() &&
Richard S. Hall1320d472010-03-03 14:58:21 +0000272 (m_libraryClauses.get(m_libraryClauses.size() - 1).getLibraryEntries() == null))
Richard S. Hall8d116d42006-09-01 19:23:28 +0000273 {
274 m_libraryHeadersOptional = true;
Richard S. Hall1320d472010-03-03 14:58:21 +0000275 m_libraryClauses.remove(m_libraryClauses.size() - 1);
Richard S. Hall8d116d42006-09-01 19:23:28 +0000276 }
277
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +0000278 //
279 // Parse activation policy.
280 //
281
Richard S. Hall10d9d2c2009-06-12 20:21:59 +0000282 // This sets m_activationPolicy, m_includedPolicyClasses, and
283 // m_excludedPolicyClasses.
284 parseActivationPolicy(headerMap);
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +0000285
Richard S. Hall1320d472010-03-03 14:58:21 +0000286 m_isExtension = checkExtensionBundle(headerMap);
287 }
288
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000289 private static boolean isSingleton(BundleCapabilityImpl cap)
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000290 {
Richard S. Hall2c0bf6b2011-06-07 18:00:08 +0000291 if (cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000292 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000293 String value = cap.getDirectives().get(Constants.SINGLETON_DIRECTIVE);
294 if ((value != null) && Boolean.valueOf(value))
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000295 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000296 return true;
Richard S. Hallda7b75e2011-03-04 21:27:59 +0000297 }
298 }
299 return false;
300 }
301
Richard S. Hall1320d472010-03-03 14:58:21 +0000302 private static List<ParsedHeaderClause> normalizeImportClauses(
303 Logger logger, List<ParsedHeaderClause> clauses, String mv)
304 throws BundleException
305 {
306 // Verify that the values are equals if the package specifies
307 // both version and specification-version attributes.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000308 Set dupeSet = new HashSet();
309 for (ParsedHeaderClause clause : clauses)
Richard S. Halla2878c12006-07-21 14:50:07 +0000310 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000311 // Check for "version" and "specification-version" attributes
312 // and verify they are the same if both are specified.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000313 Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE);
314 Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
Richard S. Hall1320d472010-03-03 14:58:21 +0000315 if ((v != null) && (sv != null))
316 {
317 // Verify they are equal.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000318 if (!((String) v).trim().equals(((String) sv).trim()))
Richard S. Hall1320d472010-03-03 14:58:21 +0000319 {
320 throw new IllegalArgumentException(
321 "Both version and specification-version are specified, but they are not equal.");
322 }
323 }
324
325 // Ensure that only the "version" attribute is used and convert
326 // it to the VersionRange type.
327 if ((v != null) || (sv != null))
328 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000329 clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
Richard S. Hall1320d472010-03-03 14:58:21 +0000330 v = (v == null) ? sv : v;
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000331 clause.m_attrs.put(
332 Constants.VERSION_ATTRIBUTE,
333 VersionRange.parse(v.toString()));
Richard S. Hall1320d472010-03-03 14:58:21 +0000334 }
335
336 // If bundle version is specified, then convert its type to VersionRange.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000337 v = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
Richard S. Hall1320d472010-03-03 14:58:21 +0000338 if (v != null)
339 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000340 clause.m_attrs.put(
341 Constants.BUNDLE_VERSION_ATTRIBUTE,
342 VersionRange.parse(v.toString()));
Richard S. Hall1320d472010-03-03 14:58:21 +0000343 }
Richard S. Hall1320d472010-03-03 14:58:21 +0000344
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000345 // Verify java.* is not imported, nor any duplicate imports.
Richard S. Hall831b7712011-06-06 20:11:46 +0000346 for (String pkgName : clause.m_paths)
Richard S. Hall1320d472010-03-03 14:58:21 +0000347 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000348 if (!dupeSet.contains(pkgName))
349 {
350 // Verify that java.* packages are not imported.
351 if (pkgName.startsWith("java."))
352 {
353 throw new BundleException(
354 "Importing java.* packages not allowed: " + pkgName);
355 }
356 // Make sure a package name was specified.
Richard S. Hall831b7712011-06-06 20:11:46 +0000357 else if (pkgName.length() == 0)
Richard S. Hall1320d472010-03-03 14:58:21 +0000358 {
359 throw new BundleException(
360 "Imported package names cannot be zero length.");
361 }
362 dupeSet.add(pkgName);
363 }
364 else
365 {
366 throw new BundleException("Duplicate import: " + pkgName);
367 }
368 }
Richard S. Hall1320d472010-03-03 14:58:21 +0000369
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000370 if (!mv.equals("2"))
Richard S. Hall1320d472010-03-03 14:58:21 +0000371 {
372 // R3 bundles cannot have directives on their imports.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000373 if (!clause.m_dirs.isEmpty())
Richard S. Hall1320d472010-03-03 14:58:21 +0000374 {
375 throw new BundleException("R3 imports cannot contain directives.");
376 }
377
378 // Remove and ignore all attributes other than version.
379 // NOTE: This is checking for "version" rather than "specification-version"
380 // because the package class normalizes to "version" to avoid having
381 // future special cases. This could be changed if more strict behavior
382 // is required.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000383 if (!clause.m_attrs.isEmpty())
Richard S. Hall1320d472010-03-03 14:58:21 +0000384 {
385 // R3 package requirements should only have version attributes.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000386 Object pkgVersion = clause.m_attrs.get(BundleCapabilityImpl.VERSION_ATTR);
387 pkgVersion = (pkgVersion == null)
388 ? new VersionRange(Version.emptyVersion, true, null, true)
389 : pkgVersion;
390 for (Entry<String, Object> entry : clause.m_attrs.entrySet())
Richard S. Hall1320d472010-03-03 14:58:21 +0000391 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000392 if (!entry.getKey().equals(BundleCapabilityImpl.VERSION_ATTR))
Richard S. Hall1320d472010-03-03 14:58:21 +0000393 {
394 logger.log(Logger.LOG_WARNING,
395 "Unknown R3 import attribute: "
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000396 + entry.getKey());
Richard S. Hall1320d472010-03-03 14:58:21 +0000397 }
398 }
399
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000400 // Remove all other attributes except package version.
401 clause.m_attrs.clear();
402 clause.m_attrs.put(BundleCapabilityImpl.VERSION_ATTR, pkgVersion);
Richard S. Hall1320d472010-03-03 14:58:21 +0000403 }
404 }
405 }
406
407 return clauses;
408 }
409
Richard S. Hall7eb28b92011-05-24 17:23:15 +0000410 public static List<BundleRequirement> parseDynamicImportHeader(
411 Logger logger, BundleRevision owner, String header)
412 throws BundleException
413 {
414
415 List<ParsedHeaderClause> importClauses = parseStandardHeader(header);
416 importClauses = normalizeDynamicImportClauses(logger, importClauses, "2");
417 List<BundleRequirement> reqs = convertImports(importClauses, owner);
418 return reqs;
419 }
420
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000421 private static List<BundleRequirement> convertImports(
422 List<ParsedHeaderClause> clauses, BundleRevision owner)
Richard S. Hall1320d472010-03-03 14:58:21 +0000423 {
424 // Now convert generic header clauses into requirements.
425 List reqList = new ArrayList();
Richard S. Hall831b7712011-06-06 20:11:46 +0000426 for (ParsedHeaderClause clause : clauses)
Richard S. Hall1320d472010-03-03 14:58:21 +0000427 {
Richard S. Hall831b7712011-06-06 20:11:46 +0000428 for (String path : clause.m_paths)
Richard S. Hall1320d472010-03-03 14:58:21 +0000429 {
430 // Prepend the package name to the array of attributes.
Richard S. Hall831b7712011-06-06 20:11:46 +0000431 Map<String, Object> attrs = clause.m_attrs;
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000432 // Note that we use a linked hash map here to ensure the
433 // package attribute is first, which will make indexing
434 // more efficient.
Richard S. Hall9b940412011-07-20 18:19:02 +0000435// TODO: OSGi R4.3 - This ordering fix is a hack...perhaps we should use the standard "key"
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000436// notion where namespace is also the name of the key attribute.
437 Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
438 newAttrs.put(
Richard S. Hallc4e75992011-06-15 18:57:20 +0000439 BundleRevision.PACKAGE_NAMESPACE,
Richard S. Hall831b7712011-06-06 20:11:46 +0000440 path);
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000441 newAttrs.putAll(attrs);
Richard S. Hall1320d472010-03-03 14:58:21 +0000442
Richard S. Hall9b940412011-07-20 18:19:02 +0000443 // Create filter now so we can inject filter directive.
444 SimpleFilter sf = BundleRequirementImpl.convertToFilter(newAttrs);
445
446 // Inject filter directive.
447// TODO: OSGi R4.3 - Can we insert this on demand somehow?
448 Map<String, String> dirs = clause.m_dirs;
449 Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
450 newDirs.putAll(dirs);
451 newDirs.put(
452 Constants.FILTER_DIRECTIVE,
453 sf.toString());
454
Richard S. Hall1320d472010-03-03 14:58:21 +0000455 // Create package requirement and add to requirement list.
456 reqList.add(
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000457 new BundleRequirementImpl(
Richard S. Hall1a879e52010-04-18 19:22:48 +0000458 owner,
Richard S. Hall2c0bf6b2011-06-07 18:00:08 +0000459 BundleRevision.PACKAGE_NAMESPACE,
Richard S. Hall9b940412011-07-20 18:19:02 +0000460 newDirs,
461 Collections.EMPTY_MAP,
462 sf));
Richard S. Hall1320d472010-03-03 14:58:21 +0000463 }
464 }
465
466 return reqList;
467 }
468
469 private static List<ParsedHeaderClause> normalizeDynamicImportClauses(
470 Logger logger, List<ParsedHeaderClause> clauses, String mv)
471 throws BundleException
472 {
473 // Verify that the values are equals if the package specifies
474 // both version and specification-version attributes.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000475 for (ParsedHeaderClause clause : clauses)
Richard S. Hall1320d472010-03-03 14:58:21 +0000476 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000477 if (!mv.equals("2"))
Richard S. Hall66ee2e62011-05-13 18:11:41 +0000478 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000479 // R3 bundles cannot have directives on their imports.
480 if (!clause.m_dirs.isEmpty())
481 {
482 throw new BundleException("R3 imports cannot contain directives.");
483 }
Richard S. Hall66ee2e62011-05-13 18:11:41 +0000484 }
485
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000486 // Add the resolution directive to indicate that these are
487 // dynamic imports.
488// TODO: OSGi R4.3 - Use real constant value for "dynamic".
489 clause.m_dirs.put(Constants.RESOLUTION_DIRECTIVE, "dynamic");
490
Richard S. Hall1320d472010-03-03 14:58:21 +0000491 // Check for "version" and "specification-version" attributes
492 // and verify they are the same if both are specified.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000493 Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE);
494 Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
Richard S. Hall1320d472010-03-03 14:58:21 +0000495 if ((v != null) && (sv != null))
496 {
497 // Verify they are equal.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000498 if (!((String) v).trim().equals(((String) sv).trim()))
Richard S. Hall1320d472010-03-03 14:58:21 +0000499 {
500 throw new IllegalArgumentException(
501 "Both version and specification-version are specified, but they are not equal.");
502 }
503 }
504
505 // Ensure that only the "version" attribute is used and convert
506 // it to the VersionRange type.
507 if ((v != null) || (sv != null))
508 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000509 clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
Richard S. Hall1320d472010-03-03 14:58:21 +0000510 v = (v == null) ? sv : v;
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000511 clause.m_attrs.put(
512 Constants.VERSION_ATTRIBUTE,
513 VersionRange.parse(v.toString()));
Richard S. Hall1320d472010-03-03 14:58:21 +0000514 }
515
516 // If bundle version is specified, then convert its type to VersionRange.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000517 v = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
Richard S. Hall1320d472010-03-03 14:58:21 +0000518 if (v != null)
519 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000520 clause.m_attrs.put(
521 Constants.BUNDLE_VERSION_ATTRIBUTE,
522 VersionRange.parse(v.toString()));
Richard S. Hall1320d472010-03-03 14:58:21 +0000523 }
Richard S. Hall1320d472010-03-03 14:58:21 +0000524
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000525 // Dynamic imports can have duplicates, so verify that java.*
526 // packages are not imported.
Richard S. Hall831b7712011-06-06 20:11:46 +0000527 for (String pkgName : clause.m_paths)
Richard S. Hall1320d472010-03-03 14:58:21 +0000528 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000529 if (pkgName.startsWith("java."))
530 {
531 throw new BundleException(
532 "Dynamically importing java.* packages not allowed: " + pkgName);
533 }
534 else if (!pkgName.equals("*") && pkgName.endsWith("*") && !pkgName.endsWith(".*"))
535 {
536 throw new BundleException(
537 "Partial package name wild carding is not allowed: " + pkgName);
538 }
539 }
540 }
541
Richard S. Hall1320d472010-03-03 14:58:21 +0000542 return clauses;
543 }
544
Richard S. Hall831b7712011-06-06 20:11:46 +0000545 private static List<ParsedHeaderClause> normalizeRequireCapabilityClauses(
546 Logger logger, List<ParsedHeaderClause> clauses, String mv)
547 throws BundleException
548 {
549
550 if (!mv.equals("2") && !clauses.isEmpty())
551 {
552 // Should we error here if we are not an R4 bundle?
553 }
554
555 return clauses;
556 }
557
558 private static List<BundleRequirement> convertRequireCapabilities(
559 List<ParsedHeaderClause> clauses, BundleRevision owner)
560 throws BundleException
561 {
562 // Now convert generic header clauses into requirements.
563 List reqList = new ArrayList();
564 for (ParsedHeaderClause clause : clauses)
565 {
566 try
567 {
Richard S. Hall9b940412011-07-20 18:19:02 +0000568 String filterStr = clause.m_dirs.get(Constants.FILTER_DIRECTIVE);
Richard S. Hall831b7712011-06-06 20:11:46 +0000569 SimpleFilter sf = (filterStr != null)
570 ? SimpleFilter.parse(filterStr)
571 : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
572 for (String path : clause.m_paths)
573 {
574 // Create requirement and add to requirement list.
575 reqList.add(
576 new BundleRequirementImpl(
577 owner,
578 path,
579 clause.m_dirs,
580 clause.m_attrs,
581 sf));
582 }
583 }
584 catch (Exception ex)
585 {
586 throw new BundleException("Error creating requirement: " + ex);
587 }
588 }
589
590 return reqList;
591 }
592
593 private static List<ParsedHeaderClause> normalizeProvideCapabilityClauses(
594 Logger logger, List<ParsedHeaderClause> clauses, String mv)
595 throws BundleException
596 {
597
598 if (!mv.equals("2") && !clauses.isEmpty())
599 {
600 // Should we error here if we are not an R4 bundle?
601 }
602
603 // Convert attributes into specified types.
604 for (ParsedHeaderClause clause : clauses)
605 {
606 for (Entry<String, String> entry : clause.m_types.entrySet())
607 {
608 String type = entry.getValue();
609 if (!type.equals("String"))
610 {
611 if (type.equals("Double"))
612 {
613 clause.m_attrs.put(
614 entry.getKey(),
615 new Double(clause.m_attrs.get(entry.getKey()).toString().trim()));
616 }
617 else if (type.equals("Version"))
618 {
619 clause.m_attrs.put(
620 entry.getKey(),
621 new Version(clause.m_attrs.get(entry.getKey()).toString().trim()));
622 }
623 else if (type.equals("Long"))
624 {
625 clause.m_attrs.put(
626 entry.getKey(),
627 new Long(clause.m_attrs.get(entry.getKey()).toString().trim()));
628 }
629 else if (type.startsWith("List"))
630 {
631 int startIdx = type.indexOf('<');
632 int endIdx = type.indexOf('>');
633 if (((startIdx > 0) && (endIdx <= startIdx))
634 || ((startIdx < 0) && (endIdx > 0)))
635 {
636 throw new BundleException(
637 "Invalid Provide-Capability attribute list type for '"
638 + entry.getKey()
639 + "' : "
640 + type);
641 }
642
643 String listType = "String";
644 if (endIdx > startIdx)
645 {
646 listType = type.substring(startIdx + 1, endIdx).trim();
647 }
648
649 List<String> tokens = parseDelimitedString(
650 clause.m_attrs.get(entry.getKey()).toString(), ",", false);
651 List<Object> values = new ArrayList<Object>(tokens.size());
652 for (String token : tokens)
653 {
654 if (listType.equals("String"))
655 {
656 values.add(token);
657 }
658 else if (listType.equals("Double"))
659 {
660 values.add(new Double(token.trim()));
661 }
662 else if (listType.equals("Version"))
663 {
664 values.add(new Version(token.trim()));
665 }
666 else if (listType.equals("Long"))
667 {
668 values.add(new Long(token.trim()));
669 }
670 else
671 {
672 throw new BundleException(
673 "Unknown Provide-Capability attribute list type for '"
674 + entry.getKey()
675 + "' : "
676 + type);
677 }
678 }
679 clause.m_attrs.put(
680 entry.getKey(),
681 values);
682 }
683 else
684 {
685 throw new BundleException(
686 "Unknown Provide-Capability attribute type for '"
687 + entry.getKey()
688 + "' : "
689 + type);
690 }
691 }
692 }
693 }
694
695 return clauses;
696 }
697
698 private static List<BundleCapability> convertProvideCapabilities(
699 List<ParsedHeaderClause> clauses, BundleRevision owner)
700 {
701 List<BundleCapability> capList = new ArrayList();
702 for (ParsedHeaderClause clause : clauses)
703 {
704 for (String path : clause.m_paths)
705 {
706 // Create package capability and add to capability list.
707 capList.add(
708 new BundleCapabilityImpl(
709 owner,
710 path,
711 clause.m_dirs,
712 clause.m_attrs));
713 }
714 }
715
716 return capList;
717 }
718
Richard S. Hall1320d472010-03-03 14:58:21 +0000719 private static List<ParsedHeaderClause> normalizeExportClauses(
720 Logger logger, List<ParsedHeaderClause> clauses,
721 String mv, String bsn, Version bv)
722 throws BundleException
723 {
724 // Verify that "java.*" packages are not exported.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000725 for (ParsedHeaderClause clause : clauses)
Richard S. Hall1320d472010-03-03 14:58:21 +0000726 {
727 // Verify that the named package has not already been declared.
Richard S. Hall831b7712011-06-06 20:11:46 +0000728 for (String pkgName : clause.m_paths)
Richard S. Hall1320d472010-03-03 14:58:21 +0000729 {
730 // Verify that java.* packages are not exported.
Richard S. Hall831b7712011-06-06 20:11:46 +0000731 if (pkgName.startsWith("java."))
Richard S. Hall1320d472010-03-03 14:58:21 +0000732 {
733 throw new BundleException(
734 "Exporting java.* packages not allowed: "
Richard S. Hall831b7712011-06-06 20:11:46 +0000735 + pkgName);
Richard S. Hall1320d472010-03-03 14:58:21 +0000736 }
Richard S. Hall831b7712011-06-06 20:11:46 +0000737 else if (pkgName.length() == 0)
Richard S. Hall1320d472010-03-03 14:58:21 +0000738 {
739 throw new BundleException(
740 "Exported package names cannot be zero length.");
741 }
742 }
Richard S. Hall1320d472010-03-03 14:58:21 +0000743
Richard S. Hall1320d472010-03-03 14:58:21 +0000744 // Check for "version" and "specification-version" attributes
745 // and verify they are the same if both are specified.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000746 Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE);
747 Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
Richard S. Hall1320d472010-03-03 14:58:21 +0000748 if ((v != null) && (sv != null))
749 {
750 // Verify they are equal.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000751 if (!((String) v).trim().equals(((String) sv).trim()))
Richard S. Hall1320d472010-03-03 14:58:21 +0000752 {
753 throw new IllegalArgumentException(
754 "Both version and specification-version are specified, but they are not equal.");
755 }
756 }
757
758 // Always add the default version if not specified.
759 if ((v == null) && (sv == null))
760 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000761 v = Version.emptyVersion;
Richard S. Hall1320d472010-03-03 14:58:21 +0000762 }
763
764 // Ensure that only the "version" attribute is used and convert
765 // it to the appropriate type.
766 if ((v != null) || (sv != null))
767 {
768 // Convert version attribute to type Version.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000769 clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
Richard S. Hall1320d472010-03-03 14:58:21 +0000770 v = (v == null) ? sv : v;
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000771 clause.m_attrs.put(
772 Constants.VERSION_ATTRIBUTE,
773 Version.parseVersion(v.toString()));
Karl Pauls9dd22332011-05-16 21:49:02 +0000774 }
Karl Pauls9dd22332011-05-16 21:49:02 +0000775
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000776 // If this is an R4 bundle, then make sure it doesn't specify
777 // bundle symbolic name or bundle version attributes.
778 if (mv.equals("2"))
Richard S. Hall1320d472010-03-03 14:58:21 +0000779 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000780 // Find symbolic name and version attribute, if present.
781 if (clause.m_attrs.containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE)
782 || clause.m_attrs.containsKey(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
Richard S. Hall1320d472010-03-03 14:58:21 +0000783 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000784 throw new BundleException(
785 "Exports must not specify bundle symbolic name or bundle version.");
Richard S. Hall1320d472010-03-03 14:58:21 +0000786 }
787
788 // Now that we know that there are no bundle symbolic name and version
789 // attributes, add them since the spec says they are there implicitly.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000790 clause.m_attrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, bsn);
791 clause.m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bv);
Richard S. Hall1320d472010-03-03 14:58:21 +0000792 }
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000793 else if (!mv.equals("2"))
Richard S. Hall1320d472010-03-03 14:58:21 +0000794 {
795 // R3 bundles cannot have directives on their exports.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000796 if (!clause.m_dirs.isEmpty())
Richard S. Hall1320d472010-03-03 14:58:21 +0000797 {
798 throw new BundleException("R3 exports cannot contain directives.");
799 }
800
801 // Remove and ignore all attributes other than version.
802 // NOTE: This is checking for "version" rather than "specification-version"
803 // because the package class normalizes to "version" to avoid having
804 // future special cases. This could be changed if more strict behavior
805 // is required.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000806 if (!clause.m_attrs.isEmpty())
Richard S. Hall1320d472010-03-03 14:58:21 +0000807 {
808 // R3 package capabilities should only have a version attribute.
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000809 Object pkgVersion = clause.m_attrs.get(BundleCapabilityImpl.VERSION_ATTR);
810 pkgVersion = (pkgVersion == null)
811 ? Version.emptyVersion
812 : pkgVersion;
813 for (Entry<String, Object> entry : clause.m_attrs.entrySet())
Richard S. Hall1320d472010-03-03 14:58:21 +0000814 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000815 if (!entry.getKey().equals(BundleCapabilityImpl.VERSION_ATTR))
Richard S. Hall1320d472010-03-03 14:58:21 +0000816 {
817 logger.log(
818 Logger.LOG_WARNING,
819 "Unknown R3 export attribute: "
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000820 + entry.getKey());
Richard S. Hall1320d472010-03-03 14:58:21 +0000821 }
822 }
823
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000824 // Remove all other attributes except package version.
825 clause.m_attrs.clear();
826 clause.m_attrs.put(BundleCapabilityImpl.VERSION_ATTR, pkgVersion);
Richard S. Hall1320d472010-03-03 14:58:21 +0000827 }
828 }
829 }
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000830
Richard S. Hall1320d472010-03-03 14:58:21 +0000831 return clauses;
Richard S. Halla2878c12006-07-21 14:50:07 +0000832 }
833
Richard S. Hall831b7712011-06-06 20:11:46 +0000834 private static List<BundleCapability> convertExports(
835 List<ParsedHeaderClause> clauses, BundleRevision owner)
836 {
837 List<BundleCapability> capList = new ArrayList();
838 for (ParsedHeaderClause clause : clauses)
839 {
840 for (String pkgName : clause.m_paths)
841 {
842 // Prepend the package name to the array of attributes.
843 Map<String, Object> attrs = clause.m_attrs;
844 Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
Richard S. Hall9b940412011-07-20 18:19:02 +0000845 newAttrs.putAll(attrs);
Richard S. Hall831b7712011-06-06 20:11:46 +0000846 newAttrs.put(
Richard S. Hallc4e75992011-06-15 18:57:20 +0000847 BundleRevision.PACKAGE_NAMESPACE,
Richard S. Hall831b7712011-06-06 20:11:46 +0000848 pkgName);
Richard S. Hall831b7712011-06-06 20:11:46 +0000849
850 // Create package capability and add to capability list.
851 capList.add(
852 new BundleCapabilityImpl(
853 owner,
Richard S. Hall2c0bf6b2011-06-07 18:00:08 +0000854 BundleRevision.PACKAGE_NAMESPACE,
Richard S. Hall831b7712011-06-06 20:11:46 +0000855 clause.m_dirs,
856 newAttrs));
857 }
858 }
859
860 return capList;
861 }
862
Richard S. Hall56b11942006-10-26 14:40:07 +0000863 public String getManifestVersion()
Richard S. Halla2878c12006-07-21 14:50:07 +0000864 {
Richard S. Hall1563de22009-10-22 16:46:48 +0000865 String manifestVersion = getManifestVersion(m_headerMap);
866 return (manifestVersion == null) ? "1" : manifestVersion;
867 }
868
869 private static String getManifestVersion(Map headerMap)
870 {
871 String manifestVersion = (String) headerMap.get(Constants.BUNDLE_MANIFESTVERSION);
872 return (manifestVersion == null) ? null : manifestVersion.trim();
Richard S. Halla2878c12006-07-21 14:50:07 +0000873 }
874
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +0000875 public int getActivationPolicy()
876 {
877 return m_activationPolicy;
878 }
879
Richard S. Hall10d9d2c2009-06-12 20:21:59 +0000880 public String getActivationIncludeDirective()
881 {
882 return m_activationIncludeDir;
883 }
884
885 public String getActivationExcludeDirective()
886 {
887 return m_activationExcludeDir;
888 }
889
Karl Pauls71294432009-03-16 23:34:01 +0000890 public boolean isExtension()
891 {
892 return m_isExtension;
893 }
894
Richard S. Hall56b11942006-10-26 14:40:07 +0000895 public String getSymbolicName()
Richard S. Halla2878c12006-07-21 14:50:07 +0000896 {
Richard S. Hall56b11942006-10-26 14:40:07 +0000897 return m_bundleSymbolicName;
898 }
899
900 public Version getBundleVersion()
901 {
902 return m_bundleVersion;
Richard S. Halla2878c12006-07-21 14:50:07 +0000903 }
904
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000905 public List<BundleCapability> getCapabilities()
Richard S. Halla2878c12006-07-21 14:50:07 +0000906 {
Richard S. Hall862eca12007-01-22 07:09:25 +0000907 return m_capabilities;
Richard S. Halla2878c12006-07-21 14:50:07 +0000908 }
909
Richard S. Hall4ecc6772011-05-17 21:20:32 +0000910 public List<BundleRequirement> getRequirements()
Richard S. Halla2878c12006-07-21 14:50:07 +0000911 {
Richard S. Hall862eca12007-01-22 07:09:25 +0000912 return m_requirements;
Richard S. Halla2878c12006-07-21 14:50:07 +0000913 }
914
Richard S. Hall1320d472010-03-03 14:58:21 +0000915 public List<R4LibraryClause> getLibraryClauses()
Richard S. Halla2878c12006-07-21 14:50:07 +0000916 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000917 return m_libraryClauses;
Richard S. Halla2878c12006-07-21 14:50:07 +0000918 }
919
Richard S. Hall8d116d42006-09-01 19:23:28 +0000920 /**
921 * <p>
922 * This method returns the selected native library metadata from
923 * the manifest. The information is not the raw metadata from the
Richard S. Hall6bae3392009-07-15 15:43:44 +0000924 * manifest, but is the native library clause selected according
Richard S. Hall8d116d42006-09-01 19:23:28 +0000925 * to the OSGi native library clause selection policy. The metadata
926 * returned by this method will be attached directly to a module and
927 * used for finding its native libraries at run time. To inspect the
928 * raw native library metadata refer to <tt>getLibraryClauses()</tt>.
929 * </p>
Richard S. Hall6bae3392009-07-15 15:43:44 +0000930 * <p>
931 * This method returns one of three values:
932 * </p>
933 * <ul>
934 * <li><tt>null</tt> - if the are no native libraries for this module;
935 * this may also indicate the native libraries are optional and
936 * did not match the current platform.</li>
937 * <li>Zero-length <tt>R4Library</tt> array - if no matching native library
938 * clause was found; this bundle should not resolve.</li>
939 * <li>Nonzero-length <tt>R4Library</tt> array - the native libraries
940 * associated with the matching native library clause.</li>
941 * </ul>
942 *
943 * @return <tt>null</tt> if there are no native libraries, a zero-length
944 * array if no libraries matched, or an array of selected libraries.
Richard S. Hall5eda19c2008-02-29 19:52:53 +0000945 **/
Richard S. Hall1320d472010-03-03 14:58:21 +0000946 public List<R4Library> getLibraries()
Richard S. Halla2878c12006-07-21 14:50:07 +0000947 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000948 ArrayList<R4Library> libs = null;
Richard S. Hall6bae3392009-07-15 15:43:44 +0000949 try
Richard S. Halla2878c12006-07-21 14:50:07 +0000950 {
Richard S. Hall6bae3392009-07-15 15:43:44 +0000951 R4LibraryClause clause = getSelectedLibraryClause();
952 if (clause != null)
Richard S. Hall8d116d42006-09-01 19:23:28 +0000953 {
Richard S. Hall6bae3392009-07-15 15:43:44 +0000954 String[] entries = clause.getLibraryEntries();
Richard S. Hall1320d472010-03-03 14:58:21 +0000955 libs = new ArrayList<R4Library>(entries.length);
Richard S. Hall6bae3392009-07-15 15:43:44 +0000956 int current = 0;
Richard S. Hall1320d472010-03-03 14:58:21 +0000957 for (int i = 0; i < entries.length; i++)
Karl Paulsfb846a42007-12-18 22:39:00 +0000958 {
Richard S. Hall5eda19c2008-02-29 19:52:53 +0000959 String name = getName(entries[i]);
Karl Paulsfb846a42007-12-18 22:39:00 +0000960 boolean found = false;
961 for (int j = 0; !found && (j < current); j++)
962 {
Richard S. Hall5eda19c2008-02-29 19:52:53 +0000963 found = getName(entries[j]).equals(name);
Karl Paulsfb846a42007-12-18 22:39:00 +0000964 }
965 if (!found)
966 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000967 libs.add(new R4Library(
Richard S. Hall5eda19c2008-02-29 19:52:53 +0000968 clause.getLibraryEntries()[i],
Karl Paulsfb846a42007-12-18 22:39:00 +0000969 clause.getOSNames(), clause.getProcessors(), clause.getOSVersions(),
Richard S. Hall1320d472010-03-03 14:58:21 +0000970 clause.getLanguages(), clause.getSelectionFilter()));
Carsten Ziegelerfc8029a2008-05-14 16:26:03 +0000971 }
Karl Paulsfb846a42007-12-18 22:39:00 +0000972 }
Richard S. Hall1320d472010-03-03 14:58:21 +0000973 libs.trimToSize();
Karl Paulsfb846a42007-12-18 22:39:00 +0000974 }
Richard S. Halla2878c12006-07-21 14:50:07 +0000975 }
Richard S. Hall6bae3392009-07-15 15:43:44 +0000976 catch (Exception ex)
977 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000978 libs = new ArrayList<R4Library>(0);
Richard S. Hall6bae3392009-07-15 15:43:44 +0000979 }
980 return libs;
Richard S. Hall8d116d42006-09-01 19:23:28 +0000981 }
982
Karl Paulsfb846a42007-12-18 22:39:00 +0000983 private String getName(String path)
984 {
985 int idx = path.lastIndexOf('/');
986 if (idx > -1)
987 {
988 return path.substring(idx);
989 }
990 return path;
991 }
992
Richard S. Hall8d116d42006-09-01 19:23:28 +0000993 private R4LibraryClause getSelectedLibraryClause() throws BundleException
994 {
Richard S. Hall1320d472010-03-03 14:58:21 +0000995 if ((m_libraryClauses != null) && (m_libraryClauses.size() > 0))
Richard S. Hall8d116d42006-09-01 19:23:28 +0000996 {
997 List clauseList = new ArrayList();
998
999 // Search for matching native clauses.
Richard S. Hall831b7712011-06-06 20:11:46 +00001000 for (R4LibraryClause libraryClause : m_libraryClauses)
Richard S. Hall8d116d42006-09-01 19:23:28 +00001001 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001002 if (libraryClause.match(m_configMap))
Richard S. Hall8d116d42006-09-01 19:23:28 +00001003 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001004 clauseList.add(libraryClause);
Richard S. Hall8d116d42006-09-01 19:23:28 +00001005 }
1006 }
1007
1008 // Select the matching native clause.
1009 int selected = 0;
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001010 if (clauseList.isEmpty())
Richard S. Hall8d116d42006-09-01 19:23:28 +00001011 {
1012 // If optional clause exists, no error thrown.
1013 if (m_libraryHeadersOptional)
1014 {
1015 return null;
1016 }
1017 else
1018 {
1019 throw new BundleException("Unable to select a native library clause.");
1020 }
1021 }
1022 else if (clauseList.size() == 1)
1023 {
1024 selected = 0;
1025 }
1026 else if (clauseList.size() > 1)
1027 {
1028 selected = firstSortedClause(clauseList);
1029 }
1030 return ((R4LibraryClause) clauseList.get(selected));
1031 }
1032
1033 return null;
1034 }
1035
Richard S. Hall831b7712011-06-06 20:11:46 +00001036 private int firstSortedClause(List<R4LibraryClause> clauseList)
Richard S. Hall8d116d42006-09-01 19:23:28 +00001037 {
1038 ArrayList indexList = new ArrayList();
1039 ArrayList selection = new ArrayList();
1040
1041 // Init index list
1042 for (int i = 0; i < clauseList.size(); i++)
1043 {
1044 indexList.add("" + i);
1045 }
1046
1047 // Select clause with 'osversion' range declared
1048 // and get back the max floor of 'osversion' ranges.
1049 Version osVersionRangeMaxFloor = new Version(0, 0, 0);
1050 for (int i = 0; i < indexList.size(); i++)
1051 {
1052 int index = Integer.parseInt(indexList.get(i).toString());
1053 String[] osversions = ((R4LibraryClause) clauseList.get(index)).getOSVersions();
1054 if (osversions != null)
1055 {
1056 selection.add("" + indexList.get(i));
1057 }
1058 for (int k = 0; (osversions != null) && (k < osversions.length); k++)
1059 {
1060 VersionRange range = VersionRange.parse(osversions[k]);
Richard S. Hall1320d472010-03-03 14:58:21 +00001061 if ((range.getFloor()).compareTo(osVersionRangeMaxFloor) >= 0)
Richard S. Hall8d116d42006-09-01 19:23:28 +00001062 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001063 osVersionRangeMaxFloor = range.getFloor();
Richard S. Hall8d116d42006-09-01 19:23:28 +00001064 }
1065 }
1066 }
1067
1068 if (selection.size() == 1)
1069 {
1070 return Integer.parseInt(selection.get(0).toString());
1071 }
1072 else if (selection.size() > 1)
1073 {
1074 // Keep only selected clauses with an 'osversion'
1075 // equal to the max floor of 'osversion' ranges.
1076 indexList = selection;
1077 selection = new ArrayList();
1078 for (int i = 0; i < indexList.size(); i++)
1079 {
1080 int index = Integer.parseInt(indexList.get(i).toString());
1081 String[] osversions = ((R4LibraryClause) clauseList.get(index)).getOSVersions();
1082 for (int k = 0; k < osversions.length; k++)
1083 {
1084 VersionRange range = VersionRange.parse(osversions[k]);
Richard S. Hall1320d472010-03-03 14:58:21 +00001085 if ((range.getFloor()).compareTo(osVersionRangeMaxFloor) >= 0)
Richard S. Hall8d116d42006-09-01 19:23:28 +00001086 {
1087 selection.add("" + indexList.get(i));
1088 }
1089 }
1090 }
1091 }
1092
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001093 if (selection.isEmpty())
Richard S. Hall8d116d42006-09-01 19:23:28 +00001094 {
1095 // Re-init index list.
1096 selection.clear();
1097 indexList.clear();
1098 for (int i = 0; i < clauseList.size(); i++)
1099 {
1100 indexList.add("" + i);
1101 }
1102 }
1103 else if (selection.size() == 1)
1104 {
1105 return Integer.parseInt(selection.get(0).toString());
1106 }
1107 else
1108 {
1109 indexList = selection;
1110 selection.clear();
1111 }
1112
1113 // Keep only clauses with 'language' declared.
1114 for (int i = 0; i < indexList.size(); i++)
1115 {
1116 int index = Integer.parseInt(indexList.get(i).toString());
1117 if (((R4LibraryClause) clauseList.get(index)).getLanguages() != null)
1118 {
1119 selection.add("" + indexList.get(i));
1120 }
1121 }
1122
1123 // Return the first sorted clause
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001124 if (selection.isEmpty())
Richard S. Hall8d116d42006-09-01 19:23:28 +00001125 {
1126 return 0;
1127 }
1128 else
1129 {
1130 return Integer.parseInt(selection.get(0).toString());
1131 }
Richard S. Halla2878c12006-07-21 14:50:07 +00001132 }
1133
Richard S. Hall1320d472010-03-03 14:58:21 +00001134 private static List<ParsedHeaderClause> calculateImplicitImports(
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001135 List<BundleCapability> exports, List<ParsedHeaderClause> imports)
Richard S. Hall1320d472010-03-03 14:58:21 +00001136 throws BundleException
Richard S. Halla2878c12006-07-21 14:50:07 +00001137 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001138 List<ParsedHeaderClause> clauseList = new ArrayList();
Richard S. Halla2878c12006-07-21 14:50:07 +00001139
1140 // Since all R3 exports imply an import, add a corresponding
Richard S. Hall862eca12007-01-22 07:09:25 +00001141 // requirement for each existing export capability. Do not
1142 // duplicate imports.
Richard S. Halla2878c12006-07-21 14:50:07 +00001143 Map map = new HashMap();
1144 // Add existing imports.
Richard S. Hall1320d472010-03-03 14:58:21 +00001145 for (int impIdx = 0; impIdx < imports.size(); impIdx++)
Richard S. Halla2878c12006-07-21 14:50:07 +00001146 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001147 for (int pathIdx = 0; pathIdx < imports.get(impIdx).m_paths.size(); pathIdx++)
Richard S. Hall862eca12007-01-22 07:09:25 +00001148 {
1149 map.put(
Richard S. Hall1320d472010-03-03 14:58:21 +00001150 imports.get(impIdx).m_paths.get(pathIdx),
1151 imports.get(impIdx).m_paths.get(pathIdx));
Richard S. Hall862eca12007-01-22 07:09:25 +00001152 }
Richard S. Halla2878c12006-07-21 14:50:07 +00001153 }
Richard S. Hall862eca12007-01-22 07:09:25 +00001154 // Add import requirement for each export capability.
Richard S. Hall1320d472010-03-03 14:58:21 +00001155 for (int i = 0; i < exports.size(); i++)
Richard S. Halla2878c12006-07-21 14:50:07 +00001156 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001157 if (map.get(exports.get(i).getAttributes()
Richard S. Hallc4e75992011-06-15 18:57:20 +00001158 .get(BundleRevision.PACKAGE_NAMESPACE)) == null)
Richard S. Halla2878c12006-07-21 14:50:07 +00001159 {
Richard S. Halla5158a22006-12-13 21:20:48 +00001160 // Convert Version to VersionRange.
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001161 Map<String, Object> attrs = new HashMap<String, Object>();
1162 Object version = exports.get(i).getAttributes().get(Constants.VERSION_ATTRIBUTE);
1163 if (version != null)
Richard S. Halla5158a22006-12-13 21:20:48 +00001164 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001165 attrs.put(
1166 Constants.VERSION_ATTRIBUTE,
1167 VersionRange.parse(version.toString()));
Richard S. Halla5158a22006-12-13 21:20:48 +00001168 }
Richard S. Hall862eca12007-01-22 07:09:25 +00001169
Richard S. Hall1320d472010-03-03 14:58:21 +00001170 List<String> paths = new ArrayList();
1171 paths.add((String)
Richard S. Hallc4e75992011-06-15 18:57:20 +00001172 exports.get(i).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
Richard S. Hall1320d472010-03-03 14:58:21 +00001173 clauseList.add(
Richard S. Hall831b7712011-06-06 20:11:46 +00001174 new ParsedHeaderClause(
1175 paths, Collections.EMPTY_MAP, attrs, Collections.EMPTY_MAP));
Richard S. Halla2878c12006-07-21 14:50:07 +00001176 }
1177 }
Richard S. Halla2878c12006-07-21 14:50:07 +00001178
Richard S. Hall1320d472010-03-03 14:58:21 +00001179 return clauseList;
1180 }
1181
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001182 private static List<BundleCapability> calculateImplicitUses(
1183 List<BundleCapability> exports, List<ParsedHeaderClause> imports)
Richard S. Hall1320d472010-03-03 14:58:21 +00001184 throws BundleException
1185 {
Richard S. Halla2878c12006-07-21 14:50:07 +00001186 // Add a "uses" directive onto each export of R3 bundles
1187 // that references every other import (which will include
1188 // exports, since export implies import); this is
1189 // necessary since R3 bundles assumed a single class space,
1190 // but R4 allows for multiple class spaces.
1191 String usesValue = "";
Richard S. Hall1320d472010-03-03 14:58:21 +00001192 for (int i = 0; i < imports.size(); i++)
Richard S. Halla2878c12006-07-21 14:50:07 +00001193 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001194 for (int pathIdx = 0; pathIdx < imports.get(i).m_paths.size(); pathIdx++)
Richard S. Hall862eca12007-01-22 07:09:25 +00001195 {
1196 usesValue = usesValue
1197 + ((usesValue.length() > 0) ? "," : "")
Richard S. Hall1320d472010-03-03 14:58:21 +00001198 + imports.get(i).m_paths.get(pathIdx);
Richard S. Hall862eca12007-01-22 07:09:25 +00001199 }
Richard S. Halla2878c12006-07-21 14:50:07 +00001200 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001201 for (int i = 0; i < exports.size(); i++)
Richard S. Halla2878c12006-07-21 14:50:07 +00001202 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001203 Map<String, String> dirs = new HashMap<String, String>(1);
1204 dirs.put(Constants.USES_DIRECTIVE, usesValue);
1205 exports.set(i, new BundleCapabilityImpl(
1206 exports.get(i).getRevision(),
Richard S. Hall2c0bf6b2011-06-07 18:00:08 +00001207 BundleRevision.PACKAGE_NAMESPACE,
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001208 dirs,
Richard S. Hall1320d472010-03-03 14:58:21 +00001209 exports.get(i).getAttributes()));
Richard S. Halla2878c12006-07-21 14:50:07 +00001210 }
1211
Richard S. Hall1320d472010-03-03 14:58:21 +00001212 return exports;
Richard S. Halla2878c12006-07-21 14:50:07 +00001213 }
1214
Richard S. Hall1320d472010-03-03 14:58:21 +00001215 private static boolean checkExtensionBundle(Map headerMap) throws BundleException
Richard S. Halla2878c12006-07-21 14:50:07 +00001216 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001217 Object extension = parseExtensionBundleHeader(
Richard S. Hall1320d472010-03-03 14:58:21 +00001218 (String) headerMap.get(Constants.FRAGMENT_HOST));
Karl Pauls71294432009-03-16 23:34:01 +00001219
Karl Paulsa2ecacc2008-10-21 21:56:31 +00001220 if (extension != null)
Karl Pauls210a2142007-02-12 23:37:08 +00001221 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001222 if (!(Constants.EXTENSION_FRAMEWORK.equals(extension) ||
1223 Constants.EXTENSION_BOOTCLASSPATH.equals(extension)))
Karl Paulsa2ecacc2008-10-21 21:56:31 +00001224 {
1225 throw new BundleException(
1226 "Extension bundle must have either 'extension:=framework' or 'extension:=bootclasspath'");
1227 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001228 if (headerMap.containsKey(Constants.IMPORT_PACKAGE) ||
1229 headerMap.containsKey(Constants.REQUIRE_BUNDLE) ||
1230 headerMap.containsKey(Constants.BUNDLE_NATIVECODE) ||
1231 headerMap.containsKey(Constants.DYNAMICIMPORT_PACKAGE) ||
1232 headerMap.containsKey(Constants.BUNDLE_ACTIVATOR))
Richard S. Hall436c0fb2009-05-13 20:34:42 +00001233 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001234 throw new BundleException("Invalid extension bundle manifest");
Richard S. Hall436c0fb2009-05-13 20:34:42 +00001235 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001236 return true;
Richard S. Hall436c0fb2009-05-13 20:34:42 +00001237 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001238 return false;
Richard S. Hall436c0fb2009-05-13 20:34:42 +00001239 }
1240
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001241 private static BundleCapabilityImpl parseBundleSymbolicName(
1242 BundleRevision owner, Map headerMap)
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001243 throws BundleException
1244 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001245 List<ParsedHeaderClause> clauses = parseStandardHeader(
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001246 (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
Richard S. Hall1320d472010-03-03 14:58:21 +00001247 if (clauses.size() > 0)
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001248 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001249 if (clauses.size() > 1)
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001250 {
1251 throw new BundleException(
1252 "Cannot have multiple symbolic names: "
1253 + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
1254 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001255 else if (clauses.get(0).m_paths.size() > 1)
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001256 {
1257 throw new BundleException(
1258 "Cannot have multiple symbolic names: "
1259 + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
1260 }
1261
1262 // Get bundle version.
Richard S. Hall7561a372009-01-27 19:34:12 +00001263 Version bundleVersion = Version.emptyVersion;
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001264 if (headerMap.get(Constants.BUNDLE_VERSION) != null)
1265 {
1266 try
1267 {
Richard S. Hall7d5669c2009-10-20 20:31:12 +00001268 bundleVersion = Version.parseVersion(
1269 (String) headerMap.get(Constants.BUNDLE_VERSION));
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001270 }
1271 catch (RuntimeException ex)
1272 {
1273 // R4 bundle versions must parse, R3 bundle version may not.
Richard S. Hall1563de22009-10-22 16:46:48 +00001274 String mv = getManifestVersion(headerMap);
1275 if (mv != null)
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001276 {
1277 throw ex;
1278 }
1279 bundleVersion = Version.emptyVersion;
1280 }
1281 }
1282
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001283 // Create a require capability and return it.
Richard S. Hall1320d472010-03-03 14:58:21 +00001284 String symName = (String) clauses.get(0).m_paths.get(0);
Richard S. Hallaa7f1002011-07-06 14:58:47 +00001285 clauses.get(0).m_attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
1286 clauses.get(0).m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001287 return new BundleCapabilityImpl(
Richard S. Hall7d5669c2009-10-20 20:31:12 +00001288 owner,
Richard S. Hall2c0bf6b2011-06-07 18:00:08 +00001289 BundleRevision.BUNDLE_NAMESPACE,
Richard S. Hall1320d472010-03-03 14:58:21 +00001290 clauses.get(0).m_dirs,
Richard S. Hallaa7f1002011-07-06 14:58:47 +00001291 clauses.get(0).m_attrs);
Richard S. Hall9802e3f2008-07-07 01:37:22 +00001292 }
1293
1294 return null;
1295 }
1296
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001297 private static List<BundleRequirementImpl> parseFragmentHost(
1298 Logger logger, BundleRevision owner, Map headerMap)
Richard S. Hall1563de22009-10-22 16:46:48 +00001299 throws BundleException
1300 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001301 List<BundleRequirementImpl> reqs = new ArrayList();
Richard S. Hall1563de22009-10-22 16:46:48 +00001302
1303 String mv = getManifestVersion(headerMap);
1304 if ((mv != null) && mv.equals("2"))
1305 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001306 List<ParsedHeaderClause> clauses = parseStandardHeader(
Richard S. Hall1563de22009-10-22 16:46:48 +00001307 (String) headerMap.get(Constants.FRAGMENT_HOST));
Richard S. Hall1320d472010-03-03 14:58:21 +00001308 if (clauses.size() > 0)
Richard S. Hall1563de22009-10-22 16:46:48 +00001309 {
1310 // Make sure that only one fragment host symbolic name is specified.
Richard S. Hall1320d472010-03-03 14:58:21 +00001311 if (clauses.size() > 1)
Richard S. Hall1563de22009-10-22 16:46:48 +00001312 {
1313 throw new BundleException(
1314 "Fragments cannot have multiple hosts: "
1315 + headerMap.get(Constants.FRAGMENT_HOST));
1316 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001317 else if (clauses.get(0).m_paths.size() > 1)
Richard S. Hall1563de22009-10-22 16:46:48 +00001318 {
1319 throw new BundleException(
1320 "Fragments cannot have multiple hosts: "
1321 + headerMap.get(Constants.FRAGMENT_HOST));
1322 }
1323
Richard S. Hall1172e832010-07-09 14:14:15 +00001324 // Strip all attributes other than bundle-version.
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001325 for (Iterator<Entry<String, Object>> it =
1326 clauses.get(0).m_attrs.entrySet().iterator();
1327 it.hasNext(); )
Richard S. Hall1563de22009-10-22 16:46:48 +00001328 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001329 Entry<String, Object> entry = it.next();
1330 if (!entry.getKey().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
Richard S. Hall1563de22009-10-22 16:46:48 +00001331 {
Richard S. Hall1172e832010-07-09 14:14:15 +00001332 it.remove();
Richard S. Hall1563de22009-10-22 16:46:48 +00001333 }
1334 }
1335
Richard S. Hall1172e832010-07-09 14:14:15 +00001336 // If the bundle-version attribute is specified, then convert
1337 // it to the proper type.
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001338 Object value = clauses.get(0).m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
1339 if (value != null)
Richard S. Hall1172e832010-07-09 14:14:15 +00001340 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001341 clauses.get(0).m_attrs.put(
1342 Constants.BUNDLE_VERSION_ATTRIBUTE,
1343 VersionRange.parse(value.toString()));
Richard S. Hall1172e832010-07-09 14:14:15 +00001344 }
1345
Richard S. Hall1563de22009-10-22 16:46:48 +00001346 // Prepend the host symbolic name to the array of attributes.
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001347 Map<String, Object> attrs = clauses.get(0).m_attrs;
1348 Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
Richard S. Hall9b940412011-07-20 18:19:02 +00001349 newAttrs.putAll(attrs);
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001350 newAttrs.put(
Richard S. Hallc4e75992011-06-15 18:57:20 +00001351 BundleRevision.HOST_NAMESPACE,
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001352 clauses.get(0).m_paths.get(0));
Richard S. Hall9b940412011-07-20 18:19:02 +00001353
1354 // Create filter now so we can inject filter directive.
1355 SimpleFilter sf = BundleRequirementImpl.convertToFilter(newAttrs);
1356
1357 // Inject filter directive.
1358// TODO: OSGi R4.3 - Can we insert this on demand somehow?
1359 Map<String, String> dirs = clauses.get(0).m_dirs;
1360 Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
1361 newDirs.putAll(dirs);
1362 newDirs.put(
1363 Constants.FILTER_DIRECTIVE,
1364 sf.toString());
Richard S. Hall1563de22009-10-22 16:46:48 +00001365
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001366 reqs.add(new BundleRequirementImpl(
Richard S. Hall2c0bf6b2011-06-07 18:00:08 +00001367 owner, BundleRevision.HOST_NAMESPACE,
Richard S. Hall9b940412011-07-20 18:19:02 +00001368 newDirs,
Richard S. Hall1320d472010-03-03 14:58:21 +00001369 newAttrs));
Richard S. Hall1563de22009-10-22 16:46:48 +00001370 }
1371 }
Richard S. Hallc3a17b72010-03-04 17:19:49 +00001372 else if (headerMap.get(Constants.FRAGMENT_HOST) != null)
Richard S. Hall1563de22009-10-22 16:46:48 +00001373 {
Richard S. Hallc4cca302010-03-04 17:12:48 +00001374 String s = (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME);
1375 s = (s == null) ? (String) headerMap.get(Constants.BUNDLE_NAME) : s;
1376 s = (s == null) ? headerMap.toString() : s;
Richard S. Hall4a546d02010-09-15 20:52:08 +00001377 logger.log(
1378 owner.getBundle(),
1379 Logger.LOG_WARNING,
1380 "Only R4 bundles can be fragments: " + s);
Richard S. Hall1563de22009-10-22 16:46:48 +00001381 }
1382
Richard S. Hall1320d472010-03-03 14:58:21 +00001383 return reqs;
Richard S. Hall1563de22009-10-22 16:46:48 +00001384 }
1385
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001386 public static List<BundleCapability> parseExportHeader(
1387 Logger logger, BundleRevision owner, String header, String bsn, Version bv)
Richard S. Hall436c0fb2009-05-13 20:34:42 +00001388 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001389
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001390 List<BundleCapability> caps = null;
Richard S. Hall436c0fb2009-05-13 20:34:42 +00001391 try
1392 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001393 List<ParsedHeaderClause> exportClauses = parseStandardHeader(header);
1394 exportClauses = normalizeExportClauses(logger, exportClauses, "2", bsn, bv);
1395 caps = convertExports(exportClauses, owner);
Richard S. Hall436c0fb2009-05-13 20:34:42 +00001396 }
1397 catch (BundleException ex)
1398 {
1399 caps = null;
1400 }
1401 return caps;
1402 }
1403
Richard S. Hall1320d472010-03-03 14:58:21 +00001404 private static List<ParsedHeaderClause> normalizeRequireClauses(
1405 Logger logger, List<ParsedHeaderClause> clauses, String mv)
Richard S. Hall4151e902006-12-27 16:11:16 +00001406 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001407 // R3 bundles cannot require other bundles.
1408 if (!mv.equals("2"))
Richard S. Hall4151e902006-12-27 16:11:16 +00001409 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001410 clauses.clear();
Richard S. Hall4151e902006-12-27 16:11:16 +00001411 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001412 else
Richard S. Hall4151e902006-12-27 16:11:16 +00001413 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001414 // Convert bundle version attribute to VersionRange type.
Richard S. Hall831b7712011-06-06 20:11:46 +00001415 for (ParsedHeaderClause clause : clauses)
Richard S. Hall4151e902006-12-27 16:11:16 +00001416 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001417 Object value = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001418 if (value != null)
Richard S. Hallfa780572009-01-23 16:01:53 +00001419 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001420 clause.m_attrs.put(
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001421 Constants.BUNDLE_VERSION_ATTRIBUTE,
1422 VersionRange.parse(value.toString()));
Richard S. Hallfa780572009-01-23 16:01:53 +00001423 }
Richard S. Hall4151e902006-12-27 16:11:16 +00001424 }
1425 }
1426
Richard S. Hall1320d472010-03-03 14:58:21 +00001427 return clauses;
Richard S. Hall4151e902006-12-27 16:11:16 +00001428 }
1429
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001430 private static List<BundleRequirementImpl> convertRequires(
1431 List<ParsedHeaderClause> clauses, BundleRevision owner)
Richard S. Hall860ee302006-12-27 18:32:04 +00001432 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001433 List<BundleRequirementImpl> reqList = new ArrayList();
Richard S. Hall831b7712011-06-06 20:11:46 +00001434 for (ParsedHeaderClause clause : clauses)
Richard S. Hall860ee302006-12-27 18:32:04 +00001435 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001436 for (String path : clause.m_paths)
Richard S. Hall860ee302006-12-27 18:32:04 +00001437 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001438 // Prepend the bundle symbolic name to the array of attributes.
Richard S. Hall831b7712011-06-06 20:11:46 +00001439 Map<String, Object> attrs = clause.m_attrs;
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001440 // Note that we use a linked hash map here to ensure the
1441 // package attribute is first, which will make indexing
1442 // more efficient.
Richard S. Hall9b940412011-07-20 18:19:02 +00001443// TODO: OSGi R4.3 - This ordering fix is a hack...perhaps we should use the standard "key"
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001444// notion where namespace is also the name of the key attribute.
Richard S. Hall860ee302006-12-27 18:32:04 +00001445 // Prepend the symbolic name to the array of attributes.
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001446 Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
1447 newAttrs.put(
Richard S. Hallc4e75992011-06-15 18:57:20 +00001448 BundleRevision.BUNDLE_NAMESPACE,
Richard S. Hall831b7712011-06-06 20:11:46 +00001449 path);
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001450 newAttrs.putAll(attrs);
Richard S. Hall860ee302006-12-27 18:32:04 +00001451
Richard S. Hall9b940412011-07-20 18:19:02 +00001452 // Create filter now so we can inject filter directive.
1453 SimpleFilter sf = BundleRequirementImpl.convertToFilter(newAttrs);
1454
1455 // Inject filter directive.
1456// TODO: OSGi R4.3 - Can we insert this on demand somehow?
1457 Map<String, String> dirs = clause.m_dirs;
1458 Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
1459 newDirs.putAll(dirs);
1460 newDirs.put(
1461 Constants.FILTER_DIRECTIVE,
1462 sf.toString());
1463
Richard S. Hall860ee302006-12-27 18:32:04 +00001464 // Create package requirement and add to requirement list.
1465 reqList.add(
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001466 new BundleRequirementImpl(
Richard S. Hall1a879e52010-04-18 19:22:48 +00001467 owner,
Richard S. Hall2c0bf6b2011-06-07 18:00:08 +00001468 BundleRevision.BUNDLE_NAMESPACE,
Richard S. Hall9b940412011-07-20 18:19:02 +00001469 newDirs,
Richard S. Hall860ee302006-12-27 18:32:04 +00001470 newAttrs));
1471 }
1472 }
1473
Richard S. Hall1320d472010-03-03 14:58:21 +00001474 return reqList;
Richard S. Hall860ee302006-12-27 18:32:04 +00001475 }
1476
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001477 public static String parseExtensionBundleHeader(String header)
Richard S. Hall2e0c0222009-05-01 19:11:02 +00001478 throws BundleException
Karl Pauls210a2142007-02-12 23:37:08 +00001479 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001480 List<ParsedHeaderClause> clauses = parseStandardHeader(header);
Karl Pauls210a2142007-02-12 23:37:08 +00001481
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001482 String result = null;
Karl Pauls210a2142007-02-12 23:37:08 +00001483
Richard S. Hall1320d472010-03-03 14:58:21 +00001484 if (clauses.size() == 1)
Karl Pauls210a2142007-02-12 23:37:08 +00001485 {
Richard S. Hall2e0c0222009-05-01 19:11:02 +00001486 // See if there is the "extension" directive.
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001487 for (Entry<String, String> entry : clauses.get(0).m_dirs.entrySet())
Karl Pauls210a2142007-02-12 23:37:08 +00001488 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001489 if (Constants.EXTENSION_DIRECTIVE.equals(entry.getKey()))
Karl Pauls210a2142007-02-12 23:37:08 +00001490 {
Richard S. Hall2e0c0222009-05-01 19:11:02 +00001491 // If the extension directive is specified, make sure
1492 // the target is the system bundle.
Richard S. Hall1320d472010-03-03 14:58:21 +00001493 if (FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(clauses.get(0).m_paths.get(0)) ||
1494 Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(clauses.get(0).m_paths.get(0)))
Karl Pauls210a2142007-02-12 23:37:08 +00001495 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001496 return entry.getValue();
Richard S. Hall2e0c0222009-05-01 19:11:02 +00001497 }
1498 else
1499 {
1500 throw new BundleException(
1501 "Only the system bundle can have extension bundles.");
Karl Pauls210a2142007-02-12 23:37:08 +00001502 }
1503 }
1504 }
1505 }
1506
1507 return result;
1508 }
1509
Richard S. Hall10d9d2c2009-06-12 20:21:59 +00001510 private void parseActivationPolicy(Map headerMap)
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001511 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001512 m_activationPolicy = BundleRevisionImpl.EAGER_ACTIVATION;
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001513
Richard S. Hall1320d472010-03-03 14:58:21 +00001514 List<ParsedHeaderClause> clauses = parseStandardHeader(
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001515 (String) headerMap.get(Constants.BUNDLE_ACTIVATIONPOLICY));
1516
Richard S. Hall1320d472010-03-03 14:58:21 +00001517 if (clauses.size() > 0)
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001518 {
1519 // Just look for a "path" matching the lazy policy, ignore
1520 // everything else.
Richard S. Hall831b7712011-06-06 20:11:46 +00001521 for (String path : clauses.get(0).m_paths)
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001522 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001523 if (path.equals(Constants.ACTIVATION_LAZY))
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001524 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001525 m_activationPolicy = BundleRevisionImpl.LAZY_ACTIVATION;
1526 for (Entry<String, String> entry : clauses.get(0).m_dirs.entrySet())
Richard S. Hall10d9d2c2009-06-12 20:21:59 +00001527 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001528 if (entry.getKey().equalsIgnoreCase(Constants.INCLUDE_DIRECTIVE))
Richard S. Hall10d9d2c2009-06-12 20:21:59 +00001529 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001530 m_activationIncludeDir = entry.getValue();
Richard S. Hall10d9d2c2009-06-12 20:21:59 +00001531 }
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001532 else if (entry.getKey().equalsIgnoreCase(Constants.EXCLUDE_DIRECTIVE))
Richard S. Hall10d9d2c2009-06-12 20:21:59 +00001533 {
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001534 m_activationExcludeDir = entry.getValue();
Richard S. Hall10d9d2c2009-06-12 20:21:59 +00001535 }
1536 }
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001537 break;
1538 }
1539 }
1540 }
Richard S. Hall2a5fb9a2009-05-14 15:14:13 +00001541 }
1542
Richard S. Hall56b11942006-10-26 14:40:07 +00001543 // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2,
1544 // path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
Richard S. Hall831b7712011-06-06 20:11:46 +00001545 public static void main(String[] headers)
Richard S. Hall56b11942006-10-26 14:40:07 +00001546 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001547 String header = headers[0];
Richard S. Hall56b11942006-10-26 14:40:07 +00001548
1549 if (header != null)
1550 {
1551 if (header.length() == 0)
1552 {
1553 throw new IllegalArgumentException(
1554 "A header cannot be an empty string.");
1555 }
1556
Richard S. Hall831b7712011-06-06 20:11:46 +00001557 List<ParsedHeaderClause> clauses = parseStandardHeader(header);
1558 for (ParsedHeaderClause clause : clauses)
Richard S. Hall56b11942006-10-26 14:40:07 +00001559 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001560 System.out.println("PATHS " + clause.m_paths);
1561 System.out.println(" DIRS " + clause.m_dirs);
1562 System.out.println(" ATTRS " + clause.m_attrs);
1563 System.out.println(" TYPES " + clause.m_types);
Richard S. Hall56b11942006-10-26 14:40:07 +00001564 }
Richard S. Hall56b11942006-10-26 14:40:07 +00001565 }
1566
Richard S. Hall831b7712011-06-06 20:11:46 +00001567// return clauses;
1568 }
1569
1570 private static List<ParsedHeaderClause> parseStandardHeader(String header)
1571 {
1572 List<ParsedHeaderClause> clauses = new ArrayList<ParsedHeaderClause>();
1573 if (header != null)
1574 {
1575 int[] startIdx = new int[1];
1576 startIdx[0] = 0;
1577 for (int i = 0; i < header.length(); i++)
1578 {
1579 clauses.add(parseClause(startIdx, header));
1580 i = startIdx[0];
1581 }
1582 }
Richard S. Hall1320d472010-03-03 14:58:21 +00001583 return clauses;
Richard S. Hall56b11942006-10-26 14:40:07 +00001584 }
1585
Richard S. Hall831b7712011-06-06 20:11:46 +00001586 private static ParsedHeaderClause parseClause(int[] startIdx, String header)
Richard S. Hall56b11942006-10-26 14:40:07 +00001587 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001588 ParsedHeaderClause clause = new ParsedHeaderClause(
1589 new ArrayList<String>(),
1590 new HashMap<String, String>(),
1591 new HashMap<String, Object>(),
1592 new HashMap<String, String>());
1593 for (int i = startIdx[0]; i < header.length(); i++)
Richard S. Hall56b11942006-10-26 14:40:07 +00001594 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001595 char c = header.charAt(i);
1596 if ((c == ':') || (c == '='))
Richard S. Hall56b11942006-10-26 14:40:07 +00001597 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001598 parseClauseParameters(startIdx, header, clause);
1599 i = startIdx[0];
Richard S. Hall56b11942006-10-26 14:40:07 +00001600 break;
1601 }
Richard S. Hall831b7712011-06-06 20:11:46 +00001602 else if ((c == ';') || (c == ',') || (i == (header.length() - 1)))
1603 {
1604 String path;
1605 if (i == (header.length() - 1))
1606 {
1607 path = header.substring(startIdx[0], header.length());
1608 }
1609 else
1610 {
1611 path = header.substring(startIdx[0], i);
1612 }
1613 clause.m_paths.add(path.trim());
1614 startIdx[0] = i + 1;
1615 if (c == ',')
1616 {
1617 break;
1618 }
1619 }
1620 }
1621 return clause;
1622 }
1623
1624 private static void parseClauseParameters(
1625 int[] startIdx, String header, ParsedHeaderClause clause)
1626 {
1627 for (int i = startIdx[0]; i < header.length(); i++)
1628 {
1629 char c = header.charAt(i);
1630 if ((c == ':') && (header.charAt(i + 1) == '='))
1631 {
1632 parseClauseDirective(startIdx, header, clause);
1633 i = startIdx[0];
1634 }
1635 else if ((c == ':') || (c == '='))
1636 {
1637 parseClauseAttribute(startIdx, header, clause);
1638 i = startIdx[0];
1639 }
1640 else if (c == ',')
1641 {
1642 startIdx[0] = i + 1;
1643 break;
1644 }
1645 }
1646 }
1647
1648 private static void parseClauseDirective(
1649 int[] startIdx, String header, ParsedHeaderClause clause)
1650 {
1651 String name = null;
1652 String value = null;
1653 boolean isQuoted = false;
1654 boolean isEscaped = false;
1655 for (int i = startIdx[0]; i < header.length(); i++)
1656 {
1657 char c = header.charAt(i);
1658 if (!isEscaped && (c == '"'))
1659 {
1660 isQuoted = !isQuoted;
1661 }
1662
1663 if (!isEscaped
1664 && !isQuoted && (c == ':'))
1665 {
1666 name = header.substring(startIdx[0], i);
1667 startIdx[0] = i + 2;
1668 }
1669 else if (!isEscaped
1670 && !isQuoted && ((c == ';') || (c == ',') || (i == (header.length() - 1))))
1671 {
1672 if (i == (header.length() - 1))
1673 {
1674 value = header.substring(startIdx[0], header.length());
1675 }
1676 else
1677 {
1678 value = header.substring(startIdx[0], i);
1679 }
1680 if (c == ',')
1681 {
1682 startIdx[0] = i - 1;
1683 }
1684 else
1685 {
1686 startIdx[0] = i + 1;
1687 }
1688 break;
1689 }
1690
1691 isEscaped = (c == '\\');
Richard S. Hall56b11942006-10-26 14:40:07 +00001692 }
1693
Richard S. Hall831b7712011-06-06 20:11:46 +00001694 // Trim whitespace.
1695 name = name.trim();
1696 value = value.trim();
1697
1698 // Remove quotes, if value is quoted.
1699 if (value.startsWith("\"") && value.endsWith("\""))
1700 {
1701 value = value.substring(1, value.length() - 1);
1702 }
1703
1704 // Check for dupes.
1705 if (clause.m_dirs.get(name) != null)
Richard S. Hall56b11942006-10-26 14:40:07 +00001706 {
1707 throw new IllegalArgumentException(
Richard S. Hall831b7712011-06-06 20:11:46 +00001708 "Duplicate directive '" + name + "' in: " + header);
Richard S. Hall56b11942006-10-26 14:40:07 +00001709 }
1710
Richard S. Hall831b7712011-06-06 20:11:46 +00001711 clause.m_dirs.put(name, value);
1712 }
1713
1714 private static void parseClauseAttribute(
1715 int[] startIdx, String header, ParsedHeaderClause clause)
1716 {
1717 String type = null;
1718
1719 String name = parseClauseAttributeName(startIdx, header);
1720 char c = header.charAt(startIdx[0]);
1721 startIdx[0]++;
1722 if (c == ':')
Richard S. Hall1320d472010-03-03 14:58:21 +00001723 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001724 type = parseClauseAttributeType(startIdx, header);
Richard S. Hall1320d472010-03-03 14:58:21 +00001725 }
Richard S. Hall56b11942006-10-26 14:40:07 +00001726
Richard S. Hall831b7712011-06-06 20:11:46 +00001727 String value = parseClauseAttributeValue(startIdx, header);
1728
1729 // Trim whitespace.
1730 name = name.trim();
1731 value = value.trim();
1732 if (type != null)
Richard S. Hall56b11942006-10-26 14:40:07 +00001733 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001734 type = type.trim();
Richard S. Hall56b11942006-10-26 14:40:07 +00001735 }
1736
Richard S. Hall831b7712011-06-06 20:11:46 +00001737 // Remove quotes, if value is quoted.
1738 if (value.startsWith("\"") && value.endsWith("\""))
1739 {
1740 value = value.substring(1, value.length() - 1);
1741 }
1742
1743 // Check for dupes.
1744 if (clause.m_attrs.get(name) != null)
1745 {
1746 throw new IllegalArgumentException(
1747 "Duplicate attribute '" + name + "' in: " + header);
1748 }
1749
1750 clause.m_attrs.put(name, value);
1751 if (type != null)
1752 {
1753 clause.m_types.put(name, type);
1754 }
1755 }
1756
1757 private static String parseClauseAttributeName(int[] startIdx, String header)
1758 {
1759 for (int i = startIdx[0]; i < header.length(); i++)
1760 {
1761 char c = header.charAt(i);
1762 if ((c == '=') || (c == ':'))
1763 {
1764 String name = header.substring(startIdx[0], i);
1765 startIdx[0] = i;
1766 return name;
1767 }
1768 }
1769 return null;
1770 }
1771
1772 private static String parseClauseAttributeType(int[] startIdx, String header)
1773 {
1774 for (int i = startIdx[0]; i < header.length(); i++)
1775 {
1776 char c = header.charAt(i);
1777 if (c == '=')
1778 {
1779 String type = header.substring(startIdx[0], i);
1780 startIdx[0] = i + 1;
1781 return type;
1782 }
1783 }
1784 return null;
1785 }
1786
1787 private static String parseClauseAttributeValue(int[] startIdx, String header)
1788 {
1789 boolean isQuoted = false;
1790 boolean isEscaped = false;
1791 for (int i = startIdx[0]; i < header.length(); i++)
1792 {
1793 char c = header.charAt(i);
1794 if (!isEscaped && (c == '"'))
1795 {
1796 isQuoted = !isQuoted;
1797 }
1798
1799 if (!isEscaped &&
1800 !isQuoted && ((c == ';') || (c == ',') || (i == (header.length() - 1))))
1801 {
1802 String value;
1803 if (i == (header.length() - 1))
1804 {
1805 value = header.substring(startIdx[0], header.length());
1806 }
1807 else
1808 {
1809 value = header.substring(startIdx[0], i);
1810 }
1811 if (c == ',')
1812 {
1813 startIdx[0] = i - 1;
1814 }
1815 else
1816 {
1817 startIdx[0] = i + 1;
1818 }
1819 return value;
1820 }
1821
1822 isEscaped = (c == '\\');
1823 }
1824 return null;
1825 }
1826
1827 public static List<String> parseDelimitedString(String value, String delim)
1828 {
1829 return parseDelimitedString(value, delim, true);
Richard S. Hall56b11942006-10-26 14:40:07 +00001830 }
Richard S. Hall78605422006-12-18 20:47:55 +00001831
1832 /**
1833 * Parses delimited string and returns an array containing the tokens. This
1834 * parser obeys quotes, so the delimiter character will be ignored if it is
1835 * inside of a quote. This method assumes that the quote character is not
1836 * included in the set of delimiter characters.
1837 * @param value the delimited string to parse.
1838 * @param delim the characters delimiting the tokens.
Richard S. Hall3b789082011-01-19 17:58:17 +00001839 * @return a list of string or an empty list if there are none.
Richard S. Hall78605422006-12-18 20:47:55 +00001840 **/
Richard S. Hall831b7712011-06-06 20:11:46 +00001841 public static List<String> parseDelimitedString(String value, String delim, boolean trim)
Richard S. Hall78605422006-12-18 20:47:55 +00001842 {
1843 if (value == null)
1844 {
1845 value = "";
1846 }
1847
Richard S. Hall1320d472010-03-03 14:58:21 +00001848 List<String> list = new ArrayList();
Richard S. Hall78605422006-12-18 20:47:55 +00001849
1850 int CHAR = 1;
1851 int DELIMITER = 2;
1852 int STARTQUOTE = 4;
1853 int ENDQUOTE = 8;
1854
1855 StringBuffer sb = new StringBuffer();
1856
1857 int expecting = (CHAR | DELIMITER | STARTQUOTE);
Karl Pauls210a2142007-02-12 23:37:08 +00001858
Richard S. Hall831b7712011-06-06 20:11:46 +00001859 boolean isEscaped = false;
Richard S. Hall78605422006-12-18 20:47:55 +00001860 for (int i = 0; i < value.length(); i++)
1861 {
1862 char c = value.charAt(i);
1863
1864 boolean isDelimiter = (delim.indexOf(c) >= 0);
Richard S. Hall78605422006-12-18 20:47:55 +00001865
Richard S. Hall831b7712011-06-06 20:11:46 +00001866 if (c == '\\')
Richard S. Hall78605422006-12-18 20:47:55 +00001867 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001868 isEscaped = true;
1869 continue;
1870 }
1871
1872 if (isEscaped)
1873 {
1874 sb.append(c);
1875 }
1876 else if (isDelimiter && ((expecting & DELIMITER) > 0))
1877 {
1878 if (trim)
1879 {
1880 list.add(sb.toString().trim());
1881 }
1882 else
1883 {
1884 list.add(sb.toString());
1885 }
Richard S. Hall78605422006-12-18 20:47:55 +00001886 sb.delete(0, sb.length());
1887 expecting = (CHAR | DELIMITER | STARTQUOTE);
1888 }
Richard S. Hall831b7712011-06-06 20:11:46 +00001889 else if ((c == '"') && ((expecting & STARTQUOTE) > 0))
Richard S. Hall78605422006-12-18 20:47:55 +00001890 {
1891 sb.append(c);
1892 expecting = CHAR | ENDQUOTE;
1893 }
Richard S. Hall831b7712011-06-06 20:11:46 +00001894 else if ((c == '"') && ((expecting & ENDQUOTE) > 0))
Richard S. Hall78605422006-12-18 20:47:55 +00001895 {
1896 sb.append(c);
1897 expecting = (CHAR | STARTQUOTE | DELIMITER);
1898 }
1899 else if ((expecting & CHAR) > 0)
1900 {
1901 sb.append(c);
1902 }
1903 else
1904 {
1905 throw new IllegalArgumentException("Invalid delimited string: " + value);
1906 }
Richard S. Hall831b7712011-06-06 20:11:46 +00001907
1908 isEscaped = false;
Richard S. Hall78605422006-12-18 20:47:55 +00001909 }
1910
1911 if (sb.length() > 0)
1912 {
Richard S. Hall831b7712011-06-06 20:11:46 +00001913 if (trim)
1914 {
1915 list.add(sb.toString().trim());
1916 }
1917 else
1918 {
1919 list.add(sb.toString());
1920 }
Richard S. Hall78605422006-12-18 20:47:55 +00001921 }
1922
Richard S. Hall1320d472010-03-03 14:58:21 +00001923 return list;
Richard S. Hall78605422006-12-18 20:47:55 +00001924 }
1925
1926 /**
1927 * Parses native code manifest headers.
1928 * @param libStrs an array of native library manifest header
1929 * strings from the bundle manifest.
1930 * @return an array of <tt>LibraryInfo</tt> objects for the
1931 * passed in strings.
1932 **/
Richard S. Hall1320d472010-03-03 14:58:21 +00001933 private static List<R4LibraryClause> parseLibraryStrings(
1934 Logger logger, List<String> libStrs)
Richard S. Hall78605422006-12-18 20:47:55 +00001935 throws IllegalArgumentException
1936 {
1937 if (libStrs == null)
1938 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001939 return new ArrayList<R4LibraryClause>(0);
Richard S. Hall78605422006-12-18 20:47:55 +00001940 }
1941
Richard S. Hall1320d472010-03-03 14:58:21 +00001942 List<R4LibraryClause> libList = new ArrayList(libStrs.size());
Richard S. Hall78605422006-12-18 20:47:55 +00001943
Richard S. Hall1320d472010-03-03 14:58:21 +00001944 for (int i = 0; i < libStrs.size(); i++)
Richard S. Hall78605422006-12-18 20:47:55 +00001945 {
Richard S. Hall1320d472010-03-03 14:58:21 +00001946 R4LibraryClause clause = R4LibraryClause.parse(logger, libStrs.get(i));
Richard S. Hall78605422006-12-18 20:47:55 +00001947 libList.add(clause);
1948 }
1949
Richard S. Hall1320d472010-03-03 14:58:21 +00001950 return libList;
Richard S. Hall78605422006-12-18 20:47:55 +00001951 }
Richard S. Hall4ecc6772011-05-17 21:20:32 +00001952}