All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax0.jamal.tools.IndexedPlaceHolders Maven / Gradle / Ivy

package javax0.jamal.tools;

import java.util.Arrays;

/**
 * Replace the placeholders to the actual values in strings. The placeholders are searched from left to right in the
 * string and are replaced one after the other. The string that gets into the place of the placeholder will not be
 * searched for additional placeholders.
 * 

* Placeholders are specified as strings and the replacement can be specified either as string or as a string supplier. * The supplier may throw an exception. */ public class IndexedPlaceHolders { private final GenericTrie trie = new GenericTrie<>(); /** * The simple implementation of the placeholder functionality that does not handle suppliers. This implementation * formally is the extension of the more complex one because and not the other way around. The whole hocus pocus * with the class extensions is to let two different {@link IndexedPlaceHolders#format(String, Value...)} replace()} * methods. One that throws exception and the other one that does not. * * @param keys the key of the placeholder * @return a placeholder implementation */ public static IndexedPlaceHolders with(String... keys) { return new IndexedPlaceHolders(keys); } private IndexedPlaceHolders(String... keys) { for (int i = 0; i < keys.length; i++) { this.trie.put(keys[i], i); } } public interface ThrowingStringSupplier { String get() throws Exception; } public static class Value { private String value; private final ThrowingStringSupplier supplier; public Value(final String value, final ThrowingStringSupplier supplier) { this.value = value; this.supplier = supplier; } public String get() throws Exception { if (value == null) { value = supplier.get(); } return value; } } public static Value value(String value) { return new Value(value, null); } public static Value value(ThrowingStringSupplier supplier) { return new Value(null, supplier); } public Value[] values(Value... vs) { return vs; } /** * Replace the placeholders in the string {@code format} with the values from the maps and return the result. *

* The placeholders and the values are stored in two maps. The keys in the maps are the placeholder strings, and the * values are used to get the string to replace the placeholders with. *

* One map contains strings as values. The other one contains string suppliers. These suppliers are NOT {@link * java.util.function.Supplier}s because they may throw an exception. Any of the suppliers are invoked only if there * is a need for them. This is an important feature, because it means that there will be no exception in case the * placeholder to be replaced by the result of the supplier is not used even if the supplier would throw an * exception. *

* Any of the suppliers are invoked at most once, when they are referenced the first time. After that the result is * stored in the string value map and used from there. Consecutive invocation of {@code format()} will use the * already calculated value. *

* Placeholder replacement starts from the left to the right and replacement texts are not scanned for placeholders. * For example: * *

{@code
     *   PlaceHolders.of("$a", "$b", "$b", "$a").format("$a $b")
     * }
*

* will become * *

{@code
     * "$b $a"
     * }
* * @param original the format string with the placeholders * @param values the values to replace the placeholders with * @return the formatted string * @throws Exception if one of the evaluated supplier throws exception */ public String format(final String original, Value... values) throws Exception { StringBuilder sb = new StringBuilder(original); int doneIndex = 0; while (true) { final var found = trie.find(sb, doneIndex); if (found.isPresent()) { final String actualValue = values[found.get().value].get(); sb.replace(found.get().start, found.get().end, actualValue); doneIndex = found.get().start + actualValue.length(); } else { break; } } return sb.toString(); } public String format(final String original, final String... values) throws Exception { return format(original, Arrays.stream(values).map(IndexedPlaceHolders::value).toArray(Value[]::new)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy