001package io.jstach.opt.spring.webmvc;
002
003import java.util.Map;
004
005import org.springframework.stereotype.Controller;
006import org.springframework.util.ClassUtils;
007import org.springframework.web.method.HandlerMethod;
008import org.springframework.web.servlet.HandlerInterceptor;
009import org.springframework.web.servlet.ModelAndView;
010import org.springframework.web.servlet.View;
011import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
012import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
013
014import io.jstach.jstache.JStache;
015import io.jstach.jstachio.JStachio;
016import jakarta.servlet.http.HttpServletRequest;
017import jakarta.servlet.http.HttpServletResponse;
018
019/**
020 * A {@link HandlerInterceptor} to automatically resolve a {@link Controller} method
021 * return value that is a {@link JStache} model into a {@link JStachioModelView}.
022 *
023 * @author dsyer
024 */
025@SuppressWarnings("exports")
026public class ViewResolvingHandlerInterceptor implements HandlerInterceptor, WebMvcConfigurer {
027
028        private final JStachio jstachio;
029
030        /**
031         * Spring will inject jstachio
032         * @param jstachio jstachio instance found by spring.
033         */
034        public ViewResolvingHandlerInterceptor(JStachio jstachio) {
035                super();
036                this.jstachio = jstachio;
037        }
038
039        /**
040         * {@inheritDoc} If the model contains an attribute that is a {@link JStache} model,
041         * in particular if it has been the result of a {@link Controller} method, then it
042         * will be automatically converted into a {@link JStachioModelView}.
043         */
044        @Override
045        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
046                        ModelAndView modelAndView) throws Exception {
047
048                if (modelAndView == null || !HandlerMethod.class.isInstance(handler)) {
049                        return;
050                }
051
052                HandlerMethod method = (HandlerMethod) handler;
053
054                View view = view(method.getReturnType().getParameterType(), modelAndView.getModel());
055                if (view != null) {
056                        modelAndView.setView(view);
057                }
058        }
059
060        private View view(Class<?> modelClass, Map<String, Object> model) {
061                if (!jstachio.supportsType(modelClass)) {
062                        return null;
063                }
064                Object value = attribute(modelClass, model);
065                if (value == null) {
066                        return null;
067                }
068                return JStachioModelView.of(value, JStachioModelView.DEFAULT_MEDIA_TYPE, jstachio);
069        }
070
071        private Object attribute(Class<?> modelClass, Map<String, Object> model) {
072                String name = ClassUtils.getShortNameAsProperty(modelClass);
073                if (ClassUtils.isAssignableValue(modelClass, model.get(name))) {
074                        return model.get(name);
075                }
076                for (String key : model.keySet()) {
077                        if (ClassUtils.isAssignableValue(modelClass, model.get(key))) {
078                                return model.get(key);
079                        }
080                }
081                return null;
082        }
083
084        @Override
085        public void addInterceptors(InterceptorRegistry registry) {
086                registry.addInterceptor(this);
087        }
088
089}