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 }