[MNG-7604] Removal of pom.* interpolation makes some older plugins defunct (#1137)

This partially reverts commit b2a21f12f8a0598dd4a286177e340bda0148e9ba.

Co-authored-by: Maarten Mulders <mthmulders@users.noreply.github.com>
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java
index b9b68a7..498e3e6 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java
@@ -53,14 +53,19 @@
     int VALIDATION_LEVEL_MAVEN_3_0 = 30;
 
     /**
-     * Denotes validation as performed by Maven 3.1. This validation level is meant for new projects.
+     * Denotes validation as performed by Maven 3.1. This validation level is meant for existing projects.
      */
     int VALIDATION_LEVEL_MAVEN_3_1 = 31;
 
     /**
+     * Denotes validation as performed by Maven 4.0. This validation level is meant for new projects.
+     */
+    int VALIDATION_LEVEL_MAVEN_4_0 = 40;
+
+    /**
      * Denotes strict validation as recommended by the current Maven version.
      */
-    int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_3_1;
+    int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_4_0;
 
     /**
      * Gets the file model to build (with profile activation).
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 37e7b02..d7b9adb 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
@@ -23,6 +23,7 @@
 import java.io.File;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -38,6 +39,7 @@
 import org.codehaus.plexus.interpolation.AbstractValueSource;
 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
 import org.codehaus.plexus.interpolation.MapBasedValueSource;
+import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
 import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
 import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
@@ -50,7 +52,10 @@
  * @author jdcasey Created on Feb 3, 2005
  */
 public abstract class AbstractStringBasedModelInterpolator implements ModelInterpolator {
-    private static final List<String> PROJECT_PREFIXES = Collections.singletonList("project.");
+    private static final String PREFIX_PROJECT = "project.";
+    private static final String PREFIX_POM = "pom.";
+    private static final List<String> PROJECT_PREFIXES_3_1 = Arrays.asList(PREFIX_POM, PREFIX_PROJECT);
+    private static final List<String> PROJECT_PREFIXES_4_0 = Collections.singletonList(PREFIX_PROJECT);
 
     private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
 
@@ -95,14 +100,40 @@
         return new org.apache.maven.model.Model(interpolateModel(model.getDelegate(), projectDir, request, problems));
     }
 
+    protected List<String> getProjectPrefixes(ModelBuildingRequest config) {
+        return config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0
+                ? PROJECT_PREFIXES_4_0
+                : PROJECT_PREFIXES_3_1;
+    }
+
     protected List<ValueSource> createValueSources(
-            final Model model, final File projectDir, final ModelBuildingRequest config) {
+            final Model model,
+            final File projectDir,
+            final ModelBuildingRequest config,
+            ModelProblemCollector problems) {
         Map<String, String> modelProperties = model.getProperties();
 
-        ValueSource projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES, model, false);
+        ValueSource projectPrefixValueSource;
+        ValueSource prefixlessObjectBasedValueSource;
+        if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0) {
+            projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_4_0, model, false);
+            prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
+        } else {
+            projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_3_1, model, false);
+            if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
+                projectPrefixValueSource =
+                        new ProblemDetectingValueSource(projectPrefixValueSource, PREFIX_POM, PREFIX_PROJECT, problems);
+            }
+
+            prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
+            if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
+                prefixlessObjectBasedValueSource =
+                        new ProblemDetectingValueSource(prefixlessObjectBasedValueSource, "", PREFIX_PROJECT, problems);
+            }
+        }
 
         // NOTE: Order counts here!
-        List<ValueSource> valueSources = new ArrayList<>(8);
+        List<ValueSource> valueSources = new ArrayList<>(9);
 
         if (projectDir != null) {
             ValueSource basedirValueSource = new PrefixedValueSourceWrapper(
@@ -115,7 +146,7 @@
                             return null;
                         }
                     },
-                    PROJECT_PREFIXES,
+                    getProjectPrefixes(config),
                     true);
             valueSources.add(basedirValueSource);
 
@@ -133,7 +164,7 @@
                             return null;
                         }
                     },
-                    PROJECT_PREFIXES,
+                    getProjectPrefixes(config),
                     false);
             valueSources.add(baseUriValueSource);
             valueSources.add(new BuildTimestampValueSource(config.getBuildStartTime(), modelProperties));
@@ -151,7 +182,7 @@
                         return null;
                     }
                 },
-                PROJECT_PREFIXES));
+                getProjectPrefixes(config)));
 
         valueSources.add(projectPrefixValueSource);
 
@@ -168,6 +199,8 @@
             }
         });
 
+        valueSources.add(prefixlessObjectBasedValueSource);
+
         return valueSources;
     }
 
@@ -176,14 +209,13 @@
         List<InterpolationPostProcessor> processors = new ArrayList<>(2);
         if (projectDir != null) {
             processors.add(new PathTranslatingPostProcessor(
-                    PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS,
-                    projectDir, pathTranslator));
+                    getProjectPrefixes(config), TRANSLATED_PATH_EXPRESSIONS, projectDir, pathTranslator));
         }
         processors.add(new UrlNormalizingPostProcessor(urlNormalizer));
         return processors;
     }
 
