Skip to main content
summaryrefslogtreecommitdiffstats
blob: bd0be54a94fda8949defbf76c501edf23325eb66 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
/*******************************************************************************
 * Copyright (c) 2007, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     EclipseSource Corporation - ongoing enhancements
 *******************************************************************************/
package org.eclipse.pde.internal.core;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.frameworkadmin.BundleInfo;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.IProvisioningAgentProvider;
import org.eclipse.equinox.p2.engine.IEngine;
import org.eclipse.equinox.p2.engine.IPhaseSet;
import org.eclipse.equinox.p2.engine.IProfile;
import org.eclipse.equinox.p2.engine.IProfileRegistry;
import org.eclipse.equinox.p2.engine.IProvisioningPlan;
import org.eclipse.equinox.p2.engine.PhaseSetFactory;
import org.eclipse.equinox.p2.engine.ProvisioningContext;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.IProvidedCapability;
import org.eclipse.equinox.p2.metadata.IRequirement;
import org.eclipse.equinox.p2.metadata.ITouchpointType;
import org.eclipse.equinox.p2.metadata.MetadataFactory;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionRange;
import org.eclipse.equinox.p2.planner.ProfileInclusionRules;
import org.eclipse.equinox.simpleconfigurator.manipulator.SimpleConfiguratorManipulator;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.BundleSpecification;
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
import org.eclipse.pde.core.plugin.IPluginBase;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.TargetPlatform;
import org.eclipse.pde.internal.build.BundleHelper;
import org.eclipse.pde.internal.core.plugin.PluginBase;
import org.osgi.framework.Constants;

/**
 * Utilities to read and write p2 files
 *
 * @since 3.4
 */
public class P2Utils {

	public static final String P2_FLAVOR_DEFAULT = "tooling"; //$NON-NLS-1$

	public static final ITouchpointType TOUCHPOINT_OSGI = MetadataFactory.createTouchpointType("org.eclipse.equinox.p2.osgi", Version.createOSGi(1, 0, 0)); //$NON-NLS-1$
	private static final String CAPABILITY_NS_OSGI_BUNDLE = "osgi.bundle"; //$NON-NLS-1$
	private static final String CAPABILITY_NS_OSGI_FRAGMENT = "osgi.fragment"; //$NON-NLS-1$
	public static final String TYPE_ECLIPSE_BUNDLE = "bundle"; //$NON-NLS-1$
	public static final String NAMESPACE_ECLIPSE_TYPE = "org.eclipse.equinox.p2.eclipse.type"; //$NON-NLS-1$
	public static final IProvidedCapability BUNDLE_CAPABILITY = MetadataFactory.createProvidedCapability(NAMESPACE_ECLIPSE_TYPE, TYPE_ECLIPSE_BUNDLE, Version.createOSGi(1, 0, 0));
	public static final String CAPABILITY_NS_JAVA_PACKAGE = "java.package"; //$NON-NLS-1$

	/**
	 * Returns bundles defined by the 'bundles.info' file in the
	 * specified location, or <code>null</code> if none. The "bundles.info" file
	 * is assumed to be at a fixed location relative to the configuration area URL.
	 * This method will also look for a "source.info".  If available, any source
	 * bundles found will also be added to the returned list.  If bundle URLs found
	 * in the bundles.info are relative, they will be appended to platformHome to
	 * make them absolute.
	 *
	 * @param platformHome absolute path in the local file system to an installation
	 * @param configurationArea url location of the configuration directory to search for bundles.info and source.info
	 * @return URLs of all bundles in the installation or <code>null</code> if not able
	 * 	to locate a bundles.info
	 */
	public static URL[] readBundlesTxt(String platformHome, File configurationArea) {
		if (configurationArea == null) {
			return null;
		}
		try {
			BundleInfo[] bundles = readBundles(platformHome, configurationArea);
			if (bundles == null) {
				return null;
			}
			int length = bundles.length;
			BundleInfo[] srcBundles = readSourceBundles(platformHome, configurationArea);
			if (srcBundles != null) {
				length += srcBundles.length;
			}
			URL[] urls = new URL[length];
			copyURLs(urls, 0, bundles);
			if (srcBundles != null && srcBundles.length > 0) {
				copyURLs(urls, bundles.length, srcBundles);
			}
			return urls;
		} catch (MalformedURLException e) {
			PDECore.log(e);
			return null;
		}
	}

