/**
* The MIT License (MIT)
*
* Copyright (c) 2014 <mickael.jeanroy@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.mjeanroy.springmvc.view.mustache;
import static com.github.mjeanroy.springmvc.view.mustache.commons.PreConditions.notNull;
import static java.util.Collections.unmodifiableMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.AbstractTemplateView;
import com.github.mjeanroy.springmvc.view.mustache.exceptions.MustachePartialsMappingException;
/**
* Implementation of mustache view.
* Use jmustache internally as template compiler.
*/
public class MustacheView extends AbstractTemplateView {
private static final Logger log = LoggerFactory.getLogger(MustacheView.class);
/**
* Mustache compiler.
*/
private MustacheCompiler compiler;
/** List of aliases that map alias name to partial path. */
private final Map<String, String> aliases;
/**
* Build new view.
*/
public MustacheView() {
setContentType("text/html; charset=utf-8");
this.aliases = new HashMap<String, String>();
}
/**
* Set new mustache compiler that can be used to compile view.
*
* @param compiler Mustache compiler.
*/
public void setCompiler(MustacheCompiler compiler) {
notNull(compiler, "Compiler must not be null");
this.compiler = compiler;
}
/**
* Get compiler that will be used to compile view.
*
* @return Mustache compiler.
*/
public MustacheCompiler getCompiler() {
return compiler;
}
/**
* Add partials mapping.
*
* @param aliases New aliases.
*/
public void addAliases(Map<String, String> aliases) {
notNull(aliases, "Aliases must not be null");
for (Map.Entry<String, String> entry : aliases.entrySet()) {
addAlias(entry.getKey(), entry.getValue());
}
}
/**
* Add alias mapping.
*
* @param key Partial key.
* @param value Partial name.
*/
public void addAlias(String key, String value) {
this.aliases.put(
notNull(key, "Key must not be null"),
notNull(value, "Value must not be null")
);
}
/**
* Get list of aliases that map alias name to partial path.
*
* @return Aliases.
*/
public Map<String, String> getAliases() {
return unmodifiableMap(aliases);
}
@Override
protected void renderMergedTemplateModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
notNull(compiler, "Compiler must not be null and must have been set");
notNull(model, "Model must not be null");
if (log.isDebugEnabled()) {
log.debug("Render template {}", viewLayoutName());
if (log.isTraceEnabled()) {
log.trace("Using model: ");
for (Map.Entry<String, Object> entry : model.entrySet()) {
log.trace(" => {} = {}", entry.getKey(), entry.getValue());
}
}
}
response.setContentType(getContentType());
renderTemplate(model, response.getWriter());
}
@SuppressWarnings("unchecked")
private void renderTemplate(Map<String, Object> model, Writer writer) {
final Map<String, String> viewPartials = new HashMap<String, String>(aliases);
final Object object = model.get(MustacheSettings.PARTIALS_KEY);
if (object != null) {
if (!(object instanceof Map)) {
throw new MustachePartialsMappingException();
}
viewPartials.putAll((Map<String, String>) object);
}
final boolean hasAliases = !viewPartials.isEmpty();
if (hasAliases) {
compiler.addTemporaryPartialAliases(viewPartials);
}
try {
final MustacheTemplate template = compiler.compile(viewLayoutName());
template.execute(model, writer);
}
finally {
if (hasAliases) {
compiler.removeTemporaryPartialAliases();
}
}
}
private String viewLayoutName() {
return getUrl();
}
}