diff --git a/api/maven-api-model/src/main/mdo/maven.mdo b/api/maven-api-model/src/main/mdo/maven.mdo
index 1cf8f2ea49..2caa1077d9 100644
--- a/api/maven-api-model/src/main/mdo/maven.mdo
+++ b/api/maven-api-model/src/main/mdo/maven.mdo
@@ -214,7 +214,7 @@
root
- 4.0.0+
+ 4.1.0+
boolean
false
+
+ preserveModelVersion
+ 4.1.0+
+
+ Since: Maven 4.0.0
+ ]]>
+
+ boolean
+ false
+
inceptionYear
3.0.0+
diff --git a/maven-core/src/test/resources/projects/future-model-version-pom.xml b/maven-core/src/test/resources/projects/future-model-version-pom.xml
index 1a73a44434..c76f97ce12 100644
--- a/maven-core/src/test/resources/projects/future-model-version-pom.xml
+++ b/maven-core/src/test/resources/projects/future-model-version-pom.xml
@@ -18,7 +18,7 @@ under the License.
-->
- 4.0.1
+ 4.9.1
tests.project
future-model-version
1
diff --git a/maven-core/src/test/resources/projects/transform/consumer-after.pom b/maven-core/src/test/resources/projects/transform/consumer-after.pom
new file mode 100644
index 0000000000..2f7ef91990
--- /dev/null
+++ b/maven-core/src/test/resources/projects/transform/consumer-after.pom
@@ -0,0 +1,81 @@
+
+
+
+
+
+ 4.0.0
+
+ test
+ test
+ 0.1-SNAPSHOT
+ pom
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.1
+
+
+ 1.5
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ test
+
+
+
+
+
+
+
+
+ default-active
+
+ true
+
+
+ UTF-8
+
+
+
+ file
+
+
+ simple.xml
+
+
+
+ activated
+
+
+
+
diff --git a/maven-core/src/test/resources/projects/transform/consumer-before.pom b/maven-core/src/test/resources/projects/transform/consumer-before.pom
new file mode 100644
index 0000000000..60d0ccedf3
--- /dev/null
+++ b/maven-core/src/test/resources/projects/transform/consumer-before.pom
@@ -0,0 +1,87 @@
+
+
+
+
+
+ 4.1.0
+
+ test
+ test
+ 0.1-SNAPSHOT
+ pom
+
+
+ lib
+ app
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.1
+
+
+ 1.5
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ test
+
+
+
+
+
+
+
+
+ default-active
+
+ true
+
+
+ UTF-8
+
+
+
+ file
+
+
+ simple.xml
+
+
+
+ activated
+
+
+
+
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
index 2594dd9c35..39d76a6bbc 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
@@ -59,6 +59,7 @@ import org.apache.maven.model.building.ModelProblem.Version;
import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
import org.apache.maven.model.interpolation.ModelVersionProcessor;
+import org.apache.maven.model.v4.MavenModelVersion;
import org.codehaus.plexus.util.StringUtils;
/**
@@ -143,15 +144,11 @@ public class DefaultModelValidator implements ModelValidator {
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
- // [MNG-6074] Maven should produce an error if no model version has been set in a POM file used to build an
- // effective model.
- //
- // As of 3.4, the model version is mandatory even in raw models. The XML element still is optional in the
- // XML schema and this will not change anytime soon. We do not want to build effective models based on
- // models without a version starting with 3.4.
- validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.V20, m.getModelVersion(), m);
-
- validateModelVersion(problems, m.getModelVersion(), m, "4.0.0");
+ // The file pom may not contain the modelVersion yet, as it may be set later by the
+ // ModelVersionXMLFilter.
+ if (m.getModelVersion() != null && !m.getModelVersion().isEmpty()) {
+ validateModelVersion(problems, m.getModelVersion(), m, "4.0.0", "4.1.0");
+ }
validateStringNoExpression("groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m);
if (parent == null) {
@@ -257,6 +254,28 @@ public class DefaultModelValidator implements ModelValidator {
public void validateRawModel(Model ma, ModelBuildingRequest request, ModelProblemCollector problems) {
org.apache.maven.api.model.Model m = ma.getDelegate();
+ // [MNG-6074] Maven should produce an error if no model version has been set in a POM file used to build an
+ // effective model.
+ //
+ // As of 3.4, the model version is mandatory even in raw models. The XML element still is optional in the
+ // XML schema and this will not change anytime soon. We do not want to build effective models based on
+ // models without a version starting with 3.4.
+ validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.V20, m.getModelVersion(), m);
+
+ validateModelVersion(problems, m.getModelVersion(), m, "4.0.0", "4.1.0");
+
+ String minVersion = new MavenModelVersion().getModelVersion(m);
+ if (m.getModelVersion() != null && compareModelVersions(minVersion, m.getModelVersion()) > 0) {
+ addViolation(
+ problems,
+ Severity.FATAL,
+ Version.V40,
+ "model",
+ null,
+ "the model contains elements that require a model version of " + minVersion,
+ m);
+ }
+
Parent parent = m.getParent();
if (parent != null) {
diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/building/SimpleProblemCollector.java b/maven-model-builder/src/test/java/org/apache/maven/model/building/SimpleProblemCollector.java
index 41d9a79fc2..9271005875 100644
--- a/maven-model-builder/src/test/java/org/apache/maven/model/building/SimpleProblemCollector.java
+++ b/maven-model-builder/src/test/java/org/apache/maven/model/building/SimpleProblemCollector.java
@@ -61,13 +61,19 @@ public class SimpleProblemCollector implements ModelProblemCollector {
public void add(ModelProblemCollectorRequest req) {
switch (req.getSeverity()) {
case FATAL:
- fatals.add(req.getMessage());
+ if (!fatals.contains(req.getMessage())) {
+ fatals.add(req.getMessage());
+ }
break;
case ERROR:
- errors.add(req.getMessage());
+ if (!errors.contains(req.getMessage())) {
+ errors.add(req.getMessage());
+ }
break;
case WARNING:
- warnings.add(req.getMessage());
+ if (!warnings.contains(req.getMessage())) {
+ warnings.add(req.getMessage());
+ }
break;
}
}
diff --git a/maven-model-transform/pom.xml b/maven-model-transform/pom.xml
index 50a90c7ec4..26edb7a2e6 100644
--- a/maven-model-transform/pom.xml
+++ b/maven-model-transform/pom.xml
@@ -28,6 +28,10 @@ under the License.
Maven Model XML Transform
+
+ org.apache.maven
+ maven-model
+
org.codehaus.plexus
plexus-xml
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionDowngradeXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionDowngradeXMLFilter.java
new file mode 100644
index 0000000000..7207fc446b
--- /dev/null
+++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionDowngradeXMLFilter.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.model.transform;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.api.model.Model;
+import org.apache.maven.model.transform.stax.BufferingParser;
+import org.apache.maven.model.v4.MavenModelVersion;
+import org.apache.maven.model.v4.MavenStaxReader;
+
+public class ModelVersionDowngradeXMLFilter extends BufferingParser {
+
+ public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
+
+ private final List buffer = new ArrayList<>();
+
+ public ModelVersionDowngradeXMLFilter(XMLStreamReader delegate) {
+ super(delegate);
+ }
+
+ @Override
+ protected boolean accept() throws XMLStreamException {
+ Event e = bufferEvent();
+ buffer.add(e);
+ if (e.event == XMLStreamReader.END_DOCUMENT) {
+ ReplayParser p = new ReplayParser(this);
+ buffer.forEach(p::pushEvent);
+ p.next();
+ String version;
+ Model model = new MavenStaxReader().read(p, false, null);
+ if (model.isPreserveModelVersion()) {
+ version = model.getModelVersion();
+ } else {
+ model = model.withPreserveModelVersion(false);
+ version = new MavenModelVersion().getModelVersion(model);
+ }
+ int depth = 0;
+ boolean isModelVersion = false;
+ for (Event event : buffer) {
+ event.namespace = NAMESPACE_PREFIX + version;
+ // rewrite namespace
+ if (event.namespaces != null) {
+ for (int i = 0; i < event.namespaces.length; i++) {
+ if (event.namespaces[i].uri.startsWith(NAMESPACE_PREFIX)) {
+ event.namespaces[i].uri = event.namespace;
+ }
+ }
+ }
+ // rewrite xsi:schemaLocation attribute
+ if (event.attributes != null) {
+ for (Attribute attribute : event.attributes) {
+ if (attribute.namespace.equals("http://www.w3.org/2001/XMLSchema-instance")
+ && attribute.name.equals("schemaLocation")) {
+ attribute.value = attribute
+ .value
+ .replaceAll(
+ "\\Q" + NAMESPACE_PREFIX + "\\E[0-9]\\.[0-9]\\.[0-9]",
+ NAMESPACE_PREFIX + version)
+ .replaceAll(
+ "http(s?)://maven\\.apache\\.org/xsd/maven-[0-9]\\.[0-9]\\.[0-9]\\.xsd",
+ "https://maven.apache.org/xsd/maven-" + version + ".xsd");
+ }
+ }
+ }
+ // Rewrite modelVersion
+ if (event.event == XMLStreamReader.START_ELEMENT) {
+ depth++;
+ isModelVersion = depth == 2 && event.name.equals("modelVersion");
+ }
+ if (event.event == XMLStreamReader.CHARACTERS && isModelVersion) {
+ event.text = version;
+ }
+ if (event.event == XMLStreamReader.END_ELEMENT) {
+ depth--;
+ isModelVersion = false;
+ }
+ pushEvent(event);
+ }
+ }
+ return false;
+ }
+
+ static class ReplayParser extends BufferingParser {
+ ReplayParser(XMLStreamReader delegate) {
+ super(delegate);
+ }
+
+ public void pushEvent(Event e) {
+ super.pushEvent(e);
+ }
+ }
+}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionXMLFilter.java
index 645030aeee..1b721f7145 100644
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionXMLFilter.java
+++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionXMLFilter.java
@@ -27,9 +27,10 @@ import org.apache.maven.model.transform.stax.NodeBufferingParser;
public class ModelVersionXMLFilter extends NodeBufferingParser {
- private static final Pattern S_FILTER = Pattern.compile("\\s+");
public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
+ private static final Pattern S_FILTER = Pattern.compile("\\s+");
+
public ModelVersionXMLFilter(XMLStreamReader delegate) {
super(delegate, "project");
}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java
index 08af39e7d4..b69dcda756 100644
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java
+++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java
@@ -44,6 +44,8 @@ public class RawToConsumerPomXMLFilterFactory {
parser = new ModulesXMLFilter(parser);
// Adjust relativePath
parser = new RelativePathXMLFilter(parser);
+ // Downgrade modelVersion if needed
+ parser = new ModelVersionDowngradeXMLFilter(parser);
return parser;
}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java
index 9352984fe9..37e69d8665 100644
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java
+++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java
@@ -120,7 +120,7 @@ class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests {
+ "\n"
+ + " https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n"
+ " 4.0.0\n"
+ " org.sonatype.mavenbook.multispring\n"
+ " parent\n"
@@ -163,7 +163,7 @@ class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests {
+ "\n"
+ "\n"
+ + " xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n"
+ " 4.0.0\n"
+ " \n"
+ " org.apache.maven\n"
@@ -226,4 +226,82 @@ class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests {
String actual = transform(input);
assertThat(actual).and(expected).areIdentical();
}
+
+ @Test
+ void downgradeModelVersion() throws Exception {
+ String input = "\n"
+ + "\n"
+ + " 4.1.0\n"
+ + " org.sonatype.mavenbook.multispring\n"
+ + " parent\n"
+ + " 0.9-SNAPSHOT\n"
+ + " pom\n"
+ + "";
+ String expected = "\n"
+ + "\n"
+ + " 4.0.0\n"
+ + " org.sonatype.mavenbook.multispring\n"
+ + " parent\n"
+ + " 0.9-SNAPSHOT\n"
+ + " pom\n"
+ + "";
+ String actual = transform(input);
+ assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
+ }
+
+ @Test
+ void downgradeNotModelVersion() throws Exception {
+ String input = "\n"
+ + "\n"
+ + " 4.1.0\n"
+ + " org.sonatype.mavenbook.multispring\n"
+ + " parent\n"
+ + " 0.9-SNAPSHOT\n"
+ + " pom\n"
+ + " "
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " 1\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + "";
+ String expected = "\n"
+ + "\n"
+ + " 4.1.0\n"
+ + " org.sonatype.mavenbook.multispring\n"
+ + " parent\n"
+ + " 0.9-SNAPSHOT\n"
+ + " pom\n"
+ + " "
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " 1\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + "";
+ String actual = transform(input);
+ assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
+ }
}
diff --git a/maven-model/pom.xml b/maven-model/pom.xml
index 95cfaafa27..42e759edaf 100644
--- a/maven-model/pom.xml
+++ b/maven-model/pom.xml
@@ -82,6 +82,7 @@ under the License.
packageModelV4=org.apache.maven.api.model
packageToolV4=org.apache.maven.model.v4
isMavenModel=true
+ minimalVersion=4.0.0
@@ -123,6 +124,7 @@ under the License.
writer-ex.vm
reader-stax.vm
writer-stax.vm
+ model-version.vm
diff --git a/maven-model/src/test/java/org/apache/maven/model/v4/MavenModelVersionTest.java b/maven-model/src/test/java/org/apache/maven/model/v4/MavenModelVersionTest.java
new file mode 100644
index 0000000000..ff2abdc981
--- /dev/null
+++ b/maven-model/src/test/java/org/apache/maven/model/v4/MavenModelVersionTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.model.v4;
+
+import java.io.InputStream;
+
+import org.apache.maven.api.model.Model;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class MavenModelVersionTest {
+
+ private static Model model;
+
+ @BeforeAll
+ static void setup() throws Exception {
+ try (InputStream is = MavenModelVersionTest.class.getResourceAsStream("/xml/pom.xml")) {
+ model = new MavenStaxReader().read(is);
+ }
+ }
+
+ @Test
+ void testV4Model() {
+ assertEquals("4.0.0", new MavenModelVersion().getModelVersion(model));
+ }
+
+ @Test
+ void testV4ModelVersion() {
+ Model m = model.withModelVersion("4.1.0");
+ assertEquals("4.0.0", new MavenModelVersion().getModelVersion(m));
+ }
+
+ @Test
+ void testV4ModelRoot() {
+ Model m = model.withRoot(true);
+ assertEquals("4.1.0", new MavenModelVersion().getModelVersion(m));
+ }
+
+ @Test
+ void testV4ModelPreserveModelVersion() {
+ Model m = model.withPreserveModelVersion(true);
+ assertEquals("4.1.0", new MavenModelVersion().getModelVersion(m));
+ }
+}
diff --git a/src/mdo/model-version.vm b/src/mdo/model-version.vm
new file mode 100644
index 0000000000..67586b6342
--- /dev/null
+++ b/src/mdo/model-version.vm
@@ -0,0 +1,180 @@
+#*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ 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.
+*#
+#parse ( "common.vm" )
+#
+#set ( $package = "${packageToolV4}" )
+#set ( $className = "${model.name}ModelVersion" )
+#
+#set ( $root = $model.getClass( $model.getRoot($version), $version ) )
+#
+#MODELLO-VELOCITY#SAVE-OUTPUT-TO ${package.replace('.','/')}/${className}.java
+// =================== DO NOT EDIT THIS FILE ====================
+// Generated by Modello Velocity from ${template}
+// template, any modifications will be overwritten.
+// ==============================================================
+package ${package};
+
+import java.io.ObjectStreamException;
+import java.nio.file.Path;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.maven.api.annotations.Generated;
+import org.apache.maven.api.xml.XmlNode;
+#foreach ( $class in $model.allClasses )
+import ${packageModelV4}.${class.Name};
+#end
+
+@Generated
+public class ${className} {
+
+ public String getModelVersion(${root.name} model) {
+ Objects.requireNonNull(model, "model cannot be null");
+
+#set ( $String = $model.getClass().forName("java.lang.String") )
+#set ( $Comparator = $model.getClass().forName("java.util.Comparator") )
+#set ( $LinkedHashSet = $model.getClass().forName("java.util.LinkedHashSet") )
+#set ( $HashMap = $model.getClass().forName("java.util.HashMap") )
+#set ( $TreeSet = $model.getClass().forName("java.util.TreeSet") )
+#set ( $Version = $model.getClass().forName("org.codehaus.modello.model.Version") )
+#set ( $versions = $TreeSet.getConstructor( $Comparator ).newInstance( $Comparator.reverseOrder() ) )
+#foreach ( $class in $model.allClasses )
+ #set ( $dummy = $versions.add( $class.versionRange.fromVersion ) )
+ #foreach ( $field in $class.allFields )
+ #if ( ! $Helper.xmlFieldMetadata( $field ).transient )
+ #set ( $dummy = $versions.add( $field.versionRange.fromVersion ) )
+ #end
+ #end
+#end
+#if ( $minimalVersion )
+ #set ( $minimal = $Version.getConstructor( $String ).newInstance( $minimalVersion ) )
+ #set ( $versions = $versions.headSet( $minimal, false ) )
+#else
+ #set ( $dummy = $versions.remove( $Version.getConstructor( $String ).newInstance( "0.0.0" ) ) )
+#end
+#set ( $dummy = $versions.remove( $Version.getConstructor( $String ).newInstance( "32767.32767.32767" ) ) )
+#foreach ( $version in $versions )
+ #set ( $v = $version.toString().replace('.', '_') )
+ // ${version}
+ if (is_${v}(model)) {
+ return "${version}";
+ }
+#end
+#if ( $minimalVersion )
+ return "$minimalVersion";
+#else
+ return null;
+#end
+ }
+
+#foreach ( $version in $versions )
+ #set ( $v = $version.toString().replace('.', '_') )
+ #set ( $classesToCheck = $TreeSet.newInstance() )
+ #set ( $classToFields = $HashMap.newInstance() )
+ #foreach($unused in [1..10])
+ #foreach ( $class in $model.allClasses )
+ #foreach ( $field in $class.allFields )
+ #if ( ! $Helper.xmlFieldMetadata( $field ).transient )
+ #set ( $newInVersion = $field.versionRange.fromVersion.equals($version) )
+ #set ( $isAsso = false )
+ #if ( $field.toClass )
+ #set ( $ancestors = $Helper.ancestors( $field.toClass ) )
+ #foreach ( $cl in $ancestors )
+ #if ( $classToFields.containsKey( $cl ) )
+ #set ( $isAsso = true )
+ #end
+ #end
+ #end
+ #if ( $newInVersion || $isAsso )
+ #set ( $fields = $classToFields.get( $class ) )
+ #if ( ! $fields )
+ #set ( $fields = $LinkedHashSet.newInstance() )
+ #set ( $dummy = $classToFields.put( $class, $fields ) )
+ #end
+ #set( $dummy = $fields.add($field) )
+ #if ( $dummy )
+ #end
+ #end
+ #end
+ #end
+ #end
+ #end
+ #foreach ( $class in $classToFields.keySet() )
+ #set ( $var = $Helper.uncapitalise( $class.name ) )
+ private boolean is_${v}(${class.name} ${var}) {
+ return ${var} != null && (
+ #set ( $pfx = " " )
+ #if ( $class.superClass )
+ #if ( $classToFields.containsKey( $model.getClass( $class.superClass, $version ) ) )
+ $pfx is_${v}((${class.superClass}) ${var})
+ #set ( $pfx = "||" )
+ #end
+ #end
+ #foreach ( $field in $classToFields.get( $class ) )
+ #if ( $field.isManyMultiplicity() )
+ #if ( $classToFields.containsKey( $model.getClass( $field.type ) ) )
+ $pfx ${var}.get${Helper.capitalise($field.name)}().stream().anyMatch(this::is_${v}) // ${class.name} : ${field.name}
+ #else
+ $pfx !${var}.get${Helper.capitalise($field.name)}().isEmpty() // ${class.name} : ${field.name}
+ #end
+ #elseif ( $field.isOneMultiplicity() )
+ $pfx is_${v}(${var}.get${Helper.capitalise($field.name)}()) // ${class.name} : ${field.name}
+ #elseif ( $field.type == "boolean" || $field.type == "Boolean" )
+ $pfx has(${var}.is${Helper.capitalise($field.name)}()) // ${class.name} : ${field.name}
+ #else
+ $pfx has(${var}.get${Helper.capitalise($field.name)}()) // ${class.name} : ${field.name}
+ #end
+ #set ( $pfx = "||" )
+ #end
+ );
+ }
+ #end
+
+#end
+ private boolean has(String str) {
+ return str != null;
+ }
+
+ private boolean has(Path path) {
+ return path != null;
+ }
+
+ private boolean has(boolean bool) {
+ return bool;
+ }
+
+ private boolean has(int val) {
+ return val != 0;
+ }
+
+ private boolean has(List> list) {
+ return !list.isEmpty();
+ }
+
+}
diff --git a/src/mdo/writer-stax.vm b/src/mdo/writer-stax.vm
index cb9097cfe8..b0ec820ebc 100644
--- a/src/mdo/writer-stax.vm
+++ b/src/mdo/writer-stax.vm
@@ -84,10 +84,25 @@ public class ${className} {
//--------------------------/
/**
- * Field NAMESPACE.
+ * Default namespace.
*/
private static final String NAMESPACE = "${namespace}";
+ /**
+ * Default schemaLocation.
+ */
+ private static final String SCHEMA_LOCATION = "${schemaLocation}";
+
+ /**
+ * Field namespace.
+ */
+ private String namespace = NAMESPACE;
+
+ /**
+ * Field schemaLocation.
+ */
+ private String schemaLocation = SCHEMA_LOCATION;
+
/**
* Field fileComment.
*/
@@ -106,6 +121,24 @@ public class ${className} {
//- Methods -/
//-----------/
+ /**
+ * Method setNamespace.
+ *
+ * @param namespace the namespace to use.
+ */
+ public void setNamespace(String namespace) {
+ this.namespace = Objects.requireNonNull(namespace);
+ } //-- void setNamespace(String)
+
+ /**
+ * Method setSchemaLocation.
+ *
+ * @param schemaLocation the schema location to use.
+ */
+ public void setSchemaLocation(String schemaLocation) {
+ this.schemaLocation = Objects.requireNonNull(schemaLocation);
+ } //-- void setSchemaLocation(String)
+
/**
* Method setFileComment.
*
@@ -183,12 +216,12 @@ public class ${className} {
serializer.writeComment(this.fileComment);
serializer.writeCharacters("\n");
}
- serializer.writeStartElement("", tagName, NAMESPACE);
- serializer.writeNamespace("", NAMESPACE);
+ serializer.writeStartElement("", tagName, namespace);
+ serializer.writeNamespace("", namespace);
serializer.writeNamespace("xsi", W3C_XML_SCHEMA_INSTANCE_NS_URI);
- serializer.writeAttribute(W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation", NAMESPACE + " ${schemaLocation}");
+ serializer.writeAttribute(W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation", namespace + " " + schemaLocation);
#else
- serializer.writeStartElement(NAMESPACE, tagName);
+ serializer.writeStartElement(namespace, tagName);
#end
#foreach ( $field in $allFields )
#if ( $Helper.xmlFieldMetadata( $field ).attribute )
@@ -283,7 +316,7 @@ public class ${className} {
#end
if (list != null && !list.isEmpty()) {
if (!flat) {
- serializer.writeStartElement(NAMESPACE, tagName);
+ serializer.writeStartElement(namespace, tagName);
}
int index = 0;
#if ( $locationTracking )
@@ -307,7 +340,7 @@ public class ${className} {
private void writeProperties(String tagName, Map props, XMLStreamWriter serializer) throws IOException, XMLStreamException {
#end
if (props != null && !props.isEmpty()) {
- serializer.writeStartElement(NAMESPACE, tagName);
+ serializer.writeStartElement(namespace, tagName);
#if ( $locationTracking )
InputLocation location = locationTracker != null ? locationTracker.getLocation(tagName) : null;
#end
@@ -326,7 +359,7 @@ public class ${className} {
private void writeDom(XmlNode dom, XMLStreamWriter serializer) throws IOException, XMLStreamException {
if (dom != null) {
- serializer.writeStartElement(NAMESPACE, dom.getName());
+ serializer.writeStartElement(namespace, dom.getName());
for (Map.Entry attr : dom.getAttributes().entrySet()) {
if (attr.getKey().startsWith("xml:")) {
serializer.writeAttribute("http://www.w3.org/XML/1998/namespace",
@@ -357,7 +390,7 @@ public class ${className} {
private void writeTag(String tagName, String defaultValue, String value, XMLStreamWriter serializer) throws IOException, XMLStreamException {
#end
if (value != null && !Objects.equals(defaultValue, value)) {
- serializer.writeStartElement(NAMESPACE, tagName);
+ serializer.writeStartElement(namespace, tagName);
serializer.writeCharacters(value);
serializer.writeEndElement();
#if ( $locationTracking )