package com.github.dreamhead.moco.resource.reader;
import com.github.dreamhead.moco.HttpRequest;
import com.github.dreamhead.moco.MocoException;
import com.github.dreamhead.moco.Request;
import com.github.dreamhead.moco.model.MessageContent;
import com.github.dreamhead.moco.resource.ContentResource;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.MediaType;
import freemarker.cache.StringTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapperBuilder;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import static com.github.dreamhead.moco.model.MessageContent.content;
import static com.google.common.collect.ImmutableMap.copyOf;
public class TemplateResourceReader implements ContentResourceReader {
private static final Version CURRENT_VERSION = Configuration.getVersion();
private static final String TEMPLATE_NAME = "template";
private static Logger logger = LoggerFactory.getLogger(TemplateResourceReader.class);
static {
System.setProperty(freemarker.log.Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY,
freemarker.log.Logger.LIBRARY_NAME_NONE);
}
private final ContentResource template;
private final ImmutableMap<String, ? extends Variable> variables;
public TemplateResourceReader(final ContentResource template,
final ImmutableMap<String, ? extends Variable> variables) {
this.template = template;
this.variables = variables;
}
@Override
public MessageContent readFor(final Optional<? extends Request> request) {
if (!request.isPresent()) {
throw new IllegalStateException("Request is required to render template");
}
MessageContent content = this.template.readFor(request);
try {
Template targetTemplate = createTemplate(content);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Writer writer = new OutputStreamWriter(stream);
targetTemplate.process(variables(request.get()), writer);
return content().withContent(stream.toByteArray()).build();
} catch (ParseException e) {
logger.error("Fail to parse template: {}", content.toString());
throw new MocoException(e);
} catch (IOException e) {
throw new MocoException(e);
} catch (TemplateException e) {
throw new MocoException(e);
}
}
private Template createTemplate(final MessageContent messageContent) throws IOException {
TemplateLoader templateLoader = createTemplateLoader(messageContent);
Configuration cfg = createConfiguration(templateLoader, messageContent.getCharset());
return cfg.getTemplate(TEMPLATE_NAME);
}
private StringTemplateLoader createTemplateLoader(final MessageContent messageContent) {
StringTemplateLoader templateLoader = new StringTemplateLoader();
templateLoader.putTemplate(TEMPLATE_NAME, messageContent.toString());
return templateLoader;
}
private Configuration createConfiguration(final TemplateLoader templateLoader, final Charset charset) {
Configuration cfg = new Configuration(CURRENT_VERSION);
cfg.setObjectWrapper(new DefaultObjectWrapperBuilder(CURRENT_VERSION).build());
cfg.setDefaultEncoding(charset.name());
cfg.setTemplateLoader(templateLoader);
return cfg;
}
private ImmutableMap<String, Object> variables(final Request request) {
return ImmutableMap.<String, Object>builder()
.putAll(toVariableString(request))
.put("req", toTemplateRequest(request))
.build();
}
private TemplateRequest toTemplateRequest(final Request request) {
return new TemplateRequest(request);
}
private ImmutableMap<String, Object> toVariableString(final Request request) {
return copyOf(Maps.transformEntries(this.variables, new Maps.EntryTransformer<String, Variable, Object>() {
@Override
public Object transformEntry(final String key, final Variable value) {
return value.toTemplateVariable(request);
}
}));
}
@Override
public MediaType getContentType(final HttpRequest request) {
return template.getContentType(request);
}
}