blob: 95036613ec91b2e8567bdfc947edc3f3f9a1fdff [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2017 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
* Genuitec, LLC - added license support
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata.repository.io;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.metadata.RequiredCapability;
import org.eclipse.equinox.internal.p2.metadata.RequiredPropertiesMatch;
import org.eclipse.equinox.internal.p2.metadata.repository.Constants;
import org.eclipse.equinox.internal.p2.persistence.XMLWriter;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.expression.*;
public class MetadataWriter extends XMLWriter implements XMLConstants {
public MetadataWriter(OutputStream output, ProcessingInstruction[] piElements) {
super(output, piElements);
// TODO: add a processing instruction for the metadata version
}
/**
* Writes a list of {@link IInstallableUnit}.
* @param units An Iterator of {@link IInstallableUnit}.
* @param size The number of units to write
*/
public void writeInstallableUnits(Iterator<IInstallableUnit> units, int size) {
if (!units.hasNext())
return;
start(INSTALLABLE_UNITS_ELEMENT);
// The size is a bummer. Is it really needed? It forces the use of a collect
attribute(COLLECTION_SIZE_ATTRIBUTE, size);
while (units.hasNext())
writeInstallableUnit(units.next());
end(INSTALLABLE_UNITS_ELEMENT);
}
protected void writeInstallableUnit(IInstallableUnit resolvedIU) {
IInstallableUnit iu = resolvedIU.unresolved();
start(INSTALLABLE_UNIT_ELEMENT);
attribute(ID_ATTRIBUTE, iu.getId());
attribute(VERSION_ATTRIBUTE, iu.getVersion());
attribute(SINGLETON_ATTRIBUTE, iu.isSingleton(), true);
// attribute(FRAGMENT_ATTRIBUTE, iu.isFragment(), false);
boolean simpleRequirements = hasOnlySimpleRequirements(iu);
if (!simpleRequirements)
attribute(GENERATION_ATTRIBUTE, 2);
if (iu instanceof IInstallableUnitFragment) {
IInstallableUnitFragment fragment = (IInstallableUnitFragment) iu;
writeHostRequirements(fragment.getHost());
}
if (iu instanceof IInstallableUnitPatch) {
IInstallableUnitPatch patch = (IInstallableUnitPatch) iu;
writeApplicabilityScope(patch.getApplicabilityScope());
writeRequirementsChange(patch.getRequirementsChange());
writeLifeCycle(patch.getLifeCycle());
}
writeUpdateDescriptor(resolvedIU, resolvedIU.getUpdateDescriptor());
writeProperties(iu.getProperties());
writeMetaRequirements(iu.getMetaRequirements());
writeProvidedCapabilities(iu.getProvidedCapabilities());
if (simpleRequirements && iu instanceof IInstallableUnitFragment) {
Collection<IRequirement> mergedRequirementsAndFragmentHostForPre36Compatibility = new LinkedHashSet<>(iu.getRequirements());
mergedRequirementsAndFragmentHostForPre36Compatibility.addAll(((IInstallableUnitFragment) iu).getHost());
writeRequirements(mergedRequirementsAndFragmentHostForPre36Compatibility);
} else {
writeRequirements(iu.getRequirements());
}
writeTrimmedCdata(IU_FILTER_ELEMENT, iu.getFilter() == null ? null : iu.getFilter().getParameters()[0].toString());
writeArtifactKeys(iu.getArtifacts());
writeTouchpointType(iu.getTouchpointType());
writeTouchpointData(iu.getTouchpointData());
writeLicenses(iu.getLicenses());
writeCopyright(iu.getCopyright());
end(INSTALLABLE_UNIT_ELEMENT);
}
private boolean hasOnlySimpleRequirements(IInstallableUnit iu) {
for (IRequirement r : iu.getRequirements())
if (r.getMax() == 0 || !RequiredCapability.isVersionRangeRequirement(r.getMatches()))
return false;
if (iu.getUpdateDescriptor() != null) {
for (IMatchExpression<IInstallableUnit> m : iu.getUpdateDescriptor().getIUsBeingUpdated()) {
if (!RequiredCapability.isVersionRangeRequirement(m))
return false;
}
}
for (IRequirement r : iu.getMetaRequirements())
if (r.getMax() == 0 || !RequiredCapability.isVersionRangeRequirement(r.getMatches()))
return false;
if (iu instanceof IInstallableUnitFragment) {
for (IRequirement r : ((IInstallableUnitFragment) iu).getHost())
if (!RequiredCapability.isVersionRangeRequirement(r.getMatches()))
return false;
}
if (iu instanceof IInstallableUnitPatch) {
IInstallableUnitPatch iuPatch = (IInstallableUnitPatch) iu;
for (IRequirement[] rArr : iuPatch.getApplicabilityScope())
for (IRequirement r : rArr)
if (!RequiredCapability.isVersionRangeRequirement(r.getMatches()))
return false;
IRequirement lifeCycle = iuPatch.getLifeCycle();
if (lifeCycle != null && !RequiredCapability.isVersionRangeRequirement(lifeCycle.getMatches()))
return false;
}
return true;
}
protected void writeLifeCycle(IRequirement capability) {
if (capability == null)
return;
start(LIFECYCLE);
writeRequirement(capability);
end(LIFECYCLE);
}
protected void writeHostRequirements(Collection<IRequirement> hostRequirements) {
if (hostRequirements != null && hostRequirements.size() > 0) {
start(HOST_REQUIREMENTS_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, hostRequirements.size());
for (IRequirement req : hostRequirements) {
writeRequirement(req);
}
end(HOST_REQUIREMENTS_ELEMENT);
}
}
protected void writeProvidedCapabilities(Collection<IProvidedCapability> capabilities) {
if (capabilities != null && capabilities.size() > 0) {
start(PROVIDED_CAPABILITIES_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, capabilities.size());
for (IProvidedCapability capability : capabilities) {
writeProvidedCapability(capability);
}
end(PROVIDED_CAPABILITIES_ELEMENT);
}
}
protected void writeProvidedCapability(IProvidedCapability capability) {
start(PROVIDED_CAPABILITY_ELEMENT);
attribute(NAMESPACE_ATTRIBUTE, capability.getNamespace());
attribute(NAME_ATTRIBUTE, capability.getName());
attribute(VERSION_ATTRIBUTE, capability.getVersion());
Map<String, Object> props = new HashMap<>(capability.getProperties());
props.remove(capability.getNamespace());
props.remove(IProvidedCapability.PROPERTY_VERSION);
if (!props.isEmpty()) {
writeProperties(props);
}
end(PROVIDED_CAPABILITY_ELEMENT);
}
protected void writeMetaRequirements(Collection<IRequirement> metaRequirements) {
if (metaRequirements != null && metaRequirements.size() > 0) {
start(META_REQUIREMENTS_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, metaRequirements.size());
for (IRequirement req : metaRequirements) {
writeRequirement(req);
}
end(META_REQUIREMENTS_ELEMENT);
}
}
protected void writeRequirements(Collection<IRequirement> requirements) {
if (requirements != null && requirements.size() > 0) {
start(REQUIREMENTS_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, requirements.size());
for (IRequirement req : requirements) {
writeRequirement(req);
}
end(REQUIREMENTS_ELEMENT);
}
}
protected void writeUpdateDescriptor(IInstallableUnit iu, IUpdateDescriptor descriptor) {
if (descriptor == null)
return;
if (descriptor.getIUsBeingUpdated().size() > 1)
throw new IllegalStateException();
IMatchExpression<IInstallableUnit> singleUD = descriptor.getIUsBeingUpdated().iterator().next();
start(UPDATE_DESCRIPTOR_ELEMENT);
if (RequiredCapability.isVersionRangeRequirement(singleUD)) {
attribute(ID_ATTRIBUTE, RequiredCapability.extractName(singleUD));
attribute(VERSION_RANGE_ATTRIBUTE, RequiredCapability.extractRange(singleUD));
} else {
writeMatchExpression(singleUD);
}
attribute(UPDATE_DESCRIPTOR_SEVERITY, descriptor.getSeverity());
attribute(DESCRIPTION_ATTRIBUTE, descriptor.getDescription());
end(UPDATE_DESCRIPTOR_ELEMENT);
}
protected void writeApplicabilityScope(IRequirement[][] capabilities) {
start(APPLICABILITY_SCOPE);
for (IRequirement[] capability : capabilities) {
start(APPLY_ON);
writeRequirements(Arrays.asList(capability));
end(APPLY_ON);
}
end(APPLICABILITY_SCOPE);
}
protected void writeRequirementsChange(List<IRequirementChange> changes) {
start(REQUIREMENT_CHANGES);
for (int i = 0; i < changes.size(); i++) {
writeRequirementChange(changes.get(i));
}
end(REQUIREMENT_CHANGES);
}
protected void writeRequirementChange(IRequirementChange change) {
start(REQUIREMENT_CHANGE);
if (change.applyOn() != null) {
start(REQUIREMENT_FROM);
writeRequirement(change.applyOn());
end(REQUIREMENT_FROM);
}
if (change.newValue() != null) {
start(REQUIREMENT_TO);
writeRequirement(change.newValue());
end(REQUIREMENT_TO);
}
end(REQUIREMENT_CHANGE);
}
protected void writeRequirement(IRequirement requirement) {
IMatchExpression<IInstallableUnit> match = requirement.getMatches();
// A (namespace, name, version-range) type of requirement
if (requirement.getMax() > 0 && RequiredCapability.isVersionRangeRequirement(match)) {
start(REQUIREMENT_ELEMENT);
attribute(NAMESPACE_ATTRIBUTE, RequiredCapability.extractNamespace(match));
attribute(NAME_ATTRIBUTE, RequiredCapability.extractName(match));
attribute(VERSION_RANGE_ATTRIBUTE, RequiredCapability.extractRange(match));
attribute(REQUIRED_CAPABILITY_OPTIONAL_ATTRIBUTE, requirement.getMin() == 0, false);
attribute(REQUIRED_CAPABILITY_MULTIPLE_ATTRIBUTE, requirement.getMax() > 1, false);
}
// A (namespace, attributes-match) type of requirement
else if (RequiredPropertiesMatch.isPropertiesMatchRequirement(match)) {
start(REQUIREMENT_PROPERTIES_ELEMENT);
attribute(NAMESPACE_ATTRIBUTE, RequiredPropertiesMatch.extractNamespace(match));
attribute(MATCH_ATTRIBUTE, RequiredPropertiesMatch.extractPropertiesMatch(match));
if (requirement.getMin() != 1) {
attribute(MIN_ATTRIBUTE, requirement.getMin());
}
if (requirement.getMax() != 1) {
attribute(MAX_ATTRIBUTE, requirement.getMax());
}
}
// A general match expression type of requirement
else {
start(REQUIREMENT_ELEMENT);
writeMatchExpression(match);
if (requirement.getMin() != 1) {
attribute(MIN_ATTRIBUTE, requirement.getMin());
}
if (requirement.getMax() != 1) {
attribute(MAX_ATTRIBUTE, requirement.getMax());
}
}
attribute(REQUIREMENT_GREED_ATTRIBUTE, requirement.isGreedy(), true);
if (requirement.getFilter() != null) {
writeTrimmedCdata(REQUIREMENT_FILTER_ELEMENT, requirement.getFilter().getParameters()[0].toString());
}
if (requirement.getDescription() != null) {
writeTrimmedCdata(REQUIREMENT_DESCRIPTION_ELEMENT, requirement.getDescription());
}
end();
}
private void writeMatchExpression(IMatchExpression<IInstallableUnit> match) {
attribute(MATCH_ATTRIBUTE, ExpressionUtil.getOperand(match));
Object[] params = match.getParameters();
if (params.length > 0) {
IExpressionFactory factory = ExpressionUtil.getFactory();
IExpression[] constantArray = new IExpression[params.length];
for (int idx = 0; idx < params.length; ++idx)
constantArray[idx] = factory.constant(params[idx]);
attribute(MATCH_PARAMETERS_ATTRIBUTE, factory.array(constantArray));
}
}
protected void writeArtifactKeys(Collection<IArtifactKey> artifactKeys) {
if (artifactKeys != null && artifactKeys.size() > 0) {
start(ARTIFACT_KEYS_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, artifactKeys.size());
for (IArtifactKey artifactKey : artifactKeys) {
start(ARTIFACT_KEY_ELEMENT);
attribute(ARTIFACT_KEY_CLASSIFIER_ATTRIBUTE, artifactKey.getClassifier());
attribute(ID_ATTRIBUTE, artifactKey.getId());
attribute(VERSION_ATTRIBUTE, artifactKey.getVersion());
end(ARTIFACT_KEY_ELEMENT);
}
end(ARTIFACT_KEYS_ELEMENT);
}
}
protected void writeTouchpointType(ITouchpointType touchpointType) {
start(TOUCHPOINT_TYPE_ELEMENT);
attribute(ID_ATTRIBUTE, touchpointType.getId());
attribute(VERSION_ATTRIBUTE, touchpointType.getVersion());
end(TOUCHPOINT_TYPE_ELEMENT);
}
protected void writeTouchpointData(Collection<ITouchpointData> touchpointData) {
if (touchpointData != null && touchpointData.size() > 0) {
start(TOUCHPOINT_DATA_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, touchpointData.size());
for (ITouchpointData nextData : touchpointData) {
Map<String, ITouchpointInstruction> instructions = nextData.getInstructions();
if (instructions.size() > 0) {
start(TOUCHPOINT_DATA_INSTRUCTIONS_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, instructions.size());
for (Map.Entry<String, ITouchpointInstruction> entry : instructions.entrySet()) {
start(TOUCHPOINT_DATA_INSTRUCTION_ELEMENT);
attribute(TOUCHPOINT_DATA_INSTRUCTION_KEY_ATTRIBUTE, entry.getKey());
ITouchpointInstruction instruction = entry.getValue();
if (instruction.getImportAttribute() != null)
attribute(TOUCHPOINT_DATA_INSTRUCTION_IMPORT_ATTRIBUTE, instruction.getImportAttribute());
cdata(instruction.getBody(), true);
end(TOUCHPOINT_DATA_INSTRUCTION_ELEMENT);
}
end(TOUCHPOINT_DATA_INSTRUCTIONS_ELEMENT);
}
}
end(TOUCHPOINT_DATA_ELEMENT);
}
}
private void writeTrimmedCdata(String element, String filter) {
String trimmed;
if (filter != null && (trimmed = filter.trim()).length() > 0) {
start(element);
cdata(trimmed);
end(element);
}
}
private void writeLicenses(Collection<ILicense> licenses) {
if (licenses != null && licenses.size() > 0) {
// In the future there may be more than one license, so we write this
// as a collection of one.
// See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=216911
start(LICENSES_ELEMENT);
attribute(COLLECTION_SIZE_ATTRIBUTE, licenses.size());
for (ILicense license : licenses) {
if (license == null)
continue;
start(LICENSE_ELEMENT);
if (license.getLocation() != null) {
attribute(URI_ATTRIBUTE, license.getLocation().toString());
try {
// we write the URL attribute for backwards compatibility with 3.4.x
// this attribute should be removed if we make a breaking format change.
attribute(URL_ATTRIBUTE, URIUtil.toURL(license.getLocation()).toExternalForm());
} catch (MalformedURLException e) {
attribute(URL_ATTRIBUTE, license.getLocation().toString());
}
}
cdata(license.getBody(), true);
end(LICENSE_ELEMENT);
}
end(LICENSES_ELEMENT);
}
}
private void writeCopyright(ICopyright copyright) {
if (copyright != null) {
start(COPYRIGHT_ELEMENT);
try {
if (copyright.getLocation() != null) {
attribute(URI_ATTRIBUTE, copyright.getLocation().toString());
try {
// we write the URL attribute for backwards compatibility with 3.4.x
// this attribute should be removed if we make a breaking format change.
attribute(URL_ATTRIBUTE, URIUtil.toURL(copyright.getLocation()).toExternalForm());
} catch (MalformedURLException e) {
attribute(URL_ATTRIBUTE, copyright.getLocation().toString());
}
}
} catch (IllegalStateException ise) {
LogHelper.log(new Status(IStatus.INFO, Constants.ID, "Error writing the copyright URL: " + copyright.getLocation())); //$NON-NLS-1$
}
cdata(copyright.getBody(), true);
end(COPYRIGHT_ELEMENT);
}
}
}