001package io.jstach.jstachio;
002
003import java.io.IOException;
004import java.io.OutputStream;
005import java.io.OutputStreamWriter;
006
007import io.jstach.jstache.JStacheConfig;
008import io.jstach.jstache.JStacheFlags;
009import io.jstach.jstache.JStacheType;
010
011/**
012 * A JStachio Template is a renderer that has template meta data.
013 * <p>
014 * Generated code implements this interface.
015 * <p>
016 * While many of the methods allow passing in custom Escapers care must be taken to choose
017 * a proper escaper that supports the original media type and charset of the template.
018 * There is currently no runtime checking that the escaper supports the template's media
019 * type and charset.
020 *
021 * @author agentgt
022 * @param <T> the model type
023 */
024public interface Template<T> extends Renderer<T>, TemplateInfo {
025
026        /**
027         * Renders the passed in model to an appendable like output.
028         * @param <A> output type
029         * @param <E> error type
030         * @param model a model assumed never to be <code>null</code>.
031         * @param appendable the appendable to write to.
032         * @throws E if there is an error writing to the output
033         * @apiNote if the eventual output is to be bytes use
034         * {@link #write(Object, io.jstach.jstachio.Output.EncodedOutput)} as it will leverage
035         * pre-encoding if the template has it.
036         */
037        @Override
038        public <A extends Output<E>, E extends Exception> A execute(T model, A appendable) throws E;
039
040        /**
041         * Renders the passed in model directly to a binary stream possibly leveraging
042         * pre-encoded parts of the template.
043         * @param <A> output type
044         * @param <E> error type
045         * @param model a model assumed never to be <code>null</code>.
046         * @param output to write to.
047         * @return the passed in output for convenience
048         * @throws E if an error occurs while writing to output
049         * @apiNote for performance and code generation reasons templates do not do validation
050         * that the encoded output has the correct charset.
051         */
052        default <A extends io.jstach.jstachio.Output.EncodedOutput<E>, E extends Exception> A write( //
053                        T model, //
054                        A output) throws E {
055                return execute(model, output);
056        }
057
058        /**
059         * Renders the passed in model directly to a binary stream using the
060         * {@link #templateCharset()} for encoding. If the template is pre-encoded the
061         * pre-encoded parts of the template will be written to the stream for performance
062         * otherwise an {@link OutputStreamWriter} will wrap the outputstream.
063         * @param model a model assumed never to be <code>null</code>.
064         * @param outputStream to write to.
065         * @throws IOException if an error occurs while writing to the outputStream
066         * @apiNote The stream will not be closed but might be flushed by this call.
067         * @see EncodedTemplate
068         */
069        default void write(T model, //
070                        OutputStream outputStream) throws IOException {
071                OutputStreamWriter ow = new OutputStreamWriter(outputStream, templateCharset());
072                execute(model, ow);
073                ow.flush();
074        }
075
076        /**
077         * Creates a template model pair.
078         * @param model never <code>null</code> model.
079         * @return executable template model pair.
080         */
081        default TemplateModel model(T model) {
082                return TemplateModel.of(this, model);
083        }
084
085        /**
086         * <strong>EXPERIMENTAL</strong> support of pre-encoded templates that have the static
087         * parts of the template already encoded into bytes. By default all
088         * {@link JStacheType#JSTACHIO} templates that are generated are of this type. To
089         * disable see {@link JStacheFlags.Flag#PRE_ENCODE_DISABLE}.
090         * <p>
091         * This interface is to support
092         * <a href="https://github.com/fizzed/rocker#near-zero-copy-rendering"> Rocker style
093         * near zero-copy rendering</a> where the static template parts are stored as byte
094         * arrays.
095         * <p>
096         * The passed in {@link OutputStream} <em>will only have
097         * {@link OutputStream#write(byte[])} called</em> thus an array or list of byte arrays
098         * can be accumulated to support almost zero-copy. <strong> Consequently absolutely no
099         * mutation of the byte arrays should happen as they could be reusable static parts of
100         * the template! </strong>
101         * <p>
102         * <em>Overall it is recommended that you do not use this interface unless you have an
103         * intimate knowledge of how your platform buffers data and have byte like access as
104         * current JMH benchmarking indicates that
105         * {@link String#getBytes(java.nio.charset.Charset)} is generally much faster for raw
106         * byte conversion albeit at the possible cost of increased memory. One should peform
107         * their own benchmarking to confirm using this interface is worth it. </em>
108         *
109         * @author agentgt
110         * @param <T> the model type
111         * @apiNote The passed in {@link OutputStream} <em>will only have
112         * {@link OutputStream#write(byte[])} called</em> and no mutation of the passed in
113         * byte array should happen downstream.
114         * @see JStacheFlags.Flag#PRE_ENCODE_DISABLE
115         */
116        public interface EncodedTemplate<T> extends Template<T> {
117
118                /**
119                 * Renders the passed in model directly to a binary stream leveraging pre-encoded
120                 * parts of the template. This <em>may</em> improve performance when rendering
121                 * UTF-8 to an OutputStream as some of the encoding is done in advance. Because
122                 * the encoding is done statically you cannot pass the charset in. The chosen
123                 * charset comes from {@link JStacheConfig#charset()}.
124                 * @param model a model assumed never to be <code>null</code>.
125                 * @param outputStream to write to.
126                 * @throws IOException if an error occurs while writing to the outputStream
127                 * @apiNote The stream will not be closed or flushed by this call.
128                 */
129                @Override
130                default void write(T model, //
131                                OutputStream outputStream) throws IOException {
132                        write(model, Output.of(outputStream, templateCharset()));
133                }
134
135                /**
136                 * Renders the passed in model directly to a binary stream leveraging pre-encoded
137                 * parts of the template. This <em>may</em> improve performance when rendering
138                 * UTF-8 to an OutputStream as some of the encoding is done in advance. Because
139                 * the encoding is done statically you cannot pass the charset in. The chosen
140                 * charset comes from {@link JStacheConfig#charset()}.
141                 * @param <A> output type
142                 * @param <E> error type
143                 * @param model a model assumed never to be <code>null</code>.
144                 * @param output to write to.
145                 * @return the passed in output for convenience
146                 * @throws E if an error occurs while writing to output
147                 */
148                @Override
149                public <A extends io.jstach.jstachio.Output.EncodedOutput<E>, E extends Exception> A write( //
150                                T model, //
151                                A output) throws E;
152
153                @Override
154                default TemplateModel model(T model) {
155                        return TemplateModel.of(this, model);
156                }
157
158        }
159
160}