/******************************************************************************* * 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 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 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 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 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 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 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 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 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 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 (int i = 0; i < capabilities.length; i++) { start(APPLY_ON); writeRequirements(Arrays.asList(capabilities[i])); end(APPLY_ON); } end(APPLICABILITY_SCOPE); } protected void writeRequirementsChange(List 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 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 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 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 touchpointData) { if (touchpointData != null && touchpointData.size() > 0) { start(TOUCHPOINT_DATA_ELEMENT); attribute(COLLECTION_SIZE_ATTRIBUTE, touchpointData.size()); for (ITouchpointData nextData : touchpointData) { Map instructions = nextData.getInstructions(); if (instructions.size() > 0) { start(TOUCHPOINT_DATA_INSTRUCTIONS_ELEMENT); attribute(COLLECTION_SIZE_ATTRIBUTE, instructions.size()); for (Map.Entry 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 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); } } }