blob: 258fc7e2b6d1d4094c9f13a1351d134c75c02a91 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* https://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.apache.ivy.plugins.parser.m2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.IvyContext;
import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ExcludeRule;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.settings.IvySettings;
import org.apache.ivy.core.settings.IvyVariableContainer;
import org.apache.ivy.plugins.parser.m2.PomWriterOptions.ConfigurationScopeMapping;
import org.apache.ivy.plugins.parser.m2.PomWriterOptions.ExtraDependency;
import org.apache.ivy.util.ConfigurationUtils;
import static org.apache.ivy.util.StringUtils.isNullOrEmpty;
public final class PomModuleDescriptorWriter {
private static final String SKIP_LINE = "SKIP_LINE";
@SuppressWarnings("serial")
private static final ConfigurationScopeMapping DEFAULT_MAPPING = new ConfigurationScopeMapping(
new LinkedHashMap<String, String>() {
{
put("compile", "compile");
put("runtime", "runtime");
put("provided", "provided");
put("test", "test");
put("system", "system");
}
});
private PomModuleDescriptorWriter() {
}
public static void write(ModuleDescriptor md, File output, PomWriterOptions options)
throws IOException {
LineNumberReader in;
if (options.getTemplate() == null) {
in = new LineNumberReader(new InputStreamReader(
PomModuleDescriptorWriter.class.getResourceAsStream("pom.template")));
} else {
in = new LineNumberReader(new InputStreamReader(new FileInputStream(
options.getTemplate())));
}
if (output.getParentFile() != null) {
output.getParentFile().mkdirs();
}
PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output),
StandardCharsets.UTF_8));
try {
IvySettings settings = IvyContext.getContext().getSettings();
IvyVariableContainer variables = new IvyVariableContainerWrapper(
settings.getVariableContainer());
variables.setVariable("ivy.pom.license", SKIP_LINE, true);
variables.setVariable("ivy.pom.header", SKIP_LINE, true);
variables.setVariable("ivy.pom.groupId", SKIP_LINE, true);
variables.setVariable("ivy.pom.artifactId", SKIP_LINE, true);
variables.setVariable("ivy.pom.version", SKIP_LINE, true);
variables.setVariable("ivy.pom.packaging", SKIP_LINE, true);
variables.setVariable("ivy.pom.name", SKIP_LINE, true);
variables.setVariable("ivy.pom.description", SKIP_LINE, true);
variables.setVariable("ivy.pom.url", SKIP_LINE, true);
if (options.getLicenseHeader() != null) {
variables.setVariable("ivy.pom.license", options.getLicenseHeader(), true);
}
if (options.isPrintIvyInfo()) {
String header = "<!--\n" + " Apache Maven 2 POM generated by Apache Ivy\n"
+ " " + Ivy.getIvyHomeURL() + "\n" + " Apache Ivy version: "
+ Ivy.getIvyVersion() + " " + Ivy.getIvyDate() + "\n" + "-->";
variables.setVariable("ivy.pom.header", header, true);
}
setModuleVariables(md, variables, options);
boolean dependenciesPrinted = false;
int lastIndent = 0;
int indent = 0;
String line = in.readLine();
while (line != null) {
line = IvyPatternHelper.substituteVariables(line, variables);
if (line.contains(SKIP_LINE)) {
// skip this line
line = in.readLine();
continue;
}
if (line.trim().isEmpty()) {
// empty line
out.println(line);
line = in.readLine();
continue;
}
lastIndent = indent;
indent = line.indexOf('<');
if (!dependenciesPrinted && line.contains("</dependencies>")) {
printDependencies(md, out, options, indent, false);
dependenciesPrinted = true;
}
if (!dependenciesPrinted && line.contains("</project>")) {
printDependencies(md, out, options, lastIndent, true);
dependenciesPrinted = true;
}
out.println(line);
line = in.readLine();
}
} finally {
in.close();
out.close();
}
}
private static void setModuleVariables(ModuleDescriptor md, IvyVariableContainer variables,
PomWriterOptions options) {
ModuleRevisionId mrid = md.getModuleRevisionId();
variables.setVariable("ivy.pom.groupId", mrid.getOrganisation(), true);
String artifactId = options.getArtifactName();
if (artifactId == null) {
artifactId = mrid.getName();
}
String packaging = options.getArtifactPackaging();
if (packaging == null) {
// find artifact to determine the packaging
Artifact artifact = findArtifact(md, artifactId);
if (artifact == null) {
// no suitable artifact found, default to 'pom'
packaging = "pom";
} else {
packaging = artifact.getType();
}
}
variables.setVariable("ivy.pom.artifactId", artifactId, true);
variables.setVariable("ivy.pom.packaging", packaging, true);
if (mrid.getRevision() != null) {
variables.setVariable("ivy.pom.version", mrid.getRevision(), true);
}
if (options.getDescription() != null) {
variables.setVariable("ivy.pom.description", options.getDescription(), true);
} else if (!isNullOrEmpty(md.getDescription())) {
variables.setVariable("ivy.pom.description", md.getDescription(), true);
}
if (md.getHomePage() != null) {
variables.setVariable("ivy.pom.url", md.getHomePage(), true);
}
}
/**
* Returns the first artifact with the correct name and without a classifier.
*/
private static Artifact findArtifact(ModuleDescriptor md, String artifactName) {
for (Artifact artifact : md.getAllArtifacts()) {
if (artifact.getName().equals(artifactName)
&& artifact.getAttribute("classifier") == null) {
return artifact;
}
}
return null;
}
private static void indent(PrintWriter out, int indent) {
for (int i = 0; i < indent; i++) {
out.print(' ');
}
}
private static void printDependencies(ModuleDescriptor md, PrintWriter out,
PomWriterOptions options, int indent, boolean printDependencies) {
List<ExtraDependency> extraDeps = options.getExtraDependencies();
DependencyDescriptor[] dds = getDependencies(md, options);
if (!extraDeps.isEmpty() || (dds.length > 0)) {
if (printDependencies) {
indent(out, indent);
out.println("<dependencies>");
}
// print the extra dependencies first
for (ExtraDependency dep : extraDeps) {
String groupId = dep.getGroup();
if (groupId == null) {
groupId = md.getModuleRevisionId().getOrganisation();
}
String version = dep.getVersion();
if (version == null) {
version = md.getModuleRevisionId().getRevision();
}
printDependency(out, indent, groupId, dep.getArtifact(), version, dep.getType(),
dep.getClassifier(), dep.getScope(), dep.isOptional(), true, null);
}
// now print the dependencies listed in the ModuleDescriptor
ConfigurationScopeMapping mapping = options.getMapping();
if (mapping == null) {
mapping = DEFAULT_MAPPING;
}
for (DependencyDescriptor dd : dds) {
ModuleRevisionId mrid = dd.getDependencyRevisionId();
ExcludeRule[] excludes = null;
if (dd.canExclude()) {
excludes = dd.getAllExcludeRules();
}
DependencyArtifactDescriptor[] dads = dd.getAllDependencyArtifacts();
if (dads.length > 0) {
for (DependencyArtifactDescriptor dad : dads) {
String type = dad.getType();
String classifier = dad.getExtraAttribute("classifier");
String scope = mapping.getScope(dd.getModuleConfigurations());
boolean optional = mapping.isOptional(dd.getModuleConfigurations());
printDependency(out, indent, mrid.getOrganisation(), mrid.getName(),
mrid.getRevision(), type, classifier, scope, optional,
dd.isTransitive(), excludes);
}
} else {
String scope = mapping.getScope(dd.getModuleConfigurations());
boolean optional = mapping.isOptional(dd.getModuleConfigurations());
final String classifier = dd.getExtraAttribute("classifier");
printDependency(out, indent, mrid.getOrganisation(), mrid.getName(),
mrid.getRevision(), null, classifier, scope, optional, dd.isTransitive(),
excludes);
}
}
if (printDependencies) {
indent(out, indent);
out.println("</dependencies>");
}
}
}
private static void printDependency(PrintWriter out, int indent, String groupId,
String artifactId, String version, String type, String classifier, String scope,
boolean isOptional, boolean isTransitive, ExcludeRule[] excludes) {
indent(out, indent * 2);
out.println("<dependency>");
indent(out, indent * 3);
out.println("<groupId>" + groupId + "</groupId>");
indent(out, indent * 3);
out.println("<artifactId>" + artifactId + "</artifactId>");
indent(out, indent * 3);
out.println("<version>" + version + "</version>");
if (type != null && !"jar".equals(type)) {
indent(out, indent * 3);
out.println("<type>" + type + "</type>");
}
if (classifier != null) {
indent(out, indent * 3);
out.println("<classifier>" + classifier + "</classifier>");
}
if (scope != null) {
indent(out, indent * 3);
out.println("<scope>" + scope + "</scope>");
}
if (isOptional) {
indent(out, indent * 3);
out.println("<optional>true</optional>");
}
if (!isTransitive) {
indent(out, indent * 3);
out.println("<exclusions>");
indent(out, indent * 4);
out.println("<exclusion>");
indent(out, indent * 5);
out.println("<groupId>*</groupId>");
indent(out, indent * 5);
out.println("<artifactId>*</artifactId>");
indent(out, indent * 4);
out.println("</exclusion>");
indent(out, indent * 3);
out.println("</exclusions>");
} else if (excludes != null) {
printExclusions(excludes, out, indent);
}
indent(out, indent * 2);
out.println("</dependency>");
}
private static void printExclusions(ExcludeRule[] exclusions, PrintWriter out, int indent) {
indent(out, indent * 3);
out.println("<exclusions>");
for (ExcludeRule exclusion : exclusions) {
indent(out, indent * 4);
out.println("<exclusion>");
indent(out, indent * 5);
out.println(
"<groupId>" + exclusion.getId().getModuleId().getOrganisation() + "</groupId>");
indent(out, indent * 5);
out.println(
"<artifactId>" + exclusion.getId().getModuleId().getName() + "</artifactId>");
indent(out, indent * 4);
out.println("</exclusion>");
}
indent(out, indent * 3);
out.println("</exclusions>");
}
private static DependencyDescriptor[] getDependencies(ModuleDescriptor md,
PomWriterOptions options) {
String[] confs = ConfigurationUtils.replaceWildcards(options.getConfs(), md);
List<DependencyDescriptor> result = new ArrayList<>();
for (DependencyDescriptor dd : md.getDependencies()) {
String[] depConfs = dd.getDependencyConfigurations(confs);
if (depConfs != null && depConfs.length > 0) {
result.add(dd);
}
}
return result.toArray(new DependencyDescriptor[result.size()]);
}
/**
* Wraps an {@link IvyVariableContainer} delegating most method calls to the wrapped instance,
* except for a set of variables which are only stored locally in the wrapper, and not
* propagated to the wrapped instance.
*/
private static final class IvyVariableContainerWrapper implements IvyVariableContainer {
private final IvyVariableContainer variables;
private Map<String, String> localVariables = new HashMap<>();
private IvyVariableContainerWrapper(IvyVariableContainer variables) {
this.variables = variables;
}
public void setVariable(String varName, String value, boolean overwrite) {
localVariables.put(varName, value);
}
public void setEnvironmentPrefix(String prefix) {
variables.setEnvironmentPrefix(prefix);
}
public String getVariable(String name) {
String result = variables.getVariable(name);
if (result == null) {
result = localVariables.get(name);
}
return result;
}
public Object clone() {
throw new UnsupportedOperationException();
}
}
}