001package io.jstach.opt.spring.webmvc; 002 003import java.nio.charset.Charset; 004import java.nio.charset.StandardCharsets; 005import java.util.Map; 006 007import org.springframework.http.MediaType; 008import org.springframework.web.servlet.View; 009import org.springframework.web.servlet.view.RedirectView; 010 011import io.jstach.jstache.JStache; 012import io.jstach.jstache.JStacheInterfaces; 013import io.jstach.jstachio.JStachio; 014import jakarta.servlet.http.HttpServletRequest; 015import jakarta.servlet.http.HttpServletResponse; 016 017/** 018 * Another way to use JStachio with Spring MVC is to have models implement Springs 019 * {@link View} interface. You can enforce that your models implement this interface with 020 * {@link JStacheInterfaces}. 021 * <p> 022 * The model will use the static jstachio singleton that will be the spring one. 023 * <p> 024 * This approach has pros and cons. It makes your models slightly coupled to Spring MVC 025 * but allows you to return different views if say you had to redirect on some inputs 026 * ({@link RedirectView}). 027 * 028 * @author agentgt 029 * 030 */ 031public interface JStachioModelView extends View { 032 033 /** 034 * The default media type is "<code>text/html; charset=UTF-8</code>". 035 */ 036 @SuppressWarnings("exports") 037 static final MediaType DEFAULT_MEDIA_TYPE = new MediaType(MediaType.TEXT_HTML, StandardCharsets.UTF_8); 038 039 @SuppressWarnings("exports") 040 @Override 041 default void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) 042 throws Exception { 043 044 String contentType = getContentType(); 045 response.setContentType(contentType); 046 Charset charset = getMediaType().getCharset(); 047 if (charset == null) { 048 charset = StandardCharsets.UTF_8; 049 } 050 try (var o = response.getOutputStream()) { 051 var bo = jstachio().write(model(), new ByteCountOutput(o, charset)); 052 long length = bo.size(); 053 if (length < Integer.MAX_VALUE) { 054 response.setContentLength((int) bo.size()); 055 } 056 } 057 058 } 059 060 /** 061 * Returns the jstachio singleton by default. 062 * @return stachio singleton by default. 063 * @see JStachio#setStatic(java.util.function.Supplier) 064 */ 065 default JStachio jstachio() { 066 return JStachio.of(); 067 } 068 069 /** 070 * The model to be rendered by {@link #jstachio()}. 071 * @return model defaulting to <code>this</code> instance. 072 */ 073 default Object model() { 074 return this; 075 } 076 077 @Override 078 default String getContentType() { 079 return getMediaType().toString(); 080 } 081 082 /** 083 * The media type for this view. The default is 084 * "<code>text/html; charset=UTF-8</code>". 085 * @return the media type 086 */ 087 @SuppressWarnings("exports") 088 default MediaType getMediaType() { 089 return DEFAULT_MEDIA_TYPE; 090 } 091 092 /** 093 * Creates a spring view from a model with content type: 094 * "<code>text/html; charset=UTF-8</code>". 095 * @param model an instance of a class annotated with {@link JStache}. 096 * @return view ready for rendering 097 */ 098 public static JStachioModelView of(Object model) { 099 return of(model, MediaType.TEXT_HTML.toString()); 100 } 101 102 /** 103 * Creates a spring view from a model. 104 * @param model an instance of a class annotated with {@link JStache}. 105 * @param contentType See {@link #getContentType()} 106 * @return view ready for rendering 107 */ 108 public static JStachioModelView of(Object model, String contentType) { 109 MediaType mediaType = MediaType.parseMediaType(contentType); 110 return JStachioModelView.of(model, mediaType); 111 } 112 113 /** 114 * Creates a spring view from a model. 115 * @param model an instance of a class annotated with {@link JStache}. 116 * @param mediaType the mediaType 117 * @return view ready for rendering 118 */ 119 static JStachioModelView of(Object model, @SuppressWarnings("exports") MediaType mediaType) { 120 /* 121 * TODO potentially make this public on the next minor version release. 122 */ 123 return new JStachioModelView() { 124 @Override 125 public Object model() { 126 return model; 127 } 128 129 @Override 130 public MediaType getMediaType() { 131 return mediaType; 132 } 133 }; 134 } 135 136}