TemplateSectionEmitter.java
package docsite.emitters;
import j2html.tags.Tag;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import docsite.*;
import docsite.util.*;
import freemarker.core.PlainTextOutputFormat;
import freemarker.template.*;
import j2html.tags.specialized.*;
import org.jetbrains.annotations.NotNull;
import static j2html.TagCreator.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.yaml.snakeyaml.Yaml;
public class TemplateSectionEmitter extends GeneratedSectionEmitter {
private final Path templatePath;
public TemplateSectionEmitter(EmitterBuildParams params) {
super(params);
this.templatePath = locateTemplate(section.template());
}
@Override
protected SectionTag generateSectionContent(Tag<?> before) {
try (StringWriter output = new StringWriter()) {
Configuration cfg = markerfreeConfiguration();
Template template = cfg.getTemplate(templatePath.getFileName().toString());
template.process(documentMap(), output);
return section().with(before).with(rawHtml(output.toString()));
} catch (IOException | TemplateException e) {
throw new DocsiteException(e);
}
}
@NotNull
private Configuration markerfreeConfiguration() throws IOException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_29);
cfg.setDirectoryForTemplateLoading(templatePath.getParent().toFile());
cfg.setDefaultEncoding(StandardCharsets.UTF_8.toString());
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setFallbackOnNullLoopVariable(false);
cfg.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);
cfg.setOutputFormat(PlainTextOutputFormat.INSTANCE);
return cfg;
}
@SuppressWarnings("unchecked")
private Map<String,Object> documentMap() throws IOException {
String origin = origin();
String extension = origin.substring(origin.indexOf(".")+1).toLowerCase();
try (InputStream inputStream = ResourceUtil.open(baseDir, origin)) {
switch (extension) {
case "xml":
return new XmlParser().parse(inputStream);
case "json":
return new ObjectMapper().readValue(inputStream, Map.class);
case "yaml":
case "yml":
return new Yaml().loadAs(inputStream, Map.class);
default:
throw new IOException("Templating cannot be applied to format of "+origin);
}
}
}
private static Path locateTemplate(String fromTemplate) {
String internalTemplate = "templates/"+fromTemplate+".ftl";
if (ResourceUtil.existsResource(internalTemplate)) {
try {
Path tempFile = Files.createTempDirectory("docsite").resolve(internalTemplate);
ResourceUtil.copyResource(internalTemplate, tempFile.getParent());
return tempFile;
} catch (IOException e) {
throw new DocsiteException(e);
}
} else {
if (!Files.exists(Path.of(fromTemplate))) {
throw new DocsiteException("Template "+fromTemplate+" is neither an existing file nor a built-in template");
}
return Path.of(fromTemplate);
}
}
}