This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new bff84bd63 Replace Properties with Map<String, String> in the v4 api 
(#808)
bff84bd63 is described below

commit bff84bd634fd4cafe87f31ea155744095f6ecd1a
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed Oct 5 16:52:17 2022 +0200

    Replace Properties with Map<String, String> in the v4 api (#808)
---
 .../main/java/org/apache/maven/api/Session.java    |   6 +-
 api/maven-api-model/src/main/mdo/model.vm          |   3 +
 api/maven-api-settings/src/main/mdo/model.vm       |  11 +-
 api/maven-api-toolchain/src/main/mdo/model.vm      |  11 +-
 .../apache/maven/internal/impl/DefaultSession.java |   9 +-
 .../maven/internal/impl/PropertiesAsMap.java       | 113 +++++++++++++++++++++
 .../PluginParameterExpressionEvaluatorV4.java      |   4 +-
 .../org/apache/maven/settings/SettingsUtils.java   |   3 +-
 .../apache/maven/settings/SettingsUtilsTest.java   |   6 +-
 .../inheritance/DefaultInheritanceAssembler.java   |   9 +-
 .../AbstractStringBasedModelInterpolator.java      |   4 +-
 .../interpolation/BuildTimestampValueSource.java   |   4 +-
 .../model/interpolation/MavenBuildTimestamp.java   |   6 +-
 .../AbstractModelInterpolatorTest.java             |  32 +++---
 .../interpolation/MavenBuildTimestampTest.java     |   6 +-
 .../org/apache/maven/model/WrapperProperties.java  |  47 +++++----
 maven-model/src/main/mdo/merger.vm                 |   6 +-
 maven-model/src/main/mdo/model-v3.vm               |   7 ++
 maven-model/src/main/mdo/reader-ex.vm              |   3 +-
 maven-model/src/main/mdo/reader.vm                 |   5 +-
 maven-model/src/main/mdo/transformer.vm            |  23 ++---
 maven-model/src/main/mdo/writer-ex.vm              |   8 +-
 maven-model/src/main/mdo/writer.vm                 |   6 +-
 .../apache/maven/settings/WrapperProperties.java   |  49 +++++----
 maven-settings/src/main/mdo/merger.vm              |   7 +-
 maven-settings/src/main/mdo/model-v3.vm            |  10 +-
 maven-settings/src/main/mdo/reader.vm              |   4 +-
 maven-settings/src/main/mdo/writer.vm              |   6 +-
 .../building/DefaultToolchainsBuilderTest.java     |  28 ++---
 .../maven/toolchain/model/WrapperProperties.java   |  49 +++++----
 maven-toolchain-model/src/main/mdo/merger.vm       |   7 +-
 maven-toolchain-model/src/main/mdo/model-v3.vm     |  10 +-
 maven-toolchain-model/src/main/mdo/reader.vm       |   4 +-
 maven-toolchain-model/src/main/mdo/writer.vm       |   6 +-
 34 files changed, 342 insertions(+), 170 deletions(-)

diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
index 0d0cf07f9..5e0e3808d 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
@@ -26,7 +26,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Optional;
-import java.util.Properties;
 
 import org.apache.maven.api.annotations.Experimental;
 import org.apache.maven.api.annotations.Nonnull;
@@ -57,12 +56,11 @@ public interface Session
     @Nonnull
     SessionData getData();
 
-    // TODO: investigate using Map<String, String> or Map<String, Object> for 
all properties in the new API
     @Nonnull
-    Properties getUserProperties();
+    Map<String, String> getUserProperties();
 
     @Nonnull
-    Properties getSystemProperties();
+    Map<String, String> getSystemProperties();
 
     /**
      * Returns the current maven version
diff --git a/api/maven-api-model/src/main/mdo/model.vm 
b/api/maven-api-model/src/main/mdo/model.vm
index 9b3c2382e..9240aeecd 100644
--- a/api/maven-api-model/src/main/mdo/model.vm
+++ b/api/maven-api-model/src/main/mdo/model.vm
@@ -51,6 +51,9 @@
           #set ( $dummy = $imports.add( "java.util.Collection" ) )
           #set ( $dummy = $imports.add( "java.util.List" ) )
           #set ( $dummy = $types.put( $field, "List<" + $field.to + ">" ) )
+      #elseif ( $field.type == "java.util.Properties" )
+          #set ( $dummy = $imports.add( "java.util.Map" ) )
+          #set ( $dummy = $types.put( $field, "Map<String, String>" ) )
       #elseif ( $field.type == "DOM" )
         #set ( $dummy = $imports.add( "org.apache.maven.api.xml.Dom" ) )
         #set ( $dummy = $types.put( $field, "Dom" ) )
diff --git a/api/maven-api-settings/src/main/mdo/model.vm 
b/api/maven-api-settings/src/main/mdo/model.vm
index 4eb6f4506..a547db349 100644
--- a/api/maven-api-settings/src/main/mdo/model.vm
+++ b/api/maven-api-settings/src/main/mdo/model.vm
@@ -47,10 +47,13 @@
     #set ( $dummy = $imports.add( 
"org.apache.maven.api.annotations.ThreadSafe" ) )
     #foreach ( $field in $allFields )
       #if ( $field.type == "java.util.List" )
-          #set ( $dummy = $imports.add( "java.util.ArrayList" ) )
-          #set ( $dummy = $imports.add( "java.util.Collection" ) )
-          #set ( $dummy = $imports.add( "java.util.List" ) )
-          #set ( $dummy = $types.put( $field, "List<" + $field.to + ">" ) )
+        #set ( $dummy = $imports.add( "java.util.ArrayList" ) )
+        #set ( $dummy = $imports.add( "java.util.Collection" ) )
+        #set ( $dummy = $imports.add( "java.util.List" ) )
+        #set ( $dummy = $types.put( $field, "List<" + $field.to + ">" ) )
+      #elseif ( $field.type == "java.util.Properties" )
+        #set ( $dummy = $imports.add( "java.util.Map" ) )
+        #set ( $dummy = $types.put( $field, "Map<String, String>" ) )
       #elseif ( $field.type == "DOM" )
         #set ( $dummy = $imports.add( "org.apache.maven.api.xml.Dom" ) )
         #set ( $dummy = $types.put( $field, "Dom" ) )
diff --git a/api/maven-api-toolchain/src/main/mdo/model.vm 
b/api/maven-api-toolchain/src/main/mdo/model.vm
index 4eb6f4506..a547db349 100644
--- a/api/maven-api-toolchain/src/main/mdo/model.vm
+++ b/api/maven-api-toolchain/src/main/mdo/model.vm
@@ -47,10 +47,13 @@
     #set ( $dummy = $imports.add( 
"org.apache.maven.api.annotations.ThreadSafe" ) )
     #foreach ( $field in $allFields )
       #if ( $field.type == "java.util.List" )
-          #set ( $dummy = $imports.add( "java.util.ArrayList" ) )
-          #set ( $dummy = $imports.add( "java.util.Collection" ) )
-          #set ( $dummy = $imports.add( "java.util.List" ) )
-          #set ( $dummy = $types.put( $field, "List<" + $field.to + ">" ) )
+        #set ( $dummy = $imports.add( "java.util.ArrayList" ) )
+        #set ( $dummy = $imports.add( "java.util.Collection" ) )
+        #set ( $dummy = $imports.add( "java.util.List" ) )
+        #set ( $dummy = $types.put( $field, "List<" + $field.to + ">" ) )
+      #elseif ( $field.type == "java.util.Properties" )
+        #set ( $dummy = $imports.add( "java.util.Map" ) )
+        #set ( $dummy = $types.put( $field, "Map<String, String>" ) )
       #elseif ( $field.type == "DOM" )
         #set ( $dummy = $imports.add( "org.apache.maven.api.xml.Dom" ) )
         #set ( $dummy = $types.put( $field, "Dom" ) )
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
index 2b4ed18cc..12af08d6e 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
@@ -28,7 +28,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-import java.util.Properties;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -180,16 +179,16 @@ public class DefaultSession extends AbstractSession
 
     @Nonnull
     @Override
-    public Properties getUserProperties()
+    public Map<String, String> getUserProperties()
     {
-        return mavenSession.getUserProperties();
+        return new PropertiesAsMap( mavenSession.getUserProperties() );
     }
 
     @Nonnull
     @Override
-    public Properties getSystemProperties()
+    public Map<String, String> getSystemProperties()
     {
-        return mavenSession.getSystemProperties();
+        return new PropertiesAsMap( mavenSession.getSystemProperties() );
     }
 
     @Nonnull
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/PropertiesAsMap.java 
b/maven-core/src/main/java/org/apache/maven/internal/impl/PropertiesAsMap.java
new file mode 100644
index 000000000..7eaa16a51
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/PropertiesAsMap.java
@@ -0,0 +1,113 @@
+package org.apache.maven.internal.impl;
+
+/*
+ * 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.
+ */
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+class PropertiesAsMap extends AbstractMap<String, String>
+{
+
+    private final Map<Object, Object> properties;
+
+    PropertiesAsMap( Map<Object, Object> properties )
+    {
+        this.properties = properties;
+    }
+
+    @Override
+    public Set<Entry<String, String>> entrySet()
+    {
+        return new AbstractSet<Entry<String, String>>()
+        {
+            @Override
+            public Iterator<Entry<String, String>> iterator()
+            {
+                Iterator<Entry<Object, Object>> iterator = 
properties.entrySet().iterator();
+                return new Iterator<Entry<String, String>>()
+                {
+                    Entry<Object, Object> next;
+
+                    @Override
+                    public boolean hasNext()
+                    {
+                        while ( next == null && iterator.hasNext() )
+                        {
+                            Entry<Object, Object> e = iterator.next();
+                            if ( PropertiesAsMap.matches( e ) )
+                            {
+                                next = e;
+                                break;
+                            }
+                        }
+                        return next != null;
+                    }
+
+                    @Override
+                    public Entry<String, String> next()
+                    {
+                        if ( next == null )
+                        {
+                            throw new NoSuchElementException();
+                        }
+                        return new Entry<String, String>()
+                        {
+                            @Override
+                            public String getKey()
+                            {
+                                return (String) next.getKey();
+                            }
+
+                            @Override
+                            public String getValue()
+                            {
+                                return (String) next.getValue();
+                            }
+
+                            @Override
+                            public String setValue( String value )
+                            {
+                                return (String) next.setValue( value );
+                            }
+                        };
+                    }
+                };
+            }
+
+            @Override
+            public int size()
+            {
+                return (int) properties.entrySet()
+                        .stream().filter( PropertiesAsMap::matches )
+                        .count();
+            }
+        };
+    }
+
+    private static boolean matches( Entry<Object, Object> entry )
+    {
+        return entry.getKey() instanceof String
+                && entry.getValue() instanceof String;
+    }
+}
diff --git 
a/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluatorV4.java
 
b/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluatorV4.java
index e4756e221..b0d1a865d 100644
--- 
a/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluatorV4.java
+++ 
b/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluatorV4.java
@@ -45,7 +45,7 @@ import 
org.codehaus.plexus.util.introspection.ReflectionValueExtractor;
  * <tr><td><code>session.*</code></td>         <td>(since Maven 
3)</td><td></td></tr>
  * <tr><td><code>localRepository</code></td>   <td></td>
  *                                             <td>{@link 
Session#getLocalRepository()}</td></tr>
- * <tr><td><code>reactorProjects</code></td>   <td></td>               
<td>{@link MavenSession#getProjects()}</td></tr>
+ * <tr><td><code>reactorProjects</code></td>   <td></td>               
<td>{@link Session#getProjects()}</td></tr>
  * <tr><td><code>project</code></td>           <td></td>
  *                                             <td>{@link 
Session#getCurrentProject()}</td></tr>
  * <tr><td><code>project.*</code></td>         <td></td>               
<td></td></tr>
@@ -402,7 +402,7 @@ public class PluginParameterExpressionEvaluatorV4
 
             if ( ( value == null ) && ( ( project != null ) && ( 
project.getModel().getProperties() != null ) ) )
             {
-                value = project.getModel().getProperties().getProperty( 
expression );
+                value = project.getModel().getProperties().get( expression );
             }
 
         }
diff --git 
a/maven-core/src/main/java/org/apache/maven/settings/SettingsUtils.java 
b/maven-core/src/main/java/org/apache/maven/settings/SettingsUtils.java
index 124dcf957..bbdd85e6b 100644
--- a/maven-core/src/main/java/org/apache/maven/settings/SettingsUtils.java
+++ b/maven-core/src/main/java/org/apache/maven/settings/SettingsUtils.java
@@ -117,7 +117,8 @@ public final class SettingsUtils
             profile.activation( activation.build() );
         }
 
-        profile.properties( modelProfile.getProperties() );
+        profile.properties( modelProfile.getProperties().entrySet().stream()
+                .collect( Collectors.toMap( e -> e.getKey().toString(), e -> 
e.getValue().toString() ) ) );
 
         List<org.apache.maven.model.Repository> repos = 
modelProfile.getRepositories();
         if ( repos != null )
diff --git 
a/maven-core/src/test/java/org/apache/maven/settings/SettingsUtilsTest.java 
b/maven-core/src/test/java/org/apache/maven/settings/SettingsUtilsTest.java
index bdbdad78a..6f59c3c35 100644
--- a/maven-core/src/test/java/org/apache/maven/settings/SettingsUtilsTest.java
+++ b/maven-core/src/test/java/org/apache/maven/settings/SettingsUtilsTest.java
@@ -21,7 +21,9 @@ package org.apache.maven.settings;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Random;
 
@@ -87,11 +89,11 @@ public class SettingsUtilsTest
                 .property( ap )
                 .os( ao )
                 .build();
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         int count = entropy.nextInt( 10 );
         for ( int i = 0; i < count; i++ )
         {
-            props.setProperty( "name" + Long.toHexString( entropy.nextLong() ),
+            props.put( "name" + Long.toHexString( entropy.nextLong() ),
                     "value" + Long.toHexString( entropy.nextLong() ) );
         }
         count = entropy.nextInt( 3 );
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java
index 20b3fc59c..1002f29ff 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java
@@ -24,7 +24,6 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 
 import javax.inject.Named;
 import javax.inject.Singleton;
@@ -64,7 +63,7 @@ public class DefaultInheritanceAssembler
                                           ModelProblemCollector problems )
     {
         Map<Object, Object> hints = new HashMap<>();
-        String childPath = child.getProperties().getProperty( 
CHILD_DIRECTORY_PROPERTY, child.getArtifactId() );
+        String childPath = child.getProperties().getOrDefault( 
CHILD_DIRECTORY_PROPERTY, child.getArtifactId() );
         hints.put( CHILD_DIRECTORY, childPath );
         hints.put( MavenModelMerger.CHILD_PATH_ADJUSTMENT, 
getChildPathAdjustment( child, parent, childPath ) );
         return merger.merge( child, parent, false, hints );
@@ -210,7 +209,7 @@ public class DefaultInheritanceAssembler
                                                   ModelBase target, ModelBase 
source, boolean sourceDominant,
                                                   Map<Object, Object> context )
         {
-            Properties merged = new Properties();
+            Map<String, String> merged = new HashMap<>();
             if ( sourceDominant )
             {
                 merged.putAll( target.getProperties() );
@@ -227,9 +226,9 @@ public class DefaultInheritanceAssembler
                                                      source.getLocation( 
"properties" ), sourceDominant ) );
         }
 
-        private void putAll( Map<Object, Object> s, Map<Object, Object> t, 
Object excludeKey )
+        private void putAll( Map<String, String> s, Map<String, String> t, 
Object excludeKey )
         {
-            for ( Map.Entry<Object, Object> e : t.entrySet() )
+            for ( Map.Entry<String, String> e : t.entrySet() )
             {
                 if ( !e.getKey().equals( excludeKey ) )
                 {
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/AbstractStringBasedModelInterpolator.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/AbstractStringBasedModelInterpolator.java
index 749033b14..0bebb3c8b 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/AbstractStringBasedModelInterpolator.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/AbstractStringBasedModelInterpolator.java
@@ -25,7 +25,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Properties;
+import java.util.Map;
 
 import javax.inject.Inject;
 
@@ -96,7 +96,7 @@ public abstract class AbstractStringBasedModelInterpolator
     protected List<ValueSource> createValueSources( final Model model, final 
File projectDir,
                                                     final ModelBuildingRequest 
config )
     {
-        Properties modelProperties = model.getProperties();
+        Map<String, String> modelProperties = model.getProperties();
 
         ValueSource projectPrefixValueSource = new PrefixedObjectValueSource( 
PROJECT_PREFIXES, model, false );
 
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/BuildTimestampValueSource.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/BuildTimestampValueSource.java
index c7ca30592..ee2b304bb 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/BuildTimestampValueSource.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/BuildTimestampValueSource.java
@@ -20,7 +20,7 @@ package org.apache.maven.model.interpolation;
  */
 
 import java.util.Date;
-import java.util.Properties;
+import java.util.Map;
 
 import org.codehaus.plexus.interpolation.AbstractValueSource;
 
@@ -29,7 +29,7 @@ class BuildTimestampValueSource
 {
     private final MavenBuildTimestamp mavenBuildTimestamp;
 
-    BuildTimestampValueSource( Date startTime, Properties properties )
+    BuildTimestampValueSource( Date startTime, Map<String, String> properties )
     {
         super( false );
         this.mavenBuildTimestamp = new MavenBuildTimestamp( startTime, 
properties );
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/MavenBuildTimestamp.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/MavenBuildTimestamp.java
index fcec526e5..e609c777f 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/MavenBuildTimestamp.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/MavenBuildTimestamp.java
@@ -22,7 +22,7 @@ package org.apache.maven.model.interpolation;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.GregorianCalendar;
-import java.util.Properties;
+import java.util.Map;
 import java.util.TimeZone;
 
 /**
@@ -49,9 +49,9 @@ public class MavenBuildTimestamp
         this( time, DEFAULT_BUILD_TIMESTAMP_FORMAT );
     }
 
-    public MavenBuildTimestamp( Date time, Properties properties )
+    public MavenBuildTimestamp( Date time, Map<String, String> properties )
     {
-        this( time, properties != null ? properties.getProperty( 
BUILD_TIMESTAMP_FORMAT_PROPERTY ) : null );
+        this( time, properties != null ? properties.get( 
BUILD_TIMESTAMP_FORMAT_PROPERTY ) : null );
     }
 
     public MavenBuildTimestamp( Date time, String timestampFormat )
diff --git 
a/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/AbstractModelInterpolatorTest.java
 
b/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/AbstractModelInterpolatorTest.java
index ff5259fbe..d55e97372 100644
--- 
a/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/AbstractModelInterpolatorTest.java
+++ 
b/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/AbstractModelInterpolatorTest.java
@@ -25,8 +25,10 @@ import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.TimeZone;
 
@@ -177,7 +179,7 @@ public abstract class AbstractModelInterpolatorTest
     public void 
testShouldNotThrowExceptionOnReferenceToValueContainingNakedExpression() throws 
Exception
     {
         Scm scm = Scm.newBuilder().connection( "${test}/somepath" ).build();
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         props.put( "test", "test" );
         Model model = Model.newBuilder().scm( scm ).properties( props 
).build();
 
@@ -316,8 +318,8 @@ public abstract class AbstractModelInterpolatorTest
         Properties context = new Properties();
         context.put( "env.HOME", "/path/to/home" );
 
-        Properties modelProperties = new Properties();
-        modelProperties.setProperty( "outputDirectory", "${env.HOME}" );
+        Map<String, String> modelProperties = new HashMap<>();
+        modelProperties.put( "outputDirectory", "${env.HOME}" );
 
         Model model = Model.newBuilder().properties( modelProperties ).build();
 
@@ -328,15 +330,15 @@ public abstract class AbstractModelInterpolatorTest
                 collector );
         assertProblemFree( collector );
 
-        assertEquals( "/path/to/home", out.getProperties().getProperty( 
"outputDirectory" ) );
+        assertEquals( "/path/to/home", out.getProperties().get( 
"outputDirectory" ) );
     }
 
     @Test
     public void envarExpressionThatEvaluatesToNullReturnsTheLiteralString() 
throws Exception
     {
 
-        Properties modelProperties = new Properties();
-        modelProperties.setProperty( "outputDirectory", 
"${env.DOES_NOT_EXIST}" );
+        Map<String, String> modelProperties = new HashMap<>();
+        modelProperties.put( "outputDirectory", "${env.DOES_NOT_EXIST}" );
 
         Model model = Model.newBuilder().properties( modelProperties ).build();
 
@@ -347,14 +349,14 @@ public abstract class AbstractModelInterpolatorTest
                 collector );
         assertProblemFree( collector );
 
-        assertEquals( out.getProperties().getProperty( "outputDirectory" ), 
"${env.DOES_NOT_EXIST}" );
+        assertEquals( out.getProperties().get( "outputDirectory" ), 
"${env.DOES_NOT_EXIST}" );
     }
 
     @Test
     public void expressionThatEvaluatesToNullReturnsTheLiteralString() throws 
Exception
     {
-        Properties modelProperties = new Properties();
-        modelProperties.setProperty( "outputDirectory", "${DOES_NOT_EXIST}" );
+        Map<String, String> modelProperties = new HashMap<>();
+        modelProperties.put( "outputDirectory", "${DOES_NOT_EXIST}" );
 
         Model model = Model.newBuilder().properties( modelProperties ).build();
 
@@ -365,7 +367,7 @@ public abstract class AbstractModelInterpolatorTest
                 collector );
         assertProblemFree( collector );
 
-        assertEquals( out.getProperties().getProperty( "outputDirectory" ), 
"${DOES_NOT_EXIST}" );
+        assertEquals( out.getProperties().get( "outputDirectory" ), 
"${DOES_NOT_EXIST}" );
     }
 
     @Test
@@ -420,9 +422,9 @@ public abstract class AbstractModelInterpolatorTest
     @Test
     public void testRecursiveExpressionCycleNPE() throws Exception
     {
-        Properties props = new Properties();
-        props.setProperty( "aa", "${bb}" );
-        props.setProperty( "bb", "${aa}" );
+        Map<String, String> props = new HashMap<>();
+        props.put( "aa", "${bb}" );
+        props.put( "bb", "${aa}" );
         DefaultModelBuildingRequest request = new 
DefaultModelBuildingRequest();
 
         Model model = Model.newBuilder().properties( props ).build();
@@ -438,8 +440,8 @@ public abstract class AbstractModelInterpolatorTest
     @Test
     public void testRecursiveExpressionCycleBaseDir() throws Exception
     {
-        Properties props = new Properties();
-        props.setProperty( "basedir", "${basedir}" );
+        Map<String, String> props = new HashMap<>();
+        props.put( "basedir", "${basedir}" );
         DefaultModelBuildingRequest request = new 
DefaultModelBuildingRequest();
 
         Model model = Model.newBuilder().properties( props ).build();
diff --git 
a/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/MavenBuildTimestampTest.java
 
b/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/MavenBuildTimestampTest.java
index 73ea3996b..005cc132e 100644
--- 
a/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/MavenBuildTimestampTest.java
+++ 
b/maven-model-builder/src/test/java/org/apache/maven/model/interpolation/MavenBuildTimestampTest.java
@@ -20,6 +20,8 @@ package org.apache.maven.model.interpolation;
  */
 
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 
 import org.junit.jupiter.api.Test;
@@ -31,8 +33,8 @@ public class MavenBuildTimestampTest
     @Test
     public void testMavenBuildTimestampUsesUTC()
     {
-        Properties interpolationProperties = new Properties();
-        interpolationProperties.setProperty( "maven.build.timestamp.format", 
"yyyyMMdd'T'HHmm'Z'" );
+        Map<String, String> interpolationProperties = new HashMap<>();
+        interpolationProperties.put( "maven.build.timestamp.format", 
"yyyyMMdd'T'HHmm'Z'" );
         MavenBuildTimestamp timestamp = new MavenBuildTimestamp( new Date(), 
interpolationProperties );
         String formattedTimestamp = timestamp.formattedTimestamp();
         assertTrue( formattedTimestamp.endsWith( "Z" ), "We expect the UTC 
marker at the end of the timestamp." );
diff --git 
a/maven-model/src/main/java/org/apache/maven/model/WrapperProperties.java 
b/maven-model/src/main/java/org/apache/maven/model/WrapperProperties.java
index 00a6dec17..e44996cdd 100644
--- a/maven-model/src/main/java/org/apache/maven/model/WrapperProperties.java
+++ b/maven-model/src/main/java/org/apache/maven/model/WrapperProperties.java
@@ -27,6 +27,7 @@ import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.Writer;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.InvalidPropertiesFormatException;
 import java.util.Map;
@@ -41,10 +42,10 @@ import java.util.function.Supplier;
 class WrapperProperties extends Properties
 {
 
-    final Supplier<Properties> getter;
+    final Supplier<Map<String, String>> getter;
     final Consumer<Properties> setter;
 
-    WrapperProperties( Supplier<Properties> getter, Consumer<Properties> 
setter )
+    WrapperProperties( Supplier<Map<String, String>> getter, 
Consumer<Properties> setter )
     {
         this.getter = getter;
         this.setter = setter;
@@ -53,25 +54,25 @@ class WrapperProperties extends Properties
     @Override
     public String getProperty( String key )
     {
-        return getter.get().getProperty( key );
+        return getter.get().get( key );
     }
 
     @Override
     public String getProperty( String key, String defaultValue )
     {
-        return getter.get().getProperty( key, defaultValue );
+        return getter.get().getOrDefault( key, defaultValue );
     }
 
     @Override
     public Enumeration<?> propertyNames()
     {
-        return getter.get().propertyNames();
+        return Collections.enumeration( getter.get().keySet() );
     }
 
     @Override
     public Set<String> stringPropertyNames()
     {
-        return getter.get().stringPropertyNames();
+        return getter.get().keySet();
     }
 
     @Override
@@ -101,19 +102,19 @@ class WrapperProperties extends Properties
     @Override
     public Enumeration<Object> keys()
     {
-        return getter.get().keys();
+        return Collections.enumeration( (Set) getter.get().keySet() );
     }
 
     @Override
     public Enumeration<Object> elements()
     {
-        return getter.get().elements();
+        return Collections.enumeration( (Collection) getter.get().values() );
     }
 
     @Override
     public boolean contains( Object value )
     {
-        return getter.get().contains( value );
+        return getter.get().containsKey( value != null ? value.toString() : 
null );
     }
 
     @Override
@@ -143,19 +144,19 @@ class WrapperProperties extends Properties
     @Override
     public Set<Object> keySet()
     {
-        return getter.get().keySet();
+        return (Set) getter.get().keySet();
     }
 
     @Override
     public Collection<Object> values()
     {
-        return getter.get().values();
+        return (Collection) getter.get().values();
     }
 
     @Override
     public Set<Map.Entry<Object, Object>> entrySet()
     {
-        return getter.get().entrySet();
+        return (Set) getter.get().entrySet();
     }
 
     @Override
@@ -177,7 +178,7 @@ class WrapperProperties extends Properties
     @Override
     public Object getOrDefault( Object key, Object defaultValue )
     {
-        return getter.get().getOrDefault( key, defaultValue );
+        return getter.get().getOrDefault( key, defaultValue != null ? 
defaultValue.toString() : null );
     }
 
     @Override
@@ -320,19 +321,25 @@ class WrapperProperties extends Properties
     @Override
     public void save( OutputStream out, String comments )
     {
-        getter.get().save( out, comments );
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.save( out, comments );
     }
 
     @Override
     public void store( Writer writer, String comments ) throws IOException
     {
-        getter.get().store( writer, comments );
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.store( writer, comments );
     }
 
     @Override
     public void store( OutputStream out, String comments ) throws IOException
     {
-        getter.get().store( out, comments );
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.store( out, comments );
     }
 
     @Override
@@ -344,13 +351,17 @@ class WrapperProperties extends Properties
     @Override
     public void storeToXML( OutputStream os, String comment ) throws 
IOException
     {
-        getter.get().storeToXML( os, comment );
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.storeToXML( os, comment );
     }
 
     @Override
     public void storeToXML( OutputStream os, String comment, String encoding ) 
throws IOException
     {
-        getter.get().storeToXML( os, comment, encoding );
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.storeToXML( os, comment, encoding );
     }
 
 }
diff --git a/maven-model/src/main/mdo/merger.vm 
b/maven-model/src/main/mdo/merger.vm
index e81c3baab..1394323f2 100644
--- a/maven-model/src/main/mdo/merger.vm
+++ b/maven-model/src/main/mdo/merger.vm
@@ -122,10 +122,10 @@ public class ${className}
       #elseif ( $field.type == "java.util.List" && $field.to == "String" && 
$field.multiplicity == "*" )
         builder.${field.name}( merge( target.get${capField}(), 
source.get${capField}(), sourceDominant, e -> e ) );
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-        Properties src = source.get${capField}();
+        Map<String, String> src = source.get${capField}();
         if ( !src.isEmpty() )
         {
-            Properties tgt = target.get${capField}();
+            Map<String, String> tgt = target.get${capField}();
             if ( tgt.isEmpty() )
             {
                 builder.${field.name}( src );
@@ -135,7 +135,7 @@ public class ${className}
             }
             else
             {
-                Properties merged = new Properties();
+                Map<String, String> merged = new HashMap<>();
                 merged.putAll( sourceDominant ? target.get${capField}() : 
source.get${capField}() );
                 merged.putAll( sourceDominant ? source.get${capField}() : 
target.get${capField}() );
                 builder.${field.name}( merged );
diff --git a/maven-model/src/main/mdo/model-v3.vm 
b/maven-model/src/main/mdo/model-v3.vm
index b6e33401b..e91bddbbe 100644
--- a/maven-model/src/main/mdo/model-v3.vm
+++ b/maven-model/src/main/mdo/model-v3.vm
@@ -197,6 +197,13 @@ public class ${class.name}
             update( getDelegate().with${cap}( ( ( Xpp3Dom ) ${field.name} 
).getDom() ) );
             ( ( Xpp3Dom ) ${field.name} ).setChildrenTracking( this::replace );
         }
+      #elseif( $field.type == "java.util.Properties" )
+        Map<String, String> map = ${field.name}.entrySet().stream()
+                .collect( Collectors.toMap( e -> e.getKey().toString(), e -> 
e.getValue().toString() ) );
+        if ( !Objects.equals( map, getDelegate().get${cap}() ) )
+        {
+            update( getDelegate().with${cap}( map ) );
+        }
       #else
         if ( !Objects.equals( ${field.name}, getDelegate().${pfx}${cap}() ) )
         {
diff --git a/maven-model/src/main/mdo/reader-ex.vm 
b/maven-model/src/main/mdo/reader-ex.vm
index 683d0a21e..49c3f6fd3 100644
--- a/maven-model/src/main/mdo/reader-ex.vm
+++ b/maven-model/src/main/mdo/reader-ex.vm
@@ -43,7 +43,6 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.Set;
 import org.apache.maven.api.annotations.Generated;
 import org.apache.maven.internal.xml.DomBuilder;
@@ -274,7 +273,7 @@ public class ${className}
                     ${classLcapName}.${field.name}( ${field.name} );
                     break;
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-                    Properties ${field.name} = new Properties();
+                    Map<String, String> ${field.name} = new HashMap<>();
                     locations = new HashMap<>();
                     while ( parser.nextTag() == XmlPullParser.START_TAG )
                     {
diff --git a/maven-model/src/main/mdo/reader.vm 
b/maven-model/src/main/mdo/reader.vm
index caa2f1059..d00e7c644 100644
--- a/maven-model/src/main/mdo/reader.vm
+++ b/maven-model/src/main/mdo/reader.vm
@@ -39,9 +39,10 @@ import java.io.Reader;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Properties;
+import java.util.Map;
 import java.util.Set;
 import org.apache.maven.api.annotations.Generated;
 import org.apache.maven.internal.xml.DomBuilder;
@@ -296,7 +297,7 @@ public class ${className}
                     ${classLcapName}.${field.name}( ${field.name} );
                     break;
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-                    Properties ${field.name} = new Properties();
+                    Map<String, String> ${field.name} = new HashMap<>();
                     while ( parser.nextTag() == XmlPullParser.START_TAG )
                     {
                         String key = parser.getName();
diff --git a/maven-model/src/main/mdo/transformer.vm 
b/maven-model/src/main/mdo/transformer.vm
index 8c7380b2b..1f7401124 100644
--- a/maven-model/src/main/mdo/transformer.vm
+++ b/maven-model/src/main/mdo/transformer.vm
@@ -107,23 +107,20 @@ public class ${className}
       #elseif ( $field.type == "java.util.List" && $field.to == "String" && 
$field.multiplicity == "*" )
         builder.${field.name}( transform( target.get${capField}(), 
this::transform ) );
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-        Properties props = target.get${capField}();
-        Properties newProps = null;
-        for ( Map.Entry<Object, Object> entry : props.entrySet() )
+        Map<String, String> props = target.get${capField}();
+        Map<String, String> newProps = null;
+        for ( Map.Entry<String, String> entry : props.entrySet() )
         {
-            if ( entry.getKey() instanceof String && entry.getValue() 
instanceof String )
+            String newVal = transform( entry.getValue() );
+            if ( newVal != null && newVal != entry.getValue() )
             {
-                String newVal = transform( ( String ) entry.getValue() );
-                if ( newVal != null && newVal != entry.getValue() )
+                if ( newProps == null )
                 {
-                    if ( newProps == null )
-                    {
-                        newProps = new Properties();
-                        newProps.putAll( props );
-                        builder.${field.name}( newProps );
-                    }
-                    newProps.put( ( String ) entry.getKey(), newVal );
+                    newProps = new HashMap<>();
+                    newProps.putAll( props );
+                    builder.${field.name}( newProps );
                 }
+                newProps.put( entry.getKey(), newVal );
             }
         }
       #elseif ( $field.to && $field.multiplicity == "1" )
diff --git a/maven-model/src/main/mdo/writer-ex.vm 
b/maven-model/src/main/mdo/writer-ex.vm
index de458ed3c..f4c1dbdd2 100644
--- a/maven-model/src/main/mdo/writer-ex.vm
+++ b/maven-model/src/main/mdo/writer-ex.vm
@@ -298,17 +298,17 @@ public class ${className}
         }
     }
 
-    private <T> void writeProperties( String tagName, Properties props, 
XmlSerializer serializer, InputLocationTracker locationTracker )
+    private <T> void writeProperties( String tagName, Map<String, String> 
props, XmlSerializer serializer, InputLocationTracker locationTracker )
             throws IOException
     {
         if ( props != null && !props.isEmpty() )
         {
             serializer.startTag( NAMESPACE, tagName );
             InputLocation location = locationTracker != null ? 
locationTracker.getLocation( tagName ) : null;
-            for ( Map.Entry<Object, Object> entry : props.entrySet() )
+            for ( Map.Entry<String, String> entry : props.entrySet() )
             {
-                String key = entry.getKey().toString();
-                writeTag( key, null, entry.getValue().toString(), serializer, 
null );
+                String key = entry.getKey();
+                writeTag( key, null, entry.getValue(), serializer, null );
                 writeLocationTracking( location, key, serializer );
             }
             serializer.endTag( NAMESPACE, tagName );
diff --git a/maven-model/src/main/mdo/writer.vm 
b/maven-model/src/main/mdo/writer.vm
index e2740f45a..c354d480f 100644
--- a/maven-model/src/main/mdo/writer.vm
+++ b/maven-model/src/main/mdo/writer.vm
@@ -273,15 +273,15 @@ public class ${className}
         }
     }
 
-    private <T> void writeProperties( String tagName, Properties props, 
XmlSerializer serializer )
+    private <T> void writeProperties( String tagName, Map<String, String> 
props, XmlSerializer serializer )
             throws IOException
     {
         if ( props != null && !props.isEmpty() )
         {
             serializer.startTag( NAMESPACE, tagName );
-            for ( Map.Entry<Object, Object> entry : props.entrySet() )
+            for ( Map.Entry<String, String> entry : props.entrySet() )
             {
-                writeTag( entry.getKey().toString(), null, 
entry.getValue().toString(), serializer );
+                writeTag( entry.getKey(), null, entry.getValue(), serializer );
             }
             serializer.endTag( NAMESPACE, tagName );
         }
diff --git 
a/maven-settings/src/main/java/org/apache/maven/settings/WrapperProperties.java 
b/maven-settings/src/main/java/org/apache/maven/settings/WrapperProperties.java
index cd3ef0b84..a84a4e36a 100644
--- 
a/maven-settings/src/main/java/org/apache/maven/settings/WrapperProperties.java
+++ 
b/maven-settings/src/main/java/org/apache/maven/settings/WrapperProperties.java
@@ -27,6 +27,7 @@ import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.Writer;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.InvalidPropertiesFormatException;
 import java.util.Map;
@@ -38,13 +39,13 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
-public class WrapperProperties extends Properties
+class WrapperProperties extends Properties
 {
 
-    final Supplier<Properties> getter;
+    final Supplier<Map<String, String>> getter;
     final Consumer<Properties> setter;
 
-    public WrapperProperties( Supplier<Properties> getter, 
Consumer<Properties> setter )
+    WrapperProperties( Supplier<Map<String, String>> getter, 
Consumer<Properties> setter )
     {
         this.getter = getter;
         this.setter = setter;
@@ -53,25 +54,25 @@ public class WrapperProperties extends Properties
     @Override
     public String getProperty( String key )
     {
-        return getter.get().getProperty( key );
+        return getter.get().get( key );
     }
 
     @Override
     public String getProperty( String key, String defaultValue )
     {
-        return getter.get().getProperty( key, defaultValue );
+        return getter.get().getOrDefault( key, defaultValue );
     }
 
     @Override
     public Enumeration<?> propertyNames()
     {
-        return getter.get().propertyNames();
+        return Collections.enumeration( getter.get().keySet() );
     }
 
     @Override
     public Set<String> stringPropertyNames()
     {
-        return getter.get().stringPropertyNames();
+        return getter.get().keySet();
     }
 
     @Override
@@ -101,19 +102,19 @@ public class WrapperProperties extends Properties
     @Override
     public Enumeration<Object> keys()
     {
-        return getter.get().keys();
+        return Collections.enumeration( (Set) getter.get().keySet() );
     }
 
     @Override
     public Enumeration<Object> elements()
     {
-        return getter.get().elements();
+        return Collections.enumeration( (Collection) getter.get().values() );
     }
 
     @Override
     public boolean contains( Object value )
     {
-        return getter.get().contains( value );
+        return getter.get().containsKey( value != null ? value.toString() : 
null );
     }
 
     @Override
@@ -143,19 +144,19 @@ public class WrapperProperties extends Properties
     @Override
     public Set<Object> keySet()
     {
-        return getter.get().keySet();
+        return (Set) getter.get().keySet();
     }
 
     @Override
     public Collection<Object> values()
     {
-        return getter.get().values();
+        return (Collection) getter.get().values();
     }
 
     @Override
     public Set<Map.Entry<Object, Object>> entrySet()
     {
-        return getter.get().entrySet();
+        return (Set) getter.get().entrySet();
     }
 
     @Override
@@ -177,7 +178,7 @@ public class WrapperProperties extends Properties
     @Override
     public Object getOrDefault( Object key, Object defaultValue )
     {
-        return getter.get().getOrDefault( key, defaultValue );
+        return getter.get().getOrDefault( key, defaultValue != null ? 
defaultValue.toString() : null );
     }
 
     @Override
@@ -320,19 +321,25 @@ public class WrapperProperties extends Properties
     @Override
     public void save( OutputStream out, String comments )
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.save( out, comments );
     }
 
     @Override
     public void store( Writer writer, String comments ) throws IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.store( writer, comments );
     }
 
     @Override
     public void store( OutputStream out, String comments ) throws IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.store( out, comments );
     }
 
     @Override
@@ -344,13 +351,17 @@ public class WrapperProperties extends Properties
     @Override
     public void storeToXML( OutputStream os, String comment ) throws 
IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.storeToXML( os, comment );
     }
 
     @Override
     public void storeToXML( OutputStream os, String comment, String encoding ) 
throws IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.storeToXML( os, comment, encoding );
     }
 
 }
diff --git a/maven-settings/src/main/mdo/merger.vm 
b/maven-settings/src/main/mdo/merger.vm
index e81c3baab..407ca8638 100644
--- a/maven-settings/src/main/mdo/merger.vm
+++ b/maven-settings/src/main/mdo/merger.vm
@@ -38,7 +38,6 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.Objects;
 import java.util.function.BinaryOperator;
 import java.util.function.Function;
@@ -122,10 +121,10 @@ public class ${className}
       #elseif ( $field.type == "java.util.List" && $field.to == "String" && 
$field.multiplicity == "*" )
         builder.${field.name}( merge( target.get${capField}(), 
source.get${capField}(), sourceDominant, e -> e ) );
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-        Properties src = source.get${capField}();
+        Map<String, String> src = source.get${capField}();
         if ( !src.isEmpty() )
         {
-            Properties tgt = target.get${capField}();
+            Map<String, String> tgt = target.get${capField}();
             if ( tgt.isEmpty() )
             {
                 builder.${field.name}( src );
@@ -135,7 +134,7 @@ public class ${className}
             }
             else
             {
-                Properties merged = new Properties();
+                Map<String, String> merged = new HashMap<>();
                 merged.putAll( sourceDominant ? target.get${capField}() : 
source.get${capField}() );
                 merged.putAll( sourceDominant ? source.get${capField}() : 
target.get${capField}() );
                 builder.${field.name}( merged );
diff --git a/maven-settings/src/main/mdo/model-v3.vm 
b/maven-settings/src/main/mdo/model-v3.vm
index fe38c72eb..0271ae33b 100644
--- a/maven-settings/src/main/mdo/model-v3.vm
+++ b/maven-settings/src/main/mdo/model-v3.vm
@@ -195,13 +195,17 @@ public class ${class.name}
 
     public void set${cap}( ${type} ${field.name} )
     {
-      #if ( $field.to != "String" && $field.type == "java.util.List" && 
$field.multiplicity == "*" )
+      #if ( $field.type == "DOM" )
+        delegate = getDelegate().with${cap}( ( ( Xpp3Dom ) ${field.name} 
).getDom() );
+      #elseif( $field.type == "java.util.Properties" )
+        Map<String, String> map = ${field.name}.entrySet().stream()
+                .collect( Collectors.toMap( e -> e.getKey().toString(), e -> 
e.getValue().toString() ) );
+        delegate = getDelegate().with${cap}( map );
+      #elseif ( $field.to != "String" && $field.type == "java.util.List" && 
$field.multiplicity == "*" )
         delegate = getDelegate().with${cap}(
                 ${field.name}.stream().map( c -> c.getDelegate() ).collect( 
Collectors.toList() ) );
       #elseif ( $field.to && $field.to != "String" )
         delegate = getDelegate().with${cap}( ${field.name}.getDelegate() );
-      #elseif ( $field.type == "DOM" )
-        delegate = getDelegate().with${cap}( ( ( Xpp3Dom ) ${field.name} 
).getDom() );
       #else
         delegate = getDelegate().with${cap}( ${field.name} );
       #end
diff --git a/maven-settings/src/main/mdo/reader.vm 
b/maven-settings/src/main/mdo/reader.vm
index a575b6534..44feeb56e 100644
--- a/maven-settings/src/main/mdo/reader.vm
+++ b/maven-settings/src/main/mdo/reader.vm
@@ -39,8 +39,10 @@ import java.io.Reader;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import org.apache.maven.api.annotations.Generated;
@@ -769,7 +771,7 @@ public class ${className}
                     ${classLcapName}.${field.name}( ${field.name} );
                     break;
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-                    Properties ${field.name} = new Properties();
+                    Map<String, String> ${field.name} = new HashMap<>();
                     while ( parser.nextTag() == XmlPullParser.START_TAG )
                     {
                         String key = parser.getName();
diff --git a/maven-settings/src/main/mdo/writer.vm 
b/maven-settings/src/main/mdo/writer.vm
index e2740f45a..c354d480f 100644
--- a/maven-settings/src/main/mdo/writer.vm
+++ b/maven-settings/src/main/mdo/writer.vm
@@ -273,15 +273,15 @@ public class ${className}
         }
     }
 
-    private <T> void writeProperties( String tagName, Properties props, 
XmlSerializer serializer )
+    private <T> void writeProperties( String tagName, Map<String, String> 
props, XmlSerializer serializer )
             throws IOException
     {
         if ( props != null && !props.isEmpty() )
         {
             serializer.startTag( NAMESPACE, tagName );
-            for ( Map.Entry<Object, Object> entry : props.entrySet() )
+            for ( Map.Entry<String, String> entry : props.entrySet() )
             {
-                writeTag( entry.getKey().toString(), null, 
entry.getValue().toString(), serializer );
+                writeTag( entry.getKey(), null, entry.getValue(), serializer );
             }
             serializer.endTag( NAMESPACE, tagName );
         }
diff --git 
a/maven-toolchain-builder/src/test/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilderTest.java
 
b/maven-toolchain-builder/src/test/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilderTest.java
index a64b66af3..48a1bb579 100644
--- 
a/maven-toolchain-builder/src/test/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilderTest.java
+++ 
b/maven-toolchain-builder/src/test/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilderTest.java
@@ -89,7 +89,7 @@ public class DefaultToolchainsBuilderTest
         ToolchainsBuildingRequest request = new 
DefaultToolchainsBuildingRequest();
         request.setUserToolchainsSource( new StringSource( "" ) );
 
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         props.put( "key", "user_value" );
         ToolchainModel toolchain = ToolchainModel.newBuilder()
                 .type( "TYPE" )
@@ -104,7 +104,7 @@ public class DefaultToolchainsBuilderTest
         assertNotNull( result.getEffectiveToolchains() );
         assertEquals( 1, 
result.getEffectiveToolchains().getToolchains().size() );
         assertEquals( "TYPE", 
result.getEffectiveToolchains().getToolchains().get(0).getType() );
-        assertEquals( "user_value", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().getProperty(
 "key" ) );
+        assertEquals( "user_value", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().get( "key" 
) );
         assertNotNull( result.getProblems() );
         assertEquals( 0, result.getProblems().size() );
     }
@@ -116,7 +116,7 @@ public class DefaultToolchainsBuilderTest
         ToolchainsBuildingRequest request = new 
DefaultToolchainsBuildingRequest();
         request.setGlobalToolchainsSource( new StringSource( "" ) );
 
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         props.put( "key", "global_value" );
         ToolchainModel toolchain = ToolchainModel.newBuilder()
                 .type( "TYPE" )
@@ -131,7 +131,7 @@ public class DefaultToolchainsBuilderTest
         assertNotNull( result.getEffectiveToolchains() );
         assertEquals( 1, 
result.getEffectiveToolchains().getToolchains().size() );
         assertEquals( "TYPE", 
result.getEffectiveToolchains().getToolchains().get(0).getType() );
-        assertEquals( "global_value", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().getProperty(
 "key" ) );
+        assertEquals( "global_value", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().get( "key" 
) );
         assertNotNull( result.getProblems() );
         assertEquals( 0, result.getProblems().size() );
     }
@@ -144,7 +144,7 @@ public class DefaultToolchainsBuilderTest
         request.setGlobalToolchainsSource( new StringSource( "" ) );
         request.setUserToolchainsSource( new StringSource( "" ) );
 
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         props.put( "key", "user_value" );
         ToolchainModel toolchain = ToolchainModel.newBuilder()
                 .type( "TYPE" )
@@ -154,7 +154,7 @@ public class DefaultToolchainsBuilderTest
                 .toolchains( Collections.singletonList( toolchain ) )
                 .build();
 
-        props = new Properties();
+        props = new HashMap<>();
         props.put( "key", "global_value" );
         toolchain = ToolchainModel.newBuilder()
                 .type( "TYPE" )
@@ -170,9 +170,9 @@ public class DefaultToolchainsBuilderTest
         assertNotNull( result.getEffectiveToolchains() );
         assertEquals( 2, 
result.getEffectiveToolchains().getToolchains().size() );
         assertEquals( "TYPE", 
result.getEffectiveToolchains().getToolchains().get(0).getType() );
-        assertEquals( "user_value", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().getProperty(
 "key" ) );
+        assertEquals( "user_value", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().get( "key" 
) );
         assertEquals( "TYPE", 
result.getEffectiveToolchains().getToolchains().get(1).getType() );
-        assertEquals( "global_value", 
result.getEffectiveToolchains().getToolchains().get(1).getProvides().getProperty(
 "key" ) );
+        assertEquals( "global_value", 
result.getEffectiveToolchains().getToolchains().get(1).getProvides().get( "key" 
) );
         assertNotNull( result.getProblems() );
         assertEquals( 0, result.getProblems().size() );
     }
@@ -222,7 +222,7 @@ public class DefaultToolchainsBuilderTest
         ToolchainsBuildingRequest request = new 
DefaultToolchainsBuildingRequest();
         request.setUserToolchainsSource( new StringSource( "" ) );
 
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         props.put( "key", "${env.testKey}" );
         Xpp3Dom configurationChild = new Xpp3Dom("jdkHome", "${env.testKey}", 
null, null, null);
         Xpp3Dom configuration = new Xpp3Dom("configuration", null, null, 
Collections.singletonList(configurationChild), null);
@@ -239,7 +239,7 @@ public class DefaultToolchainsBuilderTest
 
         ToolchainsBuildingResult result = toolchainBuilder.build( request );
         String interpolatedValue = "testValue";
-        assertEquals(interpolatedValue, 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().getProperty(
 "key" ) );
+        assertEquals(interpolatedValue, 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().get( "key" 
) );
         Xpp3Dom toolchainConfiguration = (Xpp3Dom) 
result.getEffectiveToolchains().getToolchains().get(0).getConfiguration();
         assertEquals(interpolatedValue, 
toolchainConfiguration.getChild("jdkHome").getValue());
         assertNotNull( result.getProblems() );
@@ -253,7 +253,7 @@ public class DefaultToolchainsBuilderTest
         ToolchainsBuildingRequest request = new 
DefaultToolchainsBuildingRequest();
         request.setUserToolchainsSource( new StringSource( "" ) );
 
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         props.put( "key", "${env.testNonExistingKey}" );
         ToolchainModel toolchain = ToolchainModel.newBuilder()
                 .type( "TYPE" )
@@ -266,7 +266,7 @@ public class DefaultToolchainsBuilderTest
         doReturn(persistedToolchains).when( toolchainsReader ).read( any( 
InputStream.class ), ArgumentMatchers.<String, Object>anyMap());
 
         ToolchainsBuildingResult result = toolchainBuilder.build( request );
-        assertEquals("${env.testNonExistingKey}", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().getProperty(
 "key" ) );
+        assertEquals("${env.testNonExistingKey}", 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().get( "key" 
) );
         assertNotNull( result.getProblems() );
         assertEquals( 0, result.getProblems().size() );
     }
@@ -278,7 +278,7 @@ public class DefaultToolchainsBuilderTest
         ToolchainsBuildingRequest request = new 
DefaultToolchainsBuildingRequest();
         request.setUserToolchainsSource( new StringSource( "" ) );
 
-        Properties props = new Properties();
+        Map<String, String> props = new HashMap<>();
         props.put( "key", "${env.testSpecialCharactersKey}" );
         ToolchainModel toolchain = ToolchainModel.newBuilder()
                 .type( "TYPE" )
@@ -292,7 +292,7 @@ public class DefaultToolchainsBuilderTest
 
         ToolchainsBuildingResult result = toolchainBuilder.build( request );
         String interpolatedValue = "<test&Value>";
-        assertEquals(interpolatedValue, 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().getProperty(
 "key" ) );
+        assertEquals(interpolatedValue, 
result.getEffectiveToolchains().getToolchains().get(0).getProvides().get( "key" 
) );
         assertNotNull( result.getProblems() );
         assertEquals( 0, result.getProblems().size() );
     }
diff --git 
a/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/WrapperProperties.java
 
b/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/WrapperProperties.java
index f3bc6c5ff..c8976c334 100644
--- 
a/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/WrapperProperties.java
+++ 
b/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/WrapperProperties.java
@@ -27,6 +27,7 @@ import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.Writer;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.InvalidPropertiesFormatException;
 import java.util.Map;
@@ -38,13 +39,13 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
-public class WrapperProperties extends Properties
+class WrapperProperties extends Properties
 {
 
-    final Supplier<Properties> getter;
+    final Supplier<Map<String, String>> getter;
     final Consumer<Properties> setter;
 
-    public WrapperProperties( Supplier<Properties> getter, 
Consumer<Properties> setter )
+    WrapperProperties( Supplier<Map<String, String>> getter, 
Consumer<Properties> setter )
     {
         this.getter = getter;
         this.setter = setter;
@@ -53,25 +54,25 @@ public class WrapperProperties extends Properties
     @Override
     public String getProperty( String key )
     {
-        return getter.get().getProperty( key );
+        return getter.get().get( key );
     }
 
     @Override
     public String getProperty( String key, String defaultValue )
     {
-        return getter.get().getProperty( key, defaultValue );
+        return getter.get().getOrDefault( key, defaultValue );
     }
 
     @Override
     public Enumeration<?> propertyNames()
     {
-        return getter.get().propertyNames();
+        return Collections.enumeration( getter.get().keySet() );
     }
 
     @Override
     public Set<String> stringPropertyNames()
     {
-        return getter.get().stringPropertyNames();
+        return getter.get().keySet();
     }
 
     @Override
@@ -101,19 +102,19 @@ public class WrapperProperties extends Properties
     @Override
     public Enumeration<Object> keys()
     {
-        return getter.get().keys();
+        return Collections.enumeration( (Set) getter.get().keySet() );
     }
 
     @Override
     public Enumeration<Object> elements()
     {
-        return getter.get().elements();
+        return Collections.enumeration( (Collection) getter.get().values() );
     }
 
     @Override
     public boolean contains( Object value )
     {
-        return getter.get().contains( value );
+        return getter.get().containsKey( value != null ? value.toString() : 
null );
     }
 
     @Override
@@ -143,19 +144,19 @@ public class WrapperProperties extends Properties
     @Override
     public Set<Object> keySet()
     {
-        return getter.get().keySet();
+        return (Set) getter.get().keySet();
     }
 
     @Override
     public Collection<Object> values()
     {
-        return getter.get().values();
+        return (Collection) getter.get().values();
     }
 
     @Override
     public Set<Map.Entry<Object, Object>> entrySet()
     {
-        return getter.get().entrySet();
+        return (Set) getter.get().entrySet();
     }
 
     @Override
@@ -177,7 +178,7 @@ public class WrapperProperties extends Properties
     @Override
     public Object getOrDefault( Object key, Object defaultValue )
     {
-        return getter.get().getOrDefault( key, defaultValue );
+        return getter.get().getOrDefault( key, defaultValue != null ? 
defaultValue.toString() : null );
     }
 
     @Override
@@ -320,19 +321,25 @@ public class WrapperProperties extends Properties
     @Override
     public void save( OutputStream out, String comments )
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.save( out, comments );
     }
 
     @Override
     public void store( Writer writer, String comments ) throws IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.store( writer, comments );
     }
 
     @Override
     public void store( OutputStream out, String comments ) throws IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.store( out, comments );
     }
 
     @Override
@@ -344,13 +351,17 @@ public class WrapperProperties extends Properties
     @Override
     public void storeToXML( OutputStream os, String comment ) throws 
IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.storeToXML( os, comment );
     }
 
     @Override
     public void storeToXML( OutputStream os, String comment, String encoding ) 
throws IOException
     {
-        throw new UnsupportedOperationException();
+        Properties props = new Properties();
+        props.putAll( getter.get() );
+        props.storeToXML( os, comment, encoding );
     }
 
 }
diff --git a/maven-toolchain-model/src/main/mdo/merger.vm 
b/maven-toolchain-model/src/main/mdo/merger.vm
index e81c3baab..407ca8638 100644
--- a/maven-toolchain-model/src/main/mdo/merger.vm
+++ b/maven-toolchain-model/src/main/mdo/merger.vm
@@ -38,7 +38,6 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.Objects;
 import java.util.function.BinaryOperator;
 import java.util.function.Function;
@@ -122,10 +121,10 @@ public class ${className}
       #elseif ( $field.type == "java.util.List" && $field.to == "String" && 
$field.multiplicity == "*" )
         builder.${field.name}( merge( target.get${capField}(), 
source.get${capField}(), sourceDominant, e -> e ) );
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-        Properties src = source.get${capField}();
+        Map<String, String> src = source.get${capField}();
         if ( !src.isEmpty() )
         {
-            Properties tgt = target.get${capField}();
+            Map<String, String> tgt = target.get${capField}();
             if ( tgt.isEmpty() )
             {
                 builder.${field.name}( src );
@@ -135,7 +134,7 @@ public class ${className}
             }
             else
             {
-                Properties merged = new Properties();
+                Map<String, String> merged = new HashMap<>();
                 merged.putAll( sourceDominant ? target.get${capField}() : 
source.get${capField}() );
                 merged.putAll( sourceDominant ? source.get${capField}() : 
target.get${capField}() );
                 builder.${field.name}( merged );
diff --git a/maven-toolchain-model/src/main/mdo/model-v3.vm 
b/maven-toolchain-model/src/main/mdo/model-v3.vm
index fe38c72eb..0271ae33b 100644
--- a/maven-toolchain-model/src/main/mdo/model-v3.vm
+++ b/maven-toolchain-model/src/main/mdo/model-v3.vm
@@ -195,13 +195,17 @@ public class ${class.name}
 
     public void set${cap}( ${type} ${field.name} )
     {
-      #if ( $field.to != "String" && $field.type == "java.util.List" && 
$field.multiplicity == "*" )
+      #if ( $field.type == "DOM" )
+        delegate = getDelegate().with${cap}( ( ( Xpp3Dom ) ${field.name} 
).getDom() );
+      #elseif( $field.type == "java.util.Properties" )
+        Map<String, String> map = ${field.name}.entrySet().stream()
+                .collect( Collectors.toMap( e -> e.getKey().toString(), e -> 
e.getValue().toString() ) );
+        delegate = getDelegate().with${cap}( map );
+      #elseif ( $field.to != "String" && $field.type == "java.util.List" && 
$field.multiplicity == "*" )
         delegate = getDelegate().with${cap}(
                 ${field.name}.stream().map( c -> c.getDelegate() ).collect( 
Collectors.toList() ) );
       #elseif ( $field.to && $field.to != "String" )
         delegate = getDelegate().with${cap}( ${field.name}.getDelegate() );
-      #elseif ( $field.type == "DOM" )
-        delegate = getDelegate().with${cap}( ( ( Xpp3Dom ) ${field.name} 
).getDom() );
       #else
         delegate = getDelegate().with${cap}( ${field.name} );
       #end
diff --git a/maven-toolchain-model/src/main/mdo/reader.vm 
b/maven-toolchain-model/src/main/mdo/reader.vm
index a575b6534..44feeb56e 100644
--- a/maven-toolchain-model/src/main/mdo/reader.vm
+++ b/maven-toolchain-model/src/main/mdo/reader.vm
@@ -39,8 +39,10 @@ import java.io.Reader;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import org.apache.maven.api.annotations.Generated;
@@ -769,7 +771,7 @@ public class ${className}
                     ${classLcapName}.${field.name}( ${field.name} );
                     break;
       #elseif ( $field.type == "java.util.Properties" && $field.to == "String" 
&& $field.multiplicity == "*" )
-                    Properties ${field.name} = new Properties();
+                    Map<String, String> ${field.name} = new HashMap<>();
                     while ( parser.nextTag() == XmlPullParser.START_TAG )
                     {
                         String key = parser.getName();
diff --git a/maven-toolchain-model/src/main/mdo/writer.vm 
b/maven-toolchain-model/src/main/mdo/writer.vm
index e2740f45a..c354d480f 100644
--- a/maven-toolchain-model/src/main/mdo/writer.vm
+++ b/maven-toolchain-model/src/main/mdo/writer.vm
@@ -273,15 +273,15 @@ public class ${className}
         }
     }
 
-    private <T> void writeProperties( String tagName, Properties props, 
XmlSerializer serializer )
+    private <T> void writeProperties( String tagName, Map<String, String> 
props, XmlSerializer serializer )
             throws IOException
     {
         if ( props != null && !props.isEmpty() )
         {
             serializer.startTag( NAMESPACE, tagName );
-            for ( Map.Entry<Object, Object> entry : props.entrySet() )
+            for ( Map.Entry<String, String> entry : props.entrySet() )
             {
-                writeTag( entry.getKey().toString(), null, 
entry.getValue().toString(), serializer );
+                writeTag( entry.getKey(), null, entry.getValue(), serializer );
             }
             serializer.endTag( NAMESPACE, tagName );
         }

Reply via email to