	/**
	 * Returns bundles defined by the 'bundles.info' relative to the given
	 * home and configuration area, or <code>null</code> if none.
	 * The "bundles.info" file is assumed to be at a fixed location relative to the
	 * configuration area URL.
	 *
	 * @param platformHome absolute path in the local file system to an installation
	 * @param configurationArea url location of the configuration directory to search
	 *  for bundles.info
	 * @return all bundles in the installation or <code>null</code> if not able
	 * 	to locate a bundles.info
	 */
	public static BundleInfo[] readBundles(String platformHome, File configurationArea) {
		IPath basePath = new Path(platformHome);
		if (configurationArea == null) {
			return null;
		}
		try {
			File bundlesTxt = new File(configurationArea, SimpleConfiguratorManipulator.BUNDLES_INFO_PATH);
			File home = basePath.toFile();
			BundleInfo[] bundles = getBundlesFromFile(bundlesTxt, home);
			if (bundles == null || bundles.length == 0) {
				return null;
			}
			return bundles;
		} catch (IOException e) {
			PDECore.log(e);
			return null;
		}
	}

	/**
	 * Returns source bundles defined by the 'source.info' file in the
	 * specified location, or <code>null</code> if none. The "source.info" file
	 * is assumed to be at a fixed location relative to the configuration area URL.
	 *
	 * @param platformHome absolute path in the local file system to an installation
	 * @param configurationArea url location of the configuration directory to search for bundles.info and source.info
	 * @return all source bundles in the installation or <code>null</code> if not able
	 * 	to locate a source.info
	 */
	public static BundleInfo[] readSourceBundles(String platformHome, File configurationArea) {
		IPath basePath = new Path(platformHome);
		if (configurationArea == null) {
			return null;
		}
		try {
			File home = basePath.toFile();
			File srcBundlesTxt = new File(configurationArea, SimpleConfiguratorManipulator.SOURCE_INFO_PATH);
			BundleInfo[] srcBundles = getBundlesFromFile(srcBundlesTxt, home);
			if (srcBundles == null || srcBundles.length == 0) {
				return null;
			}
			return srcBundles;
		} catch (IOException e) {
			PDECore.log(e);
			return null;
		}
	}

	/**
	 * Copies URLs from the given bundle info objects into the specified array starting at the given index.
	 *
	 * @param dest array to copy URLs into
	 * @param start index to start copying into
	 * @param infos associated bundle infos
	 * @throws MalformedURLException
	 */
	private static void copyURLs(URL[] dest, int start, BundleInfo[] infos) throws MalformedURLException {
		for (BundleInfo info : infos) {
			dest[start++] = new File(info.getLocation()).toURL();
		}
	}

	/**
	 * Returns a list of {@link BundleInfo} for each bundle entry or
	 * <code>null</code> if there is a problem reading the file.
	 *
	 * @param filePath
	 *            the file to read
	 * @param home
	 *            the path describing the base location of the platform install
	 * @return list containing URL locations or <code>null</code>
	 * @throws IOException
	 */
	private static BundleInfo[] getBundlesFromFile(File filePath, File home) throws IOException {
		SimpleConfiguratorManipulator manipulator = PDECore.getDefault()
				.acquireService(SimpleConfiguratorManipulator.class);
		if (manipulator == null) {
			return null;
		}
		// the input stream will be buffered for us
		try (InputStream in = Files.newInputStream(filePath.toPath())) {
			return manipulator.loadConfiguration(in, home.toURI());
		} catch (NoSuchFileException e) {
			return null;
		}
	}

