blob: c54c9c1f362799c8e4abdfce5105a9d001dc6bc1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2020 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
******************************************************************************/
package org.eclipse.equinox.p2.publisher;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
import org.eclipse.equinox.internal.p2.metadata.InstallableUnit;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitFragmentDescription;
import org.eclipse.equinox.p2.metadata.expression.*;
import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
public class AdviceFileParser {
private static final String ADVICE_VERSION = "advice.version"; //$NON-NLS-1$
private static final String QUALIFIER_SUBSTITUTION = "$qualifier$"; //$NON-NLS-1$
private static final String VERSION_SUBSTITUTION = "$version$"; //$NON-NLS-1$
private static final String UPDATE_DESCRIPTION = "update.description"; //$NON-NLS-1$
private static final String UPDATE_SEVERITY = "update.severity"; //$NON-NLS-1$
private static final String UPDATE_RANGE = "update.range"; //$NON-NLS-1$
private static final String UPDATE_ID = "update.id"; //$NON-NLS-1$
private static final String UPDATE_MATCH_EXP = "update.matchExp"; //$NON-NLS-1$
private static final String CLASSIFIER = "classifier"; //$NON-NLS-1$
private static final String TOUCHPOINT_VERSION = "touchpoint.version"; //$NON-NLS-1$
private static final String TOUCHPOINT_ID = "touchpoint.id"; //$NON-NLS-1$
private static final String COPYRIGHT_LOCATION = "copyright.location"; //$NON-NLS-1$
private static final String COPYRIGHT = "copyright"; //$NON-NLS-1$
private static final String ID = "id"; //$NON-NLS-1$
private static final String SINGLETON = "singleton"; //$NON-NLS-1$
private static final String IMPORT = "import"; //$NON-NLS-1$
private static final String RANGE = "range"; //$NON-NLS-1$
private static final String MIN = "min"; //$NON-NLS-1$
private static final String MAX = "max"; //$NON-NLS-1$
private static final String FILTER = "filter"; //$NON-NLS-1$
private static final String MULTIPLE = "multiple"; //$NON-NLS-1$
private static final String OPTIONAL = "optional"; //$NON-NLS-1$
private static final String GREEDY = "greedy"; //$NON-NLS-1$
private static final String VERSION = "version"; //$NON-NLS-1$
private static final String NAMESPACE = "namespace"; //$NON-NLS-1$
private static final String NAME = "name"; //$NON-NLS-1$
private static final String MATCH_EXP = "matchExp"; //$NON-NLS-1$
private static final String LOCATION = "location"; //$NON-NLS-1$
private static final String VALUE = "value"; //$NON-NLS-1$
private static final String UNITS_PREFIX = "units."; //$NON-NLS-1$
private static final String INSTRUCTIONS_PREFIX = "instructions."; //$NON-NLS-1$
private static final String REQUIRES_PREFIX = "requires."; //$NON-NLS-1$
private static final String META_REQUIREMENTS_PREFIX = "metaRequirements."; //$NON-NLS-1$
private static final String PROVIDES_PREFIX = "provides."; //$NON-NLS-1$
private static final String PROPERTIES_PREFIX = "properties."; //$NON-NLS-1$
private static final String LICENSES_PREFIX = "licenses."; //$NON-NLS-1$
private static final String ARTIFACTS_PREFIX = "artifacts."; //$NON-NLS-1$
private static final String HOST_REQUIREMENTS_PREFIX = "hostRequirements."; //$NON-NLS-1$
private static final String UPDATE_DESCRIPTOR_PREFIX = "update."; //$NON-NLS-1$
public static final Version COMPATIBLE_VERSION = Version.createOSGi(1, 0, 0);
public static final VersionRange VERSION_TOLERANCE = new VersionRange(COMPATIBLE_VERSION, true,
Version.createOSGi(2, 0, 0), false);
private Map<String, String> adviceProperties = new HashMap<>();
private List<IProvidedCapability> adviceProvides = new ArrayList<>();
private List<IRequirement> adviceRequires = new ArrayList<>();
private List<IRequirement> adviceMetaRequires = new ArrayList<>();
private IUpdateDescriptor adviceUpdateDescriptor = null;
private Map<String, ITouchpointInstruction> adviceInstructions = new HashMap<>();
private List<InstallableUnitDescription> adviceOtherIUs = new ArrayList<>();
private final Map<String, String> advice;
private Iterator<String> keysIterator;
private String current;
private String hostId;
private Version hostVersion;
public AdviceFileParser(String id, Version version, Map<String, String> advice) {
this.hostId = id;
this.hostVersion = version;
this.advice = advice;
}
public void parse() {
String adviceVersion = advice.get(ADVICE_VERSION);
if (adviceVersion != null)
checkAdviceVersion(adviceVersion);
List<String> keys = new ArrayList<>(advice.keySet());
keys.sort(null);
keysIterator = keys.iterator();
next();
while (current != null) {
if (current.startsWith(PROPERTIES_PREFIX))
parseProperties(PROPERTIES_PREFIX, adviceProperties);
else if (current.startsWith(UPDATE_DESCRIPTOR_PREFIX))
this.adviceUpdateDescriptor = parseUpdateDescriptor(UPDATE_DESCRIPTOR_PREFIX, hostId);
else if (current.startsWith(PROVIDES_PREFIX))
parseProvides(PROVIDES_PREFIX, adviceProvides);
else if (current.startsWith(REQUIRES_PREFIX))
parseRequires(REQUIRES_PREFIX, adviceRequires);
else if (current.startsWith(META_REQUIREMENTS_PREFIX))
parseRequires(META_REQUIREMENTS_PREFIX, adviceMetaRequires);
else if (current.startsWith(INSTRUCTIONS_PREFIX))
parseInstructions(INSTRUCTIONS_PREFIX, adviceInstructions);
else if (current.startsWith(UNITS_PREFIX))
parseUnits(UNITS_PREFIX, adviceOtherIUs);
else if (current.equals(ADVICE_VERSION)) {
next();
} else {
// we ignore elements we do not understand
next();
}
}
}
private void checkAdviceVersion(String adviceVersion) {
Version version = Version.parseVersion(adviceVersion);
if (!VERSION_TOLERANCE.isIncluded(version))
throw new IllegalStateException("bad version: " + version + ". Expected range was " + VERSION_TOLERANCE); //$NON-NLS-1$ //$NON-NLS-2$
}
private void next() {
current = keysIterator.hasNext() ? keysIterator.next() : null;
}
private String currentValue() {
return advice.get(current).trim();
}
private void parseProperties(String prefix, Map<String, String> properties) {
while (current != null && current.startsWith(prefix)) {
int dotIndex = current.indexOf('.', prefix.length());
if (dotIndex == -1)
throw new IllegalStateException("bad token: " + current); //$NON-NLS-1$
parseProperty(current.substring(0, dotIndex + 1), properties);
}
}
private void parseProperty(String prefix, Map<String, String> properties) {
String propertyName = null;
String propertyValue = null;
while (current != null && current.startsWith(prefix)) {
String token = current.substring(prefix.length());
switch (token) {
case NAME:
propertyName = currentValue();
break;
case VALUE:
propertyValue = currentValue();
break;
// we ignore elements we do not understand
default:
break;
}
next();
}
properties.put(propertyName, propertyValue);
}
private IUpdateDescriptor parseUpdateDescriptor(String prefix, String id) {
String name = id;
String description = null;
String range = "[0.0.0,$version$)"; //$NON-NLS-1$
String severity = "0"; //$NON-NLS-1$
String match = null;
while (current != null && current.startsWith(prefix)) {
String token = current;
switch (token) {
case UPDATE_MATCH_EXP:
match = currentValue();
break;
case UPDATE_ID:
name = currentValue();
break;
case UPDATE_DESCRIPTION:
description = currentValue();
break;
case UPDATE_RANGE:
range = currentValue();
break;
case UPDATE_SEVERITY:
severity = currentValue();
break;
// ignore
default:
break;
}
next();
}
if (match != null) {
// When update.match is specified, versionRange and id are ignored
IExpression expr = ExpressionUtil.parse(substituteVersionAndQualifier(match));
IMatchExpression<IInstallableUnit> matchExpression = ExpressionUtil.getFactory().matchExpression(expr);
Collection<IMatchExpression<IInstallableUnit>> descriptors = new ArrayList<>(1);
descriptors.add(matchExpression);
return MetadataFactory.createUpdateDescriptor(descriptors, Integer.valueOf(severity), description,
(URI) null);
}
range = substituteVersionAndQualifier(range);
VersionRange versionRange = VersionRange.create(range);
return MetadataFactory.createUpdateDescriptor(name, versionRange, Integer.valueOf(severity), description);
}
private void parseProvides(String prefix, List<IProvidedCapability> provides) {
while (current != null && current.startsWith(prefix)) {
int dotIndex = current.indexOf('.', prefix.length());
if (dotIndex == -1)
throw new IllegalStateException("bad token: " + current); //$NON-NLS-1$
parseProvided(current.substring(0, dotIndex + 1), provides);
}
}
private void parseProvided(String prefix, List<IProvidedCapability> provides) {
String namespace = null;
String name = null;
Version capabilityVersion = null;
while (current != null && current.startsWith(prefix)) {
String token = current.substring(prefix.length());
switch (token) {
case NAME:
name = currentValue();
break;
case NAMESPACE:
namespace = currentValue();
break;
case VERSION:
capabilityVersion = Version.parseVersion(substituteVersionAndQualifier(currentValue()));
break;
// we ignore elements we do not understand
default:
break;
}
next();
}
IProvidedCapability capability = MetadataFactory.createProvidedCapability(namespace, name, capabilityVersion);
provides.add(capability);
}
private void parseRequires(String prefix, List<IRequirement> requires) {
while (current != null && current.startsWith(prefix)) {
int dotIndex = current.indexOf('.', prefix.length());
if (dotIndex == -1)
throw new IllegalStateException("bad token: " + current); //$NON-NLS-1$
parseRequired(current.substring(0, dotIndex + 1), requires);
}
}
private void parseRequired(String prefix, List<IRequirement> requires) {
String namespace = null;
String name = null;
VersionRange range = null;
String matchExp = null;
String filter = null;
boolean optional = false;
boolean multiple = false;
boolean greedy = true;
int min = -1;
int max = -1;
while (current != null && current.startsWith(prefix)) {
String token = current.substring(prefix.length());
switch (token) {
case GREEDY:
greedy = Boolean.parseBoolean(currentValue());
break;
case OPTIONAL:
optional = Boolean.parseBoolean(currentValue());
break;
case MULTIPLE:
multiple = Boolean.parseBoolean(currentValue());
break;
case FILTER:
filter = currentValue();
break;
case NAME:
name = currentValue();
break;
case NAMESPACE:
namespace = currentValue();
break;
case RANGE:
range = VersionRange.create(substituteVersionAndQualifier(currentValue()));
break;
case MIN:
min = Integer.valueOf(currentValue()).intValue();
break;
case MAX:
max = Integer.valueOf(currentValue()).intValue();
break;
case MATCH_EXP:
matchExp = currentValue();
break;
// we ignore elements we do not understand
default:
break;
}
next();
}
IRequirement capability = null;
if (matchExp == null) {
if (min >= 0 && max >= 0) {
capability = createRequirement(namespace, name, range, filter, min, max, greedy);
} else {
capability = createRequirement(namespace, name, range, filter, optional, multiple, greedy);
}
} else {
// When a match expression is specified, namespace, name and versionRange are
// ignored
if (optional && min == -1 && max == -1) {
min = 0;
max = 1;
}
capability = createRequirement(matchExp, filter, min, max, greedy, null);
}
if (capability != null) {
requires.add(capability);
}
}
protected IRequirement createRequirement(String requirement, String filter, int min, int max, boolean greedy,
String description) {
IExpression expr = ExpressionUtil.parse(substituteVersionAndQualifier(requirement));
IMatchExpression<IInstallableUnit> requirementExp = ExpressionUtil.getFactory().matchExpression(expr);
IMatchExpression<IInstallableUnit> filterExp = InstallableUnit.parseFilter(filter);
return MetadataFactory.createRequirement(requirementExp, filterExp, min, max, greedy, description);
}
protected IRequirement createRequirement(String namespace, String name, VersionRange range, String filter, int min,
int max, boolean greedy) {
IMatchExpression<IInstallableUnit> filterExpression = InstallableUnit.parseFilter(filter);
return MetadataFactory.createRequirement(namespace, name, range, filterExpression, min, max, greedy);
}
protected IRequirement createRequirement(String namespace, String name, VersionRange range, String filter,
boolean optional, boolean multiple, boolean greedy) {
return MetadataFactory.createRequirement(namespace, name, range, filter, optional, multiple, greedy);
}
private void parseInstructions(String prefix, Map<String, ITouchpointInstruction> instructions) {
while (current != null && current.startsWith(prefix)) {
int dotIndex = current.indexOf('.', prefix.length());
if (dotIndex != -1)
throw new IllegalStateException("bad token: " + current); //$NON-NLS-1$
parseInstruction(current, instructions);
}
}
private void parseInstruction(String prefix, Map<String, ITouchpointInstruction> instructions) {
String phase = current.substring(current.lastIndexOf('.') + 1);
String body = currentValue();
next();
prefix += '.';
String importAttribute = null;
if (current != null && current.startsWith(prefix)) {
if (current.substring(prefix.length()).equals(IMPORT)) {
importAttribute = currentValue();
} else {
// we ignore elements we do not understand
}
next();
}
ITouchpointInstruction instruction = MetadataFactory.createTouchpointInstruction(body, importAttribute);
instructions.put(phase, instruction);
}
private void parseUnits(String prefix, List<InstallableUnitDescription> ius) {
while (current != null && current.startsWith(prefix)) {
int dotIndex = current.indexOf('.', prefix.length());
if (dotIndex == -1)
throw new IllegalStateException("bad token: " + current + " = " + currentValue()); //$NON-NLS-1$ //$NON-NLS-2$
parseUnit(current.substring(0, dotIndex + 1), ius);
}
}
private void parseUnit(String prefix, List<InstallableUnitDescription> units) {
String unitId = null;
Version unitVersion = null;
boolean unitSingleton = false;
String unitFilter = null;
String unitCopyright = null;
String unitCopyrightLocation = null;
String unitTouchpointId = null;
Version unitTouchpointVersion = null;
String unitUpdateId = null;
VersionRange unitUpdateRange = null;
int unitUpdateSeverity = 0;
String unitUpdateDescription = null;
List<IArtifactKey> unitArtifacts = new ArrayList<>();
Map<String, String> unitProperties = new HashMap<>();
List<IRequirement> unitHostRequirements = new ArrayList<>();
List<IProvidedCapability> unitProvides = new ArrayList<>();
List<IRequirement> unitRequires = new ArrayList<>();
List<IRequirement> unitMetaRequirements = new ArrayList<>();
List<ILicense> unitLicenses = new ArrayList<>();
Map<String, ITouchpointInstruction> unitInstructions = new HashMap<>();
// updatedescriptor ??
while (current != null && current.startsWith(prefix)) {
String token = current.substring(prefix.length());
if (token.equals(ID)) {
unitId = currentValue();
next();
} else if (token.equals(VERSION)) {
unitVersion = Version.parseVersion(substituteVersionAndQualifier(currentValue()));
next();
} else if (token.equals(SINGLETON)) {
unitSingleton = Boolean.parseBoolean(currentValue());
next();
} else if (token.equals(FILTER)) {
unitFilter = currentValue();
next();
} else if (token.equals(COPYRIGHT)) {
unitCopyright = currentValue();
next();
} else if (token.equals(COPYRIGHT_LOCATION)) {
unitCopyrightLocation = currentValue();
next();
} else if (token.equals(TOUCHPOINT_ID)) {
unitTouchpointId = currentValue();
next();
} else if (token.equals(TOUCHPOINT_VERSION)) {
unitTouchpointVersion = Version.parseVersion(substituteVersionAndQualifier(currentValue()));
next();
} else if (token.equals(UPDATE_ID)) {
unitUpdateId = currentValue();
next();
} else if (token.equals(UPDATE_RANGE)) {
unitUpdateRange = VersionRange.create(substituteVersionAndQualifier(currentValue()));
next();
} else if (token.equals(UPDATE_SEVERITY)) {
unitUpdateSeverity = Integer.parseInt(currentValue());
next();
} else if (token.equals(UPDATE_DESCRIPTION)) {
unitUpdateDescription = currentValue();
next();
} else if (token.startsWith(HOST_REQUIREMENTS_PREFIX))
parseRequires(prefix + HOST_REQUIREMENTS_PREFIX, unitHostRequirements);
else if (token.startsWith(ARTIFACTS_PREFIX))
parseArtifacts(prefix + ARTIFACTS_PREFIX, unitArtifacts);
else if (token.startsWith(LICENSES_PREFIX))
parseLicenses(prefix + LICENSES_PREFIX, unitLicenses);
else if (token.startsWith(PROPERTIES_PREFIX))
parseProperties(prefix + PROPERTIES_PREFIX, unitProperties);
else if (token.startsWith(PROVIDES_PREFIX))
parseProvides(prefix + PROVIDES_PREFIX, unitProvides);
else if (token.startsWith(REQUIRES_PREFIX))
parseRequires(prefix + REQUIRES_PREFIX, unitRequires);
else if (token.startsWith(META_REQUIREMENTS_PREFIX))
parseRequires(prefix + META_REQUIREMENTS_PREFIX, unitMetaRequirements);
else if (token.startsWith(INSTRUCTIONS_PREFIX))
parseInstructions(prefix + INSTRUCTIONS_PREFIX, unitInstructions);
else {
// we ignore elements we do not understand
next();
}
}
InstallableUnitDescription description = unitHostRequirements.isEmpty() ? new InstallableUnitDescription()
: new InstallableUnitFragmentDescription();
description.setId(unitId);
description.setVersion(unitVersion);
description.setSingleton(unitSingleton);
description.setFilter(unitFilter);
if (unitCopyright != null || unitCopyrightLocation != null) {
try {
URI uri = unitCopyrightLocation != null ? new URI(unitCopyrightLocation) : null;
description.setCopyright(MetadataFactory.createCopyright(uri, unitCopyright));
} catch (URISyntaxException e) {
throw new IllegalStateException("bad copyright URI at token: " + current + ", " + currentValue()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (unitTouchpointId != null)
description
.setTouchpointType(MetadataFactory.createTouchpointType(unitTouchpointId, unitTouchpointVersion));
if (unitUpdateId != null)
description.setUpdateDescriptor(MetadataFactory.createUpdateDescriptor(unitUpdateId, unitUpdateRange,
unitUpdateSeverity, unitUpdateDescription));
if (!unitLicenses.isEmpty())
description.setLicenses(unitLicenses.toArray(new ILicense[unitLicenses.size()]));
if (!unitArtifacts.isEmpty())
description.setArtifacts(unitArtifacts.toArray(new IArtifactKey[unitArtifacts.size()]));
if (!unitHostRequirements.isEmpty())
((InstallableUnitFragmentDescription) description)
.setHost(unitHostRequirements.toArray(new IRequirement[unitHostRequirements.size()]));
if (!unitProperties.isEmpty()) {
for (Entry<String, String> entry : unitProperties.entrySet()) {
description.setProperty(entry.getKey(), entry.getValue());
}
}
if (!unitProvides.isEmpty())
description.setCapabilities(unitProvides.toArray(new IProvidedCapability[unitProvides.size()]));
if (!unitRequires.isEmpty())
description.setRequirements(unitRequires.toArray(new IRequirement[unitRequires.size()]));
if (!unitMetaRequirements.isEmpty())
description
.setMetaRequirements(unitMetaRequirements.toArray(new IRequirement[unitMetaRequirements.size()]));
if (!unitInstructions.isEmpty())
description.addTouchpointData(MetadataFactory.createTouchpointData(unitInstructions));
adviceOtherIUs.add(description);
}
private void parseLicenses(String prefix, List<ILicense> licenses) {
while (current != null && current.startsWith(prefix)) {
int dotIndex = current.indexOf('.', prefix.length());
if (dotIndex != -1)
throw new IllegalStateException("bad token: " + current + " = " + currentValue()); //$NON-NLS-1$ //$NON-NLS-2$
parseLicense(current, licenses);
}
}
private void parseLicense(String prefix, List<ILicense> licenses) {
String body = currentValue();
next();
prefix += '.';
String location = null;
if (current != null && current.startsWith(prefix)) {
if (current.substring(prefix.length()).equals(LOCATION)) {
location = currentValue();
} else {
// we ignore elements we do not understand
}
next();
}
try {
URI uri = location != null ? new URI(location) : null;
ILicense license = MetadataFactory.createLicense(uri, body);
licenses.add(license);
} catch (URISyntaxException e) {
throw new IllegalStateException("bad license URI at token: " + current + ", " + currentValue()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void parseArtifacts(String prefix, List<IArtifactKey> artifacts) {
while (current != null && current.startsWith(prefix)) {
int dotIndex = current.indexOf('.', prefix.length());
if (dotIndex == -1)
throw new IllegalStateException("bad token: " + current + " = " + currentValue()); //$NON-NLS-1$ //$NON-NLS-2$
parseArtifact(current.substring(0, dotIndex + 1), artifacts);
}
}
private void parseArtifact(String prefix, List<IArtifactKey> artifacts) {
String artifactClassifier = null;
String artifactId = null;
Version artifactVersion = null;
while (current != null && current.startsWith(prefix)) {
String token = current.substring(prefix.length());
switch (token) {
case CLASSIFIER:
artifactClassifier = currentValue();
break;
case ID:
artifactId = currentValue();
break;
case VERSION:
artifactVersion = Version.parseVersion(substituteVersionAndQualifier(currentValue()));
break;
// we ignore elements we do not understand
default:
break;
}
next();
}
IArtifactKey artifactKey = new ArtifactKey(artifactClassifier, artifactId, artifactVersion);
artifacts.add(artifactKey);
}
private String substituteVersionAndQualifier(String version) {
if (version.contains(VERSION_SUBSTITUTION)) {
version = replace(version, VERSION_SUBSTITUTION, hostVersion.toString());
}
if (version.contains(QUALIFIER_SUBSTITUTION)) {
try {
String qualifier = PublisherHelper.toOSGiVersion(hostVersion).getQualifier();
if (qualifier == null)
qualifier = ""; //$NON-NLS-1$
if (qualifier.length() == 0) {
// Note: this works only for OSGi versions and version ranges
// where the qualifier if present must be at the end of a version string
version = replace(version, "." + QUALIFIER_SUBSTITUTION, ""); //$NON-NLS-1$ //$NON-NLS-2$
}
version = replace(version, QUALIFIER_SUBSTITUTION, qualifier);
} catch (UnsupportedOperationException e) {
// Version cannot be converted to OSGi
}
}
return version;
}
// originally from org.eclipse.core.internal.net.StringUtil
public static String replace(String source, String from, String to) {
if (from.length() == 0)
return source;
StringBuilder buffer = new StringBuilder();
int current = 0;
int pos = 0;
while (pos != -1) {
pos = source.indexOf(from, current);
if (pos == -1) {
buffer.append(source.substring(current));
} else {
buffer.append(source.substring(current, pos));
buffer.append(to);
current = pos + from.length();
}
}
return buffer.toString();
}
public Map<String, String> getProperties() {
if (adviceProperties.isEmpty())
return null;
return adviceProperties;
}
public IRequirement[] getRequiredCapabilities() {
if (adviceRequires.isEmpty())
return null;
return adviceRequires.toArray(new IRequirement[adviceRequires.size()]);
}
public IProvidedCapability[] getProvidedCapabilities() {
if (adviceProvides.isEmpty())
return null;
return adviceProvides.toArray(new IProvidedCapability[adviceProvides.size()]);
}
public IUpdateDescriptor getUpdateDescriptor() {
return adviceUpdateDescriptor;
}
public Map<String, ITouchpointInstruction> getTouchpointInstructions() {
if (adviceInstructions.isEmpty())
return null;
return adviceInstructions;
}
public InstallableUnitDescription[] getAdditionalInstallableUnitDescriptions() {
if (adviceOtherIUs.isEmpty())
return null;
return adviceOtherIUs.toArray(new InstallableUnitDescription[adviceOtherIUs.size()]);
}
public IRequirement[] getMetaRequiredCapabilities() {
if (adviceMetaRequires.isEmpty())
return null;
return adviceMetaRequires.toArray(new IRequirement[adviceMetaRequires.size()]);
}
}