Template.java
package com.timtrense.template;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.Data;
import lombok.NonNull;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* A compiled text containing placeholders that are to be replaced by actual values
*
* @author Tim Trense
* @since 1.0
*/
@Data
/* The circular dependency is with TemplateBuilder, used from static process(templateText, context).
While not overly clean, this method lowers the learning curve for the library quite a bit
and reduces code complexities in the main use case of "just a quick template here" for users of this library.
So we accept this circ.dep. as a tradeoff for good developer experience.
*/
@SuppressFBWarnings("FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY")
public class Template {
private final @NonNull TemplatePart[] parts;
/**
* Instantiate from a {@link List}
*
* @param parts the parts of this template. On the given {@link List} {@link List#toArray(Object[]) toArray} will be called
*/
public Template(@NonNull List<@NonNull TemplatePart> parts) {
this.parts = parts.toArray(new TemplatePart[0]);
}
/**
* @param parts the parts of this template. The given array will be copied
*/
public Template(@NonNull TemplatePart @NonNull [] parts) {
this.parts = Arrays.copyOf(parts, parts.length);
}
/**
* Ad-Hoc compiles a {@link Template} from the given source text and
* uses the given {@link Context} to {@link #process(Context)} it.
*
* @param templateText the template source text
* @param context all placeholder values to put in combined with a locale setting
* @return the actual text
*/
public static String process(@NonNull String templateText, @NonNull Context context) {
Template template = TemplateBuilder.build(templateText);
return template.process(context);
}
/**
* Ad-Hoc compiles a {@link Template} from the given source text and
* creates a default {@link Context} to {@link #process(Context)} it.
*
* @param templateText the template source text
* @param values the placeholder values to put in
* @return the actual text
*/
public static String process(@NonNull String templateText, @NonNull Map<@NonNull String, Object> values) {
Template template = TemplateBuilder.build(templateText);
return template.process(values);
}
/**
* @return an immutable copy of all parts of this
*/
public @NonNull TemplatePart[] getParts() {
return Arrays.copyOf(parts, parts.length);
}
/**
* Build the actual text from replacing the contained placeholders for their respective actual values
*
* @param context the {@link Context} containing the necessary information to resolve the placeholders
* @return the actual resolved text
*/
@SuppressFBWarnings("MOM_MISLEADING_OVERLOAD_MODEL")
public String process(Context context) {
StringBuilder builder = new StringBuilder();
appendTo(context, builder);
return builder.toString();
}
/**
* Overloading for {@link #process(Context)}
*
* @param context the context to process all {@link TemplatePart TemplateParts}
* @param consumer destination for the output of {@link TemplatePart#process(Context)}
*/
public void process(Context context, Consumer<Object> consumer) {
for (TemplatePart p : parts) {
p.process(context, consumer);
}
}
/**
* Overloading for {@link #process(Context)}
*
* @param context the context to process all {@link TemplatePart TemplateParts}
* @param appendable destination for the output of {@link TemplatePart#process(Context)}
*/
public void appendTo(Context context, StringBuilder appendable) {
for (TemplatePart p : parts) {
p.append(context, appendable);
}
}
/**
* Overloading for {@link #process(Context)}
*
* @param context the context to process all {@link TemplatePart TemplateParts}
* @param appendable destination for the output of {@link TemplatePart#process(Context)}
*/
public void appendTo(Context context, StringBuffer appendable) {
for (TemplatePart p : parts) {
p.append(context, appendable);
}
}
/**
* Overloading for {@link #process(Context)}
*
* @param context the context to process all {@link TemplatePart TemplateParts}
* @param appendable destination for the output of {@link TemplatePart#process(Context)}
*/
public void appendTo(Context context, Appendable appendable) throws IOException {
for (TemplatePart p : parts) {
p.append(context, appendable);
}
}
/**
* Overloading for {@link #process(Context)}
*
* @param context the context to process all {@link TemplatePart TemplateParts}
* @param writer destination for the output of {@link TemplatePart#process(Context)}
*/
public void writeTo(Context context, Writer writer) throws IOException {
for (TemplatePart p : parts) {
p.writeTo(context, writer);
}
}
/**
* Overloading for {@link #process(Context)}
*
* @param context the context to process all {@link TemplatePart TemplateParts}
* @param outputStream destination for the output of {@link TemplatePart#process(Context)}
*/
public void writeTo(Context context, OutputStream outputStream) throws IOException {
for (TemplatePart p : parts) {
p.writeTo(context, outputStream);
}
}
/**
* Overloading for {@link #process(Context)}
*
* @param context the context to process all {@link TemplatePart TemplateParts}
* @param outputStream destination for the output of {@link TemplatePart#process(Context)}
* @param charset the charset to encode the output of {@link TemplatePart#process(Context)} with
*/
public void writeTo(Context context, OutputStream outputStream, Charset charset) throws IOException {
for (TemplatePart p : parts) {
p.writeTo(context, outputStream, charset);
}
}
/**
* Build the actual text from replacing the contained placeholders for their respective actual values
*
* @param values the values of a {@link Context} (constructed with otherwise default settings) containing the necessary information to resolve the placeholders
* @return the actual resolved text
*/
public String process(@NonNull Map<@NonNull String, Object> values) {
Context context = new Context(values);
return process(context);
}
}