com.aestasit.markdown.slidery.converters.TextTemplateConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.aestasit.markdown.slidery.converters.TextTemplateConverter.java

Source

/*
 * Copyright (C) 2011-2014 Aestas/IT
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.aestasit.markdown.slidery.converters;

import static org.apache.commons.io.FileUtils.readFileToString;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import groovy.text.SimpleTemplateEngine;
import groovy.text.Template;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.codehaus.groovy.control.CompilationFailedException;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;

import com.aestasit.markdown.slidery.configuration.Configuration;
import com.aestasit.markdown.slidery.configuration.ConfigurationBuilder;
import com.aestasit.markdown.slidery.configuration.Location;
import com.uwyn.jhighlight.renderer.Renderer;
import com.uwyn.jhighlight.renderer.XhtmlRendererFactory;

/**
 * <p>
 * This is an extension to {@link BaseConverter} that processes configured template file with the help of Groovy's
 * {@link SimpleTemplateEngine}.
 * </p>
 * 
 * <p>
 * The template has access the following variables:
 * </p>
 * 
 * <ul>
 * <li><code>configuration</code> - converter's configuration object</li>
 * <li><code>document</code> - full <i>DOM</i> tree of the slides document</li>
 * <li><code>body</code> - body element</li>
 * <li><code>slides</code> - collection of slide elements</li>
 * <li><code>title</code> - presentation's title</li>
 * <li><code>author</code> - presentation's author</li>
 * <li><code>company</code> - author's employer</li>
 * <li><code>description</code> - presentation's description</li>
 * <li><code>date</code> - presentation's date</li>
 * <li><code>backgroundColor</code> - optional background color</li>
 * <li><code>fontColor</code> - optional font color</li>
 * <li><code>fontName</code> - custom font name</li>
 * <li><code>fontLink</code> - custom font link</li>
 * <li><code>theme</code> - selected presentation's theme</li>
 * <li><code>logoFile</code> - logo file name</li>
 * <li><code>outputFile</code> - presentation's final output file name</li>
 * </ul>
 * 
 * <p>
 * It also provides a number of hook methods for the subclasses to extend default behavior:
 * </p>
 * 
 * <ul>
 * <li>{@link #expandBinding(HashMap, Document, Configuration)} method allows adding more binding variables available to
 * the template.</li>
 * <li>{@link #transformDocument(Document, Configuration)} method allows modifying <i>DOM</i> tree of the slides
 * document.</li>
 * </ul>
 * 
 * @author Andrey Adamovich
 * 
 */
public abstract class TextTemplateConverter extends BaseConverter {

    final protected void convert(Writer output, Document slidesDocument, Configuration config) throws IOException {
        String templateText = readFileToString(config.getTemplateFile(), config.getInputEncoding().name());
        transformDocument(slidesDocument, config);
        HashMap<String, Object> parameters = createBinding(slidesDocument, config);
        compileTemplate(templateText).make(parameters).writeTo(output);
    }

    private HashMap<String, Object> createBinding(Document slidesDocument, Configuration config) {
        HashMap<String, Object> binding = new HashMap<String, Object>();
        binding.put("configuration", config);
        bindTitle(binding, slidesDocument, config);
        bindDomElements(binding, slidesDocument);
        bindConfigurationElements(binding, config);
        expandBinding(binding, slidesDocument, config);
        return binding;
    }

    private void bindDomElements(HashMap<String, Object> binding, Document slidesDocument) {
        binding.put("document", slidesDocument);
        binding.put("body", slidesDocument.getElementsByTag("body").first());
        binding.put("slides", getSlideCollection(slidesDocument));
    }

    private void bindTitle(HashMap<String, Object> binding, Document slidesDocument, Configuration config) {
        if (!isBlank(config.getTitle())) {
            binding.put("title", config.getTitle());
        } else {
            binding.put("title", getFirstSlideTitle(slidesDocument));
        }
    }

