Merge pull request #1029 from jaferkhan

* pr/1029:
  Polish "Add support for writing license and developer in Maven pom"
  Add support for writing license and developer in Maven pom

Closes gh-1029
This commit is contained in:
Stephane Nicoll 2019-12-27 11:59:43 +01:00
commit aad11c9aa3
7 changed files with 722 additions and 0 deletions

View File

@ -16,6 +16,11 @@
package io.spring.initializr.generator.buildsystem.maven;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import io.spring.initializr.generator.buildsystem.BuildSettings;
import io.spring.initializr.generator.packaging.Packaging;
@ -23,6 +28,7 @@ import io.spring.initializr.generator.packaging.Packaging;
* Maven {@link BuildSettings}.
*
* @author Stephane Nicoll
* @author Jafer Khan Shamshad
*/
public class MavenBuildSettings extends BuildSettings {
@ -34,6 +40,10 @@ public class MavenBuildSettings extends BuildSettings {
private final String description;
private final List<MavenLicense> licenses;
private final List<MavenDeveloper> developers;
private final String sourceDirectory;
private final String testSourceDirectory;
@ -44,6 +54,8 @@ public class MavenBuildSettings extends BuildSettings {
this.packaging = builder.packaging;
this.name = builder.name;
this.description = builder.description;
this.licenses = Collections.unmodifiableList(new ArrayList<>(builder.licenses));
this.developers = Collections.unmodifiableList(new ArrayList<>(builder.developers));
this.sourceDirectory = builder.sourceDirectory;
this.testSourceDirectory = builder.testSourceDirectory;
}
@ -82,6 +94,22 @@ public class MavenBuildSettings extends BuildSettings {
return this.description;
}
/**
* Return the {@linkplain MavenLicense licenses} associated with the project.
* @return the licenses of the project
*/
public List<MavenLicense> getLicenses() {
return this.licenses;
}
/**
* Return the {@linkplain MavenDeveloper developers} associated with the project.
* @return the developers of the project
*/
public List<MavenDeveloper> getDevelopers() {
return this.developers;
}
/**
* Return the location of main source code. Can use Maven properties such as
* {@code ${basedir}}.
@ -115,6 +143,10 @@ public class MavenBuildSettings extends BuildSettings {
private String description;
private List<MavenLicense> licenses = new ArrayList<>();
private List<MavenDeveloper> developers = new ArrayList<>();
private String sourceDirectory;
private String testSourceDirectory;
@ -165,6 +197,26 @@ public class MavenBuildSettings extends BuildSettings {
return self();
}
/**
* Set the licenses of the project.
* @param licenses the licenses associated with the project
* @return this for method chaining
*/
public Builder licenses(MavenLicense... licenses) {
this.licenses = (licenses != null) ? Arrays.asList(licenses) : new ArrayList<>();
return self();
}
/**
* Set the developers of the project.
* @param developers the developers associated with the project
* @return this for method chaining
*/
public Builder developers(MavenDeveloper... developers) {
this.developers = (developers != null) ? Arrays.asList(developers) : new ArrayList<>();
return self();
}
/**
* Set a human readable description of the project.
* @param description the description of the project

View File

@ -20,6 +20,8 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -46,6 +48,7 @@ import io.spring.initializr.generator.version.VersionReference;
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Olga Maciaszek-Sharma
* @author Jafer Khan Shamshad
*/
public class MavenBuildWriter {
@ -62,6 +65,8 @@ public class MavenBuildWriter {
writeProjectCoordinates(writer, settings);
writePackaging(writer, settings);
writeProjectName(writer, settings);
writeLicenses(writer, settings);
writeDevelopers(writer, settings);
writeProperties(writer, build.properties());
writeDependencies(writer, build);
writeDependencyManagement(writer, build);
@ -129,6 +134,54 @@ public class MavenBuildWriter {
});
}
private void writeLicenses(IndentingWriter writer, MavenBuildSettings settings) {
if (settings.getLicenses().isEmpty()) {
return;
}
writeElement(writer, "licenses", () -> writeCollection(writer, settings.getLicenses(), this::writeLicense));
}
private void writeLicense(IndentingWriter writer, MavenLicense license) {
writeElement(writer, "license", () -> {
writeSingleElement(writer, "name", license.getName());
writeSingleElement(writer, "url", license.getUrl());
if (license.getDistribution() != null) {
writeSingleElement(writer, "distribution",
license.getDistribution().name().toLowerCase(Locale.ENGLISH));
}
writeSingleElement(writer, "comments", license.getComments());
});
}
private void writeDevelopers(IndentingWriter writer, MavenBuildSettings settings) {
if (settings.getDevelopers().isEmpty()) {
return;
}
writeElement(writer, "developers",
() -> writeCollection(writer, settings.getDevelopers(), this::writeDeveloper));
}
private void writeDeveloper(IndentingWriter writer, MavenDeveloper developer) {
writeElement(writer, "developer", () -> {
writeSingleElement(writer, "id", developer.getId());
writeSingleElement(writer, "name", developer.getName());
writeSingleElement(writer, "email", developer.getEmail());
writeSingleElement(writer, "url", developer.getUrl());
writeSingleElement(writer, "organization", developer.getOrganization());
writeSingleElement(writer, "organizationUrl", developer.getOrganizationUrl());
List<String> roles = developer.getRoles();
if (!roles.isEmpty()) {
writeElement(writer, "roles", () -> roles.forEach((role) -> writeSingleElement(writer, "role", role)));
}
writeSingleElement(writer, "timezone", developer.getTimezone());
Map<String, String> properties = developer.getProperties();
if (!properties.isEmpty()) {
writeElement(writer, "properties",
() -> properties.forEach((key, value) -> writeSingleElement(writer, key, value)));
}
});
}
private void writeDependencies(IndentingWriter writer, MavenBuild build) {
if (build.dependencies().isEmpty()) {
return;

View File

@ -0,0 +1,254 @@
/*
* 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
*
* 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 io.spring.initializr.generator.buildsystem.maven;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A {@code <developer>} in a Maven pom.
*
* @author Jafer Khan Shamshad
*/
public class MavenDeveloper {
private final String id;
private final String name;
private final String email;
private final String url;
private final String organization;
private final String organizationUrl;
private final List<String> roles;
private final String timezone;
private final Map<String, String> properties;
MavenDeveloper(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.email = builder.email;
this.url = builder.url;
this.organization = builder.organization;
this.organizationUrl = builder.organizationUrl;
this.roles = Collections.unmodifiableList(new ArrayList<>(builder.roles));
this.timezone = builder.timezone;
this.properties = Collections.unmodifiableMap(new LinkedHashMap<>(builder.properties));
}
/**
* Return the ID of the developer.
* @return the ID
*/
public String getId() {
return this.id;
}
/**
* Return the name of the developer.
* @return the name
*/
public String getName() {
return this.name;
}
/**
* Return the email address of the developer.
* @return the email address
*/
public String getEmail() {
return this.email;
}
/**
* Return the URL of the developer.
* @return the URL
*/
public String getUrl() {
return this.url;
}
/**
* Return the organization's name of the developer.
* @return the organization
*/
public String getOrganization() {
return this.organization;
}
/**
* Return the associated organization's URL of the developer.
* @return the organization's URL
*/
public String getOrganizationUrl() {
return this.organizationUrl;
}
/**
* Return the roles of the developer.
* @return the roles
*/
public List<String> getRoles() {
return this.roles;
}
/**
* Return the timezone associated with the developer.
* @return the timezone
*/
public String getTimezone() {
return this.timezone;
}
/**
* Return other properties associated with the developer.
* @return other properties
*/
public Map<String, String> getProperties() {
return this.properties;
}
/**
* Builder for a {@link MavenDeveloper}.
*/
public static class Builder {
private String id;
private String name;
private String email;
private String url;
private String organization;
private String organizationUrl;
private final List<String> roles = new ArrayList<>();
private String timezone;
private final Map<String, String> properties = new LinkedHashMap<>();
/**
* Set the ID of the developer.
* @param id the ID of the developer or {@code null}
* @return this for method chaining
*/
public Builder id(String id) {
this.id = id;
return this;
}
/**
* Set the name of the developer.
* @param name the name of the developer or {@code null}
* @return this for method chaining
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* Set the email address of the developer.
* @param email the email address of the developer or {@code null}
* @return this for method chaining
*/
public Builder email(String email) {
this.email = email;
return this;
}
/**
* Set the URL of the developer.
* @param url the URL of the developer or {@code null}
* @return this for method chaining
*/
public Builder url(String url) {
this.url = url;
return this;
}
/**
* Set the organization's name of the developer.
* @param organization the organization of the developer or {@code null}
* @return this for method chaining
*/
public Builder organization(String organization) {
this.organization = organization;
return this;
}
/**
* Set the associated organization's URL of the developer.
* @param organizationUrl the URL of the organization or {@code null}
* @return this for method chaining
*/
public Builder organizationUrl(String organizationUrl) {
this.organizationUrl = organizationUrl;
return this;
}
/**
* Add a role of the developer.
* @param role the role of the developer
* @return this for method chaining
*/
public Builder role(String role) {
this.roles.add(role);
return this;
}
/**
* Set the timezone associated with the developer.
* @param timezone the timezone that the developer lives in or {@code null}
* @return this for method chaining
*/
public Builder timezone(String timezone) {
this.timezone = timezone;
return this;
}
/**
* Add a property associated with the developer.
* @param key the appropriate key associated with the property
* @param value the value of the property
* @return this for method chaining
*/
public Builder property(String key, String value) {
this.properties.put(key, value);
return this;
}
public MavenDeveloper build() {
return new MavenDeveloper(this);
}
}
}

View File

@ -0,0 +1,150 @@
/*
* 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
*
* 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 io.spring.initializr.generator.buildsystem.maven;
/**
* A {@code <license>} in a Maven pom.
*
* @author Jafer Khan Shamshad
* @author Stephane Nicoll
*/
public class MavenLicense {
private final String name;
private final String url;
private final Distribution distribution;
private final String comments;
MavenLicense(Builder builder) {
this.name = builder.name;
this.url = builder.url;
this.distribution = builder.distribution;
this.comments = builder.comments;
}
/**
* Return the name of the license.
* @return the name
*/
public String getName() {
return this.name;
}
/**
* Return the URL of the license.
* @return the URL
*/
public String getUrl() {
return this.url;
}
/**
* Return the distribution mechanism of the project associated with the license.
* @return the distribution mechanism
*/
public Distribution getDistribution() {
return this.distribution;
}
/**
* Return the comments associated with the license.
* @return the comments
*/
public String getComments() {
return this.comments;
}
/**
* Builder for a {@link MavenLicense}.
*/
public static class Builder {
private String name;
private String url;
private Distribution distribution;
private String comments;
/**
* Set the name of the license.
* @param name the name of the license or {@code null}
* @return this for method chaining
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* Set the URL of the license.
* @param url the URL of the license or {@code null}
* @return this for method chaining
*/
public Builder url(String url) {
this.url = url;
return this;
}
/**
* Set the distribution mechanism of the project associated with the license.
* @param distribution the distribution mechanism of the project or {@code null}
* @return this for method chaining
*/
public Builder distribution(Distribution distribution) {
this.distribution = distribution;
return this;
}
/**
* Set comments associated with the license.
* @param comments the comments for the license or {@code null}
* @return this for method chaining
*/
public Builder comments(String comments) {
this.comments = comments;
return this;
}
public MavenLicense build() {
return new MavenLicense(this);
}
}
/**
* Describes how the project may be legally distributed.
*/
public enum Distribution {
/**
* May be downloaded from a Maven repository.
*/
REPO,
/**
* Must be manually installed.
*/
MANUAL
}
}

View File

@ -24,6 +24,7 @@ import io.spring.initializr.generator.buildsystem.Dependency;
import io.spring.initializr.generator.buildsystem.Dependency.Exclusion;
import io.spring.initializr.generator.buildsystem.DependencyScope;
import io.spring.initializr.generator.buildsystem.MavenRepository;
import io.spring.initializr.generator.buildsystem.maven.MavenLicense.Distribution;
import io.spring.initializr.generator.io.IndentingWriter;
import io.spring.initializr.generator.version.VersionProperty;
import io.spring.initializr.generator.version.VersionReference;
@ -36,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Stephane Nicoll
* @author Olga Maciaszek-Sharma
* @author Jafer Khan Shamshad
*/
class MavenBuildWriterTests {
@ -84,6 +86,103 @@ class MavenBuildWriterTests {
generatePom(build, (pom) -> assertThat(pom).textAtPath("/project/packaging").isEqualTo("war"));
}
@Test
void pomWithNoLicense() {
MavenBuild build = new MavenBuild();
build.settings().coordinates("com.example.demo", "demo").build();
generatePom(build, (pom) -> assertThat(pom.nodeAtPath("/project/licenses")).isNull());
}
@Test
void pomWithBasicLicense() {
MavenBuild build = new MavenBuild();
build.settings().coordinates("com.example.demo", "demo").licenses(new MavenLicense.Builder()
.name("Apache License, Version 2.0").url("https://www.apache.org/licenses/LICENSE-2.0").build());
generatePom(build, (pom) -> {
NodeAssert license = pom.nodeAtPath("/project/licenses/license");
assertThat(license).textAtPath("name").isEqualTo("Apache License, Version 2.0");
assertThat(license).textAtPath("url").isEqualTo("https://www.apache.org/licenses/LICENSE-2.0");
assertThat(license).textAtPath("distribution").isNullOrEmpty();
assertThat(license).textAtPath("comments").isNullOrEmpty();
});
}
@Test
void pomWithFullLicense() {
MavenBuild build = new MavenBuild();
build.settings().coordinates("com.example.demo", "demo")
.licenses(new MavenLicense.Builder().name("Apache License, Version 2.0")
.url("https://www.apache.org/licenses/LICENSE-2.0").distribution(Distribution.REPO)
.comments("A business-friendly OSS license").build());
generatePom(build, (pom) -> {
NodeAssert licenses = pom.nodeAtPath("/project/licenses");
assertThat(licenses).isNotNull();
NodeAssert license = licenses.nodeAtPath("license");
assertThat(license).isNotNull();
assertThat(license).textAtPath("name").isEqualTo("Apache License, Version 2.0");
assertThat(license).textAtPath("url").isEqualTo("https://www.apache.org/licenses/LICENSE-2.0");
assertThat(license).textAtPath("distribution").isEqualTo("repo");
assertThat(license).textAtPath("comments").isEqualTo("A business-friendly OSS license");
});
}
@Test
void pomWithNoDeveloper() {
MavenBuild build = new MavenBuild();
build.settings().coordinates("com.example.demo", "demo").build();
generatePom(build, (pom) -> assertThat(pom.nodeAtPath("/project/developers")).isNull());
}
@Test
void pomWithBasicDeveloper() {
MavenBuild build = new MavenBuild();
build.settings().coordinates("com.example.demo", "demo").developers(
new MavenDeveloper.Builder().id("jsmith").name("John Smith").email("jsmith@example.com").build())
.build();
generatePom(build, (pom) -> {
NodeAssert developer = pom.nodeAtPath("/project/developers/developer");
assertThat(developer).textAtPath("id").isEqualTo("jsmith");
assertThat(developer).textAtPath("name").isEqualTo("John Smith");
assertThat(developer).textAtPath("email").isEqualTo("jsmith@example.com");
assertThat(developer).textAtPath("url").isNullOrEmpty();
assertThat(developer).textAtPath("organization").isNullOrEmpty();
assertThat(developer).textAtPath("organizationUrl").isNullOrEmpty();
assertThat(developer.nodeAtPath("roles")).isNull();
assertThat(developer).textAtPath("timezone").isNullOrEmpty();
assertThat(developer.nodeAtPath("properties")).isNull();
});
}
@Test
void pomWithFullDeveloper() {
MavenBuild build = new MavenBuild();
build.settings().coordinates("com.example.demo", "demo")
.developers(new MavenDeveloper.Builder().id("jsmith").name("John Smith").email("jsmith@example.com")
.url("https://example.com/jsmith").organization("Acme Corp")
.organizationUrl("https://example.com").timezone("Asia/Karachi").role("developer")
.role("tester").property("prop1", "test1").property("prop2", "test2").build());
generatePom(build, (pom) -> {
NodeAssert developers = pom.nodeAtPath("/project/developers");
assertThat(developers).isNotNull();
NodeAssert developer = developers.nodeAtPath("developer");
assertThat(developer).isNotNull();
assertThat(developer).textAtPath("id").isEqualTo("jsmith");
assertThat(developer).textAtPath("name").isEqualTo("John Smith");
assertThat(developer).textAtPath("email").isEqualTo("jsmith@example.com");
assertThat(developer).textAtPath("url").isEqualTo("https://example.com/jsmith");
assertThat(developer).textAtPath("organization").isEqualTo("Acme Corp");
assertThat(developer).textAtPath("organizationUrl").isEqualTo("https://example.com");
assertThat(developer).textAtPath("timezone").isEqualTo("Asia/Karachi");
NodeAssert roles = developer.nodeAtPath("roles");
roles.nodesAtPath("role").hasSize(2);
assertThat(roles).textAtPath("role[1]").isEqualTo("developer");
assertThat(roles).textAtPath("role[2]").isEqualTo("tester");
NodeAssert properties = developer.nodeAtPath("properties");
assertThat(properties).textAtPath("prop1").isEqualTo("test1");
assertThat(properties).textAtPath("prop2").isEqualTo("test2");
});
}
@Test
void pomWithProperties() {
MavenBuild build = new MavenBuild();

View File

@ -0,0 +1,63 @@
/*
* 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
*
* 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 io.spring.initializr.generator.buildsystem.maven;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/**
* Tests for {@link MavenDeveloper}.
*
* @author Jafer Khan Shamshad
*/
class MavenDeveloperTests {
@Test
void developerWithIdOnly() {
MavenDeveloper developer = new MavenDeveloper.Builder().id("jsmith").build();
assertThat(developer.getId()).isEqualTo("jsmith");
assertThat(developer.getName()).isNull();
assertThat(developer.getEmail()).isNull();
assertThat(developer.getUrl()).isNull();
assertThat(developer.getOrganization()).isNull();
assertThat(developer.getOrganizationUrl()).isNull();
assertThat(developer.getRoles()).hasSize(0);
assertThat(developer.getTimezone()).isNull();
assertThat(developer.getProperties()).hasSize(0);
}
@Test
void developerWithFullDetails() {
MavenDeveloper developer = new MavenDeveloper.Builder().id("jsmith").name("John Smith")
.email("john@example.com").url("http://www.example.com/jsmith").organization("Acme Corp")
.organizationUrl("http://www.example.com").role("developer").role("tester").timezone("Asia/Karachi")
.property("prop1", "test1").property("prop2", "test2").property("prop3", "test3").build();
assertThat(developer.getId()).isEqualTo("jsmith");
assertThat(developer.getName()).isEqualTo("John Smith");
assertThat(developer.getEmail()).isEqualTo("john@example.com");
assertThat(developer.getUrl()).isEqualTo("http://www.example.com/jsmith");
assertThat(developer.getOrganization()).isEqualTo("Acme Corp");
assertThat(developer.getOrganizationUrl()).isEqualTo("http://www.example.com");
assertThat(developer.getRoles()).containsExactly("developer", "tester");
assertThat(developer.getTimezone()).isEqualTo("Asia/Karachi");
assertThat(developer.getProperties()).containsExactly(entry("prop1", "test1"), entry("prop2", "test2"),
entry("prop3", "test3"));
}
}

View File

@ -0,0 +1,51 @@
/*
* 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
*
* 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 io.spring.initializr.generator.buildsystem.maven;
import io.spring.initializr.generator.buildsystem.maven.MavenLicense.Distribution;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MavenLicense}.
*
* @author Jafer Khan Shamshad
*/
class MavenLicenseTests {
@Test
void licenseWithNameOnly() {
MavenLicense license = new MavenLicense.Builder().name("Apache License, Version 2.0").build();
assertThat(license.getName()).isEqualTo("Apache License, Version 2.0");
assertThat(license.getUrl()).isNull();
assertThat(license.getDistribution()).isNull();
assertThat(license.getComments()).isNull();
}
@Test
void licenseWithFullDetails() {
MavenLicense license = new MavenLicense.Builder().name("Apache License, Version 2.0")
.url("https://www.apache.org/licenses/LICENSE-2.0").distribution(Distribution.MANUAL)
.comments("A business-friendly OSS license").build();
assertThat(license.getName()).isEqualTo("Apache License, Version 2.0");
assertThat(license.getUrl()).isEqualTo("https://www.apache.org/licenses/LICENSE-2.0");
assertThat(license.getDistribution()).isEqualTo(Distribution.MANUAL);
assertThat(license.getComments()).isEqualTo("A business-friendly OSS license");
}
}