[MNG-8210] Replace Maven "module" term by "subproject" (#1651)

Co-authored-by: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
This commit is contained in:
Guillaume Nodet 2024-08-29 20:21:40 +02:00 committed by GitHub
parent e11b475e8b
commit b370e5e929
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 231 additions and 74 deletions

View File

@ -76,8 +76,8 @@ public @interface Mojo {
boolean projectRequired() default true;
/**
* if the Mojo uses the Maven project and its child modules.
* @return uses the Maven project and its child modules
* if the Mojo uses the Maven project and its subprojects.
* @return uses the Maven project and its subprojects
*/
boolean aggregator() default false;

View File

@ -25,7 +25,11 @@ import org.apache.maven.api.model.Model;
public interface ModelBuilder extends Service {
List<String> VALID_MODEL_VERSIONS = List.of("4.0.0", "4.1.0");
String MODEL_VERSION_4_0_0 = "4.0.0";
String MODEL_VERSION_4_1_0 = "4.1.0";
List<String> VALID_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0);
ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException;

View File

@ -35,7 +35,8 @@ public interface ModelProblem extends BuilderProblem {
V20,
V30,
V31,
V40
V40,
V41
}
/**

View File

@ -27,7 +27,7 @@ import static org.apache.maven.api.services.BaseRequest.nonNull;
/**
* A Source specific to load POMs. The {@link #resolve(ModelLocator, String)} method
* will be used to find POMs for children modules.
* will be used to find POMs for subprojects.
*
* @since 4.0.0
*/

View File

@ -313,7 +313,7 @@
<!-- SCM -->
<!-- ====================================================================== -->
<field xdoc.separator="blank" xml.insertParentFieldsUpTo="modules">
<field xdoc.separator="blank" xml.insertParentFieldsUpTo="subprojects">
<name>scm</name>
<version>4.0.0+</version>
<description>Specification for the SCM used by the project, such as CVS, Subversion, etc.</description>
@ -527,11 +527,25 @@
<fields>
<field xdoc.separator="blank">
<name>modules</name>
<version>4.0.0+</version>
<description>The modules (sometimes called subprojects) to build as a part of this
project. Each module listed is a relative path to the directory containing the module.
To be consistent with the way default urls are calculated from parent, it is recommended
to have module names match artifact ids.</description>
<version>4.0.0/4.1.0</version>
<description>
@deprecated Use {@link #subprojects} instead.
</description>
<association>
<type>String</type>
<multiplicity>*</multiplicity>
</association>
<annotations>
<annotation>@Deprecated</annotation>
</annotations>
</field>
<field xdoc.separator="blank">
<name>subprojects</name>
<version>4.1.0</version>
<description>The subprojects (formerly called modules) to build as a part of this
project. Each subproject listed is a relative path to the directory containing the subproject.
To be consistent with the way default URLs are calculated from parent, it is recommended
to have subproject names match artifact ids.</description>
<association>
<type>String</type>
<multiplicity>*</multiplicity>
@ -755,8 +769,8 @@
<name>defaultGoal</name>
<version>3.0.0+</version>
<description>The default goal (or phase in Maven 2) to execute when none is specified for
the project. Note that in case of a multi-module build, only the default goal of the top-level
project is relevant, i.e. the default goals of child modules are ignored. Since Maven 3,
the project. Note that in case of a build with subprojects, only the default goal of the top-level
project is relevant, i.e. the default goals of subprojects are ignored. Since Maven 3,
multiple goals/phases can be separated by whitespace.</description>
<type>String</type>
</field>

View File

@ -29,6 +29,11 @@ under the License.
<name>Maven API Implementation</name>
<description>Provides the implementation classes for the Maven API</description>
<properties>
<!-- in: DefaultModelValidator, DefaultModelBuilder -->
<checkstyle.violation.ignore>FileLength</checkstyle.violation.ignore>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>

View File

@ -21,6 +21,7 @@ package org.apache.maven.internal.impl.model;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@ -40,6 +41,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.api.Session;
import org.apache.maven.api.Type;
import org.apache.maven.api.VersionRange;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.di.Inject;
@ -305,7 +307,7 @@ public class DefaultModelBuilder implements ModelBuilder {
// Maven 3.x is always using 4.0.0 version to load the supermodel, so
// do the same when loading a dependency. The model validator will also
// check that field later.
superModelVersion = "4.0.0";
superModelVersion = MODEL_VERSION_4_0_0;
}
ModelData superData = new ModelData(null, getSuperModel(superModelVersion));
@ -737,6 +739,32 @@ public class DefaultModelBuilder implements ModelBuilder {
if (modelSource.getPath() != null) {
model = model.withPomFile(modelSource.getPath());
// subprojects discovery
if (model.getSubprojects().isEmpty()
&& model.getModules().isEmpty()
// only discover subprojects if POM > 4.0.0
&& !MODEL_VERSION_4_0_0.equals(model.getModelVersion())
// and if packaging is POM (we check type, but the session is not yet available,
// we would require the project realm if we want to support extensions
&& Type.POM.equals(model.getPackaging())) {
List<String> subprojects = new ArrayList<>();
try (Stream<Path> files = Files.list(model.getProjectDirectory())) {
for (Path f : files.toList()) {
if (Files.isDirectory(f)) {
Path subproject = modelProcessor.locateExistingPom(f);
if (subproject != null) {
subprojects.add(f.getFileName().toString());
}
}
}
if (!subprojects.isEmpty()) {
model = model.withSubprojects(subprojects);
}
} catch (IOException e) {
problems.add(Severity.FATAL, ModelProblem.Version.V41, "Error discovering subprojects", e);
}
}
}
problems.setSource(model);

View File

@ -151,12 +151,16 @@ class DefaultModelTransformerContextBuilder implements ModelTransformerContextBu
ModelBuilderRequest gaBuildingRequest =
ModelBuilderRequest.build(request, ModelSource.fromPath(pom));
Model rawModel = defaultModelBuilder.readFileModel(gaBuildingRequest, problems);
for (String module : rawModel.getModules()) {
Path moduleFile = defaultModelBuilder
List<String> subprojects = rawModel.getSubprojects();
if (subprojects == null) {
subprojects = rawModel.getModules();
}
for (String subproject : subprojects) {
Path subprojectFile = defaultModelBuilder
.getModelProcessor()
.locateExistingPom(pom.getParent().resolve(module));
if (moduleFile != null) {
toLoad.add(moduleFile);
.locateExistingPom(pom.getParent().resolve(subproject));
if (subprojectFile != null) {
toLoad.add(subprojectFile);
}
}
} catch (ModelBuilderException e) {

View File

@ -20,7 +20,6 @@ package org.apache.maven.internal.impl.model;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
@ -65,6 +64,7 @@ import org.apache.maven.api.model.Reporting;
import org.apache.maven.api.model.Repository;
import org.apache.maven.api.model.Resource;
import org.apache.maven.api.services.BuilderProblem.Severity;
import org.apache.maven.api.services.ModelBuilder;
import org.apache.maven.api.services.ModelBuilderRequest;
import org.apache.maven.api.services.ModelProblem.Version;
import org.apache.maven.api.services.ModelProblemCollector;
@ -73,6 +73,8 @@ import org.apache.maven.api.services.model.ModelVersionProcessor;
import org.apache.maven.model.v4.MavenModelVersion;
import org.apache.maven.model.v4.MavenTransformer;
import static org.apache.maven.internal.impl.model.DefaultModelBuilder.NAMESPACE_PREFIX;
/**
*/
@Named
@ -81,8 +83,7 @@ public class DefaultModelValidator implements ModelValidator {
public static final String BUILD_ALLOW_EXPRESSION_IN_EFFECTIVE_PROJECT_VERSION =
"maven.build.allowExpressionInEffectiveProjectVersion";
public static final List<String> VALID_MODEL_VERSIONS =
Collections.unmodifiableList(Arrays.asList("4.0.0", "4.1.0"));
public static final List<String> VALID_MODEL_VERSIONS = ModelBuilder.VALID_MODEL_VERSIONS;
private static final Pattern EXPRESSION_NAME_PATTERN = Pattern.compile("\\$\\{(.+?)}");
private static final Pattern EXPRESSION_PROJECT_NAME_PATTERN = Pattern.compile("\\$\\{(project.+?)}");
@ -136,7 +137,6 @@ public class DefaultModelValidator implements ModelValidator {
protected Activation.Builder transformActivation_File(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
stk.push(nextFrame("file", Activation::getFile));
Optional.ofNullable(target.getFile());
try {
return super.transformActivation_File(creator, builder, target);
} finally {
@ -335,7 +335,7 @@ public class DefaultModelValidator implements ModelValidator {
if (request.getValidationLevel() == ModelBuilderRequest.VALIDATION_LEVEL_MINIMAL) {
// profiles: they are essential for proper model building (may contribute profiles, dependencies...)
HashSet<String> minProfileIds = new HashSet<>();
for (org.apache.maven.api.model.Profile profile : m.getProfiles()) {
for (Profile profile : m.getProfiles()) {
if (!minProfileIds.add(profile.getId())) {
addViolation(
problems,
@ -362,6 +362,61 @@ public class DefaultModelValidator implements ModelValidator {
m.getLocation("modules"));
}
}
String modelVersion = m.getModelVersion();
if (modelVersion == null) {
String namespace = m.getNamespaceUri();
if (namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
modelVersion = namespace.substring(NAMESPACE_PREFIX.length());
}
}
if (Objects.equals(modelVersion, ModelBuilder.MODEL_VERSION_4_0_0)) {
if (!m.getSubprojects().isEmpty()) {
addViolation(
problems,
Severity.ERROR,
Version.V40,
"subprojects",
null,
"unexpected subprojects element",
m.getLocation("subprojects"));
}
} else {
Set<String> subprojects = new HashSet<>();
for (int i = 0, n = m.getSubprojects().size(); i < n; i++) {
String subproject = m.getSubprojects().get(i);
if (!subprojects.add(subproject)) {
addViolation(
problems,
Severity.ERROR,
Version.V41,
"subprojects.subproject[" + i + "]",
null,
"specifies duplicate subproject " + subproject,
m.getLocation("subprojects"));
}
}
if (!modules.isEmpty()) {
if (subprojects.isEmpty()) {
addViolation(
problems,
Severity.WARNING,
Version.V41,
"modules",
null,
"deprecated modules element, use subprojects instead",
m.getLocation("modules"));
} else {
addViolation(
problems,
Severity.ERROR,
Version.V41,
"modules",
null,
"cannot use both modules and subprojects element",
m.getLocation("modules"));
}
}
}
Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0);
@ -928,7 +983,7 @@ public class DefaultModelValidator implements ModelValidator {
private void validate20RawDependenciesSelfReferencing(
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
Model m,
List<Dependency> dependencies,
String prefix,
ModelBuilderRequest request) {
@ -959,7 +1014,7 @@ public class DefaultModelValidator implements ModelValidator {
private void validateEffectiveDependencies(
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
Model m,
List<Dependency> dependencies,
boolean management,
ModelBuilderRequest request) {
@ -1020,11 +1075,7 @@ public class DefaultModelValidator implements ModelValidator {
}
private void validateEffectiveModelAgainstDependency(
String prefix,
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
Dependency d,
ModelBuilderRequest request) {
String prefix, ModelProblemCollector problems, Model m, Dependency d, ModelBuilderRequest request) {
String key = d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion()
+ (d.getClassifier() != null ? ":" + d.getClassifier() : EMPTY);
String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion();

View File

@ -225,7 +225,7 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
String packaging = model.getPackaging();
if (POM_PACKAGING.equals(packaging)) {
// raw to consumer transform
model = model.withRoot(false).withModules(null);
model = model.withRoot(false).withModules(null).withSubprojects(null);
if (model.getParent() != null) {
model = model.withParent(model.getParent().withRelativePath(null));
}
@ -294,6 +294,7 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
|| profile.getDependencyManagement().getDependencies().isEmpty())
&& profile.getDistributionManagement() == null
&& profile.getModules().isEmpty()
&& profile.getSubprojects().isEmpty()
&& profile.getProperties().isEmpty()
&& profile.getRepositories().isEmpty()
&& profile.getPluginRepositories().isEmpty()

View File

@ -273,7 +273,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
boolean root;
List<InterimResult> modules = Collections.emptyList();
List<InterimResult> subprojects = Collections.emptyList();
ProjectBuildingResult projectBuildingResult;
@ -624,20 +624,24 @@ public class DefaultProjectBuilder implements ProjectBuilder {
if (recursive) {
File basedir = pomFile.getParentFile();
List<File> moduleFiles = new ArrayList<>();
for (String module : model.getModules()) {
if (module == null || module.isEmpty()) {
List<String> subprojects = model.getSubprojects();
if (subprojects.isEmpty()) {
subprojects = model.getModules();
}
List<File> subprojectFiles = new ArrayList<>();
for (String subproject : subprojects) {
if (subproject == null || subproject.isEmpty()) {
continue;
}
module = module.replace('\\', File.separatorChar).replace('/', File.separatorChar);
subproject = subproject.replace('\\', File.separatorChar).replace('/', File.separatorChar);
Path modulePath = modelProcessor.locateExistingPom(new File(basedir, module).toPath());
File moduleFile = modulePath != null ? modulePath.toFile() : null;
Path subprojectPath = modelProcessor.locateExistingPom(new File(basedir, subproject).toPath());
File subprojectFile = subprojectPath != null ? subprojectPath.toFile() : null;
if (moduleFile == null) {
if (subprojectFile == null) {
ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem(
"Child module " + moduleFile + " of " + pomFile + " does not exist",
"Child subproject " + subprojectFile + " of " + pomFile + " does not exist",
ModelProblem.Severity.ERROR,
ModelProblem.Version.BASE,
model,
@ -651,23 +655,24 @@ public class DefaultProjectBuilder implements ProjectBuilder {
if (Os.IS_WINDOWS) {
// we don't canonicalize on unix to avoid interfering with symlinks
try {
moduleFile = moduleFile.getCanonicalFile();
subprojectFile = subprojectFile.getCanonicalFile();
} catch (IOException e) {
moduleFile = moduleFile.getAbsoluteFile();
subprojectFile = subprojectFile.getAbsoluteFile();
}
} else {
moduleFile = new File(moduleFile.toURI().normalize());
subprojectFile = new File(subprojectFile.toURI().normalize());
}
if (aggregatorFiles.contains(moduleFile)) {
if (aggregatorFiles.contains(subprojectFile)) {
StringBuilder buffer = new StringBuilder(256);
for (File aggregatorFile : aggregatorFiles) {
buffer.append(aggregatorFile).append(" -> ");
}
buffer.append(moduleFile);
buffer.append(subprojectFile);
ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem(
"Child module " + moduleFile + " of " + pomFile + " forms aggregation cycle " + buffer,
"Child subproject " + subprojectFile + " of " + pomFile + " forms aggregation cycle "
+ buffer,
ModelProblem.Severity.ERROR,
ModelProblem.Version.BASE,
model,
@ -679,11 +684,11 @@ public class DefaultProjectBuilder implements ProjectBuilder {
continue;
}
moduleFiles.add(moduleFile);
subprojectFiles.add(subprojectFile);
}
if (!moduleFiles.isEmpty()) {
interimResult.modules = build(projectIndex, moduleFiles, aggregatorFiles, false, recursive);
if (!subprojectFiles.isEmpty()) {
interimResult.subprojects = build(projectIndex, subprojectFiles, aggregatorFiles, false, recursive);
}
}
@ -775,7 +780,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
iarte));
}
List<ProjectBuildingResult> results = build(projectIndex, interimResult.modules);
List<ProjectBuildingResult> results = build(projectIndex, interimResult.subprojects);
project.setExecutionRoot(interimResult.root);
project.setCollectedProjects(

View File

@ -708,6 +708,9 @@ public class MavenProject implements Cloneable {
}
public List<String> getModules() {
if (!getModel().getDelegate().getSubprojects().isEmpty()) {
return getModel().getDelegate().getSubprojects();
}
return getModel().getModules();
}

View File

@ -65,7 +65,7 @@ class ConsumerPomArtifactTransformerTest {
t.transform(project, systemSessionMock, beforePomFile, tempFile);
}
XmlAssert.assertThat(afterPomFile.toFile()).and(tempFile.toFile()).areIdentical();
XmlAssert.assertThat(tempFile.toFile()).and(afterPomFile.toFile()).areIdentical();
}
@Test

View File

@ -405,4 +405,18 @@ class DefaultMavenProjectBuilderTest extends AbstractMavenProjectTestCase {
assertEquals("1.0-SNAPSHOT", mp.getVersion());
}
@Test
public void testSubprojectDiscovery() throws Exception {
File pom = getTestFile("src/test/resources/projects/subprojects-discover/pom.xml");
ProjectBuildingRequest configuration = newBuildingRequest();
List<ProjectBuildingResult> results = projectBuilder.build(List.of(pom), true, configuration);
assertEquals(2, results.size());
MavenProject p1 = results.get(0).getProject();
MavenProject p2 = results.get(1).getProject();
MavenProject parent = p1.getArtifactId().equals("parent") ? p1 : p2;
MavenProject child = p1.getArtifactId().equals("parent") ? p2 : p1;
assertEquals(List.of("child"), parent.getModel().getDelegate().getSubprojects());
}
}

View File

@ -18,7 +18,7 @@ specific language governing permissions and limitations
under the License.
-->
<project root="true" xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd">
<groupId>org.sonatype.mavenbook.multi</groupId>
<artifactId>parent</artifactId>
<version>0.9-${changelist}-SNAPSHOT</version>
@ -26,7 +26,7 @@ under the License.
<name>Multi Chapter Parent Project</name>
<!-- Optimized from https://github.com/sonatype/maven-example-en/tree/master/examples/ch-multi -->
<modules>
<module>simple-parent</module>
</modules>
<subprojects>
<subproject>simple-parent</subproject>
</subprojects>
</project>

View File

@ -17,7 +17,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd">
<parent>
<groupId>org.sonatype.mavenbook.multi</groupId>
<artifactId>parent</artifactId>
@ -26,15 +26,15 @@ under the License.
<packaging>pom</packaging>
<name>Multi Chapter Simple Parent Project</name>
<modules>
<module>simple-weather</module>
<module>simple-webapp</module>
<subprojects>
<subproject>simple-weather</subproject>
<subproject>simple-webapp</subproject>
<!-- On purpose at the end, project graph is responsible for ordering -->
<!-- Build/consumer should not be effected by reverse order -->
<module>simple-testutils</module>
<module>utils-parent</module>
</modules>
<subproject>simple-testutils</subproject>
<subproject>utils-parent</subproject>
</subprojects>
<build>
<pluginManagement>

View File

@ -17,7 +17,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd">
<parent>
<groupId>org.sonatype.mavenbook.multi</groupId>
<artifactId>utils-parent</artifactId>

View File

@ -17,7 +17,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd">
<parent>
<groupId>org.sonatype.mavenbook.multi</groupId>
<artifactId>simple-parent</artifactId>

View File

@ -17,7 +17,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0-alpha-8.xsd">
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd">
<parent>
<groupId>org.sonatype.mavenbook.multi</groupId>
<artifactId>simple-parent</artifactId>

View File

@ -4,9 +4,9 @@
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>child.xml</module>
</modules>
<subprojects>
<subproject>child.xml</subproject>
</subprojects>
<dependencyManagement>
<dependencies>

View File

@ -0,0 +1,8 @@
<project xmlns="http://maven.apache.org/POM/4.1.0">
<parent>
<groupId>subprojects-discover</groupId>
<artifactId>parent</artifactId>
</parent>
<artifactId>child</artifactId>
<packaging>jar</packaging>
</project>

View File

@ -0,0 +1,6 @@
<project xmlns="http://maven.apache.org/POM/4.1.0">
<groupId>subprojects-discover</groupId>
<artifactId>parent</artifactId>
<version>1</version>
<packaging>pom</packaging>
</project>

View File

@ -30,10 +30,10 @@ under the License.
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>lib</module> <!-- the library -->
<module>app</module> <!-- the application -->
</modules>
<subprojects>
<subproject>lib</subproject> <!-- the library -->
<subproject>app</subproject> <!-- the application -->
</subprojects>
<build>
<pluginManagement>

View File

@ -44,7 +44,8 @@ public interface ModelProblem {
V20,
V30,
V31,
V40
V40,
V41
}
/**

View File

@ -133,6 +133,9 @@ public class ${class.name}
* ${line.trim()}
#end
*/
#foreach( $ann in ${field.annotations} )
${ann}
#end
final ${type} $field.name;
#end
#if ( $locationTracking )
@ -229,6 +232,9 @@ public class ${class.name}
*
* @return a {@code ${type}}
*/
#foreach( $ann in ${field.annotations} )
${ann}
#end
#if ( $field.type == "java.util.List" || $field.type == "java.util.Properties" )
@Nonnull
#end
@ -284,6 +290,9 @@ public class ${class.name}
* @param ${field.name} the new {@code $type} to use
* @return a {@code ${class.name}} with the specified ${field.name}
*/
#foreach( $ann in ${field.annotations} )
${ann}
#end
@Nonnull
public ${class.name} with${cap}($type $field.name) {
return newBuilder(this, true).${field.name}($field.name).build();
@ -452,6 +461,9 @@ public class ${class.name}
#set ( $type = ${types.getOrDefault($field,${types.getOrDefault($field.type,$field.type)})} )
#if ( $type.startsWith("List<") )
#set ( $type = ${type.replace('List<','Collection<')} )
#end
#foreach( $ann in ${field.annotations} )
${ann}
#end
@Nonnull
public Builder ${field.name}(${type} ${field.name}) {