Skip to main content
summaryrefslogtreecommitdiffstats
blob: e315d5ba500a81c257dd3c563c345a1af0ab915e (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
/*******************************************************************************
 * Copyright (c) 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.prov.metadata.generator;

import java.io.*;
import java.util.*;
import org.eclipse.equinox.frameworkadmin.BundleInfo;
import org.eclipse.equinox.internal.prov.metadata.ArtifactKey;
import org.eclipse.equinox.internal.prov.metadata.generator.Activator;
import org.eclipse.equinox.prov.artifact.repository.ArtifactDescriptor;
import org.eclipse.equinox.prov.artifact.repository.IArtifactDescriptor;
import org.eclipse.equinox.prov.core.helpers.ServiceHelper;
import org.eclipse.equinox.prov.metadata.*;
import org.eclipse.osgi.service.environment.EnvironmentInfo;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.*;

public class MetadataGeneratorHelper {
	private static final String ECLIPSE_EXTENSIBLE_API = "Eclipse-ExtensibleAPI"; //$NON-NLS-1$

	private static final String CAPABILITY_TYPE_OSGI_PACKAGES = "osgi.packages"; //$NON-NLS-1$

	private static final Version versionMax = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);

	private static final String ECLIPSE_TOUCHPOINT = "eclipse"; //$NON-NLS-1$
	private static final Version ECLIPSE_TOUCHPOINT_VERSION = new Version(1, 0, 0);

	private static final String NATIVE_TOUCHPOINT = "native"; //$NON-NLS-1$
	private static final Version NATIVE_TOUCHPOINT_VERSION = new Version(1, 0, 0);

	private static final String ECLIPSE_ARTIFACT_NAMESPACE = "eclipse"; //$NON-NLS-1$
	private static final String ECLIPSE_ARTIFACT_CLASSIFIER = "plugin"; //$NON-NLS-1$

	private static final String ORG_ECLIPSE_EXECUTABLE = "org.eclipse.executable"; //$NON-NLS-1$
	private static final Version ORG_ECLIPSE_EXECUTABLE_VERSION = new Version(1, 0, 0);
	private static final String IU_NAMESPACE = IInstallableUnit.IU_NAMESPACE;

	private static final String[] BUNDLE_IU_PROPERTY_MAP = {Constants.BUNDLE_NAME, IInstallableUnitConstants.NAME, Constants.BUNDLE_DESCRIPTION, IInstallableUnitConstants.DESCRIPTION, Constants.BUNDLE_VENDOR, IInstallableUnitConstants.PROVIDER, Constants.BUNDLE_CONTACTADDRESS, IInstallableUnitConstants.CONTACT, Constants.BUNDLE_COPYRIGHT, IInstallableUnitConstants.COPYRIGHT, Constants.BUNDLE_DOCURL, IInstallableUnitConstants.DOC_URL, Constants.BUNDLE_UPDATELOCATION, IInstallableUnitConstants.UPDATE_SITE};

	private static final Version DEFAULT_JRE_VERSION = new Version("1.5"); //$NON-NLS-1$

	/**
	 * Creates IUs and artifact descriptors for the JRE, and adds them to the given sets.
	 * if the jreLocation is <code>null</code>, default information is generated.
	 */
	public static void createJREData(File jreLocation, Set resultantIUs, Set resultantArtifactDescriptors) {
		InstallableUnit iu = new InstallableUnit();
		iu.setSingleton(false);
		iu.setId("a.jre"); //$NON-NLS-1$
		iu.setTouchpointType(new TouchpointType(NATIVE_TOUCHPOINT, NATIVE_TOUCHPOINT_VERSION));
		if (jreLocation == null || !jreLocation.exists()) {
			//set some reasonable defaults
			iu.setVersion(DEFAULT_JRE_VERSION);
			iu.setCapabilities(generateJRECapability(null));
			resultantIUs.add(iu);
			return;
		}
		generateJREIUData(iu, jreLocation);

		//Generate artifact for JRE
		IArtifactKey key = new ArtifactKey(ECLIPSE_ARTIFACT_NAMESPACE, NATIVE_TOUCHPOINT, iu.getId(), iu.getVersion());
		iu.setArtifacts(new IArtifactKey[] {key});
		iu.setTouchpointType(new TouchpointType(NATIVE_TOUCHPOINT, new Version(1, 0, 0)));
		resultantIUs.add(iu);

		//Create the CU
		InstallableUnitFragment cu = new InstallableUnitFragment();
		cu.setId("config." + iu.getId()); //$NON-NLS-1$
		cu.setVersion(iu.getVersion());
		cu.setHost(iu.getId(), new VersionRange(iu.getVersion(), true, versionMax, true));

		cu.setTouchpointType(new TouchpointType(NATIVE_TOUCHPOINT, NATIVE_TOUCHPOINT_VERSION));
		Map touchpointData = new HashMap();
		String configurationData = "Zip.unzip(artifact, currentDir, null);";
		EnvironmentInfo info = (EnvironmentInfo) ServiceHelper.getService(Activator.getContext(), EnvironmentInfo.class.getName());
		touchpointData.put("configurationData", configurationData);
		cu.setImmutableTouchpointData(new TouchpointData(touchpointData));
		resultantIUs.add(cu);

		//Create the artifact descriptor
		IArtifactDescriptor descriptor = createArtifactDescriptor(key, jreLocation, false, true);
		resultantArtifactDescriptors.add(descriptor);
	}

	private static void generateJREIUData(InstallableUnit iu, File jreLocation) {
		//Look for a JRE profile file to set version and capabilities
		File[] profiles = jreLocation.listFiles(new FileFilter() {
			public boolean accept(File pathname) {
				return pathname.getAbsolutePath().endsWith(".profile"); //$NON-NLS-1$
			}
		});
		if (profiles.length != 1) {
			iu.setVersion(DEFAULT_JRE_VERSION);
			iu.setCapabilities(generateJRECapability(null));
			return;
		}
		String profileName = profiles[0].getAbsolutePath().substring(profiles[0].getAbsolutePath().lastIndexOf('/'));
		Version version = DEFAULT_JRE_VERSION;
		//TODO Find a better way to determine JRE version
		if (profileName.indexOf("1.5") > 0) { //$NON-NLS-1$
			version = new Version("1.5"); //$NON-NLS-1$
		} else if (profileName.indexOf("1.4") > 0) { //$NON-NLS-1$
			version = new Version("1.4"); //$NON-NLS-1$
		}
		iu.setVersion(version);
		try {
			iu.setCapabilities(generateJRECapability(new FileInputStream(profiles[0])));
		} catch (FileNotFoundException e) {
			//Shouldn't happen, but ignore and fall through to use default
		}
	}

	/**
	 * Creates IUs and artifacts for the Eclipse executable, and adds them to the given
	 * sets.
	 */
	public static void createLauncherData(File launcher, String configurationFlavor, Set resultantIUs, Set resultantArtifactDescriptors) {
		if (launcher == null || !launcher.exists())
			return;

		//Create the IU
		InstallableUnit iu = new InstallableUnit();
		iu.setSingleton(true);
		iu.setId(ORG_ECLIPSE_EXECUTABLE);
		iu.setVersion(ORG_ECLIPSE_EXECUTABLE_VERSION);

		IArtifactKey key = new ArtifactKey(ECLIPSE_ARTIFACT_NAMESPACE, NATIVE_TOUCHPOINT, ORG_ECLIPSE_EXECUTABLE, ORG_ECLIPSE_EXECUTABLE_VERSION);
		iu.setArtifacts(new IArtifactKey[] {key});
		iu.setTouchpointType(new TouchpointType(NATIVE_TOUCHPOINT, new Version(1, 0, 0)));
		resultantIUs.add(iu);

		//Create the CU
		InstallableUnitFragment cu = new InstallableUnitFragment();
		cu.setId(configurationFlavor + iu.getId());
		cu.setVersion(iu.getVersion());
		cu.setHost(iu.getId(), new VersionRange(iu.getVersion(), true, versionMax, true));

		cu.setTouchpointType(new TouchpointType(NATIVE_TOUCHPOINT, NATIVE_TOUCHPOINT_VERSION));
		Map touchpointData = new HashMap();
		String configurationData = "Zip.unzip(artifact, currentDir, null);";
		EnvironmentInfo info = (EnvironmentInfo) ServiceHelper.getService(Activator.getContext(), EnvironmentInfo.class.getName());
		if (!info.getOS().equals(org.eclipse.osgi.service.environment.Constants.OS_WIN32))
			// FIXME:  is this correct?  do all non-Windows platforms need execute permissions on the launcher?
			configurationData += " Permissions.chmod(currentDir, \"" + launcher.getName() + "\", 755);";
		touchpointData.put("configurationData", configurationData);
		cu.setImmutableTouchpointData(new TouchpointData(touchpointData));
		resultantIUs.add(cu);

		//Create the artifact descriptor
		IArtifactDescriptor descriptor = createArtifactDescriptor(new ArtifactKey(ECLIPSE_ARTIFACT_NAMESPACE, NATIVE_TOUCHPOINT, ORG_ECLIPSE_EXECUTABLE, ORG_ECLIPSE_EXECUTABLE_VERSION), launcher, false, true);
		resultantArtifactDescriptors.add(descriptor);
	}

	private static ProvidedCapability[] generateJRECapability(InputStream profileStream) {
		if (profileStream == null) {
			//use the 1.5 profile stored in the generator bundle
			try {
				profileStream = Activator.getContext().getBundle().getEntry("J2SE-1.5.profile").openStream();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		Properties p = new Properties();
		try {
			p.load(profileStream);
			ManifestElement[] jrePackages = ManifestElement.parseHeader("org.osgi.framework.system.packages", (String) p.get("org.osgi.framework.system.packages"));
			ProvidedCapability[] exportedPackageAsCapabilities = new ProvidedCapability[jrePackages.length];
			for (int i = 0; i < jrePackages.length; i++) {
				exportedPackageAsCapabilities[i] = new ProvidedCapability("osgi.packages", jrePackages[i].getValue(), null);
			}
			return exportedPackageAsCapabilities;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BundleException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (profileStream != null) {
				try {
					profileStream.close();
				} catch (IOException e) {
					//ignore secondary failure
				}
			}
		}
		return new ProvidedCapability[0];
	}

	public static IInstallableUnit createEclipseConfigurationUnit(String iuId, Version iuVersion, boolean isBundleFragment, GeneratorBundleInfo configInfo, String configurationFlavor) {
		if (configInfo == null)
			return null;

		InstallableUnitFragment cu = new InstallableUnitFragment();
		cu.setId(configurationFlavor + iuId);
		cu.setVersion(iuVersion);

		//Indicate the IU to which this CU apply 
		cu.setHost(iuId, new VersionRange(iuVersion, true, versionMax, true));

		//Add a capability describing the flavor supported
		cu.setCapabilities(new ProvidedCapability[] {new ProvidedCapability(IInstallableUnit.FLAVOR_NAMESPACE, configurationFlavor, Version.emptyVersion)});

		cu.setTouchpointType(new TouchpointType(ECLIPSE_TOUCHPOINT, ECLIPSE_TOUCHPOINT_VERSION)); //TODO Is this necessary? I think we get that from the IU

		Map touchpointData = new HashMap();
		touchpointData.put("configurationData", createConfigScript(configInfo, isBundleFragment));
		touchpointData.put("unconfigurationData", createUnconfigScript(configInfo, isBundleFragment));
		cu.setImmutableTouchpointData(new TouchpointData(touchpointData));

		return cu;
	}

	public static IInstallableUnit createEclipseDefaultConfigurationUnit(GeneratorBundleInfo configInfo, GeneratorBundleInfo unconfigInfo, String configurationFlavor) {
		InstallableUnitFragment cu = new InstallableUnitFragment();
		cu.setId(configurationFlavor + "default");
		cu.setVersion(new Version(1, 0, 0));

		//Add a capability describing the flavor supported
		cu.setCapabilities(new ProvidedCapability[] {new ProvidedCapability(IInstallableUnit.FLAVOR_NAMESPACE, configurationFlavor, Version.emptyVersion)});

		//Create a capability on bundles
		RequiredCapability[] reqs = new RequiredCapability[] {new RequiredCapability(IInstallableUnit.CAPABILITY_ECLIPSE_TYPES, IInstallableUnit.CAPABILITY_ECLIPSE_BUNDLE, VersionRange.emptyRange, null, false, true)};
		cu.setRequiredCapabilities(reqs);
		cu.setTouchpointType(new TouchpointType(ECLIPSE_TOUCHPOINT, ECLIPSE_TOUCHPOINT_VERSION)); //TODO Is this necessary? I think we get that from the IU
		Map touchpointData = new HashMap();

		touchpointData.put("configurationData", createDefaultConfigScript(configInfo));
		touchpointData.put("unconfigurationData", createDefaultUnconfigScript(unconfigInfo));

		cu.setImmutableTouchpointData(new TouchpointData(touchpointData));
		return cu;
	}

	private static String createDefaultConfigScript(GeneratorBundleInfo configInfo) {
		String configScript = "";//$NON-NLS-1$
		if (configInfo != null) {
			if (configInfo.getStartLevel() != BundleInfo.NO_LEVEL) {
				configScript += "bundleToInstall.setStartLevel(" + configInfo.getStartLevel() + ");";
			}
			if (configInfo.isMarkedAsStarted()) {
				configScript += "bundleToInstall.setMarkedAsStarted(true);";
			}
			if (configInfo.getSpecialConfigCommands() != null) {
				configScript += configInfo.getSpecialConfigCommands();
			}
		}
		return configScript;
	}

	private static String createDefaultUnconfigScript(GeneratorBundleInfo unconfigInfo) {
		String unconfigScript = ""; //$NON-NLS-1$
		if (unconfigInfo != null) {
			if (unconfigInfo.getSpecialConfigCommands() != null) {
				unconfigScript += unconfigInfo.getSpecialConfigCommands();
			}
		}
		return unconfigScript;
	}

	private static String createConfigScript(GeneratorBundleInfo configInfo, boolean isBundleFragment) {
		String configScript = "manipulator.getConfigData().addBundle(bundleToInstall);";
		if (configInfo != null) {
			if (!isBundleFragment && configInfo.getStartLevel() != BundleInfo.NO_LEVEL) {
				configScript += "bundleToInstall.setStartLevel(" + configInfo.getStartLevel() + ");";
			}
			if (!isBundleFragment && configInfo.isMarkedAsStarted()) {
				configScript += "bundleToInstall.setMarkedAsStarted(true);";
			}
			if (configInfo.getSpecialConfigCommands() != null) {
				configScript += configInfo.getSpecialConfigCommands();
			}
		}
		return configScript;
	}

	private static String createUnconfigScript(GeneratorBundleInfo configInfo, boolean isBundleFragment) {
		String unconfigScript = "";
		if (configInfo != null) {
			if (!isBundleFragment && configInfo.getStartLevel() != BundleInfo.NO_LEVEL) {
				unconfigScript += "bundleToRemove.setStartLevel(" + BundleInfo.NO_LEVEL + ");";
			}
			if (!isBundleFragment && configInfo.isMarkedAsStarted()) {
				unconfigScript += "bundleToRemove.setMarkedAsStarted(false);";
			}
			if (configInfo.getSpecialConfigCommands() != null) {
				// TODO: how should special config commands be removed
				// unconfigScript += "foobar.remove(" + configInfo.getSpecialConfigCommands() + ");";
			}
		}
		unconfigScript += "manipulator.getConfigData().removeBundle(bundleToRemove);";
		return unconfigScript;
	}

	private static boolean requireAFragment(BundleDescription bd, Map manifest) {
		if (manifest == null)
			return false;
		if (manifest.get(ECLIPSE_EXTENSIBLE_API) == null)
			return false;
		if (bd.getSymbolicName().equals("org.eclipse.osgi")) //Special case for OSGi
			return false;
		String classpath = (String) ((Map) bd.getUserObject()).get(Constants.BUNDLE_CLASSPATH);
		if (classpath == null)
			return true;
		ManifestElement[] classpathEntries;
		try {
			classpathEntries = ManifestElement.parseHeader(Constants.BUNDLE_CLASSPATH, classpath);
			if (classpathEntries.length != 0 && classpathEntries[0].getValue().equals("."))
				return true;
		} catch (BundleException e) {
			//If we are here, it is that we have already parsed the bundle manifest and it contains no error 
		}
		return false;
	}

	public static IInstallableUnit createEclipseIU(BundleDescription bd, Map manifest, boolean isFolderPlugin, IArtifactKey key) {
		InstallableUnit iu = new InstallableUnit();
		iu.setSingleton(bd.isSingleton());
		iu.setId(bd.getSymbolicName());
		iu.setVersion(bd.getVersion());
		iu.setFilter(bd.getPlatformFilter());
		iu.setProperty(IInstallableUnitConstants.UPDATE_FROM, bd.getSymbolicName());
		iu.setProperty(IInstallableUnitConstants.UPDATE_RANGE, VersionRange.emptyRange.toString());

		boolean isFragment = bd.getHost() != null;
		boolean requiresAFragment = isFragment ? false : requireAFragment(bd, manifest);

		//Process the required bundles
		BundleSpecification requiredBundles[] = bd.getRequiredBundles();
		ArrayList reqsDeps = new ArrayList();
		if (requiresAFragment)
			reqsDeps.add(new RequiredCapability("fragment", iu.getId(), VersionRange.emptyRange, null, false, false));
		if (isFragment)
			reqsDeps.add(RequiredCapability.createRequiredCapabilityForName(bd.getHost().getName(), bd.getHost().getVersionRange(), false));
		for (int j = 0; j < requiredBundles.length; j++)
			reqsDeps.add(RequiredCapability.createRequiredCapabilityForName(requiredBundles[j].getName(), requiredBundles[j].getVersionRange() == VersionRange.emptyRange ? null : requiredBundles[j].getVersionRange(), requiredBundles[j].isOptional()));

		//Process the import package
		ImportPackageSpecification osgiImports[] = bd.getImportPackages();
		for (int i = 0; i < osgiImports.length; i++) {
			// TODO we need to sort out how we want to handle wild-carded dynamic imports - for now we ignore them
			ImportPackageSpecification importSpec = osgiImports[i];
			String importPackageName = importSpec.getName();
			if (importPackageName.indexOf('*') != -1)
				continue;

			VersionRange versionRange = importSpec.getVersionRange() == VersionRange.emptyRange ? null : importSpec.getVersionRange();

			//TODO this needs to be refined to take into account all the attribute handled by imports
			reqsDeps.add(new RequiredCapability(CAPABILITY_TYPE_OSGI_PACKAGES, importPackageName, versionRange, null, isOptional(importSpec), false));
		}
		iu.setRequiredCapabilities((RequiredCapability[]) reqsDeps.toArray(new RequiredCapability[reqsDeps.size()]));

		//Process the export package
		ExportPackageDescription exports[] = bd.getExportPackages();
		ProvidedCapability[] exportedPackageAsCapabilities = new ProvidedCapability[exports.length + 1 + (isFragment ? 1 : 0)];
		exportedPackageAsCapabilities[exports.length] = new ProvidedCapability(IInstallableUnit.CAPABILITY_ECLIPSE_TYPES, IInstallableUnit.CAPABILITY_ECLIPSE_BUNDLE, new Version(1, 0, 0)); //Here we add a bundle capability to identify bundles 
		for (int i = 0; i < exports.length; i++) {
			exportedPackageAsCapabilities[i] = new ProvidedCapability(CAPABILITY_TYPE_OSGI_PACKAGES, exports[i].getName(), exports[i].getVersion() == Version.emptyVersion ? null : exports[i].getVersion()); //TODO make sure that we support all the refinement on the exports
		}
		if (isFragment)
			exportedPackageAsCapabilities[exportedPackageAsCapabilities.length - 1] = new ProvidedCapability("fragment", bd.getHost().getName(), bd.getVersion());
		iu.setCapabilities(exportedPackageAsCapabilities);
		iu.setApplicabilityFilter("");

		iu.setArtifacts(new IArtifactKey[] {key});

		iu.setTouchpointType(new TouchpointType(ECLIPSE_TOUCHPOINT, ECLIPSE_TOUCHPOINT_VERSION));

		// Set IU properties from the manifest header attributes
		// TODO The values of the attributes may be localized. Metadata generation
		//      should construct property files for the IU based on the bundle/plug-in
		//		property files in whatever locales are provided.
		if (manifest != null) {
			int i = 0;
			while (i < BUNDLE_IU_PROPERTY_MAP.length) {
				if (manifest.containsKey(BUNDLE_IU_PROPERTY_MAP[i])) {
					String value = (String) manifest.get(BUNDLE_IU_PROPERTY_MAP[i]);
					if (value != null) {
						iu.setProperty(BUNDLE_IU_PROPERTY_MAP[i + 1], value);
					}
				}
				i += 2;
			}
		}

		//Define the immutable metadata for this IU. In this case immutable means that this is something that will not impact the configuration
		Map touchpointData = new HashMap();
		if (isFolderPlugin)
			touchpointData.put("zipped", "true");
		touchpointData.put("manifest", toManifestString(manifest));
		iu.setImmutableTouchpointData(new TouchpointData(touchpointData));
		return iu;
	}

	public static VersionRange getVersionRange(FeatureEntry entry) {
		String versionSpec = entry.getVersion();
		if (versionSpec == null)
			// TODO should really be returning VersionRange.emptyRange here...
			return null;
		Version version = new Version(versionSpec);
		if (!entry.isRequires())
			return new VersionRange(version, true, version, true);
		String match = entry.getMatch();
		if (match == null)
			// TODO should really be returning VersionRange.emptyRange here...
			return null;
		if (match.equals("perfect"))
			return new VersionRange(version, true, version, true);
		if (match.equals("equivalent")) {
			Version upper = new Version(version.getMajor(), version.getMinor() + 1, 0);
			return new VersionRange(version, true, upper, false);
		}
		if (match.equals("compatible")) {
			Version upper = new Version(version.getMajor() + 1, 0, 0);
			return new VersionRange(version, true, upper, false);
		}
		if (match.equals("greaterOrEqual"))
			return new VersionRange(version, true, new VersionRange(null).getMaximum(), true);
		return null;
	}

	private static String getTransformedId(String original, boolean isPlugin) {
		return isPlugin ? original : original + ".featureIU";
	}

	public static IInstallableUnit createGroupIU(Feature feature) {
		InstallableUnit iu = new InstallableUnit();
		iu.setId(getTransformedId(feature.getId(), false));
		iu.setVersion(new Version(feature.getVersion()));
		iu.setProperty(IInstallableUnitConstants.UPDATE_FROM, iu.getId());
		iu.setProperty(IInstallableUnitConstants.UPDATE_RANGE, VersionRange.emptyRange.toString());

		FeatureEntry entries[] = feature.getEntries();
		RequiredCapability[] required = new RequiredCapability[entries.length];
		for (int i = 0; i < entries.length; i++) {
			VersionRange range = getVersionRange(entries[i]);
			required[i] = new RequiredCapability(IU_NAMESPACE, getTransformedId(entries[i].getId(), entries[i].isPlugin()), range, getFilter(entries[i]), entries[i].isOptional(), false);
		}
		iu.setRequiredCapabilities(required);
		iu.setTouchpointType(TouchpointType.NONE);
		ProvidedCapability groupCapability = new ProvidedCapability(IInstallableUnit.IU_KIND_NAMESPACE, "group", new Version("1.0.0"));
		iu.setCapabilities(new ProvidedCapability[] {groupCapability});
		return iu;
	}

	public static String getFilter(FeatureEntry entry) {
		StringBuffer result = new StringBuffer();
		result.append("(&"); //$NON-NLS-1$
		if (entry.getFilter() != null)
			result.append(entry.getFilter());
		if (entry.getOS() != null)
			result.append("(osgi.os=" + entry.getOS() + ')');//$NON-NLS-1$
		if (entry.getWS() != null)
			result.append("(osgi.ws=" + entry.getWS() + ')');//$NON-NLS-1$
		if (entry.getArch() != null)
			result.append("(osgi.arch=" + entry.getArch() + ')');//$NON-NLS-1$
		if (entry.getNL() != null)
			result.append("(osgi.nl=" + entry.getNL() + ')');//$NON-NLS-1$
		if (result.length() == 2)
			return null;
		result.append(')');
		return result.toString();
	}

	private static boolean isOptional(ImportPackageSpecification importedPackage) {
		if (importedPackage.getDirective(Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_DYNAMIC) || importedPackage.getDirective(Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_OPTIONAL))
			return true;
		return false;
	}

	private static String toManifestString(Map p) {
		if (p == null)
			return null;
		Collection properties = p.entrySet();
		StringBuffer result = new StringBuffer();
		for (Iterator iterator = properties.iterator(); iterator.hasNext();) {
			Map.Entry aProperty = (Map.Entry) iterator.next();
			result.append(aProperty.getKey()).append(": ").append(aProperty.getValue()).append('\n');
		}
		return result.toString();
	}

	public static IArtifactKey createEclipseArtifactKey(String bsn, String version) {
		return new ArtifactKey(ECLIPSE_ARTIFACT_NAMESPACE, ECLIPSE_ARTIFACT_CLASSIFIER, bsn, new Version(version));
	}

	public static IArtifactDescriptor createArtifactDescriptor(IArtifactKey key, File pathOnDisk, boolean asIs, boolean recurse) {
		//TODO this size calculation is bogus
		ArtifactDescriptor result = new ArtifactDescriptor(key);
		if (pathOnDisk != null)
			result.setProperty(IArtifactDescriptor.ARTIFACT_SIZE, Long.toString(pathOnDisk.length()));
		return result;
	}
}

Back to the top