/* * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.api.internal.artifacts.ivyservice.publisher; import com.google.common.base.Joiner; import org.gradle.api.UncheckedIOException; import org.gradle.api.artifacts.ModuleVersionSelector; import org.gradle.api.artifacts.component.ModuleComponentIdentifier; import org.gradle.api.internal.artifacts.ivyservice.NamespaceId; import org.gradle.internal.component.external.descriptor.Configuration; import org.gradle.internal.component.external.descriptor.ModuleDescriptorState; import org.gradle.internal.component.external.model.IvyModuleArtifactPublishMetadata; import org.gradle.internal.component.external.model.IvyModulePublishMetadata; import org.gradle.internal.component.model.Exclude; import org.gradle.internal.component.model.IvyArtifactName; import org.gradle.internal.component.model.LocalOriginDependencyMetadata; import org.gradle.internal.xml.SimpleXmlWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; public class IvyXmlModuleDescriptorWriter implements IvyModuleDescriptorWriter { public static final String IVY_DATE_PATTERN = "yyyyMMddHHmmss"; @Override public void write(IvyModulePublishMetadata module, File output) { try { output.getParentFile().mkdirs(); OutputStream outputStream = new FileOutputStream(output); try { SimpleXmlWriter xmlWriter = new SimpleXmlWriter(outputStream, " "); writeTo(module, xmlWriter); xmlWriter.flush(); } finally { outputStream.close(); } } catch (IOException e) { throw new UncheckedIOException(e); } } private void writeTo(IvyModulePublishMetadata metadata, SimpleXmlWriter writer) throws IOException { writer.startElement("ivy-module"); writer.attribute("version", "2.0"); writer.attribute("xmlns:" + IvyModulePublishMetadata.IVY_MAVEN_NAMESPACE_PREFIX, IvyModulePublishMetadata.IVY_MAVEN_NAMESPACE); ModuleDescriptorState descriptor = metadata.getModuleDescriptor(); printInfoTag(descriptor, writer); printConfigurations(metadata, writer); printPublications(metadata.getArtifacts(), writer); printDependencies(metadata, writer); writer.endElement(); } private static void printInfoTag(ModuleDescriptorState descriptor, SimpleXmlWriter writer) throws IOException { ModuleComponentIdentifier id = descriptor.getComponentIdentifier(); writer.startElement("info"); writer.attribute("organisation", id.getGroup()); writer.attribute("module", id.getModule()); String branch = descriptor.getBranch(); if (branch != null) { writer.attribute("branch", branch); } writer.attribute("revision", id.getVersion()); writer.attribute("status", descriptor.getStatus()); if (descriptor.isGenerated()) { writer.attribute("default", "true"); } printUnusedContent(descriptor, writer); for (Map.Entry<NamespaceId, String> entry : descriptor.getExtraInfo().entrySet()) { if (entry.getValue() == null || entry.getValue().length() == 0) { continue; } NamespaceId namespaceId = entry.getKey(); writer.startElement("ns:" + namespaceId.getName()); writer.attribute("xmlns:ns", namespaceId.getNamespace()); writer.characters(entry.getValue()); writer.endElement(); } writer.endElement(); } /** * These values are written to the desriptor, but never used. They remain here so that the checksum of the descriptor * changes when these values change: thie behaviour is utilized in integration tests. */ private static void printUnusedContent(ModuleDescriptorState descriptor, SimpleXmlWriter writer) throws IOException { SimpleDateFormat ivyDateFormat = new SimpleDateFormat(IVY_DATE_PATTERN); Date publicationDate = descriptor.getPublicationDate(); if (publicationDate != null) { writer.attribute("publication", ivyDateFormat.format(publicationDate)); } String description = descriptor.getDescription(); if (description != null && description.trim().length() > 0) { writer.startElement("description"); writer.characters(description); writer.endElement(); } } private void printConfigurations(IvyModulePublishMetadata metadata, SimpleXmlWriter writer) throws IOException { Collection<Configuration> configurations = metadata.getConfigurations().values(); if (!configurations.isEmpty()) { writer.startElement("configurations"); for (Configuration configuration : configurations) { printConfiguration(configuration, writer); } writer.endElement(); } } private void printConfiguration(Configuration conf, SimpleXmlWriter writer) throws IOException { writer.startElement("conf"); writer.attribute("name", conf.getName()); writer.attribute("visibility", conf.isVisible() ? "public" : "private"); List<String> exts = conf.getExtendsFrom(); if (exts.size() > 0) { writer.attribute("extends", Joiner.on(',').join(exts)); } if (!conf.isTransitive()) { writer.attribute("transitive", "false"); } writer.endElement(); } private static void printPublications(Collection<IvyModuleArtifactPublishMetadata> artifacts, SimpleXmlWriter writer) throws IOException { writer.startElement("publications"); for (IvyModuleArtifactPublishMetadata artifactMetadata : artifacts) { IvyArtifactName artifact = artifactMetadata.getArtifactName(); writer.startElement("artifact"); writer.attribute("name", artifact.getName()); writer.attribute("type", artifact.getType()); writer.attribute("ext", artifact.getExtension() == null ? "" : artifact.getExtension()); writer.attribute("conf", Joiner.on(",").join(artifactMetadata.getConfigurations())); if (artifact.getClassifier() != null) { printExtraAttributes(Collections.singletonMap("m:classifier", artifact.getClassifier()), writer); } writer.endElement(); } writer.endElement(); } private void printDependencies(IvyModulePublishMetadata metadata, SimpleXmlWriter writer) throws IOException { Collection<LocalOriginDependencyMetadata> dependencies = metadata.getDependencies(); if (dependencies.size() > 0) { writer.startElement("dependencies"); for (LocalOriginDependencyMetadata dd : dependencies) { printDependency(metadata, dd, writer); } printAllExcludes(metadata, writer); writer.endElement(); } } protected void printDependency(IvyModulePublishMetadata metadata, LocalOriginDependencyMetadata dep, SimpleXmlWriter writer) throws IOException { writer.startElement("dependency"); ModuleVersionSelector requested = dep.getRequested(); writer.attribute("org", requested.getGroup()); writer.attribute("name", requested.getName()); writer.attribute("rev", requested.getVersion()); if (dep.getDynamicConstraintVersion() != null && !dep.getDynamicConstraintVersion().equals(requested.getVersion())) { writer.attribute("revConstraint", dep.getDynamicConstraintVersion()); } if (dep.isForce()) { writer.attribute("force", "true"); } if (dep.isChanging()) { writer.attribute("changing", "true"); } if (!dep.isTransitive()) { writer.attribute("transitive", "false"); } writer.attribute("conf", getConfMapping(dep)); for (IvyArtifactName dependencyArtifact : dep.getArtifacts()) { printDependencyArtifact(writer, dependencyArtifact, dep.getModuleConfiguration()); } printDependencyExcludeRules(metadata, writer, dep.getExcludes()); writer.endElement(); } private String getConfMapping(LocalOriginDependencyMetadata dependency) { return dependency.getModuleConfiguration() + "->" + dependency.getDependencyConfiguration(); } private static void printAllExcludes(IvyModulePublishMetadata metadata, SimpleXmlWriter writer) throws IOException { List<Exclude> excludes = metadata.getModuleDescriptor().getExcludes(); for (Exclude exclude : excludes) { writer.startElement("exclude"); writer.attribute("org", exclude.getModuleId().getGroup()); writer.attribute("module", exclude.getModuleId().getName()); writer.attribute("artifact", exclude.getArtifact().getName()); writer.attribute("type", exclude.getArtifact().getType()); writer.attribute("ext", exclude.getArtifact().getExtension()); Set<String> ruleConfs = exclude.getConfigurations(); if (!metadata.getConfigurations().keySet().equals(ruleConfs)) { writer.attribute("conf", Joiner.on(',').join(ruleConfs)); } writer.attribute("matcher", exclude.getMatcher()); writer.endElement(); } } private static void printDependencyExcludeRules(IvyModulePublishMetadata metadata, SimpleXmlWriter writer, Collection<Exclude> excludes) throws IOException { for (Exclude exclude : excludes) { writer.startElement("exclude"); writer.attribute("org", exclude.getModuleId().getGroup()); writer.attribute("module", exclude.getModuleId().getName()); writer.attribute("name", exclude.getArtifact().getName()); writer.attribute("type", exclude.getArtifact().getType()); writer.attribute("ext", exclude.getArtifact().getExtension()); Set<String> ruleConfs = exclude.getConfigurations(); if (!metadata.getConfigurations().keySet().equals(ruleConfs)) { writer.attribute("conf", Joiner.on(',').join(ruleConfs)); } writer.attribute("matcher", exclude.getMatcher()); writer.endElement(); } } private static void printDependencyArtifact(SimpleXmlWriter writer, IvyArtifactName artifact, String configuration) throws IOException { writer.startElement("artifact"); writer.attribute("name", artifact.getName()); writer.attribute("type", artifact.getType()); writer.attribute("ext", artifact.getExtension()); if (artifact.getClassifier() != null) { printExtraAttributes(Collections.singletonMap("m:classifier", artifact.getClassifier()), writer); } writer.attribute("conf", configuration); writer.endElement(); } /** * Writes the specified <tt>Map</tt> containing the extra attributes to the given <tt>PrintWriter</tt>. * * @param extra the extra attributes, can be <tt>null</tt> * @param writer the writer to use */ private static void printExtraAttributes(Map<String, ?> extra, SimpleXmlWriter writer) throws IOException { if (extra == null) { return; } for (Map.Entry<String, ?> entry : extra.entrySet()) { writer.attribute(entry.getKey(), entry.getValue().toString()); } } }