I’d recommend using the two argument StringTemplate.interpolate for this sort 
of use case. Process values as a stream and then feed to 
StringTemplate.interpolate(fragments, values).

static final StringTemplate.Processor<String, RuntimeException> HEXER = st -> {
    List<String> values = st.values()
            .stream()
            .map(v -> v instanceof Integer i ? "0x" + Integer.toHexString(i)
                                             : String.valueOf(v))
            .toList();
    return  StringTemplate.interpolate(st.fragments(), values);
 };

public static void main(String... args) {
    int x = 10, y = 20;
    System.out.println(HEXER."\{x} + \{y} = \{x + y}");
}


0xa + 0x14 = 0x1e


On Nov 27, 2023, at 1:00 PM, Tagir Valeev <[email protected]> wrote:

Hello!

As we expect that people will create custom template processors, we can 
simplify their lives.

First, it could be common to add a finisher transformation to an existing 
processor. I think that for many purposes it would be enough to use STR 
processor as a starter, and then create a custom domain object from the 
resulting string. This could be simplified if we add a method 'andThen' to the 
Processor interface:


default <RR> Processor<RR, E> andThen(Function<R, RR> finisher) {
    Objects.requireNonNull(finisher);
    return st -> finisher.apply(process(st));
}

Second, I think that many processors may want to keep string parts intact but 
handle embedded expressions in a special way, effectively replacing the default 
'String.valueOf' behavior of the standard STR processor. We can provide a way 
doing this, encapsulating all the ceremony. Something like this:


static StringTemplate.Processor<String, RuntimeException> 
withToString(Function<Object, String> toStringFunction) {
    Objects.requireNonNull(toStringFunction);
    return st -> {
        StringBuilder sb = new StringBuilder();
        Iterator<String> fragIter = st.fragments().iterator();

        for (Object value : st.values()) {
            sb.append(fragIter.next());
            sb.append(toStringFunction.apply(value));
        }

        sb.append(fragIter.next());
        return sb.toString();
    };
}

withToString(String::valueOf) should be equivalent to the STR template (in 
functionality, not in performance)

Now, one can easily build interesting things like:


StringTemplate.Processor<Pattern, RuntimeException> REGEXP =
   withToString(obj -> Pattern.quote(obj.toString())).andThen(Pattern::compile);

What do you think?

With best regards,
Tagir Valeev

Reply via email to