Add Maven build support

This commit provides a Maven build system implementation with a writer
that can generate `pom.xml` files based on a configurable model.

Closes gh-814

Co-authored-by: Stephane Nicoll <snicoll@pivotal.io>
This commit is contained in:
Andy Wilkinson 2019-02-07 15:28:57 +01:00 committed by Stephane Nicoll
parent 2bd64ed6ed
commit 3f585337da
11 changed files with 1601 additions and 1 deletions

View File

@ -0,0 +1,132 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.buildsystem.maven;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import io.spring.initializr.generator.buildsystem.Build;
import io.spring.initializr.generator.buildsystem.BuildItemResolver;
/**
* Maven build for a project.
*
* @author Andy Wilkinson
*/
public class MavenBuild extends Build {
private MavenParent parent;
private String name;
private String description;
private String sourceDirectory;
private String testSourceDirectory;
private final Map<String, String> properties = new TreeMap<>();
private final List<MavenPlugin> plugins = new ArrayList<>();
private String packaging;
public MavenBuild(BuildItemResolver buildItemResolver) {
super(buildItemResolver);
}
public MavenBuild() {
this(null);
}
public MavenParent parent(String groupId, String artifactId, String version) {
this.parent = new MavenParent(groupId, artifactId, version);
return this.parent;
}
public MavenParent getParent() {
return this.parent;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
public void setProperty(String key, String value) {
this.properties.put(key, value);
}
public Map<String, String> getProperties() {
return Collections.unmodifiableMap(this.properties);
}
public String getSourceDirectory() {
return this.sourceDirectory;
}
public void setSourceDirectory(String sourceDirectory) {
this.sourceDirectory = sourceDirectory;
}
public String getTestSourceDirectory() {
return this.testSourceDirectory;
}
public void setTestSourceDirectory(String testSourceDirectory) {
this.testSourceDirectory = testSourceDirectory;
}
public MavenPlugin plugin(String groupId, String artifactId) {
MavenPlugin plugin = new MavenPlugin(groupId, artifactId);
this.plugins.add(plugin);
return plugin;
}
public MavenPlugin plugin(String groupId, String artifactId, String version) {
MavenPlugin plugin = new MavenPlugin(groupId, artifactId, version);
this.plugins.add(plugin);
return plugin;
}
public List<MavenPlugin> getPlugins() {
return Collections.unmodifiableList(this.plugins);
}
public void setPackaging(String packaging) {
this.packaging = packaging;
}
public String getPackaging() {
return this.packaging;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.buildsystem.maven;
import io.spring.initializr.generator.buildsystem.BuildSystem;
/**
* Maven {@link BuildSystem}.
*
* @author Andy Wilkinson
*/
public final class MavenBuildSystem implements BuildSystem {
/**
* Maven {@link BuildSystem} identifier.
*/
public static final String ID = "maven";
@Override
public String id() {
return ID;
}
@Override
public String toString() {
return id();
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.buildsystem.maven;
import io.spring.initializr.generator.buildsystem.BuildSystemFactory;
/**
* {@link BuildSystemFactory Factory} for {@link MavenBuildSystem}.
*
* @author Andy Wilkinson
*/
class MavenBuildSystemFactory implements BuildSystemFactory {
@Override
public MavenBuildSystem createBuildSystem(String id) {
if (MavenBuildSystem.ID.equals(id)) {
return new MavenBuildSystem();
}
return null;
}
}

View File

@ -0,0 +1,371 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.buildsystem.maven;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.spring.initializr.generator.buildsystem.BillOfMaterials;
import io.spring.initializr.generator.buildsystem.Dependency;
import io.spring.initializr.generator.buildsystem.DependencyComparator;
import io.spring.initializr.generator.buildsystem.DependencyContainer;
import io.spring.initializr.generator.buildsystem.DependencyScope;
import io.spring.initializr.generator.buildsystem.MavenRepository;
import io.spring.initializr.generator.buildsystem.maven.MavenPlugin.Configuration;
import io.spring.initializr.generator.buildsystem.maven.MavenPlugin.Execution;
import io.spring.initializr.generator.buildsystem.maven.MavenPlugin.Setting;
import io.spring.initializr.generator.io.IndentingWriter;
import io.spring.initializr.generator.version.VersionReference;
/**
* A {@link MavenBuild} writer.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
*/
public class MavenBuildWriter {
public void writeTo(IndentingWriter writer, MavenBuild build) throws IOException {
writeProject(writer, () -> {
writeParent(writer, build);
writeProjectCoordinates(writer, build);
writePackaging(writer, build);
writeProjectName(writer, build);
writeProperties(writer, build);
writeDependencies(writer, build);
writeDependencyManagement(writer, build);
writeBuild(writer, build);
writeRepositories(writer, build);
});
}
private void writeProject(IndentingWriter writer, Runnable whenWritten) {
writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
writer.println(
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
writer.indented(() -> {
writer.println(
"xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">");
writeSingleElement(writer, "modelVersion", "4.0.0");
whenWritten.run();
});
writer.println();
writer.println("</project>");
}
private void writeParent(IndentingWriter writer, MavenBuild build) {
MavenParent parent = build.getParent();
if (parent == null) {
return;
}
writer.println("<parent>");
writer.indented(() -> {
writeSingleElement(writer, "groupId", parent.getGroupId());
writeSingleElement(writer, "artifactId", parent.getArtifactId());
writeSingleElement(writer, "version", parent.getVersion());
writer.println("<relativePath/> <!-- lookup parent from repository -->");
});
writer.println("</parent>");
}
private void writeProjectCoordinates(IndentingWriter writer, MavenBuild build) {
writeSingleElement(writer, "groupId", build.getGroup());
writeSingleElement(writer, "artifactId", build.getArtifact());
writeSingleElement(writer, "version", build.getVersion());
}
private void writePackaging(IndentingWriter writer, MavenBuild build) {
String packaging = build.getPackaging();
if (!"jar".equals(packaging)) {
writeSingleElement(writer, "packaging", packaging);
}
}
private void writeProjectName(IndentingWriter writer, MavenBuild build) {
writeSingleElement(writer, "name", build.getName());
writeSingleElement(writer, "description", build.getDescription());
}
private void writeProperties(IndentingWriter writer, MavenBuild build) {
if (build.getProperties().isEmpty() && build.getVersionProperties().isEmpty()) {
return;
}
writer.println();
writeElement(writer, "properties", () -> {
build.getProperties()
.forEach((key, value) -> writeSingleElement(writer, key, value));
build.getVersionProperties().forEach((key,
value) -> writeSingleElement(writer, key.toStandardFormat(), value));
});
}
private void writeDependencies(IndentingWriter writer, MavenBuild build) {
if (build.dependencies().isEmpty()) {
return;
}
DependencyContainer dependencies = build.dependencies();
writer.println();
writeElement(writer, "dependencies", () -> {
Collection<Dependency> compiledDependencies = writeDependencies(writer,
dependencies, DependencyScope.COMPILE);
if (!compiledDependencies.isEmpty()) {
writer.println();
}
writeDependencies(writer, dependencies, DependencyScope.RUNTIME);
writeDependencies(writer, dependencies, DependencyScope.COMPILE_ONLY);
writeDependencies(writer, dependencies, DependencyScope.ANNOTATION_PROCESSOR);
writeDependencies(writer, dependencies, DependencyScope.PROVIDED_RUNTIME);
writeDependencies(writer, dependencies, DependencyScope.TEST_COMPILE,
DependencyScope.TEST_RUNTIME);
});
}
private Collection<Dependency> writeDependencies(IndentingWriter writer,
DependencyContainer dependencies, DependencyScope... types) {
Collection<Dependency> candidates = filterDependencies(dependencies, types);
writeCollection(writer, candidates, this::writeDependency);
return candidates;
}
private void writeDependency(IndentingWriter writer, Dependency dependency) {
writeElement(writer, "dependency", () -> {
writeSingleElement(writer, "groupId", dependency.getGroupId());
writeSingleElement(writer, "artifactId", dependency.getArtifactId());
writeSingleElement(writer, "version",
determineVersion(dependency.getVersion()));
writeSingleElement(writer, "scope", scopeForType(dependency.getScope()));
if (isOptional(dependency.getScope())) {
writeSingleElement(writer, "optional", Boolean.toString(true));
}
writeSingleElement(writer, "type", dependency.getType());
});
}
private static Collection<Dependency> filterDependencies(
DependencyContainer dependencies, DependencyScope... scopes) {
List<DependencyScope> candidates = Arrays.asList(scopes);
return dependencies.items().filter((dep) -> candidates.contains(dep.getScope()))
.sorted(DependencyComparator.INSTANCE).collect(Collectors.toList());
}
private String scopeForType(DependencyScope type) {
switch (type) {
case ANNOTATION_PROCESSOR:
return null;
case COMPILE:
return null;
case COMPILE_ONLY:
return null;
case PROVIDED_RUNTIME:
return "provided";
case RUNTIME:
return "runtime";
case TEST_COMPILE:
return "test";
case TEST_RUNTIME:
return "test";
default:
throw new IllegalStateException(
"Unrecognized dependency type '" + type + "'");
}
}
private boolean isOptional(DependencyScope type) {
return (type == DependencyScope.ANNOTATION_PROCESSOR
|| type == DependencyScope.COMPILE_ONLY);
}
private void writeDependencyManagement(IndentingWriter writer, MavenBuild build) {
if (build.boms().isEmpty()) {
return;
}
List<BillOfMaterials> boms = build.boms().items()
.sorted(Comparator.comparing(BillOfMaterials::getOrder))
.collect(Collectors.toList());
writer.println();
writeElement(writer, "dependencyManagement", () -> writeElement(writer,
"dependencies", () -> writeCollection(writer, boms, this::writeBom)));
}
private void writeBom(IndentingWriter writer, BillOfMaterials bom) {
writeElement(writer, "dependency", () -> {
writeSingleElement(writer, "groupId", bom.getGroupId());
writeSingleElement(writer, "artifactId", bom.getArtifactId());
writeSingleElement(writer, "version", determineVersion(bom.getVersion()));
writeSingleElement(writer, "type", "pom");
writeSingleElement(writer, "scope", "import");
});
}
private String determineVersion(VersionReference versionReference) {
if (versionReference == null) {
return null;
}
return (versionReference.isProperty())
? "${" + versionReference.getProperty().toStandardFormat() + "}"
: versionReference.getValue();
}
private void writeBuild(IndentingWriter writer, MavenBuild build) {
if (build.getSourceDirectory() == null && build.getTestSourceDirectory() == null
&& build.getPlugins().isEmpty()) {
return;
}
writer.println();
writeElement(writer, "build", () -> {
writeSingleElement(writer, "sourceDirectory", build.getSourceDirectory());
writeSingleElement(writer, "testSourceDirectory",
build.getTestSourceDirectory());
writePlugins(writer, build);
});
}
private void writePlugins(IndentingWriter writer, MavenBuild build) {
if (build.getPlugins().isEmpty()) {
return;
}
writeElement(writer, "plugins",
() -> writeCollection(writer, build.getPlugins(), this::writePlugin));
}
private void writePlugin(IndentingWriter writer, MavenPlugin plugin) {
writeElement(writer, "plugin", () -> {
writeSingleElement(writer, "groupId", plugin.getGroupId());
writeSingleElement(writer, "artifactId", plugin.getArtifactId());
writeSingleElement(writer, "version", plugin.getVersion());
writePluginConfiguration(writer, plugin.getConfiguration());
if (!plugin.getExecutions().isEmpty()) {
writeElement(writer, "executions", () -> writeCollection(writer,
plugin.getExecutions(), this::writePluginExecution));
}
if (!plugin.getDependencies().isEmpty()) {
writeElement(writer, "dependencies", () -> writeCollection(writer,
plugin.getDependencies(), this::writePluginDependency));
}
});
}
private void writePluginConfiguration(IndentingWriter writer,
Configuration configuration) {
if (configuration == null || configuration.getSettings().isEmpty()) {
return;
}
writeElement(writer, "configuration", () -> writeCollection(writer,
configuration.getSettings(), this::writeSetting));
}
@SuppressWarnings("unchecked")
private void writeSetting(IndentingWriter writer, Setting setting) {
if (setting.getValue() instanceof String) {
writeSingleElement(writer, setting.getName(), (String) setting.getValue());
}
else if (setting.getValue() instanceof List) {
writeElement(writer, setting.getName(), () -> writeCollection(writer,
(List<Setting>) setting.getValue(), this::writeSetting));
}
}
private void writePluginExecution(IndentingWriter writer, Execution execution) {
writeElement(writer, "execution", () -> {
writeSingleElement(writer, "id", execution.getId());
writeSingleElement(writer, "phase", execution.getPhase());
List<String> goals = execution.getGoals();
if (!goals.isEmpty()) {
writeElement(writer, "goals", () -> goals
.forEach((goal) -> writeSingleElement(writer, "goal", goal)));
}
writePluginConfiguration(writer, execution.getConfiguration());
});
}
private void writePluginDependency(IndentingWriter writer,
MavenPlugin.Dependency dependency) {
writeElement(writer, "dependency", () -> {
writeSingleElement(writer, "groupId", dependency.getGroupId());
writeSingleElement(writer, "artifactId", dependency.getArtifactId());
writeSingleElement(writer, "version", dependency.getVersion());
});
}
private void writeRepositories(IndentingWriter writer, MavenBuild build) {
List<MavenRepository> repositories = filterRepositories(
build.repositories().items());
List<MavenRepository> pluginRepositories = filterRepositories(
build.pluginRepositories().items());
if (repositories.isEmpty() && pluginRepositories.isEmpty()) {
return;
}
writer.println();
if (!repositories.isEmpty()) {
writeRepositories(writer, "repositories", "repository", repositories);
}
if (!pluginRepositories.isEmpty()) {
writeRepositories(writer, "pluginRepositories", "pluginRepository",
pluginRepositories);
}
}
private List<MavenRepository> filterRepositories(
Stream<MavenRepository> repositories) {
return repositories
.filter((repository) -> !MavenRepository.MAVEN_CENTRAL.equals(repository))
.collect(Collectors.toList());
}
private void writeRepositories(IndentingWriter writer, String containerName,
String childName, List<MavenRepository> repositories) {
writeElement(writer, containerName, () -> repositories
.forEach((repository) -> writeElement(writer, childName, () -> {
writeSingleElement(writer, "id", repository.getId());
writeSingleElement(writer, "name", repository.getName());
writeSingleElement(writer, "url", repository.getUrl());
if (repository.isSnapshotsEnabled()) {
writeElement(writer, "snapshots", () -> writeSingleElement(writer,
"enabled", Boolean.toString(true)));
}
})));
}
private void writeSingleElement(IndentingWriter writer, String name, String text) {
if (text != null) {
writer.print(String.format("<%s>", name));
writer.print(text);
writer.println(String.format("</%s>", name));
}
}
private void writeElement(IndentingWriter writer, String name, Runnable withContent) {
writer.println(String.format("<%s>", name));
writer.indented(withContent);
writer.println(String.format("</%s>", name));
}
private <T> void writeCollection(IndentingWriter writer, Collection<T> collection,
BiConsumer<IndentingWriter, T> itemWriter) {
if (!collection.isEmpty()) {
collection.forEach((item) -> itemWriter.accept(writer, item));
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.buildsystem.maven;
/**
* The {@code <parent>} in a Maven pom.
*
* @author Andy Wilkinson
*/
public class MavenParent {
private final String groupId;
private final String artifactId;
private final String version;
MavenParent(String groupId, String artifactId, String version) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
}
public String getGroupId() {
return this.groupId;
}
public String getArtifactId() {
return this.artifactId;
}
public String getVersion() {
return this.version;
}
}

View File

@ -0,0 +1,274 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.buildsystem.maven;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
/**
* A plugin in a {@link MavenBuild}.
*
* @author Andy Wilkinson
*/
public class MavenPlugin {
private final String groupId;
private final String artifactId;
private final String version;
private final List<Execution> executions = new ArrayList<>();
private final List<Dependency> dependencies = new ArrayList<>();
private ConfigurationCustomization configurationCustomization = null;
public MavenPlugin(String groupId, String artifactId) {
this(groupId, artifactId, null);
}
public MavenPlugin(String groupId, String artifactId, String version) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
}
public String getGroupId() {
return this.groupId;
}
public String getArtifactId() {
return this.artifactId;
}
public String getVersion() {
return this.version;
}
public void configuration(Consumer<ConfigurationCustomization> consumer) {
if (this.configurationCustomization == null) {
this.configurationCustomization = new ConfigurationCustomization();
}
consumer.accept(this.configurationCustomization);
}
public void execution(String id, Consumer<ExecutionBuilder> customizer) {
ExecutionBuilder builder = new ExecutionBuilder(id);
customizer.accept(builder);
this.executions.add(builder.build());
}
public List<Execution> getExecutions() {
return this.executions;
}
public void dependency(String groupId, String artifactId, String version) {
this.dependencies.add(new Dependency(groupId, artifactId, version));
}
public List<Dependency> getDependencies() {
return Collections.unmodifiableList(this.dependencies);
}
public Configuration getConfiguration() {
return (this.configurationCustomization == null) ? null
: this.configurationCustomization.build();
}
/**
* Builder for creation an {@link Execution}.
*/
public static class ExecutionBuilder {
private final String id;
private String phase;
private List<String> goals = new ArrayList<>();
private ConfigurationCustomization configurationCustomization = null;
public ExecutionBuilder(String id) {
this.id = id;
}
Execution build() {
return new Execution(this.id, this.phase, this.goals,
(this.configurationCustomization == null) ? null
: this.configurationCustomization.build());
}
public ExecutionBuilder phase(String phase) {
this.phase = phase;
return this;
}
public ExecutionBuilder goal(String goal) {
this.goals.add(goal);
return this;
}
public void configuration(Consumer<ConfigurationCustomization> consumer) {
if (this.configurationCustomization == null) {
this.configurationCustomization = new ConfigurationCustomization();
}
consumer.accept(this.configurationCustomization);
}
}
/**
* Customization of a {@link Configuration}.
*/
public static class ConfigurationCustomization {
private final List<Setting> settings = new ArrayList<>();
public ConfigurationCustomization add(String key, String value) {
this.settings.add(new Setting(key, value));
return this;
}
public ConfigurationCustomization add(String key,
Consumer<ConfigurationCustomization> consumer) {
ConfigurationCustomization nestedConfiguration = new ConfigurationCustomization();
consumer.accept(nestedConfiguration);
this.settings.add(new Setting(key, nestedConfiguration.settings));
return this;
}
Configuration build() {
return new Configuration(this.settings);
}
}
/**
* A {@code <configuration>} on an {@link Execution} or {@link MavenPlugin}.
*/
public static final class Configuration {
private final List<Setting> settings;
private Configuration(List<Setting> settings) {
this.settings = settings;
}
public List<Setting> getSettings() {
return Collections.unmodifiableList(this.settings);
}
}
/**
* A setting in a {@link Configuration}.
*/
public static final class Setting {
private final String name;
private final Object value;
private Setting(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public Object getValue() {
return this.value;
}
}
/**
* An {@code <execution>} of a {@link MavenPlugin}.
*/
public static final class Execution {
private final String id;
private final String phase;
private final List<String> goals;
private final Configuration configuration;
private Execution(String id, String phase, List<String> goals,
Configuration configuration) {
this.id = id;
this.phase = phase;
this.goals = goals;
this.configuration = configuration;
}
public String getId() {
return this.id;
}
public String getPhase() {
return this.phase;
}
public List<String> getGoals() {
return this.goals;
}
public Configuration getConfiguration() {
return this.configuration;
}
}
/**
* A {@code <dependency>} of a {@link MavenPlugin}.
*/
public static final class Dependency {
private final String groupId;
private final String artifactId;
private final String version;
private Dependency(String groupId, String artifactId, String version) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
}
public String getGroupId() {
return this.groupId;
}
public String getArtifactId() {
return this.artifactId;
}
public String getVersion() {
return this.version;
}
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2012-2019 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.
*/
/**
* Maven build system. Provides a {@link io.spring.initializr.generator.buildsystem.Build
* Build} abstraction and a writer for {@code pom.xml}.
*/
package io.spring.initializr.generator.buildsystem.maven;

View File

@ -1,5 +1,6 @@
io.spring.initializr.generator.buildsystem.BuildSystemFactory=\
io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystemFactory
io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystemFactory,\
io.spring.initializr.generator.buildsystem.maven.MavenBuildSystemFactory
io.spring.initializr.generator.language.LanguageFactory=\
io.spring.initializr.generator.language.groovy.GroovyLanguageFactory,\

View File

@ -19,6 +19,7 @@ package io.spring.initializr.generator.buildsystem;
import java.nio.file.Path;
import io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystem;
import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem;
import io.spring.initializr.generator.language.java.JavaLanguage;
import io.spring.initializr.generator.language.kotlin.KotlinLanguage;
import org.junit.jupiter.api.Test;
@ -42,6 +43,14 @@ class BuildSystemTests {
assertThat(gradle.toString()).isEqualTo("gradle");
}
@Test
void mavenBuildSystem() {
BuildSystem maven = BuildSystem.forId("maven");
assertThat(maven).isInstanceOf(MavenBuildSystem.class);
assertThat(maven.id()).isEqualTo("maven");
assertThat(maven.toString()).isEqualTo("maven");
}
@Test
void defaultMainDirectory(@TempDir Path directory) {
Path mainDirectory = BuildSystem.forId("gradle").getMainDirectory(directory,

View File

@ -0,0 +1,534 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.buildsystem.maven;
import java.io.StringWriter;
import java.util.function.Consumer;
import io.spring.initializr.generator.buildsystem.DependencyScope;
import io.spring.initializr.generator.io.IndentingWriter;
import io.spring.initializr.generator.test.assertj.NodeAssert;
import io.spring.initializr.generator.version.VersionProperty;
import io.spring.initializr.generator.version.VersionReference;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MavenBuildWriter}.
*
* @author Stephane Nicoll
*/
class MavenBuildWriterTests {
@Test
void basicPom() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/modelVersion").isEqualTo("4.0.0");
assertThat(pom).textAtPath("/project/groupId").isEqualTo("com.example.demo");
assertThat(pom).textAtPath("/project/artifactId").isEqualTo("demo");
assertThat(pom).textAtPath("/project/version").isEqualTo("0.0.1-SNAPSHOT");
});
}
@Test
void pomWithNameAndDescription() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.setName("demo project");
build.setDescription("A demo project");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/modelVersion").isEqualTo("4.0.0");
assertThat(pom).textAtPath("/project/groupId").isEqualTo("com.example.demo");
assertThat(pom).textAtPath("/project/artifactId").isEqualTo("demo");
assertThat(pom).textAtPath("/project/version").isEqualTo("0.0.1-SNAPSHOT");
assertThat(pom).textAtPath("/project/name").isEqualTo("demo project");
assertThat(pom).textAtPath("/project/description")
.isEqualTo("A demo project");
});
}
@Test
void pomWithParent() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.parent("org.springframework.boot", "spring-boot-starter-parent",
"2.1.0.RELEASE");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/parent/groupId")
.isEqualTo("org.springframework.boot");
assertThat(pom).textAtPath("/project/parent/artifactId")
.isEqualTo("spring-boot-starter-parent");
assertThat(pom).textAtPath("/project/parent/version")
.isEqualTo("2.1.0.RELEASE");
});
}
@Test
void pomWithPackaging() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.setPackaging("war");
generatePom(build, (pom) -> assertThat(pom).textAtPath("/project/packaging")
.isEqualTo("war"));
}
@Test
void pomWithProperties() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.setProperty("java.version", "1.8");
build.setProperty("alpha", "a");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/properties/java.version")
.isEqualTo("1.8");
assertThat(pom).textAtPath("/project/properties/alpha").isEqualTo("a");
});
}
@Test
void pomWithVersionProperties() throws Exception {
MavenBuild build = new MavenBuild();
build.addVersionProperty(VersionProperty.of("version.property"), "1.2.3");
build.addInternalVersionProperty("internal.property", "4.5.6");
build.addExternalVersionProperty("external.property", "7.8.9");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/properties/version.property")
.isEqualTo("1.2.3");
assertThat(pom).textAtPath("/project/properties/internal.property")
.isEqualTo("4.5.6");
assertThat(pom).textAtPath("/project/properties/external.property")
.isEqualTo("7.8.9");
});
}
@Test
void pomWithAnnotationProcessorDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("annotation-processor", "org.springframework.boot",
"spring-boot-configuration-processor",
DependencyScope.ANNOTATION_PROCESSOR);
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("groupId")
.isEqualTo("org.springframework.boot");
assertThat(dependency).textAtPath("artifactId")
.isEqualTo("spring-boot-configuration-processor");
assertThat(dependency).textAtPath("version").isNullOrEmpty();
assertThat(dependency).textAtPath("scope").isNullOrEmpty();
assertThat(dependency).textAtPath("optional").isEqualTo("true");
});
}
@Test
void pomWithCompileOnlyDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("foo-bar", "org.springframework.boot",
"spring-boot-foo-bar", DependencyScope.COMPILE_ONLY);
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("groupId")
.isEqualTo("org.springframework.boot");
assertThat(dependency).textAtPath("artifactId")
.isEqualTo("spring-boot-foo-bar");
assertThat(dependency).textAtPath("version").isNullOrEmpty();
assertThat(dependency).textAtPath("scope").isNullOrEmpty();
assertThat(dependency).textAtPath("optional").isEqualTo("true");
});
}
@Test
void pomWithCompileDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("root", "org.springframework.boot",
"spring-boot-starter", DependencyScope.COMPILE);
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("groupId")
.isEqualTo("org.springframework.boot");
assertThat(dependency).textAtPath("artifactId")
.isEqualTo("spring-boot-starter");
assertThat(dependency).textAtPath("version").isNullOrEmpty();
assertThat(dependency).textAtPath("scope").isNullOrEmpty();
assertThat(dependency).textAtPath("optional").isNullOrEmpty();
});
}
@Test
void pomWithRuntimeDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("hikari", "com.zaxxer", "HikariCP",
DependencyScope.RUNTIME);
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("groupId").isEqualTo("com.zaxxer");
assertThat(dependency).textAtPath("artifactId").isEqualTo("HikariCP");
assertThat(dependency).textAtPath("version").isNullOrEmpty();
assertThat(dependency).textAtPath("scope").isEqualTo("runtime");
assertThat(dependency).textAtPath("optional").isNullOrEmpty();
});
}
@Test
void pomWithProvidedRuntimeDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("tomcat", "org.springframework.boot",
"spring-boot-starter-tomcat", DependencyScope.PROVIDED_RUNTIME);
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("groupId")
.isEqualTo("org.springframework.boot");
assertThat(dependency).textAtPath("artifactId")
.isEqualTo("spring-boot-starter-tomcat");
assertThat(dependency).textAtPath("version").isNullOrEmpty();
assertThat(dependency).textAtPath("scope").isEqualTo("provided");
assertThat(dependency).textAtPath("optional").isNullOrEmpty();
});
}
@Test
void pomWithTestCompileDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("test", "org.springframework.boot",
"spring-boot-starter-test", DependencyScope.TEST_COMPILE);
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("groupId")
.isEqualTo("org.springframework.boot");
assertThat(dependency).textAtPath("artifactId")
.isEqualTo("spring-boot-starter-test");
assertThat(dependency).textAtPath("version").isNullOrEmpty();
assertThat(dependency).textAtPath("scope").isEqualTo("test");
assertThat(dependency).textAtPath("optional").isNullOrEmpty();
});
}
@Test
void pomWithTestRuntimeDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("embed-mongo", "de.flapdoodle.embed",
"de.flapdoodle.embed.mongo", DependencyScope.TEST_RUNTIME);
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("groupId").isEqualTo("de.flapdoodle.embed");
assertThat(dependency).textAtPath("artifactId")
.isEqualTo("de.flapdoodle.embed.mongo");
assertThat(dependency).textAtPath("version").isNullOrEmpty();
assertThat(dependency).textAtPath("scope").isEqualTo("test");
assertThat(dependency).textAtPath("optional").isNullOrEmpty();
});
}
@Test
void pomWithNonNullArtifactTypeDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.dependencies().add("root", "org.springframework.boot",
"spring-boot-starter", null, DependencyScope.COMPILE, "tar.gz");
generatePom(build, (pom) -> {
NodeAssert dependency = pom.nodeAtPath("/project/dependencies/dependency");
assertThat(dependency).textAtPath("type").isEqualTo("tar.gz");
});
}
@Test
void pomWithBom() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.boms().add("test", "com.example", "my-project-dependencies",
VersionReference.ofValue("1.0.0.RELEASE"));
generatePom(build, (pom) -> {
NodeAssert dependency = pom
.nodeAtPath("/project/dependencyManagement/dependencies/dependency");
assertBom(dependency, "com.example", "my-project-dependencies",
"1.0.0.RELEASE");
});
}
@Test
void pomWithOrderedBoms() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.boms().add("bom1", "com.example", "my-project-dependencies",
VersionReference.ofValue("1.0.0.RELEASE"), 5);
build.boms().add("bom2", "com.example", "root-dependencies",
VersionReference.ofProperty("root.version"), 2);
generatePom(build, (pom) -> {
NodeAssert dependencies = pom
.nodeAtPath("/project/dependencyManagement/dependencies");
NodeAssert firstBom = assertThat(dependencies).nodeAtPath("dependency[1]");
assertBom(firstBom, "com.example", "root-dependencies", "${root.version}");
NodeAssert secondBom = assertThat(dependencies).nodeAtPath("dependency[2]");
assertBom(secondBom, "com.example", "my-project-dependencies",
"1.0.0.RELEASE");
});
}
private void assertBom(NodeAssert firstBom, String groupId, String artifactId,
String version) {
assertThat(firstBom).textAtPath("groupId").isEqualTo(groupId);
assertThat(firstBom).textAtPath("artifactId").isEqualTo(artifactId);
assertThat(firstBom).textAtPath("version").isEqualTo(version);
assertThat(firstBom).textAtPath("type").isEqualTo("pom");
assertThat(firstBom).textAtPath("scope").isEqualTo("import");
}
@Test
void pomWithPlugin() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.plugin("org.springframework.boot", "spring-boot-maven-plugin");
generatePom(build, (pom) -> {
NodeAssert plugin = pom.nodeAtPath("/project/build/plugins/plugin");
assertThat(plugin).textAtPath("groupId")
.isEqualTo("org.springframework.boot");
assertThat(plugin).textAtPath("artifactId")
.isEqualTo("spring-boot-maven-plugin");
assertThat(plugin).textAtPath("version").isNullOrEmpty();
});
}
@Test
void pomWithPluginWithConfiguration() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
MavenPlugin kotlin = build.plugin("org.jetbrains.kotlin", "kotlin-maven-plugin");
kotlin.configuration((configuration) -> {
configuration.add("args", (args) -> args.add("arg", "-Xjsr305=strict"));
configuration.add("compilerPlugins",
(compilerPlugins) -> compilerPlugins.add("plugin", "spring"));
});
generatePom(build, (pom) -> {
NodeAssert plugin = pom.nodeAtPath("/project/build/plugins/plugin");
assertThat(plugin).textAtPath("groupId").isEqualTo("org.jetbrains.kotlin");
assertThat(plugin).textAtPath("artifactId").isEqualTo("kotlin-maven-plugin");
assertThat(plugin).textAtPath("version").isNullOrEmpty();
NodeAssert configuration = plugin.nodeAtPath("configuration");
assertThat(configuration).textAtPath("args/arg").isEqualTo("-Xjsr305=strict");
assertThat(configuration).textAtPath("compilerPlugins/plugin")
.isEqualTo("spring");
});
}
@Test
void pomWithPluginWithExecution() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
MavenPlugin asciidoctor = build.plugin("org.asciidoctor",
"asciidoctor-maven-plugin", "1.5.3");
asciidoctor.execution("generateProject-docs", (execution) -> {
execution.goal("process-asciidoc");
execution.phase("generateProject-resources");
execution.configuration((configuration) -> {
configuration.add("doctype", "book");
configuration.add("backend", "html");
});
});
generatePom(build, (pom) -> {
NodeAssert plugin = pom.nodeAtPath("/project/build/plugins/plugin");
assertThat(plugin).textAtPath("groupId").isEqualTo("org.asciidoctor");
assertThat(plugin).textAtPath("artifactId")
.isEqualTo("asciidoctor-maven-plugin");
assertThat(plugin).textAtPath("version").isEqualTo("1.5.3");
NodeAssert execution = plugin.nodeAtPath("executions/execution");
assertThat(execution).textAtPath("id").isEqualTo("generateProject-docs");
assertThat(execution).textAtPath("goals/goal").isEqualTo("process-asciidoc");
assertThat(execution).textAtPath("phase")
.isEqualTo("generateProject-resources");
NodeAssert configuration = execution.nodeAtPath("configuration");
assertThat(configuration).textAtPath("doctype").isEqualTo("book");
assertThat(configuration).textAtPath("backend").isEqualTo("html");
});
}
@Test
void pomWithPluginWithDependency() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
MavenPlugin kotlin = build.plugin("org.jetbrains.kotlin", "kotlin-maven-plugin");
kotlin.dependency("org.jetbrains.kotlin", "kotlin-maven-allopen",
"${kotlin.version}");
generatePom(build, (pom) -> {
NodeAssert plugin = pom.nodeAtPath("/project/build/plugins/plugin");
assertThat(plugin).textAtPath("groupId").isEqualTo("org.jetbrains.kotlin");
assertThat(plugin).textAtPath("artifactId").isEqualTo("kotlin-maven-plugin");
NodeAssert dependency = plugin.nodeAtPath("dependencies/dependency");
assertThat(dependency).textAtPath("groupId")
.isEqualTo("org.jetbrains.kotlin");
assertThat(dependency).textAtPath("artifactId")
.isEqualTo("kotlin-maven-allopen");
assertThat(dependency).textAtPath("version").isEqualTo("${kotlin.version}");
});
}
@Test
void pomWithMavenCentral() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.repositories().add("maven-central");
generatePom(build, (pom) -> {
assertThat(pom).nodeAtPath("/project/repositories").isNull();
assertThat(pom).nodeAtPath("/project/pluginRepositories").isNull();
});
}
@Test
void pomWithRepository() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.repositories().add("spring-milestones", "Spring Milestones",
"https://repo.spring.io/milestone");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/repositories/repository/id")
.isEqualTo("spring-milestones");
assertThat(pom).textAtPath("/project/repositories/repository/name")
.isEqualTo("Spring Milestones");
assertThat(pom).textAtPath("/project/repositories/repository/url")
.isEqualTo("https://repo.spring.io/milestone");
assertThat(pom).nodeAtPath("/project/repositories/repository/snapshots")
.isNull();
assertThat(pom).nodeAtPath("/project/pluginRepositories").isNull();
});
}
@Test
void pomWithPluginRepository() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.pluginRepositories().add("spring-milestones", "Spring Milestones",
"https://repo.spring.io/milestone");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/pluginRepositories/pluginRepository/id")
.isEqualTo("spring-milestones");
assertThat(pom)
.textAtPath("/project/pluginRepositories/pluginRepository/name")
.isEqualTo("Spring Milestones");
assertThat(pom).textAtPath("/project/pluginRepositories/pluginRepository/url")
.isEqualTo("https://repo.spring.io/milestone");
assertThat(pom).nodeAtPath("/project/repositories/repository/snapshots")
.isNull();
assertThat(pom).nodeAtPath("/project/repositories").isNull();
});
}
@Test
void pomWithSnapshotRepository() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.repositories().add("spring-snapshots", "Spring Snapshots",
"https://repo.spring.io/snapshot", true);
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/repositories/repository/id")
.isEqualTo("spring-snapshots");
assertThat(pom).textAtPath("/project/repositories/repository/name")
.isEqualTo("Spring Snapshots");
assertThat(pom).textAtPath("/project/repositories/repository/url")
.isEqualTo("https://repo.spring.io/snapshot");
assertThat(pom)
.textAtPath("/project/repositories/repository/snapshots/enabled")
.isEqualTo("true");
assertThat(pom).nodeAtPath("/project/pluginRepositories").isNull();
});
}
@Test
void pomWithSnapshotPluginRepository() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.pluginRepositories().add("spring-snapshots", "Spring Snapshots",
"https://repo.spring.io/snapshot", true);
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/pluginRepositories/pluginRepository/id")
.isEqualTo("spring-snapshots");
assertThat(pom)
.textAtPath("/project/pluginRepositories/pluginRepository/name")
.isEqualTo("Spring Snapshots");
assertThat(pom).textAtPath("/project/pluginRepositories/pluginRepository/url")
.isEqualTo("https://repo.spring.io/snapshot");
assertThat(pom).textAtPath(
"/project/pluginRepositories/pluginRepository/snapshots/enabled")
.isEqualTo("true");
assertThat(pom).nodeAtPath("/project/repositories").isNull();
});
}
@Test
void pomWithCustomSourceDirectories() throws Exception {
MavenBuild build = new MavenBuild();
build.setGroup("com.example.demo");
build.setArtifact("demo");
build.setSourceDirectory("${project.basedir}/src/main/kotlin");
build.setTestSourceDirectory("${project.basedir}/src/test/kotlin");
generatePom(build, (pom) -> {
assertThat(pom).textAtPath("/project/build/sourceDirectory")
.isEqualTo("${project.basedir}/src/main/kotlin");
assertThat(pom).textAtPath("/project/build/testSourceDirectory")
.isEqualTo("${project.basedir}/src/test/kotlin");
});
}
@Test
void pomWithCustomVersion() throws Exception {
MavenBuild build = new MavenBuild();
build.setVersion("1.2.4.RELEASE");
generatePom(build, (pom) -> assertThat(pom).textAtPath("/project/version")
.isEqualTo("1.2.4.RELEASE"));
}
private void generatePom(MavenBuild mavenBuild, Consumer<NodeAssert> consumer)
throws Exception {
MavenBuildWriter writer = new MavenBuildWriter();
StringWriter out = new StringWriter();
writer.writeTo(new IndentingWriter(out), mavenBuild);
consumer.accept(new NodeAssert(out.toString()));
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright 2012-2018 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 io.spring.initializr.generator.test.assertj;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AssertProvider;
import org.assertj.core.api.ListAssert;
import org.assertj.core.api.StringAssert;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* AssertJ {@link AssertProvider} for {@link Node} assertions.
*
* @author Andy Wilkinson
*/
public class NodeAssert extends AbstractAssert<NodeAssert, Node>
implements AssertProvider<NodeAssert> {
private static final DocumentBuilderFactory FACTORY = DocumentBuilderFactory
.newInstance();
private final XPathFactory xpathFactory = XPathFactory.newInstance();
private final XPath xpath = this.xpathFactory.newXPath();
public NodeAssert(Path xmlFile) {
this(read(xmlFile));
}
public NodeAssert(String xmlContent) {
this(read(xmlContent));
}
public NodeAssert(Node actual) {
super(actual, NodeAssert.class);
}
private static Document read(Path xmlFile) {
try {
return FACTORY.newDocumentBuilder().parse(xmlFile.toFile());
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static Document read(String xmlContent) {
try {
return FACTORY.newDocumentBuilder().parse(new ByteArrayInputStream(
xmlContent.getBytes(StandardCharsets.UTF_8)));
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public NodeAssert nodeAtPath(String xpath) {
try {
return new NodeAssert(
(Node) this.xpath.evaluate(xpath, this.actual, XPathConstants.NODE));
}
catch (XPathExpressionException ex) {
throw new RuntimeException(ex);
}
}
public ListAssert<Node> nodesAtPath(String xpath) {
try {
NodeList nodeList = (NodeList) this.xpath.evaluate(xpath, this.actual,
XPathConstants.NODESET);
return new ListAssert<>(toList(nodeList));
}
catch (XPathExpressionException ex) {
throw new RuntimeException(ex);
}
}
public StringAssert textAtPath(String xpath) {
try {
return new StringAssert((String) this.xpath.evaluate(xpath + "/text()",
this.actual, XPathConstants.STRING));
}
catch (XPathExpressionException ex) {
throw new RuntimeException(ex);
}
}
@Override
public NodeAssert assertThat() {
return this;
}
private static List<Node> toList(NodeList nodeList) {
List<Node> nodes = new ArrayList<>();
for (int i = 0; i < nodeList.getLength(); i++) {
nodes.add(nodeList.item(i));
}
return nodes;
}
}