-    protected RecursionInterceptor createRecursionInterceptor() {
-        return new PrefixAwareRecursionInterceptor(PROJECT_PREFIXES);
+    protected RecursionInterceptor createRecursionInterceptor(ModelBuildingRequest config) {
+        return new PrefixAwareRecursionInterceptor(getProjectPrefixes(config));
     }
 }
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/ProblemDetectingValueSource.java b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/ProblemDetectingValueSource.java
new file mode 100644
index 0000000..53fabd2
--- /dev/null
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/ProblemDetectingValueSource.java
@@ -0,0 +1,76 @@
+/*
+ * 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.interpolation;
+
+import java.util.List;
+
+import org.apache.maven.model.building.ModelProblem.Severity;
+import org.apache.maven.model.building.ModelProblem.Version;
+import org.apache.maven.model.building.ModelProblemCollector;
+import org.apache.maven.model.building.ModelProblemCollectorRequest;
+import org.codehaus.plexus.interpolation.ValueSource;
+
+/**
+ * Wraps another value source and intercepts interpolated expressions, checking for problems.
+ *
+ * @author Benjamin Bentmann
+ */
+class ProblemDetectingValueSource implements ValueSource {
+
+    private final ValueSource valueSource;
+
+    private final String bannedPrefix;
+
+    private final String newPrefix;
+
+    private final ModelProblemCollector problems;
+
+    ProblemDetectingValueSource(
+            ValueSource valueSource, String bannedPrefix, String newPrefix, ModelProblemCollector problems) {
+        this.valueSource = valueSource;
+        this.bannedPrefix = bannedPrefix;
+        this.newPrefix = newPrefix;
+        this.problems = problems;
+    }
+
+    @Override
+    public Object getValue(String expression) {
+        Object value = valueSource.getValue(expression);
+
+        if (value != null && expression.startsWith(bannedPrefix)) {
+            String msg = "The expression ${" + expression + "} is deprecated.";
+            if (newPrefix != null && newPrefix.length() > 0) {
+                msg += " Please use ${" + newPrefix + expression.substring(bannedPrefix.length()) + "} instead.";
+            }
+            problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.V20).setMessage(msg));
+        }
+
+        return value;
+    }
+
+    @Override
+    public List getFeedback() {
+        return valueSource.getFeedback();
+    }
+
+    @Override
+    public void clearFeedback() {
+        valueSource.clearFeedback();
+    }
+}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/StringVisitorModelInterpolator.java b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/StringVisitorModelInterpolator.java
index f2a511c..ae1d617 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/StringVisitorModelInterpolator.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/StringVisitorModelInterpolator.java
@@ -64,10 +64,10 @@
     @Override
     public Model interpolateModel(
             Model model, File projectDir, ModelBuildingRequest config, ModelProblemCollector problems) {
-        List<? extends ValueSource> valueSources = createValueSources(model, projectDir, config);
+        List<? extends ValueSource> valueSources = createValueSources(model, projectDir, config, problems);
         List<? extends InterpolationPostProcessor> postProcessors = createPostProcessors(model, projectDir, config);
 
-        InnerInterpolator innerInterpolator = createInterpolator(valueSources, postProcessors, problems);
+        InnerInterpolator innerInterpolator = createInterpolator(valueSources, postProcessors, problems, config);
 
         return new MavenTransformer(innerInterpolator::interpolate).visit(model);
     }
@@ -75,7 +75,8 @@
     private InnerInterpolator createInterpolator(
             List<? extends ValueSource> valueSources,
             List<? extends InterpolationPostProcessor> postProcessors,
-            final ModelProblemCollector problems) {
+            final ModelProblemCollector problems,
+            ModelBuildingRequest config) {
         final Map<String, String> cache = new HashMap<>();
         final StringSearchInterpolator interpolator = new StringSearchInterpolator();
         interpolator.setCacheAnswers(true);
@@ -85,7 +86,7 @@
         for (InterpolationPostProcessor postProcessor : postProcessors) {
             interpolator.addPostProcessor(postProcessor);
         }
-        final RecursionInterceptor recursionInterceptor = createRecursionInterceptor();
+        final RecursionInterceptor recursionInterceptor = createRecursionInterceptor(config);
         return value -> {
             if (value != null && value.contains("${")) {
                 String c = cache.get(value);
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 44a2a59..f5a0524 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
@@ -487,19 +487,47 @@
     @Test
     public void shouldIgnorePropertiesWithPomPrefix() throws Exception {
         final String orgName = "MyCo";
-        final String expectedName = "${pom.organization.name} Tools";
+        final String uninterpolatedName = "${pom.organization.name} Tools";
+        final String interpolatedName = uninterpolatedName;
 
         Model model = Model.newBuilder()
-                .name(expectedName)
+                .name(uninterpolatedName)
                 .organization(Organization.newBuilder().name(orgName).build())
                 .build();
 
         ModelInterpolator interpolator = createInterpolator();
         SimpleProblemCollector collector = new SimpleProblemCollector();
-        Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
+        Model out = interpolator.interpolateModel(
+                model,
+                null,
+                createModelBuildingRequest(context).setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0),
+                collector);
 
         assertCollectorState(0, 0, 0, collector);
-        assertEquals(out.getName(), expectedName);
+        assertEquals(interpolatedName, out.getName());
+    }
+
+    @Test
+    public void shouldWarnPropertiesWithPomPrefix() throws Exception {
+        final String orgName = "MyCo";
+        final String uninterpolatedName = "${pom.organization.name} Tools";
+        final String interpolatedName = "MyCo Tools";
+
+        Model model = Model.newBuilder()
+                .name(uninterpolatedName)
+                .organization(Organization.newBuilder().name(orgName).build())
+                .build();
+
+        ModelInterpolator interpolator = createInterpolator();
+        SimpleProblemCollector collector = new SimpleProblemCollector();
+        Model out = interpolator.interpolateModel(
+                model,
+                null,
+                createModelBuildingRequest(context).setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1),
+                collector);
+
+        assertCollectorState(0, 0, 1, collector);
+        assertEquals(interpolatedName, out.getName());
     }
 
     protected abstract ModelInterpolator createInterpolator() throws Exception;