    private void bindConfigurationElements(HashMap<String, Object> binding, Configuration config) {
        binding.put("author", config.getAuthor());
        binding.put("company", config.getCompany());
        binding.put("description", defaultIfNull(config.getDescription(), binding.get("title")));
        binding.put("date", new SimpleDateFormat("dd-MM-yyyy").format(config.getDate()));
        binding.put("backgroundColor", config.getBackgroundColor());
        binding.put("fontColor", config.getFontColor());
        binding.put("fontName", config.getFontName());
        binding.put("fontLink", config.getFontLink());
        binding.put("theme", config.getTheme());
        if (config.getLogo() != null) {
            binding.put("logoFile", config.getLogo().getName());
        } else {
            binding.put("logoFile", null);
        }
        if (config.getOutputFile() != null) {
            binding.put("outputFile", config.getOutputFile().getName());
        } else {
            binding.put("outputFile", null);
        }
    }

    protected void expandBinding(final HashMap<String, Object> binding, Document slidesDocument,
            ConfigurationBuilder config) {
        // Override in subclasses.
    }

    protected void transformDocument(final Document slidesDocument, final Configuration config) {
        if (!config.notesIncluded()) {
            for (Element notesElement : slidesDocument.select("aside")) {
                notesElement.remove();
            }
        }
        if ("true".equals(config.getOption("renderSyntaxHighlighting"))) {
            renderSyntaxHighlightingHtml(slidesDocument, config);
        }
    }

    private void renderSyntaxHighlightingHtml(final Document slidesDocument, final Configuration config) {
        for (Element code : slidesDocument.select("code")) {
            Charset encoding = config.getInputEncoding();
            ByteArrayInputStream input = new ByteArrayInputStream(code.text().getBytes(encoding));
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            String className = code.className();
            if (StringUtils.isBlank(className)) {
                className = "java";
            }
            Renderer renderer = XhtmlRendererFactory.getRenderer(className);
            if (renderer != null) {
                try {
                    renderer.highlight("slidery", input, out, encoding.name(), true);
                    code.html(new String(out.toByteArray(), encoding));
                    code.select("br").remove();
                    removeComments(code);
                    code.html(code.html().trim());
                    Element parent = code.parent();
                    if (parent.tagName() == "pre") {
                        parent.addClass("code");
                    }
                } catch (IOException e) {
                    // TODO: Handle exception
                }
            }
        }
    }

    private void removeComments(Node parent) {
        List<Node> nodesToRemove = new ArrayList<Node>();
        for (Node child : parent.childNodes()) {
            if (child.nodeName().equals("#comment")) {
                nodesToRemove.add(child);
            }
        }
        for (Node node : nodesToRemove) {
            node.remove();
        }
    }

    protected Elements getSlideCollection(Document slidesDocument) {
        return slidesDocument.getElementsByTag("section");
    }

    protected String getFirstSlideTitle(Document slidesDocument) {
        Elements slideCollection = getSlideCollection(slidesDocument);
        if (slideCollection != null && slideCollection.size() > 0) {
            Elements header = slideCollection.first().getElementsByTag("header");
            if (header != null && header.size() > 0) {
                return header.first().text();
            }
        }
        return "";
    }

    protected Template compileTemplate(String templateText) throws IOException {
        SimpleTemplateEngine engine = new SimpleTemplateEngine();
        Template template = null;
        try {
            template = engine.createTemplate(templateText);
        } catch (CompilationFailedException e) {
            throw new IOException(e);
        } catch (ClassNotFoundException e) {
            throw new IOException(e);
        }
        return template;
    }

    public Collection<String> getTransitionIds() {
        return Collections.<String>emptyList();
    }

    public String getDefaultTransitionId() {
        return "default";
    }

    public Collection<String> getThemeIds() {
        return Collections.<String>emptyList();
    }

    public String getDefaultThemeId() {
        return "default";
    }

    public Collection<Location> getLocations() {
        return Collections.<Location>emptyList();
    }

}