	/**
	 * Creates a bundles.info file in the given directory containing the name,
	 * version, location, start level and expected state of the bundles in the
	 * launch.  Will also create a source.info containing all of the
	 * source bundles in the launch. The map of bundles must be of the form
	 * IModelPluginBase to a String ("StartLevel:AutoStart").  Returns the
	 * URL location of the bundle.info or <code>null</code> if there was a
	 * problem creating it.
	 *
	 * @param bundles map containing all bundles to write to the bundles.info, maps IPluginModelBase to String ("StartLevel:AutoStart")
	 * @param defaultStartLevel start level to use when "default" is the start level
	 * @param defaultAutoStart auto start setting to use when "default" is the auto start setting
	 * @param directory configuration directory to create the files in
	 * @param osgiBundleList a list of bundles coming from a template config.ini
	 * @return URL location of the bundles.info or <code>null</code>
	 */
	public static URL writeBundlesTxt(Map<?, ?> bundles, int defaultStartLevel, boolean defaultAutoStart, File directory, String osgiBundleList) {
		if (bundles.isEmpty()) {
			return null;
		}

		// Parse the osgi bundle list for start levels
		Map<String, String> osgiStartLevels = new HashMap<>();
		if (osgiBundleList != null) {
			StringTokenizer tokenizer = new StringTokenizer(osgiBundleList, ","); //$NON-NLS-1$
			while (tokenizer.hasMoreTokens()) {
				String token = tokenizer.nextToken();
				int index = token.indexOf('@');
				if (index != -1) {
					String modelName = token.substring(0, index);
					String startData = token.substring(index + 1);
					index = startData.indexOf(':');
					String level = index > 0 ? startData.substring(0, index) : "default"; //$NON-NLS-1$
					String auto = index > 0 && index < startData.length() - 1 ? startData.substring(index + 1) : "default"; //$NON-NLS-1$
					if ("start".equals(auto)) { //$NON-NLS-1$
						auto = "true"; //$NON-NLS-1$
					}
					osgiStartLevels.put(modelName, level + ':' + auto);
				}
			}
		}

		List<BundleInfo> bundleInfo = new ArrayList<>(bundles.size());
		List<BundleInfo> sourceInfo = new ArrayList<>(bundles.size());
		for (final Object name : bundles.keySet()) {
			final IPluginModelBase currentModel = (IPluginModelBase) name;
			IPluginBase base = currentModel.getPluginBase();

			BundleInfo info = new BundleInfo();
			String installLocation = currentModel.getInstallLocation();
			if (installLocation != null) {
				info.setLocation(new File(installLocation).toURI());
				if (base instanceof PluginBase && ((PluginBase) base).getBundleSourceEntry() != null) {
					info.setSymbolicName(base.getId());
					info.setVersion(base.getVersion());
					info.setStartLevel(-1);
					info.setMarkedAsStarted(false);
					sourceInfo.add(info);
				} else if (base != null) {
					info.setSymbolicName(base.getId());
					info.setVersion(base.getVersion());
					String currentLevel = (String) bundles.get(currentModel);
					// override the start level setting if something comes from the config.ini
					if (osgiStartLevels.containsKey(base.getId())) {
						currentLevel = osgiStartLevels.get(base.getId());
					}
					int index = currentLevel.indexOf(':');
					String levelString = index > 0 ? currentLevel.substring(0, index) : "default"; //$NON-NLS-1$
					String auto = index > 0 && index < currentLevel.length() - 1 ? currentLevel.substring(index + 1) : "default"; //$NON-NLS-1$
					boolean isAuto = true;
					int level = -1;
					if ("default".equals(auto)) {//$NON-NLS-1$
						isAuto = defaultAutoStart;
					} else {
						isAuto = Boolean.parseBoolean(auto);
					}
					if ("default".equals(levelString)) {//$NON-NLS-1$
						level = defaultStartLevel;
					} else {
						try {
							level = Integer.parseInt(levelString);
						} catch (NumberFormatException e) {
							PDECore.log(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, "Error writing bundles, could not parse start level for bundle " + currentModel)); //$NON-NLS-1$
						}
					}
					info.setMarkedAsStarted(isAuto);
					info.setStartLevel(level);
					bundleInfo.add(info);
				}
			} else {
				PDECore.log(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, "Error writing bundles, could not find the bundle location for bundle " + currentModel)); //$NON-NLS-1$
			}
		}

		File bundlesTxt = new File(directory, SimpleConfiguratorManipulator.BUNDLES_INFO_PATH);
		File srcBundlesTxt = new File(directory, SimpleConfiguratorManipulator.SOURCE_INFO_PATH);

		BundleInfo[] infos = bundleInfo.toArray(new BundleInfo[bundleInfo.size()]);
		BundleInfo[] sources = sourceInfo.toArray(new BundleInfo[sourceInfo.size()]);

		SimpleConfiguratorManipulator manipulator = BundleHelper.getDefault()
				.acquireService(SimpleConfiguratorManipulator.class);
		try {
			manipulator.saveConfiguration(infos, bundlesTxt, null);
			manipulator.saveConfiguration(sources, srcBundlesTxt, null);
		} catch (IOException e) {
			PDECore.logException(e);
			return null;
		}

		if (!bundlesTxt.exists()) {
			return null;
		}
		try {
			return bundlesTxt.toURL();
		} catch (MalformedURLException e) {
			PDECore.logException(e);
			return null;
		}
	}

	/**
	 * Returns whether a profile with the given ID exists in a profile registry
	 * stored in the give p2 data area.
	 *
	 * @param profileID id of the profile to check
	 * @param p2DataArea data area where the profile registry is
	 * @return whether the profile exists
	 */
	public static boolean profileExists(String profileID, File p2DataArea) throws CoreException {
		IProvisioningAgentProvider provider = PDECore.getDefault().acquireService(IProvisioningAgentProvider.class);
		if (provider == null) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, PDECoreMessages.P2Utils_UnableToAcquireP2Service));
		}

		IProvisioningAgent agent = provider.createAgent(p2DataArea.toURI());
		if (agent == null) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, PDECoreMessages.P2Utils_UnableToAcquireP2Service));
		}

		IProfileRegistry registry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
		if (registry == null) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, PDECoreMessages.P2Utils_UnableToAcquireP2Service));
		}

		return registry.containsProfile(profileID);
	}

	/**
	 * Generates a profile containing metadata for all of the bundles in the provided collection.
	 * The profile will have the given profile ID and will be persisted in the profile registry
	 * directory inside the given p2 data area.
	 *
	 * @param profileID the ID to be used when creating the profile, if a profile with the same name exists, it will be overwritten
	 * @param p2DataArea the directory which contains p2 data including the profile registry, if the directory path doesn't exist it will be created
	 * @param bundles the collection of IPluginModelBase objects representing bundles to create metadata for and add to the profile
	 *
	 * @throws CoreException if the profile cannot be generated
	 */
	public static void createProfile(String profileID, File p2DataArea, Collection<?> bundles) throws CoreException {
		// Acquire the required p2 services, creating an agent in the target p2 metadata area
		IProvisioningAgentProvider provider = PDECore.getDefault().acquireService(IProvisioningAgentProvider.class);
		if (provider == null) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, PDECoreMessages.P2Utils_UnableToAcquireP2Service));
		}

		IProvisioningAgent agent = provider.createAgent(p2DataArea.toURI());
		if (agent == null) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, PDECoreMessages.P2Utils_UnableToAcquireP2Service));
		}

		IProfileRegistry registry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
		if (registry == null) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, PDECoreMessages.P2Utils_UnableToAcquireP2Service));
		}

		IEngine engine = (IEngine) agent.getService(IEngine.SERVICE_NAME);
		if (engine == null) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, PDECoreMessages.P2Utils_UnableToAcquireP2Service));
		}

		// Delete any previous profiles with the same ID
		registry.removeProfile(profileID);

		// Create the profile
		IProfile profile = null;
		Map<String, String> props = new HashMap<>();
