blob: 1cba1c0ecdadd4a927e5a54b35c23280348ea6a2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Code 9 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:
* Code 9 - initial API and implementation
* IBM - ongoing development
******************************************************************************/
package org.eclipse.equinox.p2.publisher.eclipse;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
import org.eclipse.equinox.internal.p2.publisher.Activator;
import org.eclipse.equinox.internal.p2.publisher.FileSetDescriptor;
import org.eclipse.equinox.internal.p2.publisher.eclipse.FeatureParser;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.ArtifactDescriptor;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.IArtifactDescriptor;
import org.eclipse.equinox.internal.provisional.p2.core.repository.IRepository;
import org.eclipse.equinox.internal.provisional.p2.metadata.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory.InstallableUnitPatchDescription;
import org.eclipse.equinox.internal.provisional.p2.metadata.repository.IMetadataRepository;
import org.eclipse.equinox.p2.publisher.*;
import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
import org.eclipse.osgi.service.resolver.VersionRange;
import org.osgi.framework.Version;
/**
* Publish IUs for all of the features in the given set of locations. The locations can
* be actual locations of the features or folders of features.
*/
public class FeaturesAction extends AbstractPublisherAction {
public static final String INSTALL_FEATURES_FILTER = "(org.eclipse.update.install.features=true)"; //$NON-NLS-1$
private File[] locations;
protected Feature[] features;
public static String getTransformedId(String original, boolean isPlugin, boolean isGroup) {
return (isPlugin ? original : original + (isGroup ? ".feature.group" : ".feature.jar")); //$NON-NLS-1$//$NON-NLS-2$
}
public static IArtifactKey createFeatureArtifactKey(String id, String version) {
return new ArtifactKey(PublisherHelper.ECLIPSE_FEATURE_CLASSIFIER, id, new Version(version));
}
public static Object[] createFeatureRootFileIU(String featureId, String featureVersion, File location, FileSetDescriptor descriptor) {
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
iu.setSingleton(true);
String id = featureId + '_' + descriptor.getKey();
iu.setId(id);
Version version = new Version(featureVersion);
iu.setVersion(version);
iu.setCapabilities(new ProvidedCapability[] {PublisherHelper.createSelfCapability(id, version)});
iu.setTouchpointType(PublisherHelper.TOUCHPOINT_NATIVE);
String configSpec = descriptor.getConfigSpec();
if (configSpec != null)
iu.setFilter(AbstractPublisherAction.createFilterSpec(configSpec));
File[] fileResult = attachFiles(iu, descriptor, location);
setupLinks(iu, descriptor);
setupPermissions(iu, descriptor);
IInstallableUnit iuResult = MetadataFactory.createInstallableUnit(iu);
// need to return both the iu and any files.
return new Object[] {iuResult, fileResult};
}
// attach the described files from the given location to the given iu description. Return
// the list of files identified.
private static File[] attachFiles(InstallableUnitDescription iu, FileSetDescriptor descriptor, File location) {
String fileList = descriptor.getFiles();
String[] fileSpecs = getArrayFromString(fileList, ","); //$NON-NLS-1$
File[] files = new File[fileSpecs.length];
if (fileSpecs.length > 0) {
for (int i = 0; i < fileSpecs.length; i++) {
String spec = fileSpecs[i];
if (spec.startsWith("file:"))
spec = spec.substring(5);
files[i] = new File(location, spec);
}
}
// add touchpoint actions to unzip and cleanup as needed
// TODO need to support fancy root file location specs
Map touchpointData = new HashMap(2);
String configurationData = "unzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$
touchpointData.put("install", configurationData); //$NON-NLS-1$
String unConfigurationData = "cleanupzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$
touchpointData.put("uninstall", unConfigurationData); //$NON-NLS-1$
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
// prime the IU with an artifact key that will correspond to the zipped up root files.
IArtifactKey key = new ArtifactKey(PublisherHelper.BINARY_ARTIFACT_CLASSIFIER, iu.getId(), iu.getVersion());
iu.setArtifacts(new IArtifactKey[] {key});
return files;
}
private static void setupPermissions(InstallableUnitDescription iu, FileSetDescriptor descriptor) {
Map touchpointData = new HashMap();
String[][] permsList = descriptor.getPermissions();
for (int i = 0; i < permsList.length; i++) {
String[] permSpec = permsList[i];
String configurationData = " chmod(targetDir:${installFolder}, targetFile:" + permSpec[1] + ", permissions:" + permSpec[0] + ");"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
touchpointData.put("install", configurationData); //$NON-NLS-1$
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
}
}
private static void setupLinks(InstallableUnitDescription iu, FileSetDescriptor descriptor) {
// TODO setup the link support.
}
public FeaturesAction(File[] locations) {
this.locations = locations;
}
public FeaturesAction(Feature[] features) {
this.features = features;
}
public IStatus perform(IPublisherInfo info, IPublisherResult results, IProgressMonitor monitor) {
if (features == null && locations == null)
throw new IllegalStateException("No features or locations provided");
if (features == null)
features = getFeatures(expandLocations(locations));
generateFeatureIUs(features, results, info);
return Status.OK_STATUS;
}
private File[] expandLocations(File[] list) {
ArrayList result = new ArrayList();
expandLocations(list, result);
return (File[]) result.toArray(new File[result.size()]);
}
private void expandLocations(File[] list, ArrayList result) {
if (list == null)
return;
for (int i = 0; i < list.length; i++) {
File location = list[i];
if (location.isDirectory()) {
// if the location is itself a feature, just add it. Otherwise r down
if (new File(location, "feature.xml").exists())
result.add(location);
else
expandLocations(location.listFiles(), result);
} else {
result.add(location);
}
}
}
protected void generateFeatureIUs(Feature[] features, IPublisherResult result, IPublisherInfo info) {
// Build Feature IUs, and add them to any corresponding categories
for (int i = 0; i < features.length; i++) {
Feature feature = features[i];
ArrayList childIUs = generateRootFileIUs(feature, result, info);
Properties props = getFeatureAdvice(feature, info);
IInstallableUnit featureIU = createFeatureJarIU(feature, childIUs, props);
publishFeatureArtifacts(feature, featureIU, info);
result.addIU(featureIU, IPublisherResult.ROOT);
generateSiteReferences(feature, result, info);
gatherBundleShapeAdvice(feature, info);
IInstallableUnit groupIU = createGroupIU(feature, featureIU, props);
result.addIU(groupIU, IPublisherResult.ROOT);
}
}
private void generateSiteReferences(Feature feature, IPublisherResult result, IPublisherInfo info) {
//publish feature site references
String updateURL = feature.getUpdateSiteURL();
//don't enable feature update sites by default since this results in too many
//extra sites being loaded and searched (Bug 234177)
if (updateURL != null)
generateSiteReference(updateURL, feature.getId(), info.getMetadataRepository());
URLEntry[] discoverySites = feature.getDiscoverySites();
for (int j = 0; j < discoverySites.length; j++)
generateSiteReference(discoverySites[j].getURL(), feature.getId(), info.getMetadataRepository());
}
/**
* Generates and publishes a reference to an update site location
* @param location The update site location
* @param featureId the identifier of the feature where the error occurred, or null
* @param metadataRepo The repo into which the references are added
*/
private void generateSiteReference(String location, String featureId, IMetadataRepository metadataRepo) {
try {
URL associateLocation = new URL(location);
metadataRepo.addReference(associateLocation, IRepository.TYPE_METADATA, IRepository.NONE);
metadataRepo.addReference(associateLocation, IRepository.TYPE_ARTIFACT, IRepository.NONE);
} catch (MalformedURLException e) {
String message = "Invalid site reference: " + location; //$NON-NLS-1$
if (featureId != null)
message = message + " in feature: " + featureId; //$NON-NLS-1$
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, message));
}
}
protected void publishFeatureArtifacts(Feature feature, IInstallableUnit featureIU, IPublisherInfo info) {
// add all the artifacts associated with the feature
// TODO this is a little strange. If there are several artifacts, how do we know which files go with
// which artifacts when we publish them? For now it would be surprising to have more than one
// artifact per feature IU.
IArtifactKey[] artifacts = featureIU.getArtifacts();
for (int j = 0; j < artifacts.length; j++) {
File file = new File(feature.getLocation());
IArtifactDescriptor ad = PublisherHelper.createArtifactDescriptor(artifacts[j], file);
addProperties((ArtifactDescriptor) ad, feature, info);
((ArtifactDescriptor) ad).setProperty(IArtifactDescriptor.DOWNLOAD_CONTENTTYPE, IArtifactDescriptor.TYPE_ZIP);
// if the artifact is a dir then zip it up.
if (file.isDirectory())
publishArtifact(ad, new File[] {file}, null, info, createRootPrefixComputer(file));
else
publishArtifact(ad, file, info);
}
}
/**
* Add all of the advice for the feature at the given location to the given descriptor.
* @param descriptor the descriptor to decorate
* @param feature the feature we are getting advice for
* @param info the publisher info supplying the advice
*/
private void addProperties(ArtifactDescriptor descriptor, Feature feature, IPublisherInfo info) {
Collection advice = info.getAdvice(null, false, null, null, IFeatureAdvice.class);
for (Iterator i = advice.iterator(); i.hasNext();) {
IFeatureAdvice entry = (IFeatureAdvice) i.next();
Properties props = entry.getArtifactProperties(feature);
if (props == null)
continue;
for (Iterator j = props.keySet().iterator(); j.hasNext();) {
String key = (String) j.next();
descriptor.setRepositoryProperty(key, props.getProperty(key));
}
}
}
private Properties getFeatureAdvice(Feature feature, IPublisherInfo info) {
Properties result = new Properties();
Collection advice = info.getAdvice(null, false, null, null, IFeatureAdvice.class);
for (Iterator i = advice.iterator(); i.hasNext();) {
IFeatureAdvice entry = (IFeatureAdvice) i.next();
Properties props = entry.getIUProperties(feature);
if (props != null)
result.putAll(props);
}
return result;
}
protected ArrayList generateRootFileIUs(Feature feature, IPublisherResult result, IPublisherInfo info) {
File location = new File(feature.getLocation());
Properties props = loadProperties(location, "build.properties"); //$NON-NLS-1$
return generateRootFileIUs(feature.getId(), feature.getVersion(), props, location, result, info);
}
private ArrayList generateRootFileIUs(String featureId, String featureVersion, Properties props, File location, IPublisherResult result, IPublisherInfo info) {
ArrayList ius = new ArrayList();
FileSetDescriptor[] rootFileDescriptors = getRootFileDescriptors(props);
for (int i = 0; i < rootFileDescriptors.length; i++) {
IInstallableUnit iu = generateRootFileIU(featureId, featureVersion, location, rootFileDescriptors[i], result, info);
ius.add(iu);
}
return ius;
}
private Properties loadProperties(File location, String file) {
Properties props = new Properties();
File tempFile = null;
try {
// if the feature is a dir then just return the location
if (!location.isDirectory()) {
// otherwise extract the JAR into a temp location and return that location
tempFile = File.createTempFile("p2.generator", ""); //$NON-NLS-1$ //$NON-NLS-2$
FileUtils.unzipFile(location, tempFile);
location = tempFile;
}
try {
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(new File(location, file))); //$NON-NLS-1$
props.load(in);
} finally {
if (in != null)
in.close();
}
} catch (FileNotFoundException e) {
// ignore if it is just a file not found.
} catch (IOException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Error parsing " + file, e)); //$NON-NLS-1$
}
} catch (IOException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Error publishing artifacts", e)); //$NON-NLS-1$
} finally {
if (tempFile != null)
tempFile.delete();
}
return props;
}
private IInstallableUnit generateRootFileIU(String featureId, String featureVersion, File location, FileSetDescriptor rootFile, IPublisherResult result, IPublisherInfo info) {
Object[] iuAndFiles = createFeatureRootFileIU(featureId, featureVersion, location, rootFile);
IInstallableUnit iuResult = (IInstallableUnit) iuAndFiles[0];
File[] fileResult = (File[]) iuAndFiles[1];
if (fileResult != null && fileResult.length > 0) {
IArtifactKey artifact = iuResult.getArtifacts()[0];
ArtifactDescriptor descriptor = new ArtifactDescriptor(artifact);
publishArtifact(descriptor, fileResult, null, info, FileUtils.createDynamicPathComputer(1));
}
result.addIU(iuResult, IPublisherResult.NON_ROOT);
return iuResult;
}
protected FileSetDescriptor[] getRootFileDescriptors(Properties props) {
HashMap result = new HashMap();
for (Iterator i = props.keySet().iterator(); i.hasNext();) {
String property = (String) i.next();
// we only care about root properties
if (!property.startsWith("root.")) //$NON-NLS-1$
continue;
String[] spec = getArrayFromString(property, "."); //$NON-NLS-1$
String descriptorKey = spec[0];
String configSpec = null;
// if the spec is 4 or more then there must be a config involved to get it
if (spec.length > 3) {
configSpec = createConfigSpec(spec[2], spec[1], spec[3]);
descriptorKey += "." + createIdString(configSpec); //$NON-NLS-1$
}
FileSetDescriptor descriptor = (FileSetDescriptor) result.get(descriptorKey);
if (descriptor == null) {
descriptor = new FileSetDescriptor(descriptorKey, configSpec);
result.put(descriptorKey, descriptor);
}
// if the last segment in the spec is "link"
if (spec[spec.length - 1] == "link") //$NON-NLS-1$
descriptor.setLinks(props.getProperty(property));
else {
// if the second last segment is "permissions"
if (spec[spec.length - 2].equals("permissions")) //$NON-NLS-1$
descriptor.addPermissions(new String[] {spec[spec.length - 1], props.getProperty(property)});
else {
// so it is not a link or a permissions, it must be a straight file copy (with or without a config)
descriptor.setFiles(props.getProperty(property));
}
}
}
Collection values = result.values();
return (FileSetDescriptor[]) values.toArray(new FileSetDescriptor[values.size()]);
}
/**
* Gather any advice we can from the given feature. In particular, it may have
* information about the shape of the bundles it includes. The discovered advice is
* added to the given result.
* @param feature the feature to process
* @param info the publishing info to update
*/
public void gatherBundleShapeAdvice(Feature feature, IPublisherInfo info) {
FeatureEntry entries[] = feature.getEntries();
for (int i = 0; i < entries.length; i++) {
FeatureEntry entry = entries[i];
if (entry.isUnpack() && entry.isPlugin() && !entry.isRequires())
info.addAdvice(new BundleShapeAdvice(entry.getId(), new Version(entry.getVersion()), IBundleShapeAdvice.DIR));
}
}
public IInstallableUnit createFeatureJarIU(Feature feature, ArrayList childIUs, Properties extraProperties) {
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
String id = getTransformedId(feature.getId(), /*isPlugin*/false, /*isGroup*/false);
iu.setId(id);
Version version = new Version(feature.getVersion());
iu.setVersion(version);
if (feature.getLicense() != null)
iu.setLicense(new License(feature.getLicenseURL(), feature.getLicense()));
if (feature.getCopyright() != null)
iu.setCopyright(new Copyright(feature.getCopyrightURL(), feature.getCopyright()));
// The required capabilities are not specified at this level because we don't want the feature jar to be attractive to install.
iu.setTouchpointType(PublisherHelper.TOUCHPOINT_OSGI);
iu.setFilter(INSTALL_FEATURES_FILTER);
iu.setSingleton(true);
if (feature.getInstallHandler() != null && feature.getInstallHandler().trim().length() > 0) {
String installHandlerProperty = "handler=" + feature.getInstallHandler(); //$NON-NLS-1$
if (feature.getInstallHandlerLibrary() != null)
installHandlerProperty += ", library=" + feature.getInstallHandlerLibrary(); //$NON-NLS-1$
if (feature.getInstallHandlerURL() != null)
installHandlerProperty += ", url=" + feature.getInstallHandlerURL(); //$NON-NLS-1$
iu.setProperty(PublisherHelper.ECLIPSE_INSTALL_HANDLER_PROP, installHandlerProperty);
}
iu.setCapabilities(new ProvidedCapability[] {PublisherHelper.createSelfCapability(id, version), PublisherHelper.FEATURE_CAPABILITY, MetadataFactory.createProvidedCapability(PublisherHelper.CAPABILITY_NS_UPDATE_FEATURE, feature.getId(), version)});
iu.setArtifacts(new IArtifactKey[] {createFeatureArtifactKey(feature.getId(), version.toString())});
// link in all the children (if any) as requirements.
// TODO consider if these should be linked as exact version numbers. Should be ok but may be brittle.
if (childIUs != null) {
RequiredCapability[] required = new RequiredCapability[childIUs.size()];
for (int i = 0; i < childIUs.size(); i++) {
IInstallableUnit child = (IInstallableUnit) childIUs.get(i);
required[i] = MetadataFactory.createRequiredCapability(PublisherHelper.IU_NAMESPACE, child.getId(), new VersionRange(child.getVersion(), true, child.getVersion(), true), INSTALL_FEATURES_FILTER, false, false);
}
iu.setRequiredCapabilities(required);
}
// if the feature has a location and it is not a JAR then setup the touchpoint data
// TODO its not clear when this would ever be false reasonably. Features are always
// supposed to be installed unzipped. It is also not clear what it means to set this prop.
// Anyway, in the future it seems reasonable that features be installed as JARs...
if (feature.getLocation() == null || !feature.getLocation().endsWith(".jar")) {
Map touchpointData = new HashMap();
touchpointData.put("zipped", "true"); //$NON-NLS-1$ //$NON-NLS-2$
iu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData));
}
addExtraProperties(iu, extraProperties);
return MetadataFactory.createInstallableUnit(iu);
}
private void addExtraProperties(InstallableUnitDescription iu, Properties extraProperties) {
if (extraProperties != null) {
Enumeration e = extraProperties.propertyNames();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();
iu.setProperty(name, extraProperties.getProperty(name));
}
}
}
public IInstallableUnit createGroupIU(Feature feature, IInstallableUnit featureIU, Properties extraProperties) {
if (isPatch(feature))
return createPatchIU(feature, featureIU, extraProperties);
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
String id = getTransformedId(feature.getId(), /*isPlugin*/false, /*isGroup*/true);
iu.setId(id);
Version version = new Version(feature.getVersion());
iu.setVersion(version);
iu.setProperty(IInstallableUnit.PROP_NAME, feature.getLabel());
if (feature.getDescription() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION, feature.getDescription());
if (feature.getDescriptionURL() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION_URL, feature.getDescriptionURL());
if (feature.getProviderName() != null)
iu.setProperty(IInstallableUnit.PROP_PROVIDER, feature.getProviderName());
if (feature.getLicense() != null)
iu.setLicense(new License(feature.getLicenseURL(), feature.getLicense()));
if (feature.getCopyright() != null)
iu.setCopyright(new Copyright(feature.getCopyrightURL(), feature.getCopyright()));
iu.setUpdateDescriptor(MetadataFactory.createUpdateDescriptor(id, new VersionRange(new Version(0, 0, 0), true, new Version(feature.getVersion()), false), IUpdateDescriptor.NORMAL, null));
FeatureEntry entries[] = feature.getEntries();
RequiredCapability[] required = new RequiredCapability[entries.length + (featureIU == null ? 0 : 1)];
for (int i = 0; i < entries.length; i++) {
VersionRange range = getVersionRange(entries[i]);
String requiredId = getTransformedId(entries[i].getId(), entries[i].isPlugin(), /*isGroup*/true);
required[i] = MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, requiredId, range, getFilter(entries[i]), entries[i].isOptional(), false);
}
// the feature IU could be null if we are just generating a feature structure rather than
// actual features.
if (featureIU != null)
required[entries.length] = MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, featureIU.getId(), new VersionRange(featureIU.getVersion(), true, featureIU.getVersion(), true), INSTALL_FEATURES_FILTER, false, false);
iu.setRequiredCapabilities(required);
iu.setTouchpointType(TouchpointType.NONE);
iu.setProperty(IInstallableUnit.PROP_TYPE_GROUP, Boolean.TRUE.toString());
// TODO: shouldn't the filter for the group be constructed from os, ws, arch, nl
// of the feature?
// iu.setFilter(filter);
// Create set of provided capabilities
ArrayList providedCapabilities = new ArrayList();
providedCapabilities.add(createSelfCapability(id, version));
Map localizations = feature.getLocalizations();
if (localizations != null) {
for (Iterator iter = localizations.keySet().iterator(); iter.hasNext();) {
Locale locale = (Locale) iter.next();
Properties translatedStrings = (Properties) localizations.get(locale);
Enumeration propertyKeys = translatedStrings.propertyNames();
while (propertyKeys.hasMoreElements()) {
String nextKey = (String) propertyKeys.nextElement();
iu.setProperty(locale.toString() + '.' + nextKey, translatedStrings.getProperty(nextKey));
}
providedCapabilities.add(PublisherHelper.makeTranslationCapability(id, locale));
}
}
iu.setCapabilities((ProvidedCapability[]) providedCapabilities.toArray(new ProvidedCapability[providedCapabilities.size()]));
addExtraProperties(iu, extraProperties);
return MetadataFactory.createInstallableUnit(iu);
}
private static boolean isPatch(Feature feature) {
FeatureEntry[] entries = feature.getEntries();
for (int i = 0; i < entries.length; i++) {
if (entries[i].isPatch())
return true;
}
return false;
}
public IInstallableUnit createPatchIU(Feature feature, IInstallableUnit featureIU, Properties extraProperties) {
InstallableUnitPatchDescription iu = new MetadataFactory.InstallableUnitPatchDescription();
String id = getTransformedId(feature.getId(), /*isPlugin*/false, /*isGroup*/true);
iu.setId(id);
Version version = new Version(feature.getVersion());
iu.setVersion(version);
iu.setProperty(IInstallableUnit.PROP_NAME, feature.getLabel());
if (feature.getDescription() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION, feature.getDescription());
if (feature.getDescriptionURL() != null)
iu.setProperty(IInstallableUnit.PROP_DESCRIPTION_URL, feature.getDescriptionURL());
if (feature.getProviderName() != null)
iu.setProperty(IInstallableUnit.PROP_PROVIDER, feature.getProviderName());
if (feature.getLicense() != null)
iu.setLicense(new License(feature.getLicenseURL(), feature.getLicense()));
if (feature.getCopyright() != null)
iu.setCopyright(new Copyright(feature.getCopyrightURL(), feature.getCopyright()));
iu.setUpdateDescriptor(MetadataFactory.createUpdateDescriptor(id, new VersionRange(new Version(0, 0, 0), true, new Version(feature.getVersion()), false), IUpdateDescriptor.NORMAL, null));
FeatureEntry entries[] = feature.getEntries();
ArrayList applicabilityScope = new ArrayList();
ArrayList patchRequirements = new ArrayList();
ArrayList requirementChanges = new ArrayList();
for (int i = 0; i < entries.length; i++) {
VersionRange range = getVersionRange(entries[i]);
RequiredCapability req = MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, getTransformedId(entries[i].getId(), entries[i].isPlugin(), /*isGroup*/true), range, getFilter(entries[i]), entries[i].isOptional(), false);
if (entries[i].isRequires()) {
applicabilityScope.add(req);
continue;
}
if (entries[i].isPlugin()) {
RequiredCapability from = MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, getTransformedId(entries[i].getId(), entries[i].isPlugin(), /*isGroup*/true), VersionRange.emptyRange, getFilter(entries[i]), entries[i].isOptional(), false);
requirementChanges.add(new RequirementChange(from, req));
continue;
}
patchRequirements.add(req);
}
//Always add a requirement on the IU containing the feature jar
patchRequirements.add(MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, featureIU.getId(), new VersionRange(featureIU.getVersion(), true, featureIU.getVersion(), true), INSTALL_FEATURES_FILTER, false, false));
iu.setRequiredCapabilities((RequiredCapability[]) patchRequirements.toArray(new RequiredCapability[patchRequirements.size()]));
iu.setApplicabilityScope(new RequiredCapability[][] {(RequiredCapability[]) applicabilityScope.toArray(new RequiredCapability[applicabilityScope.size()])});
iu.setRequirementChanges((RequirementChange[]) requirementChanges.toArray(new RequirementChange[requirementChanges.size()]));
//Generate lifecycle
RequiredCapability lifeCycle = null;
if (applicabilityScope.size() > 0) {
RequiredCapability req = (RequiredCapability) applicabilityScope.get(0);
lifeCycle = MetadataFactory.createRequiredCapability(req.getNamespace(), req.getName(), req.getRange(), null, false, false, false);
iu.setLifeCycle(lifeCycle);
}
iu.setTouchpointType(TouchpointType.NONE);
iu.setProperty(IInstallableUnit.PROP_TYPE_GROUP, Boolean.TRUE.toString());
iu.setProperty(IInstallableUnit.PROP_TYPE_PATCH, Boolean.TRUE.toString());
// TODO: shouldn't the filter for the group be constructed from os, ws, arch, nl
// of the feature?
// iu.setFilter(filter);
// Create set of provided capabilities
ArrayList providedCapabilities = new ArrayList();
providedCapabilities.add(createSelfCapability(id, version));
Map localizations = feature.getLocalizations();
if (localizations != null) {
for (Iterator iter = localizations.keySet().iterator(); iter.hasNext();) {
Locale locale = (Locale) iter.next();
Properties translatedStrings = (Properties) localizations.get(locale);
Enumeration propertyKeys = translatedStrings.propertyNames();
while (propertyKeys.hasMoreElements()) {
String nextKey = (String) propertyKeys.nextElement();
iu.setProperty(locale.toString() + '.' + nextKey, translatedStrings.getProperty(nextKey));
}
providedCapabilities.add(PublisherHelper.makeTranslationCapability(id, locale));
}
}
iu.setCapabilities((ProvidedCapability[]) providedCapabilities.toArray(new ProvidedCapability[providedCapabilities.size()]));
addExtraProperties(iu, extraProperties);
return MetadataFactory.createInstallableUnitPatch(iu);
}
public VersionRange getVersionRange(FeatureEntry entry) {
String versionSpec = entry.getVersion();
if (versionSpec == null)
return VersionRange.emptyRange;
Version version = new Version(versionSpec);
if (version.equals(Version.emptyVersion))
return VersionRange.emptyRange;
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")) //$NON-NLS-1$
return new VersionRange(version, true, version, true);
if (match.equals("equivalent")) { //$NON-NLS-1$
Version upper = new Version(version.getMajor(), version.getMinor() + 1, 0);
return new VersionRange(version, true, upper, false);
}
if (match.equals("compatible")) { //$NON-NLS-1$
Version upper = new Version(version.getMajor() + 1, 0, 0);
return new VersionRange(version, true, upper, false);
}
if (match.equals("greaterOrEqual")) //$NON-NLS-1$
return new VersionRange(version, true, new VersionRange(null).getMaximum(), true);
return null;
}
public 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();
}
protected Feature[] getFeatures(File[] locations) {
ArrayList result = new ArrayList(locations.length);
for (int i = 0; i < locations.length; i++) {
Feature feature = new FeatureParser().parse(locations[i]);
if (feature != null) {
feature.setLocation(locations[i].getAbsolutePath());
result.add(feature);
}
}
return (Feature[]) result.toArray(new Feature[result.size()]);
}
}