package pl.matisoft.soy.render;
import com.google.common.base.Optional;
import com.google.template.soy.data.SoyMapData;
import com.google.template.soy.msgs.SoyMsgBundle;
import com.google.template.soy.tofu.SoyTofu;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.matisoft.soy.SoyView;
import pl.matisoft.soy.config.SoyViewConfigDefaults;
import pl.matisoft.soy.data.DefaultToSoyDataConverter;
import pl.matisoft.soy.data.ToSoyDataConverter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created with IntelliJ IDEA.
* User: mati
* Date: 22/06/2013
* Time: 17:00
*
* A default implementation of TemplateRenderer, which using a print writer writes to
* a servlet output.
*
* The implementation will set all required objects if available on renderer, e.g. SoyMsgBundle, IjData (Injected Data)
* before rendering to servlet output stream.
*
* Note: in order to support "an early head flush" performance recommendation and
* "progressive rendering" a subclass of this class needs to be developed and an implementation
* needs to inspect a passed in model object, cast it accordingly and flush to system out, after rendering it.
*
* A default implementation will flush to a servlet output stream once all model objects are resolved and converted to
* SoyMapData, an important thing to notice here is that converting from model object to SoyMapData may be expensive
* depending on an implementation, i.e. if model objects have been resolved in a controller
* or if for instance they were wrapped in a Callable object.
*/
public class DefaultTemplateRenderer implements TemplateRenderer {
private static final Logger logger = LoggerFactory.getLogger(DefaultTemplateRenderer.class);
protected ToSoyDataConverter toSoyDataConverter = new DefaultToSoyDataConverter();
/**
* whether debug is on, in case it is on - Soy's Renderer Don't Add To Cache will be turned on, which means
* renderer caching will be disabled */
private boolean hotReloadMode = SoyViewConfigDefaults.DEFAULT_HOT_RELOAD_MODE;
@Override
public void render(final RenderRequest renderRequest) throws Exception {
if (!renderRequest.getCompiledTemplates().isPresent()) {
logger.warn("compiled templates are not present, nothing to render!");
return;
}
final SoyTofu compiledTemplates = renderRequest.getCompiledTemplates().get();
final String templateName = renderRequest.getTemplateName();
final SoyTofu.Renderer renderer = compiledTemplates.newRenderer(templateName);
final Optional<SoyMapData> soyModel = toSoyDataConverter.toSoyMap(renderRequest.getModel());
setupRenderer(renderer, renderRequest, soyModel);
writeResponse(renderer, renderRequest);
}
protected void setupRenderer(final SoyTofu.Renderer renderer, final RenderRequest renderRequest, final Optional<SoyMapData> model) throws Exception {
if (model.isPresent()) {
renderer.setData(model.get());
}
final Optional<SoyMapData> globalModel = renderRequest.getGlobalRuntimeModel();
if (globalModel.isPresent()) {
renderer.setIjData(globalModel.get());
}
final Optional<SoyMsgBundle> soyMsgBundleOptional = renderRequest.getSoyMsgBundle();
if (soyMsgBundleOptional.isPresent()) {
renderer.setMsgBundle(soyMsgBundleOptional.get());
if (isHotReloadModeOff()) {
if (renderRequest.getCompiledTemplates().isPresent()) {
renderRequest.getCompiledTemplates().get().addToCache(soyMsgBundleOptional.get(), null);
}
}
}
if (isHotReloadMode()) {
renderer.setDontAddToCache(true);
}
}
protected void writeResponse(final SoyTofu.Renderer renderer, final RenderRequest renderRequest) throws IOException {
final HttpServletResponse response = renderRequest.getResponse();
final SoyView soyView = renderRequest.getSoyView();
response.setContentType(soyView.getContentType());
final PrintWriter writer = response.getWriter();
writer.write(renderer.render());
writer.flush();
response.flushBuffer();
}
public void setToSoyDataConverter(final ToSoyDataConverter toSoyDataConverter) {
this.toSoyDataConverter = toSoyDataConverter;
}
public void setHotReloadMode(boolean hotReloadMode) {
this.hotReloadMode = hotReloadMode;
}
public boolean isHotReloadMode() {
return hotReloadMode;
}
public boolean isHotReloadModeOff() {
return !hotReloadMode;
}
}