001package io.jstach.jstachio.context;
002
003import java.io.IOException;
004
005import io.jstach.jstache.JStacheConfig;
006import io.jstach.jstachio.JStachio;
007import io.jstach.jstachio.Output;
008import io.jstach.jstachio.Output.EncodedOutput;
009import io.jstach.jstachio.Template;
010
011/**
012 * A special JStachio that can render models with a loose typed context object bound to
013 * {@value ContextNode#CONTEXT_BINDING_NAME}.
014 *
015 * @see JStachio
016 * @see ContextNode
017 * @since 1.3.0
018 * @author agentgt
019 */
020public interface ContextJStachio extends JStachio {
021
022        /**
023         * Renders the passed in model <strong>with a context</strong>.
024         * @param <A> output type
025         * @param <E> error type
026         * @param model a model assumed never to be <code>null</code>.
027         * @param context context node bound to {@value ContextNode#CONTEXT_BINDING_NAME}.
028         * @param appendable the output to write to.
029         * @return the output passed in returned for convenience.
030         * @throws E if there is an error writing to the output
031         */
032        public <A extends Output<E>, E extends Exception> A execute(Object model, //
033                        ContextNode context, //
034                        A appendable) throws E;
035
036        /**
037         * Renders the passed in model <strong>with a context</strong> directly to a binary
038         * stream leveraging pre-encoded parts of the template. This <em>may</em> improve
039         * performance when rendering UTF-8 to an OutputStream as some of the encoding is done
040         * in advance. Because the encoding is done statically you cannot pass the charset in.
041         * The chosen charset comes from {@link JStacheConfig#charset()}.
042         * @param <A> output type
043         * @param <E> error type
044         * @param model a model assumed never to be <code>null</code>.
045         * @param context context node bound to {@value ContextNode#CONTEXT_BINDING_NAME}.
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         */
050        <A extends io.jstach.jstachio.Output.EncodedOutput<E>, E extends Exception> A write( //
051                        Object model, //
052                        ContextNode context, //
053                        A output) throws E;
054
055        /**
056         * Decorates a JStachio instance as a {@link ContextJStachio} if it is not already
057         * one.
058         * @param jstachio the instance to be wrapped.
059         * @return the passed in jstachio if it is already a {@link ContextJStachio} otherwise
060         * wraps the passed in instance.
061         */
062        public static ContextJStachio of(JStachio jstachio) {
063                if (jstachio instanceof ContextJStachio cj) {
064                        return cj;
065                }
066                return new ForwardingJStachio(jstachio);
067        }
068
069}
070
071record ForwardingJStachio(JStachio delegate) implements ContextJStachio {
072
073        @Override
074        public <A extends Output<E>, E extends Exception> A execute(Object model, A appendable) throws E {
075                return delegate.execute(model, appendable);
076        }
077
078        @Override
079        public <A extends EncodedOutput<E>, E extends Exception> A write(Object model, A output) throws E {
080                return delegate.write(model, output);
081        }
082
083        @Override
084        public Template<Object> findTemplate(Object model) throws Exception {
085                return delegate.findTemplate(model);
086        }
087
088        @Override
089        public boolean supportsType(Class<?> modelType) {
090                return delegate.supportsType(modelType);
091        }
092
093        @Override
094        public void execute(Object model, Appendable appendable) throws IOException {
095                delegate.execute(model, appendable);
096        }
097
098        @Override
099        public StringBuilder execute(Object model, StringBuilder sb) {
100                return delegate.execute(model, sb);
101        }
102
103        @Override
104        public String execute(Object model) {
105                return delegate.execute(model);
106        }
107
108        @Override
109        public <A extends Output<E>, E extends Exception> A execute(Object model, ContextNode context, A appendable)
110                        throws E {
111                var out = ContextAwareOutput.of(appendable, context);
112                return delegate.execute(model, out).getOutput();
113        }
114
115        @Override
116        public <A extends EncodedOutput<E>, E extends Exception> A write(Object model, ContextNode context, A output)
117                        throws E {
118                var out = ContextAwareOutput.of(output, context);
119                return delegate.write(model, out).getOutput();
120        }
121
122}