//		props.setProperty(IProfile.PROP_INSTALL_FOLDER, registryArea.getAbsolutePath());
		props.put(IProfile.PROP_INSTALL_FEATURES, Boolean.TRUE.toString());
		// Set up environment and nationalization properties so OS specific fragments are installed
		props.put(IProfile.PROP_ENVIRONMENTS, generateEnvironmentProperties());
		props.put(IProfile.PROP_NL, TargetPlatform.getNL());

		profile = registry.addProfile(profileID, props);

		// Create metadata for the bundles
		Collection<IInstallableUnit> ius = new ArrayList<>(bundles.size());
		for (final Object name : bundles) {
			IPluginModelBase model = (IPluginModelBase) name;
			BundleDescription bundle = model.getBundleDescription();
			ius.add(createBundleIU(bundle));
		}

		// Add the metadata to the profile
		ProvisioningContext context = new ProvisioningContext(agent);
		IProvisioningPlan plan = engine.createPlan(profile, context);
		for (final IInstallableUnit iu : ius) {
			plan.addInstallableUnit(iu);
			plan.setInstallableUnitProfileProperty(iu, "org.eclipse.equinox.p2.internal.inclusion.rules", ProfileInclusionRules.createOptionalInclusionRule(iu)); //$NON-NLS-1$
		}
		IPhaseSet phaseSet = PhaseSetFactory.createDefaultPhaseSetExcluding(new String[] {PhaseSetFactory.PHASE_CHECK_TRUST, PhaseSetFactory.PHASE_COLLECT, PhaseSetFactory.PHASE_CONFIGURE, PhaseSetFactory.PHASE_UNCONFIGURE, PhaseSetFactory.PHASE_UNINSTALL});
		IStatus status = engine.perform(plan, phaseSet, new NullProgressMonitor());

		if (!status.isOK() && status.getSeverity() != IStatus.CANCEL) {
			throw new CoreException(status);
		}

	}

	/**
	 * Generates the environment properties string for the self hosting p2 profile by looking up the current target platform properties.
	 *
	 * @return environment properties string
	 */
	private static String generateEnvironmentProperties() {
		StringBuilder env = new StringBuilder();
		env.append("osgi.ws="); //$NON-NLS-1$
		env.append(TargetPlatform.getWS());
		env.append(","); //$NON-NLS-1$
		env.append("osgi.os="); //$NON-NLS-1$
		env.append(TargetPlatform.getOS());
		env.append(","); //$NON-NLS-1$
		env.append("osgi.arch="); //$NON-NLS-1$
		env.append(TargetPlatform.getOSArch());
		return env.toString();
	}

	/**
	 * Creates an installable unit from a bundle description
	 *
	 * @param bd bundle description to create metadata for
	 * @return an installable unit
	 */
	private static IInstallableUnit createBundleIU(BundleDescription bd) {
		InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
		iu.setSingleton(bd.isSingleton());
		iu.setId(bd.getSymbolicName());
		iu.setVersion(fromOSGiVersion(bd.getVersion()));
		iu.setFilter(bd.getPlatformFilter());
		iu.setTouchpointType(TOUCHPOINT_OSGI);

		boolean isFragment = bd.getHost() != null;

		//Process the required bundles
		BundleSpecification requiredBundles[] = bd.getRequiredBundles();
		ArrayList<IRequirement> reqsDeps = new ArrayList<>();
		if (isFragment) {
			reqsDeps.add(MetadataFactory.createRequirement(CAPABILITY_NS_OSGI_BUNDLE, bd.getHost().getName(), fromOSGiVersionRange(bd.getHost().getVersionRange()), null, false, false));
		}
		for (final BundleSpecification requiredBundle : requiredBundles) {
			reqsDeps.add(MetadataFactory.createRequirement(CAPABILITY_NS_OSGI_BUNDLE, requiredBundle.getName(), fromOSGiVersionRange(requiredBundle.getVersionRange()), null, requiredBundle.isOptional(), false));
		}

		// Process the import packages
		ImportPackageSpecification osgiImports[] = bd.getImportPackages();
		for (final ImportPackageSpecification importSpec : osgiImports) {
			String importPackageName = importSpec.getName();
			if (importPackageName.indexOf('*') != -1) {
				continue;
			}
			VersionRange versionRange = fromOSGiVersionRange(importSpec.getVersionRange());
			//TODO this needs to be refined to take into account all the attribute handled by imports
			boolean isOptional = importSpec.getDirective(Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_DYNAMIC) || importSpec.getDirective(Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_OPTIONAL);
			reqsDeps.add(MetadataFactory.createRequirement(CAPABILITY_NS_JAVA_PACKAGE, importPackageName, versionRange, null, isOptional, false));
		}
		iu.setRequirements((reqsDeps.toArray(new IRequirement[reqsDeps.size()])));

		// Create set of provided capabilities
		ArrayList<IProvidedCapability> providedCapabilities = new ArrayList<>();
		providedCapabilities.add(MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, bd.getSymbolicName(), fromOSGiVersion(bd.getVersion())));
		providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_OSGI_BUNDLE, bd.getSymbolicName(), fromOSGiVersion(bd.getVersion())));

		// Process the export package
		ExportPackageDescription exports[] = bd.getExportPackages();
		for (final ExportPackageDescription export : exports) {
			//TODO make sure that we support all the refinement on the exports
			providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_JAVA_PACKAGE, export.getName(), fromOSGiVersion(export.getVersion())));
		}
		// Here we add a bundle capability to identify bundles
		providedCapabilities.add(BUNDLE_CAPABILITY);
		if (isFragment) {
			providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_OSGI_FRAGMENT, bd.getHost().getName(), fromOSGiVersion(bd.getVersion())));
		}

		iu.setCapabilities(providedCapabilities.toArray(new IProvidedCapability[providedCapabilities.size()]));
		return MetadataFactory.createInstallableUnit(iu);
	}

	private static Version fromOSGiVersion(org.osgi.framework.Version version) {
		if (version == null) {
			return Version.MAX_VERSION;
		}
		if (version.getMajor() == Integer.MAX_VALUE && version.getMinor() == Integer.MAX_VALUE
				&& version.getMicro() == Integer.MAX_VALUE) {
			return Version.MAX_VERSION;
		}
		return Version.createOSGi(version.getMajor(), version.getMinor(), version.getMicro(), version.getQualifier());
	}

	private static VersionRange fromOSGiVersionRange(org.eclipse.osgi.service.resolver.VersionRange range) {
		if (range.equals(org.eclipse.osgi.service.resolver.VersionRange.emptyRange)) {
			return VersionRange.emptyRange;
		}
		return new VersionRange(fromOSGiVersion(range.getMinimum()), range.getIncludeMinimum(), fromOSGiVersion(range.getRight()), range.getIncludeMaximum());
	}
}

Back to the top