1 /*******************************************************************************
2  * Copyright (c) 2003, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Danail Nachev -  ProSyst - bug 218625
14  *******************************************************************************/
15 package org.eclipse.osgi.internal.resolver;
16 
17 import java.lang.reflect.Constructor;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Dictionary;
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.Hashtable;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import org.eclipse.osgi.internal.framework.EquinoxContainer;
30 import org.eclipse.osgi.internal.framework.FilterImpl;
31 import org.eclipse.osgi.internal.messages.Msg;
32 import org.eclipse.osgi.internal.util.Tokenizer;
33 import org.eclipse.osgi.service.resolver.BundleDescription;
34 import org.eclipse.osgi.service.resolver.BundleSpecification;
35 import org.eclipse.osgi.service.resolver.ExportPackageDescription;
36 import org.eclipse.osgi.service.resolver.GenericDescription;
37 import org.eclipse.osgi.service.resolver.GenericSpecification;
38 import org.eclipse.osgi.service.resolver.HostSpecification;
39 import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
40 import org.eclipse.osgi.service.resolver.NativeCodeSpecification;
41 import org.eclipse.osgi.service.resolver.State;
42 import org.eclipse.osgi.service.resolver.VersionRange;
43 import org.eclipse.osgi.util.ManifestElement;
44 import org.eclipse.osgi.util.NLS;
45 import org.osgi.framework.BundleException;
46 import org.osgi.framework.Constants;
47 import org.osgi.framework.InvalidSyntaxException;
48 import org.osgi.framework.Version;
49 import org.osgi.framework.namespace.BundleNamespace;
50 import org.osgi.framework.namespace.IdentityNamespace;
51 import org.osgi.resource.Namespace;
52 
53 /**
54  * This class builds bundle description objects from manifests
55  */
56 public class StateBuilder {
57 	private static final String[] DEFINED_EXPORT_PACKAGE_DIRECTIVES = {Constants.USES_DIRECTIVE, Constants.INCLUDE_DIRECTIVE, Constants.EXCLUDE_DIRECTIVE, StateImpl.FRIENDS_DIRECTIVE, StateImpl.INTERNAL_DIRECTIVE, Constants.MANDATORY_DIRECTIVE};
58 	private static final String[] DEFINED_IMPORT_PACKAGE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE};
59 	private static final String[] DEFINED_PACKAGE_MATCHING_ATTRS = {Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION, Constants.VERSION_ATTRIBUTE};
60 	private static final String[] DEFINED_REQUIRE_BUNDLE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.VISIBILITY_DIRECTIVE};
61 	private static final String[] DEFINED_FRAGMENT_HOST_DIRECTIVES = {Constants.EXTENSION_DIRECTIVE};
62 	static final String[] DEFINED_BSN_DIRECTIVES = {Constants.SINGLETON_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.MANDATORY_DIRECTIVE};
63 	static final String[] DEFINED_BSN_MATCHING_ATTRS = {Constants.BUNDLE_VERSION_ATTRIBUTE, StateImpl.OPTIONAL_ATTRIBUTE, StateImpl.REPROVIDE_ATTRIBUTE};
64 	private static final String[] DEFINED_REQUIRE_CAPABILITY_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.FILTER_DIRECTIVE, Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE};
65 	private static final String[] DEFINED_REQUIRE_CAPABILITY_ATTRS = {};
66 	private static final String[] DEFINED_OSGI_VALIDATE_HEADERS = {Constants.IMPORT_PACKAGE, Constants.DYNAMICIMPORT_PACKAGE, Constants.EXPORT_PACKAGE, Constants.FRAGMENT_HOST, Constants.BUNDLE_SYMBOLICNAME, Constants.REQUIRE_BUNDLE};
67 	static final String GENERIC_REQUIRE = "Eclipse-GenericRequire"; //$NON-NLS-1$
68 	static final String GENERIC_CAPABILITY = "Eclipse-GenericCapability"; //$NON-NLS-1$
69 
70 	private static final String ATTR_TYPE_STRING = "string"; //$NON-NLS-1$
71 	private static final String ATTR_TYPE_VERSION = "version"; //$NON-NLS-1$
72 	private static final String ATTR_TYPE_URI = "uri"; //$NON-NLS-1$
73 	private static final String ATTR_TYPE_LONG = "long"; //$NON-NLS-1$
74 	private static final String ATTR_TYPE_DOUBLE = "double"; //$NON-NLS-1$
75 	private static final String ATTR_TYPE_SET = "set"; //$NON-NLS-1$
76 	private static final String ATTR_TYPE_LIST = "List"; //$NON-NLS-1$
77 	private static final String OPTIONAL_ATTR = "optional"; //$NON-NLS-1$
78 	private static final String MULTIPLE_ATTR = "multiple"; //$NON-NLS-1$
79 	private static final String TRUE = "true"; //$NON-NLS-1$
80 
createBundleDescription(StateImpl state, Dictionary<String, String> manifest, String location)81 	static BundleDescription createBundleDescription(StateImpl state, Dictionary<String, String> manifest, String location) throws BundleException {
82 		BundleDescriptionImpl result = new BundleDescriptionImpl();
83 		String manifestVersionHeader = manifest.get(Constants.BUNDLE_MANIFESTVERSION);
84 		boolean jreBundle = "true".equals(manifest.get(StateImpl.Eclipse_JREBUNDLE)); //$NON-NLS-1$
85 		int manifestVersion = 1;
86 		if (manifestVersionHeader != null)
87 			manifestVersion = Integer.parseInt(manifestVersionHeader);
88 		if (manifestVersion >= 2)
89 			validateHeaders(manifest, jreBundle);
90 
91 		// retrieve the symbolic-name and the singleton status
92 		String symbolicNameHeader = manifest.get(Constants.BUNDLE_SYMBOLICNAME);
93 		if (symbolicNameHeader != null) {
94 			ManifestElement[] symbolicNameElements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, symbolicNameHeader);
95 			if (symbolicNameElements.length > 0) {
96 				ManifestElement bsnElement = symbolicNameElements[0];
97 				result.setSymbolicName(bsnElement.getValue());
98 				String singleton = bsnElement.getDirective(Constants.SINGLETON_DIRECTIVE);
99 				if (singleton == null) // TODO this is for backward compatibility; need to check manifest version < 2 to allow this after everyone has converted to new syntax
100 					singleton = bsnElement.getAttribute(Constants.SINGLETON_DIRECTIVE);
101 				result.setStateBit(BundleDescriptionImpl.SINGLETON, "true".equals(singleton)); //$NON-NLS-1$
102 				String fragmentAttachment = bsnElement.getDirective(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
103 				if (fragmentAttachment != null) {
104 					if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_RESOLVETIME)) {
105 						result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, true);
106 						result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false);
107 					} else if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
108 						result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, false);
109 						result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false);
110 					}
111 				}
112 				result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(bsnElement.getDirective(Constants.MANDATORY_DIRECTIVE)));
113 				result.setAttributes(getAttributes(bsnElement, DEFINED_BSN_MATCHING_ATTRS));
114 				result.setArbitraryDirectives(getDirectives(bsnElement, DEFINED_BSN_DIRECTIVES));
115 			}
116 		}
117 		// retrieve other headers
118 		String version = manifest.get(Constants.BUNDLE_VERSION);
119 		try {
120 			result.setVersion((version != null) ? Version.parseVersion(version) : Version.emptyVersion);
121 		} catch (IllegalArgumentException ex) {
122 			if (manifestVersion >= 2) {
123 				String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.BUNDLE_VERSION, version);
124 				throw new BundleException(message + " : " + ex.getMessage(), BundleException.MANIFEST_ERROR, ex); //$NON-NLS-1$
125 			}
126 			// prior to R4 the Bundle-Version header was not interpreted by the Framework;
127 			// must not fail for old R3 style bundles
128 		}
129 		result.setLocation(location);
130 		result.setPlatformFilter(manifest.get(StateImpl.ECLIPSE_PLATFORMFILTER));
131 		String[] brees = ManifestElement.getArrayFromList(manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT));
132 		result.setExecutionEnvironments(brees);
133 		ManifestElement[] host = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, manifest.get(Constants.FRAGMENT_HOST));
134 		if (host != null)
135 			result.setHost(createHostSpecification(host[0], state));
136 		ManifestElement[] exports = ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, manifest.get(Constants.EXPORT_PACKAGE));
137 		ManifestElement[] provides = ManifestElement.parseHeader(StateImpl.PROVIDE_PACKAGE, manifest.get(StateImpl.PROVIDE_PACKAGE));
138 		boolean strict = state != null && state.inStrictMode();
139 		List<String> providedExports = new ArrayList<>(provides == null ? 0 : provides.length);
140 		result.setExportPackages(createExportPackages(exports, provides, providedExports, strict));
141 		ManifestElement[] imports = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, manifest.get(Constants.IMPORT_PACKAGE));
142 		ManifestElement[] dynamicImports = ManifestElement.parseHeader(Constants.DYNAMICIMPORT_PACKAGE, manifest.get(Constants.DYNAMICIMPORT_PACKAGE));
143 		result.setImportPackages(createImportPackages(result.getExportPackages(), providedExports, imports, dynamicImports, manifestVersion));
144 		ManifestElement[] requires = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, manifest.get(Constants.REQUIRE_BUNDLE));
145 		result.setRequiredBundles(createRequiredBundles(requires));
146 		String[][] genericAliases = getGenericAliases(state);
147 		ManifestElement[] genericRequires = getGenericRequires(manifest, genericAliases);
148 		ManifestElement[] osgiRequires = ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, manifest.get(Constants.REQUIRE_CAPABILITY));
149 		result.setGenericRequires(createGenericRequires(genericRequires, osgiRequires, brees));
150 		ManifestElement[] genericCapabilities = getGenericCapabilities(manifest, genericAliases);
151 		ManifestElement[] osgiCapabilities = ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, manifest.get(Constants.PROVIDE_CAPABILITY));
152 		result.setGenericCapabilities(createGenericCapabilities(genericCapabilities, osgiCapabilities, result));
153 		ManifestElement[] nativeCode = ManifestElement.parseHeader(Constants.BUNDLE_NATIVECODE, manifest.get(Constants.BUNDLE_NATIVECODE));
154 		result.setNativeCodeSpecification(createNativeCode(nativeCode));
155 		return result;
156 	}
157 
getGenericRequires(Dictionary<String, String> manifest, String[][] genericAliases)158 	private static ManifestElement[] getGenericRequires(Dictionary<String, String> manifest, String[][] genericAliases) throws BundleException {
159 		ManifestElement[] genericRequires = ManifestElement.parseHeader(GENERIC_REQUIRE, manifest.get(GENERIC_REQUIRE));
160 		List<ManifestElement> aliasList = null;
161 		if (genericAliases.length > 0) {
162 			aliasList = new ArrayList<>(genericRequires == null ? 0 : genericRequires.length);
163 			for (String[] genericAlias : genericAliases) {
164 				ManifestElement[] aliasReqs = ManifestElement.parseHeader(genericAlias[1], manifest.get(genericAlias[1]));
165 				if (aliasReqs == null)
166 					continue;
167 				for (ManifestElement aliasReq : aliasReqs) {
168 					StringBuilder strBuf = new StringBuilder();
169 					strBuf.append(aliasReq.getValue()).append(':').append(genericAlias[2]);
170 					String filter = aliasReq.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE);
171 					if (filter != null)
172 						strBuf.append("; ").append(Constants.SELECTION_FILTER_ATTRIBUTE).append(filter).append("=\"").append(filter).append("\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
173 					ManifestElement[] withType = ManifestElement.parseHeader(genericAlias[1], strBuf.toString());
174 					aliasList.add(withType[0]);
175 				}
176 			}
177 		}
178 		if (aliasList == null || aliasList.size() == 0)
179 			return genericRequires;
180 		if (genericRequires != null)
181 			Collections.addAll(aliasList, genericRequires);
182 		return aliasList.toArray(new ManifestElement[aliasList.size()]);
183 	}
184 
getGenericCapabilities(Dictionary<String, String> manifest, String[][] genericAliases)185 	private static ManifestElement[] getGenericCapabilities(Dictionary<String, String> manifest, String[][] genericAliases) throws BundleException {
186 		ManifestElement[] genericCapabilities = ManifestElement.parseHeader(GENERIC_CAPABILITY, manifest.get(GENERIC_CAPABILITY));
187 		List<ManifestElement> aliasList = null;
188 		if (genericAliases.length > 0) {
189 			aliasList = new ArrayList<>(genericCapabilities == null ? 0 : genericCapabilities.length);
190 			for (String[] genericAlias : genericAliases) {
191 				ManifestElement[] aliasCapabilities = ManifestElement.parseHeader(genericAlias[0], manifest.get(genericAlias[0]));
192 				if (aliasCapabilities == null)
193 					continue;
194 				for (ManifestElement aliasCapability : aliasCapabilities) {
195 					StringBuilder strBuf = new StringBuilder();
196 					strBuf.append(aliasCapability.getValue()).append(':').append(genericAlias[2]);
197 					for (Enumeration<String> keys = aliasCapability.getKeys(); keys != null && keys.hasMoreElements();) {
198 						String key = keys.nextElement();
199 						strBuf.append("; ").append(key).append("=\"").append(aliasCapability.getAttribute(key)).append("\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
200 					}
201 					ManifestElement[] withTypes = ManifestElement.parseHeader(genericAlias[0], strBuf.toString());
202 					aliasList.add(withTypes[0]);
203 				}
204 			}
205 		}
206 		if (aliasList == null || aliasList.size() == 0)
207 			return genericCapabilities;
208 		if (genericCapabilities != null)
209 			Collections.addAll(aliasList, genericCapabilities);
210 		return aliasList.toArray(new ManifestElement[aliasList.size()]);
211 	}
212 
getGenericAliases(StateImpl state)213 	private static String[][] getGenericAliases(StateImpl state) {
214 		String genericAliasesProp = getPlatformProperty(state, "osgi.genericAliases"); //$NON-NLS-1$
215 		if (genericAliasesProp == null)
216 			return new String[0][0];
217 		String[] aliases = ManifestElement.getArrayFromList(genericAliasesProp, ","); //$NON-NLS-1$
218 		String[][] result = new String[aliases.length][];
219 		for (int i = 0; i < aliases.length; i++)
220 			result[i] = ManifestElement.getArrayFromList(aliases[i], ":"); //$NON-NLS-1$
221 		return result;
222 	}
223 
getPlatformProperty(State state, String key)224 	private static String getPlatformProperty(State state, String key) {
225 		Dictionary<Object, Object>[] platformProps = state == null ? null : state.getPlatformProperties();
226 		return platformProps == null || platformProps.length == 0 ? null : (String) platformProps[0].get(key);
227 	}
228 
validateHeaders(Dictionary<String, String> manifest, boolean jreBundle)229 	private static void validateHeaders(Dictionary<String, String> manifest, boolean jreBundle) throws BundleException {
230 		for (String definedOSGiValidateHeader : DEFINED_OSGI_VALIDATE_HEADERS) {
231 			String header = manifest.get(definedOSGiValidateHeader);
232 			if (header != null) {
233 				ManifestElement[] elements = ManifestElement.parseHeader(definedOSGiValidateHeader, header);
234 				checkForDuplicateDirectivesAttributes(definedOSGiValidateHeader, elements);
235 				if (definedOSGiValidateHeader == Constants.IMPORT_PACKAGE) {
236 					checkImportExportSyntax(definedOSGiValidateHeader, elements, false, false, jreBundle);
237 				}
238 				if (definedOSGiValidateHeader == Constants.DYNAMICIMPORT_PACKAGE) {
239 					checkImportExportSyntax(definedOSGiValidateHeader, elements, false, true, jreBundle);
240 				}
241 				if (definedOSGiValidateHeader == Constants.EXPORT_PACKAGE) {
242 					checkImportExportSyntax(definedOSGiValidateHeader, elements, true, false, jreBundle);
243 				}
244 				if (definedOSGiValidateHeader == Constants.FRAGMENT_HOST) {
245 					checkExtensionBundle(definedOSGiValidateHeader, elements);
246 				}
247 			} else if (definedOSGiValidateHeader == Constants.BUNDLE_SYMBOLICNAME) {
248 				throw new BundleException(NLS.bind(StateMsg.HEADER_REQUIRED, Constants.BUNDLE_SYMBOLICNAME), BundleException.MANIFEST_ERROR);
249 			}
250 		}
251 	}
252 
createRequiredBundles(ManifestElement[] specs)253 	private static BundleSpecification[] createRequiredBundles(ManifestElement[] specs) {
254 		if (specs == null)
255 			return null;
256 		BundleSpecification[] result = new BundleSpecification[specs.length];
257 		for (int i = 0; i < specs.length; i++)
258 			result[i] = createRequiredBundle(specs[i]);
259 		return result;
260 	}
261 
createRequiredBundle(ManifestElement spec)262 	static BundleSpecification createRequiredBundle(ManifestElement spec) {
263 		BundleSpecificationImpl result = new BundleSpecificationImpl();
264 		result.setName(spec.getValue());
265 		result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
266 		result.setExported(Constants.VISIBILITY_REEXPORT.equals(spec.getDirective(Constants.VISIBILITY_DIRECTIVE)) || "true".equals(spec.getAttribute(StateImpl.REPROVIDE_ATTRIBUTE))); //$NON-NLS-1$
267 		result.setOptional(Constants.RESOLUTION_OPTIONAL.equals(spec.getDirective(Constants.RESOLUTION_DIRECTIVE)) || "true".equals(spec.getAttribute(StateImpl.OPTIONAL_ATTRIBUTE))); //$NON-NLS-1$
268 		result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS));
269 		result.setArbitraryDirectives(getDirectives(spec, DEFINED_REQUIRE_BUNDLE_DIRECTIVES));
270 		return result;
271 	}
272 
createImportPackages(ExportPackageDescription[] exported, List<String> providedExports, ManifestElement[] imported, ManifestElement[] dynamicImported, int manifestVersion)273 	private static ImportPackageSpecification[] createImportPackages(ExportPackageDescription[] exported, List<String> providedExports, ManifestElement[] imported, ManifestElement[] dynamicImported, int manifestVersion) {
274 		List<ImportPackageSpecification> allImports = null;
275 		if (manifestVersion < 2) {
276 			// add implicit imports for each exported package if manifest verions is less than 2.
277 			if (exported.length == 0 && imported == null && dynamicImported == null)
278 				return null;
279 			allImports = new ArrayList<>(exported.length + (imported == null ? 0 : imported.length));
280 			for (ExportPackageDescription exportDescription : exported) {
281 				if (providedExports.contains(exportDescription.getName())) {
282 					continue;
283 				}
284 				ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl();
285 				result.setName(exportDescription.getName());
286 				result.setVersionRange(getVersionRange(exportDescription.getVersion().toString()));
287 				result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_STATIC);
288 				allImports.add(result);
289 			}
290 		} else {
291 			allImports = new ArrayList<>(imported == null ? 0 : imported.length);
292 		}
293 
294 		// add dynamics first so they will get overriden by static imports if
295 		// the same package is dyanamically imported and statically imported.
296 		if (dynamicImported != null)
297 			for (ManifestElement dynamicImport : dynamicImported) {
298 				addImportPackages(dynamicImport, allImports, manifestVersion, true);
299 			}
300 		if (imported != null)
301 			for (ManifestElement pkgImport : imported) {
302 				addImportPackages(pkgImport, allImports, manifestVersion, false);
303 			}
304 		return allImports.toArray(new ImportPackageSpecification[allImports.size()]);
305 	}
306 
addImportPackages(ManifestElement importPackage, List<ImportPackageSpecification> allImports, int manifestVersion, boolean dynamic)307 	public static void addImportPackages(ManifestElement importPackage, List<ImportPackageSpecification> allImports, int manifestVersion, boolean dynamic) {
308 		String[] importNames = importPackage.getValueComponents();
309 		for (String importName : importNames) {
310 			// do not allow for multiple imports of same package of manifest version < 2
311 			if (manifestVersion < 2) {
312 				Iterator<ImportPackageSpecification> iter = allImports.iterator();
313 				while (iter.hasNext()) {
314 					if (importName.equals(iter.next().getName())) {
315 						iter.remove();
316 					}
317 				}
318 			}
319 			ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl();
320 			result.setName(importName);
321 			// set common attributes for both dynamic and static imports
322 			String versionString = importPackage.getAttribute(Constants.VERSION_ATTRIBUTE);
323 			if (versionString == null) // specification-version aliases to version
324 				versionString = importPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION);
325 			result.setVersionRange(getVersionRange(versionString));
326 			result.setBundleSymbolicName(importPackage.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
327 			result.setBundleVersionRange(getVersionRange(importPackage.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
328 			// only set the matching attributes if manifest version >= 2
329 			if (manifestVersion >= 2)
330 				result.setAttributes(getAttributes(importPackage, DEFINED_PACKAGE_MATCHING_ATTRS));
331 			if (dynamic)
332 				result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_DYNAMIC);
333 			else
334 				result.setDirective(Constants.RESOLUTION_DIRECTIVE, getResolution(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE)));
335 			result.setArbitraryDirectives(getDirectives(importPackage, DEFINED_IMPORT_PACKAGE_DIRECTIVES));
336 			allImports.add(result);
337 		}
338 	}
339 
getResolution(String resolution)340 	private static String getResolution(String resolution) {
341 		String result = ImportPackageSpecification.RESOLUTION_STATIC;
342 		if (Constants.RESOLUTION_OPTIONAL.equals(resolution) || ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(resolution))
343 			result = resolution;
344 		return result;
345 	}
346 
createExportPackages(ManifestElement[] exported, ManifestElement[] provides, List<String> providedExports, boolean strict)347 	static ExportPackageDescription[] createExportPackages(ManifestElement[] exported, ManifestElement[] provides, List<String> providedExports, boolean strict) {
348 		int numExports = (exported == null ? 0 : exported.length) + (provides == null ? 0 : provides.length);
349 		if (numExports == 0)
350 			return null;
351 		List<ExportPackageDescription> allExports = new ArrayList<>(numExports);
352 		if (exported != null)
353 			for (ManifestElement packageExport : exported) {
354 				addExportPackages(packageExport, allExports, strict);
355 			}
356 		if (provides != null)
357 			addProvidePackages(provides, allExports, providedExports);
358 		return allExports.toArray(new ExportPackageDescription[allExports.size()]);
359 	}
360 
addExportPackages(ManifestElement exportPackage, List<ExportPackageDescription> allExports, boolean strict)361 	static void addExportPackages(ManifestElement exportPackage, List<ExportPackageDescription> allExports, boolean strict) {
362 		String[] exportNames = exportPackage.getValueComponents();
363 		for (String exportName : exportNames) {
364 			// if we are in strict mode and the package is marked as internal, skip it.
365 			if (strict && "true".equals(exportPackage.getDirective(StateImpl.INTERNAL_DIRECTIVE))) //$NON-NLS-1$
366 				continue;
367 			ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl();
368 			result.setName(exportName);
369 			String versionString = exportPackage.getAttribute(Constants.VERSION_ATTRIBUTE);
370 			if (versionString == null) // specification-version aliases to version
371 				versionString = exportPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION);
372 			if (versionString != null)
373 				result.setVersion(Version.parseVersion(versionString));
374 			result.setDirective(Constants.USES_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.USES_DIRECTIVE)));
375 			result.setDirective(Constants.INCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.INCLUDE_DIRECTIVE));
376 			result.setDirective(Constants.EXCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.EXCLUDE_DIRECTIVE));
377 			result.setDirective(StateImpl.FRIENDS_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(StateImpl.FRIENDS_DIRECTIVE)));
378 			result.setDirective(StateImpl.INTERNAL_DIRECTIVE, Boolean.valueOf(exportPackage.getDirective(StateImpl.INTERNAL_DIRECTIVE)));
379 			result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.MANDATORY_DIRECTIVE)));
380 			result.setAttributes(getAttributes(exportPackage, DEFINED_PACKAGE_MATCHING_ATTRS));
381 			result.setArbitraryDirectives(getDirectives(exportPackage, DEFINED_EXPORT_PACKAGE_DIRECTIVES));
382 			allExports.add(result);
383 		}
384 	}
385 
addProvidePackages(ManifestElement[] provides, List<ExportPackageDescription> allExports, List<String> providedExports)386 	private static void addProvidePackages(ManifestElement[] provides, List<ExportPackageDescription> allExports, List<String> providedExports) {
387 		ExportPackageDescription[] currentExports = allExports.toArray(new ExportPackageDescription[allExports.size()]);
388 		for (ManifestElement provide : provides) {
389 			boolean duplicate = false;
390 			for (ExportPackageDescription currentExport : currentExports) {
391 				if (provide.getValue().equals(currentExport.getName())) {
392 					duplicate = true;
393 					break;
394 				}
395 			}
396 			if (!duplicate) {
397 				ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl();
398 				result.setName(provide.getValue());
399 				allExports.add(result);
400 			}
401 			providedExports.add(provide.getValue());
402 		}
403 	}
404 
getDirectives(ManifestElement element, String[] definedDirectives)405 	static Map<String, String> getDirectives(ManifestElement element, String[] definedDirectives) {
406 		Enumeration<String> keys = element.getDirectiveKeys();
407 		if (keys == null)
408 			return null;
409 		Map<String, String> arbitraryDirectives = null;
410 		keyloop: while (keys.hasMoreElements()) {
411 			String key = keys.nextElement();
412 			for (String definedDirective : definedDirectives) {
413 				if (definedDirective.equals(key))
414 					continue keyloop;
415 			}
416 			if (arbitraryDirectives == null)
417 				arbitraryDirectives = new HashMap<>();
418 			arbitraryDirectives.put(key, element.getDirective(key));
419 		}
420 		return arbitraryDirectives;
421 	}
422 
getAttributes(ManifestElement element, String[] definedAttrs)423 	static Map<String, Object> getAttributes(ManifestElement element, String[] definedAttrs) {
424 		Enumeration<String> keys = element.getKeys();
425 		Map<String, Object> arbitraryAttrs = null;
426 		if (keys == null)
427 			return null;
428 		while (keys.hasMoreElements()) {
429 			boolean definedAttr = false;
430 			String key = keys.nextElement();
431 			for (String attr : definedAttrs) {
432 				if (attr.equals(key)) {
433 					definedAttr = true;
434 					break;
435 				}
436 			}
437 			String value = element.getAttribute(key);
438 			int colonIndex = key.indexOf(':');
439 			String type = ATTR_TYPE_STRING;
440 			if (colonIndex > 0) {
441 				type = key.substring(colonIndex + 1).trim();
442 				key = key.substring(0, colonIndex).trim();
443 			}
444 			if (!definedAttr) {
445 				if (arbitraryAttrs == null)
446 					arbitraryAttrs = new HashMap<>();
447 				arbitraryAttrs.put(key, convertValue(type, value));
448 			}
449 		}
450 		return arbitraryAttrs;
451 	}
452 
convertValue(String type, String value)453 	private static Object convertValue(String type, String value) {
454 
455 		if (ATTR_TYPE_STRING.equalsIgnoreCase(type))
456 			return value;
457 
458 		String trimmed = value.trim();
459 		if (ATTR_TYPE_DOUBLE.equalsIgnoreCase(type))
460 			return new Double(trimmed);
461 		else if (ATTR_TYPE_LONG.equalsIgnoreCase(type))
462 			return new Long(trimmed);
463 		else if (ATTR_TYPE_URI.equalsIgnoreCase(type))
464 			try {
465 				Class<?> uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$
466 				Constructor<?> constructor = uriClazz.getConstructor(new Class[] {String.class});
467 				return constructor.newInstance(new Object[] {trimmed});
468 			} catch (ClassNotFoundException e) {
469 				// oh well cannot support; just use string
470 				return value;
471 			} catch (RuntimeException e) { // got some reflection exception
472 				throw e;
473 			} catch (Exception e) {
474 				throw new RuntimeException(e.getMessage(), e);
475 			}
476 		else if (ATTR_TYPE_VERSION.equalsIgnoreCase(type))
477 			return new Version(trimmed);
478 		else if (ATTR_TYPE_SET.equalsIgnoreCase(type))
479 			return ManifestElement.getArrayFromList(trimmed, ","); //$NON-NLS-1$
480 
481 		// assume list type, anything else will throw an exception
482 		Tokenizer listTokenizer = new Tokenizer(type);
483 		String listType = listTokenizer.getToken("<"); //$NON-NLS-1$
484 		if (!ATTR_TYPE_LIST.equalsIgnoreCase(listType))
485 			throw new RuntimeException("Unsupported type: " + type); //$NON-NLS-1$
486 		char c = listTokenizer.getChar();
487 		String componentType = ATTR_TYPE_STRING;
488 		if (c == '<') {
489 			componentType = listTokenizer.getToken(">"); //$NON-NLS-1$
490 			if (listTokenizer.getChar() != '>')
491 				throw new RuntimeException("Invalid type, missing ending '>' : " + type); //$NON-NLS-1$
492 		}
493 		List<String> tokens = new Tokenizer(value).getEscapedTokens(","); //$NON-NLS-1$
494 		List<Object> components = new ArrayList<>();
495 		for (String component : tokens) {
496 			components.add(convertValue(componentType, component));
497 		}
498 		return components;
499 	}
500 
createHostSpecification(ManifestElement spec, State state)501 	static HostSpecification createHostSpecification(ManifestElement spec, State state) {
502 		if (spec == null)
503 			return null;
504 		HostSpecificationImpl result = new HostSpecificationImpl();
505 		result.setName(spec.getValue());
506 		result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
507 		String multiple = spec.getDirective("multiple-hosts"); //$NON-NLS-1$
508 		if (multiple == null)
509 			multiple = getPlatformProperty(state, "osgi.support.multipleHosts"); //$NON-NLS-1$
510 		result.setIsMultiHost("true".equals(multiple)); //$NON-NLS-1$
511 		result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS));
512 		result.setArbitraryDirectives(getDirectives(spec, DEFINED_FRAGMENT_HOST_DIRECTIVES));
513 		return result;
514 	}
515 
createGenericRequires(ManifestElement[] equinoxRequires, ManifestElement[] osgiRequires, String[] brees)516 	private static GenericSpecification[] createGenericRequires(ManifestElement[] equinoxRequires, ManifestElement[] osgiRequires, String[] brees) throws BundleException {
517 		List<GenericSpecification> result = createEquinoxRequires(equinoxRequires);
518 		result = createOSGiRequires(osgiRequires, result);
519 		result = convertBREEs(brees, result);
520 		return result == null ? null : result.toArray(new GenericSpecification[result.size()]);
521 	}
522 
convertBREEs(String[] brees, List<GenericSpecification> result)523 	static List<GenericSpecification> convertBREEs(String[] brees, List<GenericSpecification> result) throws BundleException {
524 		if (brees == null || brees.length == 0)
525 			return result;
526 		if (result == null)
527 			result = new ArrayList<>(brees.length);
528 		List<String> breeFilters = new ArrayList<>();
529 		for (String bree : brees)
530 			breeFilters.add(createOSGiEERequirementFilter(bree));
531 		String filterSpec;
532 		if (breeFilters.size() == 1) {
533 			filterSpec = breeFilters.get(0);
534 		} else {
535 			StringBuilder filterBuf = new StringBuilder("(|"); //$NON-NLS-1$
536 			for (String breeFilter : breeFilters) {
537 				filterBuf.append(breeFilter);
538 			}
539 			filterSpec = filterBuf.append(")").toString(); //$NON-NLS-1$
540 		}
541 		GenericSpecificationImpl spec = new GenericSpecificationImpl();
542 		spec.setResolution(GenericSpecificationImpl.RESOLUTION_FROM_BREE);
543 		spec.setType(StateImpl.OSGI_EE_NAMESPACE);
544 		try {
545 			FilterImpl filter = FilterImpl.newInstance(filterSpec);
546 			spec.setMatchingFilter(filter);
547 			String name = filter.getPrimaryKeyValue(spec.getType());
548 			if (name != null)
549 				spec.setName(name);
550 		} catch (InvalidSyntaxException e) {
551 			throw new BundleException("Error converting required execution environment.", e); //$NON-NLS-1$
552 		}
553 		result.add(spec);
554 		return result;
555 	}
556 
createOSGiEERequirementFilter(String bree)557 	private static String createOSGiEERequirementFilter(String bree) throws BundleException {
558 		String[] nameVersion = getOSGiEENameVersion(bree);
559 		String eeName = nameVersion[0];
560 		String v = nameVersion[1];
561 		String filterSpec;
562 		if (v == null)
563 			filterSpec = "(osgi.ee=" + eeName + ")"; //$NON-NLS-1$ //$NON-NLS-2$
564 		else
565 			filterSpec = "(&(osgi.ee=" + eeName + ")(version=" + v + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
566 		try {
567 			// do a sanity check
568 			FilterImpl.newInstance(filterSpec);
569 		} catch (InvalidSyntaxException e) {
570 			filterSpec = "(osgi.ee=" + bree + ")"; //$NON-NLS-1$ //$NON-NLS-2$
571 			try {
572 				// do another sanity check
573 				FilterImpl.newInstance(filterSpec);
574 			} catch (InvalidSyntaxException e1) {
575 				throw new BundleException("Error converting required execution environment.", e1); //$NON-NLS-1$
576 			}
577 		}
578 		return filterSpec;
579 	}
580 
getOSGiEENameVersion(String bree)581 	static String[] getOSGiEENameVersion(String bree) {
582 		String ee1 = null;
583 		String ee2 = null;
584 		String v1 = null;
585 		String v2 = null;
586 		int separator = bree.indexOf('/');
587 		if (separator <= 0 || separator == bree.length() - 1) {
588 			ee1 = bree;
589 		} else {
590 			ee1 = bree.substring(0, separator);
591 			ee2 = bree.substring(separator + 1);
592 		}
593 		int v1idx = ee1.indexOf('-');
594 		if (v1idx > 0 && v1idx < ee1.length() - 1) {
595 			// check for > 0 to avoid EEs starting with -
596 			// check for < len - 1 to avoid ending with -
597 			try {
598 				v1 = ee1.substring(v1idx + 1);
599 				// sanity check version format
600 				Version.parseVersion(v1);
601 				ee1 = ee1.substring(0, v1idx);
602 			} catch (IllegalArgumentException e) {
603 				v1 = null;
604 			}
605 		}
606 
607 		int v2idx = ee2 == null ? -1 : ee2.indexOf('-');
608 		if (v2idx > 0 && v2idx < ee2.length() - 1) {
609 			// check for > 0 to avoid EEs starting with -
610 			// check for < len - 1 to avoid ending with -
611 			try {
612 				v2 = ee2.substring(v2idx + 1);
613 				Version.parseVersion(v2);
614 				ee2 = ee2.substring(0, v2idx);
615 			} catch (IllegalArgumentException e) {
616 				v2 = null;
617 			}
618 		}
619 
620 		if (v1 == null)
621 			v1 = v2;
622 		if (v1 != null && v2 != null && !v1.equals(v2)) {
623 			ee1 = bree;
624 			ee2 = null;
625 			v1 = null;
626 			v2 = null;
627 		}
628 		if ("J2SE".equals(ee1)) //$NON-NLS-1$
629 			ee1 = "JavaSE"; //$NON-NLS-1$
630 		if ("J2SE".equals(ee2)) //$NON-NLS-1$
631 			ee2 = "JavaSE"; //$NON-NLS-1$
632 
633 		String eeName = ee1 + (ee2 == null ? "" : '/' + ee2); //$NON-NLS-1$
634 
635 		return new String[] {eeName, v1};
636 	}
637 
createOSGiRequires(ManifestElement[] osgiRequires, List<GenericSpecification> result)638 	static List<GenericSpecification> createOSGiRequires(ManifestElement[] osgiRequires, List<GenericSpecification> result) throws BundleException {
639 		if (osgiRequires == null)
640 			return result;
641 		if (result == null)
642 			result = new ArrayList<>();
643 		for (ManifestElement element : osgiRequires) {
644 			String[] namespaces = element.getValueComponents();
645 			for (String namespace : namespaces) {
646 				GenericSpecificationImpl spec = new GenericSpecificationImpl();
647 				spec.setType(namespace);
648 				String filterSpec = element.getDirective(Constants.FILTER_DIRECTIVE);
649 				if (filterSpec != null) {
650 					try {
651 						FilterImpl filter = FilterImpl.newInstance(filterSpec);
652 						spec.setMatchingFilter(filter);
653 						String name = filter.getPrimaryKeyValue(namespace);
654 						if (name != null)
655 							spec.setName(name);
656 					} catch (InvalidSyntaxException e) {
657 						String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.REQUIRE_CAPABILITY, element.toString());
658 						throw new BundleException(message + " : filter", BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$
659 					}
660 				}
661 				String resolutionDirective = element.getDirective(Constants.RESOLUTION_DIRECTIVE);
662 				int resolution = 0;
663 				if (Constants.RESOLUTION_OPTIONAL.equals(resolutionDirective))
664 					resolution |= GenericSpecification.RESOLUTION_OPTIONAL;
665 				String cardinality = element.getDirective(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE);
666 				if (Namespace.CARDINALITY_MULTIPLE.equals(cardinality))
667 					resolution |= GenericSpecification.RESOLUTION_MULTIPLE;
668 				spec.setResolution(resolution);
669 				spec.setAttributes(getAttributes(element, DEFINED_REQUIRE_CAPABILITY_ATTRS));
670 				spec.setArbitraryDirectives(getDirectives(element, DEFINED_REQUIRE_CAPABILITY_DIRECTIVES));
671 				result.add(spec);
672 			}
673 		}
674 		return result;
675 	}
676 
createEquinoxRequires(ManifestElement[] equinoxRequires)677 	private static List<GenericSpecification> createEquinoxRequires(ManifestElement[] equinoxRequires) throws BundleException {
678 		if (equinoxRequires == null)
679 			return null;
680 		ArrayList<GenericSpecification> results = new ArrayList<>(equinoxRequires.length);
681 		for (ManifestElement equinoxRequire : equinoxRequires) {
682 			String[] genericNames = equinoxRequire.getValueComponents();
683 			for (String genericName : genericNames) {
684 				GenericSpecificationImpl spec = new GenericSpecificationImpl();
685 				int colonIdx = genericName.indexOf(':');
686 				if (colonIdx > 0) {
687 					spec.setName(genericName.substring(0, colonIdx));
688 					spec.setType(genericName.substring(colonIdx + 1));
689 				} else {
690 					spec.setName(genericName);
691 				}
692 				try {
693 					spec.setMatchingFilter(equinoxRequire.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE), true);
694 				} catch (InvalidSyntaxException e) {
695 					String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, GENERIC_REQUIRE, equinoxRequire.toString());
696 					throw new BundleException(message + " : " + Constants.SELECTION_FILTER_ATTRIBUTE, BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$
697 				}
698 				String optional = equinoxRequire.getAttribute(OPTIONAL_ATTR);
699 				String multiple = equinoxRequire.getAttribute(MULTIPLE_ATTR);
700 				int resolution = 0;
701 				if (TRUE.equals(optional))
702 					resolution |= GenericSpecification.RESOLUTION_OPTIONAL;
703 				if (TRUE.equals(multiple))
704 					resolution |= GenericSpecification.RESOLUTION_MULTIPLE;
705 				spec.setResolution(resolution);
706 				results.add(spec);
707 			}
708 		}
709 		return results;
710 	}
711 
createGenericCapabilities(ManifestElement[] equinoxCapabilities, ManifestElement[] osgiCapabilities, BundleDescription description)712 	private static GenericDescription[] createGenericCapabilities(ManifestElement[] equinoxCapabilities, ManifestElement[] osgiCapabilities, BundleDescription description) throws BundleException {
713 		List<GenericDescription> result = createEquinoxCapabilities(equinoxCapabilities);
714 		result = createOSGiCapabilities(osgiCapabilities, result, description);
715 		return result == null ? null : result.toArray(new GenericDescription[result.size()]);
716 	}
717 
createOSGiCapabilities(ManifestElement[] osgiCapabilities, List<GenericDescription> result, BundleDescription description)718 	static List<GenericDescription> createOSGiCapabilities(ManifestElement[] osgiCapabilities, List<GenericDescription> result, BundleDescription description) throws BundleException {
719 		if (result == null)
720 			result = new ArrayList<>(osgiCapabilities == null ? 1 : osgiCapabilities.length + 1);
721 		// Always have an osgi.identity capability if there is a symbolic name.
722 		GenericDescription osgiIdentity = createOsgiIdentityCapability(description);
723 		if (osgiIdentity != null)
724 			// always add the capability to the front
725 			result.add(0, osgiIdentity);
726 		return createOSGiCapabilities(osgiCapabilities, result, (Integer) null);
727 	}
728 
createOSGiCapabilities(ManifestElement[] osgiCapabilities, List<GenericDescription> result, Integer profileIndex)729 	static List<GenericDescription> createOSGiCapabilities(ManifestElement[] osgiCapabilities, List<GenericDescription> result, Integer profileIndex) throws BundleException {
730 		if (osgiCapabilities == null)
731 			return result;
732 		if (result == null)
733 			result = new ArrayList<>(osgiCapabilities.length);
734 
735 		for (ManifestElement element : osgiCapabilities) {
736 			String[] namespaces = element.getValueComponents();
737 			for (String namespace : namespaces) {
738 				if (IdentityNamespace.IDENTITY_NAMESPACE.equals(namespace))
739 					throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$
740 
741 				GenericDescriptionImpl desc = new GenericDescriptionImpl();
742 				desc.setType(namespace);
743 				Map<String, Object> mapAttrs = getAttributes(element, new String[0]);
744 				if (profileIndex != null)
745 					mapAttrs.put(ExportPackageDescriptionImpl.EQUINOX_EE, profileIndex);
746 				Dictionary<String, Object> attrs = mapAttrs == null ? new Hashtable<String, Object>() : new Hashtable<>(mapAttrs);
747 				desc.setAttributes(attrs);
748 				Map<String, String> directives = new HashMap<>();
749 				Enumeration<String> keys = element.getDirectiveKeys();
750 				if (keys != null)
751 					for (keys = element.getDirectiveKeys(); keys.hasMoreElements();) {
752 						String key = keys.nextElement();
753 						directives.put(key, element.getDirective(key));
754 					}
755 				desc.setDirectives(directives);
756 				result.add(desc);
757 			}
758 		}
759 		return result;
760 	}
761 
createEquinoxCapabilities(ManifestElement[] equinoxCapabilities)762 	private static List<GenericDescription> createEquinoxCapabilities(ManifestElement[] equinoxCapabilities) throws BundleException {
763 		if (equinoxCapabilities == null)
764 			return null;
765 		ArrayList<GenericDescription> results = new ArrayList<>(equinoxCapabilities.length);
766 		for (ManifestElement equinoxCapability : equinoxCapabilities) {
767 			String[] genericNames = equinoxCapability.getValueComponents();
768 			for (String genericName : genericNames) {
769 				GenericDescriptionImpl desc = new GenericDescriptionImpl();
770 				String name = genericName;
771 				int colonIdx = genericName.indexOf(':');
772 				if (colonIdx > 0) {
773 					name = genericName.substring(0, colonIdx);
774 					desc.setType(genericName.substring(colonIdx + 1));
775 					if (IdentityNamespace.IDENTITY_NAMESPACE.equals(desc.getType()))
776 						throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$
777 				}
778 				Map<String, Object> mapAttrs = getAttributes(equinoxCapability, new String[] {Constants.VERSION_ATTRIBUTE});
779 				Dictionary<String, Object> attrs = mapAttrs == null ? new Hashtable<String, Object>() : new Hashtable<>(mapAttrs);
780 				attrs.put(desc.getType(), name);
781 				String versionString = equinoxCapability.getAttribute(Constants.VERSION_ATTRIBUTE);
782 				if (versionString != null)
783 					attrs.put(Constants.VERSION_ATTRIBUTE, Version.parseVersion(versionString));
784 				desc.setAttributes(attrs);
785 				results.add(desc);
786 			}
787 		}
788 		return results;
789 	}
790 
createNativeCode(ManifestElement[] nativeCode)791 	private static NativeCodeSpecification createNativeCode(ManifestElement[] nativeCode) throws BundleException {
792 		if (nativeCode == null)
793 			return null;
794 		NativeCodeSpecificationImpl result = new NativeCodeSpecificationImpl();
795 		result.setName(Constants.BUNDLE_NATIVECODE);
796 		int length = nativeCode.length;
797 		if (length > 0 && nativeCode[length - 1].getValue().equals("*")) { //$NON-NLS-1$
798 			result.setOptional(true);
799 			length--;
800 		}
801 		NativeCodeDescriptionImpl[] suppliers = new NativeCodeDescriptionImpl[length];
802 		for (int i = 0; i < length; i++) {
803 			suppliers[i] = createNativeCodeDescription(nativeCode[i]);
804 		}
805 		result.setPossibleSuppliers(suppliers);
806 		return result;
807 	}
808 
createNativeCodeDescription(ManifestElement manifestElement)809 	private static NativeCodeDescriptionImpl createNativeCodeDescription(ManifestElement manifestElement) throws BundleException {
810 		NativeCodeDescriptionImpl result = new NativeCodeDescriptionImpl();
811 		result.setName(Constants.BUNDLE_NATIVECODE);
812 		result.setNativePaths(manifestElement.getValueComponents());
813 		result.setOSNames(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_OSNAME));
814 		result.setProcessors(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_PROCESSOR));
815 		result.setOSVersions(createVersionRanges(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_OSVERSION)));
816 		result.setLanguages(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_LANGUAGE));
817 		try {
818 			result.setFilter(manifestElement.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE));
819 		} catch (InvalidSyntaxException e) {
820 			String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.BUNDLE_NATIVECODE, manifestElement.toString());
821 			throw new BundleException(message + " : " + Constants.SELECTION_FILTER_ATTRIBUTE, BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$
822 		}
823 		return result;
824 	}
825 
createVersionRanges(String[] ranges)826 	private static VersionRange[] createVersionRanges(String[] ranges) {
827 		if (ranges == null)
828 			return null;
829 		VersionRange[] result = new VersionRange[ranges.length];
830 		for (int i = 0; i < result.length; i++)
831 			result[i] = new VersionRange(ranges[i]);
832 		return result;
833 	}
834 
getVersionRange(String versionRange)835 	private static VersionRange getVersionRange(String versionRange) {
836 		if (versionRange == null)
837 			return null;
838 		return new VersionRange(versionRange);
839 	}
840 
checkImportExportSyntax(String headerKey, ManifestElement[] elements, boolean export, boolean dynamic, boolean jreBundle)841 	public static void checkImportExportSyntax(String headerKey, ManifestElement[] elements, boolean export, boolean dynamic, boolean jreBundle) throws BundleException {
842 		if (elements == null)
843 			return;
844 		int length = elements.length;
845 		Set<String> packages = new HashSet<>(length);
846 		for (int i = 0; i < length; i++) {
847 			// check for duplicate imports
848 			String[] packageNames = elements[i].getValueComponents();
849 			for (String packageName : packageNames) {
850 				if (!export && !dynamic && packages.contains(packageName)) {
851 					String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
852 					throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_DUPLICATES, packageName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
853 				}
854 				// check for java.*
855 				if (export && !jreBundle && packageName.startsWith("java.")) { //$NON-NLS-1$
856 					String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
857 					throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_JAVA, packageName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
858 				}
859 				packages.add(packageName);
860 			}
861 			// check for version/specification version mismatch
862 			String version = elements[i].getAttribute(Constants.VERSION_ATTRIBUTE);
863 			if (version != null) {
864 				String specVersion = elements[i].getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION);
865 				if (specVersion != null && !specVersion.equals(version))
866 					throw new BundleException(NLS.bind(StateMsg.HEADER_VERSION_ERROR, Constants.VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION), BundleException.MANIFEST_ERROR);
867 			}
868 			// check for bundle-symbolic-name and bundle-verion attibures
869 			// (failure)
870 			if (export) {
871 				if (elements[i].getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE) != null) {
872 					String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
873 					throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
874 				}
875 				if (elements[i].getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) != null) {
876 					String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
877 					throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
878 				}
879 			}
880 		}
881 	}
882 
checkForDuplicateDirectivesAttributes(String headerKey, ManifestElement[] elements)883 	private static void checkForDuplicateDirectivesAttributes(String headerKey, ManifestElement[] elements) throws BundleException {
884 		// check for duplicate directives
885 		for (ManifestElement element : elements) {
886 			Enumeration<String> directiveKeys = element.getDirectiveKeys();
887 			if (directiveKeys != null) {
888 				while (directiveKeys.hasMoreElements()) {
889 					String key = directiveKeys.nextElement();
890 					String[] directives = element.getDirectives(key);
891 					if (directives.length > 1) {
892 						String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString());
893 						throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_DIRECTIVE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
894 					}
895 				}
896 			}
897 			Enumeration<String> attrKeys = element.getKeys();
898 			if (attrKeys != null) {
899 				while (attrKeys.hasMoreElements()) {
900 					String key = attrKeys.nextElement();
901 					String[] attrs = element.getAttributes(key);
902 					if (attrs.length > 1) {
903 						String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString());
904 						throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_ATTRIBUTE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
905 					}
906 				}
907 			}
908 		}
909 	}
910 
checkExtensionBundle(String headerKey, ManifestElement[] elements)911 	private static void checkExtensionBundle(String headerKey, ManifestElement[] elements) throws BundleException {
912 		if (elements.length == 0 || elements[0].getDirective(Constants.EXTENSION_DIRECTIVE) == null)
913 			return;
914 		String hostName = elements[0].getValue();
915 		// XXX: The extension bundle check is done against system.bundle and org.eclipse.osgi
916 		if (!hostName.equals(Constants.SYSTEM_BUNDLE_SYMBOLICNAME) && !hostName.equals(EquinoxContainer.NAME)) {
917 			String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[0].toString());
918 			throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXTENSION_ERROR, hostName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
919 		}
920 	}
921 
createOsgiIdentityCapability(BundleDescription description)922 	static GenericDescription createOsgiIdentityCapability(BundleDescription description) {
923 		if (description.getSymbolicName() == null)
924 			return null;
925 		GenericDescriptionImpl result = new GenericDescriptionImpl();
926 		result.setType(IdentityNamespace.IDENTITY_NAMESPACE);
927 		Dictionary<String, Object> attributes = new Hashtable<>(description.getDeclaredAttributes());
928 		// remove osgi.wiring.bundle and bundle-version attributes
929 		attributes.remove(BundleNamespace.BUNDLE_NAMESPACE);
930 		attributes.remove(Constants.BUNDLE_VERSION_ATTRIBUTE);
931 		attributes.put(IdentityNamespace.IDENTITY_NAMESPACE, description.getSymbolicName());
932 		attributes.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, description.getHost() == null ? IdentityNamespace.TYPE_BUNDLE : IdentityNamespace.TYPE_FRAGMENT);
933 		attributes.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, description.getVersion());
934 		result.setAttributes(attributes);
935 		Map<String, String> directives = new HashMap<>(description.getDeclaredDirectives());
936 		// remove defaults directive values
937 		if (!description.isSingleton())
938 			directives.remove(Constants.SINGLETON_DIRECTIVE);
939 		if (description.attachFragments() && description.dynamicFragments())
940 			directives.remove(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
941 		result.setDirectives(directives);
942 		return result;
943 	}
944 }
945