1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.maven.api.services;
20
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.function.BiFunction;
27 import java.util.function.Function;
28
29 import org.apache.maven.api.Service;
30 import org.apache.maven.api.annotations.Experimental;
31 import org.apache.maven.api.annotations.Nonnull;
32 import org.apache.maven.api.annotations.Nullable;
33
34 /**
35 * The Interpolator service provides methods for variable substitution in strings and maps.
36 * It allows for the replacement of placeholders (e.g., ${variable}) with their corresponding values.
37 *
38 * @since 4.0.0
39 */
40 @Experimental
41 public interface Interpolator extends Service {
42
43 /**
44 * Interpolates the values in the given map using the provided callback function.
45 * This method defaults to setting empty strings for unresolved placeholders.
46 *
47 * @param properties The map containing key-value pairs to be interpolated.
48 * @param callback The function to resolve variable values not found in the map.
49 */
50 default void interpolate(@Nonnull Map<String, String> properties, @Nullable Function<String, String> callback) {
51 interpolate(properties, callback, null, true);
52 }
53
54 /**
55 * Interpolates the values in the given map using the provided callback function.
56 *
57 * @param map The map containing key-value pairs to be interpolated.
58 * @param callback The function to resolve variable values not found in the map.
59 * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings. If false, they are left unchanged.
60 */
61 default void interpolate(
62 @Nonnull Map<String, String> map, @Nullable Function<String, String> callback, boolean defaultsToEmpty) {
63 interpolate(map, callback, null, defaultsToEmpty);
64 }
65
66 /**
67 * Interpolates the values in the given map using the provided callback function.
68 *
69 * @param map The map containing key-value pairs to be interpolated.
70 * @param callback The function to resolve variable values not found in the map.
71 * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings. If false, they are left unchanged.
72 */
73 void interpolate(
74 @Nonnull Map<String, String> map,
75 @Nullable Function<String, String> callback,
76 @Nullable BiFunction<String, String, String> postprocessor,
77 boolean defaultsToEmpty);
78
79 /**
80 * Interpolates a single string value using the provided callback function.
81 * This method defaults to not replacing unresolved placeholders.
82 *
83 * @param val The string to be interpolated.
84 * @param callback The function to resolve variable values.
85 * @return The interpolated string, or null if the input was null.
86 */
87 @Nullable
88 default String interpolate(@Nullable String val, @Nullable Function<String, String> callback) {
89 return interpolate(val, callback, false);
90 }
91
92 /**
93 * Interpolates a single string value using the provided callback function.
94 *
95 * @param val The string to be interpolated.
96 * @param callback The function to resolve variable values.
97 * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings.
98 * @return The interpolated string, or null if the input was null.
99 */
100 @Nullable
101 default String interpolate(
102 @Nullable String val, @Nullable Function<String, String> callback, boolean defaultsToEmpty) {
103 return interpolate(val, callback, null, defaultsToEmpty);
104 }
105
106 /**
107 * Interpolates a single string value using the provided callback function.
108 *
109 * @param val The string to be interpolated.
110 * @param callback The function to resolve variable values.
111 * @param defaultsToEmpty If true, unresolved placeholders are replaced with empty strings.
112 * @return The interpolated string, or null if the input was null.
113 */
114 @Nullable
115 String interpolate(
116 @Nullable String val,
117 @Nullable Function<String, String> callback,
118 @Nullable BiFunction<String, String, String> postprocessor,
119 boolean defaultsToEmpty);
120
121 /**
122 * Creates a composite function from a collection of functions.
123 *
124 * @param functions A collection of functions, each taking a String as input and returning a String.
125 * @return A function that applies each function in the collection in order until a non-null result is found.
126 * If all functions return null, the composite function returns null.
127 *
128 * @throws NullPointerException if the input collection is null or contains null elements.
129 */
130 static Function<String, String> chain(Collection<? extends Function<String, String>> functions) {
131 return s -> {
132 for (Function<String, String> function : functions) {
133 String v = function.apply(s);
134 if (v != null) {
135 return v;
136 }
137 }
138 return null;
139 };
140 }
141
142 @SafeVarargs
143 static Function<String, String> chain(Function<String, String>... functions) {
144 return chain(List.of(functions));
145 }
146
147 /**
148 * Memoizes a given function that takes a String input and produces a String output.
149 * This method creates a new function that caches the results of the original function,
150 * improving performance for repeated calls with the same input.
151 *
152 * @param callback The original function to be memoized. It takes a String as input and returns a String.
153 * @return A new {@code Function<String, String>} that caches the results of the original function.
154 * If the original function returns null for a given input, null will be cached and returned for subsequent calls with the same input.
155 *
156 * @see Function
157 * @see Optional
158 * @see HashMap#computeIfAbsent(Object, Function)
159 */
160 static Function<String, String> memoize(Function<String, String> callback) {
161 Map<String, Optional<String>> cache = new HashMap<>();
162 return s -> cache.computeIfAbsent(s, v -> Optional.ofNullable(callback.apply(v)))
163 .orElse(null);
164 }
165 }