gnodet-bot commented on code in PR #13554:
URL: https://github.com/apache/maven/pull/13554#discussion_r4318669853


##########
impl/maven-utils/src/main/java/org/apache/maven/utils/StringInterpolator.java:
##########
@@ -0,0 +1,501 @@
+/*
+ * 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.utils;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
+
+/**
+ * Performs property substitution without depending on Maven services.
+ */
+public class StringInterpolator {
+
+    private static final char ESCAPE_CHAR = '\\';
+    private static final String DELIM_START = "${";
+    private static final String DELIM_STOP = "}";
+    private static final String MARKER = "$__";
+    private static final String OP_DEFAULT = ":-";
+    private static final String OP_ALTERNATE = ":+";
+
+    /**
+     * Interpolates map values, replacing unresolved placeholders with empty 
strings.
+     *
+     * @param properties the properties to interpolate
+     * @param callback the callback for values not present in the map
+     */
+    public void interpolate(Map<String, String> properties, 
UnaryOperator<String> callback) {
+        interpolate(properties, callback, null, true);
+    }
+
+    /**
+     * Interpolates map values.
+     *
+     * @param properties the properties to interpolate
+     * @param callback the callback for values not present in the map
+     * @param defaultsToEmpty whether unresolved placeholders become empty 
strings
+     */
+    public void interpolate(Map<String, String> properties, 
UnaryOperator<String> callback, boolean defaultsToEmpty) {
+        interpolate(properties, callback, null, defaultsToEmpty);
+    }
+
+    /**
+     * Interpolates a string, preserving unresolved placeholders.
+     *
+     * @param value the string to interpolate, possibly null
+     * @param callback the callback for replacement values
+     * @return the interpolated string
+     */
+    public String interpolate(String value, UnaryOperator<String> callback) {
+        return interpolate(value, callback, null, false);
+    }
+
+    /**
+     * Interpolates a string.
+     *
+     * @param value the string to interpolate, possibly null
+     * @param callback the callback for replacement values
+     * @param defaultsToEmpty whether unresolved placeholders become empty 
strings
+     * @return the interpolated string
+     */
+    public String interpolate(String value, UnaryOperator<String> callback, 
boolean defaultsToEmpty) {
+        return interpolate(value, callback, null, defaultsToEmpty);
+    }
+
+    /**
+     * Interpolates map values using a snapshot of the original properties.
+     *
+     * @param map the properties to update
+     * @param callback the optional resolver for values not present in the map
+     * @param postprocessor the optional transformation of resolved values
+     * @param defaultsToEmpty whether unresolved placeholders become empty 
strings
+     * @throws StringInterpolatorException if a recursive reference is 
encountered
+     */
+    public void interpolate(
+            Map<String, String> map,
+            UnaryOperator<String> callback,
+            BinaryOperator<String> postprocessor,
+            boolean defaultsToEmpty) {
+        Map<String, String> org = new HashMap<>(map);
+        for (String name : map.keySet()) {
+            map.compute(
+                    name,
+                    (k, value) -> interpolate(
+                            value,
+                            name,
+                            null,
+                            v -> {
+                                String r = org.get(v);
+                                if (r == null && callback != null) {
+                                    r = callback.apply(v);
+                                }
+                                return r;
+                            },
+                            postprocessor,
+                            defaultsToEmpty));
+        }
+    }
+
+    /**
+     * Interpolates a string using a callback to resolve placeholders.
+     *
+     * @param val the string to interpolate, possibly null
+     * @param callback the optional resolver for replacement values
+     * @param postprocessor the optional transformation of resolved values
+     * @param defaultsToEmpty whether unresolved placeholders become empty 
strings
+     * @return the interpolated string, or null for null input
+     * @throws StringInterpolatorException if a recursive reference is 
encountered
+     */
+    public String interpolate(
+            String val, UnaryOperator<String> callback, BinaryOperator<String> 
postprocessor, boolean defaultsToEmpty) {
+        return interpolate(val, null, null, callback, postprocessor, 
defaultsToEmpty);
+    }
+
+    /**
+     * Interpolates a string within an existing property-resolution context.
+     *
+     * @param val the string to interpolate, possibly null
+     * @param currentKey the property currently being resolved, or null
+     * @param cycleMap the active variable names, or null to start a new 
resolution
+     * @param callback the optional resolver for replacement values
+     * @param postprocessor the optional transformation of resolved values
+     * @param defaultsToEmpty whether unresolved placeholders become empty 
strings
+     * @return the interpolated string, or null for null input
+     * @throws StringInterpolatorException if a recursive reference is 
encountered
+     */
+    public String interpolate(
+            String val,
+            String currentKey,
+            Set<String> cycleMap,
+            UnaryOperator<String> callback,
+            BinaryOperator<String> postprocessor,
+            boolean defaultsToEmpty) {
+        return substVars(val, currentKey, cycleMap, null, callback, 
postprocessor, defaultsToEmpty);
+    }
+
+    /**
+     * Perform substitution on a property set
+     *
+     * @param properties the property set to perform substitution on
+     * @param callback Callback for substitution
+     */
+    public void performSubstitution(Map<String, String> properties, 
UnaryOperator<String> callback) {
+        performSubstitution(properties, callback, true);
+    }
+
+    /**
+     * Perform substitution on a property set
+     *
+     * @param properties the property set to perform substitution on
+     * @param callback the callback to obtain substitution values
+     * @param defaultsToEmptyString sets an empty string if a replacement 
value is not found, leaves intact otherwise
+     */
+    public void performSubstitution(
+            Map<String, String> properties, UnaryOperator<String> callback, 
boolean defaultsToEmptyString) {
+        Map<String, String> org = new HashMap<>(properties);
+        for (String name : properties.keySet()) {
+            properties.compute(
+                    name, (k, value) -> substVars(value, name, null, org, 
callback, null, defaultsToEmptyString));
+        }
+    }
+
+    /**
+     * <p>
+     * This method performs property variable substitution on the
+     * specified value. If the specified value contains the syntax
+     * {@code ${&lt;prop-name&gt;}}, where {@code &lt;prop-name&gt;}
+     * refers to either a configuration property or a system property,
+     * then the corresponding property value is substituted for the variable
+     * placeholder. Multiple variable placeholders may exist in the
+     * specified value as well as nested variable placeholders, which
+     * are substituted from innermost to outermost. Configuration
+     * properties override system properties.
+     * </p>
+     *
+     * @param val The string on which to perform property substitution.
+     * @param currentKey The key of the property being evaluated used to
+     *        detect cycles.
+     * @param cycleMap Map of variable references used to detect nested cycles.
+     * @param configProps Set of configuration properties.
+     * @return The value of the specified string after system property 
substitution.
+     * @throws StringInterpolatorException If there was a syntax error in the
+     *         property placeholder syntax or a recursive variable reference.
+     **/
+    public String substVars(String val, String currentKey, Set<String> 
cycleMap, Map<String, String> configProps) {
+        return substVars(val, currentKey, cycleMap, configProps, null);
+    }
+
+    /**
+     * <p>
+     * This method performs property variable substitution on the
+     * specified value. If the specified value contains the syntax
+     * {@code ${&lt;prop-name&gt;}}, where {@code &lt;prop-name&gt;}
+     * refers to either a configuration property or a system property,
+     * then the corresponding property value is substituted for the variable
+     * placeholder. Multiple variable placeholders may exist in the
+     * specified value as well as nested variable placeholders, which
+     * are substituted from innermost to outermost. Configuration
+     * properties override system properties.
+     * </p>
+     *
+     * @param val The string on which to perform property substitution.
+     * @param currentKey The key of the property being evaluated used to
+     *        detect cycles.
+     * @param cycleMap Map of variable references used to detect nested cycles.
+     * @param configProps Set of configuration properties.
+     * @param callback the callback to obtain substitution values
+     * @return The value of the specified string after system property 
substitution.
+     * @throws StringInterpolatorException If there was a syntax error in the
+     *         property placeholder syntax or a recursive variable reference.
+     **/
+    public String substVars(
+            String val,
+            String currentKey,
+            Set<String> cycleMap,
+            Map<String, String> configProps,
+            UnaryOperator<String> callback) {
+        return substVars(val, currentKey, cycleMap, configProps, callback, 
null, false);
+    }
+
+    /**
+     * <p>
+     * This method performs property variable substitution on the
+     * specified value. If the specified value contains the syntax
+     * {@code ${&lt;prop-name&gt;}}, where {@code &lt;prop-name&gt;}
+     * refers to either a configuration property or a system property,
+     * then the corresponding property value is substituted for the variable
+     * placeholder. Multiple variable placeholders may exist in the
+     * specified value as well as nested variable placeholders, which
+     * are substituted from innermost to outermost. Configuration
+     * properties override system properties.
+     * </p>
+     *
+     * @param val The string on which to perform property substitution.
+     * @param currentKey The key of the property being evaluated used to
+     *        detect cycles.
+     * @param cycleMap Map of variable references used to detect nested cycles.
+     * @param configProps Set of configuration properties.
+     * @param callback the callback to obtain substitution values
+     * @param defaultsToEmptyString sets an empty string if a replacement 
value is not found, leaves intact otherwise
+     * @return The value of the specified string after system property 
substitution.
+     * @throws IllegalArgumentException If there was a syntax error in the

Review Comment:
   💡 **Nit:** The static `substVars` documents `@throws 
IllegalArgumentException`, while the instance overloads document `@throws 
StringInterpolatorException`. Since `StringInterpolatorException extends 
IllegalArgumentException`, using the more specific type here would make the 
Javadoc consistent:
   
   ```suggestion
        * @throws StringInterpolatorException If there was a syntax error in the
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to