001package io.jstach.opt.dropwizard;
002
003import java.io.IOException;
004import java.io.OutputStream;
005import java.util.LinkedHashMap;
006import java.util.Locale;
007import java.util.Map;
008import java.util.ServiceLoader;
009
010import org.eclipse.jdt.annotation.Nullable;
011
012import io.dropwizard.views.common.View;
013import io.dropwizard.views.common.ViewRenderException;
014import io.dropwizard.views.common.ViewRenderer;
015import io.jstach.jstachio.JStachio;
016import io.jstach.jstachio.Output.EncodedOutput;
017import io.jstach.jstachio.spi.JStachioConfig;
018import io.jstach.jstachio.spi.JStachioExtension;
019import io.jstach.jstachio.spi.JStachioFactory;
020
021/**
022 *
023 * Dropwizard view support. Doing the below will automatically pick up JStachio
024 * <pre><code class="language-java">
025 * bootstrap.addBundle(new ViewBundle&lt;&gt;());
026 * </code> </pre> Or alternatively you can pass it directly:
027 * <pre><code class="language-java">
028 * JStachio jstachio = ...; // See JStachioFactory
029 * bootstrap.addBundle(new ViewBundle&lt;&gt;(new JStachioViewRenderer(jstachio)));
030 * </code> </pre>
031 *
032 * @author agentgt
033 */
034@SuppressWarnings("exports")
035public class JStachioViewRenderer implements ViewRenderer {
036
037        private @Nullable JStachio jstachio = null;
038
039        /**
040         * ServiceLoader will call this
041         */
042        public JStachioViewRenderer() {
043                // Empty on purpose (placate sonar)
044        }
045
046        /**
047         * Programmatically create the renderer with the given jstachio.
048         * @param jstachio a jstachio instance.
049         * @see JStachioFactory
050         */
051        public JStachioViewRenderer(JStachio jstachio) {
052                this.jstachio = jstachio;
053        }
054
055        @Override
056        public boolean isRenderable(View view) {
057                return view instanceof JStachioView;
058        }
059
060        @Override
061        public void render(View view, Locale locale, OutputStream output) throws IOException {
062                if (view instanceof JStachioView jv) {
063                        try (var out = EncodedOutput.of(output, jv.charset())) {
064                                jstachio().write(jv.model(), out);
065                        }
066                }
067                else {
068                        throw new ViewRenderException("Not a JStachioView: " + view);
069                }
070        }
071
072        /**
073         * Internal getter for the jstachio backing this renderer
074         * @return not null
075         * @throws NullPointerException if jstachio has not been set yet.
076         */
077        protected JStachio jstachio() {
078                var j = jstachio;
079                if (j == null) {
080                        throw new NullPointerException("JStachio has not been set!");
081                }
082                return j;
083        }
084
085        @Override
086        public void configure(Map<String, String> options) {
087                if (jstachio != null) {
088                        return;
089                }
090                if (options.isEmpty()) {
091                        jstachio = JStachio.of();
092                }
093                else {
094                        Map<String, String> resolved = new LinkedHashMap<>();
095                        for (var e : options.entrySet()) {
096                                if (e.getKey() != null && e.getValue() != null) {
097                                        resolved.put(getConfigurationKey() + "." + e.getKey(), e.getValue());
098                                }
099                        }
100                        resolved = Map.copyOf(resolved);
101                        JStachioConfig dropWizardConfig = resolved::get;
102                        /*
103                         * Hmm I wonder if we should add system properties here
104                         */
105                        var j = jstachio = JStachioFactory.builder() //
106                                        .add(dropWizardConfig) //
107                                        .add(ServiceLoader.load(JStachioExtension.class)) //
108                                        .build();
109
110                        JStachio.setStatic(() -> j);
111                }
112        }
113
114        @Override
115        public final String getConfigurationKey() {
116                return "jstachio";
117        }
118
119}